aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-06-22 06:42:39 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-06-22 06:56:47 -0700
commit2cdbd2de83981aa0e0bc26db5b5ebd261507f112 (patch)
treec27008292b846217a0e2e3f3571f2fa2205f2472
parentd9f6a3f2d26750b5a1c6c3a10b0eab0f37a7c19c (diff)
downloadbun-2cdbd2de83981aa0e0bc26db5b5ebd261507f112.tar.gz
bun-2cdbd2de83981aa0e0bc26db5b5ebd261507f112.tar.zst
bun-2cdbd2de83981aa0e0bc26db5b5ebd261507f112.zip
Polyfills for `undici`, `streams/web`, `streams/consumer` `timers` `timers/promises`, `fs/promises`, `ws`
-rw-r--r--src/javascript/jsc/fs_promises.exports.js108
-rw-r--r--src/javascript/jsc/node_streams_consumer.exports.js10
-rw-r--r--src/javascript/jsc/node_streams_web.exports.js18
-rw-r--r--src/javascript/jsc/node_timers.exports.js20
-rw-r--r--src/javascript/jsc/node_timers_promises.exports.js242
-rw-r--r--src/javascript/jsc/undici.exports.js104
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,
+};