瀏覽代碼

Refactor: use `foxts` more

SukkaW 10 月之前
父節點
當前提交
bdc1f5ec82

+ 2 - 2
Build/build-apple-cdn.ts

@@ -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());

+ 2 - 2
Build/build-chn-cidr.ts

@@ -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>)

+ 2 - 2
Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts

@@ -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[] = [];

+ 2 - 2
Build/build-microsoft-cdn.ts

@@ -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]

+ 2 - 2
Build/build-telegram-cidr.ts

@@ -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();

+ 10 - 8
Build/lib/is-domain-alive.ts

@@ -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;
   });
   });
 }
 }

+ 0 - 26
Build/lib/memo-promise.ts

@@ -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;
-  };
-}

+ 1 - 1
package.json

@@ -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",

+ 12 - 12
pnpm-lock.yaml

@@ -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
-        version: 3.3.3
+        specifier: ^3.4.0
+        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:
-    resolution: {integrity: sha512-f5flzdfLAWKlW2hJAHFh2iyMhTlX6tEMaBuRXwvSKtyYEqv9OR0y7mJz6tHs3CDB2opBU5hTWpm9AROhfWTK7A==}
+  fast-escape-html@1.1.0:
+    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:
-    resolution: {integrity: sha512-9TkeFw6bXXphI9Pu7SOI/+gO8g5bNjs3/s2kPDRwRHnJ46weDxoB6V+gG7mzr+9xsVedjSVxv+4xErNGpgDOtQ==}
+  foxts@3.4.0:
+    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: {}