GM Fetch

A fetch API of GM_xmlhttpRequest

이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greatest.deepsurf.us/scripts/472236/1811775/GM%20Fetch.js을(를) 사용하여 포함하는 라이브러리입니다.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         GM Fetch
// @namespace    https://github.com/Sec-ant
// @version      1.2.4
// @author       Ze-Zheng Wu
// @description  A fetch API for GM_xmlhttpRequest / GM.xmlHttpRequest
// @license      MIT
// @homepage     https://github.com/Sec-ant/gm-fetch
// @homepageURL  https://github.com/Sec-ant/gm-fetch
// @source       https://github.com/Sec-ant/gm-fetch.git
// @supportURL   https://github.com/Sec-ant/gm-fetch/issues
// @downloadURL  https://fastly.jsdelivr.net/npm/@sec-ant/gm-fetch@latest/dist/gm-fetch.user.js
// @updateURL    https://fastly.jsdelivr.net/npm/@sec-ant/gm-fetch@latest/dist/gm-fetch.meta.js
// @match        *://*/*
// @grant        GM.xmlHttpRequest
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
	( __commonJSMin(((exports, module) => {
		(function(global, factory) {
			typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define([], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.gmFetch = factory());
		})(exports, function() {
			var _GM = typeof GM != "undefined" ? GM : void 0;
			var _GM_xmlhttpRequest = typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0;
			function parseHeaders(rawHeaders) {
				const headers = new Headers();
				const preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, " ");
				for (const line of preProcessedHeaders.split(/\r?\n/)) {
					const parts = line.split(":");
					const key = parts.shift()?.trim();
					if (key) {
						const value = parts.join(":").trim();
						try {
							headers.append(key, value);
						} catch (error) {
							console.warn(`Response ${error.message}`);
						}
					}
				}
				return headers;
			}
			var gmFetch = async (input, init) => {
				const gmXhr = _GM_xmlhttpRequest || _GM.xmlHttpRequest;
				if (typeof gmXhr !== "function") throw new DOMException("GM_xmlhttpRequest or GM.xmlHttpRequest is not granted.", "NotFoundError");
				const request = new Request(input, init);
				if (request.signal.aborted) throw new DOMException("Network request aborted.", "AbortError");
				const data = await request.blob();
				const headers = Object.fromEntries(request.headers);
				new Headers(init?.headers).forEach((value, key) => {
					headers[key] = value;
				});
				return new Promise((resolve, reject) => {
					let settled = false;
					const responseBlobPromise = new Promise((resolveBlob) => {
						const { abort } = gmXhr({
							method: request.method.toUpperCase(),
							url: request.url || location.href,
							headers,
							data: data.size ? data : void 0,
							redirect: request.redirect,
							binary: true,
							nocache: request.cache === "no-store",
							revalidate: request.cache === "reload",
							timeout: 3e5,
							responseType: gmXhr.RESPONSE_TYPE_STREAM ?? "blob",
							overrideMimeType: request.headers.get("Content-Type") ?? void 0,
							anonymous: request.credentials === "omit",
							onload: ({ response: responseBody }) => {
								if (settled) {
									resolveBlob(null);
									return;
								}
								resolveBlob(responseBody);
							},
							async onreadystatechange({ readyState, responseHeaders, status, statusText, finalUrl, response: responseBody }) {
								if (readyState === XMLHttpRequest.DONE) request.signal.removeEventListener("abort", abort);
								else if (readyState !== XMLHttpRequest.HEADERS_RECEIVED) return;
								if (settled) {
									resolveBlob(null);
									return;
								}
								const parsedHeaders = parseHeaders(responseHeaders);
								const redirected = request.url !== finalUrl;
								const response = new Response(responseBody instanceof ReadableStream ? responseBody : await responseBlobPromise, {
									headers: parsedHeaders,
									status,
									statusText
								});
								Object.defineProperties(response, {
									url: { value: finalUrl },
									type: { value: "basic" },
									...response.redirected !== redirected ? { redirected: { value: redirected } } : {},
									...parsedHeaders.has("set-cookie") || parsedHeaders.has("set-cookie2") ? { headers: { value: parsedHeaders } } : {}
								});
								resolve(response);
								settled = true;
							},
							onerror: ({ statusText, error }) => {
								reject(new TypeError(statusText || error || "Network request failed."));
								resolveBlob(null);
							},
							ontimeout() {
								reject( new TypeError("Network request timeout."));
								resolveBlob(null);
							},
							onabort() {
								reject(new DOMException("Network request aborted.", "AbortError"));
								resolveBlob(null);
							}
						});
						request.signal.addEventListener("abort", abort);
					});
				});
			};
			return gmFetch;
		});
	})))();
})();