hifun-tools 1.3.21 → 1.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/init/__tests/utils.test.js +1 -16
- package/dist/init/index.js +2 -2
- package/dist/init/utils.d.ts +0 -7
- package/dist/init/utils.js +48 -52
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @jest-environment jsdom
|
|
3
3
|
*/
|
|
4
|
-
import { isIPv4,
|
|
4
|
+
import { isIPv4, isDomainMatch, toStandardUrl, getOptimalDecodedString, } from "../utils";
|
|
5
5
|
// --------------------- isIPv4 -------------------------
|
|
6
6
|
describe("isIPv4", () => {
|
|
7
7
|
test("纯 IPv4", () => {
|
|
@@ -17,21 +17,6 @@ describe("isIPv4", () => {
|
|
|
17
17
|
expect(isIPv4("localhost")).toBe(false);
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
|
-
// --------------------- extractRootDomain -------------------------
|
|
21
|
-
describe("extractRootDomain", () => {
|
|
22
|
-
test("普通域名", () => {
|
|
23
|
-
expect(extractRootDomain("https://sub.example.com")).toBe("example.com");
|
|
24
|
-
});
|
|
25
|
-
test("多级后缀", () => {
|
|
26
|
-
expect(extractRootDomain("a.b.example.com.cn")).toBe("example.com.cn");
|
|
27
|
-
});
|
|
28
|
-
test("无协议", () => {
|
|
29
|
-
expect(extractRootDomain("www.test.co.uk")).toBe("test.co.uk");
|
|
30
|
-
});
|
|
31
|
-
test("非法 URL 返回空", () => {
|
|
32
|
-
expect(extractRootDomain("%%%--非法")).toBe("");
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
20
|
// --------------------- isDomainMatch -------------------------
|
|
36
21
|
describe("isDomainMatch", () => {
|
|
37
22
|
test("匹配根域", () => {
|
package/dist/init/index.js
CHANGED
|
@@ -133,9 +133,9 @@ class InitCls {
|
|
|
133
133
|
const response = await fetch(`/lineAddress.txt?t=${Date.now()}`);
|
|
134
134
|
const configText = await response.text();
|
|
135
135
|
let baseUrl = JSON.parse(AesDecrypt(configText));
|
|
136
|
-
const
|
|
136
|
+
const { lineGroup, tenant } = this.getTenantDict();
|
|
137
137
|
const dictList = this.getTenantDictList();
|
|
138
|
-
baseUrl = filterSmartLines(dictList, baseUrl,
|
|
138
|
+
baseUrl = filterSmartLines(dictList, baseUrl, lineGroup, tenant);
|
|
139
139
|
if (Array.isArray(baseUrl)) {
|
|
140
140
|
try {
|
|
141
141
|
this.domainBaseUrl = toStandardUrl(await getOptimalDecodedString(baseUrl));
|
package/dist/init/utils.d.ts
CHANGED
|
@@ -6,13 +6,6 @@ export type TenantDict = {
|
|
|
6
6
|
};
|
|
7
7
|
export declare function isIPv4(str: string): boolean;
|
|
8
8
|
export declare function getOptimalDecodedString(encodedArray: string[]): Promise<string>;
|
|
9
|
-
/**
|
|
10
|
-
* 提取主域名(支持多级 TLD)
|
|
11
|
-
*/
|
|
12
|
-
export declare function extractRootDomain(url: string): string;
|
|
13
|
-
/**
|
|
14
|
-
* 判断 b 是否匹配 a[] 中任意一个域名
|
|
15
|
-
*/
|
|
16
9
|
export declare function isDomainMatch(list: string[], target: string): boolean;
|
|
17
10
|
export declare function toStandardUrl(input: string): string;
|
|
18
11
|
/**
|
package/dist/init/utils.js
CHANGED
|
@@ -61,74 +61,69 @@ export async function getOptimalDecodedString(encodedArray) {
|
|
|
61
61
|
});
|
|
62
62
|
return isIp ? await getOptimal(ipList) : await getOptimal(domainList);
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"org.uk",
|
|
71
|
-
"gov.uk",
|
|
72
|
-
"com.cn",
|
|
73
|
-
"net.cn",
|
|
74
|
-
"gov.cn",
|
|
75
|
-
"org.cn",
|
|
76
|
-
"co.jp",
|
|
77
|
-
"ne.jp",
|
|
78
|
-
"or.jp",
|
|
79
|
-
"go.jp",
|
|
80
|
-
"com.au",
|
|
81
|
-
"net.au",
|
|
82
|
-
"org.au",
|
|
83
|
-
"co.kr",
|
|
84
|
-
"ne.kr",
|
|
85
|
-
"or.kr",
|
|
86
|
-
]);
|
|
87
|
-
/**
|
|
88
|
-
* 提取主域名(支持多级 TLD)
|
|
89
|
-
*/
|
|
90
|
-
export function extractRootDomain(url) {
|
|
91
|
-
try {
|
|
92
|
-
url = url.trim(); // ✨ 增加
|
|
93
|
-
if (!/^https?:\/\//i.test(url)) {
|
|
94
|
-
url = "http://" + url;
|
|
95
|
-
}
|
|
96
|
-
const { hostname } = new URL(url);
|
|
97
|
-
const parts = hostname.split(".").filter(Boolean);
|
|
98
|
-
if (parts.length < 2)
|
|
99
|
-
return hostname;
|
|
100
|
-
const last2 = parts.slice(-2).join(".");
|
|
101
|
-
if (MULTI_LEVEL_TLDS.has(last2)) {
|
|
102
|
-
if (parts.length >= 3) {
|
|
103
|
-
return parts.slice(-3).join(".");
|
|
104
|
-
}
|
|
64
|
+
function normalizeHost(hostname) {
|
|
65
|
+
hostname = hostname.trim().toLowerCase();
|
|
66
|
+
// 去掉典型协议
|
|
67
|
+
if (hostname.startsWith("http://") || hostname.startsWith("https://")) {
|
|
68
|
+
try {
|
|
69
|
+
hostname = new URL(hostname).hostname;
|
|
105
70
|
}
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
catch (e) {
|
|
109
|
-
return "";
|
|
71
|
+
catch (_) { }
|
|
110
72
|
}
|
|
73
|
+
return hostname;
|
|
111
74
|
}
|
|
112
75
|
/**
|
|
113
|
-
*
|
|
76
|
+
* 域名是否严格匹配,允许 www 特例
|
|
77
|
+
*
|
|
78
|
+
* 允许:
|
|
79
|
+
* www.a.com <=> a.com
|
|
80
|
+
* 完全相同
|
|
81
|
+
*
|
|
82
|
+
* 不允许:
|
|
83
|
+
* 1.a.com vs 2.a.com
|
|
84
|
+
* 1.a.com vs a.com
|
|
85
|
+
* 2.a.com vs a.com
|
|
86
|
+
* 1.a.com vs www.a.com
|
|
114
87
|
*/
|
|
88
|
+
function isDomainStrictEqual(a, b) {
|
|
89
|
+
a = normalizeHost(a);
|
|
90
|
+
b = normalizeHost(b);
|
|
91
|
+
if (a === b)
|
|
92
|
+
return true;
|
|
93
|
+
// www 特例:只允许这种
|
|
94
|
+
// www.x.com <-> x.com
|
|
95
|
+
const stripWww = (h) => (h.startsWith("www.") ? h.slice(4) : h);
|
|
96
|
+
return stripWww(a) === stripWww(b);
|
|
97
|
+
}
|
|
115
98
|
export function isDomainMatch(list, target) {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
99
|
+
const t = normalizeHost(target);
|
|
100
|
+
return list.some((item) => {
|
|
101
|
+
const h = normalizeHost(item);
|
|
102
|
+
return isDomainStrictEqual(h, t);
|
|
103
|
+
});
|
|
119
104
|
}
|
|
120
105
|
export function toStandardUrl(input) {
|
|
121
106
|
try {
|
|
122
|
-
|
|
107
|
+
if (!input)
|
|
108
|
+
return "";
|
|
109
|
+
input = input.trim();
|
|
123
110
|
let protocol = window?.location?.protocol || "https:";
|
|
111
|
+
// 如果没有 http / https,则自动补协议
|
|
124
112
|
if (!/^https?:\/\//i.test(input)) {
|
|
125
113
|
input = protocol + "//" + input;
|
|
126
114
|
}
|
|
127
115
|
const url = new URL(input);
|
|
128
|
-
|
|
116
|
+
const hostname = url.hostname;
|
|
117
|
+
// ❗ hostname 必须包含至少 1 个点 或是合法 IPv4,否则判断为无效
|
|
118
|
+
const isHostnameValid = /^[a-zA-Z0-9.-]+$/.test(hostname) &&
|
|
119
|
+
(hostname.includes(".") || isIPv4(hostname));
|
|
120
|
+
if (!isHostnameValid) {
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
return `${url.protocol}//${hostname}`;
|
|
129
124
|
}
|
|
130
125
|
catch (e) {
|
|
131
|
-
return
|
|
126
|
+
return "";
|
|
132
127
|
}
|
|
133
128
|
}
|
|
134
129
|
/**
|
|
@@ -209,6 +204,7 @@ export function filterSmartLines(list1, list2, groupType, tenant) {
|
|
|
209
204
|
if (groupType) {
|
|
210
205
|
result = result.filter((item) => item.lineGroup === groupType);
|
|
211
206
|
}
|
|
207
|
+
console.log("list2.map(toStandardUrl)", list2.map(toStandardUrl), list2);
|
|
212
208
|
const lines = result.map((item) => toStandardUrl(item.line));
|
|
213
209
|
return list2.map(toStandardUrl).filter((v) => isDomainMatch(lines, v)); // ✨ 使用域名匹配
|
|
214
210
|
}
|