mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-14 02:00:37 +08:00
Replace more utilities w/ foxts
This commit is contained in:
parent
80ac403944
commit
72d953b230
@ -14,7 +14,7 @@ import { task } from './trace';
|
|||||||
import { SHARED_DESCRIPTION } from './constants/description';
|
import { SHARED_DESCRIPTION } from './constants/description';
|
||||||
import { getPhishingDomains } from './lib/get-phishing-domains';
|
import { getPhishingDomains } from './lib/get-phishing-domains';
|
||||||
|
|
||||||
import { setAddFromArray } from './lib/set-add-from-array';
|
import { addArrayElementsToSet } from 'foxts/add-array-elements-to-set';
|
||||||
import { appendArrayInPlace } from './lib/append-array-in-place';
|
import { appendArrayInPlace } from './lib/append-array-in-place';
|
||||||
import { OUTPUT_INTERNAL_DIR, SOURCE_DIR } from './constants/dir';
|
import { OUTPUT_INTERNAL_DIR, SOURCE_DIR } from './constants/dir';
|
||||||
import { DomainsetOutput } from './lib/create-file';
|
import { DomainsetOutput } from './lib/create-file';
|
||||||
@ -77,7 +77,7 @@ export const buildRejectDomainSet = task(require.main === module, __filename)(as
|
|||||||
shouldStop = true;
|
shouldStop = true;
|
||||||
// we should not break here, as we want to see full matches from all data source
|
// we should not break here, as we want to see full matches from all data source
|
||||||
}
|
}
|
||||||
setAddFromArray(filterRuleWhitelistDomainSets, white);
|
addArrayElementsToSet(filterRuleWhitelistDomainSets, white);
|
||||||
appendArrayToRejectOutput(black);
|
appendArrayToRejectOutput(black);
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
@ -89,13 +89,13 @@ export const buildRejectDomainSet = task(require.main === module, __filename)(as
|
|||||||
shouldStop = true;
|
shouldStop = true;
|
||||||
// we should not break here, as we want to see full matches from all data source
|
// we should not break here, as we want to see full matches from all data source
|
||||||
}
|
}
|
||||||
setAddFromArray(filterRuleWhitelistDomainSets, white);
|
addArrayElementsToSet(filterRuleWhitelistDomainSets, white);
|
||||||
appendArrayToRejectExtraOutput(black);
|
appendArrayToRejectExtraOutput(black);
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
ADGUARD_FILTERS_WHITELIST.map(entry => processFilterRules(childSpan, ...entry).then(({ white, black }) => {
|
ADGUARD_FILTERS_WHITELIST.map(entry => processFilterRules(childSpan, ...entry).then(({ white, black }) => {
|
||||||
setAddFromArray(filterRuleWhitelistDomainSets, white);
|
addArrayElementsToSet(filterRuleWhitelistDomainSets, white);
|
||||||
setAddFromArray(filterRuleWhitelistDomainSets, black);
|
addArrayElementsToSet(filterRuleWhitelistDomainSets, black);
|
||||||
})),
|
})),
|
||||||
getPhishingDomains(childSpan).then(appendArrayToRejectExtraOutput),
|
getPhishingDomains(childSpan).then(appendArrayToRejectExtraOutput),
|
||||||
readFileIntoProcessedArray(path.join(SOURCE_DIR, 'domainset/reject_sukka.conf')).then(appendArrayToRejectOutput),
|
readFileIntoProcessedArray(path.join(SOURCE_DIR, 'domainset/reject_sukka.conf')).then(appendArrayToRejectOutput),
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import path from 'node:path';
|
|||||||
import { createReadlineInterfaceFromResponse, readFileIntoProcessedArray } from './lib/fetch-text-by-line';
|
import { createReadlineInterfaceFromResponse, readFileIntoProcessedArray } 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 { isProbablyIpv4, isProbablyIpv6 } from './lib/is-fast-ip';
|
import { isProbablyIpv4, isProbablyIpv6 } from 'foxts/is-probably-ip';
|
||||||
import { fsFetchCache, getFileContentHash } from './lib/cache-filesystem';
|
import { fsFetchCache, getFileContentHash } from './lib/cache-filesystem';
|
||||||
import { processLine } from './lib/process-line';
|
import { processLine } from './lib/process-line';
|
||||||
import { RulesetOutput } from './lib/create-file';
|
import { RulesetOutput } from './lib/create-file';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { createReadlineInterfaceFromResponse } from './lib/fetch-text-by-line';
|
import { createReadlineInterfaceFromResponse } from './lib/fetch-text-by-line';
|
||||||
import { isProbablyIpv4, isProbablyIpv6 } from './lib/is-fast-ip';
|
import { isProbablyIpv4, isProbablyIpv6 } from 'foxts/is-probably-ip';
|
||||||
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 { createMemoizedPromise } from './lib/memo-promise';
|
||||||
|
|||||||
@ -1,74 +0,0 @@
|
|||||||
import { fetchRemoteTextByLine } from './fetch-text-by-line';
|
|
||||||
|
|
||||||
import createKeywordFilter from './aho-corasick';
|
|
||||||
|
|
||||||
// eslint-disable import-x/no-unresolved -- benchmark
|
|
||||||
import ModernAhoCorasick from 'modern-ahocorasick';
|
|
||||||
import { AhoCorasick as MonyoneAhoCorasick } from '@monyone/aho-corasick';
|
|
||||||
// @ts-expect-error -- no types
|
|
||||||
import FastScanner from 'fastscan';
|
|
||||||
import { AhoCorasick as RustAhoCorasick } from '@blackglory/aho-corasick';
|
|
||||||
// eslint-enable import-x/no-unresolved
|
|
||||||
|
|
||||||
function runKeywordFilter(data: string[], testFn: (line: string) => boolean) {
|
|
||||||
for (let i = 0, len = data.length; i < len; i++) {
|
|
||||||
testFn(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFns(keywordsSet: string[] | readonly string[]) {
|
|
||||||
const tmp1 = new ModernAhoCorasick(keywordsSet.slice());
|
|
||||||
const tmp2 = new MonyoneAhoCorasick(keywordsSet.slice());
|
|
||||||
const scanner = new FastScanner(keywordsSet.slice());
|
|
||||||
const tmp3 = new RustAhoCorasick(keywordsSet.slice(), { caseSensitive: true });
|
|
||||||
|
|
||||||
return [
|
|
||||||
['createKeywordFilter', createKeywordFilter(keywordsSet.slice())],
|
|
||||||
['modern-ahocorasick', (line: string) => tmp1.search(line).length > 0],
|
|
||||||
['@monyone/aho-corasick', (line: string) => tmp2.hasKeywordInText(line)],
|
|
||||||
['fastscan', (line: string) => scanner.search(line).length > 0],
|
|
||||||
['@blackglory/aho-corasick', (line: string) => tmp3.isMatch(line)]
|
|
||||||
] as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
(async () => {
|
|
||||||
const { bench, group, run } = await import('mitata');
|
|
||||||
|
|
||||||
const data = await Array.fromAsync(await fetchRemoteTextByLine('https://easylist.to/easylist/easylist.txt', true));
|
|
||||||
console.log({ dataLen: data.length });
|
|
||||||
const keywordsSet = [
|
|
||||||
'!',
|
|
||||||
'?',
|
|
||||||
'*',
|
|
||||||
'[',
|
|
||||||
'(',
|
|
||||||
']',
|
|
||||||
')',
|
|
||||||
',',
|
|
||||||
'#',
|
|
||||||
'%',
|
|
||||||
'&',
|
|
||||||
'=',
|
|
||||||
'~',
|
|
||||||
// special modifier
|
|
||||||
'$popup',
|
|
||||||
'$removeparam',
|
|
||||||
'$popunder',
|
|
||||||
'$cname',
|
|
||||||
'$frame',
|
|
||||||
// some bad syntax
|
|
||||||
'^popup'
|
|
||||||
];
|
|
||||||
|
|
||||||
const fns = getFns(keywordsSet);
|
|
||||||
|
|
||||||
group(() => {
|
|
||||||
fns.forEach(([name, fn]) => {
|
|
||||||
bench(name, () => runKeywordFilter(data, fn));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
run();
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import { describe, it } from 'mocha';
|
|
||||||
import { expect } from 'expect';
|
|
||||||
import { getFns } from './aho-corasick.bench';
|
|
||||||
|
|
||||||
describe('AhoCorasick', () => {
|
|
||||||
for (const test of ([
|
|
||||||
[
|
|
||||||
['ap', 'an'],
|
|
||||||
['bananan', 'apple', 'melon'],
|
|
||||||
[true, true, false]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
['cdn', 'sukka'],
|
|
||||||
['bananan', 'apple', 'melon'],
|
|
||||||
[false, false, false]
|
|
||||||
]
|
|
||||||
] as const)) {
|
|
||||||
const kwtests = getFns(test[0]);
|
|
||||||
const fixtures = test[1];
|
|
||||||
const expected = test[2];
|
|
||||||
|
|
||||||
for (const kwtest of kwtests) {
|
|
||||||
const fnName = kwtest[0];
|
|
||||||
const fn = kwtest[1];
|
|
||||||
|
|
||||||
it(fnName, () => {
|
|
||||||
for (let i = 0, len = fixtures.length; i < len; i++) {
|
|
||||||
expect(fn(fixtures[i])).toBe(expected[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
class Node extends Map<string, Node> {
|
|
||||||
constructor(
|
|
||||||
public wordEnd: boolean,
|
|
||||||
public fail: Node | undefined
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createKeywordFilter(keys: string[] | Set<string>) {
|
|
||||||
const root = new Node(false, undefined);
|
|
||||||
|
|
||||||
// Create a trie with extra fields and information
|
|
||||||
const put = (key: string) => {
|
|
||||||
let node = root;
|
|
||||||
|
|
||||||
for (let idx = 0, len = key.length; idx < len; idx++) {
|
|
||||||
const char = key[idx];
|
|
||||||
|
|
||||||
if (node.has(char)) {
|
|
||||||
node = node.get(char)!;
|
|
||||||
} else {
|
|
||||||
const newNode = new Node(false, undefined);
|
|
||||||
node.set(char, newNode);
|
|
||||||
node = newNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a new node is created, mark it as a word end when loop finish
|
|
||||||
if (node !== root) {
|
|
||||||
node.wordEnd = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
keys.forEach(put);
|
|
||||||
|
|
||||||
// const build = () => {
|
|
||||||
const queue: Node[] = [root];
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const beginNode = queue.pop()!;
|
|
||||||
|
|
||||||
beginNode.forEach((node, char) => {
|
|
||||||
let failNode = beginNode.fail;
|
|
||||||
|
|
||||||
while (failNode && !failNode.has(char)) {
|
|
||||||
failNode = failNode.fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.fail = failNode ? failNode.get(char) : root;
|
|
||||||
|
|
||||||
queue.push(node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// };
|
|
||||||
// build();
|
|
||||||
|
|
||||||
return (text: string) => {
|
|
||||||
let node: Node | undefined = root;
|
|
||||||
|
|
||||||
for (let i = 0, textLen = text.length; i < textLen; i++) {
|
|
||||||
const char = text[i];
|
|
||||||
|
|
||||||
while (node && !node.has(char)) {
|
|
||||||
node = node.fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = node ? node.get(char)! : root;
|
|
||||||
|
|
||||||
if (node.wordEnd) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createKeywordFilter;
|
|
||||||
@ -10,7 +10,7 @@ import { identity } from 'foxts/identity';
|
|||||||
import { fastStringArrayJoin } from 'foxts/fast-string-array-join';
|
import { fastStringArrayJoin } from 'foxts/fast-string-array-join';
|
||||||
import { performance } from 'node:perf_hooks';
|
import { performance } from 'node:perf_hooks';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import { stringHash } from './string-hash';
|
import { simpleStringHash } from 'foxts/simple-string-hash';
|
||||||
import { defaultRequestInit, requestWithLog, ResponseError } from './fetch-retry';
|
import { defaultRequestInit, requestWithLog, ResponseError } from './fetch-retry';
|
||||||
import type { UndiciResponseData } from './fetch-retry';
|
import type { UndiciResponseData } from './fetch-retry';
|
||||||
// import type { UndiciResponseData } from './fetch-retry';
|
// import type { UndiciResponseData } from './fetch-retry';
|
||||||
@ -447,7 +447,7 @@ export const deserializeSet = (str: string) => new Set(str.split(separator));
|
|||||||
export const serializeArray = (arr: string[]) => fastStringArrayJoin(arr, separator);
|
export const serializeArray = (arr: string[]) => fastStringArrayJoin(arr, separator);
|
||||||
export const deserializeArray = (str: string) => str.split(separator);
|
export const deserializeArray = (str: string) => str.split(separator);
|
||||||
|
|
||||||
export const getFileContentHash = (filename: string) => stringHash(fs.readFileSync(filename, 'utf-8'));
|
export const getFileContentHash = (filename: string) => simpleStringHash(fs.readFileSync(filename, 'utf-8'));
|
||||||
export function createCacheKey(filename: string) {
|
export function createCacheKey(filename: string) {
|
||||||
const fileHash = getFileContentHash(filename);
|
const fileHash = getFileContentHash(filename);
|
||||||
return (key: string) => key + '$' + fileHash + '$';
|
return (key: string) => key + '$' + fileHash + '$';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import { defaultRequestInit, requestWithLog, ResponseError } from './fetch-retry';
|
import { defaultRequestInit, requestWithLog, ResponseError } from './fetch-retry';
|
||||||
import { setTimeout } from 'node:timers/promises';
|
import { wait } from 'foxts/wait';
|
||||||
|
|
||||||
// eslint-disable-next-line sukka/unicorn/custom-error-definition -- typescript is better
|
// eslint-disable-next-line sukka/unicorn/custom-error-definition -- typescript is better
|
||||||
export class CustomAbortError extends Error {
|
export class CustomAbortError extends Error {
|
||||||
@ -35,8 +35,7 @@ export function sleepWithAbort(ms: number, signal: AbortSignal) {
|
|||||||
|
|
||||||
signal.addEventListener('abort', stop, { once: true });
|
signal.addEventListener('abort', stop, { once: true });
|
||||||
|
|
||||||
// eslint-disable-next-line sukka/prefer-timer-id -- node:timers/promises
|
wait(ms).then(resolve).catch(reject).finally(() => signal.removeEventListener('abort', stop));
|
||||||
setTimeout(ms, undefined, { ref: false }).then(resolve).catch(reject).finally(() => signal.removeEventListener('abort', stop));
|
|
||||||
|
|
||||||
function stop(this: AbortSignal) { reject(this.reason as Error); }
|
function stop(this: AbortSignal) { reject(this.reason as Error); }
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { appendArrayInPlaceCurried } from './append-array-in-place';
|
|||||||
import { DEBUG_DOMAIN_TO_FIND, PHISHING_DOMAIN_LISTS_EXTRA, PHISHING_HOSTS_EXTRA } from '../constants/reject-data-source';
|
import { DEBUG_DOMAIN_TO_FIND, PHISHING_DOMAIN_LISTS_EXTRA, PHISHING_HOSTS_EXTRA } from '../constants/reject-data-source';
|
||||||
import { loosTldOptWithPrivateDomains } from '../constants/loose-tldts-opt';
|
import { loosTldOptWithPrivateDomains } from '../constants/loose-tldts-opt';
|
||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import createKeywordFilter from './aho-corasick';
|
import { createAhoCorasick as createKeywordFilter } from 'foxts/ahocorasick';
|
||||||
import { createCacheKey, deserializeArray, serializeArray } from './cache-filesystem';
|
import { createCacheKey, deserializeArray, serializeArray } from './cache-filesystem';
|
||||||
import { cache } from './fs-memo';
|
import { cache } from './fs-memo';
|
||||||
import { isCI } from 'ci-info';
|
import { isCI } from 'ci-info';
|
||||||
|
|||||||
@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* Check if a hostname is an IP. You should be aware that this only works
|
|
||||||
* because `hostname` is already garanteed to be a valid hostname!
|
|
||||||
*/
|
|
||||||
export function isProbablyIpv4(hostname: string): boolean {
|
|
||||||
// Cannot be shorted than 1.1.1.1 or longer than 255.255.255.255
|
|
||||||
if (hostname.length < 7 || hostname.length > 15) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let numberOfDots = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < hostname.length; i += 1) {
|
|
||||||
const code = hostname.charCodeAt(i);
|
|
||||||
|
|
||||||
if (code === 46 /* '.' */) {
|
|
||||||
numberOfDots += 1;
|
|
||||||
} else if (code < 48 /* '0' */ || code > 57 /* '9' */) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
numberOfDots === 3
|
|
||||||
&& hostname.charCodeAt(0) !== 46 /* '.' */
|
|
||||||
&& hostname.charCodeAt(hostname.length - 1) !== 46 /* '.' */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isProbablyIpv6(hostname: string): boolean {
|
|
||||||
if (hostname.length < 3) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = hostname[0] === '[' ? 1 : 0;
|
|
||||||
let end = hostname.length;
|
|
||||||
|
|
||||||
if (hostname[end - 1] === ']') {
|
|
||||||
end -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only consider the maximum size of a normal IPV6. Note that this will
|
|
||||||
// fail on so-called "IPv4 mapped IPv6 addresses" but this is a corner-case
|
|
||||||
// and a proper validation library should be used for these.
|
|
||||||
if (end - start > 39) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint-disable sukka/no-single-return -- here it goes */
|
|
||||||
let hasColon = false;
|
|
||||||
|
|
||||||
for (; start < end; start += 1) {
|
|
||||||
const code = hostname.charCodeAt(start);
|
|
||||||
|
|
||||||
if (code === 58 /* ':' */) {
|
|
||||||
hasColon = true;
|
|
||||||
} else if (
|
|
||||||
!(
|
|
||||||
(
|
|
||||||
(code >= 48 && code <= 57) // 0-9
|
|
||||||
|| (code >= 97 && code <= 102) // a-f
|
|
||||||
|| (code >= 65 && code <= 90) // A-F
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasColon;
|
|
||||||
/* eslint-enable sukka/no-single-return -- here it goes */
|
|
||||||
}
|
|
||||||
@ -6,7 +6,7 @@ import picocolors from 'picocolors';
|
|||||||
import { normalizeDomain } from './normalize-domain';
|
import { normalizeDomain } from './normalize-domain';
|
||||||
import { deserializeArray, fsFetchCache, serializeArray, getFileContentHash } from './cache-filesystem';
|
import { deserializeArray, fsFetchCache, serializeArray, getFileContentHash } from './cache-filesystem';
|
||||||
import type { Span } from '../trace';
|
import type { Span } from '../trace';
|
||||||
import createKeywordFilter from './aho-corasick';
|
import { createAhoCorasick as createKeywordFilter } from 'foxts/ahocorasick';
|
||||||
import { looseTldtsOpt } from '../constants/loose-tldts-opt';
|
import { looseTldtsOpt } from '../constants/loose-tldts-opt';
|
||||||
import { identity } from 'foxts/identity';
|
import { identity } from 'foxts/identity';
|
||||||
import { DEBUG_DOMAIN_TO_FIND } from '../constants/reject-data-source';
|
import { DEBUG_DOMAIN_TO_FIND } from '../constants/reject-data-source';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { invariant } from 'foxts/guard';
|
import { invariant } from 'foxts/guard';
|
||||||
import createKeywordFilter from '../aho-corasick';
|
import { createAhoCorasick as createKeywordFilter } from 'foxts/ahocorasick';
|
||||||
import { RuleOutput } from './base';
|
import { RuleOutput } from './base';
|
||||||
import type { SingboxSourceFormat } from '../singbox';
|
import type { SingboxSourceFormat } from '../singbox';
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { merge } from 'fast-cidr-tools';
|
import { merge } from 'fast-cidr-tools';
|
||||||
import type { Span } from '../../trace';
|
import type { Span } from '../../trace';
|
||||||
import createKeywordFilter from '../aho-corasick';
|
import { createAhoCorasick as createKeywordFilter } from 'foxts/ahocorasick';
|
||||||
import { appendArrayInPlace } from '../append-array-in-place';
|
import { appendArrayInPlace } from '../append-array-in-place';
|
||||||
import { appendSetElementsToArray } from 'foxts/append-set-elements-to-array';
|
import { appendSetElementsToArray } from 'foxts/append-set-elements-to-array';
|
||||||
import type { SingboxSourceFormat } from '../singbox';
|
import type { SingboxSourceFormat } from '../singbox';
|
||||||
import { RuleOutput } from './base';
|
import { RuleOutput } from './base';
|
||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import { normalizeDomain } from '../normalize-domain';
|
import { normalizeDomain } from '../normalize-domain';
|
||||||
import { isProbablyIpv4, isProbablyIpv6 } from '../is-fast-ip';
|
import { isProbablyIpv4, isProbablyIpv6 } from 'foxts/is-probably-ip';
|
||||||
|
|
||||||
type Preprocessed = [domain: string[], domainSuffix: string[], sortedDomainRules: string[]];
|
type Preprocessed = [domain: string[], domainSuffix: string[], sortedDomainRules: string[]];
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
import { fetchRemoteTextByLine } from './fetch-text-by-line';
|
|
||||||
|
|
||||||
import { bench, group, run } from 'mitata';
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const data = await Array.fromAsync(await fetchRemoteTextByLine('https://osint.digitalside.it/Threat-Intel/lists/latestdomains.txt', true));
|
|
||||||
|
|
||||||
group(() => {
|
|
||||||
bench('setAddFromArray', () => {
|
|
||||||
const set = new Set(['1', '2', '1', '3', 'skk.moe']);
|
|
||||||
for (let i = 0, len = data.length; i < len; i++) {
|
|
||||||
set.add(data[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
group(() => {
|
|
||||||
bench('', () => {
|
|
||||||
const set = new Set(['1', '2', '1', '3', 'skk.moe']);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method -- thisArg is passed
|
|
||||||
data.forEach(set.add, set);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
run();
|
|
||||||
})();
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* In-place adding of elements from an array to a set.
|
|
||||||
*/
|
|
||||||
export function setAddFromArray<T>(set: Set<T>, arr: T[]): void {
|
|
||||||
// for (let i = 0, len = arr.length; i < len; i++) {
|
|
||||||
// set.add(arr[i]);
|
|
||||||
// }
|
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method -- thisArg is passed
|
|
||||||
arr.forEach(set.add, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method -- thisArg is passed
|
|
||||||
export const setAddFromArrayCurried = <T>(set: Set<T>) => (arr: T[]) => arr.forEach(set.add, set);
|
|
||||||
@ -1,11 +1,11 @@
|
|||||||
const unsupported = Symbol('unsupported');
|
// const unsupported = Symbol('unsupported');
|
||||||
|
|
||||||
// https://sing-box.sagernet.org/configuration/rule-set/source-format/
|
// https://sing-box.sagernet.org/configuration/rule-set/source-format/
|
||||||
export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => [key: keyof SingboxHeadlessRule, value: Required<SingboxHeadlessRule>[keyof SingboxHeadlessRule][number]] | null) | typeof unsupported> = {
|
// export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => [key: keyof SingboxHeadlessRule, value: Required<SingboxHeadlessRule>[keyof SingboxHeadlessRule][number]] | null) | typeof unsupported> = {
|
||||||
'IP-ASN': unsupported,
|
// 'IP-ASN': unsupported,
|
||||||
'URL-REGEX': unsupported,
|
// 'URL-REGEX': unsupported,
|
||||||
'USER-AGENT': unsupported
|
// 'USER-AGENT': unsupported
|
||||||
};
|
// };
|
||||||
|
|
||||||
interface SingboxHeadlessRule {
|
interface SingboxHeadlessRule {
|
||||||
domain?: string[],
|
domain?: string[],
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
/**
|
|
||||||
* FNV-1a Hash implementation
|
|
||||||
* @author Travis Webb (tjwebb) <me@traviswebb.com>
|
|
||||||
*
|
|
||||||
* Ported from https://github.com/tjwebb/fnv-plus/blob/master/index.js
|
|
||||||
*
|
|
||||||
* Simplified, optimized and add modified for 52 bit, which provides a larger hash space
|
|
||||||
* and still making use of Javascript's 53-bit integer space.
|
|
||||||
*/
|
|
||||||
export function fnv1a52(str: string) {
|
|
||||||
const len = str.length;
|
|
||||||
let i = 0,
|
|
||||||
t0 = 0,
|
|
||||||
v0 = 0x2325,
|
|
||||||
t1 = 0,
|
|
||||||
v1 = 0x8422,
|
|
||||||
t2 = 0,
|
|
||||||
v2 = 0x9CE4,
|
|
||||||
t3 = 0,
|
|
||||||
v3 = 0xCBF2;
|
|
||||||
|
|
||||||
while (i < len) {
|
|
||||||
v0 ^= str.charCodeAt(i++);
|
|
||||||
t0 = v0 * 435;
|
|
||||||
t1 = v1 * 435;
|
|
||||||
t2 = v2 * 435;
|
|
||||||
t3 = v3 * 435;
|
|
||||||
t2 += v0 << 8;
|
|
||||||
t3 += v1 << 8;
|
|
||||||
t1 += t0 >>> 16;
|
|
||||||
v0 = t0 & 65535;
|
|
||||||
t2 += t1 >>> 16;
|
|
||||||
v1 = t1 & 65535;
|
|
||||||
v3 = (t3 + (t2 >>> 16)) & 65535;
|
|
||||||
v2 = t2 & 65535;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
(v3 & 15) * 281_474_976_710_656
|
|
||||||
+ v2 * 4_294_967_296
|
|
||||||
+ v1 * 65536
|
|
||||||
+ (v0 ^ (v3 >> 4))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fnv1a(s: string) {
|
|
||||||
let h = 0x81_1C_9D_C5;
|
|
||||||
|
|
||||||
for (let i = 0, l = s.length; i < l; i++) {
|
|
||||||
h ^= s.charCodeAt(i);
|
|
||||||
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (h >>> 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const stringHash = (payload: string) => fnv1a52(payload).toString(36) + payload.length.toString(36);
|
|
||||||
@ -10,7 +10,7 @@ import { newQueue } from '@henrygd/queue';
|
|||||||
import asyncRetry from 'async-retry';
|
import asyncRetry from 'async-retry';
|
||||||
import * as whoiser from 'whoiser';
|
import * as whoiser from 'whoiser';
|
||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import createKeywordFilter from './lib/aho-corasick';
|
import { createAhoCorasick as createKeywordFilter } from 'foxts/ahocorasick';
|
||||||
import './lib/fetch-retry';
|
import './lib/fetch-retry';
|
||||||
|
|
||||||
const dohServers: Array<[string, DNS2.DnsResolver]> = ([
|
const dohServers: Array<[string, DNS2.DnsResolver]> = ([
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { readFileByLine } from './lib/fetch-text-by-line';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { OUTPUT_SURGE_DIR } from './constants/dir';
|
import { OUTPUT_SURGE_DIR } from './constants/dir';
|
||||||
import { $fetch } from './lib/make-fetch-happen';
|
import { $fetch } from './lib/make-fetch-happen';
|
||||||
import createKeywordFilter from './lib/aho-corasick';
|
import { createAhoCorasick as createKeywordFilter } from 'foxts/ahocorasick';
|
||||||
|
|
||||||
export async function parseGfwList() {
|
export async function parseGfwList() {
|
||||||
const whiteSet = new Set<string>();
|
const whiteSet = new Set<string>();
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
"escape-string-regexp-node": "^1.0.2",
|
"escape-string-regexp-node": "^1.0.2",
|
||||||
"fast-cidr-tools": "^0.3.1",
|
"fast-cidr-tools": "^0.3.1",
|
||||||
"fdir": "^6.4.2",
|
"fdir": "^6.4.2",
|
||||||
"foxts": "1.0.6",
|
"foxts": "1.0.7",
|
||||||
"hash-wasm": "^4.12.0",
|
"hash-wasm": "^4.12.0",
|
||||||
"json-stringify-pretty-compact": "^3.0.0",
|
"json-stringify-pretty-compact": "^3.0.0",
|
||||||
"make-fetch-happen": "^14.0.3",
|
"make-fetch-happen": "^14.0.3",
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -51,8 +51,8 @@ importers:
|
|||||||
specifier: ^6.4.2
|
specifier: ^6.4.2
|
||||||
version: 6.4.2(picomatch@4.0.2)
|
version: 6.4.2(picomatch@4.0.2)
|
||||||
foxts:
|
foxts:
|
||||||
specifier: 1.0.6
|
specifier: 1.0.7
|
||||||
version: 1.0.6
|
version: 1.0.7
|
||||||
hash-wasm:
|
hash-wasm:
|
||||||
specifier: ^4.12.0
|
specifier: ^4.12.0
|
||||||
version: 4.12.0
|
version: 4.12.0
|
||||||
@ -1139,8 +1139,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
|
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
foxts@1.0.6:
|
foxts@1.0.7:
|
||||||
resolution: {integrity: sha512-hVrqkUX5kH1BIQLXgD4a7VZcwWW6xJEFiiVBwA46i9Kq1TzGLhZHY2nDwxDOOp2DSgKaL70zmwGFR845hes3lQ==}
|
resolution: {integrity: sha512-JcLSIXujoAG6GoxKPgpwB6ME0cK23/txZZCPhANo9GdYaMkRwBZa9pN3ghf67juM46A1Trh2nxJLX01Oak+UvQ==}
|
||||||
|
|
||||||
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==}
|
||||||
@ -2997,7 +2997,7 @@ snapshots:
|
|||||||
combined-stream: 1.0.8
|
combined-stream: 1.0.8
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
foxts@1.0.6: {}
|
foxts@1.0.7: {}
|
||||||
|
|
||||||
fs-constants@1.0.0: {}
|
fs-constants@1.0.0: {}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user