@trim21/gm-fetch
Version:
fetch for UserScript
60 lines (50 loc) • 1.74 kB
text/typescript
import { parseGMResponse } from "./utils";
export default async function GM_fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
const request = new Request(input, init);
let data: string | undefined;
if (init?.body) {
data = await request.text();
}
return await XHR(request, init, data);
}
function XHR(request: Request, init: RequestInit | undefined, data: string | undefined): Promise<Response> {
return new Promise((resolve, reject) => {
if (request.signal && request.signal.aborted) {
return reject(new DOMException("Aborted", "AbortError"));
}
GM.xmlHttpRequest({
url: request.url,
method: gmXHRMethod(request.method.toUpperCase()),
headers: Object.fromEntries(new Headers(init?.headers).entries()),
data: data,
responseType: "blob",
onload(res) {
try {
resolve(parseGMResponse(request, res));
} catch (e) {
reject(e);
}
},
onabort() {
reject(new DOMException("Aborted", "AbortError"));
},
ontimeout() {
reject(new TypeError("Network request failed, timeout"));
},
onerror(err) {
reject(new TypeError("Failed to fetch: " + err.finalUrl));
},
});
});
}
const httpMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "TRACE", "OPTIONS", "CONNECT"] as const;
// a ts type helper to narrow type
function includes<T extends U, U>(array: ReadonlyArray<T>, element: U): element is T {
return array.includes(element as T);
}
function gmXHRMethod(method: string): (typeof httpMethods)[number] {
if (includes(httpMethods, method)) {
return method;
}
throw new Error(`unsupported http method ${method}`);
}