hifun-tools 1.3.22 → 1.3.24
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/getResource.d.ts +1 -1
- package/dist/init/getResource.js +34 -12
- package/dist/init/index.js +7 -4
- package/dist/init/utils.d.ts +0 -16
- package/dist/init/utils.js +47 -71
- 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/getResource.js
CHANGED
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 通用资源加载函数(支持 Vite
|
|
2
|
+
* 通用资源加载函数(支持 Vite / Webpack / Rspack)
|
|
3
3
|
* @param path - 相对路径,如 "tenant/config.json" 或 "tenant/logo.png"
|
|
4
4
|
*/
|
|
5
5
|
export function getResource(path) {
|
|
6
6
|
const isVite = typeof import.meta !== "undefined" && !!import.meta.env;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
// ---------------- 新增 RSPACK 检测 ----------------
|
|
8
|
+
const isRspack = typeof __rspack_require__ === "function" ||
|
|
9
|
+
(typeof __webpack_require__ === "function" &&
|
|
10
|
+
!!__webpack_require__.s && // Rspack 专属字段
|
|
11
|
+
!__webpack_require__.m); // Webpack 有 m,Rspack 不一定有
|
|
12
|
+
const isWebpack = !isRspack && // 不冲突:优先识别 Rspack
|
|
13
|
+
(typeof __webpack_require__ === "function" ||
|
|
14
|
+
(typeof require !== "undefined" &&
|
|
15
|
+
typeof require.context === "function"));
|
|
16
|
+
// ---------------- RSPACK ----------------
|
|
17
|
+
if (isRspack) {
|
|
18
|
+
try {
|
|
19
|
+
const ctx = require.context("/config", true, /\.(json|png|jpg|jpeg|svg|webp)$/);
|
|
20
|
+
const files = ctx.keys();
|
|
21
|
+
const match = files.find((key) => key.endsWith(path));
|
|
22
|
+
if (!match) {
|
|
23
|
+
console.warn(`[getResource] 未找到文件: ${path}`);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return ctx(match);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
console.error("[getResource] Rspack context 加载失败", e);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// ---------------- VITE ----------------
|
|
33
|
+
else if (isVite) {
|
|
34
|
+
const { glob } = import.meta;
|
|
35
|
+
const modules = glob([
|
|
13
36
|
"/config/**/*.json",
|
|
14
37
|
"/config/**/*.png",
|
|
15
38
|
"/config/**/*.jpg",
|
|
@@ -25,11 +48,10 @@ export function getResource(path) {
|
|
|
25
48
|
console.warn(`[getResource] 未找到文件: ${path}`);
|
|
26
49
|
return null;
|
|
27
50
|
}
|
|
28
|
-
|
|
29
|
-
return mod;
|
|
51
|
+
return match[1];
|
|
30
52
|
}
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
// ---------------- WEBPACK ----------------
|
|
54
|
+
else if (isWebpack) {
|
|
33
55
|
const ctx = require.context("/config", true, /\.(json|png|jpg|jpeg|svg|webp)$/);
|
|
34
56
|
const files = ctx.keys();
|
|
35
57
|
const match = files.find((key) => key.endsWith(path));
|
|
@@ -39,6 +61,6 @@ export function getResource(path) {
|
|
|
39
61
|
}
|
|
40
62
|
return ctx(match);
|
|
41
63
|
}
|
|
42
|
-
console.error("[getResource] 不支持的构建环境(需要 Vite
|
|
64
|
+
console.error("[getResource] 不支持的构建环境(需要 Vite/Webpack/Rspack)");
|
|
43
65
|
return null;
|
|
44
66
|
}
|
package/dist/init/index.js
CHANGED
|
@@ -70,7 +70,7 @@ class InitCls {
|
|
|
70
70
|
}
|
|
71
71
|
// 默认租户匹配
|
|
72
72
|
if (host.includes("iggame") || host.includes("localhost")) {
|
|
73
|
-
console.info(`🏠 iggame
|
|
73
|
+
console.info(`🏠 iggame匹配到默认租户2`);
|
|
74
74
|
return { tenant: "iggame" };
|
|
75
75
|
}
|
|
76
76
|
throw new Error("未找到匹配租户");
|
|
@@ -80,8 +80,9 @@ class InitCls {
|
|
|
80
80
|
const host = location.host;
|
|
81
81
|
if (host.includes("iggame") ||
|
|
82
82
|
host.includes("localhost") ||
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
(!!this.localPath &&
|
|
84
|
+
location.origin.includes(this.localPath.replace(/\/+$/, "")))) {
|
|
85
|
+
console.info(`🏠 iggame匹配到默认租户1`);
|
|
85
86
|
return { tenant: "iggame" };
|
|
86
87
|
}
|
|
87
88
|
throw new Error("无法获取有效的租户信息");
|
|
@@ -153,10 +154,12 @@ class InitCls {
|
|
|
153
154
|
return this.domainBaseUrl;
|
|
154
155
|
}
|
|
155
156
|
catch (error) {
|
|
157
|
+
console.error(error);
|
|
156
158
|
if ((this.defaultBaseUrl &&
|
|
157
159
|
(location.host.includes("iggame") ||
|
|
158
160
|
location.host.includes("localhost"))) ||
|
|
159
|
-
|
|
161
|
+
(!!this.localPath &&
|
|
162
|
+
location.origin.includes(this.localPath.replace(/\/+$/, "")))) {
|
|
160
163
|
console.info(`🏠 iggame匹配到默认链接`, this.defaultBaseUrl);
|
|
161
164
|
this.domainBaseUrl = toStandardUrl(this.defaultBaseUrl);
|
|
162
165
|
return this.domainBaseUrl;
|
package/dist/init/utils.d.ts
CHANGED
|
@@ -6,23 +6,7 @@ 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
|
-
/**
|
|
19
|
-
* 匹配规则:模糊 + 正则 + 不区分大小写
|
|
20
|
-
*/
|
|
21
11
|
export declare function matchBrowser(checkItem: string, currentBrowser: string): boolean;
|
|
22
|
-
/**
|
|
23
|
-
* 1. 过滤 browserCheck
|
|
24
|
-
* 2. 按 tenant 过滤
|
|
25
|
-
* 3. 按 groupType(可为空)过滤
|
|
26
|
-
* 4. 与 list2 求交集
|
|
27
|
-
*/
|
|
28
12
|
export declare function filterSmartLines(list1: TenantDict[], list2: string[], groupType?: string, tenant?: string): string[];
|
package/dist/init/utils.js
CHANGED
|
@@ -18,7 +18,6 @@ export async function getOptimalDecodedString(encodedArray) {
|
|
|
18
18
|
throw new Error("输入数组为空或无效");
|
|
19
19
|
const hostname = window.location.hostname;
|
|
20
20
|
const isIp = isIPv4(hostname);
|
|
21
|
-
// ✨ 关键增强:先 trim 再过滤
|
|
22
21
|
const decodedList = encodedArray
|
|
23
22
|
.filter(Boolean)
|
|
24
23
|
.map((v) => v.trim());
|
|
@@ -30,7 +29,6 @@ export async function getOptimalDecodedString(encodedArray) {
|
|
|
30
29
|
return;
|
|
31
30
|
}
|
|
32
31
|
const currentProtocol = window.location.protocol;
|
|
33
|
-
// 过滤协议
|
|
34
32
|
const filteredArr = arr.filter((url) => url.startsWith("http") ? url.startsWith(currentProtocol) : true);
|
|
35
33
|
const targetArr = filteredArr.length ? filteredArr : arr;
|
|
36
34
|
if (targetArr.length === 1)
|
|
@@ -62,77 +60,70 @@ export async function getOptimalDecodedString(encodedArray) {
|
|
|
62
60
|
return isIp ? await getOptimal(ipList) : await getOptimal(domainList);
|
|
63
61
|
}
|
|
64
62
|
/**
|
|
65
|
-
*
|
|
66
|
-
* 如 .co.uk、.com.cn、.gov.cn、.org.cn 等
|
|
63
|
+
* normalizeHost:保留端口,只清理协议
|
|
67
64
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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) {
|
|
65
|
+
function normalizeHost(hostname) {
|
|
66
|
+
hostname = hostname.trim().toLowerCase();
|
|
67
|
+
// 如果没有协议,加上 http:// 方便 URL 解析
|
|
68
|
+
let urlStr = hostname;
|
|
69
|
+
if (!/^https?:\/\//i.test(hostname)) {
|
|
70
|
+
urlStr = "http://" + hostname;
|
|
71
|
+
}
|
|
91
72
|
try {
|
|
92
|
-
url =
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return last2;
|
|
73
|
+
const url = new URL(urlStr);
|
|
74
|
+
// 保留 hostname + port
|
|
75
|
+
return url.port ? `${url.hostname}:${url.port}` : url.hostname;
|
|
107
76
|
}
|
|
108
|
-
catch (
|
|
109
|
-
return
|
|
77
|
+
catch (_) {
|
|
78
|
+
return hostname; // 保留原始字符串
|
|
110
79
|
}
|
|
111
80
|
}
|
|
112
81
|
/**
|
|
113
|
-
*
|
|
82
|
+
* 域名严格匹配,允许 www 特例
|
|
114
83
|
*/
|
|
84
|
+
function isDomainStrictEqual(a, b) {
|
|
85
|
+
a = normalizeHost(a);
|
|
86
|
+
b = normalizeHost(b);
|
|
87
|
+
if (a === b)
|
|
88
|
+
return true;
|
|
89
|
+
const stripWww = (h) => (h.startsWith("www.") ? h.slice(4) : h);
|
|
90
|
+
return stripWww(a) === stripWww(b);
|
|
91
|
+
}
|
|
115
92
|
export function isDomainMatch(list, target) {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
93
|
+
const t = normalizeHost(target);
|
|
94
|
+
return list.some((item) => {
|
|
95
|
+
const h = normalizeHost(item);
|
|
96
|
+
return isDomainStrictEqual(h, t);
|
|
97
|
+
});
|
|
119
98
|
}
|
|
120
99
|
export function toStandardUrl(input) {
|
|
121
100
|
try {
|
|
122
|
-
|
|
101
|
+
if (!input)
|
|
102
|
+
return "";
|
|
103
|
+
input = input.trim();
|
|
123
104
|
let protocol = window?.location?.protocol || "https:";
|
|
124
105
|
if (!/^https?:\/\//i.test(input)) {
|
|
125
106
|
input = protocol + "//" + input;
|
|
126
107
|
}
|
|
127
108
|
const url = new URL(input);
|
|
128
|
-
|
|
109
|
+
const hostname = url.hostname;
|
|
110
|
+
const port = url.port;
|
|
111
|
+
const isHostnameValid = hostname === "localhost" ||
|
|
112
|
+
(/^[a-zA-Z0-9.-]+$/.test(hostname) &&
|
|
113
|
+
(hostname.includes(".") || isIPv4(hostname)));
|
|
114
|
+
if (!isHostnameValid) {
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
return port
|
|
118
|
+
? `${url.protocol}//${hostname}:${port}`
|
|
119
|
+
: `${url.protocol}//${hostname}`;
|
|
129
120
|
}
|
|
130
121
|
catch (e) {
|
|
131
|
-
return
|
|
122
|
+
return "";
|
|
132
123
|
}
|
|
133
124
|
}
|
|
134
125
|
/**
|
|
135
|
-
*
|
|
126
|
+
* 浏览器识别
|
|
136
127
|
*/
|
|
137
128
|
function detectBrowser() {
|
|
138
129
|
const ua = navigator.userAgent.toLowerCase();
|
|
@@ -165,17 +156,11 @@ function detectBrowser() {
|
|
|
165
156
|
}
|
|
166
157
|
return "unknown";
|
|
167
158
|
}
|
|
168
|
-
/**
|
|
169
|
-
* 匹配规则:模糊 + 正则 + 不区分大小写
|
|
170
|
-
*/
|
|
171
159
|
export function matchBrowser(checkItem, currentBrowser) {
|
|
172
|
-
if (!checkItem)
|
|
173
|
-
return false;
|
|
174
|
-
if (!currentBrowser)
|
|
160
|
+
if (!checkItem || !currentBrowser)
|
|
175
161
|
return false;
|
|
176
162
|
const normalized = checkItem.toString().toLowerCase();
|
|
177
163
|
const browser = currentBrowser.toLowerCase();
|
|
178
|
-
// 正则格式,如 "/uc/i"
|
|
179
164
|
if (normalized.startsWith("/") && normalized.endsWith("/")) {
|
|
180
165
|
try {
|
|
181
166
|
const body = normalized.slice(1, -1);
|
|
@@ -184,15 +169,8 @@ export function matchBrowser(checkItem, currentBrowser) {
|
|
|
184
169
|
}
|
|
185
170
|
catch (_) { }
|
|
186
171
|
}
|
|
187
|
-
// 字符串模糊匹配
|
|
188
172
|
return browser.includes(normalized);
|
|
189
173
|
}
|
|
190
|
-
/**
|
|
191
|
-
* 1. 过滤 browserCheck
|
|
192
|
-
* 2. 按 tenant 过滤
|
|
193
|
-
* 3. 按 groupType(可为空)过滤
|
|
194
|
-
* 4. 与 list2 求交集
|
|
195
|
-
*/
|
|
196
174
|
export function filterSmartLines(list1, list2, groupType, tenant) {
|
|
197
175
|
if (!Array.isArray(list1) || !Array.isArray(list2)) {
|
|
198
176
|
throw new Error("前两个参数必须是数组");
|
|
@@ -203,12 +181,10 @@ export function filterSmartLines(list1, list2, groupType, tenant) {
|
|
|
203
181
|
const blocked = checks.some((checkItem) => matchBrowser(checkItem, currentBrowser));
|
|
204
182
|
return !blocked;
|
|
205
183
|
});
|
|
206
|
-
if (tenant)
|
|
184
|
+
if (tenant)
|
|
207
185
|
result = result.filter((item) => item.tenant === tenant);
|
|
208
|
-
|
|
209
|
-
if (groupType) {
|
|
186
|
+
if (groupType)
|
|
210
187
|
result = result.filter((item) => item.lineGroup === groupType);
|
|
211
|
-
}
|
|
212
188
|
const lines = result.map((item) => toStandardUrl(item.line));
|
|
213
|
-
return list2.map(toStandardUrl).filter((v) => isDomainMatch(lines, v));
|
|
189
|
+
return list2.map(toStandardUrl).filter((v) => isDomainMatch(lines, v));
|
|
214
190
|
}
|