diff --git a/.changeset/global-fetch-instanceof.md b/.changeset/global-fetch-instanceof.md
new file mode 100644
index 00000000000..b9dff67555e
--- /dev/null
+++ b/.changeset/global-fetch-instanceof.md
@@ -0,0 +1,5 @@
+---
+"@remix-run/node": patch
+---
+
+ensures fetch() return is instanceof global Response by removing extended classes for NodeRequest and NodeResponse in favor of custom interface type cast.
diff --git a/contributors.yml b/contributors.yml
index 91a7e07bbdd..ae7de85f83d 100644
--- a/contributors.yml
+++ b/contributors.yml
@@ -230,6 +230,7 @@
- jaydiablo
- jca41
- jdeniau
+- jeeyoungk
- JeffBeltran
- jenseng
- jeremyjfleming
diff --git a/integration/fetch-globals-test.ts b/integration/fetch-globals-test.ts
new file mode 100644
index 00000000000..172602c8f3a
--- /dev/null
+++ b/integration/fetch-globals-test.ts
@@ -0,0 +1,39 @@
+import { test, expect } from "@playwright/test";
+
+import type { Fixture, AppFixture } from "./helpers/create-fixture";
+import { createAppFixture, createFixture, js } from "./helpers/create-fixture";
+
+let fixture: Fixture;
+let appFixture: AppFixture;
+
+test.beforeAll(async () => {
+ fixture = await createFixture({
+ files: {
+ "app/routes/_index.tsx": js`
+ import { json } from "@remix-run/node";
+ import { useLoaderData } from "@remix-run/react";
+ export async function loader() {
+ const resp = await fetch('https://reqres.in/api/users?page=2');
+ return (resp instanceof Response) ? 'is an instance of global Response' : 'is not an instance of global Response';
+ }
+ export default function Index() {
+ let data = useLoaderData();
+ return (
+
+ {data}
+
+ )
+ }
+ `,
+ },
+ });
+
+ appFixture = await createAppFixture(fixture);
+});
+
+test.afterAll(async () => appFixture.close());
+
+test("returned variable from fetch() should be instance of global Response", async () => {
+ let response = await fixture.requestDocument("/");
+ expect(await response.text()).toMatch("is an instance of global Response");
+});
diff --git a/packages/remix-node/__tests__/fetch-test.ts b/packages/remix-node/__tests__/fetch-test.ts
index 6f7c8480662..e8fac2dffc6 100644
--- a/packages/remix-node/__tests__/fetch-test.ts
+++ b/packages/remix-node/__tests__/fetch-test.ts
@@ -1,6 +1,10 @@
import { ReadableStream } from "@remix-run/web-stream";
+import {
+ Request as WebRequest,
+ Response as WebResponse,
+} from "@remix-run/web-fetch";
-import { Request } from "../fetch";
+import { Request, Response } from "../fetch";
let test = {
source: [
@@ -111,6 +115,22 @@ describe("Request", () => {
expect(file.size).toBe(1023);
expect(cloned instanceof Request).toBeTruthy();
+ expect(cloned instanceof WebRequest).toBeTruthy();
+ });
+
+ it("instanceOf", async () => {
+ let nodeReq = new Request("http://example.com");
+ let webReq = new WebRequest("http://example.com");
+ let nodeRes = new Response("http://example.com");
+ let webRes = new WebResponse("http://example.com");
+ expect(nodeReq instanceof Request).toBeTruthy();
+ expect(nodeReq instanceof WebRequest).toBeTruthy();
+ expect(webReq instanceof Request).toBeTruthy();
+ expect(webReq instanceof WebRequest).toBeTruthy();
+ expect(nodeRes instanceof Response).toBeTruthy();
+ expect(nodeRes instanceof WebResponse).toBeTruthy();
+ expect(webRes instanceof Response).toBeTruthy();
+ expect(webRes instanceof WebResponse).toBeTruthy();
});
});
diff --git a/packages/remix-node/fetch.ts b/packages/remix-node/fetch.ts
index b4182a055e7..1047e998ab4 100644
--- a/packages/remix-node/fetch.ts
+++ b/packages/remix-node/fetch.ts
@@ -9,6 +9,7 @@ export { FormData } from "@remix-run/web-fetch";
export { File, Blob } from "@remix-run/web-file";
type NodeHeadersInit = ConstructorParameters[0];
+type NodeResponseInfo = ConstructorParameters[0];
type NodeResponseInit = NonNullable<
ConstructorParameters[1]
>;
@@ -31,30 +32,27 @@ export type {
NodeResponseInit as ResponseInit,
};
-class NodeRequest extends WebRequest {
- constructor(info: NodeRequestInfo, init?: NodeRequestInit) {
- super(info, init as RequestInit);
- }
+interface NodeRequest extends WebRequest {
+ get headers(): WebHeaders;
- public get headers(): WebHeaders {
- return super.headers as WebHeaders;
- }
-
- public clone(): NodeRequest {
- return new NodeRequest(this);
- }
+ clone(): NodeRequest;
}
-class NodeResponse extends WebResponse {
- public get headers(): WebHeaders {
- return super.headers as WebHeaders;
- }
+interface NodeResponse extends WebResponse {
+ get headers(): WebHeaders;
- public clone(): NodeResponse {
- return super.clone() as NodeResponse;
- }
+ clone(): NodeResponse;
}
+const NodeRequest = WebRequest as new (
+ info: NodeRequestInfo,
+ init?: NodeRequestInit
+) => NodeRequest;
+const NodeResponse = WebResponse as unknown as new (
+ info: NodeResponseInfo,
+ init?: NodeResponseInit
+) => NodeResponse;
+
export {
WebHeaders as Headers,
NodeRequest as Request,