From 3eb385d26ed81bb4e9576f3752ff7abb61ce6050 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Wed, 14 Jan 2026 09:13:29 +0000 Subject: [PATCH 01/27] refactor: design monorepos --- builder/package.json | 20 +++++++++++++++++++ {sources => builder/source}/banner.txt | 0 builder.ts => builder/source/builder.ts | 2 +- builder/tsconfig.json | 10 ++++++++++ package.json | 20 ++++--------------- sources/esbuild.inject.ts | 9 --------- tsconfig.json | 9 ++++++++- userscript/package.json | 8 ++++++++ .../src => userscript/source}/as-weakmap.ts | 0 {sources/src => userscript/source}/index.ts | 0 {sources => userscript/source}/interface.ts | 2 +- {sources/src => userscript/source}/utils.ts | 0 userscript/tsconfig.json | 9 +++++++++ 13 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 builder/package.json rename {sources => builder/source}/banner.txt (100%) rename builder.ts => builder/source/builder.ts (99%) create mode 100644 builder/tsconfig.json delete mode 100644 sources/esbuild.inject.ts create mode 100644 userscript/package.json rename {sources/src => userscript/source}/as-weakmap.ts (100%) rename {sources/src => userscript/source}/index.ts (100%) rename {sources => userscript/source}/interface.ts (86%) rename {sources/src => userscript/source}/utils.ts (100%) create mode 100644 userscript/tsconfig.json diff --git a/builder/package.json b/builder/package.json new file mode 100644 index 0000000..7dbeae0 --- /dev/null +++ b/builder/package.json @@ -0,0 +1,20 @@ +{ + "name": "@filteringdev/tinyshield-builder", + "private": true, + "type": "module", + "dependencies": { + "@types/node": "^24.9.2" + }, + "devDependencies": { + "@npmcli/package-json": "^7.0.4", + "@types/npmcli__package-json": "^4.0.4", + "@types/semver": "^7.7.1", + "esbuild": "^0.27.0", + "eslint": "^9.38.0", + "semver": "^7.7.3", + "tsx": "^4.21.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.46.2", + "zod": "^4.3.5" + } +} \ No newline at end of file diff --git a/sources/banner.txt b/builder/source/banner.txt similarity index 100% rename from sources/banner.txt rename to builder/source/banner.txt diff --git a/builder.ts b/builder/source/builder.ts similarity index 99% rename from builder.ts rename to builder/source/builder.ts index 478665b..326276f 100644 --- a/builder.ts +++ b/builder/source/builder.ts @@ -99,7 +99,7 @@ let AttachHeaderPath = `/tmp/${crypto.randomUUID()}` Fs.writeFileSync(AttachHeaderPath, ConvertedHeader, { encoding: 'utf-8', mode: 0o700 }) console.log('Written temporary header file to:', AttachHeaderPath) await ESBuild.build({ - entryPoints: ['./sources/src/index.ts'], + entryPoints: ['./sources/index.ts'], bundle: true, minify: BuildType === 'production', define: { diff --git a/builder/tsconfig.json b/builder/tsconfig.json new file mode 100644 index 0000000..5d2dc03 --- /dev/null +++ b/builder/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "../../" + }, + "include": [ + "source/**/*.ts", + "test/**/*.ts" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 170f47a..b4ef4ca 100644 --- a/package.json +++ b/package.json @@ -28,20 +28,8 @@ "url": "git+https://github.com/FilteringDev/tinyShield.git" }, "license": "MPL-2.0", - "dependencies": { - "@types/node": "^24.9.2" - }, - "devDependencies": { - "@npmcli/package-json": "^7.0.4", - "@types/npmcli__package-json": "^4.0.4", - "@types/semver": "^7.7.1", - "esbuild": "^0.27.0", - "eslint": "^9.38.0", - "semver": "^7.7.3", - "tsx": "^4.21.0", - "typescript": "^5.9.3", - "typescript-eslint": "^8.46.2", - "zod": "^4.3.5" - }, - "packageManager": "npm@11.5.1+" + "packageManager": "npm@11.5.1+", + "workspaces": [ + "userscript", "builder" + ] } diff --git a/sources/esbuild.inject.ts b/sources/esbuild.inject.ts deleted file mode 100644 index 02b2d13..0000000 --- a/sources/esbuild.inject.ts +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * @license MPL-2.0 - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * Contributors: - * - See Git history at https://github.com/FilteringDev/tinyShield for detailed authorship information. - */ \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 398aab3..776fc5d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,13 @@ "moduleResolution": "NodeNext", "removeComments": false, "alwaysStrict": false, - "skipLibCheck": true + "skipLibCheck": true, + "paths": { + "@builder/*": ["./builder/sources/*"], + "@userscript/*": ["./userscript/sources/*"], + "@root/*": ["./sources/*"], + "@reporoot/*": ["./*"] + }, + "baseUrl": "." } } \ No newline at end of file diff --git a/userscript/package.json b/userscript/package.json new file mode 100644 index 0000000..40c6b63 --- /dev/null +++ b/userscript/package.json @@ -0,0 +1,8 @@ +{ + "name": "@filteringdev/tinyshield-userscript", + "private": true, + "type": "module", + "devDependencies": { + "@types/web": "^0.0.317" + } +} diff --git a/sources/src/as-weakmap.ts b/userscript/source/as-weakmap.ts similarity index 100% rename from sources/src/as-weakmap.ts rename to userscript/source/as-weakmap.ts diff --git a/sources/src/index.ts b/userscript/source/index.ts similarity index 100% rename from sources/src/index.ts rename to userscript/source/index.ts diff --git a/sources/interface.ts b/userscript/source/interface.ts similarity index 86% rename from sources/interface.ts rename to userscript/source/interface.ts index 5621353..659cced 100644 --- a/sources/interface.ts +++ b/userscript/source/interface.ts @@ -8,4 +8,4 @@ * - See Git history at https://github.com/FilteringDev/tinyShield for detailed authorship information. */ -export { RunTinyShieldUserscript } from './src/index.js' \ No newline at end of file +export { RunTinyShieldUserscript } from './index.js' \ No newline at end of file diff --git a/sources/src/utils.ts b/userscript/source/utils.ts similarity index 100% rename from sources/src/utils.ts rename to userscript/source/utils.ts diff --git a/userscript/tsconfig.json b/userscript/tsconfig.json new file mode 100644 index 0000000..590db8a --- /dev/null +++ b/userscript/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "../../" + }, + "include": [ + "source/**/*.ts" + ] +} \ No newline at end of file From 1b9724c6837bc9fb13134091adfbd9b3364bd7f9 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Wed, 14 Jan 2026 09:18:44 +0000 Subject: [PATCH 02/27] ci: fix ESLint chain --- builder/package.json | 3 +++ package.json | 2 +- userscript/package.json | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/builder/package.json b/builder/package.json index 7dbeae0..889e3ff 100644 --- a/builder/package.json +++ b/builder/package.json @@ -2,6 +2,9 @@ "name": "@filteringdev/tinyshield-builder", "private": true, "type": "module", + "scripts": { + "lint": "tsc --noEmit && eslint source/**/*.ts" + }, "dependencies": { "@types/node": "^24.9.2" }, diff --git a/package.json b/package.json index b4ef4ca..52dad9c 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build:userscript": "tsx builder.ts -- production", "build": "npm run build:interface && npm run build:userscript", "debug": "tsx builder.ts -- development", - "lint": "tsc --noEmit && eslint sources/**/*.ts" + "lint": "npm run lint -w builder && npm run lint -w userscript" }, "keywords": [ "Ad-Shield" diff --git a/userscript/package.json b/userscript/package.json index 40c6b63..243f1af 100644 --- a/userscript/package.json +++ b/userscript/package.json @@ -2,6 +2,9 @@ "name": "@filteringdev/tinyshield-userscript", "private": true, "type": "module", + "scripts": { + "lint": "tsc --noEmit && eslint source/**/*.ts" + }, "devDependencies": { "@types/web": "^0.0.317" } From 2973ddc2ad73342878942dd01efa4e01ac92456e Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Wed, 14 Jan 2026 09:33:12 +0000 Subject: [PATCH 03/27] ci: expand ESLint checking to all Typescript files --- builder/package.json | 2 +- userscript/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/package.json b/builder/package.json index 889e3ff..9c9f540 100644 --- a/builder/package.json +++ b/builder/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "lint": "tsc --noEmit && eslint source/**/*.ts" + "lint": "tsc --noEmit && eslint **/*.ts" }, "dependencies": { "@types/node": "^24.9.2" diff --git a/userscript/package.json b/userscript/package.json index 243f1af..92dd5e1 100644 --- a/userscript/package.json +++ b/userscript/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "lint": "tsc --noEmit && eslint source/**/*.ts" + "lint": "tsc --noEmit && eslint **/*.ts" }, "devDependencies": { "@types/web": "^0.0.317" From f2bf1afdb01389be887e784b6f5f5525c9e88620 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Thu, 15 Jan 2026 12:17:46 +0000 Subject: [PATCH 04/27] refactor: split IAB sellers downloader --- builder/source/references/iabsellers.ts | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 builder/source/references/iabsellers.ts diff --git a/builder/source/references/iabsellers.ts b/builder/source/references/iabsellers.ts new file mode 100644 index 0000000..cbfab0e --- /dev/null +++ b/builder/source/references/iabsellers.ts @@ -0,0 +1,38 @@ +import * as Zod from 'zod' +import { HTTPSRequest } from '@typescriptprime/securereq' + + +const IABSellersJsonURL = 'https://info.ad-shield.io/sellers.json' + +export async function FetchIABSellersJsonData(): Promise { + const IABSellersJsonResponse: { StatusCode: number, Headers: Record, Body: unknown } = await HTTPSRequest(new URL(IABSellersJsonURL), { ExpectedAs: 'JSON' }) + let IABSellersJsonData =IABSellersJsonResponse.Body as { + // eslint-disable-next-line @typescript-eslint/naming-convention + sellers: Array<{ + // eslint-disable-next-line @typescript-eslint/naming-convention + seller_id: number, + // eslint-disable-next-line @typescript-eslint/naming-convention + seller_type: string, + // eslint-disable-next-line @typescript-eslint/naming-convention + name: string, + // eslint-disable-next-line @typescript-eslint/naming-convention + domain: string + }> + } + IABSellersJsonData = await Zod.object({ + sellers: Zod.array(Zod.object({ + seller_id: Zod.number(), + seller_type: Zod.string(), + name: Zod.string(), + domain: Zod.string().refine(D => { + try { + new URL(`https://${D}`) + } catch { + return false + } + return true + }) + })) + }).parseAsync(IABSellersJsonData) + return [...new Set(IABSellersJsonData.sellers.map(S => S.domain))] +} \ No newline at end of file From 0173bdb23ea64747763b438a8a053e2a36fb6db2 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Thu, 15 Jan 2026 12:18:01 +0000 Subject: [PATCH 05/27] refactor: update pacakge.json in `builder` --- builder/package.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/builder/package.json b/builder/package.json index 9c9f540..962d537 100644 --- a/builder/package.json +++ b/builder/package.json @@ -3,21 +3,27 @@ "private": true, "type": "module", "scripts": { - "lint": "tsc --noEmit && eslint **/*.ts" + "lint": "tsc --noEmit && eslint **/*.ts", + "build": "tsx source/build.ts", + "debug": "tsx source/debug.ts" }, "dependencies": { - "@types/node": "^24.9.2" + "@types/node": "^24.10.8", + "simple-git": "^3.30.0", + "tldts": "^7.0.19" }, "devDependencies": { "@npmcli/package-json": "^7.0.4", "@types/npmcli__package-json": "^4.0.4", "@types/semver": "^7.7.1", - "esbuild": "^0.27.0", - "eslint": "^9.38.0", + "@typescriptprime/securereq": "^1.0.1", + "chokidar": "^5.0.0", + "esbuild": "^0.27.2", + "eslint": "^9.39.2", "semver": "^7.7.3", "tsx": "^4.21.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.46.2", + "typescript-eslint": "^8.53.0", "zod": "^4.3.5" } -} \ No newline at end of file +} From b7c2fd80cda40f7b8be780017bcef353ed4ecc9a Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Thu, 15 Jan 2026 12:18:40 +0000 Subject: [PATCH 06/27] fix: `baseUrl` in `tsconfig.json` leads fatal of `tsx` execution --- builder/tsconfig.json | 3 --- tsconfig.json | 9 ++++----- userscript/tsconfig.json | 3 --- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/builder/tsconfig.json b/builder/tsconfig.json index 5d2dc03..bcb6707 100644 --- a/builder/tsconfig.json +++ b/builder/tsconfig.json @@ -1,8 +1,5 @@ { "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": "../../" - }, "include": [ "source/**/*.ts", "test/**/*.ts" diff --git a/tsconfig.json b/tsconfig.json index 776fc5d..ac06520 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,11 +7,10 @@ "alwaysStrict": false, "skipLibCheck": true, "paths": { - "@builder/*": ["./builder/sources/*"], - "@userscript/*": ["./userscript/sources/*"], - "@root/*": ["./sources/*"], + "@builder/*": ["./builder/source/*"], + "@userscript/*": ["./userscript/source/*"], + "@root/*": ["./source/*"], "@reporoot/*": ["./*"] - }, - "baseUrl": "." + } } } \ No newline at end of file diff --git a/userscript/tsconfig.json b/userscript/tsconfig.json index 590db8a..ed149fb 100644 --- a/userscript/tsconfig.json +++ b/userscript/tsconfig.json @@ -1,8 +1,5 @@ { "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": "../../" - }, "include": [ "source/**/*.ts" ] From 45468dbbfbae39314b7e9007dde3df2c678a09d7 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Fri, 16 Jan 2026 11:36:10 +0000 Subject: [PATCH 07/27] chore: add http-server utils --- builder/source/utils/http-server.ts | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 builder/source/utils/http-server.ts diff --git a/builder/source/utils/http-server.ts b/builder/source/utils/http-server.ts new file mode 100644 index 0000000..7de809f --- /dev/null +++ b/builder/source/utils/http-server.ts @@ -0,0 +1,33 @@ +import * as HTTP from 'node:http' +import * as Fs from 'node:fs' + +function IsLoopBack(IP: string) { + return IP === '127.0.0.1' || IP === '::1' || IP === '::ffff:127.0.0.1' +} + +export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHTTPResponse: boolean) { + const HTTPServer = HTTP.createServer((Req, Res) => { + if (!FileName.includes(Req.url?.substring(1) || '')) { + Res.writeHead(404) + Res.end() + return + } else if (!IsLoopBack(Req.socket.remoteAddress)) { + Res.writeHead(403) + Res.end() + return + } else if (ShouldPreventHTTPResponse || !Fs.existsSync(`${process.cwd()}/dist/${Req.url?.substring(1)}`)) { + Res.writeHead(503) + Res.end('File not built yet.') + return + } + + const Content = Fs.readFileSync(`${process.cwd()}/dist/${Req.url?.substring(1)}`, 'utf-8') + Res.writeHead(200, { + 'content-type': 'application/javascript; charset=utf-8', + 'content-length': new TextEncoder().encode(Content).byteLength.toString() + }) + Res.end(Content) + }) + + HTTPServer.listen(Port) +} \ No newline at end of file From e4dcb6ae47e905e27ceac45b4db0b7d9863bcb2c Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Fri, 16 Jan 2026 11:36:31 +0000 Subject: [PATCH 08/27] chore: update .gitignore and package.json for new monorepo structure --- .gitignore | 3 ++- builder/package.json | 11 +++++++---- package.json | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index c2d28d0..c7a2ae9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules dist -sources/src/#generated-*.ts \ No newline at end of file +sources/src/#generated-*.ts +.buildcache \ No newline at end of file diff --git a/builder/package.json b/builder/package.json index 962d537..30d80af 100644 --- a/builder/package.json +++ b/builder/package.json @@ -5,22 +5,25 @@ "scripts": { "lint": "tsc --noEmit && eslint **/*.ts", "build": "tsx source/build.ts", - "debug": "tsx source/debug.ts" + "debug": "tsx source/debug.ts", + "cache": "tsx source/cache.ts", + "clean": "rm -rf dist && rm -rf .buildcache" }, "dependencies": { - "@types/node": "^24.10.8", - "simple-git": "^3.30.0", - "tldts": "^7.0.19" + "@types/node": "^24.10.8" }, "devDependencies": { + "@adguard/agtree": "^3.4.3", "@npmcli/package-json": "^7.0.4", "@types/npmcli__package-json": "^4.0.4", "@types/semver": "^7.7.1", + "@typescriptprime/parsing": "^1.0.4", "@typescriptprime/securereq": "^1.0.1", "chokidar": "^5.0.0", "esbuild": "^0.27.2", "eslint": "^9.39.2", "semver": "^7.7.3", + "tldts": "^7.0.19", "tsx": "^4.21.0", "typescript": "^5.9.3", "typescript-eslint": "^8.53.0", diff --git a/package.json b/package.json index 52dad9c..31b569f 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "license": "MPL-2.0", "packageManager": "npm@11.5.1+", "workspaces": [ - "userscript", "builder" + "userscript", + "builder" ] } From f2532bd02d0c43609c70cf360a18e6c140175ac0 Mon Sep 17 00:00:00 2001 From: PiQuark6046 Date: Sun, 18 Jan 2026 21:20:45 +0900 Subject: [PATCH 09/27] Potential fix for code scanning alert no. 4: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- builder/source/utils/http-server.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/builder/source/utils/http-server.ts b/builder/source/utils/http-server.ts index 7de809f..0806c56 100644 --- a/builder/source/utils/http-server.ts +++ b/builder/source/utils/http-server.ts @@ -1,5 +1,6 @@ import * as HTTP from 'node:http' import * as Fs from 'node:fs' +import * as Path from 'node:path' function IsLoopBack(IP: string) { return IP === '127.0.0.1' || IP === '::1' || IP === '::ffff:127.0.0.1' @@ -7,7 +8,18 @@ function IsLoopBack(IP: string) { export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHTTPResponse: boolean) { const HTTPServer = HTTP.createServer((Req, Res) => { - if (!FileName.includes(Req.url?.substring(1) || '')) { + const distRoot = Path.join(process.cwd(), 'dist') + const requestPath = Req.url?.substring(1) || '' + const resolvedPath = Path.resolve(distRoot, requestPath) + + // Ensure the resolved path stays within the dist root to prevent directory traversal + if (!resolvedPath.startsWith(distRoot + Path.sep) && resolvedPath !== distRoot) { + Res.writeHead(403) + Res.end() + return + } + + if (!FileName.includes(requestPath)) { Res.writeHead(404) Res.end() return @@ -15,13 +27,13 @@ export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHT Res.writeHead(403) Res.end() return - } else if (ShouldPreventHTTPResponse || !Fs.existsSync(`${process.cwd()}/dist/${Req.url?.substring(1)}`)) { + } else if (ShouldPreventHTTPResponse || !Fs.existsSync(resolvedPath)) { Res.writeHead(503) Res.end('File not built yet.') return } - const Content = Fs.readFileSync(`${process.cwd()}/dist/${Req.url?.substring(1)}`, 'utf-8') + const Content = Fs.readFileSync(resolvedPath, 'utf-8') Res.writeHead(200, { 'content-type': 'application/javascript; charset=utf-8', 'content-length': new TextEncoder().encode(Content).byteLength.toString() From f6f448f010cbb5abe8bd45a19d263aac9a3a47af Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 12:24:23 +0000 Subject: [PATCH 10/27] chore: follow PascalCase --- builder/source/utils/http-server.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builder/source/utils/http-server.ts b/builder/source/utils/http-server.ts index 0806c56..c97a38e 100644 --- a/builder/source/utils/http-server.ts +++ b/builder/source/utils/http-server.ts @@ -8,18 +8,18 @@ function IsLoopBack(IP: string) { export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHTTPResponse: boolean) { const HTTPServer = HTTP.createServer((Req, Res) => { - const distRoot = Path.join(process.cwd(), 'dist') - const requestPath = Req.url?.substring(1) || '' - const resolvedPath = Path.resolve(distRoot, requestPath) + const DistRoot = Path.join(process.cwd(), 'dist') + const RequestPath = Req.url?.substring(1) || '' + const ResolvedPath = Path.resolve(DistRoot, RequestPath) // Ensure the resolved path stays within the dist root to prevent directory traversal - if (!resolvedPath.startsWith(distRoot + Path.sep) && resolvedPath !== distRoot) { + if (!ResolvedPath.startsWith(DistRoot + Path.sep) && ResolvedPath !== DistRoot) { Res.writeHead(403) Res.end() return } - if (!FileName.includes(requestPath)) { + if (!FileName.includes(RequestPath)) { Res.writeHead(404) Res.end() return @@ -27,13 +27,13 @@ export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHT Res.writeHead(403) Res.end() return - } else if (ShouldPreventHTTPResponse || !Fs.existsSync(resolvedPath)) { + } else if (ShouldPreventHTTPResponse || !Fs.existsSync(ResolvedPath)) { Res.writeHead(503) Res.end('File not built yet.') return } - const Content = Fs.readFileSync(resolvedPath, 'utf-8') + const Content = Fs.readFileSync(ResolvedPath, 'utf-8') Res.writeHead(200, { 'content-type': 'application/javascript; charset=utf-8', 'content-length': new TextEncoder().encode(Content).byteLength.toString() From 785d13908ab446abfb589642396182690d13d30d Mon Sep 17 00:00:00 2001 From: PiQuark6046 Date: Sun, 18 Jan 2026 21:26:20 +0900 Subject: [PATCH 11/27] Potential fix for code scanning alert no. 9: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- builder/source/utils/http-server.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builder/source/utils/http-server.ts b/builder/source/utils/http-server.ts index c97a38e..8a27af0 100644 --- a/builder/source/utils/http-server.ts +++ b/builder/source/utils/http-server.ts @@ -8,12 +8,13 @@ function IsLoopBack(IP: string) { export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHTTPResponse: boolean) { const HTTPServer = HTTP.createServer((Req, Res) => { - const DistRoot = Path.join(process.cwd(), 'dist') + const DistRoot = Path.resolve(process.cwd(), 'dist') const RequestPath = Req.url?.substring(1) || '' const ResolvedPath = Path.resolve(DistRoot, RequestPath) + const RelativePath = Path.relative(DistRoot, ResolvedPath) // Ensure the resolved path stays within the dist root to prevent directory traversal - if (!ResolvedPath.startsWith(DistRoot + Path.sep) && ResolvedPath !== DistRoot) { + if (RelativePath.startsWith('..') || Path.isAbsolute(RelativePath)) { Res.writeHead(403) Res.end() return From 57f8f2653eed592350d171b1e6a801e7b0511417 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 20:44:55 +0000 Subject: [PATCH 12/27] feat: add `IndexAdShieldDomainsFromAG` func to refer AG Base filters list --- builder/source/references/filterslists/ADG.ts | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 builder/source/references/filterslists/ADG.ts diff --git a/builder/source/references/filterslists/ADG.ts b/builder/source/references/filterslists/ADG.ts new file mode 100644 index 0000000..2c10883 --- /dev/null +++ b/builder/source/references/filterslists/ADG.ts @@ -0,0 +1,50 @@ +import { HTTPS2Request } from '@typescriptprime/securereq' +import * as AGTree from '@adguard/agtree' + +const AGBaseFilterListSpecificURL = 'https://adguardteam.github.io/AdguardFilters/BaseFilter/sections/specific.txt' +const AGBaseFilterListAdShieldKeys = { + Starting: 'START: Ad-Shield ad reinsertion', + Ending: 'END: Ad-Shield ad reinsertion' +} + +export async function IndexAdShieldDomainsFromAG(): Promise> { + const FiltersListContent = await HTTPS2Request(new URL(AGBaseFilterListSpecificURL), { ExpectedAs: 'String' }) + const AGTreeFiltersList = AGTree.FilterListParser.parse(FiltersListContent.Body) + let StatrtingLine = -1 + let EndingLine = -1 + for (const [Index, Filter] of AGTreeFiltersList.children.entries()) { + if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(AGBaseFilterListAdShieldKeys.Starting)) { + StatrtingLine = Index + } else if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(AGBaseFilterListAdShieldKeys.Ending)) { + EndingLine = Index + } else if (StatrtingLine !== -1 && EndingLine !== -1) { + break + } else if (Index === AGTreeFiltersList.children.length - 1) { + throw new Error('Could not find Ad-Shield ad reinsertion section in ' + AGBaseFilterListSpecificURL) + } + } + const AdShieldFilters = AGTreeFiltersList.children.filter((Filter, Index) => Index > StatrtingLine && Index < EndingLine) + + const AdShieldDomains = new Set() + for (const Filter of AdShieldFilters) { + if (Filter.category === 'Cosmetic' && Filter.type === 'ScriptletInjectionRule') { + Filter.domains.children.forEach(Domain => AdShieldDomains.add(Domain.value)) + } else if (Filter.category === 'Cosmetic' && Filter.type === 'JsInjectionRule') { + Filter.domains.children.forEach(Domain => AdShieldDomains.add(Domain.value)) + } else if (Filter.category === 'Network' && Filter.type === 'NetworkRule' && typeof Filter.modifiers !== 'undefined' && Filter.modifiers.children.some(M => M.name.value === 'domain')) { + let DomainValue = Filter.modifiers.children.find(M => M.name.value === 'domain').value.value + DomainValue.split('|').forEach(Domain => AdShieldDomains.add(Domain)) + } + } + + let FilteredDomains = [...AdShieldDomains].filter(Domain => { + try { + new URLPattern(`https://${Domain}/`) + } catch { + return false + } + return true + }) + + return new Set(FilteredDomains) +} \ No newline at end of file From 5d8fdcf2061cd97758c958c9c72fa1770f5acb28 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 20:50:32 +0000 Subject: [PATCH 13/27] feat: add checking func to discard all CDN domains owned by Ad-Shield --- builder/source/references/filterslists/ADG.ts | 3 ++- builder/source/references/filterslists/keywords.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 builder/source/references/filterslists/keywords.ts diff --git a/builder/source/references/filterslists/ADG.ts b/builder/source/references/filterslists/ADG.ts index 2c10883..7f39e82 100644 --- a/builder/source/references/filterslists/ADG.ts +++ b/builder/source/references/filterslists/ADG.ts @@ -1,5 +1,6 @@ import { HTTPS2Request } from '@typescriptprime/securereq' import * as AGTree from '@adguard/agtree' +import { AdShieldCDNDomains } from './keywords.js' const AGBaseFilterListSpecificURL = 'https://adguardteam.github.io/AdguardFilters/BaseFilter/sections/specific.txt' const AGBaseFilterListAdShieldKeys = { @@ -43,7 +44,7 @@ export async function IndexAdShieldDomainsFromAG(): Promise> { } catch { return false } - return true + return !AdShieldCDNDomains.has(Domain) }) return new Set(FilteredDomains) diff --git a/builder/source/references/filterslists/keywords.ts b/builder/source/references/filterslists/keywords.ts new file mode 100644 index 0000000..d3de966 --- /dev/null +++ b/builder/source/references/filterslists/keywords.ts @@ -0,0 +1,13 @@ +import { HTTPS2Request } from '@typescriptprime/securereq' + +export const AdShieldCDNDomains: Set = new Set([ + 'html-load.com', + 'css-load.com', + 'ads.adthrive.com' +]) + +export async function IsAdShieldCDNDomain(Domain: string): Promise { + const AdShieldCDNCheckResponse = await HTTPS2Request(new URL(`https://${Domain}/`), { ExpectedAs: 'String' }).catch(() => false) + return typeof AdShieldCDNCheckResponse !== 'boolean' && AdShieldCDNCheckResponse.StatusCode === 200 && + AdShieldCDNCheckResponse.Body.includes('This domain is a part of the Ad-Shield (ad-shield.io) platform,') +} \ No newline at end of file From 0b7640a38cb372ad737c7bad944dc088da821105 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 21:19:19 +0000 Subject: [PATCH 14/27] feat: add `IndexAdShieldDomainsFromUBO` func to refer uBO filters list --- builder/source/references/filterslists/uBO.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 builder/source/references/filterslists/uBO.ts diff --git a/builder/source/references/filterslists/uBO.ts b/builder/source/references/filterslists/uBO.ts new file mode 100644 index 0000000..f3b9c92 --- /dev/null +++ b/builder/source/references/filterslists/uBO.ts @@ -0,0 +1,51 @@ +import { HTTPS2Request } from '@typescriptprime/securereq' +import * as AGTree from '@adguard/agtree' +import { AdShieldCDNDomains } from './keywords.js' + +const UBOFilterListSpecificURL = 'https://ublockorigin.github.io/uAssets/filters/filters.min.txt' +const UBOFilterListAdShieldKeys = { + Starting: '! SECTION: Ad-Shield', + Ending: '! !SECTION: Ad-Shield' +} + +export async function IndexAdShieldDomainsFromUBO(): Promise> { + const FiltersListContent = await HTTPS2Request(new URL(UBOFilterListSpecificURL), { ExpectedAs: 'String' }) + const AGTreeFiltersList = AGTree.FilterListParser.parse(FiltersListContent.Body) + let StatrtingLine = -1 + let EndingLine = -1 + for (const [Index, Filter] of AGTreeFiltersList.children.entries()) { + if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(UBOFilterListAdShieldKeys.Starting)) { + StatrtingLine = Index + } else if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(UBOFilterListAdShieldKeys.Ending)) { + EndingLine = Index + } else if (StatrtingLine !== -1 && EndingLine !== -1) { + break + } else if (Index === AGTreeFiltersList.children.length - 1) { + throw new Error('Could not find Ad-Shield ad reinsertion section in ' + UBOFilterListSpecificURL) + } + } + const AdShieldFilters = AGTreeFiltersList.children.filter((Filter, Index) => Index > StatrtingLine && Index < EndingLine) + + const AdShieldDomains = new Set() + for (const Filter of AdShieldFilters) { + if (Filter.category === 'Cosmetic' && Filter.type === 'ScriptletInjectionRule') { + Filter.domains.children.forEach(Domain => AdShieldDomains.add(Domain.value)) + } else if (Filter.category === 'Cosmetic' && Filter.type === 'JsInjectionRule') { + Filter.domains.children.forEach(Domain => AdShieldDomains.add(Domain.value)) + } else if (Filter.category === 'Network' && Filter.type === 'NetworkRule' && typeof Filter.modifiers !== 'undefined' && Filter.modifiers.children.some(M => M.name.value === 'domain')) { + let DomainValue = Filter.modifiers.children.find(M => M.name.value === 'domain').value.value + DomainValue.split('|').forEach(Domain => AdShieldDomains.add(Domain)) + } + } + + let FilteredDomains = [...AdShieldDomains].filter(Domain => { + try { + new URLPattern(`https://${Domain}/`) + } catch { + return false + } + return !AdShieldCDNDomains.has(Domain) + }) + + return new Set(FilteredDomains) +} \ No newline at end of file From c0bf75c392c536b7115f06e7885d1ddfdf6e09ac Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 21:21:23 +0000 Subject: [PATCH 15/27] feat: add `FetchAdShieldDomains` func to refer AG Base and uBO filters list --- builder/source/references/filterslists.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 builder/source/references/filterslists.ts diff --git a/builder/source/references/filterslists.ts b/builder/source/references/filterslists.ts new file mode 100644 index 0000000..a4a03c3 --- /dev/null +++ b/builder/source/references/filterslists.ts @@ -0,0 +1,13 @@ +import { HTTPSRequest } from '@typescriptprime/securereq' +import { IndexAdShieldDomainsFromAG } from './filterslists/ADG.js' +import { IndexAdShieldDomainsFromUBO } from './filterslists/uBO.js' + +export async function FetchAdShieldDomains(): Promise> { + const [AGDomains, UBODomains] = await Promise.all([ + IndexAdShieldDomainsFromAG(), + IndexAdShieldDomainsFromUBO() + ]) + + const CombinedDomains = new Set([...AGDomains, ...UBODomains]) + return CombinedDomains +} \ No newline at end of file From b253d9b1d0adb204560ae925d71a55cd3dd0d191 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 21:22:01 +0000 Subject: [PATCH 16/27] chore: remove unnecessary import --- builder/source/references/filterslists.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/builder/source/references/filterslists.ts b/builder/source/references/filterslists.ts index a4a03c3..9d9af97 100644 --- a/builder/source/references/filterslists.ts +++ b/builder/source/references/filterslists.ts @@ -1,4 +1,3 @@ -import { HTTPSRequest } from '@typescriptprime/securereq' import { IndexAdShieldDomainsFromAG } from './filterslists/ADG.js' import { IndexAdShieldDomainsFromUBO } from './filterslists/uBO.js' From 4be8bd9ed2c0c67c14ec638c8e60228a48aab197 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 21:25:42 +0000 Subject: [PATCH 17/27] ci: run ESLint workflow for all branches --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cb716a..8781b98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Check building and linting on: push: - branches-ignore: [ "main" ] + branches: [ "**" ] pull_request: # The branches below must be a subset of the branches above branches: [ "**" ] From 5a5ad1122a44fa03de43fa92d0b63a6fd5f2722e Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 21:57:38 +0000 Subject: [PATCH 18/27] chore: rename `FetchAdShieldDomainsFromFiltersLists` --- builder/source/references/filterslists.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/source/references/filterslists.ts b/builder/source/references/filterslists.ts index 9d9af97..7189599 100644 --- a/builder/source/references/filterslists.ts +++ b/builder/source/references/filterslists.ts @@ -1,7 +1,7 @@ import { IndexAdShieldDomainsFromAG } from './filterslists/ADG.js' import { IndexAdShieldDomainsFromUBO } from './filterslists/uBO.js' -export async function FetchAdShieldDomains(): Promise> { +export async function FetchAdShieldDomainsFromFiltersLists(): Promise> { const [AGDomains, UBODomains] = await Promise.all([ IndexAdShieldDomainsFromAG(), IndexAdShieldDomainsFromUBO() From 79f1ab58859f949bb97476dfd5cc7649615f2a35 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 21:59:11 +0000 Subject: [PATCH 19/27] feat: add `FetchAdShieldDomains` func to refer all references --- builder/source/references/index.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 builder/source/references/index.ts diff --git a/builder/source/references/index.ts b/builder/source/references/index.ts new file mode 100644 index 0000000..c0c47ec --- /dev/null +++ b/builder/source/references/index.ts @@ -0,0 +1,12 @@ +import { FetchAdShieldDomainsFromFiltersLists } from './filterslists.js' +import { FetchIABSellersJsonData } from './iabsellers.js' + +export async function FetchAdShieldDomains(): Promise> { + const [IABSellersDomains, FiltersListsDomains] = await Promise.all([ + FetchIABSellersJsonData(), + FetchAdShieldDomainsFromFiltersLists() + ]) + + const CombinedDomains = new Set([...IABSellersDomains, ...FiltersListsDomains]) + return CombinedDomains +} \ No newline at end of file From 5dab3b18e6ac29f8e009e6987e24ea4f2e8c298a Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 22:16:59 +0000 Subject: [PATCH 20/27] feat: provide `CustomDefinedMatches` Set --- builder/source/references/custom-defined.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 builder/source/references/custom-defined.ts diff --git a/builder/source/references/custom-defined.ts b/builder/source/references/custom-defined.ts new file mode 100644 index 0000000..1f10b78 --- /dev/null +++ b/builder/source/references/custom-defined.ts @@ -0,0 +1 @@ +export const CustomDefinedMatches: Set = new Set() \ No newline at end of file From b1c9c84d37e8b3ca5c0ed151e7d21ec082fb445d Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 22:17:18 +0000 Subject: [PATCH 21/27] feat: add building cache for caching remote domains --- builder/source/cache.ts | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 builder/source/cache.ts diff --git a/builder/source/cache.ts b/builder/source/cache.ts new file mode 100644 index 0000000..bfb09d7 --- /dev/null +++ b/builder/source/cache.ts @@ -0,0 +1,46 @@ +import * as Zod from 'zod' +import * as Fs from 'node:fs' +import * as Process from 'node:process' +import { FetchAdShieldDomains } from './references/index.js' + +const CachePath = Process.cwd() + '/.buildcache' +const CacheDomainsPath = CachePath + '/domains.json' + +export function CreateCache(Domains: Set) { + if (!Fs.existsSync(CachePath)) { + Fs.mkdirSync(CachePath) + } else if (!Fs.statSync(CachePath).isDirectory()) { + throw new Error('.buildcache exists and is not a directory!') + } + if (Fs.existsSync(CacheDomainsPath)) { + throw new Error('Cache already exists!') + } + Fs.writeFileSync(CacheDomainsPath, JSON.stringify([...Domains], null, 2), { encoding: 'utf-8' }) +} + +export async function LoadCache(): Promise> { + if (!Fs.existsSync(CacheDomainsPath)) { + throw new Error('Cache does not exist!') + } + const DomainsRaw = Fs.readFileSync(CacheDomainsPath, { encoding: 'utf-8' }) + const DomainsArray: string[] = JSON.parse(DomainsRaw) + await Zod.array(Zod.string().refine((Value) => { + try { + new URLPattern(`https://${Value}/`) + return true + } catch { + return false + } + })).parseAsync(DomainsArray) + return new Set(DomainsArray) +} + +export async function LoadDomainsFromCache(): Promise> { + if (!Fs.existsSync(CacheDomainsPath)) { + const Domains = await FetchAdShieldDomains() + CreateCache(Domains) + return Domains + } else { + return await LoadCache() + } +} \ No newline at end of file From 9fb4d9fee69c2f3fa0969422c33d1e621537c8f4 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 23:54:59 +0000 Subject: [PATCH 22/27] feat: create `CreateBanner` func --- builder/source/banner/index.ts | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 builder/source/banner/index.ts diff --git a/builder/source/banner/index.ts b/builder/source/banner/index.ts new file mode 100644 index 0000000..89cc27a --- /dev/null +++ b/builder/source/banner/index.ts @@ -0,0 +1,47 @@ +export interface BannerOptions { + Version: string + BuildType: 'production' | 'development' + Domains: Set + Author: string + Name: string + Namespace: string + HomepageURL: URL + SupportURL: URL + UpdateURL: URL + DownloadURL: URL + License: string + Description: Record<'en' | 'ko' | 'ja' | string, string> +} + +export function CreateBanner(Options: BannerOptions): string { + let BannerString: string = '// ==UserScript==\n' + BannerString += `// @name ${Options.BuildType === 'production' ? Options.Name : Options.Name + ' (Development)'}\n` + BannerString += '//\n' + BannerString += `// @namespace ${Options.Namespace}\n` + BannerString += `// @homepageURL ${Options.HomepageURL.href}\n` + BannerString += `// @supportURL ${Options.SupportURL.href}\n` + BannerString += `// @updateURL ${Options.UpdateURL.href}\n` + BannerString += `// @downloadURL ${Options.DownloadURL.href}\n` + BannerString += `// @license ${Options.License}\n` + BannerString += '//\n' + BannerString += `// @version ${Options.Version}\n` + BannerString += `// @author ${Options.Author}\n` + BannerString += '//\n' + BannerString += '// @grant unsafeWindow\n' + BannerString += '// @run-at document-start\n' + BannerString += '//\n' + BannerString += `// @description ${Options.Description['en']}\n` + + for (const Key of Object.keys(Options.Description)) { + if (Key === 'en') continue + BannerString += `// @description:${Key} ${Options.Description[Key]}\n` + } + BannerString += '//\n' + + for (const Domain of Options.Domains) { + BannerString += `// @match *://${Domain}/*\n` + BannerString += `// @match *://*.${Domain}/*\n` + } + BannerString += '// ==/UserScript==\n\n' + return BannerString +} \ No newline at end of file From 33f3ae66f868249c0d5031c8e5d77a21dd70f632 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 23:55:37 +0000 Subject: [PATCH 23/27] fix: path is not resolved correctly if `npm run` is used --- builder/source/utils/http-server.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/builder/source/utils/http-server.ts b/builder/source/utils/http-server.ts index 8a27af0..812d86f 100644 --- a/builder/source/utils/http-server.ts +++ b/builder/source/utils/http-server.ts @@ -1,5 +1,6 @@ import * as HTTP from 'node:http' import * as Fs from 'node:fs' +import * as Process from 'node:process' import * as Path from 'node:path' function IsLoopBack(IP: string) { @@ -8,10 +9,13 @@ function IsLoopBack(IP: string) { export function RunDebugServer(Port: number, FileName: string[], ShouldPreventHTTPResponse: boolean) { const HTTPServer = HTTP.createServer((Req, Res) => { - const DistRoot = Path.resolve(process.cwd(), 'dist') + let ProjectRoot = Process.cwd() + if (Process.cwd().endsWith('/builder')) { + ProjectRoot = Process.cwd() + '/..' + } const RequestPath = Req.url?.substring(1) || '' - const ResolvedPath = Path.resolve(DistRoot, RequestPath) - const RelativePath = Path.relative(DistRoot, ResolvedPath) + const ResolvedPath = Path.resolve(ProjectRoot + '/dist', RequestPath) + const RelativePath = Path.relative(ProjectRoot + '/dist', ResolvedPath) // Ensure the resolved path stays within the dist root to prevent directory traversal if (RelativePath.startsWith('..') || Path.isAbsolute(RelativePath)) { From 3b37419db0388875dffe9c7b3d419ef445460f3d Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Sun, 18 Jan 2026 23:55:55 +0000 Subject: [PATCH 24/27] feat: use new builder --- builder/package.json | 7 +- builder/source/banner.txt | 22 ---- builder/source/build.ts | 74 +++++++++++ builder/source/buildci.ts | 13 ++ builder/source/builder.ts | 117 ------------------ builder/source/debug.ts | 37 ++++++ .../source/utils/wildcard-suffix-converter.ts | 12 ++ package.json | 6 +- userscript/package.json | 2 +- 9 files changed, 143 insertions(+), 147 deletions(-) delete mode 100644 builder/source/banner.txt create mode 100644 builder/source/build.ts create mode 100644 builder/source/buildci.ts delete mode 100644 builder/source/builder.ts create mode 100644 builder/source/debug.ts create mode 100644 builder/source/utils/wildcard-suffix-converter.ts diff --git a/builder/package.json b/builder/package.json index 30d80af..9e32c7c 100644 --- a/builder/package.json +++ b/builder/package.json @@ -4,13 +4,12 @@ "type": "module", "scripts": { "lint": "tsc --noEmit && eslint **/*.ts", - "build": "tsx source/build.ts", + "build": "tsx source/buildci.ts", "debug": "tsx source/debug.ts", - "cache": "tsx source/cache.ts", "clean": "rm -rf dist && rm -rf .buildcache" }, "dependencies": { - "@types/node": "^24.10.8" + "@types/node": "^24.10.9" }, "devDependencies": { "@adguard/agtree": "^3.4.3", @@ -18,7 +17,7 @@ "@types/npmcli__package-json": "^4.0.4", "@types/semver": "^7.7.1", "@typescriptprime/parsing": "^1.0.4", - "@typescriptprime/securereq": "^1.0.1", + "@typescriptprime/securereq": "^1.1.0", "chokidar": "^5.0.0", "esbuild": "^0.27.2", "eslint": "^9.39.2", diff --git a/builder/source/banner.txt b/builder/source/banner.txt deleted file mode 100644 index ebafe48..0000000 --- a/builder/source/banner.txt +++ /dev/null @@ -1,22 +0,0 @@ -// ==UserScript== -// @name %%NAME%% -// @encoding utf-8 -// @namespace https://github.com/FilteringDev/tinyShield -// @homepageURL https://github.com/FilteringDev/tinyShield -// @supportURL https://github.com/FilteringDev/tinyShield/issues -// @updateURL https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js -// @downloadURL https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js -// @license MIT -// -// @version %%VERSION_VALUE%% -// @author PiQuark6046 and contributors -// -%%DOMAIN_INJECTION%% -// -// @description tinyShield allows AdGuard, uBlock Origin, Brave and ABP to resist against Ad-Shield quickly. -// @description:ko tinyShield는 AdGuard, uBlock Origin, Brave 와 ABP가 애드쉴드에 빠르게 저항할 수 있도록 합니다. -// @description:ja tinyShieldを使うと、AdGuard, uBlock Origin, Brave, およびABPがAd-Shieldに素早く対抗できます。 -// -// @grant unsafeWindow -// @run-at document-start -// ==/UserScript== diff --git a/builder/source/build.ts b/builder/source/build.ts new file mode 100644 index 0000000..2bdaa7d --- /dev/null +++ b/builder/source/build.ts @@ -0,0 +1,74 @@ +import * as ESBuild from 'esbuild' +import * as Zod from 'zod' +import * as Process from 'node:process' +import * as TLDTS from 'tldts' +import PackageJson from '@npmcli/package-json' +import { LoadDomainsFromCache } from './cache.js' +import { FetchAdShieldDomains } from './references/index.js' +import { CustomDefinedMatches } from './references/custom-defined.js' +import { ConvertWildcardSuffixToRegexPattern } from './utils/wildcard-suffix-converter.js' +import { CreateBanner } from './banner/index.js' + +export type BuildOptions = { + Minify: boolean + UseCache: boolean + BuildType: 'production' | 'development' +} + +export async function Build(OptionsParam?: BuildOptions): Promise { + const Options = await Zod.strictObject({ + Minify: Zod.boolean(), + UseCache: Zod.boolean(), + BuildType: Zod.enum(['production', 'development']) + }).parseAsync(OptionsParam) + + let MatchingDomains: Set = new Set() + if (Options.UseCache) { + MatchingDomains = await LoadDomainsFromCache() + } else { + MatchingDomains = await FetchAdShieldDomains() + } + CustomDefinedMatches.forEach(Domain => MatchingDomains.add(Domain)) + + MatchingDomains = new Set([...MatchingDomains].map(Domain => TLDTS.parse(Domain).domain ?? Domain).filter((D): D is string => D !== null)) + for (const Domain of MatchingDomains) { + if (Domain.endsWith('.*')) { + MatchingDomains.delete(Domain) + ConvertWildcardSuffixToRegexPattern(Domain).forEach(GeneratedPattern => MatchingDomains.add(GeneratedPattern)) + } + } + + const Banner = CreateBanner({ + Version: (await PackageJson.load(Process.cwd())).content.version ?? '0.0.0', + BuildType: Options.BuildType ?? 'production', + Domains: MatchingDomains, + Name: 'tinyShield', + Namespace: 'https://github.com/FilteringDev/tinyShield', + DownloadURL: new URL('https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js'), + UpdateURL: new URL('https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js'), + HomepageURL: new URL('https://github.com/FilteringDev/tinyShield'), + SupportURL: new URL('https://github.com/FilteringDev/tinyShield/issues'), + License: 'MPL-2.0', + Author: 'PiQuark6046 and contributors', + Description: { + en: 'tinyShield allows AdGuard, uBlock Origin, Brave and ABP to resist against Ad-Shield quickly.', + ko: 'tinyShield는 AdGuard, uBlock Origin, Brave 와 ABP가 애드쉴드에 빠르게 저항할 수 있도록 합니다.', + ja: 'tinyShieldを使うと、AdGuard, uBlock Origin, Brave, およびABPがAd-Shieldに素早く対抗できます。' + } + }) + + let ProjectRoot = Process.cwd() + if (Process.cwd().endsWith('/builder')) { + ProjectRoot = Process.cwd() + '/..' + } + await ESBuild.build({ + entryPoints: [ProjectRoot + '/userscript/source/index.ts'], + bundle: true, + minify: Options.Minify, + outfile: `${ProjectRoot}/dist/tinyShield${Options.BuildType === 'development' ? '.dev' : ''}.user.js`, + banner: { + js: Banner + }, + target: ['es2024', 'chrome119', 'firefox142', 'safari26'] + }) +} \ No newline at end of file diff --git a/builder/source/buildci.ts b/builder/source/buildci.ts new file mode 100644 index 0000000..6abe627 --- /dev/null +++ b/builder/source/buildci.ts @@ -0,0 +1,13 @@ +import * as Zod from 'zod' +import * as Process from 'node:process' +import { PreProcessing, PostProcessing } from '@typescriptprime/parsing' +import { Build, BuildOptions } from './build.js' + +let ParsedArgv = (await PostProcessing(PreProcessing(Process.argv))).Options +let Options = await Zod.strictObject({ + Minify: Zod.string().pipe(Zod.enum(['true', 'false'])).transform(Value => Value === 'true').default(true), + UseCache: Zod.string().pipe(Zod.enum(['true', 'false'])).transform(Value => Value === 'true').default(true), + BuildType: Zod.enum(['production', 'development']) +}).parseAsync(ParsedArgv) + +await Build(Options) \ No newline at end of file diff --git a/builder/source/builder.ts b/builder/source/builder.ts deleted file mode 100644 index 326276f..0000000 --- a/builder/source/builder.ts +++ /dev/null @@ -1,117 +0,0 @@ -// This is a build script temporarily. -// If development of https://github.com/TypescriptPrime/userscript-build-toolkit is completed, -// this script will be replaced by that package. - -import * as ESBuild from 'esbuild' -import * as Zod from 'zod' -import PackageJson from '@npmcli/package-json' -import * as Semver from 'semver' -import * as Fs from 'node:fs' -import * as NodeHttps from 'node:https' -import * as Process from 'node:process' - -let BuildType: 'production' | 'development' = Zod.string().refine(BT => BT === 'production' || BT === 'development').default('production').parse(Process.argv[3] ?? 'production') -console.log('Build type set to:', BuildType) - -let Version: string = (await PackageJson.load('./')).content.version -Version = await Zod.string().refine(V => Semver.valid(V) !== null).parseAsync(Version) -console.log('Applying version value:', Version) - -let DomainsList: Set = new Set() - -const IABSellersJsonURL = 'https://info.ad-shield.io/sellers.json' -const IABSellersJsonResponse: { StatusCode: number, Headers: Record, Body: string } = await new Promise((Resolve, Reject) => { - const IABSellersJsonReq = NodeHttps.get({ - hostname: new URL(IABSellersJsonURL).hostname, - path: new URL(IABSellersJsonURL).pathname, - headers: { - 'user-agent': 'node/v24.12.0 linux x64 workspaces/true' - }, - ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256', - ecdhCurve: 'X25519MLKEM768', - minVersion: 'TLSv1.3' - }, (Res) => { - const Chunks: Buffer[] = [] - Res.on('data', (Chunk: Buffer) => Chunks.push(Chunk)) - Res.on('end', () => { - Resolve({ - StatusCode: Res.statusCode, - Headers: Res.headers, - Body: new TextDecoder().decode(Buffer.concat(Chunks)) - }) - }) - IABSellersJsonReq.on('error', (Err) => Reject(Err)) - }) -}) -console.log('Fetched IAB Sellers.json with status code:', IABSellersJsonResponse.StatusCode) -let IABSellersJsonData = JSON.parse(IABSellersJsonResponse.Body) as { - // eslint-disable-next-line @typescript-eslint/naming-convention - sellers: Array<{ - // eslint-disable-next-line @typescript-eslint/naming-convention - seller_id: number, - // eslint-disable-next-line @typescript-eslint/naming-convention - seller_type: string, - // eslint-disable-next-line @typescript-eslint/naming-convention - name: string, - // eslint-disable-next-line @typescript-eslint/naming-convention - domain: string - }> -} -IABSellersJsonData = await Zod.object({ - sellers: Zod.array(Zod.object({ - seller_id: Zod.number(), - seller_type: Zod.string(), - name: Zod.string(), - domain: Zod.string().refine(D => { - try { - new URL(`https://${D}`) - } catch { - return false - } - return true - }) - })) -}).parseAsync(IABSellersJsonData) -console.log('Validated IAB Sellers.json data') -for (const SellerEntry of IABSellersJsonData.sellers) { - DomainsList.add(SellerEntry.domain) -} -console.log('Collected', DomainsList.size, 'unique domains from IAB Sellers.json') - -const HeaderLocation = './sources/banner.txt' -let ConvertedHeader: string = '' -for (const Line of Fs.readFileSync(HeaderLocation, 'utf-8').split('\n')) { - if (Line.includes('%%VERSION_VALUE%%')) { - ConvertedHeader += Line.replaceAll('%%VERSION_VALUE%%', Version) + '\n' - } else if (Line.includes('%%NAME%%')) { - ConvertedHeader += Line.replaceAll('%%NAME%%', BuildType === 'production' ? 'tinyShield' : 'tinyShield (Development)') + '\n' - } else if (Line === '%%DOMAIN_INJECTION%%') { - for (const DomainEntry of DomainsList) { - ConvertedHeader += `// @match *://${DomainEntry}/*\n` - ConvertedHeader += `// @match *://*.${DomainEntry}/*\n` - } - } else { - ConvertedHeader += Line + '\n' - } -} -console.log('Generated header with domain injections and processing') -let AttachHeaderPath = `/tmp/${crypto.randomUUID()}` -Fs.writeFileSync(AttachHeaderPath, ConvertedHeader, { encoding: 'utf-8', mode: 0o700 }) -console.log('Written temporary header file to:', AttachHeaderPath) -await ESBuild.build({ - entryPoints: ['./sources/index.ts'], - bundle: true, - minify: BuildType === 'production', - define: { - global: 'window' - }, - inject: ['./sources/esbuild.inject.ts'], - banner: { - js: Fs.readFileSync(AttachHeaderPath, 'utf-8') - }, - target: ['es2024', 'chrome119', 'firefox142', 'safari26'], - outfile: BuildType === 'production' ? './dist/tinyShield.user.js' : './dist/tinyShield.dev.user.js', -}) -console.log('Build completed') -Fs.rmSync(AttachHeaderPath) -console.log('Temporary header file removed') \ No newline at end of file diff --git a/builder/source/debug.ts b/builder/source/debug.ts new file mode 100644 index 0000000..9d684f4 --- /dev/null +++ b/builder/source/debug.ts @@ -0,0 +1,37 @@ + +import * as Chokidar from 'chokidar' +import * as Process from 'node:process' +import * as Crypto from 'node:crypto' +import { RunDebugServer } from './utils/http-server.js' +import { Build } from './build.js' + +const ProcessCwd = Process.cwd() +const WatchingGlob = []; +['builder/', 'userscript/', ''].forEach(Dir => { + WatchingGlob.push(`${ProcessCwd}/${Dir}sources/**/*.ts`) + WatchingGlob.push(`${ProcessCwd}/${Dir}sources/**/*.json`) + WatchingGlob.push(`${ProcessCwd}/${Dir}sources/**/*.txt`) +}) +const Watcher = Chokidar.watch([ + `${ProcessCwd}/sources/**/*.ts`, + `${ProcessCwd}/sources/**/*.json` +], { + cwd: ProcessCwd, + ignored: '**/node_modules/**', +}) + +let BuildCooldownTimer: NodeJS.Timeout = null +let ShouldPreventHTTPResponse = false +Watcher.on('all', (WatcherEvent, WatcherPath) => { + clearTimeout(BuildCooldownTimer) + BuildCooldownTimer = setTimeout(async () => { + console.log(`Detected file change (${WatcherEvent}):`, WatcherPath) + ShouldPreventHTTPResponse = true + await Build({ Minify: false, UseCache: true, BuildType: 'development' }) + ShouldPreventHTTPResponse = false + }, 1500) +}) + +let RandomPort = Crypto.randomInt(8000, 8999) +RunDebugServer(RandomPort, ['tinyShield.dev.user.js'], ShouldPreventHTTPResponse) +console.log(`Debug HTTP server running on http://localhost:${RandomPort}/tinyShield.dev.user.js`) \ No newline at end of file diff --git a/builder/source/utils/wildcard-suffix-converter.ts b/builder/source/utils/wildcard-suffix-converter.ts new file mode 100644 index 0000000..3ecb00a --- /dev/null +++ b/builder/source/utils/wildcard-suffix-converter.ts @@ -0,0 +1,12 @@ +const PublicSuffixList = [ + 'com', 'org', 'co', 'de', 'ru', 'fr', 'me', 'it', 'nl', 'io', 'cc', 'in', 'pl', 'xyz', 'es', 'se', 'uk', 'tv', 'info', + 'site', 'us', 'online', 'ch', 'at', 'eu', 'top', 'be', 'cz', 'app', 'ca', 'to', 'jp', 'dev', 'kr' +] + +export function ConvertWildcardSuffixToRegexPattern(Domain: string): string[] { + const Result: string[] = [] + PublicSuffixList.forEach(Suffix => { + Result.push(Domain.replaceAll(/\.\*$/g, '.' + Suffix)) + }) + return Result +} \ No newline at end of file diff --git a/package.json b/package.json index 31b569f..9025d27 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "description": "", "type": "module", "scripts": { - "build:interface": "esbuild sources/interface.ts --bundle --format=esm --splitting --sourcemap --target=es2024 --external:/node_modules --outdir=dist && tsc sources/interface.ts --outDir dist/types --declaration --emitDeclarationOnly", - "build:userscript": "tsx builder.ts -- production", + "build:interface": "esbuild userscript/source/interface.ts --bundle --format=esm --splitting --sourcemap --target=es2024 --external:/node_modules --outdir=dist && tsc userscript/source/interface.ts --outDir dist/types --declaration --emitDeclarationOnly --skipLibCheck", + "build:userscript": "npm run build -w builder -- --minify true --use-cache false --build-type production", "build": "npm run build:interface && npm run build:userscript", - "debug": "tsx builder.ts -- development", + "debug": "npm run debug -w builder", "lint": "npm run lint -w builder && npm run lint -w userscript" }, "keywords": [ diff --git a/userscript/package.json b/userscript/package.json index 92dd5e1..d16ae18 100644 --- a/userscript/package.json +++ b/userscript/package.json @@ -6,6 +6,6 @@ "lint": "tsc --noEmit && eslint **/*.ts" }, "devDependencies": { - "@types/web": "^0.0.317" + "@types/web": "^0.0.318" } } From 4f8b9808a20a0d005c3d349ae8821d0fb6be68f7 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Mon, 19 Jan 2026 00:19:37 +0000 Subject: [PATCH 25/27] chore: add `SubscriptionUrl` customization for debugging --- builder/source/build.ts | 21 ++++++++++++--------- builder/source/buildci.ts | 3 ++- builder/source/debug.ts | 20 ++++++++++++-------- package.json | 2 +- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/builder/source/build.ts b/builder/source/build.ts index 2bdaa7d..60102b9 100644 --- a/builder/source/build.ts +++ b/builder/source/build.ts @@ -12,14 +12,16 @@ import { CreateBanner } from './banner/index.js' export type BuildOptions = { Minify: boolean UseCache: boolean - BuildType: 'production' | 'development' + BuildType: 'production' | 'development', + SubscriptionUrl: string } export async function Build(OptionsParam?: BuildOptions): Promise { const Options = await Zod.strictObject({ Minify: Zod.boolean(), UseCache: Zod.boolean(), - BuildType: Zod.enum(['production', 'development']) + BuildType: Zod.enum(['production', 'development']), + SubscriptionUrl: Zod.string().transform(Value => new URL(Value)).default(new URL('https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js')) }).parseAsync(OptionsParam) let MatchingDomains: Set = new Set() @@ -37,15 +39,20 @@ export async function Build(OptionsParam?: BuildOptions): Promise { ConvertWildcardSuffixToRegexPattern(Domain).forEach(GeneratedPattern => MatchingDomains.add(GeneratedPattern)) } } + + let ProjectRoot = Process.cwd() + if (Process.cwd().endsWith('/builder')) { + ProjectRoot = Process.cwd() + '/..' + } const Banner = CreateBanner({ - Version: (await PackageJson.load(Process.cwd())).content.version ?? '0.0.0', + Version: (await PackageJson.load(ProjectRoot)).content.version ?? '0.0.0', BuildType: Options.BuildType ?? 'production', Domains: MatchingDomains, Name: 'tinyShield', Namespace: 'https://github.com/FilteringDev/tinyShield', - DownloadURL: new URL('https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js'), - UpdateURL: new URL('https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js'), + DownloadURL: Options.SubscriptionUrl, + UpdateURL: Options.SubscriptionUrl, HomepageURL: new URL('https://github.com/FilteringDev/tinyShield'), SupportURL: new URL('https://github.com/FilteringDev/tinyShield/issues'), License: 'MPL-2.0', @@ -57,10 +64,6 @@ export async function Build(OptionsParam?: BuildOptions): Promise { } }) - let ProjectRoot = Process.cwd() - if (Process.cwd().endsWith('/builder')) { - ProjectRoot = Process.cwd() + '/..' - } await ESBuild.build({ entryPoints: [ProjectRoot + '/userscript/source/index.ts'], bundle: true, diff --git a/builder/source/buildci.ts b/builder/source/buildci.ts index 6abe627..07d4cd3 100644 --- a/builder/source/buildci.ts +++ b/builder/source/buildci.ts @@ -7,7 +7,8 @@ let ParsedArgv = (await PostProcessing(PreProcessing(Process.argv) let Options = await Zod.strictObject({ Minify: Zod.string().pipe(Zod.enum(['true', 'false'])).transform(Value => Value === 'true').default(true), UseCache: Zod.string().pipe(Zod.enum(['true', 'false'])).transform(Value => Value === 'true').default(true), - BuildType: Zod.enum(['production', 'development']) + BuildType: Zod.enum(['production', 'development']), + SubscriptionUrl: Zod.string() }).parseAsync(ParsedArgv) await Build(Options) \ No newline at end of file diff --git a/builder/source/debug.ts b/builder/source/debug.ts index 9d684f4..f8f3319 100644 --- a/builder/source/debug.ts +++ b/builder/source/debug.ts @@ -5,18 +5,21 @@ import * as Crypto from 'node:crypto' import { RunDebugServer } from './utils/http-server.js' import { Build } from './build.js' -const ProcessCwd = Process.cwd() +let ProjectRoot = Process.cwd() +if (Process.cwd().endsWith('/builder')) { + ProjectRoot = Process.cwd().replaceAll(/\/builder$/g, '') +} const WatchingGlob = []; ['builder/', 'userscript/', ''].forEach(Dir => { - WatchingGlob.push(`${ProcessCwd}/${Dir}sources/**/*.ts`) - WatchingGlob.push(`${ProcessCwd}/${Dir}sources/**/*.json`) - WatchingGlob.push(`${ProcessCwd}/${Dir}sources/**/*.txt`) + WatchingGlob.push(`${ProjectRoot}/${Dir}sources/**/*.ts`) + WatchingGlob.push(`${ProjectRoot}/${Dir}sources/**/*.json`) + WatchingGlob.push(`${ProjectRoot}/${Dir}sources/**/*.txt`) }) const Watcher = Chokidar.watch([ - `${ProcessCwd}/sources/**/*.ts`, - `${ProcessCwd}/sources/**/*.json` + `${ProjectRoot}/sources/**/*.ts`, + `${ProjectRoot}/sources/**/*.json` ], { - cwd: ProcessCwd, + cwd: ProjectRoot, ignored: '**/node_modules/**', }) @@ -27,11 +30,12 @@ Watcher.on('all', (WatcherEvent, WatcherPath) => { BuildCooldownTimer = setTimeout(async () => { console.log(`Detected file change (${WatcherEvent}):`, WatcherPath) ShouldPreventHTTPResponse = true - await Build({ Minify: false, UseCache: true, BuildType: 'development' }) + await Build({ Minify: false, UseCache: true, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` }) ShouldPreventHTTPResponse = false }, 1500) }) let RandomPort = Crypto.randomInt(8000, 8999) +await Build({ Minify: false, UseCache: true, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` }) RunDebugServer(RandomPort, ['tinyShield.dev.user.js'], ShouldPreventHTTPResponse) console.log(`Debug HTTP server running on http://localhost:${RandomPort}/tinyShield.dev.user.js`) \ No newline at end of file diff --git a/package.json b/package.json index 9025d27..254eacc 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build:interface": "esbuild userscript/source/interface.ts --bundle --format=esm --splitting --sourcemap --target=es2024 --external:/node_modules --outdir=dist && tsc userscript/source/interface.ts --outDir dist/types --declaration --emitDeclarationOnly --skipLibCheck", - "build:userscript": "npm run build -w builder -- --minify true --use-cache false --build-type production", + "build:userscript": "npm run build -w builder -- --minify true --use-cache false --build-type production --SubscriptionUrl https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js", "build": "npm run build:interface && npm run build:userscript", "debug": "npm run debug -w builder", "lint": "npm run lint -w builder && npm run lint -w userscript" From efef1c4d3e0bd5639906861d741e9a8c3b2dc008 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Mon, 19 Jan 2026 00:45:11 +0000 Subject: [PATCH 26/27] fix: chokidar does not built-in glob support --- builder/source/build.ts | 8 +++++--- builder/source/debug.ts | 20 +++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/builder/source/build.ts b/builder/source/build.ts index 60102b9..83c0c33 100644 --- a/builder/source/build.ts +++ b/builder/source/build.ts @@ -13,7 +13,8 @@ export type BuildOptions = { Minify: boolean UseCache: boolean BuildType: 'production' | 'development', - SubscriptionUrl: string + SubscriptionUrl: string, + Version?: string } export async function Build(OptionsParam?: BuildOptions): Promise { @@ -21,7 +22,8 @@ export async function Build(OptionsParam?: BuildOptions): Promise { Minify: Zod.boolean(), UseCache: Zod.boolean(), BuildType: Zod.enum(['production', 'development']), - SubscriptionUrl: Zod.string().transform(Value => new URL(Value)).default(new URL('https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js')) + SubscriptionUrl: Zod.string().transform(Value => new URL(Value)).default(new URL('https://cdn.jsdelivr.net/npm/@filteringdev/tinyshield@latest/dist/tinyShield.user.js')), + Version: Zod.string().optional() }).parseAsync(OptionsParam) let MatchingDomains: Set = new Set() @@ -46,7 +48,7 @@ export async function Build(OptionsParam?: BuildOptions): Promise { } const Banner = CreateBanner({ - Version: (await PackageJson.load(ProjectRoot)).content.version ?? '0.0.0', + Version: Options.Version ?? (await PackageJson.load(ProjectRoot)).content.version ?? '0.0.0', BuildType: Options.BuildType ?? 'production', Domains: MatchingDomains, Name: 'tinyShield', diff --git a/builder/source/debug.ts b/builder/source/debug.ts index f8f3319..494b042 100644 --- a/builder/source/debug.ts +++ b/builder/source/debug.ts @@ -2,6 +2,7 @@ import * as Chokidar from 'chokidar' import * as Process from 'node:process' import * as Crypto from 'node:crypto' +import * as Fs from 'node:fs' import { RunDebugServer } from './utils/http-server.js' import { Build } from './build.js' @@ -11,31 +12,28 @@ if (Process.cwd().endsWith('/builder')) { } const WatchingGlob = []; ['builder/', 'userscript/', ''].forEach(Dir => { - WatchingGlob.push(`${ProjectRoot}/${Dir}sources/**/*.ts`) - WatchingGlob.push(`${ProjectRoot}/${Dir}sources/**/*.json`) - WatchingGlob.push(`${ProjectRoot}/${Dir}sources/**/*.txt`) + WatchingGlob.push(...Fs.globSync(`${ProjectRoot}/${Dir}source/**/*.ts`)) + WatchingGlob.push(...Fs.globSync(`${ProjectRoot}/${Dir}source/**/*.json`)) + WatchingGlob.push(...Fs.globSync(`${ProjectRoot}/${Dir}source/**/*.txt`)) }) -const Watcher = Chokidar.watch([ - `${ProjectRoot}/sources/**/*.ts`, - `${ProjectRoot}/sources/**/*.json` -], { - cwd: ProjectRoot, +const Watcher = Chokidar.watch([...WatchingGlob], { ignored: '**/node_modules/**', }) let BuildCooldownTimer: NodeJS.Timeout = null let ShouldPreventHTTPResponse = false -Watcher.on('all', (WatcherEvent, WatcherPath) => { +let Version: number = 0 +Watcher.on('all', async (WatcherEvent, WatcherPath) => { clearTimeout(BuildCooldownTimer) BuildCooldownTimer = setTimeout(async () => { console.log(`Detected file change (${WatcherEvent}):`, WatcherPath) ShouldPreventHTTPResponse = true - await Build({ Minify: false, UseCache: true, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` }) + await Build({ Version: `0.0.${Version}`, Minify: false, UseCache: true, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` }) + Version++ ShouldPreventHTTPResponse = false }, 1500) }) let RandomPort = Crypto.randomInt(8000, 8999) -await Build({ Minify: false, UseCache: true, BuildType: 'development', SubscriptionUrl: `http://localhost:${RandomPort}/tinyShield.dev.user.js` }) RunDebugServer(RandomPort, ['tinyShield.dev.user.js'], ShouldPreventHTTPResponse) console.log(`Debug HTTP server running on http://localhost:${RandomPort}/tinyShield.dev.user.js`) \ No newline at end of file From b12ec19aa288dc67dd5fbe09a1eec17ea19ad336 Mon Sep 17 00:00:00 2001 From: piquark6046 Date: Mon, 19 Jan 2026 00:45:32 +0000 Subject: [PATCH 27/27] fix: typo --- builder/source/references/filterslists/ADG.ts | 8 ++++---- builder/source/references/filterslists/uBO.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/builder/source/references/filterslists/ADG.ts b/builder/source/references/filterslists/ADG.ts index 7f39e82..3b3bcb9 100644 --- a/builder/source/references/filterslists/ADG.ts +++ b/builder/source/references/filterslists/ADG.ts @@ -11,20 +11,20 @@ const AGBaseFilterListAdShieldKeys = { export async function IndexAdShieldDomainsFromAG(): Promise> { const FiltersListContent = await HTTPS2Request(new URL(AGBaseFilterListSpecificURL), { ExpectedAs: 'String' }) const AGTreeFiltersList = AGTree.FilterListParser.parse(FiltersListContent.Body) - let StatrtingLine = -1 + let StartingLine = -1 let EndingLine = -1 for (const [Index, Filter] of AGTreeFiltersList.children.entries()) { if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(AGBaseFilterListAdShieldKeys.Starting)) { - StatrtingLine = Index + StartingLine = Index } else if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(AGBaseFilterListAdShieldKeys.Ending)) { EndingLine = Index - } else if (StatrtingLine !== -1 && EndingLine !== -1) { + } else if (StartingLine !== -1 && EndingLine !== -1) { break } else if (Index === AGTreeFiltersList.children.length - 1) { throw new Error('Could not find Ad-Shield ad reinsertion section in ' + AGBaseFilterListSpecificURL) } } - const AdShieldFilters = AGTreeFiltersList.children.filter((Filter, Index) => Index > StatrtingLine && Index < EndingLine) + const AdShieldFilters = AGTreeFiltersList.children.filter((Filter, Index) => Index > StartingLine && Index < EndingLine) const AdShieldDomains = new Set() for (const Filter of AdShieldFilters) { diff --git a/builder/source/references/filterslists/uBO.ts b/builder/source/references/filterslists/uBO.ts index f3b9c92..1307063 100644 --- a/builder/source/references/filterslists/uBO.ts +++ b/builder/source/references/filterslists/uBO.ts @@ -11,20 +11,20 @@ const UBOFilterListAdShieldKeys = { export async function IndexAdShieldDomainsFromUBO(): Promise> { const FiltersListContent = await HTTPS2Request(new URL(UBOFilterListSpecificURL), { ExpectedAs: 'String' }) const AGTreeFiltersList = AGTree.FilterListParser.parse(FiltersListContent.Body) - let StatrtingLine = -1 + let StartingLine = -1 let EndingLine = -1 for (const [Index, Filter] of AGTreeFiltersList.children.entries()) { if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(UBOFilterListAdShieldKeys.Starting)) { - StatrtingLine = Index + StartingLine = Index } else if (Filter.category === 'Comment' && typeof Filter.raws.text === 'string' && Filter.raws.text.includes(UBOFilterListAdShieldKeys.Ending)) { EndingLine = Index - } else if (StatrtingLine !== -1 && EndingLine !== -1) { + } else if (StartingLine !== -1 && EndingLine !== -1) { break } else if (Index === AGTreeFiltersList.children.length - 1) { throw new Error('Could not find Ad-Shield ad reinsertion section in ' + UBOFilterListSpecificURL) } } - const AdShieldFilters = AGTreeFiltersList.children.filter((Filter, Index) => Index > StatrtingLine && Index < EndingLine) + const AdShieldFilters = AGTreeFiltersList.children.filter((Filter, Index) => Index > StartingLine && Index < EndingLine) const AdShieldDomains = new Set() for (const Filter of AdShieldFilters) {