diff --git a/config/metadata.cjs b/config/metadata.cjs index 2eb6b68..e8c002f 100644 --- a/config/metadata.cjs +++ b/config/metadata.cjs @@ -14,6 +14,8 @@ module.exports = { ], require: [ 'https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js', + `https://cdn.jsdelivr.net/npm/axios@${pkg.dependencies.axios}/dist/axios.min.js`, + `https://cdn.jsdelivr.net/npm/axios-userscript-adapter@${pkg.dependencies['axios-userscript-adapter']}/dist/axiosGmxhrAdapter.min.js` ], grant: [ 'GM.xmlHttpRequest', diff --git a/package-lock.json b/package-lock.json index 6eed2ef..eb59a75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,14 @@ { "name": "3cx-tapi", - "version": "9.2.0", + "version": "9.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "3cx-tapi", - "version": "9.2.0", + "version": "9.1.1", "dependencies": { + "@trim21/gm-fetch": "^0.1.15", "chrono-node": "^2.7.7" }, "devDependencies": { @@ -1863,6 +1864,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@trim21/gm-fetch": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/@trim21/gm-fetch/-/gm-fetch-0.1.15.tgz", + "integrity": "sha512-197WkYDd1XY8eDNWMBWSrAV77kCJzvh8EOnKSgIoHDXTb1AFLDN1iFnK/Y2x4XTOUDdOfUQd25rKUlVGWmQYhQ==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", diff --git a/package.json b/package.json index 4743ea4..a42aeb4 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ }, "private": true, "dependencies": { - "chrono-node": "^2.7.7" + "chrono-node": "^2.7.7", + "@trim21/gm-fetch": "^0.1.15" }, "devDependencies": { "@types/greasemonkey": "^4.0.7", diff --git a/src/call-history.ts b/src/call-history.ts index 98e416a..8f43c11 100644 --- a/src/call-history.ts +++ b/src/call-history.ts @@ -1,7 +1,7 @@ import * as chrono from 'chrono-node' import { TapiContact } from './tapi-contact' import { extractNumber } from './utils' -import GM_fetch from './gm-fetch' +import GM_fetch from '@trim21/gm-fetch' export class CallHistory { private callerIds: { [number: string]: TapiContact } = {} diff --git a/src/call-notification.ts b/src/call-notification.ts index 3b16348..c6358e2 100644 --- a/src/call-notification.ts +++ b/src/call-notification.ts @@ -1,4 +1,4 @@ -import GM_fetch from './gm-fetch' +import GM_fetch from '@trim21/gm-fetch' import { TapiContact } from './tapi-contact' import { extractNumber } from './utils' diff --git a/src/gm-fetch/index.ts b/src/gm-fetch/index.ts deleted file mode 100644 index 27da486..0000000 --- a/src/gm-fetch/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { parseGMResponse } from "./utils"; - -export default async function GM_fetch(input: RequestInfo | URL, init?: RequestInit): Promise { - 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 { - 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) { - resolve(parseGMResponse(request, res)); - }, - 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(array: ReadonlyArray, 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}`); -} diff --git a/src/gm-fetch/utils.ts b/src/gm-fetch/utils.ts deleted file mode 100644 index 7e3d25f..0000000 --- a/src/gm-fetch/utils.ts +++ /dev/null @@ -1,145 +0,0 @@ -export function parseRawHeaders(h: string): Headers { - const s = h.trim(); - if (!s) { - return new Headers(); - } - - const array: [string, string][] = s.split("\r\n").map((value) => { - let s = value.split(":"); - return [s[0].trim(), s[1].trim()]; - }); - - return new Headers(array); -} - -export function parseGMResponse(req: Request, res: GM.Response): Response { - return new ResImpl(res.response as Blob, { - statusCode: res.status, - statusText: res.statusText, - headers: parseRawHeaders(res.responseHeaders), - finalUrl: res.finalUrl, - redirected: res.finalUrl === req.url, - }); -} - -interface ResInit { - redirected: boolean; - headers: Headers; - statusCode: number; - statusText: string; - finalUrl: string; -} - -class ResImpl implements Response { - private _bodyUsed: boolean; - private readonly rawBody: Blob; - private readonly init: ResInit; - - readonly body: ReadableStream | null; - readonly headers: Headers; - readonly redirected: boolean; - readonly status: number; - readonly statusText: string; - readonly type: ResponseType; - readonly url: string; - - constructor(body: Blob, init: ResInit) { - this.rawBody = body; - this.init = init; - - //this.body = toReadableStream(body); - const { headers, statusCode, statusText, finalUrl, redirected } = init; - this.headers = headers; - this.status = statusCode; - this.statusText = statusText; - this.url = finalUrl; - this.type = "basic"; - this.redirected = redirected; - this._bodyUsed = false; - } - - get bodyUsed(): boolean { - return this._bodyUsed; - } - - get ok(): boolean { - return this.status < 300; - } - - arrayBuffer(): Promise { - if (this.bodyUsed) { - throw new TypeError("Failed to execute 'arrayBuffer' on 'Response': body stream already read"); - } - this._bodyUsed = true; - return this.rawBody.arrayBuffer(); - } - - blob(): Promise { - if (this.bodyUsed) { - throw new TypeError("Failed to execute 'blob' on 'Response': body stream already read"); - } - this._bodyUsed = true; - // `slice` will use empty string as default value, so need to pass all arguments. - return Promise.resolve(this.rawBody.slice(0, this.rawBody.size, this.rawBody.type)); - } - - clone(): Response { - if (this.bodyUsed) { - throw new TypeError("Failed to execute 'clone' on 'Response': body stream already read"); - } - return new ResImpl(this.rawBody, this.init); - } - - formData(): Promise { - if (this.bodyUsed) { - throw new TypeError("Failed to execute 'formData' on 'Response': body stream already read"); - } - this._bodyUsed = true; - return this.rawBody.text().then(decode); - } - - async json(): Promise { - if (this.bodyUsed) { - throw new TypeError("Failed to execute 'json' on 'Response': body stream already read"); - } - this._bodyUsed = true; - return JSON.parse(await this.rawBody.text()); - } - - text(): Promise { - if (this.bodyUsed) { - throw new TypeError("Failed to execute 'text' on 'Response': body stream already read"); - } - this._bodyUsed = true; - return this.rawBody.text(); - } -} - -function decode(body: string) { - const form = new FormData(); - - body - .trim() - .split("&") - .forEach(function (bytes) { - if (bytes) { - const split = bytes.split("="); - const name = split.shift()?.replace(/\+/g, " "); - const value = split.join("=").replace(/\+/g, " "); - form.append(decodeURIComponent(name!), decodeURIComponent(value)); - } - }); - - return form; -} - -/* -function toReadableStream(value: Blob) { - return new ReadableStream({ - start(controller) { - controller.enqueue(value); - controller.close(); - }, - }); -} -*/ diff --git a/src/search.ts b/src/search.ts index a399be2..f056826 100644 --- a/src/search.ts +++ b/src/search.ts @@ -2,7 +2,7 @@ import './search.css' import { TapiContact } from './tapi-contact' import { debounce } from './debounce' import { fireChangeEvents } from './utils' -import GM_fetch from './gm-fetch' +import GM_fetch from '@trim21/gm-fetch' export class Search { private currentSearchText = '' diff --git a/src/status.ts b/src/status.ts index 457c43d..02c5812 100644 --- a/src/status.ts +++ b/src/status.ts @@ -1,6 +1,6 @@ -import GM_fetch from './gm-fetch'; import './status.css'; import { ZcStatus } from './zc-status'; +import GM_fetch from "@trim21/gm-fetch"; declare function waitForKeyElements(selectorOrFunction: any, callback: any, waitOnce: boolean): any;