diff --git a/src/bun.js/bindings/NodeFetch.cpp b/src/bun.js/bindings/NodeFetch.cpp new file mode 100644 index 0000000000..1b25af677d --- /dev/null +++ b/src/bun.js/bindings/NodeFetch.cpp @@ -0,0 +1,50 @@ +#include "root.h" +#include "JSDOMGlobalObjectInlines.h" +#include "ZigGlobalObject.h" + +#include "JSFetchHeaders.h" +#include "JSDOMFormData.h" +#include "JavaScriptCore/ObjectConstructor.h" + +#include "helpers.h" +#include "BunClientData.h" + +#include "JavaScriptCore/AggregateError.h" +#include "JavaScriptCore/JSFunction.h" +#include "JSDOMFile.h" + +namespace Bun { + +using namespace JSC; +using namespace WebCore; + +// Ensure overriding globals doesn't impact usages. +JSC::JSValue createNodeFetchInternalBinding(Zig::GlobalObject* globalObject) +{ + auto& vm = globalObject->vm(); + + auto* obj = constructEmptyObject(globalObject); + obj->putDirectIndex( + globalObject, 0, + globalObject->JSResponseConstructor()); + obj->putDirectIndex( + globalObject, 1, + globalObject->JSRequestConstructor()); + obj->putDirectIndex( + globalObject, 2, + globalObject->JSBlobConstructor()); + obj->putDirectIndex( + globalObject, 3, + WebCore::JSFetchHeaders::getConstructor(vm, globalObject)); + + obj->putDirectIndex( + globalObject, 4, + WebCore::JSDOMFormData::getConstructor(vm, globalObject)); + obj->putDirectIndex( + globalObject, 5, + globalObject->JSDOMFileConstructor()); + + return obj; +} + +} \ No newline at end of file diff --git a/src/bun.js/bindings/NodeFetch.h b/src/bun.js/bindings/NodeFetch.h new file mode 100644 index 0000000000..2b20c2d0be --- /dev/null +++ b/src/bun.js/bindings/NodeFetch.h @@ -0,0 +1,7 @@ +#include "config.h" + +namespace Bun { + +JSC::JSValue createNodeFetchInternalBinding(Zig::GlobalObject*); + +} \ No newline at end of file diff --git a/src/js/thirdparty/node-fetch.ts b/src/js/thirdparty/node-fetch.ts index 788ee6600f..d2b5831690 100644 --- a/src/js/thirdparty/node-fetch.ts +++ b/src/js/thirdparty/node-fetch.ts @@ -1,13 +1,13 @@ import type * as s from "stream"; -const { - Headers: WebHeaders, - Request: WebRequest, - Response: WebResponse, - Blob, - File = Blob, - FormData, -} = globalThis as any; +// Users may override the global fetch implementation, so we need to ensure these are the originals. +const bindings = $cpp("NodeFetch.cpp", "createNodeFetchInternalBinding"); +const WebResponse: typeof globalThis.Response = bindings[0]; +const WebRequest: typeof globalThis.Request = bindings[1]; +const Blob: typeof globalThis.Blob = bindings[2]; +const WebHeaders: typeof globalThis.Headers = bindings[3]; +const FormData: typeof globalThis.FormData = bindings[4]; +const File: typeof globalThis.File = bindings[5]; const nativeFetch = Bun.fetch; // node-fetch extends from URLSearchParams in their implementation... diff --git a/test/bun.lockb b/test/bun.lockb index b3cdc08715..b2294918f4 100755 Binary files a/test/bun.lockb and b/test/bun.lockb differ diff --git a/test/js/node/http/node-fetch-primordials.test.ts b/test/js/node/http/node-fetch-primordials.test.ts new file mode 100644 index 0000000000..de674023c3 --- /dev/null +++ b/test/js/node/http/node-fetch-primordials.test.ts @@ -0,0 +1,20 @@ +import { test, expect } from "bun:test"; + +test("fetch, Response, Request can be overriden", async () => { + const { Response, Request } = globalThis; + globalThis.Response = class BadResponse {}; + globalThis.Request = class BadRequest {}; + globalThis.fetch = function badFetch() {}; + + const fetch = require("node-fetch").fetch; + + using server = Bun.serve({ + port: 0, + async fetch(req) { + return new Response("Hello, World!"); + }, + }); + + const response = await fetch(server.url); + expect(response).toBeInstanceOf(Response); +}); diff --git a/test/package.json b/test/package.json index 3008ded2ec..e0a9a275a3 100644 --- a/test/package.json +++ b/test/package.json @@ -61,5 +61,8 @@ "private": true, "scripts": { "typecheck": "tsc --noEmit" + }, + "resolutions": { + "react": "../node_modules/react" } }