Polyfills for undici, streams/web, streams/consumer timers timers/promises, fs/promises, ws

This commit is contained in:
Jarred Sumner
2022-06-22 06:42:39 -07:00
committed by Jarred Sumner
parent d9f6a3f2d2
commit 2cdbd2de83
6 changed files with 502 additions and 0 deletions

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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,
};

View File

@@ -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,
};