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.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @jest-environment jsdom
3
3
  */
4
- import { isIPv4, extractRootDomain, isDomainMatch, toStandardUrl, getOptimalDecodedString, } from "../utils";
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("匹配根域", () => {
@@ -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 dict = this.getTenantDict();
136
+ const { lineGroup, tenant } = this.getTenantDict();
137
137
  const dictList = this.getTenantDictList();
138
- baseUrl = filterSmartLines(dictList, baseUrl, dict?.lineGroup);
138
+ baseUrl = filterSmartLines(dictList, baseUrl, lineGroup, tenant);
139
139
  if (Array.isArray(baseUrl)) {
140
140
  try {
141
141
  this.domainBaseUrl = toStandardUrl(await getOptimalDecodedString(baseUrl));
@@ -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
  /**
@@ -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
- * 常见多级后缀(Public Suffix List 的子集,可按需扩展)
66
- * 如 .co.uk、.com.cn、.gov.cn、.org.cn 等
67
- */
68
- const MULTI_LEVEL_TLDS = new Set([
69
- "co.uk",
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
- return last2;
107
- }
108
- catch (e) {
109
- return "";
71
+ catch (_) { }
110
72
  }
73
+ return hostname;
111
74
  }
112
75
  /**
113
- * 判断 b 是否匹配 a[] 中任意一个域名
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 normalizedList = list.map(extractRootDomain).filter(Boolean);
117
- const targetDomain = extractRootDomain(target);
118
- return normalizedList.includes(targetDomain);
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
- input = input.trim(); // ✨ 增加
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
- return `${url.protocol}//${url.hostname}`;
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 input;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hifun-tools",
3
- "version": "1.3.21",
3
+ "version": "1.3.23",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",