2025/10/5 GM_fetch v0.1.13 yosider (not used)
結局いらなかった
2025/10/5 GM_fetch v0.1.13 yosider (not used)
code:user.js
import { GM_fetch } from "./mod.ts";
unsafeWindow.GM_fetch = GM_fetch;
code:mod.ts
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
const parseHeaders = (rawHeaders: string) =>
new Headers(
rawHeaders
// Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
.replace(/\r?\n\t +/g, " ") .split(/\r\n|\r|\n/)
.flatMap((header) => {
const key, value = header.split(":").map((text) => text.trim()); if (!key) return [];
})
);
/** CORSを突破してfetchする
*
* interfaceはfetchと同一
*/
export const GM_fetch = (
input: RequestInfo | URL,
init?: RequestInit
): Promise<Response> =>
new Promise((resolve, reject) => {
const headers = Object.fromEntries(
new Headers(
input instanceof Request ? input.headers : init?.headers
).entries()
);
if (input instanceof Request) {
headers.Referer = input.referrer;
}
if (init?.referrer) {
headers.Referer = init.referrer;
}
if (init?.referrerPolicy) {
}
const request = new Request(input, init);
if (request.signal?.aborted) {
reject(new DOMException("Aborted", "AbortError"));
return;
}
const { abort } = GM_xmlhttpRequest({
// @ts-ignore 多分変な文字列は入ってこないはず
method: request.method,
url: request.url,
headers,
// await request.text() でも同じことができるが、二度手間なのでinitから直接取り出す
...(init?.body ? { data: init.body } : {}),
anonymous: request.credentials === "omit",
fetch: true,
responseType: "arraybuffer",
onerror: () => {
reject(new TypeError("Network request failed"));
},
ontimeout: () => {
reject(new TypeError("Network request timeout"));
},
onabort: () => {
reject(new DOMException("Aborted", "AbortError"));
},
// readyState 4 (DONE) でResponseを返すことで、bodyが完全に読み込まれてからPromiseを解決する
onreadystatechange: (res) => {
switch (res.readyState) {
case 4: {
const response = new Response(res.response, {
status: res.status,
statusText: res.statusText,
headers: parseHeaders(res.responseHeaders),
});
// urlはconstructor経由で設定できない
Object.defineProperty(response, "url", { value: res.finalUrl });
resolve(response);
request.signal?.removeEventListener?.("abort", abort);
break;
}
default:
break;
}
},
});
request.signal?.addEventListener?.("abort", abort);
});
code:scrapbox.user.js
// ==UserScript==
// @name GM_fetch
// @version 0.1.13-fix
// @description An implementation of the fetch API which leverages GM_xmlhttpRequest
// @author yosider
// @connect *
// @grant GM_xmlhttpRequest
// @license MIT
// @copyright Copyright (c) 2025 yosider
// ==/UserScript==
var l=r=>new Headers(r.replace(/\r?\n\t +/g," ").split(/\r\n|\r|\n/).flatMap(e=>{letn,s=e.split(":").map(t=>t.trim());return n?n,s:[]})),c=(r,e)=>new Promise((n,s)=>{let t=Object.fromEntries(new Headers(r instanceof Request?r.headers:e?.headers).entries());r instanceof Request&&(t.Referer=r.referrer,t"Referrer-Policy"=r.referrerPolicy),e?.referrer&&(t.Referer=e.referrer),e?.referrerPolicy&&(t"Referrer-Policy"=e.referrerPolicy);let o=new Request(r,e);if(o.signal?.aborted){s(new DOMException("Aborted","AbortError"));return}let{abort:f}=GM_xmlhttpRequest({method:o.method,url:o.url,headers:t,...e?.body?{data:e.body}:{},anonymous:o.credentials==="omit",fetch:!0,responseType:"arraybuffer",onerror:()=>{s(new TypeError("Network request failed"))},ontimeout:()=>{s(new TypeError("Network request timeout"))},onabort:()=>{s(new DOMException("Aborted","AbortError"))},onreadystatechange:a=>{switch(a.readyState){case 4:{let d=new Response(a.response,{status:a.status,statusText:a.statusText,headers:l(a.responseHeaders)});Object.defineProperty(d,"url",{value:a.finalUrl}),n(d),o.signal?.removeEventListener?.("abort",f);break}default:break}}});o.signal?.addEventListener?.("abort",f)});unsafeWindow.GM_fetch=c;