Skip to content

Instantly share code, notes, and snippets.

@karthik2804
Created March 10, 2026 15:03
Show Gist options
  • Select an option

  • Save karthik2804/322d4358138442929f82bad590f4a3d1 to your computer and use it in GitHub Desktop.

Select an option

Save karthik2804/322d4358138442929f82bad590f4a3d1 to your computer and use it in GitHub Desktop.
Spin JS `response.clone` patch
const OriginalResponse = globalThis.Response;
interface PatchedResponseReadOnlyProps {
url?: string;
redirected?: boolean;
type?: ResponseType;
}
class PatchedResponse extends OriginalResponse {
private _clonableStream: BodyInit | null;
private _init: ResponseInit | undefined;
private _readOnlyProps: PatchedResponseReadOnlyProps;
constructor(body: BodyInit | null, init?: ResponseInit, readOnlyProps?: PatchedResponseReadOnlyProps) {
// 1. If body is a stream, tee it immediately
if (body instanceof ReadableStream) {
const [stream1, stream2]: [ReadableStream, ReadableStream] = body.tee();
super(stream1, init);
this._clonableStream = stream2;
} else {
super(body, init);
this._clonableStream = body;
}
this._init = init;
this._readOnlyProps = readOnlyProps ?? {};
// Forward read-only properties that can't be set via ResponseInit
if (readOnlyProps) {
const descriptors: PropertyDescriptorMap = {};
if (readOnlyProps.url !== undefined) {
descriptors.url = { value: readOnlyProps.url, enumerable: true, configurable: true };
}
if (readOnlyProps.redirected !== undefined) {
descriptors.redirected = { value: readOnlyProps.redirected, enumerable: true, configurable: true };
}
if (readOnlyProps.type !== undefined) {
descriptors.type = { value: readOnlyProps.type, enumerable: true, configurable: true };
}
Object.defineProperties(this, descriptors);
}
}
clone(): PatchedResponse {
// 2. When clone() is called, create a new branch from the saved stream
if (this._clonableStream instanceof ReadableStream) {
const [s1, s2]: [ReadableStream, ReadableStream] = this._clonableStream.tee();
this._clonableStream = s1; // Keep one for the next possible clone()
return new PatchedResponse(s2, this._init, this._readOnlyProps);
}
// For non-stream bodies (strings, blobs), just pass it through
return new PatchedResponse(this._clonableStream, this._init, this._readOnlyProps);
}
}
// 3. Overwrite the global constructor
globalThis.Response = PatchedResponse as unknown as typeof Response;
const originalFetch = globalThis.fetch;
globalThis.fetch = async (...args: Parameters<typeof fetch>): Promise<Response> => {
const res = await originalFetch(...args);
return new PatchedResponse(
res.body,
{
status: res.status,
statusText: res.statusText,
headers: new Headers(res.headers),
},
{
url: res.url,
redirected: res.redirected,
type: res.type,
},
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment