Refactor: use foxts more
Some checks failed
Build / Build (push) Has been cancelled
Build / Diff output (push) Has been cancelled
Build / Deploy to Cloudflare Pages (3.114.6) (push) Has been cancelled
Build / Deploy to GitHub and GitLab (push) Has been cancelled

This commit is contained in:
SukkaW 2025-06-02 00:44:07 +08:00
parent 017ff38b5e
commit bdc1f5ec82
9 changed files with 33 additions and 57 deletions

View File

@ -1,11 +1,11 @@
import { parseFelixDnsmasqFromResp } from './lib/parse-dnsmasq'; import { parseFelixDnsmasqFromResp } from './lib/parse-dnsmasq';
import { task } from './trace'; import { task } from './trace';
import { SHARED_DESCRIPTION } from './constants/description'; import { SHARED_DESCRIPTION } from './constants/description';
import { createMemoizedPromise } from './lib/memo-promise'; import { once } from 'foxts/once';
import { DomainsetOutput } from './lib/rules/domainset'; import { DomainsetOutput } from './lib/rules/domainset';
import { $$fetch } from './lib/fetch-retry'; import { $$fetch } from './lib/fetch-retry';
export const getAppleCdnDomainsPromise = createMemoizedPromise(() => $$fetch('https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/apple.china.conf').then(parseFelixDnsmasqFromResp)); export const getAppleCdnDomainsPromise = once(() => $$fetch('https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/apple.china.conf').then(parseFelixDnsmasqFromResp));
export const buildAppleCdn = task(require.main === module, __filename)(async (span) => { export const buildAppleCdn = task(require.main === module, __filename)(async (span) => {
const res: string[] = await span.traceChildPromise('get apple cdn domains', getAppleCdnDomainsPromise()); const res: string[] = await span.traceChildPromise('get apple cdn domains', getAppleCdnDomainsPromise());

View File

@ -1,11 +1,11 @@
import { fetchRemoteTextByLine } from './lib/fetch-text-by-line'; import { fetchRemoteTextByLine } from './lib/fetch-text-by-line';
import { task } from './trace'; import { task } from './trace';
import { createMemoizedPromise } from './lib/memo-promise'; import { once } from 'foxts/once';
import { IPListOutput } from './lib/rules/ip'; import { IPListOutput } from './lib/rules/ip';
import { createFileDescription } from './constants/description'; import { createFileDescription } from './constants/description';
export const getChnCidrPromise = createMemoizedPromise(async function getChnCidr() { export const getChnCidrPromise = once(async function getChnCidr() {
return Promise.all([ return Promise.all([
fetchRemoteTextByLine('https://chnroutes2.cdn.skk.moe/chnroutes.txt', true).then(Array.fromAsync<string>), fetchRemoteTextByLine('https://chnroutes2.cdn.skk.moe/chnroutes.txt', true).then(Array.fromAsync<string>),
fetchRemoteTextByLine('https://gaoyifan.github.io/china-operator-ip/china6.txt', true).then(Array.fromAsync<string>) fetchRemoteTextByLine('https://gaoyifan.github.io/china-operator-ip/china6.txt', true).then(Array.fromAsync<string>)

View File

@ -7,7 +7,7 @@ import { readFileIntoProcessedArray } from './lib/fetch-text-by-line';
import { compareAndWriteFile } from './lib/create-file'; import { compareAndWriteFile } from './lib/create-file';
import { task } from './trace'; import { task } from './trace';
import { SHARED_DESCRIPTION } from './constants/description'; import { SHARED_DESCRIPTION } from './constants/description';
import { createMemoizedPromise } from './lib/memo-promise'; import { once } from 'foxts/once';
import * as yaml from 'yaml'; import * as yaml from 'yaml';
import { appendArrayInPlace } from 'foxts/append-array-in-place'; import { appendArrayInPlace } from 'foxts/append-array-in-place';
import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR, OUTPUT_MODULES_RULES_DIR, SOURCE_DIR } from './constants/dir'; import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR, OUTPUT_MODULES_RULES_DIR, SOURCE_DIR } from './constants/dir';
@ -52,7 +52,7 @@ export function createGetDnsMappingRule(allowWildcard: boolean) {
}; };
} }
export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(async () => { export const getDomesticAndDirectDomainsRulesetPromise = once(async () => {
const domestics = await readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/domestic.conf')); const domestics = await readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/domestic.conf'));
const directs = await readFileIntoProcessedArray(path.resolve(SOURCE_DIR, 'non_ip/direct.conf')); const directs = await readFileIntoProcessedArray(path.resolve(SOURCE_DIR, 'non_ip/direct.conf'));
const lans: string[] = []; const lans: string[] = [];

View File

@ -1,6 +1,6 @@
import { task } from './trace'; import { task } from './trace';
import { SHARED_DESCRIPTION } from './constants/description'; import { SHARED_DESCRIPTION } from './constants/description';
import { createMemoizedPromise } from './lib/memo-promise'; import { once } from 'foxts/once';
import { RulesetOutput } from './lib/rules/ruleset'; import { RulesetOutput } from './lib/rules/ruleset';
import Worktank from 'worktank'; import Worktank from 'worktank';
@ -44,7 +44,7 @@ const pool = new Worktank({
} }
}); });
export const getMicrosoftCdnRulesetPromise = createMemoizedPromise<[domains: string[], domainSuffixes: string[]]>(async () => { export const getMicrosoftCdnRulesetPromise = once<Promise<[domains: string[], domainSuffixes: string[]]>>(async () => {
const res = await pool.exec( const res = await pool.exec(
'getMicrosoftCdnRuleset', 'getMicrosoftCdnRuleset',
[import.meta.url] [import.meta.url]

View File

@ -2,12 +2,12 @@
import { createReadlineInterfaceFromResponse } from './lib/fetch-text-by-line'; import { createReadlineInterfaceFromResponse } from './lib/fetch-text-by-line';
import { task } from './trace'; import { task } from './trace';
import { SHARED_DESCRIPTION } from './constants/description'; import { SHARED_DESCRIPTION } from './constants/description';
import { createMemoizedPromise } from './lib/memo-promise'; import { once } from 'foxts/once';
import { RulesetOutput } from './lib/rules/ruleset'; import { RulesetOutput } from './lib/rules/ruleset';
import { $$fetch } from './lib/fetch-retry'; import { $$fetch } from './lib/fetch-retry';
import { fastIpVersion } from './lib/misc'; import { fastIpVersion } from './lib/misc';
export const getTelegramCIDRPromise = createMemoizedPromise(async () => { export const getTelegramCIDRPromise = once(async () => {
const resp = await $$fetch('https://core.telegram.org/resources/cidr.txt'); const resp = await $$fetch('https://core.telegram.org/resources/cidr.txt');
const lastModified = resp.headers.get('last-modified'); const lastModified = resp.headers.get('last-modified');
const date = lastModified ? new Date(lastModified) : new Date(); const date = lastModified ? new Date(lastModified) : new Date();

View File

@ -80,7 +80,11 @@ const domesticDohServers: Array<[string, DNS2.DnsResolver]> = ([
const domainAliveMutex = createKeyedAsyncMutex('isDomainAlive'); const domainAliveMutex = createKeyedAsyncMutex('isDomainAlive');
export async function isDomainAlive(domain: string, isIncludeAllSubdomain: boolean = domain[0] === '.'): Promise<boolean> { export async function isDomainAlive(
domain: string,
// we dont need to check domain[0] here, this is only from runAgainstSourceFile
isIncludeAllSubdomain: boolean
): Promise<boolean> {
if (domainAliveMap.has(domain)) { if (domainAliveMap.has(domain)) {
return domainAliveMap.get(domain)!; return domainAliveMap.get(domain)!;
} }
@ -102,8 +106,6 @@ export async function isDomainAlive(domain: string, isIncludeAllSubdomain: boole
return domainAliveMutex.acquire(domain, async () => { return domainAliveMutex.acquire(domain, async () => {
domain = domain[0] === '.' ? domain.slice(1) : domain; domain = domain[0] === '.' ? domain.slice(1) : domain;
const $domain = isIncludeAllSubdomain ? '.' + domain : domain;
const aDns: string[] = []; const aDns: string[] = [];
const aaaaDns: string[] = []; const aaaaDns: string[] = [];
@ -113,7 +115,7 @@ export async function isDomainAlive(domain: string, isIncludeAllSubdomain: boole
// eslint-disable-next-line no-await-in-loop -- sequential // eslint-disable-next-line no-await-in-loop -- sequential
const aRecords = (await $resolve(domain, 'A', servers[i])); const aRecords = (await $resolve(domain, 'A', servers[i]));
if (aRecords.answers.length > 0) { if (aRecords.answers.length > 0) {
domainAliveMap.set($domain, true); domainAliveMap.set(domain, true);
return true; return true;
} }
@ -123,7 +125,7 @@ export async function isDomainAlive(domain: string, isIncludeAllSubdomain: boole
// eslint-disable-next-line no-await-in-loop -- sequential // eslint-disable-next-line no-await-in-loop -- sequential
const aaaaRecords = (await $resolve(domain, 'AAAA', servers[i])); const aaaaRecords = (await $resolve(domain, 'AAAA', servers[i]));
if (aaaaRecords.answers.length > 0) { if (aaaaRecords.answers.length > 0) {
domainAliveMap.set($domain, true); domainAliveMap.set(domain, true);
return true; return true;
} }
@ -135,7 +137,7 @@ export async function isDomainAlive(domain: string, isIncludeAllSubdomain: boole
// eslint-disable-next-line no-await-in-loop -- sequential // eslint-disable-next-line no-await-in-loop -- sequential
const aRecords = (await $resolve(domain, 'A', pickOne(domesticDohServers))); const aRecords = (await $resolve(domain, 'A', pickOne(domesticDohServers)));
if (aRecords.answers.length > 0) { if (aRecords.answers.length > 0) {
domainAliveMap.set($domain, true); domainAliveMap.set(domain, true);
return true; return true;
} }
aDns.push(aRecords.dns); aDns.push(aRecords.dns);
@ -144,7 +146,7 @@ export async function isDomainAlive(domain: string, isIncludeAllSubdomain: boole
// eslint-disable-next-line no-await-in-loop -- sequential // eslint-disable-next-line no-await-in-loop -- sequential
const aaaaRecords = (await $resolve(domain, 'AAAA', pickOne(domesticDohServers))); const aaaaRecords = (await $resolve(domain, 'AAAA', pickOne(domesticDohServers)));
if (aaaaRecords.answers.length > 0) { if (aaaaRecords.answers.length > 0) {
domainAliveMap.set($domain, true); domainAliveMap.set(domain, true);
return true; return true;
} }
aaaaDns.push(aaaaRecords.dns); aaaaDns.push(aaaaRecords.dns);
@ -152,7 +154,7 @@ export async function isDomainAlive(domain: string, isIncludeAllSubdomain: boole
console.log(picocolors.red('[domain dead]'), 'no A/AAAA records', { domain, a: aDns, aaaa: aaaaDns }); console.log(picocolors.red('[domain dead]'), 'no A/AAAA records', { domain, a: aDns, aaaa: aaaaDns });
domainAliveMap.set($domain, false); domainAliveMap.set(domain, false);
return false; return false;
}); });
} }

View File

@ -1,26 +0,0 @@
const notError = Symbol('notError');
export function createMemoizedPromise<T>(
fn: () => Promise<T>,
/** whether to create promise immediately or only create after first access */
preload = true
): () => Promise<T> {
let error: Error | typeof notError = notError;
let promise: Promise<T> | null = preload
? fn().catch(e => {
// Here we record the error so that we can throw it later when the function is called
error = e;
// Here we make sure the Promise still returns the never type
throw e;
})
: null;
return () => {
if (error !== notError) {
return Promise.reject(error);
}
promise ??= fn();
return promise;
};
}

View File

@ -31,7 +31,7 @@
"fast-cidr-tools": "^0.3.2", "fast-cidr-tools": "^0.3.2",
"fast-fifo": "^1.3.2", "fast-fifo": "^1.3.2",
"fdir": "^6.4.5", "fdir": "^6.4.5",
"foxts": "^3.3.3", "foxts": "^3.4.0",
"hash-wasm": "^4.12.0", "hash-wasm": "^4.12.0",
"json-stringify-pretty-compact": "3.0.0", "json-stringify-pretty-compact": "3.0.0",
"null-prototype-object": "^1.2.0", "null-prototype-object": "^1.2.0",

24
pnpm-lock.yaml generated
View File

@ -50,8 +50,8 @@ importers:
specifier: ^6.4.5 specifier: ^6.4.5
version: 6.4.5(picomatch@4.0.2) version: 6.4.5(picomatch@4.0.2)
foxts: foxts:
specifier: ^3.3.3 specifier: ^3.4.0
version: 3.3.3 version: 3.4.0
hash-wasm: hash-wasm:
specifier: ^4.12.0 specifier: ^4.12.0
version: 4.12.0 version: 4.12.0
@ -1200,8 +1200,8 @@ packages:
fast-deep-equal@3.1.3: fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-escape-html@1.0.0: fast-escape-html@1.1.0:
resolution: {integrity: sha512-f5flzdfLAWKlW2hJAHFh2iyMhTlX6tEMaBuRXwvSKtyYEqv9OR0y7mJz6tHs3CDB2opBU5hTWpm9AROhfWTK7A==} resolution: {integrity: sha512-nRvjfywv8gzIBs0fM/ht6S5scNUamm+o+91p/69cYYNWODb7b/UiQfjFx+6n8NMtdHs6K80kh+hW1dPNS/opIA==}
fast-fifo@1.3.2: fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
@ -1257,8 +1257,8 @@ packages:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'} engines: {node: '>=14'}
foxts@3.3.3: foxts@3.4.0:
resolution: {integrity: sha512-9TkeFw6bXXphI9Pu7SOI/+gO8g5bNjs3/s2kPDRwRHnJ46weDxoB6V+gG7mzr+9xsVedjSVxv+4xErNGpgDOtQ==} resolution: {integrity: sha512-zpI41ZJ/ZjG745XMVJy338CU1WLwkp1AobgGNKvFDEvSz8y7Lv+DTUpAj/BUyXg7TocGDpSwRH6o9XyAvkMO5A==}
fs-constants@1.0.0: fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
@ -2764,7 +2764,7 @@ snapshots:
eslint-plugin-regexp: 2.7.0(eslint@9.27.0) eslint-plugin-regexp: 2.7.0(eslint@9.27.0)
eslint-plugin-sukka: 6.20.0(eslint@9.27.0)(typescript@5.8.3) eslint-plugin-sukka: 6.20.0(eslint@9.27.0)(typescript@5.8.3)
eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0)(typescript@5.8.3))(eslint@9.27.0)(typescript@5.8.3))(eslint@9.27.0) eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0)(typescript@5.8.3))(eslint@9.27.0)(typescript@5.8.3))(eslint@9.27.0)
foxts: 3.3.3 foxts: 3.4.0
jsonc-eslint-parser: 2.4.0 jsonc-eslint-parser: 2.4.0
picocolors: 1.1.1 picocolors: 1.1.1
typescript-eslint: 8.32.1(eslint@9.27.0)(typescript@5.8.3) typescript-eslint: 8.32.1(eslint@9.27.0)(typescript@5.8.3)
@ -2898,7 +2898,7 @@ snapshots:
'@eslint-sukka/shared': 6.20.0(eslint@9.27.0)(typescript@5.8.3) '@eslint-sukka/shared': 6.20.0(eslint@9.27.0)(typescript@5.8.3)
'@typescript-eslint/type-utils': 8.32.1(eslint@9.27.0)(typescript@5.8.3) '@typescript-eslint/type-utils': 8.32.1(eslint@9.27.0)(typescript@5.8.3)
'@typescript-eslint/utils': 8.32.1(eslint@9.27.0)(typescript@5.8.3) '@typescript-eslint/utils': 8.32.1(eslint@9.27.0)(typescript@5.8.3)
foxts: 3.3.3 foxts: 3.4.0
optionalDependencies: optionalDependencies:
typescript: 5.8.3 typescript: 5.8.3
transitivePeerDependencies: transitivePeerDependencies:
@ -3002,7 +3002,7 @@ snapshots:
fast-deep-equal@3.1.3: {} fast-deep-equal@3.1.3: {}
fast-escape-html@1.0.0: {} fast-escape-html@1.1.0: {}
fast-fifo@1.3.2: {} fast-fifo@1.3.2: {}
@ -3055,9 +3055,9 @@ snapshots:
cross-spawn: 7.0.6 cross-spawn: 7.0.6
signal-exit: 4.1.0 signal-exit: 4.1.0
foxts@3.3.3: foxts@3.4.0:
dependencies: dependencies:
fast-escape-html: 1.0.0 fast-escape-html: 1.1.0
fs-constants@1.0.0: {} fs-constants@1.0.0: {}
@ -3656,7 +3656,7 @@ snapshots:
undici-cache-store-better-sqlite3@1.0.0(undici@7.10.0): undici-cache-store-better-sqlite3@1.0.0(undici@7.10.0):
dependencies: dependencies:
better-sqlite3: 11.10.0 better-sqlite3: 11.10.0
foxts: 3.3.3 foxts: 3.4.0
undici: 7.10.0 undici: 7.10.0
undici-types@6.21.0: {} undici-types@6.21.0: {}