diff options
author | 2022-06-22 06:42:39 -0700 | |
---|---|---|
committer | 2022-06-22 06:56:47 -0700 | |
commit | 2cdbd2de83981aa0e0bc26db5b5ebd261507f112 (patch) | |
tree | c27008292b846217a0e2e3f3571f2fa2205f2472 /src | |
parent | d9f6a3f2d26750b5a1c6c3a10b0eab0f37a7c19c (diff) | |
download | bun-2cdbd2de83981aa0e0bc26db5b5ebd261507f112.tar.gz bun-2cdbd2de83981aa0e0bc26db5b5ebd261507f112.tar.zst bun-2cdbd2de83981aa0e0bc26db5b5ebd261507f112.zip |
Polyfills for `undici`, `streams/web`, `streams/consumer` `timers` `timers/promises`, `fs/promises`, `ws`
Diffstat (limited to 'src')
-rw-r--r-- | src/javascript/jsc/fs_promises.exports.js | 108 | ||||
-rw-r--r-- | src/javascript/jsc/node_streams_consumer.exports.js | 10 | ||||
-rw-r--r-- | src/javascript/jsc/node_streams_web.exports.js | 18 | ||||
-rw-r--r-- | src/javascript/jsc/node_timers.exports.js | 20 | ||||
-rw-r--r-- | src/javascript/jsc/node_timers_promises.exports.js | 242 | ||||
-rw-r--r-- | src/javascript/jsc/undici.exports.js | 104 |
6 files changed, 502 insertions, 0 deletions
diff --git a/src/javascript/jsc/fs_promises.exports.js b/src/javascript/jsc/fs_promises.exports.js new file mode 100644 index 000000000..c1dce2291 --- /dev/null +++ b/src/javascript/jsc/fs_promises.exports.js @@ -0,0 +1,108 @@ +var fs = Bun.fs(); + +// note: this is not quite the same as how node does it +// in some cases, node swaps around arguments or makes small tweaks to the return type +// this is just better than nothing. +function promisify(fsFunction) { + // TODO: remove variadic arguments + // we can use new Function() here instead + // based on fsFucntion.length + var obj = { + [fsFunction.name]: function (resolve, reject, args) { + var result; + try { + result = fsFunction.apply(fs, args); + args = undefined; + } catch (err) { + args = undefined; + reject(err); + return; + } + + resolve(result); + }, + }; + + var func = obj[fsFunction.name]; + + // TODO: consider @createPromiseCapabiilty intrinsic + return (...args) => { + return new Promise((resolve, reject) => { + func(resolve, reject, args); + }); + }; +} + +export var access = promisify(fs.accessSync); +export var appendFile = promisify(fs.appendFileSync); +export var close = promisify(fs.closeSync); +export var copyFile = promisify(fs.copyFileSync); +export var exists = promisify(fs.existsSync); +export var chown = promisify(fs.chownSync); +export var chmod = promisify(fs.chmodSync); +export var fchmod = promisify(fs.fchmodSync); +export var fchown = promisify(fs.fchownSync); +export var fstat = promisify(fs.fstatSync); +export var fsync = promisify(fs.fsyncSync); +export var ftruncate = promisify(fs.ftruncateSync); +export var futimes = promisify(fs.futimesSync); +export var lchmod = promisify(fs.lchmodSync); +export var lchown = promisify(fs.lchownSync); +export var link = promisify(fs.linkSync); +export var lstat = promisify(fs.lstatSync); +export var mkdir = promisify(fs.mkdirSync); +export var mkdtemp = promisify(fs.mkdtempSync); +export var open = promisify(fs.openSync); +export var read = promisify(fs.readSync); +export var write = promisify(fs.writeSync); +export var readdir = promisify(fs.readdirSync); +export var readFile = promisify(fs.readFileSync); +export var readfile = readFile; +export var writeFile = promisify(fs.writeFileSync); +export var readlink = promisify(fs.readlinkSync); +export var realpath = promisify(fs.realpathSync); +export var rename = promisify(fs.renameSync); +export var stat = promisify(fs.statSync); +export var symlink = promisify(fs.symlinkSync); +export var truncate = promisify(fs.truncateSync); +export var unlink = promisify(fs.unlinkSync); +export var utimes = promisify(fs.utimesSync); +export var lutimes = promisify(fs.lutimesSync); + +export default { + access, + appendFile, + close, + copyFile, + exists, + chown, + chmod, + fchmod, + fchown, + fstat, + readfile, + fsync, + ftruncate, + futimes, + lchmod, + lchown, + link, + lstat, + mkdir, + mkdtemp, + open, + read, + write, + readdir, + readFile, + writeFile, + readlink, + realpath, + rename, + stat, + symlink, + truncate, + unlink, + utimes, + lutimes, +}; diff --git a/src/javascript/jsc/node_streams_consumer.exports.js b/src/javascript/jsc/node_streams_consumer.exports.js new file mode 100644 index 000000000..83d447a43 --- /dev/null +++ b/src/javascript/jsc/node_streams_consumer.exports.js @@ -0,0 +1,10 @@ +export const arrayBuffer = Bun.readableStreamToArrayBuffer; +export const text = Bun.readableStreamToText; +export const json = (stream) => + Bun.readableStreamToText(stream).then(JSON.parse); + +export const buffer = async (readableStream) => { + return new Buffer(await arrayBuffer(readableStream)); +}; + +export const blob = Bun.readableStreamToBlob; diff --git a/src/javascript/jsc/node_streams_web.exports.js b/src/javascript/jsc/node_streams_web.exports.js new file mode 100644 index 000000000..a856839ec --- /dev/null +++ b/src/javascript/jsc/node_streams_web.exports.js @@ -0,0 +1,18 @@ +export const ReadableStream = globalThis.ReadableStream; +export const ReadableStreamDefaultController = + globalThis.ReadableStreamDefaultController; +export const WritableStream = globalThis.WritableStream; +export const WritableStreamDefaultController = + globalThis.WritableStreamDefaultController; +export const WritableStreamDefaultWriter = + globalThis.WritableStreamDefaultWriter; +export const TransformStream = globalThis.TransformStream; +export const TransformStreamDefaultController = + globalThis.TransformStreamDefaultController; + +export const ByteLengthQueuingStrategy = globalThis.ByteLengthQueuingStrategy; +export const CountQueuingStrategy = globalThis.CountQueuingStrategy; +export const ReadableStreamBYOBReader = globalThis.ReadableStreamBYOBReader; +export const ReadableStreamBYOBRequest = globalThis.ReadableStreamBYOBRequest; +export const ReadableStreamDefaultReader = + globalThis.ReadableStreamDefaultReader; diff --git a/src/javascript/jsc/node_timers.exports.js b/src/javascript/jsc/node_timers.exports.js new file mode 100644 index 000000000..58e660412 --- /dev/null +++ b/src/javascript/jsc/node_timers.exports.js @@ -0,0 +1,20 @@ +export const setInterval = globalThis.setInterval; +export const setImmediate = globalThis.queueMicrotask; +export const setTimeout = globalThis.setTimeout; +export const clearInterval = globalThis.clearInterval; + +// not implemented +export const clearImmediate = () => {}; + +export const clearTimeout = globalThis.clearTimeout; +export const queueMicrotask = globalThis.queueMicrotask; + +export default { + setInterval, + queueMicrotask, + setImmediate, + setTimeout, + clearInterval, + clearImmediate, + clearTimeout, +}; diff --git a/src/javascript/jsc/node_timers_promises.exports.js b/src/javascript/jsc/node_timers_promises.exports.js new file mode 100644 index 000000000..a2c6d4558 --- /dev/null +++ b/src/javascript/jsc/node_timers_promises.exports.js @@ -0,0 +1,242 @@ +// https://github.com/niksy/isomorphic-timers-promises/blob/master/index.js + +const symbolAsyncIterator = Symbol.asyncIterator; + +class ERR_INVALID_ARG_TYPE extends Error { + constructor(name, expected, actual) { + super(`${name} must be ${expected}, ${typeof actual} given`); + this.code = "ERR_INVALID_ARG_TYPE"; + } +} + +class AbortError extends Error { + constructor() { + super("The operation was aborted"); + this.code = "ABORT_ERR"; + } +} + +function validateObject(object, name) { + if (object === null || typeof object !== "object") { + throw new ERR_INVALID_ARG_TYPE(name, "Object", object); + } +} + +function validateBoolean(value, name) { + if (typeof value !== "boolean") { + throw new ERR_INVALID_ARG_TYPE(name, "boolean", value); + } +} + +function validateAbortSignal(signal, name) { + if ( + typeof signal !== "undefined" && + (signal === null || typeof signal !== "object" || !("aborted" in signal)) + ) { + throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); + } +} + +function asyncIterator({ next: nextFunction, return: returnFunction }) { + const result = {}; + if (typeof nextFunction === "function") { + result.next = nextFunction; + } + if (typeof returnFunction === "function") { + result.return = returnFunction; + } + result[symbolAsyncIterator] = function () { + return this; + }; + + return result; +} + +function setTimeoutPromise(after = 1, value, options = {}) { + const arguments_ = [].concat(value ?? []); + try { + validateObject(options, "options"); + } catch (error) { + return Promise.reject(error); + } + const { signal, ref: reference = true } = options; + try { + validateAbortSignal(signal, "options.signal"); + } catch (error) { + return Promise.reject(error); + } + try { + validateBoolean(reference, "options.ref"); + } catch (error) { + return Promise.reject(error); + } + if (signal?.aborted) { + return Promise.reject(new AbortError()); + } + let onCancel; + const returnValue = new Promise((resolve, reject) => { + const timeout = setTimeout(() => resolve(value), after, ...arguments_); + if (!reference) { + timeout?.unref?.(); + } + if (signal) { + onCancel = () => { + clearTimeout(timeout); + reject(new AbortError()); + }; + signal.addEventListener("abort", onCancel); + } + }); + if (typeof onCancel !== "undefined") { + returnValue.finally(() => signal.removeEventListener("abort", onCancel)); + } + return returnValue; +} + +function setImmediatePromise(value, options = {}) { + try { + validateObject(options, "options"); + } catch (error) { + return Promise.reject(error); + } + const { signal, ref: reference = true } = options; + try { + validateAbortSignal(signal, "options.signal"); + } catch (error) { + return Promise.reject(error); + } + try { + validateBoolean(reference, "options.ref"); + } catch (error) { + return Promise.reject(error); + } + if (signal?.aborted) { + return Promise.reject(new AbortError()); + } + let onCancel; + const returnValue = new Promise((resolve, reject) => { + const immediate = setImmediate(() => resolve(value)); + if (!reference) { + immediate?.unref?.(); + } + if (signal) { + onCancel = () => { + clearImmediate(immediate); + reject(new AbortError()); + }; + signal.addEventListener("abort", onCancel); + } + }); + if (typeof onCancel !== "undefined") { + returnValue.finally(() => signal.removeEventListener("abort", onCancel)); + } + return returnValue; +} + +function setIntervalPromise(after = 1, value, options = {}) { + /* eslint-disable no-undefined, no-unreachable-loop, no-loop-func */ + try { + validateObject(options, "options"); + } catch (error) { + return asyncIterator({ + next: function () { + return Promise.reject(error); + }, + }); + } + const { signal, ref: reference = true } = options; + try { + validateAbortSignal(signal, "options.signal"); + } catch (error) { + return asyncIterator({ + next: function () { + return Promise.reject(error); + }, + }); + } + try { + validateBoolean(reference, "options.ref"); + } catch (error) { + return asyncIterator({ + next: function () { + return Promise.reject(error); + }, + }); + } + if (signal?.aborted) { + return asyncIterator({ + next: function () { + return Promise.reject(new AbortError()); + }, + }); + } + + let onCancel, interval; + + try { + let notYielded = 0; + let callback; + interval = setInterval(() => { + notYielded++; + if (callback) { + callback(); + callback = undefined; + } + }, after); + if (!reference) { + interval?.unref?.(); + } + if (signal) { + onCancel = () => { + clearInterval(interval); + if (callback) { + callback(); + callback = undefined; + } + }; + signal.addEventListener("abort", onCancel); + } + + return asyncIterator({ + next: function () { + return new Promise((resolve, reject) => { + if (!signal?.aborted) { + if (notYielded === 0) { + callback = resolve; + } else { + resolve(); + } + } else if (notYielded === 0) { + reject(new AbortError()); + } else { + resolve(); + } + }).then(() => { + if (notYielded > 0) { + notYielded = notYielded - 1; + return { done: false, value: value }; + } + return { done: true }; + }); + }, + return: function () { + clearInterval(interval); + signal?.removeEventListener("abort", onCancel); + return Promise.resolve({}); + }, + }); + } catch (error) { + return asyncIterator({ + next: function () { + clearInterval(interval); + signal?.removeEventListener("abort", onCancel); + }, + }); + } +} + +export { + setTimeoutPromise as setTimeout, + setImmediatePromise as setImmediate, + setIntervalPromise as setInterval, +}; diff --git a/src/javascript/jsc/undici.exports.js b/src/javascript/jsc/undici.exports.js new file mode 100644 index 000000000..70d7ebcae --- /dev/null +++ b/src/javascript/jsc/undici.exports.js @@ -0,0 +1,104 @@ +export var fetch = Bun.fetch; +export var Response = globalThis.Response; +export var Headers = globalThis.Headers; +export var Request = globalThis.Request; +export var URLSearchParams = globalThis.URLSearchParams; +export var URL = globalThis.URL; +export class File extends Blob {} +export class FileReader extends EventTarget { + constructor() { + throw new Error("Not implemented yet!"); + } +} + +export class FormData { + constructor() { + throw new Error("Not implemented yet!"); + } +} +function notImplemented() { + throw new Error("Not implemented in bun"); +} +export function request() { + throw new Error("Not implemented in bun"); +} +export function stream() { + throw new Error("Not implemented in bun"); +} +export function pipeline() { + throw new Error("Not implemented in bun"); +} +export function connect() { + throw new Error("Not implemented in bun"); +} +export function upgrade() { + throw new Error("Not implemented in bun"); +} + +export class MockClient { + constructor() { + throw new Error("Not implemented in bun"); + } +} +export class MockPool { + constructor() { + throw new Error("Not implemented in bun"); + } +} +export class MockAgent { + constructor() { + throw new Error("Not implemented in bun"); + } +} + +export function mockErrors() { + throw new Error("Not implemented in bun"); +} + +export function Undici() { + throw new Error("Not implemented in bun"); +} + +Undici.Dispatcher = + Undici.Pool = + Undici.BalancedPool = + Undici.Client = + Undici.buildConnector = + Undici.errors = + Undici.Agent = + Undici.setGlobalDispatcher = + Undici.getGlobalDispatcher = + Undici.request = + Undici.stream = + Undici.pipeline = + Undici.connect = + Undici.upgrade = + Undici.MockClient = + Undici.MockPool = + Undici.MockAgent = + Undici.mockErrors = + notImplemented; + +Undici.fetch = fetch; + +export default { + fetch, + Response, + Headers, + Request, + URLSearchParams, + URL, + File, + FileReader, + FormData, + request, + stream, + pipeline, + connect, + upgrade, + MockClient, + MockPool, + MockAgent, + mockErrors, + Undici, +}; |