aboutsummaryrefslogtreecommitdiff
path: root/src/js/thirdparty/node-fetch.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/thirdparty/node-fetch.ts')
-rw-r--r--src/js/thirdparty/node-fetch.ts97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/js/thirdparty/node-fetch.ts b/src/js/thirdparty/node-fetch.ts
new file mode 100644
index 000000000..794bdf601
--- /dev/null
+++ b/src/js/thirdparty/node-fetch.ts
@@ -0,0 +1,97 @@
+import type * as s from "stream";
+
+const { Headers, Request, Response: WebResponse, Blob, File = Blob, FormData } = globalThis as any;
+const nativeFetch = Bun.fetch;
+
+const { Readable } = require("node:stream");
+
+class Response extends WebResponse {
+ _body: any;
+
+ get body() {
+ return this._body ?? (this._body = Readable.fromWeb(super.body));
+ }
+}
+
+/**
+ * `node-fetch` works like the browser-fetch API, except it's a little more strict on some features,
+ * and uses node streams instead of web streams.
+ *
+ * It's overall a positive on speed to override the implementation, since most people will use something
+ * like `.json()` or `.text()`, which is faster in Bun's native fetch, vs `node-fetch` going
+ * through `node:http`, a node stream, then processing the data.
+ */
+async function fetch(url: any, init?: RequestInit & { body?: any }) {
+ // input node stream -> web stream
+ let body: s.Readable | undefined = init?.body;
+ if (body) {
+ const chunks: any = [];
+ if (body instanceof Readable) {
+ // TODO: Bun fetch() doesn't support ReadableStream at all.
+ for await (const chunk of body) {
+ chunks.push(chunk);
+ }
+ init = { ...init, body: new Blob(chunks) };
+ }
+ }
+
+ const response = await nativeFetch(url, init);
+ Object.setPrototypeOf(response, Response.prototype);
+ return response;
+}
+
+class AbortError extends DOMException {
+ constructor(message) {
+ super(message, "AbortError");
+ }
+}
+
+class FetchBaseError extends Error {
+ type: string;
+
+ constructor(message, type) {
+ super(message);
+ this.type = type;
+ }
+}
+
+class FetchError extends FetchBaseError {
+ constructor(message, type, systemError) {
+ super(message, type);
+ this.code = systemError?.code;
+ }
+}
+
+function blobFrom(path, options) {
+ return Promise.resolve(Bun.file(path, options));
+}
+
+function blobFromSync(path, options) {
+ return Bun.file(path, options);
+}
+
+var fileFrom = blobFrom;
+var fileFromSync = blobFromSync;
+
+function isRedirect(code) {
+ return code === 301 || code === 302 || code === 303 || code === 307 || code === 308;
+}
+
+export default Object.assign(fetch, {
+ AbortError,
+ Blob,
+ FetchBaseError,
+ FetchError,
+ File,
+ FormData,
+ Headers,
+ Request,
+ Response,
+ blobFrom,
+ blobFromSync,
+ fileFrom,
+ fileFromSync,
+ isRedirect,
+ fetch,
+ default: fetch,
+});