diff options
Diffstat (limited to 'src/js/node')
-rw-r--r-- | src/js/node/assert.js | 45 | ||||
-rw-r--r-- | src/js/node/async_hooks.js | 5 | ||||
-rw-r--r-- | src/js/node/child_process.js | 37 | ||||
-rw-r--r-- | src/js/node/crypto.js | 126 | ||||
-rw-r--r-- | src/js/node/dns.promises.js | 4 | ||||
-rw-r--r-- | src/js/node/events.js | 25 | ||||
-rw-r--r-- | src/js/node/fs.js | 135 | ||||
-rw-r--r-- | src/js/node/fs.promises.ts | 85 | ||||
-rw-r--r-- | src/js/node/http.ts (renamed from src/js/node/http.js) | 463 | ||||
-rw-r--r-- | src/js/node/https.js | 4 | ||||
-rw-r--r-- | src/js/node/https.ts | 65 | ||||
-rw-r--r-- | src/js/node/net.js | 158 | ||||
-rw-r--r-- | src/js/node/os.js | 23 | ||||
-rw-r--r-- | src/js/node/path.js | 1 | ||||
-rw-r--r-- | src/js/node/perf_hooks.js | 15 | ||||
-rw-r--r-- | src/js/node/readline.js | 10 | ||||
-rw-r--r-- | src/js/node/readline.promises.js | 6 | ||||
-rw-r--r-- | src/js/node/stream.consumers.js | 2 | ||||
-rw-r--r-- | src/js/node/stream.js | 123 | ||||
-rw-r--r-- | src/js/node/stream.promises.js | 4 | ||||
-rw-r--r-- | src/js/node/timers.promises.js | 1 | ||||
-rw-r--r-- | src/js/node/tls.js | 413 | ||||
-rw-r--r-- | src/js/node/trace_events.ts | 2 | ||||
-rw-r--r-- | src/js/node/url.js | 1194 | ||||
-rw-r--r-- | src/js/node/util.js | 136 | ||||
-rw-r--r-- | src/js/node/zlib.js | 110 |
26 files changed, 2217 insertions, 975 deletions
diff --git a/src/js/node/assert.js b/src/js/node/assert.js index 3cf158f57..ba4df43cc 100644 --- a/src/js/node/assert.js +++ b/src/js/node/assert.js @@ -1,43 +1,12 @@ // Hardcoded module "node:assert" -var { Bun } = import.meta.primordials; +var { Bun } = globalThis[Symbol.for("Bun.lazy")]("primordials"); +import util from "node:util"; + var isDeepEqual = Bun.deepEquals; -var __create = Object.create; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __getProtoOf = Object.getPrototypeOf, - __hasOwnProp = Object.prototype.hasOwnProperty; -var __markAsModule = target => __defProp(target, "__esModule", { value: !0 }); var __commonJS = (cb, mod) => function () { return mod || (0, cb[Object.keys(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; -var __reExport = (target, module2, desc) => { - if ((module2 && typeof module2 == "object") || typeof module2 == "function") - for (let key of __getOwnPropNames(module2)) - !__hasOwnProp.call(target, key) && - key !== "default" && - __defProp(target, key, { - get: () => module2[key], - enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable, - }); - return target; - }, - __toModule = module2 => - __reExport( - __markAsModule( - __defProp( - module2 != null ? __create(__getProtoOf(module2)) : {}, - "default", - module2 && module2.__esModule && "default" in module2 - ? { get: () => module2.default, enumerable: !0 } - : { value: module2, enumerable: !0 }, - ), - ), - module2, - ); - -var require = path => import.meta.require(path); // assert/build/internal/errors.js var require_errors = __commonJS({ @@ -175,7 +144,6 @@ var require_errors = __commonJS({ "ERR_INVALID_ARG_VALUE", function (name, value) { var reason = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "is invalid"; - util === void 0 && (util = require("util")); var inspected = util.inspect(value); return ( inspected.length > 128 && (inspected = "".concat(inspected.slice(0, 128), "...")), @@ -387,8 +355,7 @@ var require_assertion_error = __commonJS({ _typeof(obj) ); } - var _require = require("util"), - inspect = _require.inspect, + var inspect = util.inspect, _require2 = require_errors(), ERR_INVALID_ARG_TYPE = _require2.codes.ERR_INVALID_ARG_TYPE; function endsWith(str, search, this_len) { @@ -871,9 +838,9 @@ var require_assert = __commonJS({ ERR_INVALID_RETURN_VALUE = _require$codes.ERR_INVALID_RETURN_VALUE, ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS, AssertionError = require_assertion_error(), - _require2 = require("util"), + _require2 = util, inspect = _require2.inspect, - _require$types = require("util").types, + _require$types = util.types, isPromise = _require$types.isPromise, isRegExp = _require$types.isRegExp, objectAssign = Object.assign, diff --git a/src/js/node/async_hooks.js b/src/js/node/async_hooks.js index 7887bb64f..ec82ce8aa 100644 --- a/src/js/node/async_hooks.js +++ b/src/js/node/async_hooks.js @@ -1,8 +1,5 @@ // Hardcoded module "node:async_hooks" -var drainMicrotasks = () => { - ({ drainMicrotasks } = import.meta.require("bun:jsc")); - drainMicrotasks(); -}; +import { drainMicrotasks } from "bun:jsc"; var notImplemented = () => { console.warn( diff --git a/src/js/node/child_process.js b/src/js/node/child_process.js index 691c9e096..29b203219 100644 --- a/src/js/node/child_process.js +++ b/src/js/node/child_process.js @@ -1,15 +1,11 @@ // Hardcoded module "node:child_process" -const EventEmitter = import.meta.require("node:events"); -const { - Readable: { fromWeb: ReadableFromWeb }, - NativeWritable, -} = import.meta.require("node:stream"); -const { - constants: { signals }, -} = import.meta.require("node:os"); -const { promisify } = import.meta.require("node:util"); - -const { ArrayBuffer, Uint8Array, String, Object, Buffer, Promise } = import.meta.primordials; +import { EventEmitter } from "node:events"; +import * as StreamModule from "node:stream"; +import { constants } from "node:os"; +import { promisify } from "node:util"; +const signals = constants.signals; + +const { ArrayBuffer, Uint8Array, String, Object, Buffer, Promise } = globalThis[Symbol.for("Bun.lazy")]("primordials"); var ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty; var ObjectCreate = Object.create; @@ -21,8 +17,6 @@ var BufferIsEncoding = Buffer.isEncoding; var kEmptyObject = ObjectCreate(null); var ArrayPrototypePush = Array.prototype.push; -var ArrayPrototypeReduce = Array.prototype.reduce; -var ArrayPrototypeFilter = Array.prototype.filter; var ArrayPrototypeJoin = Array.prototype.join; var ArrayPrototypeMap = Array.prototype.map; var ArrayPrototypeIncludes = Array.prototype.includes; @@ -60,6 +54,9 @@ if (__TRACK_STDIO__) { }; } +var NativeWritable; +var ReadableFromWeb; + // Sections: // 1. Exported child_process functions // 2. child_process helpers @@ -961,6 +958,10 @@ export class ChildProcess extends EventEmitter { debug("ChildProcess: getBunSpawnIo: this.#handle is undefined"); } } + + NativeWritable ||= StreamModule.NativeWritable; + ReadableFromWeb ||= StreamModule.Readable.fromWeb; + const io = this.#stdioOptions[i]; switch (i) { case 0: { @@ -979,15 +980,7 @@ export class ChildProcess extends EventEmitter { case 1: { switch (io) { case "pipe": - return ReadableFromWeb( - this.#handle[fdToStdioName(i)], - __TRACK_STDIO__ - ? { - encoding, - __id: `PARENT_${fdToStdioName(i).toUpperCase()}-${globalThis.__getId()}`, - } - : { encoding }, - ); + return ReadableFromWeb(this.#handle[fdToStdioName(i)], { encoding }); case "inherit": return process[fdToStdioName(i)] || null; case "destroyed": diff --git a/src/js/node/crypto.js b/src/js/node/crypto.js index a644499c8..20e052e3e 100644 --- a/src/js/node/crypto.js +++ b/src/js/node/crypto.js @@ -5,10 +5,12 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf, __hasOwnProp = Object.prototype.hasOwnProperty; +import { StringDecoder } from "node:string_decoder"; +import * as BufferModule from "node:buffer"; +import * as StreamModule from "node:stream"; const MAX_STRING_LENGTH = 536870888; - -var __require = id => import.meta.require(id); +var Buffer = globalThis.Buffer; const crypto = globalThis.crypto; const globalCrypto = crypto; @@ -48,7 +50,7 @@ var __export = (target, all) => { // node_modules/safe-buffer/index.js var require_safe_buffer = __commonJS({ "node_modules/safe-buffer/index.js"(exports, module) { - var buffer = __require("buffer"), + var buffer = BufferModule, Buffer2 = buffer.Buffer; function copyProps(src, dst) { for (var key in src) dst[key] = src[key]; @@ -146,21 +148,20 @@ var require_hash_base = __commonJS({ "node_modules/hash-base/index.js"(exports, module) { "use strict"; var Buffer2 = require_safe_buffer().Buffer, - Transform = __require("readable-stream").Transform, inherits = require_inherits_browser(); function throwIfNotStringOrBuffer(val, prefix) { if (!Buffer2.isBuffer(val) && typeof val != "string") throw new TypeError(prefix + " must be a string or a buffer"); } function HashBase(blockSize) { - Transform.call(this), + StreamModule.Transform.call(this), (this._block = Buffer2.allocUnsafe(blockSize)), (this._blockSize = blockSize), (this._blockOffset = 0), (this._length = [0, 0, 0, 0]), (this._finalized = !1); } - inherits(HashBase, Transform); + inherits(HashBase, StreamModule.Transform); HashBase.prototype._transform = function (chunk, encoding, callback) { var error = null; try { @@ -341,7 +342,7 @@ var require_md5 = __commonJS({ var require_ripemd160 = __commonJS({ "node_modules/ripemd160/index.js"(exports, module) { "use strict"; - var Buffer2 = __require("buffer").Buffer, + var Buffer2 = Buffer, inherits = require_inherits_browser(), HashBase = require_hash_base(), ARRAY16 = new Array(16), @@ -1063,25 +1064,20 @@ var require_sha2 = __commonJS({ }, }); -// stream.js -var stream_exports = import.meta.require("node:stream"); - // node_modules/cipher-base/index.js var require_cipher_base = __commonJS({ "node_modules/cipher-base/index.js"(exports, module) { var Buffer2 = require_safe_buffer().Buffer, - Transform = stream_exports.Transform, - StringDecoder = __require("string_decoder").StringDecoder, inherits = require_inherits_browser(); function CipherBase(hashMode) { - Transform.call(this), + StreamModule.Transform.call(this), (this.hashMode = typeof hashMode == "string"), this.hashMode ? (this[hashMode] = this._finalOrDigest) : (this.final = this._finalOrDigest), this._final && ((this.__final = this._final), (this._final = null)), (this._decoder = null), (this._encoding = null); } - inherits(CipherBase, Transform); + inherits(CipherBase, StreamModule.Transform); CipherBase.prototype.update = function (data, inputEnc, outputEnc) { typeof data == "string" && (data = Buffer2.from(data, inputEnc)); var outData = this._update(data); @@ -1134,15 +1130,13 @@ var require_cipher_base = __commonJS({ var require_browser2 = __commonJS({ "node_modules/create-hash/browser.js"(exports, module) { ("use strict"); - const { Transform } = stream_exports; - // does not become a node stream unless you create it into one const LazyHash = function Hash(algorithm, options) { this._options = options; this._hasher = new CryptoHasher(algorithm, options); this._finalized = false; }; - LazyHash.prototype = Object.create(Transform.prototype); + LazyHash.prototype = Object.create(StreamModule.Transform.prototype); LazyHash.prototype.update = function update(data, encoding) { this._checkFinalized(); this._hasher.update(data, encoding); @@ -1169,7 +1163,7 @@ var require_browser2 = __commonJS({ }; const lazyHashFullInitProto = { - __proto__: Transform.prototype, + __proto__: StreamModule.Transform.prototype, ...LazyHash.prototype, _transform(data, encoding, callback) { this.update(data, encoding); @@ -1271,7 +1265,7 @@ var require_browser2 = __commonJS({ Object.defineProperty(LazyHash.prototype, method, { get() { Object.setPrototypeOf(this, lazyHashFullInitProto); - Transform.call(this, this._options); + StreamModule.Transform.call(this, this._options); return this[method]; }, enumerable: false, @@ -3330,12 +3324,7 @@ var require_bn = __commonJS({ this._init(number || 0, base || 10, endian || "be")); } typeof module2 == "object" ? (module2.exports = BN) : (exports2.BN = BN), (BN.BN = BN), (BN.wordSize = 26); - var Buffer2; - try { - typeof window < "u" && typeof window.Buffer < "u" - ? (Buffer2 = window.Buffer) - : (Buffer2 = __require("buffer").Buffer); - } catch {} + var Buffer2 = Buffer; (BN.isBN = function (num) { return num instanceof BN ? !0 @@ -5322,12 +5311,7 @@ var require_bn2 = __commonJS({ this._init(number || 0, base || 10, endian || "be")); } typeof module2 == "object" ? (module2.exports = BN) : (exports2.BN = BN), (BN.BN = BN), (BN.wordSize = 26); - var Buffer2; - try { - typeof window < "u" && typeof window.Buffer < "u" - ? (Buffer2 = window.Buffer) - : (Buffer2 = __require("buffer").Buffer); - } catch {} + var Buffer2 = Buffer; (BN.isBN = function (num) { return num instanceof BN ? !0 @@ -7670,12 +7654,7 @@ var require_bn3 = __commonJS({ this._init(number || 0, base || 10, endian || "be")); } typeof module2 == "object" ? (module2.exports = BN) : (exports2.BN = BN), (BN.BN = BN), (BN.wordSize = 26); - var Buffer2; - try { - typeof window < "u" && typeof window.Buffer < "u" - ? (Buffer2 = window.Buffer) - : (Buffer2 = __require("buffer").Buffer); - } catch {} + var Buffer2 = Buffer; (BN.isBN = function (num) { return num instanceof BN ? !0 @@ -9797,12 +9776,7 @@ var require_bn4 = __commonJS({ this._init(number || 0, base || 10, endian || "be")); } typeof module2 == "object" ? (module2.exports = BN) : (exports2.BN = BN), (BN.BN = BN), (BN.wordSize = 26); - var Buffer2; - try { - typeof window < "u" && typeof window.Buffer < "u" - ? (Buffer2 = window.Buffer) - : (Buffer2 = __require("buffer").Buffer); - } catch {} + var Buffer2 = Buffer; (BN.isBN = function (num) { return num instanceof BN ? !0 @@ -15491,12 +15465,8 @@ var require_bn5 = __commonJS({ this._init(number || 0, base || 10, endian || "be")); } typeof module2 == "object" ? (module2.exports = BN) : (exports2.BN = BN), (BN.BN = BN), (BN.wordSize = 26); - var Buffer2; - try { - typeof window < "u" && typeof window.Buffer < "u" - ? (Buffer2 = window.Buffer) - : (Buffer2 = __require("buffer").Buffer); - } catch {} + var Buffer2 = Buffer; + (BN.isBN = function (num) { return num instanceof BN ? !0 @@ -17461,8 +17431,8 @@ var require_bn5 = __commonJS({ var require_safer = __commonJS({ "node_modules/safer-buffer/safer.js"(exports, module) { "use strict"; - var buffer = __require("buffer"), - Buffer2 = buffer.Buffer, + var buffer = BufferModule, + Buffer2 = Buffer, safer = {}, key; for (key in buffer) @@ -19334,7 +19304,6 @@ var require_browser8 = __commonJS({ "node_modules/browserify-sign/browser/index.js"(exports, module) { var Buffer2 = require_safe_buffer().Buffer, createHash = require_browser2(), - stream = __require("readable-stream"), inherits = require_inherits_browser(), sign = require_sign(), verify = require_verify(), @@ -19343,7 +19312,7 @@ var require_browser8 = __commonJS({ (algorithms[key].id = Buffer2.from(algorithms[key].id, "hex")), (algorithms[key.toLowerCase()] = algorithms[key]); }); function Sign(algorithm) { - stream.Writable.call(this); + StreamModule.Writable.call(this); var data = algorithms[algorithm]; if (!data) throw new Error("Unknown message digest"); (this._hashType = data.hash), @@ -19351,7 +19320,7 @@ var require_browser8 = __commonJS({ (this._tag = data.id), (this._signType = data.sign); } - inherits(Sign, stream.Writable); + inherits(Sign, StreamModule.Writable); Sign.prototype._write = function (data, _, done) { this._hash.update(data), done(); }; @@ -19365,12 +19334,12 @@ var require_browser8 = __commonJS({ return enc ? sig.toString(enc) : sig; }; function Verify(algorithm) { - stream.Writable.call(this); + StreamModule.Writable.call(this); var data = algorithms[algorithm]; if (!data) throw new Error("Unknown message digest"); (this._hash = createHash(data.hash)), (this._tag = data.id), (this._signType = data.sign); } - inherits(Verify, stream.Writable); + inherits(Verify, StreamModule.Writable); Verify.prototype._write = function (data, _, done) { this._hash.update(data), done(); }; @@ -19423,12 +19392,7 @@ var require_bn6 = __commonJS({ this._init(number || 0, base || 10, endian || "be")); } typeof module2 == "object" ? (module2.exports = BN) : (exports2.BN = BN), (BN.BN = BN), (BN.wordSize = 26); - var Buffer2; - try { - typeof window < "u" && typeof window.Buffer < "u" - ? (Buffer2 = window.Buffer) - : (Buffer2 = __require("buffer").Buffer); - } catch {} + var Buffer2 = Buffer; (BN.isBN = function (num) { return num instanceof BN ? !0 @@ -23788,6 +23752,7 @@ var crypto_exports = { var DEFAULT_ENCODING = "buffer", getRandomValues = array => crypto.getRandomValues(array), randomUUID = () => crypto.randomUUID(), + randomInt = (...args) => crypto.randomInt(...args), timingSafeEqual = "timingSafeEqual" in crypto ? (a, b) => { @@ -23837,11 +23802,35 @@ timingSafeEqual && Object.defineProperty(scryptSync, "name", { value: "::bunternal::", })); + +const harcoded_curves = [ + "p192", + "p224", + "p256", + "p384", + "p521", + "curve25519", + "ed25519", + "secp256k1", + "secp224r1", + "prime256v1", + "prime192v1", + "ed25519", + "secp384r1", + "secp521r1", +]; + +function getCurves() { + return harcoded_curves; +} + var webcrypto = crypto; __export(crypto_exports, { DEFAULT_ENCODING: () => DEFAULT_ENCODING, getRandomValues: () => getRandomValues, randomUUID: () => randomUUID, + randomInt: () => randomInt, + getCurves: () => getCurves, scrypt: () => scrypt, scryptSync: () => scryptSync, timingSafeEqual: () => timingSafeEqual, @@ -23890,6 +23879,17 @@ export const { createCredentials, constants, } = crypto_exports; -export { DEFAULT_ENCODING, getRandomValues, randomUUID, scrypt, scryptSync, timingSafeEqual, webcrypto }; + +export { + DEFAULT_ENCODING, + getRandomValues, + getCurves, + randomUUID, + randomInt, + scrypt, + scryptSync, + timingSafeEqual, + webcrypto, +}; export default crypto_exports; /*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */ diff --git a/src/js/node/dns.promises.js b/src/js/node/dns.promises.js index b41cc2b22..bcc47faee 100644 --- a/src/js/node/dns.promises.js +++ b/src/js/node/dns.promises.js @@ -1,11 +1,12 @@ // Hardcoded module "node:dns/promises" -const { promises } = import.meta.require("node:dns"); +import { promises } from "node:dns"; export const { lookup, lookupService, resolve, resolve4, + resolve6, resolveAny, resolveCname, resolveCaa, @@ -27,6 +28,7 @@ export default { lookupService, resolve, resolve4, + resolve6, resolveAny, resolveCname, resolveCaa, diff --git a/src/js/node/events.js b/src/js/node/events.js index e42f89ad1..111fdb524 100644 --- a/src/js/node/events.js +++ b/src/js/node/events.js @@ -1,7 +1,8 @@ // Reimplementation of https://nodejs.org/api/events.html // Reference: https://github.com/nodejs/node/blob/main/lib/events.js import { throwNotImplemented } from "../shared"; -var { isPromise, Array, Object } = import.meta.primordials; + +var { isPromise, Array, Object } = globalThis[Symbol.for("Bun.lazy")]("primordials"); const SymbolFor = Symbol.for; const ObjectDefineProperty = Object.defineProperty; const kCapture = Symbol("kCapture"); @@ -386,8 +387,6 @@ Object.defineProperties(EventEmitter, { EventEmitter.init = EventEmitter; EventEmitter[Symbol.for("CommonJS")] = 0; -export default EventEmitter; - function eventTargetAgnosticRemoveListener(emitter, name, listener, flags) { if (typeof emitter.removeListener === "function") { emitter.removeListener(name, listener); @@ -454,10 +453,24 @@ function checkListener(listener) { } } -export class EventEmitterAsyncResource extends EventEmitter { +class EventEmitterAsyncResource extends EventEmitter { constructor(options = undefined) { throwNotImplemented("EventEmitterAsyncResource", 1832); } } - -EventEmitter.EventEmitterAsyncResource = EventEmitterAsyncResource; +const usingDomains = false; +// EventEmitter[Symbol.for("CommonJS")] = 0; +Object.assign(EventEmitter, { once, on, getEventListeners, setMaxListeners, listenerCount, EventEmitterAsyncResource }); +export { + EventEmitter, + captureRejectionSymbol, + kErrorMonitor as errorMonitor, + getEventListeners, + listenerCount, + on, + once, + setMaxListeners, + usingDomains, + EventEmitterAsyncResource, +}; +export default EventEmitter; diff --git a/src/js/node/fs.js b/src/js/node/fs.js index f117020dd..072102c35 100644 --- a/src/js/node/fs.js +++ b/src/js/node/fs.js @@ -1,12 +1,73 @@ -// Hardcoded module "node:fs" -var { direct, isPromise, isCallable } = import.meta.primordials; -var promises = import.meta.require("node:fs/promises"); +export var ReadStream; +export var WriteStream; + +import { EventEmitter } from "node:events"; -var { Readable, NativeWritable, _getNativeReadableStreamPrototype, eos: eos_ } = import.meta.require("node:stream"); -var NativeReadable = _getNativeReadableStreamPrototype(2, Readable); // 2 means native type is a file here +// Hardcoded module "node:fs" +var { direct, isPromise, isCallable } = globalThis[Symbol.for("Bun.lazy")]("primordials"); +import promises from "node:fs/promises"; +export { default as promises } from "node:fs/promises"; +import * as Stream from "node:stream"; var fs = Bun.fs(); var debug = process.env.DEBUG ? console.log : () => {}; + +class FSWatcher extends EventEmitter { + #watcher; + #listener; + constructor(path, options, listener) { + super(); + + if (typeof options === "function") { + listener = options; + options = {}; + } else if (typeof options === "string") { + options = { encoding: options }; + } + + if (typeof listener !== "function") { + listener = () => {}; + } + + this.#listener = listener; + try { + this.#watcher = fs.watch(path, options || {}, this.#onEvent.bind(this)); + } catch (e) { + if (!e.message?.startsWith("FileNotFound")) { + throw e; + } + const notFound = new Error(`ENOENT: no such file or directory, watch '${path}'`); + notFound.code = "ENOENT"; + notFound.errno = -2; + notFound.path = path; + notFound.syscall = "watch"; + notFound.filename = path; + throw notFound; + } + } + + #onEvent(eventType, filenameOrError) { + if (eventType === "error" || eventType === "close") { + this.emit(eventType, filenameOrError); + } else { + this.emit("change", eventType, filenameOrError); + this.#listener(eventType, filenameOrError); + } + } + + close() { + this.#watcher?.close(); + this.#watcher = null; + } + + ref() { + this.#watcher?.ref(); + } + + unref() { + this.#watcher?.unref(); + } +} export var access = function access(...args) { callbackify(fs.accessSync, args); }, @@ -151,9 +212,45 @@ export var access = function access(...args) { lutimesSync = fs.lutimesSync.bind(fs), rmSync = fs.rmSync.bind(fs), rmdirSync = fs.rmdirSync.bind(fs), + writev = (fd, buffers, position, callback) => { + if (typeof position === "function") { + callback = position; + position = null; + } + + queueMicrotask(() => { + try { + var written = fs.writevSync(fd, buffers, position); + } catch (e) { + callback(e); + } + + callback(null, written, buffers); + }); + }, + writevSync = fs.writevSync.bind(fs), + readv = (fd, buffers, position, callback) => { + if (typeof position === "function") { + callback = position; + position = null; + } + + queueMicrotask(() => { + try { + var written = fs.readvSync(fd, buffers, position); + } catch (e) { + callback(e); + } + + callback(null, written, buffers); + }); + }, + readvSync = fs.readvSync.bind(fs), Dirent = fs.Dirent, Stats = fs.Stats, - promises = import.meta.require("node:fs/promises"); + watch = function watch(path, options, listener) { + return new FSWatcher(path, options, listener); + }; function callbackify(fsFunction, args) { try { @@ -222,7 +319,8 @@ var defaultReadStreamOptions = { }; var ReadStreamClass; -export var ReadStream = (function (InternalReadStream) { + +ReadStream = (function (InternalReadStream) { ReadStreamClass = InternalReadStream; Object.defineProperty(ReadStreamClass.prototype, Symbol.toStringTag, { value: "ReadStream", @@ -241,7 +339,7 @@ export var ReadStream = (function (InternalReadStream) { }, ); })( - class ReadStream extends NativeReadable { + class ReadStream extends Stream._getNativeReadableStreamPrototype(2, Stream.Readable) { constructor(pathOrFd, options = defaultReadStreamOptions) { if (typeof options !== "object" || !options) { throw new TypeError("Expected options to be an object"); @@ -569,7 +667,7 @@ var defaultWriteStreamOptions = { }; var WriteStreamClass; -export var WriteStream = (function (InternalWriteStream) { +WriteStream = (function (InternalWriteStream) { WriteStreamClass = InternalWriteStream; Object.defineProperty(WriteStreamClass.prototype, Symbol.toStringTag, { value: "WritesStream", @@ -577,8 +675,8 @@ export var WriteStream = (function (InternalWriteStream) { }); return Object.defineProperty( - function WriteStream(options) { - return new InternalWriteStream(options); + function WriteStream(path, options) { + return new InternalWriteStream(path, options); }, Symbol.hasInstance, { @@ -588,7 +686,7 @@ export var WriteStream = (function (InternalWriteStream) { }, ); })( - class WriteStream extends NativeWritable { + class WriteStream extends Stream.NativeWritable { constructor(path, options = defaultWriteStreamOptions) { if (!options) { throw new TypeError("Expected options to be an object"); @@ -877,7 +975,7 @@ export function createWriteStream(path, options) { } // NOTE: This was too smart and doesn't actually work -// export var WriteStream = Object.defineProperty( +// WriteStream = Object.defineProperty( // function WriteStream(path, options) { // var _InternalWriteStream = getLazyWriteStream(); // return new _InternalWriteStream(path, options); @@ -886,7 +984,7 @@ export function createWriteStream(path, options) { // { value: (instance) => instance[writeStreamSymbol] === true }, // ); -// export var ReadStream = Object.defineProperty( +// ReadStream = Object.defineProperty( // function ReadStream(path, options) { // var _InternalReadStream = getLazyReadStream(); // return new _InternalReadStream(path, options); @@ -1002,7 +1100,12 @@ export default { writeSync, WriteStream, ReadStream, - + watch, + FSWatcher, + writev, + writevSync, + readv, + readvSync, [Symbol.for("::bunternal::")]: { ReadStreamClass, WriteStreamClass, @@ -1014,3 +1117,5 @@ export default { // return getLazyReadStream(); // }, }; + +export { constants } from "node:fs/promises"; diff --git a/src/js/node/fs.promises.ts b/src/js/node/fs.promises.ts index de802928b..12278ef53 100644 --- a/src/js/node/fs.promises.ts +++ b/src/js/node/fs.promises.ts @@ -1,4 +1,5 @@ // Hardcoded module "node:fs/promises" + // Note: `constants` is injected into the top of this file declare var constants: typeof import("node:fs/promises").constants; @@ -38,6 +39,55 @@ var promisify = { }, }[notrace]; +export function watch( + filename: string | Buffer | URL, + options: { encoding?: BufferEncoding; persistent?: boolean; recursive?: boolean; signal?: AbortSignal } = {}, +) { + type Event = { + eventType: string; + filename: string | Buffer | undefined; + }; + const events: Array<Event> = []; + if (filename instanceof URL) { + throw new TypeError("Watch URLs are not supported yet"); + } else if (Buffer.isBuffer(filename)) { + filename = filename.toString(); + } else if (typeof filename !== "string") { + throw new TypeError("Expected path to be a string or Buffer"); + } + let nextEventResolve: Function | null = null; + if (typeof options === "string") { + options = { encoding: options }; + } + fs.watch(filename, options || {}, (eventType: string, filename: string | Buffer | undefined) => { + events.push({ eventType, filename }); + if (nextEventResolve) { + const resolve = nextEventResolve; + nextEventResolve = null; + resolve(); + } + }); + return { + async *[Symbol.asyncIterator]() { + let closed = false; + while (!closed) { + while (events.length) { + let event = events.shift() as Event; + if (event.eventType === "close") { + closed = true; + break; + } + if (event.eventType === "error") { + closed = true; + throw event.filename; + } + yield event; + } + await new Promise((resolve: Function) => (nextEventResolve = resolve)); + } + }, + }; +} export var access = promisify(fs.accessSync), appendFile = promisify(fs.appendFileSync), close = promisify(fs.closeSync), @@ -73,7 +123,37 @@ export var access = promisify(fs.accessSync), utimes = promisify(fs.utimesSync), lutimes = promisify(fs.lutimesSync), rm = promisify(fs.rmSync), - rmdir = promisify(fs.rmdirSync); + rmdir = promisify(fs.rmdirSync), + writev = (fd, buffers, position) => { + return new Promise((resolve, reject) => { + try { + var bytesWritten = fs.writevSync(fd, buffers, position); + } catch (err) { + reject(err); + return; + } + + resolve({ + bytesWritten, + buffers, + }); + }); + }, + readv = (fd, buffers, position) => { + return new Promise((resolve, reject) => { + try { + var bytesRead = fs.readvSync(fd, buffers, position); + } catch (err) { + reject(err); + return; + } + + resolve({ + bytesRead, + buffers, + }); + }); + }; export default { access, @@ -112,6 +192,9 @@ export default { lutimes, rm, rmdir, + watch, + writev, + readv, constants, [Symbol.for("CommonJS")]: 0, }; diff --git a/src/js/node/http.js b/src/js/node/http.ts index 8839c9af7..fe075c832 100644 --- a/src/js/node/http.js +++ b/src/js/node/http.ts @@ -1,10 +1,63 @@ // Hardcoded module "node:http" -const { EventEmitter } = import.meta.require("node:events"); -const { isIPv6 } = import.meta.require("node:net"); -const { Readable, Writable, Duplex } = import.meta.require("node:stream"); -const { URL } = import.meta.require("node:url"); -const { newArrayWithSize, String, Object, Array } = import.meta.primordials; -const { isTypedArray } = import.meta.require("util/types"); +import { EventEmitter } from "node:events"; +import { Readable, Writable, Duplex } from "node:stream"; +import { isTypedArray } from "util/types"; + +const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; +/** + * True if val contains an invalid field-vchar + * field-value = *( field-content / obs-fold ) + * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] + * field-vchar = VCHAR / obs-text + */ +function checkInvalidHeaderChar(val: string) { + return RegExpPrototypeExec.call(headerCharRegex, val) !== null; +} + +export const validateHeaderName = (name, label) => { + if (typeof name !== "string" || !name || !checkIsHttpToken(name)) { + // throw new ERR_INVALID_HTTP_TOKEN(label || "Header name", name); + throw new Error("ERR_INVALID_HTTP_TOKEN"); + } +}; + +export const validateHeaderValue = (name, value) => { + if (value === undefined) { + // throw new ERR_HTTP_INVALID_HEADER_VALUE(value, name); + throw new Error("ERR_HTTP_INVALID_HEADER_VALUE"); + } + if (checkInvalidHeaderChar(value)) { + // throw new ERR_INVALID_CHAR("header content", name); + throw new Error("ERR_INVALID_CHAR"); + } +}; + +// Cheaper to duplicate this than to import it from node:net +function isIPv6(input) { + const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; + const v4Str = `(${v4Seg}[.]){3}${v4Seg}`; + const v6Seg = "(?:[0-9a-fA-F]{1,4})"; + const IPv6Reg = new RegExp( + "^(" + + `(?:${v6Seg}:){7}(?:${v6Seg}|:)|` + + `(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|` + + `(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|` + + `(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|` + + `(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|` + + `(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|` + + `(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|` + + `(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:))` + + ")(%[0-9a-zA-Z-.:]{1,})?$", + ); + + return IPv6Reg.test(input); +} + +// TODO: add primordial for URL +// Importing from node:url is unnecessary +const { URL } = globalThis; + +const { newArrayWithSize, String, Object, Array } = globalThis[Symbol.for("Bun.lazy")]("primordials"); const globalReportError = globalThis.reportError; const setTimeout = globalThis.setTimeout; @@ -39,7 +92,6 @@ const INVALID_PATH_REGEX = /[^\u0021-\u00ff]/; const NODE_HTTP_WARNING = "WARN: Agent is mostly unused in Bun's implementation of http. If you see strange behavior, this is probably the cause."; -var _globalAgent; var _defaultHTTPSAgent; var kInternalRequest = Symbol("kInternalRequest"); var kInternalSocketData = Symbol.for("::bunternal::"); @@ -57,18 +109,40 @@ function isValidTLSArray(obj) { } } +class ERR_INVALID_ARG_TYPE extends TypeError { + constructor(name, expected, actual) { + super(`The ${name} argument must be of type ${expected}. Received type ${typeof actual}`); + this.code = "ERR_INVALID_ARG_TYPE"; + } +} + +function validateMsecs(numberlike: any, field: string) { + if (typeof numberlike !== "number" || numberlike < 0) { + throw new ERR_INVALID_ARG_TYPE(field, "number", numberlike); + } + + return numberlike; +} +function validateFunction(callable: any, field: string) { + if (typeof callable !== "function") { + throw new ERR_INVALID_ARG_TYPE(field, "Function", callable); + } + + return callable; +} + function getHeader(headers, name) { if (!headers) return; const result = headers.get(name); return result == null ? undefined : result; } +type FakeSocket = InstanceType<typeof FakeSocket>; var FakeSocket = class Socket extends Duplex { bytesRead = 0; bytesWritten = 0; connecting = false; - remoteAddress = null; - localAddress = "127.0.0.1"; + remoteAddress: string | null = null; remotePort; timeout = 0; @@ -149,25 +223,25 @@ export function createServer(options, callback) { } export class Agent extends EventEmitter { - #defaultPort = 80; - #protocol = "http:"; - #options; - #requests; - #sockets; - #freeSockets; - - #keepAliveMsecs; - #keepAlive; - #maxSockets; - #maxFreeSockets; - #scheduling; - #maxTotalSockets; - #totalSocketCount; + defaultPort = 80; + protocol = "http:"; + options; + requests; + sockets; + freeSockets; + + keepAliveMsecs; + keepAlive; + maxSockets; + maxFreeSockets; + scheduling; + maxTotalSockets; + totalSocketCount; #fakeSocket; static get globalAgent() { - return (_globalAgent ??= new Agent()); + return globalAgent; } static get defaultMaxSockets() { @@ -176,75 +250,23 @@ export class Agent extends EventEmitter { constructor(options = kEmptyObject) { super(); - this.#options = options = { ...options, path: null }; + this.options = options = { ...options, path: null }; if (options.noDelay === undefined) options.noDelay = true; // Don't confuse net and make it think that we're connecting to a pipe - this.#requests = kEmptyObject; - this.#sockets = kEmptyObject; - this.#freeSockets = kEmptyObject; - - this.#keepAliveMsecs = options.keepAliveMsecs || 1000; - this.#keepAlive = options.keepAlive || false; - this.#maxSockets = options.maxSockets || Agent.defaultMaxSockets; - this.#maxFreeSockets = options.maxFreeSockets || 256; - this.#scheduling = options.scheduling || "lifo"; - this.#maxTotalSockets = options.maxTotalSockets; - this.#totalSocketCount = 0; - this.#defaultPort = options.defaultPort || 80; - this.#protocol = options.protocol || "http:"; - } + this.requests = kEmptyObject; + this.sockets = kEmptyObject; + this.freeSockets = kEmptyObject; - get defaultPort() { - return this.#defaultPort; - } - - get protocol() { - return this.#protocol; - } - - get requests() { - return this.#requests; - } - - get sockets() { - return this.#sockets; - } - - get freeSockets() { - return this.#freeSockets; - } - - get options() { - return this.#options; - } - - get keepAliveMsecs() { - return this.#keepAliveMsecs; - } - - get keepAlive() { - return this.#keepAlive; - } - - get maxSockets() { - return this.#maxSockets; - } - - get maxFreeSockets() { - return this.#maxFreeSockets; - } - - get scheduling() { - return this.#scheduling; - } - - get maxTotalSockets() { - return this.#maxTotalSockets; - } - - get totalSocketCount() { - return this.#totalSocketCount; + this.keepAliveMsecs = options.keepAliveMsecs || 1000; + this.keepAlive = options.keepAlive || false; + this.maxSockets = options.maxSockets || Agent.defaultMaxSockets; + this.maxFreeSockets = options.maxFreeSockets || 256; + this.scheduling = options.scheduling || "lifo"; + this.maxTotalSockets = options.maxTotalSockets; + this.totalSocketCount = 0; + this.defaultPort = options.defaultPort || 80; + this.protocol = options.protocol || "http:"; } createConnection() { @@ -315,6 +337,7 @@ export class Server extends EventEmitter { #tls; #is_tls = false; listening = false; + serverName; constructor(options, callback) { super(); @@ -403,7 +426,7 @@ export class Server extends EventEmitter { // not actually implemented } - close(optionalCallback) { + close(optionalCallback?) { const server = this.#server; if (!server) { if (typeof optionalCallback === "function") @@ -459,7 +482,7 @@ export class Server extends EventEmitter { if (tls) { this.serverName = tls.serverName || host || "localhost"; } - this.#server = Bun.serve({ + this.#server = Bun.serve<any>({ tls, port, hostname: host, @@ -555,6 +578,9 @@ function getDefaultHTTPSAgent() { } export class IncomingMessage extends Readable { + method: string; + complete: boolean; + constructor(req, defaultIncomingOpts) { const method = req.method; @@ -579,7 +605,7 @@ export class IncomingMessage extends Readable { this.#type = type; this.complete = !!this.#noBody; - this.#bodyStream = null; + this.#bodyStream = undefined; const socket = new FakeSocket(); socket.remoteAddress = url.hostname; socket.remotePort = url.port; @@ -594,8 +620,8 @@ export class IncomingMessage extends Readable { rawHeaders; _consuming = false; _dumped = false; - #bodyStream = null; - #fakeSocket = undefined; + #bodyStream: ReadableStreamDefaultReader | undefined; + #fakeSocket: FakeSocket | undefined; #noBody = false; #aborted = false; #req; @@ -625,14 +651,19 @@ export class IncomingMessage extends Readable { callback(); } - #closeBodyStream() { - debug("closeBodyStream()"); - var bodyStream = this.#bodyStream; - if (bodyStream == null) return; - this.complete = true; - this.#bodyStream = undefined; - this.push(null); - // process.nextTick(destroyBodyStreamNT, bodyStream); + async #consumeStream(reader: ReadableStreamDefaultReader) { + while (true) { + var { done, value } = await reader.readMany(); + if (this.#aborted) return; + if (done) { + this.push(null); + this.destroy(); + break; + } + for (var v of value) { + this.push(v); + } + } } _read(size) { @@ -640,37 +671,13 @@ export class IncomingMessage extends Readable { this.push(null); this.complete = true; } else if (this.#bodyStream == null) { - const contentLength = this.#req.headers.get("content-length"); - let remaining = contentLength ? parseInt(contentLength, 10) : 0; - this.#bodyStream = Readable.fromWeb(this.#req.body, { - highWaterMark: Number.isFinite(remaining) ? Math.min(remaining, 16384) : 16384, - }); - - const isBodySizeKnown = remaining > 0 && Number.isSafeInteger(remaining); - - if (isBodySizeKnown) { - this.#bodyStream.on("data", chunk => { - debug("body size known", remaining); - this.push(chunk); - // when we are streaming a known body size, automatically close the stream when we have read enough - remaining -= chunk?.byteLength ?? 0; - if (remaining <= 0) { - this.#closeBodyStream(); - } - }); - } else { - this.#bodyStream.on("data", chunk => { - this.push(chunk); - }); + const reader = this.#req.body?.getReader() as ReadableStreamDefaultReader; + if (!reader) { + this.push(null); + return; } - - // this can be closed by the time we get here if enough data was synchronously available - this.#bodyStream && - this.#bodyStream.on("end", () => { - this.#closeBodyStream(); - }); - } else { - // this.#bodyStream.read(size); + this.#bodyStream = reader; + this.#consumeStream(reader); } } @@ -678,11 +685,15 @@ export class IncomingMessage extends Readable { return this.#aborted; } - abort() { + #abort() { if (this.#aborted) return; this.#aborted = true; - - this.#closeBodyStream(); + var bodyStream = this.#bodyStream; + if (!bodyStream) return; + bodyStream.cancel(); + this.complete = true; + this.#bodyStream = undefined; + this.push(null); } get connection() { @@ -803,13 +814,17 @@ export class OutgoingMessage extends Writable { headersSent = false; sendDate = true; req; + timeout; #finished = false; [kEndCalled] = false; #fakeSocket; - #timeoutTimer = null; - [kAbortController] = null; + #timeoutTimer?: Timer; + [kAbortController]: AbortController | null = null; + + // Express "compress" package uses this + _implicitHeader() {} // For compat with IncomingRequest get headers() { @@ -902,27 +917,50 @@ export class OutgoingMessage extends Writable { [kClearTimeout]() { if (this.#timeoutTimer) { clearTimeout(this.#timeoutTimer); - this.#timeoutTimer = null; + this.removeAllListeners("timeout"); + this.#timeoutTimer = undefined; } } + #onTimeout() { + this.#timeoutTimer = undefined; + this[kAbortController]?.abort(); + this.emit("timeout"); + } + setTimeout(msecs, callback) { - if (this.#timeoutTimer) return this; - if (callback) { - this.on("timeout", callback); - } + if (this.destroyed) return this; + + this.timeout = msecs = validateMsecs(msecs, "msecs"); + + // Attempt to clear an existing timer in both cases - + // even if it will be rescheduled we don't want to leak an existing timer. + clearTimeout(this.#timeoutTimer!); + + if (msecs === 0) { + if (callback !== undefined) { + validateFunction(callback, "callback"); + this.removeListener("timeout", callback); + } + + this.#timeoutTimer = undefined; + } else { + this.#timeoutTimer = setTimeout(this.#onTimeout.bind(this), msecs).unref(); - this.#timeoutTimer = setTimeout(async () => { - this.#timeoutTimer = null; - this[kAbortController]?.abort(); - this.emit("timeout"); - }, msecs); + if (callback !== undefined) { + validateFunction(callback, "callback"); + this.once("timeout", callback); + } + } return this; } } +let OriginalWriteHeadFn, OriginalImplicitHeadFn; export class ServerResponse extends Writable { + declare _writableState: any; + constructor({ req, reply }) { super(); this.req = req; @@ -935,6 +973,10 @@ export class ServerResponse extends Writable { this.#firstWrite = undefined; this._writableState.decodeStrings = false; this.#deferred = undefined; + + // this is matching node's behaviour + // https://github.com/nodejs/node/blob/cf8c6994e0f764af02da4fa70bc5962142181bf3/lib/_http_server.js#L192 + if (req.method === "HEAD") this._hasBody = false; } req; @@ -950,8 +992,14 @@ export class ServerResponse extends Writable { _defaultKeepAlive = false; _removedConnection = false; _removedContLen = false; - #deferred = undefined; + _hasBody = true; + #deferred: (() => void) | undefined = undefined; #finished = false; + // Express "compress" package uses this + _implicitHeader() { + // @ts-ignore + this.writeHead(this.statusCode); + } _write(chunk, encoding, callback) { if (!this.#firstWrite && !this.headersSent) { @@ -1013,11 +1061,20 @@ export class ServerResponse extends Writable { ); } + #drainHeadersIfObservable() { + if (this._implicitHeader === OriginalImplicitHeadFn && this.writeHead === OriginalWriteHeadFn) { + return; + } + + this._implicitHeader(); + } + _final(callback) { if (!this.headersSent) { var data = this.#firstWrite || ""; this.#firstWrite = undefined; this.#finished = true; + this.#drainHeadersIfObservable(); this._reply( new Response(data, { headers: this.#headers, @@ -1136,9 +1193,12 @@ export class ServerResponse extends Writable { } } +OriginalWriteHeadFn = ServerResponse.prototype.writeHead; +OriginalImplicitHeadFn = ServerResponse.prototype._implicitHeader; + export class ClientRequest extends OutgoingMessage { #timeout; - #res = null; + #res: IncomingMessage | null = null; #upgradeOrConnect = false; #parser = null; #maxHeadersCount = null; @@ -1150,15 +1210,15 @@ export class ClientRequest extends OutgoingMessage { #useDefaultPort; #joinDuplicateHeaders; #maxHeaderSize; - #agent = _globalAgent; + #agent = globalAgent; #path; #socketPath; - #body = null; + #body: string | null = null; #fetchRequest; - #signal = null; - [kAbortController] = null; - #timeoutTimer = null; + #signal: AbortSignal | null = null; + [kAbortController]: AbortController | null = null; + #timeoutTimer?: Timer = undefined; #options; #finished; @@ -1227,6 +1287,9 @@ export class ClientRequest extends OutgoingMessage { redirect: "manual", verbose: Boolean(__DEBUG__), signal: this[kAbortController].signal, + + // Timeouts are handled via this.setTimeout. + timeout: false, }, ) .then(response => { @@ -1258,7 +1321,7 @@ export class ClientRequest extends OutgoingMessage { abort() { if (this.aborted) return; - this[kAbortController].abort(); + this[kAbortController]!.abort(); // TODO: Close stream if body streaming } @@ -1298,8 +1361,8 @@ export class ClientRequest extends OutgoingMessage { } else { protocol = defaultAgent.protocol || "http:"; } - this.#protocol = protocol; } + this.#protocol = protocol; switch (this.#agent?.protocol) { case undefined: { @@ -1351,8 +1414,6 @@ export class ClientRequest extends OutgoingMessage { this.#socketPath = options.socketPath; - if (options.timeout !== undefined) this.setTimeout(options.timeout, null); - const signal = options.signal; if (signal) { //We still want to control abort function and timeout so signal call our AbortController @@ -1430,7 +1491,12 @@ export class ClientRequest extends OutgoingMessage { this.#reusedSocket = false; this.#host = host; this.#protocol = protocol; - this.#timeoutTimer = null; + + var timeout = options.timeout; + if (timeout !== undefined && timeout !== 0) { + this.setTimeout(timeout, undefined); + } + const headersArray = ArrayIsArray(headers); if (!headersArray) { var headers = options.headers; @@ -1485,17 +1551,8 @@ export class ClientRequest extends OutgoingMessage { // this[kUniqueHeaders] = parseUniqueHeadersOption(options.uniqueHeaders); - var optsWithoutSignal = options; - if (optsWithoutSignal.signal) { - optsWithoutSignal = ObjectAssign({}, options); - delete optsWithoutSignal.signal; - } + var { signal: _signal, ...optsWithoutSignal } = options; this.#options = optsWithoutSignal; - - var timeout = options.timeout; - if (timeout) { - this.setTimeout(timeout); - } } setSocketKeepAlive(enable = true, initialDelay = 0) { @@ -1508,21 +1565,41 @@ export class ClientRequest extends OutgoingMessage { [kClearTimeout]() { if (this.#timeoutTimer) { clearTimeout(this.#timeoutTimer); - this.#timeoutTimer = null; + this.#timeoutTimer = undefined; + this.removeAllListeners("timeout"); } } + #onTimeout() { + this.#timeoutTimer = undefined; + this[kAbortController]?.abort(); + this.emit("timeout"); + } + setTimeout(msecs, callback) { - if (this.#timeoutTimer) return this; - if (callback) { - this.on("timeout", callback); - } + if (this.destroyed) return this; - this.#timeoutTimer = setTimeout(async () => { - this.#timeoutTimer = null; - this[kAbortController]?.abort(); - this.emit("timeout"); - }, msecs); + this.timeout = msecs = validateMsecs(msecs, "msecs"); + + // Attempt to clear an existing timer in both cases - + // even if it will be rescheduled we don't want to leak an existing timer. + clearTimeout(this.#timeoutTimer!); + + if (msecs === 0) { + if (callback !== undefined) { + validateFunction(callback, "callback"); + this.removeListener("timeout", callback); + } + + this.#timeoutTimer = undefined; + } else { + this.#timeoutTimer = setTimeout(this.#onTimeout.bind(this), msecs).unref(); + + if (callback !== undefined) { + validateFunction(callback, "callback"); + this.once("timeout", callback); + } + } return this; } @@ -1702,7 +1779,7 @@ function _normalizeArgs(args) { } const arg0 = args[0]; - let options = {}; + let options: any = {}; if (typeof arg0 === "object" && arg0 !== null) { // (options[...][, cb]) options = arg0; @@ -1763,6 +1840,20 @@ function _writeHead(statusCode, reason, obj, response) { } } } + + if (statusCode === 204 || statusCode === 304 || (statusCode >= 100 && statusCode <= 199)) { + // RFC 2616, 10.2.5: + // The 204 response MUST NOT include a message-body, and thus is always + // terminated by the first empty line after the header fields. + // RFC 2616, 10.3.5: + // The 304 response MUST NOT contain a message-body, and thus is always + // terminated by the first empty line after the header fields. + // RFC 2616, 10.1 Informational 1xx: + // This class of status code indicates a provisional response, + // consisting only of the Status-Line and optional headers, and is + // terminated by an empty line. + response._hasBody = false; + } } /** @@ -1789,6 +1880,7 @@ export function get(url, options, cb) { return req; } +export var globalAgent = new Agent(); var defaultObject = { Agent, Server, @@ -1800,15 +1892,14 @@ var defaultObject = { request, get, maxHeaderSize: 16384, - // validateHeaderName, - // validateHeaderValue, + validateHeaderName, + validateHeaderValue, setMaxIdleHTTPParsers(max) { debug(`${NODE_HTTP_WARNING}\n`, "setMaxIdleHTTPParsers() is a no-op"); }, - get globalAgent() { - return (_globalAgent ??= new Agent()); - }, - set globalAgent(agent) {}, + globalAgent, + ClientRequest, + OutgoingMessage, [Symbol.for("CommonJS")]: 0, }; diff --git a/src/js/node/https.js b/src/js/node/https.js deleted file mode 100644 index 8253e2905..000000000 --- a/src/js/node/https.js +++ /dev/null @@ -1,4 +0,0 @@ -// Hardcoded module "node:https" -export * from "node:http"; -const HTTP = import.meta.require("node:http"); -export default HTTP; diff --git a/src/js/node/https.ts b/src/js/node/https.ts new file mode 100644 index 000000000..08eb89a01 --- /dev/null +++ b/src/js/node/https.ts @@ -0,0 +1,65 @@ +// Hardcoded module "node:https" +import * as http from "node:http"; + +var { + Agent, + Server, + METHODS, + STATUS_CODES, + createServer, + ServerResponse, + IncomingMessage, + maxHeaderSize, + validateHeaderName, + validateHeaderValue, + globalAgent, +} = http; + +function request(input, options, cb) { + if (input && typeof input === "object" && !(input instanceof URL)) { + input.protocol ??= "https:"; + } else if (typeof options === "object") { + options.protocol ??= "https:"; + } + + return http.request(input, options, cb); +} + +function get(input, options, cb) { + const req = request(input, options, cb); + req.end(); + return req; +} + +var defaultExport = { + Agent, + Server, + METHODS, + STATUS_CODES, + createServer, + ServerResponse, + IncomingMessage, + request, + get, + maxHeaderSize, + validateHeaderName, + validateHeaderValue, + globalAgent, +}; + +export { + Agent, + Server, + METHODS, + STATUS_CODES, + createServer, + ServerResponse, + IncomingMessage, + request, + get, + maxHeaderSize, + validateHeaderName, + validateHeaderValue, + globalAgent, +}; +export default defaultExport; diff --git a/src/js/node/net.js b/src/js/node/net.js index e767d0096..6c690b349 100644 --- a/src/js/node/net.js +++ b/src/js/node/net.js @@ -19,6 +19,8 @@ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +import { Duplex } from "node:stream"; +import { EventEmitter } from "node:events"; // IPv4 Segment const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; @@ -54,16 +56,15 @@ function isIP(s) { return 0; } -const { Bun, createFIFO, Object } = import.meta.primordials; +const { Bun, createFIFO, Object } = globalThis[Symbol.for("Bun.lazy")]("primordials"); const { connect: bunConnect } = Bun; -const { Duplex } = import.meta.require("node:stream"); -const { EventEmitter } = import.meta.require("node:events"); var { setTimeout } = globalThis; const bunTlsSymbol = Symbol.for("::buntls::"); const bunSocketServerHandlers = Symbol.for("::bunsocket_serverhandlers::"); const bunSocketServerConnections = Symbol.for("::bunnetserverconnections::"); const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::"); +const bunSocketInternal = Symbol.for("::bunnetsocketinternal::"); var SocketClass; const Socket = (function (InternalSocket) { @@ -117,13 +118,18 @@ const Socket = (function (InternalSocket) { const self = socket.data; socket.timeout(self.timeout); socket.ref(); - self.#socket = socket; + self[bunSocketInternal] = socket; self.connecting = false; - self.emit("connect", self); + if (!self.#upgraded) { + // this is not actually emitted on nodejs when socket used on the connection + // this is already emmited on non-TLS socket and on TLS socket is emmited secureConnect on handshake + self.emit("connect", self); + } Socket.#Drain(socket); }, handshake(socket, success, verifyError) { const { data: self } = socket; + self._securePending = false; self.secureConnecting = false; self._secureEstablished = !!success; @@ -164,7 +170,7 @@ const Socket = (function (InternalSocket) { if (self.#closed) return; self.#closed = true; //socket cannot be used after close - self.#socket = null; + self[bunSocketInternal] = null; const queue = self.#readQueue; if (queue.isEmpty()) { if (self.push(null)) return; @@ -289,23 +295,35 @@ const Socket = (function (InternalSocket) { localAddress = "127.0.0.1"; #readQueue = createFIFO(); remotePort; - #socket; + [bunSocketInternal] = null; timeout = 0; #writeCallback; #writeChunk; #pendingRead; isServer = false; + _handle; + _parent; + _parentWrap; + #socket; + #upgraded; constructor(options) { - const { signal, write, read, allowHalfOpen = false, ...opts } = options || {}; + const { socket, signal, write, read, allowHalfOpen = false, ...opts } = options || {}; super({ ...opts, allowHalfOpen, readable: true, writable: true, }); + this._handle = this; + this._parent = this; + this._parentWrap = this; this.#pendingRead = undefined; + this.#upgraded = false; + if (socket instanceof Socket) { + this.#socket = socket; + } signal?.once("abort", () => this.destroy()); this.once("connect", () => this.emit("ready")); } @@ -327,7 +345,7 @@ const Socket = (function (InternalSocket) { socket.data = this; socket.timeout(this.timeout); socket.ref(); - this.#socket = socket; + this[bunSocketInternal] = socket; this.connecting = false; this.emit("connect", this); Socket.#Drain(socket); @@ -335,6 +353,7 @@ const Socket = (function (InternalSocket) { connect(port, host, connectListener) { var path; + var connection = this.#socket; if (typeof port === "string") { path = port; port = undefined; @@ -357,6 +376,7 @@ const Socket = (function (InternalSocket) { port, host, path, + socket, // TODOs localAddress, localPort, @@ -371,7 +391,11 @@ const Socket = (function (InternalSocket) { pauseOnConnect, servername, } = port; + this.servername = servername; + if (socket) { + connection = socket; + } } if (!pauseOnConnect) { @@ -399,41 +423,109 @@ const Socket = (function (InternalSocket) { } else { tls.rejectUnauthorized = rejectUnauthorized; tls.requestCert = true; + if (!connection && tls.socket) { + connection = tls.socket; + } + } + } + if (connection) { + if ( + typeof connection !== "object" || + !(connection instanceof Socket) || + typeof connection[bunTlsSymbol] === "function" + ) { + throw new TypeError("socket must be an instance of net.Socket"); } } - this.authorized = false; this.secureConnecting = true; this._secureEstablished = false; this._securePending = true; if (connectListener) this.on("secureConnect", connectListener); } else if (connectListener) this.on("connect", connectListener); - bunConnect( - path - ? { + // start using existing connection + + if (connection) { + const socket = connection[bunSocketInternal]; + + if (socket) { + this.connecting = true; + this.#upgraded = true; + const result = socket.upgradeTLS({ + data: this, + tls, + socket: Socket.#Handlers, + }); + if (result) { + const [raw, tls] = result; + // replace socket + connection[bunSocketInternal] = raw; + raw.timeout(raw.timeout); + raw.connecting = false; + this[bunSocketInternal] = tls; + } else { + this[bunSocketInternal] = null; + throw new Error("Invalid socket"); + } + } else { + // wait to be connected + connection.once("connect", () => { + const socket = connection[bunSocketInternal]; + if (!socket) return; + + this.connecting = true; + this.#upgraded = true; + const result = socket.upgradeTLS({ data: this, - unix: path, - socket: Socket.#Handlers, tls, - } - : { - data: this, - hostname: host || "localhost", - port: port, socket: Socket.#Handlers, - tls, - }, - ); + }); + + if (result) { + const [raw, tls] = result; + // replace socket + connection[bunSocketInternal] = raw; + raw.timeout(raw.timeout); + raw.connecting = false; + this[bunSocketInternal] = tls; + } else { + this[bunSocketInternal] = null; + throw new Error("Invalid socket"); + } + }); + } + } else if (path) { + // start using unix socket + bunConnect({ + data: this, + unix: path, + socket: Socket.#Handlers, + tls, + }).catch(error => { + this.emit("error", error); + }); + } else { + // default start + bunConnect({ + data: this, + hostname: host || "localhost", + port: port, + socket: Socket.#Handlers, + tls, + }).catch(error => { + this.emit("error", error); + }); + } return this; } _destroy(err, callback) { - this.#socket?.end(); + this[bunSocketInternal]?.end(); callback(err); } _final(callback) { - this.#socket?.end(); + this[bunSocketInternal]?.end(); callback(); } @@ -446,7 +538,7 @@ const Socket = (function (InternalSocket) { } get localPort() { - return this.#socket?.localPort; + return this[bunSocketInternal]?.localPort; } get pending() { @@ -472,11 +564,11 @@ const Socket = (function (InternalSocket) { } ref() { - this.#socket?.ref(); + this[bunSocketInternal]?.ref(); } get remoteAddress() { - return this.#socket?.remoteAddress; + return this[bunSocketInternal]?.remoteAddress; } get remoteFamily() { @@ -484,7 +576,7 @@ const Socket = (function (InternalSocket) { } resetAndDestroy() { - this.#socket?.end(); + this[bunSocketInternal]?.end(); } setKeepAlive(enable = false, initialDelay = 0) { @@ -498,19 +590,19 @@ const Socket = (function (InternalSocket) { } setTimeout(timeout, callback) { - this.#socket?.timeout(timeout); + this[bunSocketInternal]?.timeout(timeout); this.timeout = timeout; if (callback) this.once("timeout", callback); return this; } unref() { - this.#socket?.unref(); + this[bunSocketInternal]?.unref(); } _write(chunk, encoding, callback) { - if (typeof chunk == "string" && encoding !== "utf8") chunk = Buffer.from(chunk, encoding); - var written = this.#socket?.write(chunk); + if (typeof chunk == "string" && encoding !== "ascii") chunk = Buffer.from(chunk, encoding); + var written = this[bunSocketInternal]?.write(chunk); if (written == chunk.length) { callback(); } else if (this.#writeCallback) { diff --git a/src/js/node/os.js b/src/js/node/os.js index 3315708ad..3cd0288bd 100644 --- a/src/js/node/os.js +++ b/src/js/node/os.js @@ -1,4 +1,22 @@ // Hardcoded module "node:os" + +export var tmpdir = function () { + var lazy = Symbol.for("Bun.lazy"); + var primordials = globalThis[lazy]("primordials"); + + var { Bun } = primordials; + var env = Bun.env; + + tmpdir = function () { + var path = env["TMPDIR"] || env["TMP"] || env["TEMP"] || "/tmp"; + const length = path.length; + if (length > 1 && path[length - 1] === "/") path = path.slice(0, -1); + return path; + }; + + return tmpdir(); +}; + function bound(obj) { return { arch: obj.arch.bind(obj), @@ -13,7 +31,9 @@ function bound(obj) { platform: obj.platform.bind(obj), release: obj.release.bind(obj), setPriority: obj.setPriority.bind(obj), - tmpdir: obj.tmpdir.bind(obj), + get tmpdir() { + return tmpdir; + }, totalmem: obj.totalmem.bind(obj), type: obj.type.bind(obj), uptime: obj.uptime.bind(obj), @@ -42,7 +62,6 @@ export var { platform, release, setPriority, - tmpdir, totalmem, type, uptime, diff --git a/src/js/node/path.js b/src/js/node/path.js index bcdd1f7f7..7c20d520b 100644 --- a/src/js/node/path.js +++ b/src/js/node/path.js @@ -1,6 +1,5 @@ // Hardcoded module "node:path" export const createModule = obj => Object.assign(Object.create(null), obj); - function bound(obj) { var result = createModule({ basename: obj.basename.bind(obj), diff --git a/src/js/node/perf_hooks.js b/src/js/node/perf_hooks.js index fb5929c57..592868ab5 100644 --- a/src/js/node/perf_hooks.js +++ b/src/js/node/perf_hooks.js @@ -1,6 +1,20 @@ // Hardcoded module "node:perf_hooks" import { throwNotImplemented } from "../shared"; +export var constants = { + NODE_PERFORMANCE_GC_MAJOR: 4, + NODE_PERFORMANCE_GC_MINOR: 1, + NODE_PERFORMANCE_GC_INCREMENTAL: 8, + NODE_PERFORMANCE_GC_WEAKCB: 16, + NODE_PERFORMANCE_GC_FLAGS_NO: 0, + NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED: 2, + NODE_PERFORMANCE_GC_FLAGS_FORCED: 4, + NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING: 8, + NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE: 16, + NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY: 32, + NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE: 64, +}; + export var performance = globalThis.performance; export class PerformanceObserver { @@ -22,6 +36,7 @@ export class PerformanceNodeTiming { export default { performance, + constants, PerformanceEntry, PerformanceNodeTiming, [Symbol.for("CommonJS")]: 0, diff --git a/src/js/node/readline.js b/src/js/node/readline.js index 0c253e8a0..64e73172a 100644 --- a/src/js/node/readline.js +++ b/src/js/node/readline.js @@ -25,10 +25,10 @@ // ---------------------------------------------------------------------------- // Section: Imports // ---------------------------------------------------------------------------- -var { Array, RegExp, String, Bun } = import.meta.primordials; -var EventEmitter = import.meta.require("node:events"); -var { clearTimeout, setTimeout } = import.meta.require("timers"); -var { StringDecoder } = import.meta.require("string_decoder"); +var { Array, RegExp, String, Bun } = globalThis[Symbol.for("Bun.lazy")]("primordials"); +import { EventEmitter } from "node:events"; +import { clearTimeout, setTimeout } from "timers"; +import { StringDecoder } from "string_decoder"; var isWritable; var { inspect } = Bun; @@ -1573,7 +1573,7 @@ function InterfaceConstructor(input, output, completer, terminal) { } ObjectSetPrototypeOf(InterfaceConstructor.prototype, EventEmitter.prototype); -ObjectSetPrototypeOf(InterfaceConstructor, EventEmitter); +// ObjectSetPrototypeOf(InterfaceConstructor, EventEmitter); var _Interface = class Interface extends InterfaceConstructor { // TODO: Enumerate all the properties of the class diff --git a/src/js/node/readline.promises.js b/src/js/node/readline.promises.js index 94d9b3f96..6890235b4 100644 --- a/src/js/node/readline.promises.js +++ b/src/js/node/readline.promises.js @@ -1,7 +1,7 @@ // Hardcoded module "node:readline/promises" -var { - promises: { Readline, Interface, createInterface }, -} = import.meta.require("node:readline"); +import { promises } from "node:readline"; + +export const { Readline, Interface, createInterface } = promises; export default { Readline, diff --git a/src/js/node/stream.consumers.js b/src/js/node/stream.consumers.js index 39d436eed..a1f85ab94 100644 --- a/src/js/node/stream.consumers.js +++ b/src/js/node/stream.consumers.js @@ -1,5 +1,5 @@ // Hardcoded module "node:stream/consumers" / "readable-stream/consumer" -const { Bun } = import.meta.primordials; +const { Bun } = globalThis[Symbol.for("Bun.lazy")]("primordials"); export const arrayBuffer = Bun.readableStreamToArrayBuffer; export const text = Bun.readableStreamToText; diff --git a/src/js/node/stream.js b/src/js/node/stream.js index 67d82d287..30c76d797 100644 --- a/src/js/node/stream.js +++ b/src/js/node/stream.js @@ -1,13 +1,20 @@ // Hardcoded module "node:stream" / "readable-stream" // "readable-stream" npm package // just transpiled -var { isPromise, isCallable, direct, Object } = import.meta.primordials; -globalThis.__IDS_TO_TRACK = process.env.DEBUG_TRACK_EE?.length - ? process.env.DEBUG_TRACK_EE.split(",") - : process.env.DEBUG_STREAMS?.length - ? process.env.DEBUG_STREAMS.split(",") - : null; +// This must go at the top of the file, before any side effects. +// IS_BUN_DEVELOPMENT is a bundle-only global variable that is set to true when +// building a development bundle. +const __TRACK_EE__ = IS_BUN_DEVELOPMENT && !!process.env.DEBUG_TRACK_EE; +const __DEBUG__ = IS_BUN_DEVELOPMENT && !!(process.env.DEBUG || process.env.DEBUG_STREAMS || __TRACK_EE__); + +if (__DEBUG__) { + globalThis.__IDS_TO_TRACK = process.env.DEBUG_TRACK_EE?.length + ? process.env.DEBUG_TRACK_EE.split(",") + : process.env.DEBUG_STREAMS?.length + ? process.env.DEBUG_STREAMS.split(",") + : null; +} // Separating DEBUG, DEBUG_STREAMS and DEBUG_TRACK_EE env vars makes it easier to focus on the // events in this file rather than all debug output across all files @@ -16,9 +23,6 @@ globalThis.__IDS_TO_TRACK = process.env.DEBUG_TRACK_EE?.length // The events and/or all of the outputs for the given stream IDs assigned at stream construction // By default, child_process gives -const __TRACK_EE__ = !!process.env.DEBUG_TRACK_EE; -const __DEBUG__ = !!(process.env.DEBUG || process.env.DEBUG_STREAMS || __TRACK_EE__); - var debug = __DEBUG__ ? globalThis.__IDS_TO_TRACK ? // If we are tracking IDs for debug event emitters, we should prefix the debug output with the ID @@ -30,6 +34,10 @@ var debug = __DEBUG__ : (...args) => console.log(...args.slice(0, -1)) : () => {}; +var { isPromise, isCallable, direct, Object } = globalThis[Symbol.for("Bun.lazy")]("primordials"); +import { EventEmitter as EE } from "bun:events_native"; +import { StringDecoder } from "node:string_decoder"; + var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; @@ -37,48 +45,6 @@ var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __ObjectSetPrototypeOf = Object.setPrototypeOf; -var __require = x => import.meta.require(x); - -var _EE = __require("bun:events_native"); - -function DebugEventEmitter(opts) { - if (!(this instanceof DebugEventEmitter)) return new DebugEventEmitter(opts); - _EE.call(this, opts); - const __id = opts.__id; - if (__id) { - __defProp(this, "__id", { - value: __id, - readable: true, - writable: false, - enumerable: false, - }); - } -} - -__ObjectSetPrototypeOf(DebugEventEmitter.prototype, _EE.prototype); -__ObjectSetPrototypeOf(DebugEventEmitter, _EE); - -DebugEventEmitter.prototype.emit = function (event, ...args) { - var __id = this.__id; - if (__id) { - debug("emit", event, ...args, __id); - } else { - debug("emit", event, ...args); - } - return _EE.prototype.emit.call(this, event, ...args); -}; -DebugEventEmitter.prototype.on = function (event, handler) { - var __id = this.__id; - if (__id) { - debug("on", event, "added", __id); - } else { - debug("on", event, "added"); - } - return _EE.prototype.on.call(this, event, handler); -}; -DebugEventEmitter.prototype.addListener = function (event, handler) { - return this.on(event, handler); -}; var __commonJS = (cb, mod) => function __require2() { @@ -260,9 +226,8 @@ var require_primordials = __commonJS({ var require_util = __commonJS({ "node_modules/readable-stream/lib/ours/util.js"(exports, module) { "use strict"; - var bufferModule = __require("buffer"); + var AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; - var Blob = globalThis.Blob || bufferModule.Blob; var isBlob = typeof Blob !== "undefined" ? function isBlob2(b) { @@ -1388,7 +1353,6 @@ var require_end_of_stream = __commonJS({ var require_operators = __commonJS({ "node_modules/readable-stream/lib/internal/streams/operators.js"(exports, module) { "use strict"; - var AbortController = globalThis.AbortController || __require("abort-controller").AbortController; var { codes: { ERR_INVALID_ARG_TYPE, ERR_MISSING_ARGS, ERR_OUT_OF_RANGE }, AbortError, @@ -2084,13 +2048,6 @@ var require_legacy = __commonJS({ "node_modules/readable-stream/lib/internal/streams/legacy.js"(exports, module) { "use strict"; var { ArrayIsArray, ObjectSetPrototypeOf } = require_primordials(); - var { EventEmitter: _EE } = __require("bun:events_native"); - var EE; - if (__TRACK_EE__) { - EE = DebugEventEmitter; - } else { - EE = _EE; - } function Stream(options) { if (!(this instanceof Stream)) return new Stream(options); @@ -2332,6 +2289,7 @@ var require_from = __commonJS({ }); var _ReadableFromWeb; +var _ReadableFromWebForUndici; // node_modules/readable-stream/lib/internal/streams/readable.js var require_readable = __commonJS({ @@ -2352,7 +2310,6 @@ var require_readable = __commonJS({ } = require_primordials(); var ReadableState = globalThis[Symbol.for("Bun.lazy")]("bun:stream").ReadableState; - var { EventEmitter: EE } = __require("bun:events_native"); var { Stream, prependListener } = require_legacy(); function Readable(options) { @@ -2537,6 +2494,8 @@ var require_readable = __commonJS({ } } + _ReadableFromWebForUndici = ReadableFromWeb; + /** * @param {ReadableStream} readableStream * @param {{ @@ -2596,7 +2555,7 @@ var require_readable = __commonJS({ } module.exports = Readable; - _ReadableFromWeb = ReadableFromWeb; + _ReadableFromWeb = newStreamReadableFromReadableStream; var { addAbortSignal } = require_add_abort_signal(); var eos = require_end_of_stream(); @@ -2626,7 +2585,6 @@ var require_readable = __commonJS({ }, } = require_errors(); var { validateObject } = require_validators(); - var { StringDecoder } = __require("string_decoder"); var from = require_from(); var nop = () => {}; var { errorOrDestroy } = destroyImpl; @@ -3422,7 +3380,6 @@ var require_writable = __commonJS({ SymbolHasInstance, } = require_primordials(); - var { EventEmitter: EE } = __require("bun:events_native"); var Stream = require_legacy().Stream; var destroyImpl = require_destroy(); var { addAbortSignal } = require_add_abort_signal(); @@ -4048,7 +4005,6 @@ var require_writable = __commonJS({ var require_duplexify = __commonJS({ "node_modules/readable-stream/lib/internal/streams/duplexify.js"(exports, module) { "use strict"; - var bufferModule = __require("buffer"); var { isReadable, isWritable, @@ -4068,7 +4024,6 @@ var require_duplexify = __commonJS({ var Readable = require_readable(); var { createDeferredPromise } = require_util(); var from = require_from(); - var Blob = globalThis.Blob || bufferModule.Blob; var isBlob = typeof Blob !== "undefined" ? function isBlob2(b) { @@ -4077,7 +4032,6 @@ var require_duplexify = __commonJS({ : function isBlob2(b) { return false; }; - var AbortController = globalThis.AbortController || __require("abort-controller").AbortController; var { FunctionPrototypeCall } = require_primordials(); class Duplexify extends Duplex { constructor(options) { @@ -4619,7 +4573,6 @@ var require_pipeline = __commonJS({ } = require_errors(); var { validateFunction, validateAbortSignal } = require_validators(); var { isIterable, isReadable, isReadableNodeStream, isNodeStream } = require_utils(); - var AbortController = globalThis.AbortController || __require("abort-controller").AbortController; var PassThrough; var Readable; function destroyer(stream, reading, writing) { @@ -5304,7 +5257,7 @@ function createNativeStreamReadable(nativeType, Readable) { const finalizer = new FinalizationRegistry(ptr => ptr && deinit(ptr)); const MIN_BUFFER_SIZE = 512; var NativeReadable = class NativeReadable extends Readable { - #ptr; + #bunNativePtr; #refCount = 1; #constructed = false; #remainingChunk = undefined; @@ -5319,12 +5272,12 @@ function createNativeStreamReadable(nativeType, Readable) { } else { this.#highWaterMark = 256 * 1024; } - this.#ptr = ptr; + this.#bunNativePtr = ptr; this.#constructed = false; this.#remainingChunk = undefined; this.#pendingRead = false; this.#unregisterToken = {}; - finalizer.register(this, this.#ptr, this.#unregisterToken); + finalizer.register(this, this.#bunNativePtr, this.#unregisterToken); } // maxToRead is by default the highWaterMark passed from the Readable.read call to this fn @@ -5337,7 +5290,7 @@ function createNativeStreamReadable(nativeType, Readable) { return; } - var ptr = this.#ptr; + var ptr = this.#bunNativePtr; __DEBUG__ && debug("ptr @ NativeReadable._read", ptr, this.__id); if (ptr === 0) { this.push(null); @@ -5403,10 +5356,10 @@ function createNativeStreamReadable(nativeType, Readable) { return chunk; } - push(result, encoding) { - __DEBUG__ && debug("NativeReadable push -- result, encoding", result, encoding, this.__id); - return super.push(...arguments); - } + // push(result, encoding) { + // __DEBUG__ && debug("NativeReadable push -- result, encoding", result, encoding, this.__id); + // return super.push(...arguments); + // } #handleResult(result, view, isClosed) { __DEBUG__ && debug("result, isClosed @ #handleResult", result, isClosed, this.__id); @@ -5419,7 +5372,9 @@ function createNativeStreamReadable(nativeType, Readable) { return handleNumberResult(this, result, view, isClosed); } else if (typeof result === "boolean") { - this.push(null); + process.nextTick(() => { + this.push(null); + }); return view?.byteLength ?? 0 > 0 ? view : undefined; } else if (ArrayBuffer.isView(result)) { if (result.byteLength >= this.#highWaterMark && !this.#hasResized && !isClosed) { @@ -5458,14 +5413,14 @@ function createNativeStreamReadable(nativeType, Readable) { } _destroy(error, callback) { - var ptr = this.#ptr; + var ptr = this.#bunNativePtr; if (ptr === 0) { callback(error); return; } finalizer.unregister(this.#unregisterToken); - this.#ptr = 0; + this.#bunNativePtr = 0; if (updateRef) { updateRef(ptr, false); } @@ -5475,7 +5430,7 @@ function createNativeStreamReadable(nativeType, Readable) { } ref() { - var ptr = this.#ptr; + var ptr = this.#bunNativePtr; if (ptr === 0) return; if (this.#refCount++ === 0) { updateRef(ptr, true); @@ -5483,7 +5438,7 @@ function createNativeStreamReadable(nativeType, Readable) { } unref() { - var ptr = this.#ptr; + var ptr = this.#bunNativePtr; if (ptr === 0) return; if (this.#refCount-- === 1) { updateRef(ptr, false); @@ -5632,7 +5587,7 @@ var NativeWritable = class NativeWritable extends Writable { const stream_exports = require_ours(); stream_exports[Symbol.for("CommonJS")] = 0; -stream_exports[Symbol.for("::bunternal::")] = { _ReadableFromWeb }; +stream_exports[Symbol.for("::bunternal::")] = { _ReadableFromWeb, _ReadableFromWebForUndici }; export default stream_exports; export var _uint8ArrayToBuffer = stream_exports._uint8ArrayToBuffer; export var _isUint8Array = stream_exports._isUint8Array; @@ -5654,4 +5609,4 @@ export var Stream = stream_exports.Stream; export var eos = (stream_exports["eos"] = require_end_of_stream); export var _getNativeReadableStreamPrototype = stream_exports._getNativeReadableStreamPrototype; export var NativeWritable = stream_exports.NativeWritable; -export var promises = Stream.promise; +export var promises = Stream.promises; diff --git a/src/js/node/stream.promises.js b/src/js/node/stream.promises.js index d00c424a6..323785a4c 100644 --- a/src/js/node/stream.promises.js +++ b/src/js/node/stream.promises.js @@ -1,5 +1,5 @@ -// Hardcoded module "node:stream" -var { promises } = import.meta.require("node:stream"); +// Hardcoded module "node:stream/promises" +import { promises } from "node:stream"; export var { pipeline, finished } = promises; diff --git a/src/js/node/timers.promises.js b/src/js/node/timers.promises.js index 2bb7bce49..3e2e7bcd5 100644 --- a/src/js/node/timers.promises.js +++ b/src/js/node/timers.promises.js @@ -233,3 +233,4 @@ function setIntervalPromise(after = 1, value, options = {}) { } export { setTimeoutPromise as setTimeout, setImmediatePromise as setImmediate, setIntervalPromise as setInterval }; +export default { setTimeout: setTimeoutPromise, setImmediate: setImmediatePromise, setInterval: setIntervalPromise }; diff --git a/src/js/node/tls.js b/src/js/node/tls.js index b3b089daf..310a36620 100644 --- a/src/js/node/tls.js +++ b/src/js/node/tls.js @@ -1,7 +1,30 @@ // Hardcoded module "node:tls" -import { isTypedArray } from "util/types"; - +import { isArrayBufferView, isTypedArray } from "util/types"; +import net, { Server as NetServer } from "node:net"; +const InternalTCPSocket = net[Symbol.for("::bunternal::")]; +const bunSocketInternal = Symbol.for("::bunnetsocketinternal::"); + +const { RegExp, Array, String } = globalThis[Symbol.for("Bun.lazy")]("primordials"); +const SymbolReplace = Symbol.replace; +const RegExpPrototypeSymbolReplace = RegExp.prototype[SymbolReplace]; +const RegExpPrototypeExec = RegExp.prototype.exec; + +const StringPrototypeStartsWith = String.prototype.startsWith; +const StringPrototypeSlice = String.prototype.slice; +const StringPrototypeIncludes = String.prototype.includes; +const StringPrototypeSplit = String.prototype.split; +const StringPrototypeIndexOf = String.prototype.indexOf; +const StringPrototypeSubstring = String.prototype.substring; +const StringPrototypeEndsWith = String.prototype.endsWith; + +const ArrayPrototypeIncludes = Array.prototype.includes; +const ArrayPrototypeJoin = Array.prototype.join; +const ArrayPrototypeForEach = Array.prototype.forEach; +const ArrayPrototypePush = Array.prototype.push; +const ArrayPrototypeSome = Array.prototype.some; +const ArrayPrototypeReduce = Array.prototype.reduce; function parseCertString() { + // Removed since JAN 2022 Node v18.0.0+ https://github.com/nodejs/node/pull/41479 throwNotImplemented("Not implemented"); } @@ -16,6 +39,164 @@ function isValidTLSArray(obj) { } } +function unfqdn(host) { + return RegExpPrototypeSymbolReplace(/[.]$/, host, ""); +} + +function splitHost(host) { + return StringPrototypeSplit.call(RegExpPrototypeSymbolReplace(/[A-Z]/g, unfqdn(host), toLowerCase), "."); +} + +function check(hostParts, pattern, wildcards) { + // Empty strings, null, undefined, etc. never match. + if (!pattern) return false; + + const patternParts = splitHost(pattern); + + if (hostParts.length !== patternParts.length) return false; + + // Pattern has empty components, e.g. "bad..example.com". + if (ArrayPrototypeIncludes.call(patternParts, "")) return false; + + // RFC 6125 allows IDNA U-labels (Unicode) in names but we have no + // good way to detect their encoding or normalize them so we simply + // reject them. Control characters and blanks are rejected as well + // because nothing good can come from accepting them. + const isBad = s => RegExpPrototypeExec.call(/[^\u0021-\u007F]/u, s) !== null; + if (ArrayPrototypeSome.call(patternParts, isBad)) return false; + + // Check host parts from right to left first. + for (let i = hostParts.length - 1; i > 0; i -= 1) { + if (hostParts[i] !== patternParts[i]) return false; + } + + const hostSubdomain = hostParts[0]; + const patternSubdomain = patternParts[0]; + const patternSubdomainParts = StringPrototypeSplit.call(patternSubdomain, "*"); + + // Short-circuit when the subdomain does not contain a wildcard. + // RFC 6125 does not allow wildcard substitution for components + // containing IDNA A-labels (Punycode) so match those verbatim. + if (patternSubdomainParts.length === 1 || StringPrototypeIncludes.call(patternSubdomain, "xn--")) + return hostSubdomain === patternSubdomain; + + if (!wildcards) return false; + + // More than one wildcard is always wrong. + if (patternSubdomainParts.length > 2) return false; + + // *.tld wildcards are not allowed. + if (patternParts.length <= 2) return false; + + const { 0: prefix, 1: suffix } = patternSubdomainParts; + + if (prefix.length + suffix.length > hostSubdomain.length) return false; + + if (!StringPrototypeStartsWith.call(hostSubdomain, prefix)) return false; + + if (!StringPrototypeEndsWith.call(hostSubdomain, suffix)) return false; + + return true; +} + +// This pattern is used to determine the length of escaped sequences within +// the subject alt names string. It allows any valid JSON string literal. +// This MUST match the JSON specification (ECMA-404 / RFC8259) exactly. +const jsonStringPattern = + // eslint-disable-next-line no-control-regex + /^"(?:[^"\\\u0000-\u001f]|\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4}))*"/; + +function splitEscapedAltNames(altNames) { + const result = []; + let currentToken = ""; + let offset = 0; + while (offset !== altNames.length) { + const nextSep = StringPrototypeIndexOf.call(altNames, ", ", offset); + const nextQuote = StringPrototypeIndexOf.call(altNames, '"', offset); + if (nextQuote !== -1 && (nextSep === -1 || nextQuote < nextSep)) { + // There is a quote character and there is no separator before the quote. + currentToken += StringPrototypeSubstring.call(altNames, offset, nextQuote); + const match = RegExpPrototypeExec.call(jsonStringPattern, StringPrototypeSubstring.call(altNames, nextQuote)); + if (!match) { + let error = new SyntaxError("ERR_TLS_CERT_ALTNAME_FORMAT: Invalid subject alternative name string"); + error.name = ERR_TLS_CERT_ALTNAME_FORMAT; + throw error; + } + currentToken += JSON.parse(match[0]); + offset = nextQuote + match[0].length; + } else if (nextSep !== -1) { + // There is a separator and no quote before it. + currentToken += StringPrototypeSubstring.call(altNames, offset, nextSep); + ArrayPrototypePush.call(result, currentToken); + currentToken = ""; + offset = nextSep + 2; + } else { + currentToken += StringPrototypeSubstring.call(altNames, offset); + offset = altNames.length; + } + } + ArrayPrototypePush.call(result, currentToken); + return result; +} +function checkServerIdentity(hostname, cert) { + const subject = cert.subject; + const altNames = cert.subjectaltname; + const dnsNames = []; + const ips = []; + + hostname = "" + hostname; + + if (altNames) { + const splitAltNames = StringPrototypeIncludes.call(altNames, '"') + ? splitEscapedAltNames(altNames) + : StringPrototypeSplit.call(altNames, ", "); + ArrayPrototypeForEach.call(splitAltNames, name => { + if (StringPrototypeStartsWith.call(name, "DNS:")) { + ArrayPrototypePush.call(dnsNames, StringPrototypeSlice.call(name, 4)); + } else if (StringPrototypeStartsWith.call(name, "IP Address:")) { + ArrayPrototypePush.call(ips, canonicalizeIP(StringPrototypeSlice.call(name, 11))); + } + }); + } + + let valid = false; + let reason = "Unknown reason"; + + hostname = unfqdn(hostname); // Remove trailing dot for error messages. + + if (net.isIP(hostname)) { + valid = ArrayPrototypeIncludes.call(ips, canonicalizeIP(hostname)); + if (!valid) reason = `IP: ${hostname} is not in the cert's list: ` + ArrayPrototypeJoin.call(ips, ", "); + } else if (dnsNames.length > 0 || subject?.CN) { + const hostParts = splitHost(hostname); + const wildcard = pattern => check(hostParts, pattern, true); + + if (dnsNames.length > 0) { + valid = ArrayPrototypeSome.call(dnsNames, wildcard); + if (!valid) reason = `Host: ${hostname}. is not in the cert's altnames: ${altNames}`; + } else { + // Match against Common Name only if no supported identifiers exist. + const cn = subject.CN; + + if (ArrayIsArray(cn)) valid = ArrayPrototypeSome.call(cn, wildcard); + else if (cn) valid = wildcard(cn); + + if (!valid) reason = `Host: ${hostname}. is not cert's CN: ${cn}`; + } + } else { + reason = "Cert does not contain a DNS name"; + } + + if (!valid) { + let error = new Error(`ERR_TLS_CERT_ALTNAME_INVALID: Hostname/IP does not match certificate's altnames: ${reason}`); + error.name = "ERR_TLS_CERT_ALTNAME_INVALID"; + error.reason = reason; + error.host = host; + error.cert = cert; + return error; + } +} + var InternalSecureContext = class SecureContext { context; @@ -81,7 +262,35 @@ function createSecureContext(options) { return new SecureContext(options); } -const { [Symbol.for("::bunternal::")]: InternalTCPSocket, Server: NetServer } = import.meta.require("net"); +// Translate some fields from the handle's C-friendly format into more idiomatic +// javascript object representations before passing them back to the user. Can +// be used on any cert object, but changing the name would be semver-major. +function translatePeerCertificate(c) { + if (!c) return null; + + if (c.issuerCertificate != null && c.issuerCertificate !== c) { + c.issuerCertificate = translatePeerCertificate(c.issuerCertificate); + } + if (c.infoAccess != null) { + const info = c.infoAccess; + c.infoAccess = { __proto__: null }; + + // XXX: More key validation? + RegExpPrototypeSymbolReplace(/([^\n:]*):([^\n]*)(?:\n|$)/g, info, (all, key, val) => { + if (val.charCodeAt(0) === 0x22) { + // The translatePeerCertificate function is only + // used on internally created legacy certificate + // objects, and any value that contains a quote + // will always be a valid JSON string literal, + // so this should never throw. + val = JSONParse(val); + } + if (key in c.infoAccess) ArrayPrototypePush.call(c.infoAccess[key], val); + else c.infoAccess[key] = [val]; + }); + } + return c; +} const buntls = Symbol.for("::buntls::"); @@ -107,8 +316,22 @@ const TLSSocket = (function (InternalTLSSocket) { })( class TLSSocket extends InternalTCPSocket { #secureContext; - constructor(options) { - super(options); + ALPNProtocols; + #socket; + + constructor(socket, options) { + super(socket instanceof InternalTCPSocket ? options : options || socket); + options = options || socket || {}; + if (typeof options === "object") { + const { ALPNProtocols } = options; + if (ALPNProtocols) { + convertALPNProtocols(ALPNProtocols, this); + } + if (socket instanceof InternalTCPSocket) { + this.#socket = socket; + } + } + this.#secureContext = options.secureContext || createSecureContext(options); this.authorized = false; this.secureConnecting = true; @@ -123,28 +346,52 @@ const TLSSocket = (function (InternalTLSSocket) { secureConnecting = false; _SNICallback; servername; - alpnProtocol; authorized = false; authorizationError; encrypted = true; - exportKeyingMaterial() { - throw Error("Not implented in Bun yet"); + _start() { + // some frameworks uses this _start internal implementation is suposed to start TLS handshake + // on Bun we auto start this after on_open callback and when wrapping we start it after the socket is attached to the net.Socket/tls.Socket } - setMaxSendFragment() { + + exportKeyingMaterial(length, label, context) { + //SSL_export_keying_material throw Error("Not implented in Bun yet"); } - setServername() { + setMaxSendFragment(size) { + // SSL_set_max_send_fragment throw Error("Not implented in Bun yet"); } + setServername(name) { + if (this.isServer) { + let error = new Error("ERR_TLS_SNI_FROM_SERVER: Cannot issue SNI from a TLS server-side socket"); + error.name = "ERR_TLS_SNI_FROM_SERVER"; + throw error; + } + // if the socket is detached we can't set the servername but we set this property so when open will auto set to it + this.servername = name; + this[bunSocketInternal]?.setServername(name); + } setSession() { throw Error("Not implented in Bun yet"); } getPeerCertificate() { + // need to implement peerCertificate on socket.zig + // const cert = this[bunSocketInternal]?.peerCertificate; + // if(cert) { + // return translatePeerCertificate(cert); + // } throw Error("Not implented in Bun yet"); } getCertificate() { + // need to implement certificate on socket.zig + // const cert = this[bunSocketInternal]?.certificate; + // if(cert) { + // It's not a peer cert, but the formatting is identical. + // return translatePeerCertificate(cert); + // } throw Error("Not implented in Bun yet"); } getPeerX509Certificate() { @@ -154,16 +401,17 @@ const TLSSocket = (function (InternalTLSSocket) { throw Error("Not implented in Bun yet"); } - [buntls](port, host) { - var { servername } = this; - if (servername) { - return { - serverName: typeof servername === "string" ? servername : host, - ...this.#secureContext, - }; - } + get alpnProtocol() { + return this[bunSocketInternal]?.alpnProtocol; + } - return true; + [buntls](port, host) { + return { + socket: this.#socket, + ALPNProtocols: this.ALPNProtocols, + serverName: this.servername || host || "localhost", + ...this.#secureContext, + }; } }, ); @@ -177,9 +425,12 @@ class Server extends NetServer { _rejectUnauthorized; _requestCert; servername; + ALPNProtocols; + #checkServerIdentity; constructor(options, secureConnectionListener) { super(options, secureConnectionListener); + this.#checkServerIdentity = options?.checkServerIdentity || checkServerIdentity; this.setSecureContext(options); } emit(event, args) { @@ -197,6 +448,12 @@ class Server extends NetServer { options = options.context; } if (options) { + const { ALPNProtocols } = options; + + if (ALPNProtocols) { + convertALPNProtocols(ALPNProtocols, this); + } + let key = options.key; if (key) { if (!isValidTLSArray(key)) { @@ -277,6 +534,8 @@ class Server extends NetServer { // Client always is NONE on set_verify rejectUnauthorized: isClient ? false : this._rejectUnauthorized, requestCert: isClient ? false : this._requestCert, + ALPNProtocols: this.ALPNProtocols, + checkServerIdentity: this.#checkServerIdentity, }, SocketClass, ]; @@ -286,7 +545,7 @@ class Server extends NetServer { function createServer(options, connectionListener) { return new Server(options, connectionListener); } -export const CLIENT_RENEG_LIMIT = 3, +const CLIENT_RENEG_LIMIT = 3, CLIENT_RENEG_WINDOW = 600, DEFAULT_ECDH_CURVE = "auto", // https://github.com/Jarred-Sumner/uSockets/blob/fafc241e8664243fc0c51d69684d5d02b9805134/src/crypto/openssl.c#L519-L523 @@ -296,6 +555,11 @@ export const CLIENT_RENEG_LIMIT = 3, DEFAULT_MAX_VERSION = "TLSv1.3", createConnection = (port, host, connectListener) => { if (typeof port === "object") { + port.checkServerIdentity || checkServerIdentity; + const { ALPNProtocols } = port; + if (ALPNProtocols) { + convertALPNProtocols(ALPNProtocols, port); + } // port is option pass Socket options and let connect handle connection options return new TLSSocket(port).connect(port, host, connectListener); } @@ -304,34 +568,103 @@ export const CLIENT_RENEG_LIMIT = 3, }, connect = createConnection; -var exports = { - createSecureContext, - parseCertString, +function getCiphers() { + return DEFAULT_CIPHERS.split(":"); +} - getCiphers() { - return DEFAULT_CIPHERS.split(":"); - }, +function getCurves() { + return; +} - getCurves() { - return; - }, +// Convert protocols array into valid OpenSSL protocols list +// ("\x06spdy/2\x08http/1.1\x08http/1.0") +function convertProtocols(protocols) { + const lens = new Array(protocols.length); + const buff = Buffer.allocUnsafe( + ArrayPrototypeReduce.call( + protocols, + (p, c, i) => { + const len = Buffer.byteLength(c); + if (len > 255) { + throw new RangeError( + "The byte length of the protocol at index " + `${i} exceeds the maximum length.`, + "<= 255", + len, + true, + ); + } + lens[i] = len; + return p + 1 + len; + }, + 0, + ), + ); - convertALPNProtocols(protocols, out) {}, - TLSSocket, - SecureContext, + let offset = 0; + for (let i = 0, c = protocols.length; i < c; i++) { + buff[offset++] = lens[i]; + buff.write(protocols[i], offset); + offset += lens[i]; + } + + return buff; +} + +function convertALPNProtocols(protocols, out) { + // If protocols is Array - translate it into buffer + if (Array.isArray(protocols)) { + out.ALPNProtocols = convertProtocols(protocols); + } else if (isTypedArray(protocols)) { + // Copy new buffer not to be modified by user. + out.ALPNProtocols = Buffer.from(protocols); + } else if (isArrayBufferView(protocols)) { + out.ALPNProtocols = Buffer.from( + protocols.buffer.slice(protocols.byteOffset, protocols.byteOffset + protocols.byteLength), + ); + } else if (Buffer.isBuffer(protocols)) { + out.ALPNProtocols = protocols; + } +} + +var exports = { + [Symbol.for("CommonJS")]: 0, CLIENT_RENEG_LIMIT, CLIENT_RENEG_WINDOW, - DEFAULT_ECDH_CURVE, + connect, + convertALPNProtocols, + createConnection, + createSecureContext, + createServer, DEFAULT_CIPHERS, - DEFAULT_MIN_VERSION, + DEFAULT_ECDH_CURVE, DEFAULT_MAX_VERSION, - [Symbol.for("CommonJS")]: 0, + DEFAULT_MIN_VERSION, + getCiphers, + getCurves, + parseCertString, + SecureContext, + Server, + TLSSocket, +}; + +export { + CLIENT_RENEG_LIMIT, + CLIENT_RENEG_WINDOW, connect, + convertALPNProtocols, createConnection, - Server, + createSecureContext, createServer, + DEFAULT_CIPHERS, + DEFAULT_ECDH_CURVE, + DEFAULT_MAX_VERSION, + DEFAULT_MIN_VERSION, + getCiphers, + getCurves, + parseCertString, + SecureContext, + checkServerIdentity, + Server, + TLSSocket, + exports as default, }; - -export default exports; - -export { createSecureContext, parseCertString, TLSSocket, SecureContext }; diff --git a/src/js/node/trace_events.ts b/src/js/node/trace_events.ts index 789c41222..7edcc57d0 100644 --- a/src/js/node/trace_events.ts +++ b/src/js/node/trace_events.ts @@ -13,10 +13,12 @@ function ERR_INVALID_ARG_TYPE(name, type, value) { function createTracing(opts) { if (typeof opts !== "object" || opts == null) { + // @ts-ignore throw new ERR_INVALID_ARG_TYPE("options", "Object", opts); } // TODO: validate categories + // @ts-ignore return new Tracing(opts); } diff --git a/src/js/node/url.js b/src/js/node/url.js index f9a4427ce..bb7093bcc 100644 --- a/src/js/node/url.js +++ b/src/js/node/url.js @@ -1,398 +1,852 @@ -// Hardcoded module "node:url" +/* + * Copyright Joyent, Inc. and other Node contributors. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + "use strict"; -const { URL: F, URLSearchParams: M, [Symbol.for("Bun.lazy")]: S } = globalThis; -function it(s) { - return typeof s == "string"; + +const { URL, URLSearchParams } = globalThis; + +function Url() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.host = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.query = null; + this.pathname = null; + this.path = null; + this.href = null; } -function D(s) { - return typeof s == "object" && s !== null; -} -function I(s) { - return s === null; -} -function E(s) { - return s == null; -} -function ft(s) { - return s === void 0; -} -function m() { - (this.protocol = null), - (this.slashes = null), - (this.auth = null), - (this.host = null), - (this.port = null), - (this.hostname = null), - (this.hash = null), - (this.search = null), - (this.query = null), - (this.pathname = null), - (this.path = null), - (this.href = null); -} -var tt = /^([a-z0-9.+-]+:)/i, - st = /:[0-9]*$/, - ht = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, - et = [ - "<", - ">", - '"', - "`", - " ", - "\r", - ` -`, - " ", - ], - rt = ["{", "}", "|", "\\", "^", "`"].concat(et), - B = ["'"].concat(rt), - G = ["%", "/", "?", ";", "#"].concat(B), - J = ["/", "?", "#"], - ot = 255, - K = /^[+a-z0-9A-Z_-]{0,63}$/, - at = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, - nt = { javascript: !0, "javascript:": !0 }, - N = { javascript: !0, "javascript:": !0 }, - R = { - http: !0, - https: !0, - ftp: !0, - gopher: !0, - file: !0, - "http:": !0, - "https:": !0, - "ftp:": !0, - "gopher:": !0, - "file:": !0, + +// Reference: RFC 3986, RFC 1808, RFC 2396 + +/* + * define these here so at least they only have to be + * compiled once on the first module load. + */ +var protocolPattern = /^([a-z0-9.+-]+:)/i, + portPattern = /:[0-9]*$/, + // Special case for a simple path URL + simplePathPattern = /^(\/\/?(?!\/)[^?\s]*)(\?[^\s]*)?$/, + /* + * RFC 2396: characters reserved for delimiting URLs. + * We actually just auto-escape these. + */ + delims = ["<", ">", '"', "`", " ", "\r", "\n", "\t"], + // RFC 2396: characters not allowed for various reasons. + unwise = ["{", "}", "|", "\\", "^", "`"].concat(delims), + // Allowed by RFCs, but cause of XSS attacks. Always escape these. + autoEscape = ["'"].concat(unwise), + /* + * Characters that are never ever allowed in a hostname. + * Note that any invalid chars are also handled, but these + * are the ones that are *expected* to be seen, so we fast-path + * them. + */ + nonHostChars = ["%", "/", "?", ";", "#"].concat(autoEscape), + hostEndingChars = ["/", "?", "#"], + hostnameMaxLen = 255, + hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/, + hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, + // protocols that can allow "unsafe" and "unwise" chars. + unsafeProtocol = { + javascript: true, + "javascript:": true, + }, + // protocols that never have a hostname. + hostlessProtocol = { + javascript: true, + "javascript:": true, }, - Z = { - parse(s) { - var r = decodeURIComponent; - return (s + "") - .replace(/\+/g, " ") - .split("&") - .filter(Boolean) - .reduce(function (t, o, a) { - var l = o.split("="), - f = r(l[0] || ""), - h = r(l[1] || ""), - g = t[f]; - return (t[f] = g === void 0 ? h : [].concat(g, h)), t; - }, {}); - }, - stringify(s) { - var r = encodeURIComponent; - return Object.keys(s || {}) - .reduce(function (t, o) { - return ( - [].concat(s[o]).forEach(function (a) { - t.push(r(o) + "=" + r(a)); - }), - t - ); - }, []) - .join("&") - .replace(/\s/g, "+"); - }, + // protocols that always contain a // bit. + slashedProtocol = { + http: true, + https: true, + ftp: true, + gopher: true, + file: true, + "http:": true, + "https:": true, + "ftp:": true, + "gopher:": true, + "file:": true, }; -function A(s, r, t) { - if (s && D(s) && s instanceof m) return s; - var o = new m(); - return o.parse(s, r, t), o; + +function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && typeof url === "object" && url instanceof Url) { + return url; + } + + var u = new Url(); + u.parse(url, parseQueryString, slashesDenoteHost); + return u; } -m.prototype.parse = function (s, r, t) { - if (!it(s)) throw new TypeError("Parameter 'url' must be a string, not " + typeof s); - var o = s.indexOf("?"), - a = o !== -1 && o < s.indexOf("#") ? "?" : "#", - l = s.split(a), - f = /\\/g; - (l[0] = l[0].replace(f, "/")), (s = l.join(a)); - var h = s; - if (((h = h.trim()), !t && s.split("#").length === 1)) { - var g = ht.exec(h); - if (g) - return ( - (this.path = h), - (this.href = h), - (this.pathname = g[1]), - g[2] - ? ((this.search = g[2]), - r ? (this.query = Z.parse(this.search.substr(1))) : (this.query = this.search.substr(1))) - : r && ((this.search = ""), (this.query = {})), - this - ); - } - var c = tt.exec(h); - if (c) { - c = c[0]; - var v = c.toLowerCase(); - (this.protocol = v), (h = h.substr(c.length)); - } - if (t || c || h.match(/^\/\/[^@\/]+@[^@\/]+/)) { - var j = h.substr(0, 2) === "//"; - j && !(c && N[c]) && ((h = h.substr(2)), (this.slashes = !0)); - } - if (!N[c] && (j || (c && !R[c]))) { - for (var u = -1, n = 0; n < J.length; n++) { - var b = h.indexOf(J[n]); - b !== -1 && (u === -1 || b < u) && (u = b); + +Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { + if (typeof url !== "string") { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); + } + + /* + * Copy chrome, IE, opera backslash-handling behavior. + * Back slashes before the query string get converted to forward slashes + * See: https://code.google.com/p/chromium/issues/detail?id=25916 + */ + var queryIndex = url.indexOf("?"), + splitter = queryIndex !== -1 && queryIndex < url.indexOf("#") ? "?" : "#", + uSplit = url.split(splitter), + slashRegex = /\\/g; + uSplit[0] = uSplit[0].replace(slashRegex, "/"); + url = uSplit.join(splitter); + + var rest = url; + + /* + * trim before proceeding. + * This is to support parse stuff like " http://foo.com \n" + */ + rest = rest.trim(); + + if (!slashesDenoteHost && url.split("#").length === 1) { + // Try fast path regexp + var simplePath = simplePathPattern.exec(rest); + if (simplePath) { + this.path = rest; + this.href = rest; + this.pathname = simplePath[1]; + if (simplePath[2]) { + this.search = simplePath[2]; + if (parseQueryString) { + this.query = new URLSearchParams(this.search.substr(1)).toJSON(); + } else { + this.query = this.search.substr(1); + } + } else if (parseQueryString) { + this.search = ""; + this.query = {}; + } + return this; + } + } + + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.substr(proto.length); + } + + /* + * figure out if it's got a host + * user@server is *always* interpreted as a hostname, and url + * resolution will treat //foo/bar as host=foo,path=bar because that's + * how the browser resolves relative URLs. + */ + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@/]+@[^@/]+/)) { + var slashes = rest.substr(0, 2) === "//"; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + this.slashes = true; + } + } + + if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) { + /* + * there's a hostname. + * the first instance of /, ?, ;, or # ends the host. + * + * If there is an @ in the hostname, then non-host chars *are* allowed + * to the left of the last @ sign, unless some host-ending character + * comes *before* the @-sign. + * URLs are obnoxious. + * + * ex: + * http://a@b@c/ => user:a@b host:c + * http://a@b?@c => user:a host:c path:/?@c + */ + + /* + * v0.12 TODO(isaacs): This is not quite how Chrome does things. + * Review our test case against browsers more comprehensively. + */ + + // find the first instance of any hostEndingChars + var hostEnd = -1; + for (var i = 0; i < hostEndingChars.length; i++) { + var hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { + hostEnd = hec; + } + } + + /* + * at this point, either we have an explicit point where the + * auth portion cannot go past, or the last @ char is the decider. + */ + var auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf("@"); + } else { + /* + * atSign must be in auth portion. + * http://a@b/c@d => host:b auth:a path:/c@d + */ + atSign = rest.lastIndexOf("@", hostEnd); + } + + /* + * Now we have a portion which is definitely the auth. + * Pull that off. + */ + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = decodeURIComponent(auth); } - var P, p; - u === -1 ? (p = h.lastIndexOf("@")) : (p = h.lastIndexOf("@", u)), - p !== -1 && ((P = h.slice(0, p)), (h = h.slice(p + 1)), (this.auth = decodeURIComponent(P))), - (u = -1); - for (var n = 0; n < G.length; n++) { - var b = h.indexOf(G[n]); - b !== -1 && (u === -1 || b < u) && (u = b); + + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (var i = 0; i < nonHostChars.length; i++) { + var hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { + hostEnd = hec; + } + } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) { + hostEnd = rest.length; } - u === -1 && (u = h.length), - (this.host = h.slice(0, u)), - (h = h.slice(u)), - this.parseHost(), - (this.hostname = this.hostname || ""); - var C = this.hostname[0] === "[" && this.hostname[this.hostname.length - 1] === "]"; - if (!C) - for (var e = this.hostname.split(/\./), n = 0, i = e.length; n < i; n++) { - var d = e[n]; - if (!!d && !d.match(K)) { - for (var y = "", x = 0, _ = d.length; x < _; x++) d.charCodeAt(x) > 127 ? (y += "x") : (y += d[x]); - if (!y.match(K)) { - var q = e.slice(0, n), - O = e.slice(n + 1), - U = d.match(at); - U && (q.push(U[1]), O.unshift(U[2])), - O.length && (h = "/" + O.join(".") + h), - (this.hostname = q.join(".")); + + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); + + // pull out port. + this.parseHost(); + + /* + * we've indicated that there is a hostname, + * so even if it's empty, it has to be present. + */ + this.hostname = this.hostname || ""; + + /* + * if hostname begins with [ and ends with ] + * assume that it's an IPv6 address. + */ + var ipv6Hostname = this.hostname[0] === "[" && this.hostname[this.hostname.length - 1] === "]"; + + // validate a little. + if (!ipv6Hostname) { + var hostparts = this.hostname.split(/\./); + for (var i = 0, l = hostparts.length; i < l; i++) { + var part = hostparts[i]; + if (!part) { + continue; + } + if (!part.match(hostnamePartPattern)) { + var newpart = ""; + for (var j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + /* + * we replace non-ASCII char with a temporary placeholder + * we need this to make sure size of hostname is not + * broken by replacing non-ASCII by nothing + */ + newpart += "x"; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + var validParts = hostparts.slice(0, i); + var notHost = hostparts.slice(i + 1); + var bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = "/" + notHost.join(".") + rest; + } + this.hostname = validParts.join("."); break; } } } - this.hostname.length > ot ? (this.hostname = "") : (this.hostname = this.hostname.toLowerCase()), - C || (this.hostname = new F(`https://${this.hostname}`).hostname); - var w = this.port ? ":" + this.port : "", - H = this.hostname || ""; - (this.host = H + w), - (this.href += this.host), - C && ((this.hostname = this.hostname.substr(1, this.hostname.length - 2)), h[0] !== "/" && (h = "/" + h)); - } - if (!nt[v]) - for (var n = 0, i = B.length; n < i; n++) { - var L = B[n]; - if (h.indexOf(L) !== -1) { - var z = encodeURIComponent(L); - z === L && (z = escape(L)), (h = h.split(L).join(z)); + } + + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ""; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); + } + + if (!ipv6Hostname) { + /* + * IDNA Support: Returns a punycoded representation of "domain". + * It only converts parts of the domain name that + * have non-ASCII characters, i.e. it doesn't matter if + * you call it with a domain that already is ASCII-only. + */ + this.hostname = new URL("http://" + this.hostname).hostname; + } + + var p = this.port ? ":" + this.port : ""; + var h = this.hostname || ""; + this.host = h + p; + this.href += this.host; + + /* + * strip [ and ] from the hostname + * the host field still retains them, though + */ + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + if (rest[0] !== "/") { + rest = "/" + rest; + } + } + } + + /* + * now rest is set to the post-host stuff. + * chop off any delim chars. + */ + if (!unsafeProtocol[lowerProto]) { + /* + * First, make 100% sure that any "autoEscape" chars get + * escaped, even if encodeURIComponent doesn't think they + * need to be. + */ + for (var i = 0, l = autoEscape.length; i < l; i++) { + var ae = autoEscape[i]; + if (rest.indexOf(ae) === -1) { + continue; + } + var esc = encodeURIComponent(ae); + if (esc === ae) { + esc = escape(ae); } + rest = rest.split(ae).join(esc); + } + } + + // chop off from the tail first. + var hash = rest.indexOf("#"); + if (hash !== -1) { + // got a fragment string. + this.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + var qm = rest.indexOf("?"); + if (qm !== -1) { + this.search = rest.substr(qm); + this.query = rest.substr(qm + 1); + if (parseQueryString) { + this.query = new URLSearchParams(this.query); } - var $ = h.indexOf("#"); - $ !== -1 && ((this.hash = h.substr($)), (h = h.slice(0, $))); - var T = h.indexOf("?"); - if ( - (T !== -1 - ? ((this.search = h.substr(T)), - (this.query = h.substr(T + 1)), - r && (this.query = Z.parse(this.query)), - (h = h.slice(0, T))) - : r && ((this.search = ""), (this.query = {})), - h && (this.pathname = h), - R[v] && this.hostname && !this.pathname && (this.pathname = "/"), - this.pathname || this.search) - ) { - var w = this.pathname || "", - Q = this.search || ""; - this.path = w + Q; - } - return (this.href = this.format()), this; + rest = rest.slice(0, qm); + } else if (parseQueryString) { + // no query string, but parseQueryString still requested + this.search = ""; + this.query = {}; + } + if (rest) { + this.pathname = rest; + } + if (slashedProtocol[lowerProto] && this.hostname && !this.pathname) { + this.pathname = "/"; + } + + // to support http.request + if (this.pathname || this.search) { + var p = this.pathname || ""; + var s = this.search || ""; + this.path = p + s; + } + + // finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; }; -function V(s) { - return it(s) && (s = A(s)), s instanceof m ? s.format() : m.prototype.format.call(s); + +// format a parsed object into a url string +function urlFormat(obj) { + /* + * ensure it's an object, and not a string url. + * If it's an obj, this is a no-op. + * this way, you can call url_format() on strings + * to clean up potentially wonky urls. + */ + if (typeof obj === "string") { + obj = urlParse(obj); + } + if (!(obj instanceof Url)) { + return Url.prototype.format.call(obj); + } + return obj.format(); } -m.prototype.format = function () { - var s = this.auth || ""; - s && ((s = encodeURIComponent(s)), (s = s.replace(/%3A/i, ":")), (s += "@")); - var r = this.protocol || "", - t = this.pathname || "", - o = this.hash || "", - a = !1, - l = ""; - this.host - ? (a = s + this.host) - : this.hostname && - ((a = s + (this.hostname.indexOf(":") === -1 ? this.hostname : "[" + this.hostname + "]")), - this.port && (a += ":" + this.port)), - this.query && D(this.query) && Object.keys(this.query).length && (l = Z.stringify(this.query)); - var f = this.search || (l && "?" + l) || ""; - return ( - r && r.substr(-1) !== ":" && (r += ":"), - this.slashes || ((!r || R[r]) && a !== !1) - ? ((a = "//" + (a || "")), t && t.charAt(0) !== "/" && (t = "/" + t)) - : a || (a = ""), - o && o.charAt(0) !== "#" && (o = "#" + o), - f && f.charAt(0) !== "?" && (f = "?" + f), - (t = t.replace(/[?#]/g, function (h) { - return encodeURIComponent(h); - })), - (f = f.replace("#", "%23")), - r + a + t + f + o - ); + +Url.prototype.format = function () { + var auth = this.auth || ""; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ":"); + auth += "@"; + } + + var protocol = this.protocol || "", + pathname = this.pathname || "", + hash = this.hash || "", + host = false, + query = ""; + + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + (this.hostname.indexOf(":") === -1 ? this.hostname : "[" + this.hostname + "]"); + if (this.port) { + host += ":" + this.port; + } + } + + if (this.query && typeof this.query === "object" && Object.keys(this.query).length) { + query = new URLSearchParams(this.query).toString(); + } + + var search = this.search || (query && "?" + query) || ""; + + if (protocol && protocol.substr(-1) !== ":") { + protocol += ":"; + } + + /* + * only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + * unless they had them to begin with. + */ + if (this.slashes || ((!protocol || slashedProtocol[protocol]) && host !== false)) { + host = "//" + (host || ""); + if (pathname && pathname.charAt(0) !== "/") { + pathname = "/" + pathname; + } + } else if (!host) { + host = ""; + } + + if (hash && hash.charAt(0) !== "#") { + hash = "#" + hash; + } + if (search && search.charAt(0) !== "?") { + search = "?" + search; + } + + pathname = pathname.replace(/[?#]/g, function (match) { + return encodeURIComponent(match); + }); + search = search.replace("#", "%23"); + + return protocol + host + pathname + search + hash; }; -function W(s, r) { - return A(s, !1, !0).resolve(r); + +function urlResolve(source, relative) { + return urlParse(source, false, true).resolve(relative); } -m.prototype.resolve = function (s) { - return this.resolveObject(A(s, !1, !0)).format(); + +Url.prototype.resolve = function (relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); }; -function X(s, r) { - return s ? A(s, !1, !0).resolveObject(r) : r; + +function urlResolveObject(source, relative) { + if (!source) { + return relative; + } + return urlParse(source, false, true).resolveObject(relative); } -(m.prototype.resolveObject = function (s) { - if (it(s)) { - var r = new m(); - r.parse(s, !1, !0), (s = r); - } - for (var t = new m(), o = Object.keys(this), a = 0; a < o.length; a++) { - var l = o[a]; - t[l] = this[l]; - } - if (((t.hash = s.hash), s.href === "")) return (t.href = t.format()), t; - if (s.slashes && !s.protocol) { - for (var f = Object.keys(s), h = 0; h < f.length; h++) { - var g = f[h]; - g !== "protocol" && (t[g] = s[g]); + +Url.prototype.resolveObject = function (relative) { + if (typeof relative === "string") { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; + } + + var result = new Url(); + var tkeys = Object.keys(this); + for (var tk = 0; tk < tkeys.length; tk++) { + var tkey = tkeys[tk]; + result[tkey] = this[tkey]; + } + + /* + * hash is always overridden, no matter what. + * even href="" will remove it. + */ + result.hash = relative.hash; + + // if the relative url is empty, then there's nothing left to do here. + if (relative.href === "") { + result.href = result.format(); + return result; + } + + // hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + // take everything except the protocol from relative + var rkeys = Object.keys(relative); + for (var rk = 0; rk < rkeys.length; rk++) { + var rkey = rkeys[rk]; + if (rkey !== "protocol") { + result[rkey] = relative[rkey]; + } } - return R[t.protocol] && t.hostname && !t.pathname && (t.path = t.pathname = "/"), (t.href = t.format()), t; + + // urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[result.protocol] && result.hostname && !result.pathname) { + result.pathname = "/"; + result.path = result.pathname; + } + + result.href = result.format(); + return result; } - if (s.protocol && s.protocol !== t.protocol) { - if (!R[s.protocol]) { - for (var c = Object.keys(s), v = 0; v < c.length; v++) { - var j = c[v]; - t[j] = s[j]; + + if (relative.protocol && relative.protocol !== result.protocol) { + /* + * if it's a known url protocol, then changing + * the protocol does weird things + * first, if it's not file:, then we MUST have a host, + * and if there was a path + * to begin with, then we MUST have a path. + * if it is file:, then the host is dropped, + * because that's known to be hostless. + * anything else is assumed to be absolute. + */ + if (!slashedProtocol[relative.protocol]) { + var keys = Object.keys(relative); + for (var v = 0; v < keys.length; v++) { + var k = keys[v]; + result[k] = relative[k]; } - return (t.href = t.format()), t; + result.href = result.format(); + return result; } - if (((t.protocol = s.protocol), !s.host && !N[s.protocol])) { - for (var i = (s.pathname || "").split("/"); i.length && !(s.host = i.shift()); ); - s.host || (s.host = ""), - s.hostname || (s.hostname = ""), - i[0] !== "" && i.unshift(""), - i.length < 2 && i.unshift(""), - (t.pathname = i.join("/")); - } else t.pathname = s.pathname; - if ( - ((t.search = s.search), - (t.query = s.query), - (t.host = s.host || ""), - (t.auth = s.auth), - (t.hostname = s.hostname || s.host), - (t.port = s.port), - t.pathname || t.search) - ) { - var u = t.pathname || "", - n = t.search || ""; - t.path = u + n; + + result.protocol = relative.protocol; + if (!relative.host && !hostlessProtocol[relative.protocol]) { + var relPath = (relative.pathname || "").split("/"); + while (relPath.length && !(relative.host = relPath.shift())) {} + if (!relative.host) { + relative.host = ""; + } + if (!relative.hostname) { + relative.hostname = ""; + } + if (relPath[0] !== "") { + relPath.unshift(""); + } + if (relPath.length < 2) { + relPath.unshift(""); + } + result.pathname = relPath.join("/"); + } else { + result.pathname = relative.pathname; } - return (t.slashes = t.slashes || s.slashes), (t.href = t.format()), t; - } - var b = t.pathname && t.pathname.charAt(0) === "/", - P = s.host || (s.pathname && s.pathname.charAt(0) === "/"), - p = P || b || (t.host && s.pathname), - C = p, - e = (t.pathname && t.pathname.split("/")) || [], - i = (s.pathname && s.pathname.split("/")) || [], - d = t.protocol && !R[t.protocol]; - if ( - (d && - ((t.hostname = ""), - (t.port = null), - t.host && (e[0] === "" ? (e[0] = t.host) : e.unshift(t.host)), - (t.host = ""), - s.protocol && - ((s.hostname = null), - (s.port = null), - s.host && (i[0] === "" ? (i[0] = s.host) : i.unshift(s.host)), - (s.host = null)), - (p = p && (i[0] === "" || e[0] === ""))), - P) - ) - (t.host = s.host || s.host === "" ? s.host : t.host), - (t.hostname = s.hostname || s.hostname === "" ? s.hostname : t.hostname), - (t.search = s.search), - (t.query = s.query), - (e = i); - else if (i.length) e || (e = []), e.pop(), (e = e.concat(i)), (t.search = s.search), (t.query = s.query); - else if (!E(s.search)) { - if (d) { - t.hostname = t.host = e.shift(); - var y = t.host && t.host.indexOf("@") > 0 ? t.host.split("@") : !1; - y && ((t.auth = y.shift()), (t.host = t.hostname = y.shift())); + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ""; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // to support http.request + if (result.pathname || result.search) { + var p = result.pathname || ""; + var s = result.search || ""; + result.path = p + s; } - return ( - (t.search = s.search), - (t.query = s.query), - (!I(t.pathname) || !I(t.search)) && (t.path = (t.pathname ? t.pathname : "") + (t.search ? t.search : "")), - (t.href = t.format()), - t - ); - } - if (!e.length) - return (t.pathname = null), t.search ? (t.path = "/" + t.search) : (t.path = null), (t.href = t.format()), t; - for ( - var x = e.slice(-1)[0], - _ = ((t.host || s.host || e.length > 1) && (x === "." || x === "..")) || x === "", - q = 0, - O = e.length; - O >= 0; - O-- - ) - (x = e[O]), x === "." ? e.splice(O, 1) : x === ".." ? (e.splice(O, 1), q++) : q && (e.splice(O, 1), q--); - if (!p && !C) for (; q--; q) e.unshift(".."); - p && e[0] !== "" && (!e[0] || e[0].charAt(0) !== "/") && e.unshift(""), - _ && e.join("/").substr(-1) !== "/" && e.push(""); - var U = e[0] === "" || (e[0] && e[0].charAt(0) === "/"); - if (d) { - t.hostname = t.host = U ? "" : e.length ? e.shift() : ""; - var y = t.host && t.host.indexOf("@") > 0 ? t.host.split("@") : !1; - y && ((t.auth = y.shift()), (t.host = t.hostname = y.shift())); - } - return ( - (p = p || (t.host && e.length)), - p && !U && e.unshift(""), - e.length ? (t.pathname = e.join("/")) : ((t.pathname = null), (t.path = null)), - (!I(t.pathname) || !I(t.search)) && (t.path = (t.pathname ? t.pathname : "") + (t.search ? t.search : "")), - (t.auth = s.auth || t.auth), - (t.slashes = t.slashes || s.slashes), - (t.href = t.format()), - t - ); -}), - (m.prototype.parseHost = function () { - var s = this.host, - r = st.exec(s); - r && ((r = r[0]), r !== ":" && (this.port = r.substr(1)), (s = s.substr(0, s.length - r.length))), - s && (this.hostname = s); - }); -var Y, k; -S && ((Y = S("pathToFileURL")), (k = S("fileURLToPath"))); -var ut = { - parse: A, - resolve: W, - resolveObject: X, - format: V, - Url: m, - pathToFileURL: Y, - fileURLToPath: k, - URL: F, - URLSearchParams: M, + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + } + + var isSourceAbs = result.pathname && result.pathname.charAt(0) === "/", + isRelAbs = relative.host || (relative.pathname && relative.pathname.charAt(0) === "/"), + mustEndAbs = isRelAbs || isSourceAbs || (result.host && relative.pathname), + removeAllDots = mustEndAbs, + srcPath = (result.pathname && result.pathname.split("/")) || [], + relPath = (relative.pathname && relative.pathname.split("/")) || [], + psychotic = result.protocol && !slashedProtocol[result.protocol]; + + /* + * if the url is a non-slashed url, then relative + * links like ../.. should be able + * to crawl up to the hostname, as well. This is strange. + * result.protocol has already been set by now. + * Later on, put the first path part into the host field. + */ + if (psychotic) { + result.hostname = ""; + result.port = null; + if (result.host) { + if (srcPath[0] === "") { + srcPath[0] = result.host; + } else { + srcPath.unshift(result.host); + } + } + result.host = ""; + if (relative.protocol) { + relative.hostname = null; + relative.port = null; + if (relative.host) { + if (relPath[0] === "") { + relPath[0] = relative.host; + } else { + relPath.unshift(relative.host); + } + } + relative.host = null; + } + mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === ""); + } + + if (isRelAbs) { + // it's absolute. + result.host = relative.host || relative.host === "" ? relative.host : result.host; + result.hostname = relative.hostname || relative.hostname === "" ? relative.hostname : result.hostname; + result.search = relative.search; + result.query = relative.query; + srcPath = relPath; + // fall through to the dot-handling below. + } else if (relPath.length) { + /* + * it's relative + * throw away the existing file, and take the new path instead. + */ + if (!srcPath) { + srcPath = []; + } + srcPath.pop(); + srcPath = srcPath.concat(relPath); + result.search = relative.search; + result.query = relative.query; + } else if (relative.search != null) { + /* + * just pull out the search. + * like href='?foo'. + * Put this after the other two cases because it simplifies the booleans + */ + if (psychotic) { + result.host = srcPath.shift(); + result.hostname = result.host; + /* + * occationaly the auth can get stuck only in host + * this especially happens in cases like + * url.resolveObject('mailto:local1@domain1', 'local2@domain2') + */ + var authInHost = result.host && result.host.indexOf("@") > 0 ? result.host.split("@") : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.hostname = authInHost.shift(); + result.host = result.hostname; + } + } + result.search = relative.search; + result.query = relative.query; + // to support http.request + if (result.pathname !== null || result.search !== null) { + result.path = (result.pathname ? result.pathname : "") + (result.search ? result.search : ""); + } + result.href = result.format(); + return result; + } + + if (!srcPath.length) { + /* + * no path at all. easy. + * we've already handled the other stuff above. + */ + result.pathname = null; + // to support http.request + if (result.search) { + result.path = "/" + result.search; + } else { + result.path = null; + } + result.href = result.format(); + return result; + } + + /* + * if a url ENDs in . or .., then it must get a trailing slash. + * however, if it ends in anything else non-slashy, + * then it must NOT get a trailing slash. + */ + var last = srcPath.slice(-1)[0]; + var hasTrailingSlash = + ((result.host || relative.host || srcPath.length > 1) && (last === "." || last === "..")) || last === ""; + + /* + * strip single dots, resolve double dots to parent dir + * if the path tries to go above the root, `up` ends up > 0 + */ + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last === ".") { + srcPath.splice(i, 1); + } else if (last === "..") { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift(".."); + } + } + + if (mustEndAbs && srcPath[0] !== "" && (!srcPath[0] || srcPath[0].charAt(0) !== "/")) { + srcPath.unshift(""); + } + + if (hasTrailingSlash && srcPath.join("/").substr(-1) !== "/") { + srcPath.push(""); + } + + var isAbsolute = srcPath[0] === "" || (srcPath[0] && srcPath[0].charAt(0) === "/"); + + // put the host back + if (psychotic) { + result.hostname = isAbsolute ? "" : srcPath.length ? srcPath.shift() : ""; + result.host = result.hostname; + /* + * occationaly the auth can get stuck only in host + * this especially happens in cases like + * url.resolveObject('mailto:local1@domain1', 'local2@domain2') + */ + var authInHost = result.host && result.host.indexOf("@") > 0 ? result.host.split("@") : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.hostname = authInHost.shift(); + result.host = result.hostname; + } + } + + mustEndAbs = mustEndAbs || (result.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(""); + } + + if (srcPath.length > 0) { + result.pathname = srcPath.join("/"); + } else { + result.pathname = null; + result.path = null; + } + + // to support request.http + if (result.pathname !== null || result.search !== null) { + result.path = (result.pathname ? result.pathname : "") + (result.search ? result.search : ""); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; +}; + +Url.prototype.parseHost = function () { + var host = this.host; + var port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ":") { + this.port = port.substr(1); + } + host = host.substr(0, host.length - port.length); + } + if (host) { + this.hostname = host; + } +}; +function urlToHttpOptions(url) { + const options = { + protocol: url.protocol, + hostname: + typeof url.hostname === "string" && url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname, + hash: url.hash, + search: url.search, + pathname: url.pathname, + path: `${url.pathname || ""}${url.search || ""}`, + href: url.href, + }; + if (url.port !== "") { + options.port = Number(url.port); + } + if (url.username || url.password) { + options.auth = `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`; + } + return options; +} + +const lazy = globalThis[Symbol.for("Bun.lazy")]; +const pathToFileURL = lazy("pathToFileURL"); +const fileURLToPath = lazy("fileURLToPath"); +const defaultObject = { + parse: urlParse, + resolve: urlResolve, + resolveObject: urlResolveObject, + format: urlFormat, + Url, + URLSearchParams, + URL, + pathToFileURL, + fileURLToPath, + urlToHttpOptions, + [Symbol.for("CommonJS")]: 0, }; -("use strict"); + export { - F as URL, - M as URLSearchParams, - m as Url, - ut as default, - k as fileURLToPath, - V as format, - A as parse, - Y as pathToFileURL, - W as resolve, - X as resolveObject, + defaultObject as default, + urlParse as parse, + urlResolve as resolve, + urlResolveObject as resolveObject, + urlFormat as format, + Url, + URLSearchParams, + URL, + pathToFileURL, + fileURLToPath, + urlToHttpOptions, }; diff --git a/src/js/node/util.js b/src/js/node/util.js index 282f4b371..2ec4aadb9 100644 --- a/src/js/node/util.js +++ b/src/js/node/util.js @@ -1,4 +1,11 @@ // Hardcoded module "node:util" +import * as types from "node:util/types"; +export { default as types } from "node:util/types"; + +var cjs_exports = {}; + +export default cjs_exports; + var __getOwnPropNames = Object.getOwnPropertyNames; var __commonJS = (cb, mod) => function __require() { @@ -17,6 +24,10 @@ export function isBuffer(value) { ); } +function isFunction(value) { + return typeof value === "function"; +} + // node_modules/inherits/inherits_browser.js var require_inherits_browser = __commonJS({ "node_modules/inherits/inherits_browser.js"(exports, module2) { @@ -35,9 +46,6 @@ var require_inherits_browser = __commonJS({ }); const deepEquals = Bun.deepEquals; const isDeepStrictEqual = (a, b) => deepEquals(a, b, true); -const exports = { - isDeepStrictEqual, -}; var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors; var formatRegExp = /%[sdj%]/g; function format(f) { @@ -78,21 +86,19 @@ function format(f) { } return str; } -exports.format = format; -function deprecate(fn, msg) { - if (typeof process !== "undefined" && process.noDeprecation === true) { + +function deprecate(fn, msg, code) { + if (process.noDeprecation === true) { return fn; } - if (typeof process === "undefined") { - return function () { - return exports.deprecate(fn, msg).apply(this, arguments); - }; - } + var warned = false; function deprecated() { if (!warned) { if (process.throwDeprecation) { - throw new Error(msg); + var err = new Error(msg); + if (code) err.code = code; + throw err; } else if (process.traceDeprecation) { console.trace(msg); } else { @@ -104,7 +110,7 @@ function deprecate(fn, msg) { } return deprecated; } -exports.deprecate = deprecate; + var debugs = {}; var debugEnvRegex = /^$/; if (process.env.NODE_DEBUG) { @@ -123,7 +129,7 @@ function debuglog(set) { if (debugEnvRegex.test(set)) { var pid = process.pid; debugs[set] = function () { - var msg = exports.format.apply(exports, arguments); + var msg = format.apply(cjs_exports, arguments); console.error("%s %d: %s", set, pid, msg); }; } else { @@ -132,7 +138,6 @@ function debuglog(set) { } return debugs[set]; } -exports.debuglog = debuglog; function inspect(obj, opts) { var ctx = { seen: [], @@ -143,7 +148,7 @@ function inspect(obj, opts) { if (isBoolean(opts)) { ctx.showHidden = opts; } else if (opts) { - exports._extend(ctx, opts); + _extend(ctx, opts); } if (isUndefined(ctx.showHidden)) ctx.showHidden = false; if (isUndefined(ctx.depth)) ctx.depth = 2; @@ -152,7 +157,6 @@ function inspect(obj, opts) { if (ctx.colors) ctx.stylize = stylizeWithColor; return formatValue(ctx, obj, ctx.depth); } -exports.inspect = inspect; inspect.colors = { bold: [1, 22], italic: [3, 23], @@ -201,7 +205,7 @@ function formatValue(ctx, value, recurseTimes) { ctx.customInspect && value && isFunction(value.inspect) && - value.inspect !== exports.inspect && + value.inspect !== inspect && !(value.constructor && value.constructor.prototype === value) ) { var ret = value.inspect(recurseTimes, ctx); @@ -388,51 +392,42 @@ function reduceToSingleString(output, base, braces) { } return braces[0] + base + " " + output.join(", ") + " " + braces[1]; } -const types = import.meta.require("node:util/types"); -exports.types = types; + function isArray(ar) { return Array.isArray(ar); } -exports.isArray = isArray; + function isBoolean(arg) { return typeof arg === "boolean"; } -exports.isBoolean = isBoolean; + function isNull(arg) { return arg === null; } -exports.isNull = isNull; + function isNullOrUndefined(arg) { return arg == null; } -exports.isNullOrUndefined = isNullOrUndefined; + function isNumber(arg) { return typeof arg === "number"; } -exports.isNumber = isNumber; + function isString(arg) { return typeof arg === "string"; } -exports.isString = isString; function isSymbol(arg) { return typeof arg === "symbol"; } -exports.isSymbol = isSymbol; function isUndefined(arg) { return arg === void 0; } -exports.isUndefined = isUndefined; -var isRegExp = (exports.isRegExp = exports.types.isRegExp); +var isRegExp = types.isRegExp; function isObject(arg) { return typeof arg === "object" && arg !== null; } -exports.isObject = isObject; -var isDate = (exports.isDate = exports.types.isDate); -var isError = (exports.isError = exports.types.isNativeError); -function isFunction(arg) { - return typeof arg === "function"; -} -var isFunction = (exports.isFunction = isFunction); +var isDate = types.isDate; +var isError = types.isNativeError; function isPrimitive(arg) { return ( arg === null || @@ -443,8 +438,6 @@ function isPrimitive(arg) { typeof arg === "undefined" ); } -exports.isPrimitive = isPrimitive; -exports.isBuffer = isBuffer; function pad(n) { return n < 10 ? "0" + n.toString(10) : n.toString(10); } @@ -454,11 +447,11 @@ function timestamp() { var time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(":"); return [d.getDate(), months[d.getMonth()], time].join(" "); } -var log = (exports.log = function () { - console.log("%s - %s", timestamp(), exports.format.apply(exports, arguments)); -}); -var inherits = (exports.inherits = require_inherits_browser()); -var _extend = (exports._extend = function (origin, add) { +var log = function log() { + console.log("%s - %s", timestamp(), format.apply(cjs_exports, arguments)); +}; +var inherits = (inherits = require_inherits_browser()); +var _extend = function (origin, add) { if (!add || !isObject(add)) return origin; var keys = Object.keys(add); var i = keys.length; @@ -466,12 +459,12 @@ var _extend = (exports._extend = function (origin, add) { origin[keys[i]] = add[keys[i]]; } return origin; -}); +}; function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } var kCustomPromisifiedSymbol = Symbol.for("util.promisify.custom"); -var promisify = (exports.promisify = function promisify(original) { +var promisify = function promisify(original) { if (typeof original !== "function") throw new TypeError('The "original" argument must be of type Function'); if (kCustomPromisifiedSymbol && original[kCustomPromisifiedSymbol]) { var fn = original[kCustomPromisifiedSymbol]; @@ -519,12 +512,13 @@ var promisify = (exports.promisify = function promisify(original) { configurable: true, }); return Object.defineProperties(fn, getOwnPropertyDescriptors(original)); -}); -exports.promisify.custom = kCustomPromisifiedSymbol; +}; +promisify.custom = kCustomPromisifiedSymbol; function callbackifyOnRejected(reason, cb) { if (!reason) { var newReason = new Error("Promise was rejected with a falsy value"); newReason.reason = reason; + newReason.code = "ERR_FALSY_VALUE_REJECTION"; reason = newReason; } return cb(reason); @@ -545,10 +539,10 @@ function callbackify(original) { }; original.apply(this, args).then( function (ret) { - process.nextTick(cb, null, null, ret); + process.nextTick(cb, null, ret); }, function (rej) { - process.nextTick(callbackifyOnRejected, null, rej, cb); + process.nextTick(callbackifyOnRejected, rej, cb); }, ); } @@ -556,11 +550,44 @@ function callbackify(original) { Object.defineProperties(callbackified, getOwnPropertyDescriptors(original)); return callbackified; } -exports.callbackify = callbackify; -export var TextDecoder = (exports.TextDecoder = globalThis.TextDecoder); -export var TextEncoder = (exports.TextEncoder = globalThis.TextEncoder); -exports[Symbol.for("CommonJS")] = 0; -export default exports; +export var TextDecoder = globalThis.TextDecoder; +export var TextEncoder = globalThis.TextEncoder; +var toUSVString = input => { + return (input + "").toWellFormed(); +}; + +Object.assign(cjs_exports, { + format, + deprecate, + debuglog, + _extend, + inspect, + types, + isArray, + isBoolean, + isNull, + isNullOrUndefined, + isNumber, + isString, + isSymbol, + isUndefined, + isRegExp, + isObject, + isDate, + isFunction, + isError, + isPrimitive, + isBuffer, + log, + inherits, + toUSVString, + promisify, + callbackify, + isDeepStrictEqual, + TextDecoder, + TextEncoder, + [Symbol.for("CommonJS")]: 0, +}); export { format, @@ -586,4 +613,5 @@ export { promisify, callbackify, isDeepStrictEqual, + toUSVString, }; diff --git a/src/js/node/zlib.js b/src/js/node/zlib.js index 1414f4664..77a9e8089 100644 --- a/src/js/node/zlib.js +++ b/src/js/node/zlib.js @@ -3,6 +3,41 @@ // This is a very slow module! // It should really be fixed. It will show up in benchmarking. It also loads // slowly. We need to fix it! +import { default as assert } from "node:assert"; +import * as AssertModule from "node:assert"; +import * as BufferModule from "node:buffer"; +import * as StreamModule from "node:stream"; +import * as Util from "node:util"; + +export var Deflate, + Inflate, + Gzip, + Gunzip, + DeflateRaw, + InflateRaw, + Unzip, + createDeflate, + createInflate, + createDeflateRaw, + createInflateRaw, + createGzip, + createGunzip, + createUnzip, + deflate, + deflateSync, + gzip, + gzipSync, + deflateRaw, + deflateRawSync, + unzip, + unzipSync, + inflate, + inflateSync, + gunzip, + gunzipSync, + inflateRaw, + inflateRawSync, + constants; var __create = Object.create; var __defProp = Object.defineProperty; @@ -38,8 +73,6 @@ var __toESM = (mod, isNodeMode, target) => ( ); var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", { value: true }), mod); -const require = id => import.meta.require(id); - // node_modules/pako/lib/zlib/zstream.js var require_zstream = __commonJS({ "node_modules/pako/lib/zlib/zstream.js"(exports, module2) { @@ -3593,7 +3626,7 @@ var require_constants = __commonJS({ var require_binding = __commonJS({ "node_modules/browserify-zlib/lib/binding.js"(exports) { "use strict"; - var assert = require("assert"); + var Zstream = require_zstream(); var zlib_deflate = require_deflate(); var zlib_inflate = require_inflate(); @@ -3937,12 +3970,12 @@ var require_binding = __commonJS({ var require_lib = __commonJS({ "node_modules/browserify-zlib/lib/index.js"(exports) { "use strict"; - var Buffer2 = require("buffer").Buffer; - var Transform = require("stream").Transform; + var Buffer2 = BufferModule.Buffer; + var Transform = StreamModule.Transform; var binding = require_binding(); - var util = require("util"); - var assert = require("assert").ok; - var kMaxLength = require("buffer").kMaxLength; + var util = Util; + var assert = AssertModule.ok; + var kMaxLength = BufferModule.kMaxLength; var kRangeErrorMessage = "Cannot create final Buffer. It would be larger than 0x" + kMaxLength.toString(16) + " bytes"; binding.Z_MIN_WINDOWBITS = 8; @@ -4437,36 +4470,35 @@ var require_lib = __commonJS({ // zlib.js var zlib_exports = require_lib(); zlib_exports[Symbol.for("CommonJS")] = 0; + export default zlib_exports; -export var { - Deflate, - Inflate, - Gzip, - Gunzip, - DeflateRaw, - InflateRaw, - Unzip, - createDeflate, - createInflate, - createDeflateRaw, - createInflateRaw, - createGzip, - createGunzip, - createUnzip, - deflate, - deflateSync, - gzip, - gzipSync, - deflateRaw, - deflateRawSync, - unzip, - unzipSync, - inflate, - inflateSync, - gunzip, - gunzipSync, - inflateRaw, - inflateRawSync, - constants, -} = zlib_exports; +Deflate = zlib_exports.Deflate; +Inflate = zlib_exports.Inflate; +Gzip = zlib_exports.Gzip; +Gunzip = zlib_exports.Gunzip; +DeflateRaw = zlib_exports.DeflateRaw; +InflateRaw = zlib_exports.InflateRaw; +Unzip = zlib_exports.Unzip; +createDeflate = zlib_exports.createDeflate; +createInflate = zlib_exports.createInflate; +createDeflateRaw = zlib_exports.createDeflateRaw; +createInflateRaw = zlib_exports.createInflateRaw; +createGzip = zlib_exports.createGzip; +createGunzip = zlib_exports.createGunzip; +createUnzip = zlib_exports.createUnzip; +deflate = zlib_exports.deflate; +deflateSync = zlib_exports.deflateSync; +gzip = zlib_exports.gzip; +gzipSync = zlib_exports.gzipSync; +deflateRaw = zlib_exports.deflateRaw; +deflateRawSync = zlib_exports.deflateRawSync; +unzip = zlib_exports.unzip; +unzipSync = zlib_exports.unzipSync; +inflate = zlib_exports.inflate; +inflateSync = zlib_exports.inflateSync; +gunzip = zlib_exports.gunzip; +gunzipSync = zlib_exports.gunzipSync; +inflateRaw = zlib_exports.inflateRaw; +inflateRawSync = zlib_exports.inflateRawSync; +constants = zlib_exports.constants; |