GM Requests

Imitating Python's Requests library based on GM_xmlHttpRequest.

目前為 2023-07-03 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/470000/1214515/GM%20Requests.js

// ==UserScript==
// @name        GM Requests
// @description Imitating Python's Requests library based on GM_xmlHttpRequest.
// @version     0.0.2
// @author      大碗宽面wtw
// @homepage    https://github.com/bigbowl-wtw/gm-requests/
// @supportURL  https://github.com/bigbowl-wtw/gm-requests/issues
// @match       *://*/*
// @grant       GM_xmlhttpRequest
// @license     MIT
// @namespace   com.github.bigbowl-wtw
// ==/UserScript==

(function webpackUniversalModuleDefinition(root, factory) {
    if (typeof exports === "object" && typeof module === "object") module.exports = factory(); else if (typeof define === "function" && define.amd) define([], factory); else if (typeof exports === "object") exports["requests"] = factory(); else root["requests"] = factory();
})(self, (() => (() => {
    "use strict";
    var __webpack_require__ = {};
    (() => {
        __webpack_require__.d = (exports, definition) => {
            for (var key in definition) {
                if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
                    Object.defineProperty(exports, key, {
                        enumerable: true,
                        get: definition[key]
                    });
                }
            }
        };
    })();
    (() => {
        __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
    })();
    (() => {
        __webpack_require__.r = exports => {
            if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, {
                    value: "Module"
                });
            }
            Object.defineProperty(exports, "__esModule", {
                value: true
            });
        };
    })();
    var __webpack_exports__ = {};
    __webpack_require__.r(__webpack_exports__);
    __webpack_require__.d(__webpack_exports__, {
        Session: () => Session,
        get: () => get,
        post: () => post,
        session: () => session
    });
    var ErrorStatus;
    (function(ErrorStatus) {
        ErrorStatus[ErrorStatus["BadRequest"] = 400] = "BadRequest";
        ErrorStatus[ErrorStatus["Unauthorized"] = 401] = "Unauthorized";
        ErrorStatus[ErrorStatus["Forbidden"] = 403] = "Forbidden";
        ErrorStatus[ErrorStatus["NotFound"] = 404] = "NotFound";
        ErrorStatus[ErrorStatus["MethodNotAllowed"] = 405] = "MethodNotAllowed";
        ErrorStatus[ErrorStatus["RequestTimeout"] = 408] = "RequestTimeout";
        ErrorStatus[ErrorStatus["TooManyRequests"] = 429] = "TooManyRequests";
        ErrorStatus[ErrorStatus["InternalServerError"] = 500] = "InternalServerError";
        ErrorStatus[ErrorStatus["BadGateway"] = 502] = "BadGateway";
        ErrorStatus[ErrorStatus["ServiceUnavailable"] = 503] = "ServiceUnavailable";
    })(ErrorStatus || (ErrorStatus = {}));
    function utils_assign(target, ...rest) {
        const __assign = Object.assign || function(t, ...others) {
            for (let s, i = 0, n = others.length; i < n; i++) {
                s = others[i];
                for (const p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
            }
            return t;
        };
        return __assign.call(this, target, ...rest);
    }
    function assignDeepCopy(target, ...rest) {
        rest = rest.filter((r => r !== undefined));
        utils_assign(target, ...rest);
        return utils_assign.call(this, target, ...JSON.parse(JSON.stringify(rest)));
    }
    function bodyToString(data) {
        if (Object.values(data).some((v => typeof v.toString !== "function"))) throw new TypeError("value must has `.toString`");
        return Object.entries(data).map((([key, value]) => `${key}=${encodeURIComponent(value.toString())}`)).join("&");
    }
    class SimpleCookieJar {
        get empty() {
            return !Object.keys(this.cookies).length;
        }
        constructor(cookies) {
            var _a;
            this.never = new Set;
            this.setCookies((_a = cookies) !== null && _a !== void 0 ? _a : {});
        }
        update(cookies) {
            utils_assign(this.cookies, this.normalizeCookie(cookies));
        }
        updateFromString(cookieStringArray) {
            if (typeof cookieStringArray === "string") cookieStringArray = [ cookieStringArray ];
            this.update(cookieStringArray);
        }
        differenceUpdate(cookies) {
            let cookieEntries;
            if (Array.isArray(cookies)) cookieEntries = cookies.map(this.stringToEntry); else cookieEntries = Object.entries(isCookieJar(cookies) ? cookies.cookies : cookies);
            const set = new Set(Object.keys(this.cookies));
            cookieEntries = cookieEntries.filter((([name]) => !set.has(name)));
            this.update(Object.fromEntries(cookieEntries));
        }
        deleteFromString(cookieStringArray) {
            if (typeof cookieStringArray === "string") cookieStringArray = [ cookieStringArray ];
            const names = cookieStringArray.map(this.stringToEntry).map((([name]) => name));
            names.forEach((name => {
                this.never.add(name);
                delete this.cookies[name];
            }));
        }
        setCookies(cookies) {
            this.cookies = new Proxy(this.normalizeCookie(cookies), {
                set: (target, name, value) => {
                    if (this.never.has(name)) return true;
                    target[name] = value;
                    return true;
                }
            });
        }
        toString() {
            return Object.entries(this.cookies).map((([k, v]) => `${k}=${v}`)).join(";");
        }
        parseCookieString(cookieStrings) {
            return Object.fromEntries(cookieStrings.map(this.stringToEntry));
        }
        normalizeCookie(cookies) {
            if (Array.isArray(cookies)) return this.parseCookieString(cookies);
            if (isCookieJar(cookies)) return cookies.cookies;
            return cookies;
        }
        stringToEntry(cookieString) {
            return cookieString.slice(0, cookieString.indexOf(";")).split("=");
        }
    }
    function isCookieJar(obj) {
        return typeof obj.update === "function";
    }
    var __rest = undefined && undefined.__rest || function(s, e) {
        var t = {};
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
        if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
        }
        return t;
    };
    class Header {
        constructor(headers) {
            this.cookies = new SimpleCookieJar;
            if (!headers) {
                this.headers = {};
                return;
            }
            const {cookie} = headers, others = __rest(headers, [ "cookie" ]);
            this.headers = others;
            if (cookie) {
                this.cookies.update(cookie);
            }
        }
        setFromString(headerString) {
            const entrise = headerString.split("\r\n").map((kv => kv.split(":"))).map((([k, v]) => [ k.toLowerCase(), v.trim() ]));
            for (const [name, value] of entrise) {
                const header = this.headers[name];
                if (header) {
                    if (typeof header === "string") this.headers[name] = [ header ];
                    this.headers[name].push(value);
                } else {
                    this.headers[name] = value;
                }
            }
            return this;
        }
        update(headers) {
            if (isIHeader(headers)) {
                this.cookies.update(headers.cookies);
                this.update(headers.headers);
            } else {
                const _a = headers !== null && headers !== void 0 ? headers : {}, {cookie} = _a, others = __rest(_a, [ "cookie" ]);
                utils_assign(this.headers, others);
                if (cookie) {
                    this.cookies.update(cookie);
                }
            }
        }
        getHeaders() {
            if (!Object.keys(this.headers).length) {
                if (this.cookies.empty) return {};
                return {
                    cookie: this.cookies.toString()
                };
            }
            const headers = {};
            for (const [name, value] of Object.entries(this.headers)) headers[name] = value.toString();
            return Object.assign(Object.assign({}, headers), {
                cookie: this.cookies.toString()
            });
        }
        append(header, value) {
            if (header === "cookie") this.cookies.update([ header ]); else if (!this[header]) this[header] = value; else if (!Array.isArray(header)) this[header] = [ value ].concat(this[header]); else this[header].concat(value);
        }
        get(name) {
            return this.headers[name];
        }
    }
    function isIHeader(obj) {
        return typeof obj.update === "function";
    }
    var __awaiter = undefined && undefined.__awaiter || function(thisArg, _arguments, P, generator) {
        function adopt(value) {
            return value instanceof P ? value : new P((function(resolve) {
                resolve(value);
            }));
        }
        return new (P || (P = Promise))((function(resolve, reject) {
            function fulfilled(value) {
                try {
                    step(generator.next(value));
                } catch (e) {
                    reject(e);
                }
            }
            function rejected(value) {
                try {
                    step(generator["throw"](value));
                } catch (e) {
                    reject(e);
                }
            }
            function step(result) {
                result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
            }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        }));
    };
    var session_rest = undefined && undefined.__rest || function(s, e) {
        var t = {};
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
        if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
        }
        return t;
    };
    class Details {
        constructor(url, method, options) {
            this.pendings = [];
            this.url = url;
            this.method = method;
            this.finalHeader = new Header;
            if (options) {
                const {query, json, data, cookie, auth, headers} = options, others = session_rest(options, [ "query", "json", "data", "cookie", "auth", "headers" ]);
                this.query = query;
                this.json = json;
                this.data = data;
                this.cookie = cookie;
                this.auth = auth;
                this.headers = headers;
                this.others = others;
            }
        }
        build(session) {
            return __awaiter(this, void 0, void 0, (function*() {
                this.buildURL().buildHeaderAndCookie(session).buildAuth(session);
                this.pendings.concat(session.buildHooks.map((hook => hook.call(this, session))));
                return this.toDetails();
            }));
        }
        buildURL() {
            this.parseQuery(this.query);
            return this;
        }
        buildHeaderAndCookie(session) {
            const finalCookie = this.finalHeader.cookies;
            if (!session.cookies.empty) finalCookie.update(session.cookies);
            if (this.cookie) finalCookie.update(this.cookie);
            this.finalHeader.update(session.headers);
            if (this.headers) this.finalHeader.update(this.headers);
            return this;
        }
        buildAuth(session) {
            var _a;
            this.pendings.push((_a = session.auth) === null || _a === void 0 ? void 0 : _a.build(this.finalHeader));
            return this;
        }
        buildBody() {
            if (this.json) {
                const contentType = "application/json";
                this.finalHeader.update({
                    "Content-Type": contentType
                });
                const data = JSON.stringify(this.json);
                this.finalData = data;
                return this;
            }
            if (this.data) {
                if (typeof this.data === "string") {
                    this.finalData = this.data;
                    return this;
                }
                if (this.data instanceof FormData) return this;
                const contentType = "application/x-www-form-urlencoded";
                this.finalHeader.update({
                    "Content-Type": contentType
                });
                this.finalData = bodyToString(this.data);
                return this;
            }
            return this;
        }
        toDetails() {
            return __awaiter(this, void 0, void 0, (function*() {
                yield Promise.all(this.pendings);
                const url = this.url.toString();
                this.buildBody();
                const details = Object.assign({
                    url,
                    method: this.method,
                    headers: this.finalHeader.getHeaders()
                }, this.others);
                if (this.finalData) details.data = this.finalData;
                return details;
            }));
        }
        parseQuery(query) {
            let url;
            if (typeof this.url === "string") url = new URL(this.url); else url = this.url;
            if (query) Object.entries(query).filter((([_, v]) => typeof v === "string")).forEach((([k, v]) => url.searchParams.append(k, v.toString())));
            this.url = url;
        }
    }
    class Session {
        constructor() {
            this.buildHooks = [];
            const headers = new Header;
            Object.defineProperty(this, "headers", {
                get: () => headers,
                set(value) {
                    headers.update(value);
                }
            });
            Object.defineProperty(this, "cookies", {
                get: () => headers.cookies,
                set(value) {
                    headers.cookies.setCookies(value);
                }
            });
        }
        get(url, options) {
            return __awaiter(this, void 0, void 0, (function*() {
                return this.request("GET", url, options);
            }));
        }
        post(url, options) {
            return __awaiter(this, void 0, void 0, (function*() {
                return this.request("POST", url, options);
            }));
        }
        request(method, url, options) {
            return __awaiter(this, void 0, void 0, (function*() {
                const details = new Details(url, method, options);
                return new Promise(((resolve, reject) => {
                    if (!options.onload) {
                        details.others.onload = resp => {
                            if (!this.cookies.empty) {
                                const respHeaders = (new Header).setFromString(resp.responseHeaders);
                                this.cookies.deleteFromString(respHeaders["set-cookie"]);
                            }
                            if (resp.status === 200 && !(resp.status in ErrorStatus)) resolve((options === null || options === void 0 ? void 0 : options.responseType) ? resp.response : resp); else reject(resp);
                        };
                    }
                    if (!options.onerror) details.others.onerror = resp => reject(resp);
                    details.build(this).then((dtl => GM_xmlhttpRequest(dtl)));
                }));
            }));
        }
        registerBuildHook(hook) {
            this.buildHooks.push(hook);
        }
    }
    function get(url, query, options) {
        return (new Session).get(url, Object.assign({
            query
        }, options));
    }
    function post(url, options) {
        return (new Session).post(url, Object.assign({}, options));
    }
    function session() {
        return new Session;
    }
    return __webpack_exports__;
})()));