From b85c4d9e12df5174a32650b18f0913a3067f7a7b Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Mon, 9 Sep 2024 00:36:43 +0800 Subject: [PATCH 001/148] fix: correct handling of legacy binary-string --- src/ext/background/main.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ext/background/main.js b/src/ext/background/main.js index f584ed5a..21b8a249 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -420,7 +420,11 @@ async function handleMessage(message, sender) { // deprecate once body supports more data types // the `binary` key will no longer needed if (typeof body === "string" && details.binary) { - body = new TextEncoder().encode(body); + const arr = new Uint8Array(body.length); + for (let i = 0; i < body.length; i++) { + arr[i] = body.charCodeAt(i); + } + body = arr; } // xhr instances automatically filter out unexpected user values xhr.timeout = details.timeout; From fb71afab828866e5c2d7fc650f63e46fd0c7d121 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:16:07 +0800 Subject: [PATCH 002/148] refactor: add xhr transport data types definition --- jsconfig.json | 2 +- src/dev/jsconfig.json | 26 ++++++++++++++++++++++++++ src/ext/types.d.ts | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/dev/jsconfig.json create mode 100644 src/ext/types.d.ts diff --git a/jsconfig.json b/jsconfig.json index 4c9004b8..8f85baf3 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -22,5 +22,5 @@ "skipLibCheck": true, "sourceMap": true }, - "include": ["*.js", "src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] + "include": ["*.d.ts", "*.js"] } diff --git a/src/dev/jsconfig.json b/src/dev/jsconfig.json new file mode 100644 index 00000000..899038cd --- /dev/null +++ b/src/dev/jsconfig.json @@ -0,0 +1,26 @@ +// https://code.visualstudio.com/docs/languages/jsconfig +// https://www.typescriptlang.org/docs/handbook/tsconfig-json.html +// https://www.typescriptlang.org/tsconfig + +// https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html +// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html + +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + + "verbatimModuleSyntax": true, + "isolatedModules": true, + "resolveJsonModule": true, + + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "sourceMap": true + }, + "include": ["**/*.d.ts", "**/*.js", "**/*.svelte"] +} diff --git a/src/ext/types.d.ts b/src/ext/types.d.ts new file mode 100644 index 00000000..149f3ac3 --- /dev/null +++ b/src/ext/types.d.ts @@ -0,0 +1,34 @@ +declare namespace TypeExtMessages { + interface XHRProcessedFile { + data: Array; + lastModified: number; + name: string; + mime: string; + } + + type XHRProcessedFormData = [string, string | XHRProcessedFile][]; + + type XHRProcessedData = + | { + data: string; + type: "Text" | "URLSearchParams"; + } + | { + data: string; + mime: string; + type: "Document"; + } + | { + data: Array; + type: "ArrayBuffer" | "ArrayBufferView"; + } + | { + data: Array; + mime: string; + type: "Blob"; + } + | { + data: XHRProcessedFormData; + type: "FormData"; + }; +} From 6717dd6103ba7a9b9ad300ded0c061be1c379058 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:28:35 +0800 Subject: [PATCH 003/148] feat: asynchronized `GM.xmlHttpRequest` api `GM.xmlHttpRequest` return a promise resolved with response object --- src/ext/content-scripts/api.js | 42 +++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index 0988c063..d0286e27 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -108,9 +108,17 @@ async function setClipboard(clipboardData, type) { }); } -function xhr(details) { +/** + * @param {Object} details + * @param {Object} control + * @param {{resolve: Function, reject: Function}=} promise + * @returns {Promise} + */ +async function xhr(details, control, promise) { if (details == null) return console.error("xhr invalid details arg"); if (!details.url) return console.error("xhr details missing url key"); + // define control method, will be replaced after port is established + control.abort = () => console.error("xhr has not yet been initialized"); // generate random port name for single xhr const xhrPortName = Math.random().toString(36).substring(1, 9); // strip out functions from details @@ -125,10 +133,6 @@ function xhr(details) { for (const e of events) { if (typeof details[e] === "function") detailsParsed[e] = true; } - // define return method, will be populated after port is established - const response = { - abort: () => console.error("xhr has not yet been initialized"), - }; /** * port listener, most of the messaging logic goes here * @type {Parameters[0]} @@ -209,8 +213,10 @@ function xhr(details) { details[msg.name](msg.response); } // all messages received - // tell background it's safe to close port - if (msg.name === "onloadend") { + if (msg.handler === "onloadend") { + // resolving asynchronous xmlHttpRequest + promise && promise.resolve(msg.response); + // tell background it's safe to close port port.postMessage({ name: "DISCONNECT" }); } }); @@ -222,7 +228,7 @@ function xhr(details) { browser.runtime.onConnect.removeListener(listener); }); // fill the method returned to the user script - response.abort = () => port.postMessage({ name: "ABORT" }); + control.abort = () => port.postMessage({ name: "ABORT" }); }; // wait for the background to establish a port connection browser.runtime.onConnect.addListener(listener); @@ -234,7 +240,21 @@ function xhr(details) { events, }; sendMessageProxy(message); - return response; +} + +function xmlHttpRequest(details) { + let promise; + const control = new Promise((resolve, reject) => { + promise = { resolve, reject }; + }); + xhr(details, control, promise); + return control; +} + +function GM_xmlhttpRequest(details) { + const control = {}; + xhr(details, control); + return control; } export default { @@ -251,6 +271,6 @@ export default { // notification, // registerMenuCommand, // getResourceUrl, - xmlHttpRequest: xhr, - GM_xmlhttpRequest: xhr, + xmlHttpRequest, + GM_xmlhttpRequest, }; From 5fb7df5a615ea75fb8f8a8d665a9ca7f37cc567d Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:35:48 +0800 Subject: [PATCH 004/148] refactor: separate xhr response data processor --- src/ext/content-scripts/api.js | 125 +++++++++++++++++---------------- 1 file changed, 65 insertions(+), 60 deletions(-) diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index d0286e27..b194cfc2 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -108,6 +108,66 @@ async function setClipboard(clipboardData, type) { }); } +function xhrResponseProcessor(response) { + const res = response; + /** + * only include responseXML when needed + * NOTE: Only add implementation at this time, not enable, to avoid + * unnecessary calculations, and this legacy default behavior is not + * recommended, users should explicitly use `responseType: "document"` + * to obtain it. + if (res.responseType === "" && typeof res.response === "string") { + const mimeTypes = [ + "text/xml", + "application/xml", + "application/xhtml+xml", + "image/svg+xml", + ]; + for (const mimeType of mimeTypes) { + if (res.contentType.includes(mimeType)) { + const parser = new DOMParser(); + res.responseXML = parser.parseFromString(res.response, "text/xml"); + break; + } + } + } + */ + if (res.responseType === "arraybuffer" && Array.isArray(res.response)) { + // arraybuffer responses had their data converted in background + // convert it back to arraybuffer + try { + res.response = new Uint8Array(res.response).buffer; + } catch (err) { + console.error("error parsing xhr arraybuffer", err); + } + } + if (res.responseType === "blob" && Array.isArray(res.response)) { + // blob responses had their data converted in background + // convert it back to blob + try { + const typedArray = new Uint8Array(res.response); + const type = res.contentType ?? ""; + res.response = new Blob([typedArray], { type }); + } catch (err) { + console.error("error parsing xhr blob", err); + } + } + if (res.responseType === "document" && typeof res.response === "string") { + // document responses had their data converted in background + // convert it back to document + try { + const parser = new DOMParser(); + const mimeType = res.contentType.includes("text/html") + ? "text/html" + : "text/xml"; + res.response = parser.parseFromString(res.response, mimeType); + res.responseXML = res.response; + } catch (err) { + console.error("error parsing xhr document", err); + } + } +} + /** * @param {Object} details * @param {Object} control @@ -145,69 +205,14 @@ async function xhr(details, control, promise) { typeof details[msg.name] === "function" ) { // process xhr response - const r = msg.response; + const response = msg.response; // only include responseText when needed - if (["", "text"].includes(r.responseType)) { - r.responseText = r.response; - } - /** - * only include responseXML when needed - * NOTE: Only add implementation at this time, not enable, to avoid - * unnecessary calculations, and this legacy default behavior is not - * recommended, users should explicitly use `responseType: "document"` - * to obtain it. - if (r.responseType === "") { - const mimeTypes = [ - "text/xml", - "application/xml", - "application/xhtml+xml", - "image/svg+xml", - ]; - for (const mimeType of mimeTypes) { - if (r.contentType.includes(mimeType)) { - const parser = new DOMParser(); - r.responseXML = parser.parseFromString(r.response, "text/xml"); - break; - } - } + if (["", "text"].includes(response.responseType)) { + response.responseText = response.response; } - */ // only process when xhr is complete and data exist - if (r.readyState === 4 && r.response !== null) { - if (r.responseType === "arraybuffer" && Array.isArray(r.response)) { - // arraybuffer responses had their data converted in background - // convert it back to arraybuffer - try { - r.response = new Uint8Array(r.response).buffer; - } catch (err) { - console.error("error parsing xhr arraybuffer", err); - } - } - if (r.responseType === "blob" && Array.isArray(r.response)) { - // blob responses had their data converted in background - // convert it back to blob - try { - const typedArray = new Uint8Array(r.response); - const type = r.contentType ?? ""; - r.response = new Blob([typedArray], { type }); - } catch (err) { - console.error("error parsing xhr blob", err); - } - } - if (r.responseType === "document" && typeof r.response === "string") { - // document responses had their data converted in background - // convert it back to document - try { - const parser = new DOMParser(); - const mimeType = r.contentType.includes("text/html") - ? "text/html" - : "text/xml"; - r.response = parser.parseFromString(r.response, mimeType); - r.responseXML = r.response; - } catch (err) { - console.error("error parsing xhr document", err); - } - } + if (response.readyState === 4 && response.response !== null) { + xhrResponseProcessor(response); } // call userscript method details[msg.name](msg.response); From 79cb8752e3272d75403cf956544cf7316f5fbb17 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:53:27 +0800 Subject: [PATCH 005/148] perf: reduce loops and the amount of data transferred --- src/ext/background/main.js | 192 +++++++++++++++++---------------- src/ext/content-scripts/api.js | 20 ++-- 2 files changed, 107 insertions(+), 105 deletions(-) diff --git a/src/ext/background/main.js b/src/ext/background/main.js index 21b8a249..6cabf70a 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -392,106 +392,110 @@ async function handleMessage(message, sender) { return { status: "fulfilled", result }; } case "API_XHR": { - // initializing an xhr instance - const xhr = new XMLHttpRequest(); - // establish a long-lived port connection to content script - const port = browser.tabs.connect(sender.tab.id, { - name: message.xhrPortName, - }); - // receive messages from content script and process them - port.onMessage.addListener((msg) => { - if (msg.name === "ABORT") xhr.abort(); - if (msg.name === "DISCONNECT") port.disconnect(); - }); - // handle port disconnect and clean tasks - port.onDisconnect.addListener((p) => { - if (p?.error) { - console.error( - `port disconnected due to an error: ${p.error.message}`, - ); + try { + // initializing an xhr instance + const xhr = new XMLHttpRequest(); + // establish a long-lived port connection to content script + const port = browser.tabs.connect(sender.tab.id, { + name: message.xhrPortName, + }); + // receive messages from content script and process them + port.onMessage.addListener((msg) => { + if (msg.name === "ABORT") xhr.abort(); + if (msg.name === "DISCONNECT") port.disconnect(); + }); + // handle port disconnect and clean tasks + port.onDisconnect.addListener((p) => { + if (p?.error) { + console.error( + `port disconnected due to an error: ${p.error.message}`, + ); + } + }); + // parse details and set up for xhr instance + const details = message.details; + const method = details.method || "GET"; + const user = details.user || null; + const password = details.password || null; + let body = details.data || null; + // deprecate once body supports more data types + // the `binary` key will no longer needed + if (typeof body === "string" && details.binary) { + const arr = new Uint8Array(body.length); + for (let i = 0; i < body.length; i++) { + arr[i] = body.charCodeAt(i); + } + body = arr; } - }); - // parse details and set up for xhr instance - const details = message.details; - const method = details.method || "GET"; - const user = details.user || null; - const password = details.password || null; - let body = details.data || null; - // deprecate once body supports more data types - // the `binary` key will no longer needed - if (typeof body === "string" && details.binary) { - const arr = new Uint8Array(body.length); - for (let i = 0; i < body.length; i++) { - arr[i] = body.charCodeAt(i); + // xhr instances automatically filter out unexpected user values + xhr.timeout = details.timeout; + xhr.responseType = details.responseType; + // record parsed values for subsequent use + const responseType = xhr.responseType; + // avoid unexpected behavior of legacy defaults such as parsing XML + if (responseType === "") xhr.responseType = "text"; + // transfer to content script via arraybuffer and then parse to blob + if (responseType === "blob") xhr.responseType = "arraybuffer"; + // transfer to content script via text and then parse to document + if (responseType === "document") xhr.responseType = "text"; + // add required listeners and send result back to the content script + const handlers = details.handlers || {}; + for (const handler of Object.keys(handlers)) { + xhr[handler] = async () => { + // can not send xhr through postMessage + // construct new object to be sent as "response" + const response = { + contentType: undefined, // non-standard + readyState: xhr.readyState, + response: xhr.response, + responseHeaders: xhr.getAllResponseHeaders(), + responseType, + responseURL: xhr.responseURL, + status: xhr.status, + statusText: xhr.statusText, + timeout: xhr.timeout, + }; + // get content-type when headers received + if (xhr.readyState >= xhr.HEADERS_RECEIVED) { + response.contentType = xhr.getResponseHeader("Content-Type"); + } + // only process when xhr is complete and data exist + if (xhr.readyState === xhr.DONE && xhr.response !== null) { + // need to convert arraybuffer data to postMessage + if ( + xhr.responseType === "arraybuffer" && + xhr.response instanceof ArrayBuffer + ) { + const buffer = xhr.response; + response.response = Array.from(new Uint8Array(buffer)); + } + } + port.postMessage({ handler, response }); + }; } - body = arr; - } - // xhr instances automatically filter out unexpected user values - xhr.timeout = details.timeout; - xhr.responseType = details.responseType; - // record parsed values for subsequent use - const responseType = xhr.responseType; - // avoid unexpected behavior of legacy defaults such as parsing XML - if (responseType === "") xhr.responseType = "text"; - // transfer to content script via arraybuffer and then parse to blob - if (responseType === "blob") xhr.responseType = "arraybuffer"; - // transfer to content script via text and then parse to document - if (responseType === "document") xhr.responseType = "text"; - // add required listeners and send result back to the content script - for (const e of message.events) { - if (!details[e]) continue; - xhr[e] = async (event) => { - // can not send xhr through postMessage - // construct new object to be sent as "response" - const x = { - contentType: undefined, // non-standard - readyState: xhr.readyState, - response: xhr.response, - responseHeaders: xhr.getAllResponseHeaders(), - responseType, - responseURL: xhr.responseURL, - status: xhr.status, - statusText: xhr.statusText, - timeout: xhr.timeout, + // if onloadend not set in xhr details + // onloadend event won't be passed to content script + // if that happens port DISCONNECT message won't be posted + // if details lacks onloadend attach listener + if (!handlers.onloadend) { + xhr.onloadend = () => { + port.postMessage({ handler: "onloadend" }); }; - // get content-type when headers received - if (xhr.readyState >= xhr.HEADERS_RECEIVED) { - x.contentType = xhr.getResponseHeader("Content-Type"); - } - // only process when xhr is complete and data exist - if (xhr.readyState === xhr.DONE && xhr.response !== null) { - // need to convert arraybuffer data to postMessage - if ( - xhr.responseType === "arraybuffer" && - xhr.response instanceof ArrayBuffer - ) { - const buffer = xhr.response; - x.response = Array.from(new Uint8Array(buffer)); - } + } + if (details.overrideMimeType) { + xhr.overrideMimeType(details.overrideMimeType); + } + xhr.open(method, details.url, true, user, password); + // must set headers after `xhr.open()`, but before `xhr.send()` + if (typeof details.headers === "object") { + for (const [key, val] of Object.entries(details.headers)) { + xhr.setRequestHeader(key, val); } - port.postMessage({ name: e, event, response: x }); - }; - } - // if onloadend not set in xhr details - // onloadend event won't be passed to content script - // if that happens port DISCONNECT message won't be posted - // if details lacks onloadend attach listener - if (!details.onloadend) { - xhr.onloadend = (event) => { - port.postMessage({ name: "onloadend", event }); - }; - } - if (details.overrideMimeType) { - xhr.overrideMimeType(details.overrideMimeType); - } - xhr.open(method, details.url, true, user, password); - // must set headers after `xhr.open()`, but before `xhr.send()` - if (typeof details.headers === "object") { - for (const [key, val] of Object.entries(details.headers)) { - xhr.setRequestHeader(key, val); } + xhr.send(body); + } catch (error) { + console.error(error); } - xhr.send(body); return { status: "fulfilled" }; } case "REFRESH_DNR_RULES": { diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index b194cfc2..675b4e8e 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -184,14 +184,13 @@ async function xhr(details, control, promise) { // strip out functions from details const detailsParsed = JSON.parse(JSON.stringify(details)); // get all the "on" events from XMLHttpRequest object - const events = []; for (const k in XMLHttpRequest.prototype) { - if (k.slice(0, 2) === "on") events.push(k); - } - // check which functions are included in the original details object - // add a bool to indicate if event listeners should be attached - for (const e of events) { - if (typeof details[e] === "function") detailsParsed[e] = true; + if (k.slice(0, 2) !== "on") continue; + // check which handlers are included in the original details object + if (typeof details[k] === "function") { + // add a bool to indicate if event listeners should be attached + detailsParsed.handlers[k] = true; + } } /** * port listener, most of the messaging logic goes here @@ -201,8 +200,8 @@ async function xhr(details, control, promise) { if (port.name !== xhrPortName) return; port.onMessage.addListener(async (msg) => { if ( - events.includes(msg.name) && - typeof details[msg.name] === "function" + detailsParsed.handlers[msg.handler] && + typeof details[msg.handler] === "function" ) { // process xhr response const response = msg.response; @@ -215,7 +214,7 @@ async function xhr(details, control, promise) { xhrResponseProcessor(response); } // call userscript method - details[msg.name](msg.response); + details[msg.handler](response); } // all messages received if (msg.handler === "onloadend") { @@ -242,7 +241,6 @@ async function xhr(details, control, promise) { name: "API_XHR", details: detailsParsed, xhrPortName, - events, }; sendMessageProxy(message); } From 013268ec859ba59c98e382954b77d9a303f7da14 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:08:49 +0800 Subject: [PATCH 006/148] feat: supports more data types to comply with xhr `send()` method --- src/ext/background/main.js | 75 +++++++++++++++++--- src/ext/content-scripts/api.js | 124 +++++++++++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 13 deletions(-) diff --git a/src/ext/background/main.js b/src/ext/background/main.js index 6cabf70a..7e79716a 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -414,18 +414,75 @@ async function handleMessage(message, sender) { }); // parse details and set up for xhr instance const details = message.details; + /** @type {Parameters[0]} */ const method = details.method || "GET"; + /** @type {Parameters[1]} */ + const url = details.url; + /** @type {Parameters[3]} */ const user = details.user || null; + /** @type {Parameters[4]} */ const password = details.password || null; - let body = details.data || null; - // deprecate once body supports more data types - // the `binary` key will no longer needed - if (typeof body === "string" && details.binary) { - const arr = new Uint8Array(body.length); - for (let i = 0; i < body.length; i++) { - arr[i] = body.charCodeAt(i); + /** @type {Parameters[0]} */ + let body = null; + if (typeof details.data === "object") { + /** @type {TypeExtMessages.XHRProcessedData} */ + const data = details.data; + if (typeof data.data === "string") { + if (data.type === "Text") { + // deprecate once body supports more data types + // the `binary` key will no longer needed + if (details.binary) { + const binaryString = data.data; + const view = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + view[i] = binaryString.charCodeAt(i); + } + body = view; + } else { + body = data.data; + } + } + if (data.type === "Document") { + body = data.data; + if (!("content-type" in details.headers)) { + details.headers["content-type"] = data.mime; + } + } + if (data.type === "URLSearchParams") { + body = new URLSearchParams(data.data); + } + } + if (Array.isArray(data.data)) { + if ( + data.type === "ArrayBuffer" || + data.type === "ArrayBufferView" + ) { + body = new Uint8Array(data.data); + } + if (data.type === "Blob") { + body = new Uint8Array(data.data); + if (!("content-type" in details.headers)) { + details.headers["content-type"] = data.mime; + } + } + if (data.type === "FormData") { + body = new FormData(); + for (const [k, v] of data.data) { + if (typeof v === "string") { + body.append(k, v); + } else { + const view = new Uint8Array(v.data); + body.append( + k, + new File([view], v.name, { + type: v.mime, + lastModified: v.lastModified, + }), + ); + } + } + } } - body = arr; } // xhr instances automatically filter out unexpected user values xhr.timeout = details.timeout; @@ -485,7 +542,7 @@ async function handleMessage(message, sender) { if (details.overrideMimeType) { xhr.overrideMimeType(details.overrideMimeType); } - xhr.open(method, details.url, true, user, password); + xhr.open(method, url, true, user, password); // must set headers after `xhr.open()`, but before `xhr.send()` if (typeof details.headers === "object") { for (const [key, val] of Object.entries(details.headers)) { diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index 675b4e8e..4a319968 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -168,6 +168,96 @@ function xhrResponseProcessor(response) { } } +/** + * Process data into a transportable object + * @param {Parameters[0]} data + * @returns {Promise} + */ +async function xhrDataProcessor(data) { + if (typeof data === "undefined") return undefined; + if (typeof data === "string") { + return { data, type: "Text" }; + } + if (data instanceof Document) { + if (data instanceof XMLDocument) { + try { + return { + data: new XMLSerializer().serializeToString(data), + type: "Document", + mime: data.contentType || "text/xml", + }; + } catch (error) { + console.error( + "XML serialization failed, the data will be omitted", + error, + ); + } + } else { + let html = data.documentElement.outerHTML; + if (data.doctype) { + html = `` + html; + } + return { + data: html, + type: "Document", + mime: data.contentType || "text/html", + }; + } + } + if (data instanceof Blob) { + try { + const buffer = await data.arrayBuffer(); + return { + data: Array.from(new Uint8Array(buffer)), + type: "Blob", + mime: data.type, + }; + } catch (error) { + throw Error("Document serialization failed, the data will be omitted", { + cause: error, + }); + } + } + if (data instanceof ArrayBuffer) { + return { + data: Array.from(new Uint8Array(data)), + type: "ArrayBuffer", + }; + } + if (ArrayBuffer.isView(data)) { + return { + data: Array.from(new Uint8Array(data.buffer)), + type: "ArrayBufferView", + }; + } + if (data instanceof FormData) { + /** @type {TypeExtMessages.XHRProcessedFormData} */ + const entries = []; + for (const [k, v] of data.entries()) { + if (typeof v === "string") { + entries.push([k, v]); + continue; + } else { + const buffer = await v.arrayBuffer(); + entries.push([ + k, + { + data: Array.from(new Uint8Array(buffer)), + lastModified: v.lastModified, + name: v.name, + mime: v.type, + }, + ]); + } + } + return { data: entries, type: "FormData" }; + } + if (data instanceof URLSearchParams) { + return { data: data.toString(), type: "URLSearchParams" }; + } + throw Error("Unexpected data type, the data will be omitted"); +} + /** * @param {Object} details * @param {Object} control @@ -179,10 +269,33 @@ async function xhr(details, control, promise) { if (!details.url) return console.error("xhr details missing url key"); // define control method, will be replaced after port is established control.abort = () => console.error("xhr has not yet been initialized"); - // generate random port name for single xhr - const xhrPortName = Math.random().toString(36).substring(1, 9); - // strip out functions from details - const detailsParsed = JSON.parse(JSON.stringify(details)); + // can not send details (func, blob, etc.) through message + // construct a new processed object send to background page + const detailsParsed = { + binary: details.binary, + data: undefined, + headers: {}, + method: details.method, + overrideMimeType: details.overrideMimeType, + password: details.password, + responseType: details.responseType, + timeout: details.timeout, + url: details.url, + user: details.user, + handlers: {}, + }; + // preprocess data key + try { + detailsParsed.data = await xhrDataProcessor(details.data); + } catch (error) { + console.error(error); + } + // preprocess headers key + if (typeof details.headers === "object") { + for (const [k, v] of Object.entries(details.headers)) { + detailsParsed.headers[k.toLowerCase()] = v; + } + } // get all the "on" events from XMLHttpRequest object for (const k in XMLHttpRequest.prototype) { if (k.slice(0, 2) !== "on") continue; @@ -192,6 +305,8 @@ async function xhr(details, control, promise) { detailsParsed.handlers[k] = true; } } + // generate random port name for single xhr + const xhrPortName = Math.random().toString(36).substring(1, 9); /** * port listener, most of the messaging logic goes here * @type {Parameters[0]} @@ -200,6 +315,7 @@ async function xhr(details, control, promise) { if (port.name !== xhrPortName) return; port.onMessage.addListener(async (msg) => { if ( + msg.response && detailsParsed.handlers[msg.handler] && typeof details[msg.handler] === "function" ) { From 85baf1bdfb2fb223924d6bce71b47f2fbc8819a4 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:11:20 +0800 Subject: [PATCH 007/148] feat: add deprecation notice for `binary` key --- src/ext/content-scripts/api.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index 4a319968..817b7343 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -269,6 +269,13 @@ async function xhr(details, control, promise) { if (!details.url) return console.error("xhr details missing url key"); // define control method, will be replaced after port is established control.abort = () => console.error("xhr has not yet been initialized"); + // depreciation notice + if (details.binary) { + console.warn( + "Please make sure your xhr `data` is a binary-string since you have set the `binary` true, however this legacy format is no longer recommended.", + "The `binary` key is deprecated and will be removed in the future, use binary data objects such as `Blob`, `ArrayBuffer`, `TypedArray`, etc. instead.", + ); + } // can not send details (func, blob, etc.) through message // construct a new processed object send to background page const detailsParsed = { From 6e7b83ade7eb6de8030802478ba6b283a56670f7 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:39:00 +0800 Subject: [PATCH 008/148] docs: update apis description to match changes --- README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8591027c..68aa426d 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,10 @@ Userscripts Safari currently supports the following userscript metadata: - `@noframes` - this key takes no value - prevents code from being injected into nested frames +- `@grant` + - Imperative controls which special [`APIs`](#api) (if any) your script uses, one on each `@grant` line, only those API methods will be provided. + - If you don’t write `@grant`, value `none` will be assumed. + - If you specify `none` and something else, `none` takes precedence. **All userscripts need at least 1 `@match` or `@include` to run!** @@ -194,6 +198,8 @@ Userscripts Safari currently supports the following userscript metadata: Userscripts currently supports the following api methods. All methods are asynchronous unless otherwise noted. Users must `@grant` these methods in order to use them in a userscript. When using API methods, it's only possible to inject into the content script scope due to security concerns. +For API type definitions, please refer to: [`types.d.ts`](https://github.com/userscriptsup/testscripts/blob/363b322208a6733c7e5f4b9d9705957889af6837/userscripts/types.d.ts) + - `GM.addStyle(css)` - `css: String` - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved if succeeds, rejected with error message if fails @@ -258,8 +264,8 @@ Userscripts currently supports the following api methods. All methods are asynch - `headers: Object` - optional - `overrideMimeType: String` - optional - `timeout: Int` - optional - - `binary: Bool` - optional - - `data: String` - optional + - `binary: Bool` - optional (Deprecated, use binary data objects such as `Blob`, `ArrayBuffer`, `TypedArray`, etc. instead.) + - `data: String | Blob | ArrayBuffer | TypedArray | DataView | FormData | URLSearchParams` - optional - `responseType: String` - optional - refer to [`XMLHttpRequests`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) - event handlers: @@ -281,10 +287,17 @@ Userscripts currently supports the following api methods. All methods are asynch - `statusText` - `timeout` - `responseText` (when `responseType` is `text`) + - returns a custom [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) contains an additional property `abort`, resolved with the response object. + - usage: + - `const xhr = GM.xmlHttpRequest({...});` + - `xhr.abort();` to abort the request + - `const response = await xhr;` + - or just: + - `const response = await GM.xmlHttpRequest({...});` +- `GM_xmlhttpRequest(details)` + - Basically the same as `GM.xmlHttpRequest(details)`, except: - returns an object with a single property, `abort`, which is a `Function` - usage: `const foo = GM.xmlHttpRequest({...});` ... `foo.abort();` to abort the request -- `GM_xmlhttpRequest(details)` - - an alias for `GM.xmlHttpRequest`, works exactly the same ## Scripts Directory From 16afc422df4948e14ff26f297bc0c6ee42680c9c Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:16:00 +0800 Subject: [PATCH 009/148] fix: avoid potential thread crashes of parse --- xcode/Ext-Safari/Functions.swift | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/xcode/Ext-Safari/Functions.swift b/xcode/Ext-Safari/Functions.swift index 9d8de01f..5765d11c 100644 --- a/xcode/Ext-Safari/Functions.swift +++ b/xcode/Ext-Safari/Functions.swift @@ -128,6 +128,7 @@ func parse(_ content: String) -> [String: Any]? { let range = NSRange(location: 0, length: content.utf16.count) // return nil/fail if metablock missing guard let match = regex.firstMatch(in: content, options: [], range: range) else { + logger?.debug("\(#function, privacy: .public) - Non matched content: \(content, privacy: .public)") return nil } @@ -136,15 +137,23 @@ func parse(_ content: String) -> [String: Any]? { // rather than being too strict, text content can precede the opening userscript tag, however it will be ignored // adjust start index of file content while assigning group numbers to account for any text content preceding opening tag let contentStartIndex = content.index(content.startIndex, offsetBy: match.range.lowerBound) - var g1, g2, g3:Int - if (content[contentStartIndex.. Date: Wed, 11 Sep 2024 20:27:54 +0800 Subject: [PATCH 010/148] fix: avoid potential thread crashes of url Internal error in Foundation URL.host(percentEncoded:) --- xcode/Shared/UrlPolyfill.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/xcode/Shared/UrlPolyfill.swift b/xcode/Shared/UrlPolyfill.swift index 915ae8a7..f4be2267 100644 --- a/xcode/Shared/UrlPolyfill.swift +++ b/xcode/Shared/UrlPolyfill.swift @@ -49,15 +49,23 @@ func jsLikeURL(_ urlString: String, baseString: String? = nil) -> [String: Strin guard let url = _url else { return nil } - guard let scheme = url.scheme else { return nil } + /* + The issue still exists as of macOS 14.4, iOS 17.0 + https://stackoverflow.com/questions/74445926/url-host-deprecated-but-replacement-crashes + https://forums.swift.org/t/does-url-query-percentencoded-calls-url-host-percentencoded-under-the-hood/70452 + https://forums.developer.apple.com/forums/thread/722451 + */ + guard let hostname = url.host else { + return nil + } var port = (url.port == nil) ? "" : String(url.port!) if (scheme == "http" && port == "80") { port = "" } if (scheme == "https" && port == "443") { port = "" } if #available(macOS 13.0, iOS 16.0, *) { - let hostname = url.host(percentEncoded: true) ?? "" +// let hostname = url.host(percentEncoded: true) ?? "" let host = (port == "") ? hostname : "\(hostname):\(port)" let query = url.query(percentEncoded: true) ?? "" let fragment = url.fragment(percentEncoded: true) ?? "" @@ -75,7 +83,6 @@ func jsLikeURL(_ urlString: String, baseString: String? = nil) -> [String: Strin "username": url.user(percentEncoded: true) ?? "" ] } else { - let hostname = url.host ?? "" let host = (port == "") ? hostname : "\(hostname):\(port)" let query = url.query ?? "" let fragment = url.fragment ?? "" From 3e0a290261af084c93532bb1c1ead80f3d094227 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Fri, 13 Sep 2024 08:36:16 +0800 Subject: [PATCH 011/148] perf: process and transmit xhr done data only once --- src/ext/background/main.js | 12 ++++++- src/ext/content-scripts/api.js | 66 ++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/ext/background/main.js b/src/ext/background/main.js index 7e79716a..bbd1b7e5 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -512,12 +512,22 @@ async function handleMessage(message, sender) { statusText: xhr.statusText, timeout: xhr.timeout, }; + // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/response#value + if (xhr.readyState < xhr.DONE && xhr.responseType !== "text") { + response.response = null; + } // get content-type when headers received if (xhr.readyState >= xhr.HEADERS_RECEIVED) { response.contentType = xhr.getResponseHeader("Content-Type"); } // only process when xhr is complete and data exist - if (xhr.readyState === xhr.DONE && xhr.response !== null) { + // note the status of the last `progress` event in Safari is DONE/4 + // exclude this event to avoid unnecessary processing and transmission + if ( + xhr.readyState === xhr.DONE && + xhr.response !== null && + handler !== "onprogress" + ) { // need to convert arraybuffer data to postMessage if ( xhr.responseType === "arraybuffer" && diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index 817b7343..42333c2b 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -303,15 +303,45 @@ async function xhr(details, control, promise) { detailsParsed.headers[k.toLowerCase()] = v; } } - // get all the "on" events from XMLHttpRequest object - for (const k in XMLHttpRequest.prototype) { - if (k.slice(0, 2) !== "on") continue; + // preprocess handlers + const XHRHandlers = [ + "onreadystatechange", + "onloadstart", + "onprogress", + "onabort", + "onerror", + "onload", + "ontimeout", + "onloadend", + ]; + for (const handler of XHRHandlers) { // check which handlers are included in the original details object - if (typeof details[k] === "function") { + if ( + handler in XMLHttpRequest.prototype && + typeof details[handler] === "function" + ) { // add a bool to indicate if event listeners should be attached - detailsParsed.handlers[k] = true; + detailsParsed.handlers[handler] = true; } } + // resolving asynchronous xmlHttpRequest + if (promise) { + detailsParsed.handlers.onloadend = true; + const _onloadend = details.onloadend; + details.onloadend = (response) => { + promise.resolve(response); + if (typeof _onloadend === "function") _onloadend(response); + }; + } + // make sure to listen to XHR.DONE events only once, to avoid processing + // and transmitting the same response data multiple times + if (detailsParsed.handlers.onreadystatechange) { + delete detailsParsed.handlers.onload; + delete detailsParsed.handlers.onloadend; + } + if (detailsParsed.handlers.onload) { + delete detailsParsed.handlers.onloadend; + } // generate random port name for single xhr const xhrPortName = Math.random().toString(36).substring(1, 9); /** @@ -321,10 +351,11 @@ async function xhr(details, control, promise) { const listener = (port) => { if (port.name !== xhrPortName) return; port.onMessage.addListener(async (msg) => { + const handler = msg.handler; if ( msg.response && - detailsParsed.handlers[msg.handler] && - typeof details[msg.handler] === "function" + detailsParsed.handlers[handler] && + typeof details[handler] === "function" ) { // process xhr response const response = msg.response; @@ -337,12 +368,25 @@ async function xhr(details, control, promise) { xhrResponseProcessor(response); } // call userscript method - details[msg.handler](response); + details[handler](response); + // call the deleted XHR.DONE handlers above + if (response.readyState === 4) { + if (handler === "onreadystatechange") { + if (typeof details.onload === "function") { + details.onload(response); + } + if (typeof details.onloadend === "function") { + details.onloadend(response); + } + } else if (handler === "onload") { + if (typeof details.onloadend === "function") { + details.onloadend(response); + } + } + } } // all messages received - if (msg.handler === "onloadend") { - // resolving asynchronous xmlHttpRequest - promise && promise.resolve(msg.response); + if (handler === "onloadend") { // tell background it's safe to close port port.postMessage({ name: "DISCONNECT" }); } From fb7a28e1cd4a18ca33b24becb0433fb577a71a31 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:10:41 +0800 Subject: [PATCH 012/148] docs: adjust description sentences Co-authored-by: Justin Wasack <7660254+quoid@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68aa426d..1ac5316c 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ Userscripts Safari currently supports the following userscript metadata: - prevents code from being injected into nested frames - `@grant` - Imperative controls which special [`APIs`](#api) (if any) your script uses, one on each `@grant` line, only those API methods will be provided. - - If you don’t write `@grant`, value `none` will be assumed. + - If no `@grant` values are provided, `none` will be assumed. - If you specify `none` and something else, `none` takes precedence. **All userscripts need at least 1 `@match` or `@include` to run!** From e382a6c3de9d2958e3f22ef26f8b882b91094627 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Mon, 16 Sep 2024 04:11:16 +0800 Subject: [PATCH 013/148] refactor: improve jsdoc types and some adjustments --- README.md | 2 +- src/ext/background/main.js | 4 +- src/ext/content-scripts/api.js | 90 +++++++++++++++++++++------------- src/ext/types.d.ts | 47 ++++++++++++++++++ 4 files changed, 106 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 1ac5316c..5c5ea696 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ Userscripts Safari currently supports the following userscript metadata: Userscripts currently supports the following api methods. All methods are asynchronous unless otherwise noted. Users must `@grant` these methods in order to use them in a userscript. When using API methods, it's only possible to inject into the content script scope due to security concerns. -For API type definitions, please refer to: [`types.d.ts`](https://github.com/userscriptsup/testscripts/blob/363b322208a6733c7e5f4b9d9705957889af6837/userscripts/types.d.ts) +For API type definitions, please refer to: [`types.d.ts`](https://github.com/userscriptsup/testscripts/blob/bfce18746cd6bcab0616727401fa7ab6ef4086ac/userscripts/types.d.ts) - `GM.addStyle(css)` - `css: String` diff --git a/src/ext/background/main.js b/src/ext/background/main.js index bbd1b7e5..b65c43fd 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -413,6 +413,7 @@ async function handleMessage(message, sender) { } }); // parse details and set up for xhr instance + /** @type {TypeExtMessages.XHRTransportableDetails} */ const details = message.details; /** @type {Parameters[0]} */ const method = details.method || "GET"; @@ -496,11 +497,12 @@ async function handleMessage(message, sender) { // transfer to content script via text and then parse to document if (responseType === "document") xhr.responseType = "text"; // add required listeners and send result back to the content script - const handlers = details.handlers || {}; + const handlers = details.hasHandlers || {}; for (const handler of Object.keys(handlers)) { xhr[handler] = async () => { // can not send xhr through postMessage // construct new object to be sent as "response" + /** @type {TypeExtMessages.XHRTransportableResponse} */ const response = { contentType: undefined, // non-standard readyState: xhr.readyState, diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index 42333c2b..fc7c447a 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -108,8 +108,13 @@ async function setClipboard(clipboardData, type) { }); } -function xhrResponseProcessor(response) { - const res = response; +/** + * Restore `response.response` to required `responseType` + * @param {TypeExtMessages.XHRTransportableResponse} msgResponse + * @param {TypeExtMessages.XHRResponse} response + */ +function xhrResponseProcessor(msgResponse, response) { + const res = msgResponse; /** * only include responseXML when needed * NOTE: Only add implementation at this time, not enable, to avoid @@ -136,7 +141,7 @@ function xhrResponseProcessor(response) { // arraybuffer responses had their data converted in background // convert it back to arraybuffer try { - res.response = new Uint8Array(res.response).buffer; + response.response = new Uint8Array(res.response).buffer; } catch (err) { console.error("error parsing xhr arraybuffer", err); } @@ -147,7 +152,7 @@ function xhrResponseProcessor(response) { try { const typedArray = new Uint8Array(res.response); const type = res.contentType ?? ""; - res.response = new Blob([typedArray], { type }); + response.response = new Blob([typedArray], { type }); } catch (err) { console.error("error parsing xhr blob", err); } @@ -160,8 +165,8 @@ function xhrResponseProcessor(response) { const mimeType = res.contentType.includes("text/html") ? "text/html" : "text/xml"; - res.response = parser.parseFromString(res.response, mimeType); - res.responseXML = res.response; + response.response = parser.parseFromString(res.response, mimeType); + response.responseXML = response.response; } catch (err) { console.error("error parsing xhr document", err); } @@ -278,18 +283,19 @@ async function xhr(details, control, promise) { } // can not send details (func, blob, etc.) through message // construct a new processed object send to background page + /** @type {TypeExtMessages.XHRTransportableDetails} */ const detailsParsed = { - binary: details.binary, + binary: Boolean(details.binary), data: undefined, headers: {}, - method: details.method, - overrideMimeType: details.overrideMimeType, - password: details.password, + method: String(details.method), + overrideMimeType: String(details.overrideMimeType), + password: String(details.password), responseType: details.responseType, - timeout: details.timeout, - url: details.url, - user: details.user, - handlers: {}, + timeout: Number(details.timeout), + url: String(details.url), + user: String(details.user), + hasHandlers: {}, }; // preprocess data key try { @@ -304,6 +310,14 @@ async function xhr(details, control, promise) { } } // preprocess handlers + /** + * Record the handlers existing in details to a new object + * to avoid modifying the original object, and to prevent + * the original object from being changed by user scripts + * @type {TypeExtMessages.XHRHandlersObj} + */ + const handlers = {}; + /** @type {TypeExtMessages.XHRHandlers} */ const XHRHandlers = [ "onreadystatechange", "onloadstart", @@ -321,26 +335,28 @@ async function xhr(details, control, promise) { typeof details[handler] === "function" ) { // add a bool to indicate if event listeners should be attached - detailsParsed.handlers[handler] = true; + detailsParsed.hasHandlers[handler] = true; + // record to the new object + handlers[handler] = details[handler]; } } // resolving asynchronous xmlHttpRequest if (promise) { - detailsParsed.handlers.onloadend = true; - const _onloadend = details.onloadend; - details.onloadend = (response) => { + detailsParsed.hasHandlers.onloadend = true; + const _onloadend = handlers.onloadend; + handlers.onloadend = (response) => { promise.resolve(response); - if (typeof _onloadend === "function") _onloadend(response); + _onloadend?.(response); }; } // make sure to listen to XHR.DONE events only once, to avoid processing // and transmitting the same response data multiple times - if (detailsParsed.handlers.onreadystatechange) { - delete detailsParsed.handlers.onload; - delete detailsParsed.handlers.onloadend; + if (detailsParsed.hasHandlers.onreadystatechange) { + delete detailsParsed.hasHandlers.onload; + delete detailsParsed.hasHandlers.onloadend; } - if (detailsParsed.handlers.onload) { - delete detailsParsed.handlers.onloadend; + if (detailsParsed.hasHandlers.onload) { + delete detailsParsed.hasHandlers.onloadend; } // generate random port name for single xhr const xhrPortName = Math.random().toString(36).substring(1, 9); @@ -351,36 +367,40 @@ async function xhr(details, control, promise) { const listener = (port) => { if (port.name !== xhrPortName) return; port.onMessage.addListener(async (msg) => { + /** @type {TypeExtMessages.XHRHandlers[number]} */ const handler = msg.handler; if ( msg.response && - detailsParsed.handlers[handler] && - typeof details[handler] === "function" + detailsParsed.hasHandlers[handler] && + typeof handlers[handler] === "function" ) { // process xhr response - const response = msg.response; + /** @type {TypeExtMessages.XHRTransportableResponse} */ + const msgResponse = msg.response; + /** @type {TypeExtMessages.XHRResponse} */ + const response = msgResponse; // only include responseText when needed if (["", "text"].includes(response.responseType)) { response.responseText = response.response; } // only process when xhr is complete and data exist if (response.readyState === 4 && response.response !== null) { - xhrResponseProcessor(response); + xhrResponseProcessor(msgResponse, response); } // call userscript method - details[handler](response); + handlers[handler](response); // call the deleted XHR.DONE handlers above if (response.readyState === 4) { if (handler === "onreadystatechange") { - if (typeof details.onload === "function") { - details.onload(response); + if (typeof handlers.onload === "function") { + handlers.onload(response); } - if (typeof details.onloadend === "function") { - details.onloadend(response); + if (typeof handlers.onloadend === "function") { + handlers.onloadend(response); } } else if (handler === "onload") { - if (typeof details.onloadend === "function") { - details.onloadend(response); + if (typeof handlers.onloadend === "function") { + handlers.onloadend(response); } } } diff --git a/src/ext/types.d.ts b/src/ext/types.d.ts index 149f3ac3..b194b29f 100644 --- a/src/ext/types.d.ts +++ b/src/ext/types.d.ts @@ -31,4 +31,51 @@ declare namespace TypeExtMessages { data: XHRProcessedFormData; type: "FormData"; }; + + type XHRHandlers = [ + "onreadystatechange", + "onloadstart", + "onprogress", + "onabort", + "onerror", + "onload", + "ontimeout", + "onloadend", + ]; + + type XHRHandlersObj = { + [handler in XHRHandlers[number]]?: (response: XHRResponse) => void; + }; + + interface XHRTransportableDetails { + binary: boolean; + data: XHRProcessedData; + headers: { [x: Lowercase]: string }; + method: string; + overrideMimeType: string; + password: string; + responseType: XMLHttpRequestResponseType; + timeout: number; + url: string; + user: string; + hasHandlers: { [handler in XHRHandlers[number]]?: boolean }; + } + + interface XHRTransportableResponse { + contentType: string; // non-standard + readyState: number; + response: string | number[]; + responseHeaders: string; + responseType: XMLHttpRequestResponseType; + responseURL: string; + status: number; + statusText: string; + timeout: number; + } + + interface XHRResponse extends Omit { + response: any; + responseText?: string; + responseXML?: Document; + } } From d1306ca7c8392ac3cc5d952d1621f081c9bae2b4 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Tue, 17 Sep 2024 01:53:25 +0800 Subject: [PATCH 014/148] docs: add versioning documentation guidelines --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 5c5ea696..ec701d72 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,13 @@ Userscripts Safari currently supports the following userscript metadata: Userscripts currently supports the following api methods. All methods are asynchronous unless otherwise noted. Users must `@grant` these methods in order to use them in a userscript. When using API methods, it's only possible to inject into the content script scope due to security concerns. +> [!NOTE] +> +> The following API description applies to the latest development branch, you may need to check the documentation for the corresponding version. Please switch to the version you want to check via `Branches` or `Tags` at the top. +> +> For example, for the v4.x.x version of the App Store: +> https://github.com/quoid/userscripts/tree/release/4.x.x + For API type definitions, please refer to: [`types.d.ts`](https://github.com/userscriptsup/testscripts/blob/bfce18746cd6bcab0616727401fa7ab6ef4086ac/userscripts/types.d.ts) - `GM.addStyle(css)` From 49723d926af0409919d2d6ea57d9dae48e8c256e Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:49:29 +0800 Subject: [PATCH 015/148] build: update dependencies --- package-lock.json | 548 +++++++++++++++++++++++----------------------- package.json | 18 +- 2 files changed, 287 insertions(+), 279 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95fa5364..16844d98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,21 +6,21 @@ "": { "name": "userscripts", "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.1.1", - "@types/webextension-polyfill": "^0.10.7", - "autoprefixer": "^10.4.19", + "@sveltejs/vite-plugin-svelte": "^4.0.0-next.7", + "@types/webextension-polyfill": "^0.12.1", + "autoprefixer": "^10.4.20", "cm-show-invisibles": "^3.1.0", "codemirror": "^5.65.17", - "eslint": "^9.7.0", + "eslint": "^9.10.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.43.0", - "prettier": "3.0.3", - "prettier-plugin-svelte": "3.2.3", + "eslint-plugin-svelte": "^2.44.0", + "prettier": "3.3.3", + "prettier-plugin-svelte": "3.2.6", "stylelint": "^16.7.0", "stylelint-config-html": "^1.1.0", "stylelint-config-standard": "^36.0.0", - "svelte": "5.0.0-next.192", - "vite": "^5.3.4" + "svelte": "^5.0.0-next.250", + "vite": "^5.4.6" } }, "node_modules/@ampproject/remapping": { @@ -163,9 +163,9 @@ } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz", - "integrity": "sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", + "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", "dev": true, "funding": [ { @@ -179,16 +179,16 @@ ], "license": "MIT", "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.4.1" + "@csstools/css-tokenizer": "^3.0.1" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz", - "integrity": "sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", + "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", "dev": true, "funding": [ { @@ -202,13 +202,13 @@ ], "license": "MIT", "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.13.tgz", - "integrity": "sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", + "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", "dev": true, "funding": [ { @@ -222,17 +222,17 @@ ], "license": "MIT", "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.7.1", - "@csstools/css-tokenizer": "^2.4.1" + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1" } }, "node_modules/@csstools/selector-specificity": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz", - "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", + "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", "dev": true, "funding": [ { @@ -246,10 +246,10 @@ ], "license": "MIT-0", "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" }, "peerDependencies": { - "postcss-selector-parser": "^6.0.13" + "postcss-selector-parser": "^6.1.0" } }, "node_modules/@dual-bundle/import-meta-resolve": { @@ -684,9 +684,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "license": "MIT", "engines": { @@ -694,9 +694,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz", - "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -733,9 +733,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", - "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", "dev": true, "license": "MIT", "engines": { @@ -752,6 +752,19 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -872,9 +885,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz", - "integrity": "sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz", + "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==", "cpu": [ "arm" ], @@ -886,9 +899,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.0.tgz", - "integrity": "sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz", + "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==", "cpu": [ "arm64" ], @@ -900,9 +913,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.0.tgz", - "integrity": "sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz", + "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==", "cpu": [ "arm64" ], @@ -914,9 +927,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.0.tgz", - "integrity": "sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz", + "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==", "cpu": [ "x64" ], @@ -928,9 +941,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.0.tgz", - "integrity": "sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz", + "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==", "cpu": [ "arm" ], @@ -942,9 +955,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.0.tgz", - "integrity": "sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz", + "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==", "cpu": [ "arm" ], @@ -956,9 +969,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.0.tgz", - "integrity": "sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz", + "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==", "cpu": [ "arm64" ], @@ -970,9 +983,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.0.tgz", - "integrity": "sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz", + "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==", "cpu": [ "arm64" ], @@ -984,9 +997,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.0.tgz", - "integrity": "sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz", + "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==", "cpu": [ "ppc64" ], @@ -998,9 +1011,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.0.tgz", - "integrity": "sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz", + "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==", "cpu": [ "riscv64" ], @@ -1012,9 +1025,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.0.tgz", - "integrity": "sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz", + "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==", "cpu": [ "s390x" ], @@ -1026,9 +1039,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.0.tgz", - "integrity": "sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz", + "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==", "cpu": [ "x64" ], @@ -1040,9 +1053,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.0.tgz", - "integrity": "sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz", + "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==", "cpu": [ "x64" ], @@ -1054,9 +1067,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.0.tgz", - "integrity": "sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz", + "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==", "cpu": [ "arm64" ], @@ -1068,9 +1081,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.0.tgz", - "integrity": "sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz", + "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==", "cpu": [ "ia32" ], @@ -1082,9 +1095,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.0.tgz", - "integrity": "sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz", + "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==", "cpu": [ "x64" ], @@ -1096,59 +1109,45 @@ ] }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.1.tgz", - "integrity": "sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==", + "version": "4.0.0-next.7", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0-next.7.tgz", + "integrity": "sha512-yMUnAqquoayvBDztk1rWUgdtvjv7YcHgopCAB7sWl9SQht8U/7lqwTlJU0ZTAY09pFFRe6bbakd7YoiyyIvJiA==", "dev": true, "license": "MIT", "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", - "debug": "^4.3.4", + "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", + "debug": "^4.3.6", "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.30.10", - "svelte-hmr": "^0.16.0", - "vitefu": "^0.2.5" + "magic-string": "^0.30.11", + "vitefu": "^1.0.2" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^18.0.0 || ^20.0.0 || >=22" }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", + "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", - "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "version": "3.0.0-next.3", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.0-next.3.tgz", + "integrity": "sha512-kuGJ2CZ5lAw3gKF8Kw0AfKtUJWbwdlDHY14K413B0MCyrzvQvsKTorwmwZcky0+QqY6RnVIZ/5FttB9bQmkLXg==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.4" + "debug": "^4.3.5" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^18.0.0 || ^20.0.0 || >=22" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", + "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", + "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, - "node_modules/@sveltejs/vite-plugin-svelte/node_modules/svelte-hmr": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", - "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" - }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -1157,9 +1156,9 @@ "license": "MIT" }, "node_modules/@types/webextension-polyfill": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@types/webextension-polyfill/-/webextension-polyfill-0.10.7.tgz", - "integrity": "sha512-10ql7A0qzBmFB+F+qAke/nP1PIonS0TXZAOMVOxEUsm+lGSW6uwVcISFNa0I4Oyj0884TZVWGGMIWeXOVSNFHw==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/webextension-polyfill/-/webextension-polyfill-0.12.1.tgz", + "integrity": "sha512-xPTFWwQ8BxPevPF2IKsf4hpZNss4LxaOLZXypQH4E63BDLmcwX/RMGdI4tB4VO4Nb6xDBH3F/p4gz4wvof1o9w==", "dev": true, "license": "MIT" }, @@ -1247,13 +1246,13 @@ "license": "Python-2.0" }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", "dev": true, "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" + "engines": { + "node": ">= 0.4" } }, "node_modules/array-union": { @@ -1277,9 +1276,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -1297,11 +1296,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -1356,9 +1355,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -1376,9 +1375,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -1399,9 +1398,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001642", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", - "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", "dev": true, "funding": [ { @@ -1564,13 +1563,13 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1598,16 +1597,6 @@ "node": ">=0.10.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1685,9 +1674,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.832", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.832.tgz", - "integrity": "sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==", + "version": "1.5.24", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz", + "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA==", "dev": true, "license": "ISC" }, @@ -1772,9 +1761,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -1795,17 +1784,18 @@ } }, "node_modules/eslint": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz", - "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.17.0", + "@eslint/config-array": "^0.18.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.7.0", + "@eslint/js": "9.10.0", + "@eslint/plugin-kit": "^0.1.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -1828,7 +1818,6 @@ "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", @@ -1844,6 +1833,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-compat-utils": { @@ -1876,9 +1873,9 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "2.43.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.43.0.tgz", - "integrity": "sha512-REkxQWvg2pp7QVLxQNa+dJ97xUqRe7Y2JJbSWkHSuszu0VcblZtXkPBPckkivk99y5CdLw4slqfPylL2d/X4jQ==", + "version": "2.44.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.44.0.tgz", + "integrity": "sha512-wav4MOs02vBb1WjvTCYItwJCxMkuk2Z4p+K/eyjL0N/z7ahXLP+0LtQQjiKc2ezuif7GnZLbD1F3o1VHzSvdVg==", "dev": true, "license": "MIT", "dependencies": { @@ -1892,7 +1889,7 @@ "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.1.0", "semver": "^7.6.2", - "svelte-eslint-parser": "^0.41.0" + "svelte-eslint-parser": "^0.41.1" }, "engines": { "node": "^14.17.0 || >=16.0.0" @@ -2333,9 +2330,9 @@ } }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -2618,13 +2615,13 @@ "license": "MIT" }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/mathml-tag-names": { @@ -2669,9 +2666,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -2696,9 +2693,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, @@ -2729,9 +2726,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz", - "integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true, "license": "MIT" }, @@ -2868,9 +2865,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true, "license": "ISC" }, @@ -2888,9 +2885,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -2909,8 +2906,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2964,9 +2961,9 @@ } }, "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", "dev": true, "license": "MIT" }, @@ -3015,9 +3012,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", - "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { @@ -3046,9 +3043,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "license": "MIT", "bin": { @@ -3062,9 +3059,9 @@ } }, "node_modules/prettier-plugin-svelte": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.3.tgz", - "integrity": "sha512-wJq8RunyFlWco6U0WJV5wNCM7zpBFakS76UBSbmzMGpncpK98NZABaE+s7n8/APDCEVNHXC5Mpq+MLebQtsRlg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.6.tgz", + "integrity": "sha512-Y1XWLw7vXUQQZmgv1JAEiLcErqUniAF2wO7QJsw8BVMvpLET2dI5WpEIEJx1r11iHVdSMzQxivyfrH9On9t2IQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3135,9 +3132,9 @@ } }, "node_modules/rollup": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.0.tgz", - "integrity": "sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz", + "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==", "dev": true, "license": "MIT", "dependencies": { @@ -3151,22 +3148,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.19.0", - "@rollup/rollup-android-arm64": "4.19.0", - "@rollup/rollup-darwin-arm64": "4.19.0", - "@rollup/rollup-darwin-x64": "4.19.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.19.0", - "@rollup/rollup-linux-arm-musleabihf": "4.19.0", - "@rollup/rollup-linux-arm64-gnu": "4.19.0", - "@rollup/rollup-linux-arm64-musl": "4.19.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.19.0", - "@rollup/rollup-linux-riscv64-gnu": "4.19.0", - "@rollup/rollup-linux-s390x-gnu": "4.19.0", - "@rollup/rollup-linux-x64-gnu": "4.19.0", - "@rollup/rollup-linux-x64-musl": "4.19.0", - "@rollup/rollup-win32-arm64-msvc": "4.19.0", - "@rollup/rollup-win32-ia32-msvc": "4.19.0", - "@rollup/rollup-win32-x64-msvc": "4.19.0", + "@rollup/rollup-android-arm-eabi": "4.21.3", + "@rollup/rollup-android-arm64": "4.21.3", + "@rollup/rollup-darwin-arm64": "4.21.3", + "@rollup/rollup-darwin-x64": "4.21.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.3", + "@rollup/rollup-linux-arm-musleabihf": "4.21.3", + "@rollup/rollup-linux-arm64-gnu": "4.21.3", + "@rollup/rollup-linux-arm64-musl": "4.21.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3", + "@rollup/rollup-linux-riscv64-gnu": "4.21.3", + "@rollup/rollup-linux-s390x-gnu": "4.21.3", + "@rollup/rollup-linux-x64-gnu": "4.21.3", + "@rollup/rollup-linux-x64-musl": "4.21.3", + "@rollup/rollup-win32-arm64-msvc": "4.21.3", + "@rollup/rollup-win32-ia32-msvc": "4.21.3", + "@rollup/rollup-win32-x64-msvc": "4.21.3", "fsevents": "~2.3.2" } }, @@ -3272,9 +3269,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3323,9 +3320,9 @@ } }, "node_modules/stylelint": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.7.0.tgz", - "integrity": "sha512-Q1ATiXlz+wYr37a7TGsfvqYn2nSR3T/isw3IWlZQzFzCNoACHuGBb6xBplZXz56/uDRJHIygxjh7jbV/8isewA==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.9.0.tgz", + "integrity": "sha512-31Nm3WjxGOBGpQqF43o3wO9L5AC36TPIe6030Lnm13H3vDMTcS21DrLh69bMX+DBilKqMMVLian4iG6ybBoNRQ==", "dev": true, "funding": [ { @@ -3339,17 +3336,17 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^2.7.1", - "@csstools/css-tokenizer": "^2.4.1", - "@csstools/media-query-list-parser": "^2.1.13", - "@csstools/selector-specificity": "^3.1.1", + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1", + "@csstools/media-query-list-parser": "^3.0.1", + "@csstools/selector-specificity": "^4.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.2", "css-tree": "^2.3.1", - "debug": "^4.3.5", + "debug": "^4.3.6", "fast-glob": "^3.3.2", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^9.0.0", @@ -3357,24 +3354,24 @@ "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^5.3.1", + "ignore": "^5.3.2", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.34.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", - "micromatch": "^4.0.7", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.0.1", - "postcss": "^8.4.39", - "postcss-resolve-nested-selector": "^0.1.1", + "postcss": "^8.4.41", + "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.0", - "postcss-selector-parser": "^6.1.0", + "postcss-selector-parser": "^6.1.2", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", "strip-ansi": "^7.1.0", - "supports-hyperlinks": "^3.0.0", + "supports-hyperlinks": "^3.1.0", "svg-tags": "^1.0.0", "table": "^6.8.2", "write-file-atomic": "^5.0.1" @@ -3453,9 +3450,9 @@ } }, "node_modules/stylelint/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -3473,9 +3470,9 @@ "license": "MIT" }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", - "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", + "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", "dev": true, "license": "MIT", "dependencies": { @@ -3566,9 +3563,9 @@ } }, "node_modules/supports-hyperlinks": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", - "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", + "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", "dev": true, "license": "MIT", "dependencies": { @@ -3577,27 +3574,30 @@ }, "engines": { "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/svelte": { - "version": "5.0.0-next.192", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.192.tgz", - "integrity": "sha512-UgjiqTCsEWyQ157x5YNbmx859vBVFfznKaxuiMCPqHS3HRZ1iqTsSyO3LI/4BHjqPrtxwrOn1Z63VwoJkYBBDA==", + "version": "5.0.0-next.250", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.250.tgz", + "integrity": "sha512-QZWi8TvIC8m3fQeSvC8Fh8l9vYEEzQtu5mOwd62Jq4rfrHTMuwdnf1SNccmxNwcm9iPV26MYjElLSGH945agZw==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", "@types/estree": "^1.0.5", - "acorn": "^8.11.3", + "acorn": "^8.12.1", "acorn-typescript": "^1.4.13", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", "esm-env": "^1.0.0", "esrap": "^1.2.2", "is-reference": "^3.0.2", "locate-character": "^3.0.0", - "magic-string": "^0.30.5", + "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" }, "engines": { @@ -3605,9 +3605,9 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.0.tgz", - "integrity": "sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==", + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.1.tgz", + "integrity": "sha512-08ndI6zTghzI8SuJAFpvMbA/haPSGn3xz19pjre19yYMw8Nw/wQJ2PrZBI/L8ijGTgtkWCQQiLLy+Z1tfaCwNA==", "dev": true, "license": "MIT", "dependencies": { @@ -3809,15 +3809,15 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz", - "integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -3836,6 +3836,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -3853,6 +3854,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -3865,11 +3869,15 @@ } }, "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.2.tgz", + "integrity": "sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg==", "dev": true, "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*" + ], "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, diff --git a/package.json b/package.json index ca946143..b5205ec1 100644 --- a/package.json +++ b/package.json @@ -21,20 +21,20 @@ "prettier": "prettier --write . --plugin prettier-plugin-svelte" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.1.1", - "@types/webextension-polyfill": "^0.10.7", - "autoprefixer": "^10.4.19", + "@sveltejs/vite-plugin-svelte": "^4.0.0-next.7", + "@types/webextension-polyfill": "^0.12.1", + "autoprefixer": "^10.4.20", "cm-show-invisibles": "^3.1.0", "codemirror": "^5.65.17", - "eslint": "^9.7.0", + "eslint": "^9.10.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.43.0", - "prettier": "3.0.3", - "prettier-plugin-svelte": "3.2.3", + "eslint-plugin-svelte": "^2.44.0", + "prettier": "3.3.3", + "prettier-plugin-svelte": "3.2.6", "stylelint": "^16.7.0", "stylelint-config-html": "^1.1.0", "stylelint-config-standard": "^36.0.0", - "svelte": "^5.0.0-next.192", - "vite": "^5.3.4" + "svelte": "^5.0.0-next.250", + "vite": "^5.4.6" } } From 8159a51f05f90591826c321fa95ef5bbd792b9bd Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:50:26 +0800 Subject: [PATCH 016/148] chore: apply prettier 3.3.3 --- Userscripts.code-workspace | 8 ++++---- docs/dev.md | 2 ++ scripts/utils.js | 4 ++-- src/ext/extension-page/Components/Notification.svelte | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Userscripts.code-workspace b/Userscripts.code-workspace index a0b39827..11c4d094 100644 --- a/Userscripts.code-workspace +++ b/Userscripts.code-workspace @@ -3,12 +3,12 @@ "folders": [ { "name": "Userscripts-JS", - "path": "." + "path": ".", }, { "name": "Userscripts-Xcode", - "path": "xcode" - } + "path": "xcode", + }, ], - "settings": {} + "settings": {}, } diff --git a/docs/dev.md b/docs/dev.md index 85633998..e34d084d 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -35,7 +35,9 @@ Reviewing the template will help you understand the composition of the project. - `xcodebuild -scheme Mac` [^1][^2][^3] or build with `Xcode` App [^1]: These commands can also be executed directly through the vscode tasks. Please refer to: [/.vscode/tasks.json](../.vscode/tasks.json) + [^2]: Select the corresponding target and platform to build. Please refer to: [/package.json](../package.json) and [xcode-schemes](../xcode/Userscripts.xcodeproj/xcshareddata/xcschemes/) + [^3]: Local setup may be required. Please refer to: [Building from the Command Line with Xcode FAQ](https://developer.apple.com/library/archive/technotes/tn2339/_index.html) # Xcode diff --git a/scripts/utils.js b/scripts/utils.js index 668399e0..f88bd921 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -186,9 +186,9 @@ export const baseConfig = { ), "import.meta.env.GIT_TAG": JSON.stringify(gitTag), "import.meta.env.GIT_COMMIT": JSON.stringify(gitCommit), - } + } : { "import.meta.env.GIT_TAG": JSON.stringify(gitTag), "import.meta.env.GIT_COMMIT": JSON.stringify(gitCommit), - }, + }, }; diff --git a/src/ext/extension-page/Components/Notification.svelte b/src/ext/extension-page/Components/Notification.svelte index f79e5343..79a505c3 100644 --- a/src/ext/extension-page/Components/Notification.svelte +++ b/src/ext/extension-page/Components/Notification.svelte @@ -19,8 +19,8 @@ item.type === "error" ? iconError : item.type === "info" - ? iconInfo - : iconWarn; + ? iconInfo + : iconWarn; let previousProgress; function pause() { From c819e3f17ad1655718be3f9d3ba4775be3111c3e Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:56:13 +0800 Subject: [PATCH 017/148] fix: newly generated errors by svelte compiler `CodeMirror.svelte` currently uses a lot of ignores to avoid svelte compiler warnings (but are reported as errors in eslint), and related components will be refactored in the future. --- src/dev/App.svelte | 1 + .../extension-page/Components/Editor/CodeMirror.svelte | 8 +++++++- src/ext/shared/Components/Toggle.svelte | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dev/App.svelte b/src/dev/App.svelte index 2ac83b9c..602dae45 100644 --- a/src/dev/App.svelte +++ b/src/dev/App.svelte @@ -8,6 +8,7 @@ href="https://github.com/quoid/userscripts" target="_blank" rel="noreferrer" + aria-label="open userscripts github" > diff --git a/src/ext/extension-page/Components/Editor/CodeMirror.svelte b/src/ext/extension-page/Components/Editor/CodeMirror.svelte index 7d5c9f9f..4e1c55b1 100644 --- a/src/ext/extension-page/Components/Editor/CodeMirror.svelte +++ b/src/ext/extension-page/Components/Editor/CodeMirror.svelte @@ -1,4 +1,4 @@ -
-
- Userscripts App Icon - - -
- {#if import.meta.env.GIT_TAG && import.meta.env.GIT_COMMIT} - - {import.meta.env.GIT_TAG} - - ( - {import.meta.env.GIT_COMMIT.slice(0, 7)} - ) - {:else} - {version} - {build} - {/if} + {#await initialize() then app} +
+ Userscripts App Icon + + +
+ {#if import.meta.env.GIT_TAG && import.meta.env.GIT_COMMIT} + + {import.meta.env.GIT_TAG} + + ( + {import.meta.env.GIT_COMMIT.slice(0, 7)} + ) + {:else} + v{app.version} + ({app.build}) + {/if} +
-
-
-

- You can turn on the Userscripts iOS Safari extension in Settings or - Safari, then use the extension in Safari. Please refer to the "Usage" - section in the - README of this version. -

-
-
- -
CURRENT DIRECTORY:
- -
+
+

+ You can turn on the Userscripts iOS Safari extension in Settings or + Safari, then use the extension in Safari. Please refer to the "Usage" + section in the + README of this version. +

+
+
+ +
CURRENT DIRECTORY:
+ +
+ {:catch error} +
+ {error} +
+ {/await} +