@skrillex1224/playwright-toolkit 2.1.171 → 2.1.180
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/browser.js +15 -15
- package/dist/browser.js.map +2 -2
- package/dist/index.cjs +885 -338
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +885 -338
- package/dist/index.js.map +4 -4
- package/index.d.ts +52 -39
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -335,18 +335,18 @@ var fallbackLog = {
|
|
|
335
335
|
error: (...args) => console.error(...args),
|
|
336
336
|
debug: (...args) => console.debug ? console.debug(...args) : console.log(...args)
|
|
337
337
|
};
|
|
338
|
-
var resolveLogMethod = (
|
|
339
|
-
if (
|
|
340
|
-
return
|
|
338
|
+
var resolveLogMethod = (logger12, name) => {
|
|
339
|
+
if (logger12 && typeof logger12[name] === "function") {
|
|
340
|
+
return logger12[name].bind(logger12);
|
|
341
341
|
}
|
|
342
|
-
if (name === "warning" &&
|
|
343
|
-
return
|
|
342
|
+
if (name === "warning" && logger12 && typeof logger12.warn === "function") {
|
|
343
|
+
return logger12.warn.bind(logger12);
|
|
344
344
|
}
|
|
345
345
|
return fallbackLog[name];
|
|
346
346
|
};
|
|
347
347
|
var defaultLogger = null;
|
|
348
|
-
var setDefaultLogger = (
|
|
349
|
-
defaultLogger =
|
|
348
|
+
var setDefaultLogger = (logger12) => {
|
|
349
|
+
defaultLogger = logger12;
|
|
350
350
|
};
|
|
351
351
|
var resolveLogger = (explicitLogger) => {
|
|
352
352
|
if (explicitLogger && typeof explicitLogger.info === "function") {
|
|
@@ -373,8 +373,8 @@ var colorize = (text, color) => {
|
|
|
373
373
|
var createBaseLogger = (prefix = "", explicitLogger) => {
|
|
374
374
|
const name = prefix ? String(prefix) : "";
|
|
375
375
|
const dispatch = (methodName, icon, message, color) => {
|
|
376
|
-
const
|
|
377
|
-
const logFn = resolveLogMethod(
|
|
376
|
+
const logger12 = resolveLogger(explicitLogger);
|
|
377
|
+
const logFn = resolveLogMethod(logger12, methodName);
|
|
378
378
|
const timestamp = colorize(`[${formatTimestamp()}]`, ANSI.gray);
|
|
379
379
|
const line = formatLine(name, icon, message);
|
|
380
380
|
const coloredLine = colorize(line, color);
|
|
@@ -937,8 +937,582 @@ var ProxyMeterRuntime = {
|
|
|
937
937
|
getProxyMeterSnapshot
|
|
938
938
|
};
|
|
939
939
|
|
|
940
|
+
// src/runtime-env.js
|
|
941
|
+
var BROWSER_PROFILE_SCHEMA_VERSION = 1;
|
|
942
|
+
var rememberedRuntimeState = null;
|
|
943
|
+
var isPlainObject = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
944
|
+
var deepClone = (value) => {
|
|
945
|
+
if (value == null) return value;
|
|
946
|
+
try {
|
|
947
|
+
return JSON.parse(JSON.stringify(value));
|
|
948
|
+
} catch {
|
|
949
|
+
return value;
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
var tryParseJSON = (value) => {
|
|
953
|
+
if (value == null) return null;
|
|
954
|
+
if (typeof value === "object") return value;
|
|
955
|
+
const raw = String(value || "").trim();
|
|
956
|
+
if (!raw) return null;
|
|
957
|
+
try {
|
|
958
|
+
return JSON.parse(raw);
|
|
959
|
+
} catch {
|
|
960
|
+
return null;
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
var normalizeLocalStorage = (value) => {
|
|
964
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
965
|
+
return Object.entries(source).reduce((acc, [key, item]) => {
|
|
966
|
+
const safeKey = String(key || "").trim();
|
|
967
|
+
const safeValue = String(item ?? "").trim();
|
|
968
|
+
if (!safeKey || !safeValue || safeValue === "<nil>") return acc;
|
|
969
|
+
acc[safeKey] = safeValue;
|
|
970
|
+
return acc;
|
|
971
|
+
}, {});
|
|
972
|
+
};
|
|
973
|
+
var normalizeSessionStorage = (value) => {
|
|
974
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
975
|
+
return Object.entries(source).reduce((acc, [key, item]) => {
|
|
976
|
+
const safeKey = String(key || "").trim();
|
|
977
|
+
const safeValue = String(item ?? "").trim();
|
|
978
|
+
if (!safeKey || !safeValue || safeValue === "<nil>") return acc;
|
|
979
|
+
acc[safeKey] = safeValue;
|
|
980
|
+
return acc;
|
|
981
|
+
}, {});
|
|
982
|
+
};
|
|
983
|
+
var normalizeAuth = (value) => {
|
|
984
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
985
|
+
return Object.entries(source).reduce((acc, [key, item]) => {
|
|
986
|
+
const safeKey = String(key || "").trim();
|
|
987
|
+
const safeValue = String(item ?? "").trim();
|
|
988
|
+
if (!safeKey || !safeValue || safeValue === "<nil>") return acc;
|
|
989
|
+
acc[safeKey] = safeValue;
|
|
990
|
+
return acc;
|
|
991
|
+
}, {});
|
|
992
|
+
};
|
|
993
|
+
var normalizeCookies = (value) => {
|
|
994
|
+
if (!Array.isArray(value)) return [];
|
|
995
|
+
return value.map((item) => {
|
|
996
|
+
const raw = item && typeof item === "object" ? item : {};
|
|
997
|
+
const name = String(raw.name || "").trim();
|
|
998
|
+
const cookieValue = String(raw.value ?? "").trim();
|
|
999
|
+
if (!name || !cookieValue || cookieValue === "<nil>") return null;
|
|
1000
|
+
const domain = String(raw.domain || "").trim();
|
|
1001
|
+
const path2 = String(raw.path || "").trim() || "/";
|
|
1002
|
+
const sameSiteRaw = String(raw.sameSite || "").trim();
|
|
1003
|
+
return {
|
|
1004
|
+
...raw,
|
|
1005
|
+
name,
|
|
1006
|
+
value: cookieValue,
|
|
1007
|
+
domain,
|
|
1008
|
+
path: path2,
|
|
1009
|
+
...sameSiteRaw ? { sameSite: sameSiteRaw } : {}
|
|
1010
|
+
};
|
|
1011
|
+
}).filter(Boolean);
|
|
1012
|
+
};
|
|
1013
|
+
var buildCookieMap = (cookies) => cookies.reduce((acc, item) => {
|
|
1014
|
+
acc[item.name] = item.value;
|
|
1015
|
+
return acc;
|
|
1016
|
+
}, {});
|
|
1017
|
+
var normalizeObservedBrowserProfile = (value) => {
|
|
1018
|
+
const source = isPlainObject(value) ? value : {};
|
|
1019
|
+
const profile = {};
|
|
1020
|
+
const stringFields = {
|
|
1021
|
+
user_agent: source.user_agent,
|
|
1022
|
+
platform: source.platform,
|
|
1023
|
+
language: source.language,
|
|
1024
|
+
timezone: source.timezone
|
|
1025
|
+
};
|
|
1026
|
+
Object.entries(stringFields).forEach(([key, item]) => {
|
|
1027
|
+
const safeValue = String(item || "").trim();
|
|
1028
|
+
if (safeValue) {
|
|
1029
|
+
profile[key] = safeValue;
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
if (Array.isArray(source.languages)) {
|
|
1033
|
+
const languages = source.languages.map((item) => String(item || "").trim()).filter(Boolean);
|
|
1034
|
+
if (languages.length > 0) {
|
|
1035
|
+
profile.languages = languages;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
const numericFields = {
|
|
1039
|
+
hardware_concurrency: Number(source.hardware_concurrency || 0),
|
|
1040
|
+
device_memory: Number(source.device_memory || 0)
|
|
1041
|
+
};
|
|
1042
|
+
Object.entries(numericFields).forEach(([key, item]) => {
|
|
1043
|
+
if (Number.isFinite(item) && item > 0) {
|
|
1044
|
+
profile[key] = item;
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
if (isPlainObject(source.viewport)) {
|
|
1048
|
+
const width = Number(source.viewport.width || 0);
|
|
1049
|
+
const height = Number(source.viewport.height || 0);
|
|
1050
|
+
if (width > 0 && height > 0) {
|
|
1051
|
+
profile.viewport = { width, height };
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
if (isPlainObject(source.screen)) {
|
|
1055
|
+
const screenProfile = {};
|
|
1056
|
+
const screenFields = {
|
|
1057
|
+
width: Number(source.screen.width || 0),
|
|
1058
|
+
height: Number(source.screen.height || 0),
|
|
1059
|
+
avail_width: Number(source.screen.avail_width || 0),
|
|
1060
|
+
avail_height: Number(source.screen.avail_height || 0),
|
|
1061
|
+
color_depth: Number(source.screen.color_depth || 0),
|
|
1062
|
+
pixel_depth: Number(source.screen.pixel_depth || 0)
|
|
1063
|
+
};
|
|
1064
|
+
Object.entries(screenFields).forEach(([key, item]) => {
|
|
1065
|
+
if (Number.isFinite(item) && item > 0) {
|
|
1066
|
+
screenProfile[key] = item;
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
if (Object.keys(screenProfile).length > 0) {
|
|
1070
|
+
profile.screen = screenProfile;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
return profile;
|
|
1074
|
+
};
|
|
1075
|
+
var normalizeBrowserProfileCore = (value) => {
|
|
1076
|
+
const source = isPlainObject(value) ? value : {};
|
|
1077
|
+
const profile = {};
|
|
1078
|
+
if (isPlainObject(source.fingerprint) && Object.keys(source.fingerprint).length > 0) {
|
|
1079
|
+
profile.fingerprint = deepClone(source.fingerprint);
|
|
1080
|
+
}
|
|
1081
|
+
const timezoneId = String(source.timezone_id || "").trim();
|
|
1082
|
+
if (timezoneId) {
|
|
1083
|
+
profile.timezone_id = timezoneId;
|
|
1084
|
+
}
|
|
1085
|
+
const locale = String(source.locale || "").trim();
|
|
1086
|
+
if (locale) {
|
|
1087
|
+
profile.locale = locale;
|
|
1088
|
+
}
|
|
1089
|
+
const profileKey = String(source.profile_key || "").trim();
|
|
1090
|
+
if (profileKey) {
|
|
1091
|
+
profile.profile_key = profileKey;
|
|
1092
|
+
}
|
|
1093
|
+
const browserMajorVersion = Number(source.browser_major_version || 0);
|
|
1094
|
+
if (Number.isFinite(browserMajorVersion) && browserMajorVersion > 0) {
|
|
1095
|
+
profile.browser_major_version = browserMajorVersion;
|
|
1096
|
+
}
|
|
1097
|
+
const schemaVersion = Number(source.schema_version || 0);
|
|
1098
|
+
if (Number.isFinite(schemaVersion) && schemaVersion > 0) {
|
|
1099
|
+
profile.schema_version = schemaVersion;
|
|
1100
|
+
} else if (Object.keys(profile).length > 0) {
|
|
1101
|
+
profile.schema_version = BROWSER_PROFILE_SCHEMA_VERSION;
|
|
1102
|
+
}
|
|
1103
|
+
return profile;
|
|
1104
|
+
};
|
|
1105
|
+
var buildBrowserProfilePayload = (core = {}, observed = {}) => {
|
|
1106
|
+
const payload = {};
|
|
1107
|
+
if (isPlainObject(core) && Object.keys(core).length > 0) {
|
|
1108
|
+
payload.core = core;
|
|
1109
|
+
}
|
|
1110
|
+
if (isPlainObject(observed) && Object.keys(observed).length > 0) {
|
|
1111
|
+
payload.observed = observed;
|
|
1112
|
+
}
|
|
1113
|
+
return payload;
|
|
1114
|
+
};
|
|
1115
|
+
var mergePlainObject = (target = {}, source = {}) => {
|
|
1116
|
+
const next = isPlainObject(target) ? deepClone(target) : {};
|
|
1117
|
+
if (!isPlainObject(source)) return next;
|
|
1118
|
+
Object.entries(source).forEach(([key, value]) => {
|
|
1119
|
+
if (!key) return;
|
|
1120
|
+
if (isPlainObject(value) && isPlainObject(next[key])) {
|
|
1121
|
+
next[key] = mergePlainObject(next[key], value);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
next[key] = deepClone(value);
|
|
1125
|
+
});
|
|
1126
|
+
return next;
|
|
1127
|
+
};
|
|
1128
|
+
var mergeBrowserProfilePayload = (target = {}, source = {}) => {
|
|
1129
|
+
const current = isPlainObject(target) ? target : {};
|
|
1130
|
+
const incoming = isPlainObject(source) ? source : {};
|
|
1131
|
+
const currentCore = normalizeBrowserProfileCore(current.core);
|
|
1132
|
+
const incomingCore = normalizeBrowserProfileCore(incoming.core);
|
|
1133
|
+
const currentObserved = normalizeObservedBrowserProfile(current.observed);
|
|
1134
|
+
const incomingObserved = normalizeObservedBrowserProfile(incoming.observed);
|
|
1135
|
+
let mergedCore = currentCore;
|
|
1136
|
+
if (Object.keys(mergedCore).length === 0 && Object.keys(incomingCore).length > 0) {
|
|
1137
|
+
mergedCore = incomingCore;
|
|
1138
|
+
} else if (Object.keys(incomingCore).length > 0) {
|
|
1139
|
+
const currentVersion = Number(currentCore.browser_major_version || 0);
|
|
1140
|
+
const incomingVersion = Number(incomingCore.browser_major_version || 0);
|
|
1141
|
+
if (currentVersion > 0 && incomingVersion > 0 && currentVersion !== incomingVersion) {
|
|
1142
|
+
mergedCore = incomingCore;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
const mergedObserved = mergePlainObject(currentObserved, incomingObserved);
|
|
1146
|
+
return buildBrowserProfilePayload(mergedCore, mergedObserved);
|
|
1147
|
+
};
|
|
1148
|
+
var mergeEnvPatchObjects = (...patches) => {
|
|
1149
|
+
const merged = {};
|
|
1150
|
+
patches.forEach((patch) => {
|
|
1151
|
+
if (!isPlainObject(patch)) return;
|
|
1152
|
+
Object.entries(patch).forEach(([key, value]) => {
|
|
1153
|
+
if (!key) return;
|
|
1154
|
+
if (key === "browser_profile") {
|
|
1155
|
+
const browserProfile = mergeBrowserProfilePayload(merged.browser_profile, value);
|
|
1156
|
+
if (Object.keys(browserProfile).length > 0) {
|
|
1157
|
+
merged.browser_profile = browserProfile;
|
|
1158
|
+
}
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (isPlainObject(value) && isPlainObject(merged[key])) {
|
|
1162
|
+
merged[key] = mergePlainObject(merged[key], value);
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
merged[key] = deepClone(value);
|
|
1166
|
+
});
|
|
1167
|
+
});
|
|
1168
|
+
return Object.keys(merged).length > 0 ? merged : null;
|
|
1169
|
+
};
|
|
1170
|
+
var normalizeBrowserProfile = (value) => {
|
|
1171
|
+
const source = isPlainObject(value) ? value : {};
|
|
1172
|
+
const coreSource = isPlainObject(source.core) ? source.core : {};
|
|
1173
|
+
const observedSource = isPlainObject(source.observed) ? source.observed : {};
|
|
1174
|
+
const core = normalizeBrowserProfileCore(coreSource);
|
|
1175
|
+
const observed = normalizeObservedBrowserProfile(observedSource);
|
|
1176
|
+
return {
|
|
1177
|
+
core,
|
|
1178
|
+
observed,
|
|
1179
|
+
payload: buildBrowserProfilePayload(core, observed)
|
|
1180
|
+
};
|
|
1181
|
+
};
|
|
1182
|
+
var rememberRuntimeState = (state) => {
|
|
1183
|
+
rememberedRuntimeState = deepClone(state);
|
|
1184
|
+
return rememberedRuntimeState;
|
|
1185
|
+
};
|
|
1186
|
+
var normalizeRuntimeState = (source = {}, actor = "") => {
|
|
1187
|
+
if (source && typeof source === "object" && source.runtime && typeof source.runtime === "object" && !Array.isArray(source.runtime) && Object.prototype.hasOwnProperty.call(source, "browserProfileCore")) {
|
|
1188
|
+
return source;
|
|
1189
|
+
}
|
|
1190
|
+
return RuntimeEnv.parseInput(source, actor);
|
|
1191
|
+
};
|
|
1192
|
+
var RuntimeEnv = {
|
|
1193
|
+
tryParseJSON,
|
|
1194
|
+
normalizeCookies,
|
|
1195
|
+
normalizeLocalStorage,
|
|
1196
|
+
normalizeSessionStorage,
|
|
1197
|
+
normalizeAuth,
|
|
1198
|
+
normalizeBrowserProfileCore,
|
|
1199
|
+
normalizeObservedBrowserProfile,
|
|
1200
|
+
mergeEnvPatches(...patches) {
|
|
1201
|
+
return mergeEnvPatchObjects(...patches);
|
|
1202
|
+
},
|
|
1203
|
+
// parseInput 把 Actor 输入里的 runtime 字段标准化成 toolkit 内部统一结构。
|
|
1204
|
+
// 后续 visitor 只和这个 state 交互,不再自己拼 token/cookie 逻辑。
|
|
1205
|
+
parseInput(input = {}, actor = "") {
|
|
1206
|
+
const runtime2 = tryParseJSON(input?.runtime) || {};
|
|
1207
|
+
const resolvedActor = String(actor || input?.actor || "").trim();
|
|
1208
|
+
const cookies = normalizeCookies(runtime2?.cookies);
|
|
1209
|
+
const cookieMap = buildCookieMap(cookies);
|
|
1210
|
+
const localStorage = normalizeLocalStorage(runtime2?.local_storage);
|
|
1211
|
+
const sessionStorage = normalizeSessionStorage(runtime2?.session_storage);
|
|
1212
|
+
const auth = normalizeAuth(runtime2?.auth);
|
|
1213
|
+
const browserProfile = normalizeBrowserProfile(runtime2?.browser_profile);
|
|
1214
|
+
const envId = String(input?.env_id || "").trim();
|
|
1215
|
+
const normalizedRuntime = {
|
|
1216
|
+
...runtime2,
|
|
1217
|
+
...cookies.length > 0 ? { cookies } : {},
|
|
1218
|
+
...Object.keys(localStorage).length > 0 ? { local_storage: localStorage } : {},
|
|
1219
|
+
...Object.keys(sessionStorage).length > 0 ? { session_storage: sessionStorage } : {},
|
|
1220
|
+
...Object.keys(auth).length > 0 ? { auth } : {}
|
|
1221
|
+
};
|
|
1222
|
+
delete normalizedRuntime.actor;
|
|
1223
|
+
delete normalizedRuntime.env_id;
|
|
1224
|
+
if (Object.keys(browserProfile.payload).length > 0) {
|
|
1225
|
+
normalizedRuntime.browser_profile = browserProfile.payload;
|
|
1226
|
+
} else {
|
|
1227
|
+
delete normalizedRuntime.browser_profile;
|
|
1228
|
+
}
|
|
1229
|
+
const state = {
|
|
1230
|
+
actor: resolvedActor,
|
|
1231
|
+
runtime: normalizedRuntime,
|
|
1232
|
+
envId,
|
|
1233
|
+
auth,
|
|
1234
|
+
cookies,
|
|
1235
|
+
cookieMap,
|
|
1236
|
+
localStorage,
|
|
1237
|
+
sessionStorage,
|
|
1238
|
+
browserProfile: browserProfile.payload,
|
|
1239
|
+
browserProfileCore: browserProfile.core,
|
|
1240
|
+
browserProfileObserved: browserProfile.observed
|
|
1241
|
+
};
|
|
1242
|
+
rememberRuntimeState(state);
|
|
1243
|
+
return state;
|
|
1244
|
+
},
|
|
1245
|
+
// buildEnvPatch 只构造允许回写到后端 env 的字段集合。
|
|
1246
|
+
buildEnvPatch(source = {}, actor = "") {
|
|
1247
|
+
const state = normalizeRuntimeState(source, actor);
|
|
1248
|
+
const browserProfile = buildBrowserProfilePayload(state.browserProfileCore, state.browserProfileObserved);
|
|
1249
|
+
const envPatch = {
|
|
1250
|
+
...Object.keys(state.auth || {}).length > 0 ? { auth: state.auth } : {},
|
|
1251
|
+
...Array.isArray(state.cookies) && state.cookies.length > 0 ? { cookies: state.cookies } : {},
|
|
1252
|
+
...Object.keys(state.localStorage || {}).length > 0 ? { local_storage: state.localStorage } : {},
|
|
1253
|
+
...Object.keys(state.sessionStorage || {}).length > 0 ? { session_storage: state.sessionStorage } : {},
|
|
1254
|
+
...Object.keys(browserProfile).length > 0 ? { browser_profile: browserProfile } : {}
|
|
1255
|
+
};
|
|
1256
|
+
return Object.keys(envPatch).length > 0 ? envPatch : null;
|
|
1257
|
+
},
|
|
1258
|
+
// hasLoginState 用来区分“必须鉴权的平台”和“可匿名运行的平台”。
|
|
1259
|
+
hasLoginState(source = {}, actor = "") {
|
|
1260
|
+
const state = normalizeRuntimeState(source, actor);
|
|
1261
|
+
return Array.isArray(state.cookies) && state.cookies.length > 0 || Object.keys(state.localStorage || {}).length > 0 || Object.keys(state.sessionStorage || {}).length > 0 || Object.keys(state.auth || {}).length > 0;
|
|
1262
|
+
},
|
|
1263
|
+
getAuthValue(source = {}, key = "", actor = "") {
|
|
1264
|
+
const state = normalizeRuntimeState(source, actor);
|
|
1265
|
+
const safeKey = String(key || "").trim();
|
|
1266
|
+
if (!safeKey) return "";
|
|
1267
|
+
return String(state.auth?.[safeKey] ?? "").trim();
|
|
1268
|
+
},
|
|
1269
|
+
rememberState(source = {}) {
|
|
1270
|
+
const state = normalizeRuntimeState(source);
|
|
1271
|
+
rememberRuntimeState(state);
|
|
1272
|
+
return RuntimeEnv.peekRememberedState();
|
|
1273
|
+
},
|
|
1274
|
+
peekRememberedState() {
|
|
1275
|
+
return rememberedRuntimeState ? deepClone(rememberedRuntimeState) : null;
|
|
1276
|
+
},
|
|
1277
|
+
getBrowserProfileCore(source = {}, actor = "") {
|
|
1278
|
+
const state = normalizeRuntimeState(source, actor);
|
|
1279
|
+
return deepClone(state.browserProfileCore || {});
|
|
1280
|
+
},
|
|
1281
|
+
setBrowserProfileCore(source = {}, core = {}, actor = "") {
|
|
1282
|
+
const state = normalizeRuntimeState(source, actor);
|
|
1283
|
+
const normalizedCore = normalizeBrowserProfileCore(core);
|
|
1284
|
+
state.browserProfileCore = normalizedCore;
|
|
1285
|
+
state.browserProfile = buildBrowserProfilePayload(normalizedCore, state.browserProfileObserved);
|
|
1286
|
+
if (Object.keys(state.browserProfile).length > 0) {
|
|
1287
|
+
state.runtime.browser_profile = state.browserProfile;
|
|
1288
|
+
} else {
|
|
1289
|
+
delete state.runtime.browser_profile;
|
|
1290
|
+
}
|
|
1291
|
+
rememberRuntimeState(state);
|
|
1292
|
+
return state;
|
|
1293
|
+
},
|
|
1294
|
+
// applyToPage 只负责把登录态相关字段注入页面:
|
|
1295
|
+
// cookies / localStorage / sessionStorage。
|
|
1296
|
+
// 指纹、时区、UA、viewport 的回放发生在 launch.js 启动阶段,不在这里做。
|
|
1297
|
+
async applyToPage(page, source = {}, options = {}) {
|
|
1298
|
+
if (!page) return;
|
|
1299
|
+
const state = normalizeRuntimeState(source, options?.actor || "");
|
|
1300
|
+
const localStorage = state.localStorage || {};
|
|
1301
|
+
const sessionStorage = state.sessionStorage || {};
|
|
1302
|
+
const cookies = (state.cookies || []).map((cookie) => {
|
|
1303
|
+
const normalized = { ...cookie };
|
|
1304
|
+
if (!normalized.path) {
|
|
1305
|
+
normalized.path = "/";
|
|
1306
|
+
}
|
|
1307
|
+
if (!normalized.domain) {
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1310
|
+
return normalized;
|
|
1311
|
+
}).filter(Boolean);
|
|
1312
|
+
if (cookies.length > 0) {
|
|
1313
|
+
await page.context().addCookies(cookies);
|
|
1314
|
+
}
|
|
1315
|
+
if (Object.keys(localStorage).length > 0) {
|
|
1316
|
+
await page.addInitScript((payload) => {
|
|
1317
|
+
try {
|
|
1318
|
+
Object.entries(payload || {}).forEach(([key, value]) => {
|
|
1319
|
+
if (!key) return;
|
|
1320
|
+
window.localStorage.setItem(key, String(value ?? ""));
|
|
1321
|
+
});
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
}
|
|
1324
|
+
}, localStorage);
|
|
1325
|
+
}
|
|
1326
|
+
if (Object.keys(sessionStorage).length > 0) {
|
|
1327
|
+
await page.addInitScript((payload) => {
|
|
1328
|
+
try {
|
|
1329
|
+
Object.entries(payload || {}).forEach(([key, value]) => {
|
|
1330
|
+
if (!key) return;
|
|
1331
|
+
window.sessionStorage.setItem(key, String(value ?? ""));
|
|
1332
|
+
});
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
}
|
|
1335
|
+
}, sessionStorage);
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1338
|
+
// captureEnvPatch 在任务结束时采集最新环境快照,用于 pushSuccess / pushFailed 自动回写。
|
|
1339
|
+
async captureEnvPatch(page, source = {}, options = {}) {
|
|
1340
|
+
const state = normalizeRuntimeState(source, options?.actor || "");
|
|
1341
|
+
const baseline = RuntimeEnv.buildEnvPatch(state) || {};
|
|
1342
|
+
const patch = {
|
|
1343
|
+
...baseline
|
|
1344
|
+
};
|
|
1345
|
+
if (!page || typeof page.evaluate !== "function" || typeof page.context !== "function") {
|
|
1346
|
+
return Object.keys(patch).length > 0 ? patch : null;
|
|
1347
|
+
}
|
|
1348
|
+
try {
|
|
1349
|
+
const contextCookies = await page.context().cookies();
|
|
1350
|
+
const normalizedCookies = normalizeCookies(contextCookies || []);
|
|
1351
|
+
if (normalizedCookies.length > 0) {
|
|
1352
|
+
patch.cookies = normalizedCookies;
|
|
1353
|
+
}
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
}
|
|
1356
|
+
try {
|
|
1357
|
+
const snapshot = await page.evaluate(() => {
|
|
1358
|
+
const localStorage2 = {};
|
|
1359
|
+
const sessionStorage2 = {};
|
|
1360
|
+
try {
|
|
1361
|
+
for (let i = 0; i < window.localStorage.length; i += 1) {
|
|
1362
|
+
const key = window.localStorage.key(i);
|
|
1363
|
+
if (!key) continue;
|
|
1364
|
+
const value = window.localStorage.getItem(key);
|
|
1365
|
+
if (value !== null) localStorage2[key] = value;
|
|
1366
|
+
}
|
|
1367
|
+
} catch (error) {
|
|
1368
|
+
}
|
|
1369
|
+
try {
|
|
1370
|
+
for (let i = 0; i < window.sessionStorage.length; i += 1) {
|
|
1371
|
+
const key = window.sessionStorage.key(i);
|
|
1372
|
+
if (!key) continue;
|
|
1373
|
+
const value = window.sessionStorage.getItem(key);
|
|
1374
|
+
if (value !== null) sessionStorage2[key] = value;
|
|
1375
|
+
}
|
|
1376
|
+
} catch (error) {
|
|
1377
|
+
}
|
|
1378
|
+
const nav = window.navigator || {};
|
|
1379
|
+
const screen = window.screen || {};
|
|
1380
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "";
|
|
1381
|
+
return {
|
|
1382
|
+
localStorage: localStorage2,
|
|
1383
|
+
sessionStorage: sessionStorage2,
|
|
1384
|
+
browserProfileObserved: {
|
|
1385
|
+
user_agent: nav.userAgent || "",
|
|
1386
|
+
platform: nav.platform || "",
|
|
1387
|
+
language: nav.language || "",
|
|
1388
|
+
languages: Array.isArray(nav.languages) ? nav.languages : [],
|
|
1389
|
+
hardware_concurrency: Number(nav.hardwareConcurrency || 0),
|
|
1390
|
+
device_memory: Number(nav.deviceMemory || 0),
|
|
1391
|
+
timezone,
|
|
1392
|
+
viewport: {
|
|
1393
|
+
width: Number(window.innerWidth || 0),
|
|
1394
|
+
height: Number(window.innerHeight || 0)
|
|
1395
|
+
},
|
|
1396
|
+
screen: {
|
|
1397
|
+
width: Number(screen.width || 0),
|
|
1398
|
+
height: Number(screen.height || 0),
|
|
1399
|
+
avail_width: Number(screen.availWidth || 0),
|
|
1400
|
+
avail_height: Number(screen.availHeight || 0),
|
|
1401
|
+
color_depth: Number(screen.colorDepth || 0),
|
|
1402
|
+
pixel_depth: Number(screen.pixelDepth || 0)
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
});
|
|
1407
|
+
const localStorage = normalizeLocalStorage(snapshot?.localStorage);
|
|
1408
|
+
if (Object.keys(localStorage).length > 0) {
|
|
1409
|
+
patch.local_storage = localStorage;
|
|
1410
|
+
}
|
|
1411
|
+
const sessionStorage = normalizeSessionStorage(snapshot?.sessionStorage);
|
|
1412
|
+
if (Object.keys(sessionStorage).length > 0) {
|
|
1413
|
+
patch.session_storage = sessionStorage;
|
|
1414
|
+
}
|
|
1415
|
+
const observedProfile = normalizeObservedBrowserProfile(snapshot?.browserProfileObserved);
|
|
1416
|
+
const browserProfile = buildBrowserProfilePayload(state.browserProfileCore, observedProfile);
|
|
1417
|
+
if (Object.keys(browserProfile).length > 0) {
|
|
1418
|
+
patch.browser_profile = browserProfile;
|
|
1419
|
+
}
|
|
1420
|
+
} catch (error) {
|
|
1421
|
+
}
|
|
1422
|
+
return Object.keys(patch).length > 0 ? patch : null;
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
|
|
940
1426
|
// src/apify-kit.js
|
|
941
1427
|
var logger3 = createInternalLogger("ApifyKit");
|
|
1428
|
+
var resolveRuntimeContext = (input) => {
|
|
1429
|
+
const rememberedState = RuntimeEnv.peekRememberedState();
|
|
1430
|
+
const state = rememberedState || RuntimeEnv.parseInput(input || {});
|
|
1431
|
+
const envPatch = RuntimeEnv.buildEnvPatch(state) || null;
|
|
1432
|
+
return {
|
|
1433
|
+
actor: state.actor,
|
|
1434
|
+
runtime: state.runtime,
|
|
1435
|
+
envPatch
|
|
1436
|
+
};
|
|
1437
|
+
};
|
|
1438
|
+
var isPageLike = (value) => {
|
|
1439
|
+
if (!value || typeof value !== "object") return false;
|
|
1440
|
+
return typeof value.evaluate === "function" && typeof value.context === "function";
|
|
1441
|
+
};
|
|
1442
|
+
var pickPage = (...candidates) => {
|
|
1443
|
+
for (const candidate of candidates) {
|
|
1444
|
+
if (isPageLike(candidate)) {
|
|
1445
|
+
return candidate;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
return null;
|
|
1449
|
+
};
|
|
1450
|
+
var toSerializable = (value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) => {
|
|
1451
|
+
if (depth > 6) return "[MaxDepth]";
|
|
1452
|
+
if (value == null) return value;
|
|
1453
|
+
const valueType = typeof value;
|
|
1454
|
+
if (valueType === "string" || valueType === "number" || valueType === "boolean") {
|
|
1455
|
+
return value;
|
|
1456
|
+
}
|
|
1457
|
+
if (valueType === "bigint") {
|
|
1458
|
+
return value.toString();
|
|
1459
|
+
}
|
|
1460
|
+
if (valueType === "function" || valueType === "symbol") {
|
|
1461
|
+
return void 0;
|
|
1462
|
+
}
|
|
1463
|
+
if (value instanceof Error) {
|
|
1464
|
+
return (0, import_serialize_error2.serializeError)(value);
|
|
1465
|
+
}
|
|
1466
|
+
if (Array.isArray(value)) {
|
|
1467
|
+
return value.map((item) => toSerializable(item, depth + 1, seen)).filter((item) => item !== void 0);
|
|
1468
|
+
}
|
|
1469
|
+
if (valueType !== "object") {
|
|
1470
|
+
return String(value);
|
|
1471
|
+
}
|
|
1472
|
+
if (seen.has(value)) {
|
|
1473
|
+
return "[Circular]";
|
|
1474
|
+
}
|
|
1475
|
+
seen.add(value);
|
|
1476
|
+
const ctorName = String(value?.constructor?.name || "");
|
|
1477
|
+
if (ctorName === "Page" || ctorName === "BrowserContext" || ctorName === "Browser" || ctorName === "Frame" || ctorName === "ElementHandle" || ctorName === "JSHandle") {
|
|
1478
|
+
return `[${ctorName}]`;
|
|
1479
|
+
}
|
|
1480
|
+
const output = {};
|
|
1481
|
+
Object.entries(value).forEach(([key, item]) => {
|
|
1482
|
+
const safeValue = toSerializable(item, depth + 1, seen);
|
|
1483
|
+
if (safeValue !== void 0) {
|
|
1484
|
+
output[key] = safeValue;
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
return output;
|
|
1488
|
+
};
|
|
1489
|
+
var sanitizeMeta = (meta) => {
|
|
1490
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
|
|
1491
|
+
return {};
|
|
1492
|
+
}
|
|
1493
|
+
const result = toSerializable(meta) || {};
|
|
1494
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
1495
|
+
delete result.page;
|
|
1496
|
+
}
|
|
1497
|
+
return result;
|
|
1498
|
+
};
|
|
1499
|
+
var captureAutoEnvPatch = async (page, runtimeContext) => {
|
|
1500
|
+
const currentPage = pickPage(page);
|
|
1501
|
+
if (!currentPage) return null;
|
|
1502
|
+
try {
|
|
1503
|
+
return await RuntimeEnv.captureEnvPatch(
|
|
1504
|
+
currentPage,
|
|
1505
|
+
{
|
|
1506
|
+
actor: runtimeContext.actor,
|
|
1507
|
+
runtime: runtimeContext.runtime
|
|
1508
|
+
},
|
|
1509
|
+
{ actor: runtimeContext.actor }
|
|
1510
|
+
);
|
|
1511
|
+
} catch (error) {
|
|
1512
|
+
logger3.warn(`\u81EA\u52A8\u91C7\u96C6 env_patch \u5931\u8D25: ${error?.message || error}`);
|
|
1513
|
+
return null;
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
942
1516
|
async function createApifyKit() {
|
|
943
1517
|
let apify = null;
|
|
944
1518
|
try {
|
|
@@ -947,6 +1521,9 @@ async function createApifyKit() {
|
|
|
947
1521
|
throw new Error("\u26A0\uFE0F apify \u5E93\u672A\u5B89\u88C5\uFF0CApifyKit \u7684 Actor \u76F8\u5173\u529F\u80FD\u4E0D\u53EF\u7528");
|
|
948
1522
|
}
|
|
949
1523
|
const { Actor: Actor2 } = apify;
|
|
1524
|
+
const actorInput = await Actor2.getInput() || {};
|
|
1525
|
+
let lastPage = null;
|
|
1526
|
+
const getRuntimeContext = () => resolveRuntimeContext(actorInput);
|
|
950
1527
|
return {
|
|
951
1528
|
/**
|
|
952
1529
|
* 核心封装:执行步骤,带自动日志确认、失败截图处理和重试机制
|
|
@@ -962,6 +1539,9 @@ async function createApifyKit() {
|
|
|
962
1539
|
* @param {Function} [options.retry.before] - 重试前钩子,可覆盖默认等待行为
|
|
963
1540
|
*/
|
|
964
1541
|
async runStep(step, page, actionFn, options = {}) {
|
|
1542
|
+
if (isPageLike(page)) {
|
|
1543
|
+
lastPage = page;
|
|
1544
|
+
}
|
|
965
1545
|
const { failActor = true, retry = {} } = options;
|
|
966
1546
|
const { times: retryTimes = 0, mode: retryMode = "direct", before: beforeRetry } = retry;
|
|
967
1547
|
const executeAction = async (attemptNumber) => {
|
|
@@ -1023,13 +1603,18 @@ async function createApifyKit() {
|
|
|
1023
1603
|
} catch (snapErr) {
|
|
1024
1604
|
logger3.warn(`\u622A\u56FE\u751F\u6210\u5931\u8D25: ${snapErr.message}`);
|
|
1025
1605
|
}
|
|
1026
|
-
await this.pushFailed(
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1606
|
+
await this.pushFailed(
|
|
1607
|
+
finalError,
|
|
1608
|
+
{
|
|
1609
|
+
step,
|
|
1610
|
+
base64,
|
|
1611
|
+
retryAttempts: retryTimes
|
|
1612
|
+
},
|
|
1613
|
+
{
|
|
1614
|
+
page,
|
|
1615
|
+
options
|
|
1616
|
+
}
|
|
1617
|
+
);
|
|
1033
1618
|
await Actor2.fail(`Run Step ${step} \u5931\u8D25 (\u5DF2\u91CD\u8BD5 ${retryTimes} \u6B21): ${finalError.message}`);
|
|
1034
1619
|
} else {
|
|
1035
1620
|
throw finalError;
|
|
@@ -1051,42 +1636,83 @@ async function createApifyKit() {
|
|
|
1051
1636
|
});
|
|
1052
1637
|
},
|
|
1053
1638
|
/**
|
|
1054
|
-
*
|
|
1639
|
+
* 推送成功数据的通用方法。
|
|
1640
|
+
* 这里会自动做三件事:
|
|
1641
|
+
* 1. 采集当前页面的 env_patch;
|
|
1642
|
+
* 2. 合并显式传入的 env_patch;
|
|
1643
|
+
* 3. 一并写入 dataset,供后端回写 runtime_env_json。
|
|
1055
1644
|
* @param {Object} data - 要推送的数据对象
|
|
1056
1645
|
*/
|
|
1057
|
-
async pushSuccess(data) {
|
|
1646
|
+
async pushSuccess(data, options = {}) {
|
|
1647
|
+
const runtimeContext = getRuntimeContext();
|
|
1648
|
+
const page = pickPage(options?.page, data?.page, lastPage);
|
|
1649
|
+
if (page) {
|
|
1650
|
+
lastPage = page;
|
|
1651
|
+
}
|
|
1652
|
+
let payloadData = data;
|
|
1653
|
+
let explicitPatch = null;
|
|
1654
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
1655
|
+
payloadData = { ...data };
|
|
1656
|
+
explicitPatch = payloadData.env_patch;
|
|
1657
|
+
delete payloadData.env_patch;
|
|
1658
|
+
delete payloadData.page;
|
|
1659
|
+
}
|
|
1660
|
+
const capturedPatch = await captureAutoEnvPatch(page, runtimeContext);
|
|
1661
|
+
const envPatch = RuntimeEnv.mergeEnvPatches(runtimeContext.envPatch, capturedPatch, explicitPatch);
|
|
1058
1662
|
const traffic = await ProxyMeterRuntime.getProxyMeterSnapshot({ finalize: true });
|
|
1059
|
-
|
|
1060
|
-
// 固定为0
|
|
1663
|
+
const payload = {
|
|
1061
1664
|
code: Code.Success,
|
|
1062
1665
|
status: Status.Success,
|
|
1063
1666
|
...traffic ? { traffic } : {},
|
|
1064
1667
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1065
|
-
data
|
|
1668
|
+
data: payloadData
|
|
1669
|
+
};
|
|
1670
|
+
if (envPatch) {
|
|
1671
|
+
payload.env_patch = envPatch;
|
|
1672
|
+
}
|
|
1673
|
+
await Actor2.pushData({
|
|
1674
|
+
...payload
|
|
1066
1675
|
});
|
|
1067
1676
|
logger3.success("pushSuccess", "Data pushed");
|
|
1068
1677
|
},
|
|
1069
1678
|
/**
|
|
1070
|
-
* 推送失败数据的通用方法(私有方法,仅供runStep
|
|
1071
|
-
*
|
|
1679
|
+
* 推送失败数据的通用方法(私有方法,仅供 runStep 内部使用)。
|
|
1680
|
+
* 即便任务失败,这里也会自动回收 env_patch,确保 cookies/storage 的最新状态不会丢。
|
|
1072
1681
|
* @param {Error|CrawlerError} error - 错误对象
|
|
1073
|
-
* @param {Object} [meta] - 额外的数据(如failedStep
|
|
1682
|
+
* @param {Object} [meta] - 额外的数据(如 failedStep、screenshotBase64 等)
|
|
1683
|
+
* @param {Object} [options] - 附加选项(如 page)
|
|
1074
1684
|
* @private
|
|
1075
1685
|
*/
|
|
1076
|
-
async pushFailed(error, meta = {}) {
|
|
1686
|
+
async pushFailed(error, meta = {}, options = {}) {
|
|
1687
|
+
const runtimeContext = getRuntimeContext();
|
|
1688
|
+
const page = pickPage(options?.page, meta?.page, lastPage);
|
|
1689
|
+
if (page) {
|
|
1690
|
+
lastPage = page;
|
|
1691
|
+
}
|
|
1692
|
+
const capturedPatch = await captureAutoEnvPatch(page, runtimeContext);
|
|
1693
|
+
const explicitPatch = meta?.env_patch;
|
|
1694
|
+
const envPatch = RuntimeEnv.mergeEnvPatches(runtimeContext.envPatch, capturedPatch, explicitPatch);
|
|
1695
|
+
const safeMeta = sanitizeMeta(meta);
|
|
1696
|
+
delete safeMeta.env_patch;
|
|
1077
1697
|
const isCrawlerError = CrawlerError.isCrawlerError(error);
|
|
1078
1698
|
const code = isCrawlerError ? error.code : Code.UnknownError;
|
|
1079
1699
|
const context = isCrawlerError ? error.context : {};
|
|
1080
1700
|
const traffic = await ProxyMeterRuntime.getProxyMeterSnapshot({ finalize: true });
|
|
1081
|
-
|
|
1701
|
+
const payload = {
|
|
1082
1702
|
// 如果是 CrawlerError,使用其 code,否则使用默认 Failed code
|
|
1083
1703
|
code,
|
|
1084
1704
|
status: Status.Failed,
|
|
1085
1705
|
...traffic ? { traffic } : {},
|
|
1086
1706
|
error: (0, import_serialize_error2.serializeError)(error),
|
|
1087
|
-
meta,
|
|
1707
|
+
meta: safeMeta,
|
|
1088
1708
|
context,
|
|
1089
1709
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1710
|
+
};
|
|
1711
|
+
if (envPatch) {
|
|
1712
|
+
payload.env_patch = envPatch;
|
|
1713
|
+
}
|
|
1714
|
+
await Actor2.pushData({
|
|
1715
|
+
...payload
|
|
1090
1716
|
});
|
|
1091
1717
|
logger3.success("pushFailed", "Error data pushed");
|
|
1092
1718
|
}
|
|
@@ -1177,12 +1803,13 @@ var DEFAULT_LAUNCH_ARGS = [
|
|
|
1177
1803
|
// '--disable-blink-features=AutomationControlled', // Crawlee 可能会自动处理,过多干预反而会被识别
|
|
1178
1804
|
"--no-sandbox",
|
|
1179
1805
|
"--disable-setuid-sandbox",
|
|
1180
|
-
"--window-position=0,0"
|
|
1181
|
-
`--lang=${BASE_CONFIG.locale}`
|
|
1806
|
+
"--window-position=0,0"
|
|
1182
1807
|
];
|
|
1183
|
-
function buildFingerprintOptions(locale) {
|
|
1808
|
+
function buildFingerprintOptions({ locale = BASE_CONFIG.locale, browserMajorVersion = 0 } = {}) {
|
|
1809
|
+
const normalizedBrowserMajorVersion = Number(browserMajorVersion || 0);
|
|
1810
|
+
const browserDescriptor = normalizedBrowserMajorVersion > 0 ? [{ name: "chrome", minVersion: normalizedBrowserMajorVersion, maxVersion: normalizedBrowserMajorVersion }] : [{ name: "chrome", minVersion: 110 }];
|
|
1184
1811
|
return {
|
|
1185
|
-
browsers:
|
|
1812
|
+
browsers: browserDescriptor,
|
|
1186
1813
|
devices: ["desktop"],
|
|
1187
1814
|
operatingSystems: ["windows", "macos", "linux"],
|
|
1188
1815
|
locales: [locale]
|
|
@@ -1198,20 +1825,21 @@ var AntiCheat = {
|
|
|
1198
1825
|
/**
|
|
1199
1826
|
* 用于 Crawlee fingerprint generator 的统一配置(桌面端)。
|
|
1200
1827
|
*/
|
|
1201
|
-
getFingerprintGeneratorOptions() {
|
|
1202
|
-
return buildFingerprintOptions(
|
|
1828
|
+
getFingerprintGeneratorOptions(options = {}) {
|
|
1829
|
+
return buildFingerprintOptions(options);
|
|
1203
1830
|
},
|
|
1204
1831
|
/**
|
|
1205
1832
|
* 获取基础启动参数。
|
|
1206
1833
|
*/
|
|
1207
|
-
getLaunchArgs() {
|
|
1208
|
-
|
|
1834
|
+
getLaunchArgs(options = {}) {
|
|
1835
|
+
const locale = String(options?.locale || BASE_CONFIG.locale).trim() || BASE_CONFIG.locale;
|
|
1836
|
+
return [...DEFAULT_LAUNCH_ARGS, `--lang=${locale}`];
|
|
1209
1837
|
},
|
|
1210
1838
|
/**
|
|
1211
1839
|
* 为 got-scraping 生成与浏览器一致的 TLS 指纹配置(桌面端)。
|
|
1212
1840
|
*/
|
|
1213
1841
|
getTlsFingerprintOptions(userAgent = "", acceptLanguage = "") {
|
|
1214
|
-
return buildFingerprintOptions(
|
|
1842
|
+
return buildFingerprintOptions();
|
|
1215
1843
|
},
|
|
1216
1844
|
/**
|
|
1217
1845
|
* 规范化请求头
|
|
@@ -1702,6 +2330,11 @@ var Humanize = {
|
|
|
1702
2330
|
}
|
|
1703
2331
|
};
|
|
1704
2332
|
|
|
2333
|
+
// src/launch.js
|
|
2334
|
+
var import_node_child_process = require("node:child_process");
|
|
2335
|
+
var import_fingerprint_generator = require("fingerprint-generator");
|
|
2336
|
+
var import_fingerprint_injector = require("fingerprint-injector");
|
|
2337
|
+
|
|
1705
2338
|
// src/proxy-bypass.js
|
|
1706
2339
|
var import_picomatch = __toESM(require("picomatch"), 1);
|
|
1707
2340
|
var normalizeByPassDomains = (domains) => {
|
|
@@ -1785,12 +2418,17 @@ var ByPass = {
|
|
|
1785
2418
|
// src/launch.js
|
|
1786
2419
|
var logger7 = createInternalLogger("Launch");
|
|
1787
2420
|
var REQUEST_HOOK_FLAG = Symbol("playwright-toolkit-request-hook");
|
|
2421
|
+
var injectedContexts = /* @__PURE__ */ new WeakSet();
|
|
2422
|
+
var browserMajorVersionCache = /* @__PURE__ */ new Map();
|
|
2423
|
+
var DEFAULT_BROWSER_PROFILE_SCHEMA_VERSION = 1;
|
|
2424
|
+
var DEFAULT_LOCALE = "zh-CN";
|
|
1788
2425
|
var DEFAULT_CRAWLER_BASE_OPTIONS = Object.freeze({
|
|
1789
2426
|
maxConcurrency: 1,
|
|
1790
2427
|
maxRequestRetries: 0,
|
|
1791
2428
|
requestHandlerTimeoutSecs: 240,
|
|
1792
2429
|
navigationTimeoutSecs: 120
|
|
1793
2430
|
});
|
|
2431
|
+
var fingerprintInjector = new import_fingerprint_injector.FingerprintInjector();
|
|
1794
2432
|
var resolveProxyLaunchOptions = (proxyConfiguration = {}) => {
|
|
1795
2433
|
const config = proxyConfiguration && typeof proxyConfiguration === "object" && !Array.isArray(proxyConfiguration) ? proxyConfiguration : {};
|
|
1796
2434
|
const proxyUrl = String(config.proxy_url || "").trim();
|
|
@@ -1801,6 +2439,146 @@ var resolveProxyLaunchOptions = (proxyConfiguration = {}) => {
|
|
|
1801
2439
|
const byPassDomains = ByPass.normalizeByPassDomains(config.by_pass_domains);
|
|
1802
2440
|
return { byPassDomains, enableProxy, proxyUrl };
|
|
1803
2441
|
};
|
|
2442
|
+
var parseChromeMajorVersion = (rawValue = "") => {
|
|
2443
|
+
const match = String(rawValue || "").match(/Chrome\/(\d+)/i);
|
|
2444
|
+
return match ? Number(match[1] || 0) : 0;
|
|
2445
|
+
};
|
|
2446
|
+
var resolveLauncherExecutablePath = (launcher) => {
|
|
2447
|
+
if (!launcher || typeof launcher !== "object") return "";
|
|
2448
|
+
if (typeof launcher.executablePath === "function") {
|
|
2449
|
+
try {
|
|
2450
|
+
return String(launcher.executablePath() || "").trim();
|
|
2451
|
+
} catch {
|
|
2452
|
+
return "";
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
return "";
|
|
2456
|
+
};
|
|
2457
|
+
var detectBrowserMajorVersion = (launcher) => {
|
|
2458
|
+
const executablePath = resolveLauncherExecutablePath(launcher);
|
|
2459
|
+
if (!executablePath) return 0;
|
|
2460
|
+
if (browserMajorVersionCache.has(executablePath)) {
|
|
2461
|
+
return browserMajorVersionCache.get(executablePath);
|
|
2462
|
+
}
|
|
2463
|
+
let detectedVersion = 0;
|
|
2464
|
+
try {
|
|
2465
|
+
const rawVersion = (0, import_node_child_process.execFileSync)(executablePath, ["--version"], {
|
|
2466
|
+
encoding: "utf8",
|
|
2467
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2468
|
+
});
|
|
2469
|
+
detectedVersion = parseChromeMajorVersion(rawVersion);
|
|
2470
|
+
} catch (error) {
|
|
2471
|
+
logger7.warn(`\u8BFB\u53D6\u6D4F\u89C8\u5668\u7248\u672C\u5931\u8D25: ${error?.message || error}`);
|
|
2472
|
+
}
|
|
2473
|
+
browserMajorVersionCache.set(executablePath, detectedVersion);
|
|
2474
|
+
return detectedVersion;
|
|
2475
|
+
};
|
|
2476
|
+
var buildFingerprintGenerator = ({ locale, browserMajorVersion }) => {
|
|
2477
|
+
return new import_fingerprint_generator.FingerprintGenerator(
|
|
2478
|
+
AntiCheat.getFingerprintGeneratorOptions({
|
|
2479
|
+
locale,
|
|
2480
|
+
browserMajorVersion
|
|
2481
|
+
})
|
|
2482
|
+
);
|
|
2483
|
+
};
|
|
2484
|
+
var buildReplayableBrowserProfile = (runtimeState, launcher) => {
|
|
2485
|
+
if (!runtimeState || !RuntimeEnv.hasLoginState(runtimeState)) {
|
|
2486
|
+
return { runtimeState, browserProfileCore: null };
|
|
2487
|
+
}
|
|
2488
|
+
let nextState = RuntimeEnv.rememberState(runtimeState);
|
|
2489
|
+
let browserProfileCore = RuntimeEnv.getBrowserProfileCore(nextState);
|
|
2490
|
+
const timezoneId = String(browserProfileCore?.timezone_id || "").trim() || AntiCheat.getBaseConfig().timezoneId;
|
|
2491
|
+
const locale = DEFAULT_LOCALE;
|
|
2492
|
+
const currentBrowserMajorVersion = detectBrowserMajorVersion(launcher);
|
|
2493
|
+
const storedBrowserMajorVersion = Number(browserProfileCore?.browser_major_version || 0);
|
|
2494
|
+
const needsRebuild = !browserProfileCore?.fingerprint || Object.keys(browserProfileCore.fingerprint || {}).length === 0 || currentBrowserMajorVersion > 0 && storedBrowserMajorVersion > 0 && storedBrowserMajorVersion !== currentBrowserMajorVersion;
|
|
2495
|
+
if (needsRebuild) {
|
|
2496
|
+
const generator = buildFingerprintGenerator({
|
|
2497
|
+
locale,
|
|
2498
|
+
browserMajorVersion: currentBrowserMajorVersion
|
|
2499
|
+
});
|
|
2500
|
+
const fingerprint = generator.getFingerprint();
|
|
2501
|
+
const fingerprintBrowserMajorVersion = parseChromeMajorVersion(fingerprint?.fingerprint?.navigator?.userAgent || "");
|
|
2502
|
+
browserProfileCore = {
|
|
2503
|
+
fingerprint,
|
|
2504
|
+
timezone_id: timezoneId,
|
|
2505
|
+
locale,
|
|
2506
|
+
browser_major_version: currentBrowserMajorVersion > 0 ? currentBrowserMajorVersion : fingerprintBrowserMajorVersion,
|
|
2507
|
+
profile_key: String(nextState.envId || "").trim(),
|
|
2508
|
+
schema_version: DEFAULT_BROWSER_PROFILE_SCHEMA_VERSION
|
|
2509
|
+
};
|
|
2510
|
+
nextState = RuntimeEnv.setBrowserProfileCore(nextState, browserProfileCore);
|
|
2511
|
+
logger7.info(
|
|
2512
|
+
`\u5DF2\u751F\u6210\u6D4F\u89C8\u5668\u6307\u7EB9\u771F\u6E90 | env=${String(nextState.envId || "-")} | version=${browserProfileCore.browser_major_version || "-"} | timezone=${timezoneId}`
|
|
2513
|
+
);
|
|
2514
|
+
return { runtimeState: nextState, browserProfileCore };
|
|
2515
|
+
}
|
|
2516
|
+
const normalizedBrowserProfileCore = {
|
|
2517
|
+
...browserProfileCore,
|
|
2518
|
+
timezone_id: timezoneId,
|
|
2519
|
+
locale,
|
|
2520
|
+
profile_key: String(browserProfileCore.profile_key || nextState.envId || "").trim(),
|
|
2521
|
+
schema_version: Number(browserProfileCore.schema_version || 0) > 0 ? Number(browserProfileCore.schema_version || 0) : DEFAULT_BROWSER_PROFILE_SCHEMA_VERSION,
|
|
2522
|
+
browser_major_version: currentBrowserMajorVersion > 0 ? currentBrowserMajorVersion : Number(browserProfileCore.browser_major_version || 0)
|
|
2523
|
+
};
|
|
2524
|
+
const currentCoreRaw = JSON.stringify(browserProfileCore || {});
|
|
2525
|
+
const nextCoreRaw = JSON.stringify(normalizedBrowserProfileCore);
|
|
2526
|
+
if (currentCoreRaw !== nextCoreRaw) {
|
|
2527
|
+
nextState = RuntimeEnv.setBrowserProfileCore(nextState, normalizedBrowserProfileCore);
|
|
2528
|
+
}
|
|
2529
|
+
return {
|
|
2530
|
+
runtimeState: nextState,
|
|
2531
|
+
browserProfileCore: normalizedBrowserProfileCore
|
|
2532
|
+
};
|
|
2533
|
+
};
|
|
2534
|
+
var buildReplayBrowserPoolOptions = (browserProfileCore) => {
|
|
2535
|
+
const fingerprintWithHeaders = browserProfileCore?.fingerprint;
|
|
2536
|
+
const fingerprint = fingerprintWithHeaders?.fingerprint;
|
|
2537
|
+
if (!fingerprintWithHeaders || !fingerprint) {
|
|
2538
|
+
return null;
|
|
2539
|
+
}
|
|
2540
|
+
return {
|
|
2541
|
+
useFingerprints: false,
|
|
2542
|
+
prePageCreateHooks: [
|
|
2543
|
+
(_pageId, _browserController, pageOptions = {}) => {
|
|
2544
|
+
if (!pageOptions || typeof pageOptions !== "object") return;
|
|
2545
|
+
const screen = fingerprint.screen || {};
|
|
2546
|
+
const userAgent = String(fingerprint.navigator?.userAgent || "").trim();
|
|
2547
|
+
if (userAgent) {
|
|
2548
|
+
pageOptions.userAgent = userAgent;
|
|
2549
|
+
}
|
|
2550
|
+
if (Number(screen.width || 0) > 0 && Number(screen.height || 0) > 0) {
|
|
2551
|
+
pageOptions.viewport = {
|
|
2552
|
+
width: Number(screen.width),
|
|
2553
|
+
height: Number(screen.height)
|
|
2554
|
+
};
|
|
2555
|
+
pageOptions.screen = {
|
|
2556
|
+
width: Number(screen.width),
|
|
2557
|
+
height: Number(screen.height)
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
if (browserProfileCore.locale) {
|
|
2561
|
+
pageOptions.locale = browserProfileCore.locale;
|
|
2562
|
+
}
|
|
2563
|
+
if (browserProfileCore.timezone_id) {
|
|
2564
|
+
pageOptions.timezoneId = browserProfileCore.timezone_id;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
],
|
|
2568
|
+
postPageCreateHooks: [
|
|
2569
|
+
async (page) => {
|
|
2570
|
+
const context = page?.context?.();
|
|
2571
|
+
if (!context) return;
|
|
2572
|
+
if (!injectedContexts.has(context)) {
|
|
2573
|
+
await fingerprintInjector.attachFingerprintToPlaywright(context, fingerprintWithHeaders);
|
|
2574
|
+
injectedContexts.add(context);
|
|
2575
|
+
}
|
|
2576
|
+
await page.emulateMedia({ colorScheme: "dark" }).catch(() => {
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
]
|
|
2580
|
+
};
|
|
2581
|
+
};
|
|
1804
2582
|
var Launch = {
|
|
1805
2583
|
getPlaywrightCrawlerOptions(options = {}) {
|
|
1806
2584
|
const normalizedOptions = Array.isArray(options) ? { customArgs: options } : options || {};
|
|
@@ -1813,7 +2591,8 @@ var Launch = {
|
|
|
1813
2591
|
isRunningOnApify = false,
|
|
1814
2592
|
launcher = null,
|
|
1815
2593
|
preNavigationHooks = [],
|
|
1816
|
-
postNavigationHooks = []
|
|
2594
|
+
postNavigationHooks = [],
|
|
2595
|
+
runtimeState = null
|
|
1817
2596
|
} = normalizedOptions;
|
|
1818
2597
|
const { byPassDomains, enableProxy, proxyUrl } = resolveProxyLaunchOptions(proxyConfiguration);
|
|
1819
2598
|
const byPassRules = ByPass.buildByPassDomainRules(byPassDomains);
|
|
@@ -1822,9 +2601,12 @@ var Launch = {
|
|
|
1822
2601
|
if (launchProxy && byPassDomains.length > 0) {
|
|
1823
2602
|
launchProxy.bypass = byPassDomains.join(",");
|
|
1824
2603
|
}
|
|
2604
|
+
const replayContext = buildReplayableBrowserProfile(runtimeState, launcher);
|
|
2605
|
+
const replayBrowserPoolOptions = buildReplayBrowserPoolOptions(replayContext.browserProfileCore);
|
|
2606
|
+
const launchLocale = String(replayContext.browserProfileCore?.locale || DEFAULT_LOCALE).trim() || DEFAULT_LOCALE;
|
|
1825
2607
|
const launchOptions = {
|
|
1826
2608
|
args: [
|
|
1827
|
-
...AntiCheat.getLaunchArgs(),
|
|
2609
|
+
...AntiCheat.getLaunchArgs({ locale: launchLocale }),
|
|
1828
2610
|
...customArgs
|
|
1829
2611
|
],
|
|
1830
2612
|
ignoreDefaultArgs: ["--enable-automation"]
|
|
@@ -1836,7 +2618,7 @@ var Launch = {
|
|
|
1836
2618
|
if (enableByPassLogger && launchProxy) {
|
|
1837
2619
|
let upstreamLabel = "";
|
|
1838
2620
|
try {
|
|
1839
|
-
const parsedProxyUrl = new URL(proxyUrl);
|
|
2621
|
+
const parsedProxyUrl = new URL(proxyUrl.includes("://") ? proxyUrl : `http://${proxyUrl}`);
|
|
1840
2622
|
upstreamLabel = `${parsedProxyUrl.protocol}//${parsedProxyUrl.host}`;
|
|
1841
2623
|
} catch {
|
|
1842
2624
|
}
|
|
@@ -1845,14 +2627,10 @@ var Launch = {
|
|
|
1845
2627
|
);
|
|
1846
2628
|
logger7.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
1847
2629
|
} else if (enableByPassLogger && enableProxy && !launchProxy) {
|
|
1848
|
-
logger7.info(
|
|
1849
|
-
`[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=true \u4F46 proxy_url \u4E3A\u7A7A`
|
|
1850
|
-
);
|
|
2630
|
+
logger7.info("[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=true \u4F46 proxy_url \u4E3A\u7A7A");
|
|
1851
2631
|
logger7.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
1852
2632
|
} else if (enableByPassLogger && !enableProxy && proxyUrl) {
|
|
1853
|
-
logger7.info(
|
|
1854
|
-
`[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=false \u4E14 proxy_url \u5DF2\u914D\u7F6E`
|
|
1855
|
-
);
|
|
2633
|
+
logger7.info("[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=false \u4E14 proxy_url \u5DF2\u914D\u7F6E");
|
|
1856
2634
|
logger7.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
1857
2635
|
} else if (enableByPassLogger) {
|
|
1858
2636
|
logger7.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
@@ -1892,7 +2670,8 @@ var Launch = {
|
|
|
1892
2670
|
const crawlerBaseOptions = {
|
|
1893
2671
|
...DEFAULT_CRAWLER_BASE_OPTIONS,
|
|
1894
2672
|
headless: !runInHeadfulMode || isRunningOnApify,
|
|
1895
|
-
|
|
2673
|
+
// 有 core.fingerprint 时走固定回放;否则退回 Crawlee 默认指纹模式。
|
|
2674
|
+
browserPoolOptions: replayBrowserPoolOptions || {
|
|
1896
2675
|
useFingerprints: true,
|
|
1897
2676
|
fingerprintOptions: {
|
|
1898
2677
|
fingerprintGeneratorOptions: AntiCheat.getFingerprintGeneratorOptions()
|
|
@@ -2095,241 +2874,9 @@ var Captcha = {
|
|
|
2095
2874
|
useCaptchaMonitor
|
|
2096
2875
|
};
|
|
2097
2876
|
|
|
2098
|
-
// src/sse.js
|
|
2099
|
-
var import_https = __toESM(require("https"), 1);
|
|
2100
|
-
var import_url2 = require("url");
|
|
2101
|
-
var logger10 = createInternalLogger("Sse");
|
|
2102
|
-
var Sse = {
|
|
2103
|
-
/**
|
|
2104
|
-
* 解析 SSE 流文本
|
|
2105
|
-
* 支持 `data: {...}` 和 `data:{...}` 两种格式
|
|
2106
|
-
* @param {string} sseStreamText
|
|
2107
|
-
* @returns {Array<Object>} events
|
|
2108
|
-
*/
|
|
2109
|
-
parseSseStream(sseStreamText) {
|
|
2110
|
-
const events = [];
|
|
2111
|
-
const lines = sseStreamText.split("\n");
|
|
2112
|
-
for (const line of lines) {
|
|
2113
|
-
if (line.startsWith("data:")) {
|
|
2114
|
-
try {
|
|
2115
|
-
const jsonContent = line.substring(5).trim();
|
|
2116
|
-
if (jsonContent) {
|
|
2117
|
-
events.push(JSON.parse(jsonContent));
|
|
2118
|
-
}
|
|
2119
|
-
} catch (e) {
|
|
2120
|
-
logger10.debug("parseSseStream", `JSON \u89E3\u6790\u5931\u8D25: ${e.message}, line: ${line.substring(0, 100)}...`);
|
|
2121
|
-
}
|
|
2122
|
-
}
|
|
2123
|
-
}
|
|
2124
|
-
logger10.success("parseSseStream", `\u89E3\u6790\u5B8C\u6210, events \u6570\u91CF: ${events.length}`);
|
|
2125
|
-
return events;
|
|
2126
|
-
},
|
|
2127
|
-
/**
|
|
2128
|
-
* 拦截网络请求并使用 Node.js 原生 https 模块转发,以解决流式数据捕获问题。
|
|
2129
|
-
* @param {import('playwright').Page} page
|
|
2130
|
-
* @param {string|RegExp} urlPattern - 拦截的 URL 模式
|
|
2131
|
-
* @param {object} options
|
|
2132
|
-
* @param {function(string, function, string): void} [options.onData] - (textChunk, resolve, accumulatedText) => void
|
|
2133
|
-
* @param {function(string, function): void} [options.onEnd] - (fullText, resolve) => void
|
|
2134
|
-
* @param {function(Error, function): void} [options.onTimeout] - (error, reject) => void
|
|
2135
|
-
* @param {number} [options.initialTimeout=90000] - 初始数据接收超时 (ms),默认 90s
|
|
2136
|
-
* @param {number} [options.overallTimeout=180000] - 整体请求超时时间 (ms),默认 180s
|
|
2137
|
-
* @param {boolean} [options.autoUnroute=true] - resolve/reject 后是否自动取消拦截
|
|
2138
|
-
* @param {boolean} [options.firstMatchOnly=true] - 只拦截第一个命中的请求,后续请求全部放行
|
|
2139
|
-
* @returns {Promise<any>} - 返回 Promise,当流满足条件时 resolve
|
|
2140
|
-
*/
|
|
2141
|
-
async intercept(page, urlPattern, options = {}) {
|
|
2142
|
-
const {
|
|
2143
|
-
onData,
|
|
2144
|
-
onEnd,
|
|
2145
|
-
onTimeout,
|
|
2146
|
-
initialTimeout = 9e4,
|
|
2147
|
-
overallTimeout = 18e4,
|
|
2148
|
-
autoUnroute = true,
|
|
2149
|
-
firstMatchOnly = true
|
|
2150
|
-
} = options;
|
|
2151
|
-
let initialTimer = null;
|
|
2152
|
-
let overallTimer = null;
|
|
2153
|
-
let hasReceivedInitialData = false;
|
|
2154
|
-
const clearAllTimers = () => {
|
|
2155
|
-
if (initialTimer) clearTimeout(initialTimer);
|
|
2156
|
-
if (overallTimer) clearTimeout(overallTimer);
|
|
2157
|
-
initialTimer = null;
|
|
2158
|
-
overallTimer = null;
|
|
2159
|
-
};
|
|
2160
|
-
let safeResolve = () => {
|
|
2161
|
-
};
|
|
2162
|
-
let safeReject = () => {
|
|
2163
|
-
};
|
|
2164
|
-
let safeUnroute = () => {
|
|
2165
|
-
};
|
|
2166
|
-
const workPromise = new Promise((resolve, reject) => {
|
|
2167
|
-
let finished = false;
|
|
2168
|
-
let unrouteRequested = false;
|
|
2169
|
-
let hasMatchedOnce = false;
|
|
2170
|
-
safeUnroute = () => {
|
|
2171
|
-
if (!autoUnroute) return;
|
|
2172
|
-
if (unrouteRequested) return;
|
|
2173
|
-
unrouteRequested = true;
|
|
2174
|
-
logger10.info("[MITM] autoUnroute: \u53D6\u6D88\u540E\u7EED\u62E6\u622A");
|
|
2175
|
-
page.unroute(urlPattern, routeHandler).catch(() => {
|
|
2176
|
-
});
|
|
2177
|
-
};
|
|
2178
|
-
safeResolve = (value) => {
|
|
2179
|
-
if (finished) return;
|
|
2180
|
-
finished = true;
|
|
2181
|
-
clearAllTimers();
|
|
2182
|
-
safeUnroute();
|
|
2183
|
-
resolve(value);
|
|
2184
|
-
};
|
|
2185
|
-
safeReject = (error) => {
|
|
2186
|
-
if (finished) return;
|
|
2187
|
-
finished = true;
|
|
2188
|
-
clearAllTimers();
|
|
2189
|
-
safeUnroute();
|
|
2190
|
-
reject(error);
|
|
2191
|
-
};
|
|
2192
|
-
const routeHandler = async (route) => {
|
|
2193
|
-
if (firstMatchOnly && hasMatchedOnce) {
|
|
2194
|
-
logger10.info(`[MITM] firstMatchOnly: \u653E\u884C\u540E\u7EED\u8BF7\u6C42: ${route.request().url()}`);
|
|
2195
|
-
route.continue().catch(() => {
|
|
2196
|
-
});
|
|
2197
|
-
return;
|
|
2198
|
-
}
|
|
2199
|
-
if (firstMatchOnly && !hasMatchedOnce) {
|
|
2200
|
-
hasMatchedOnce = true;
|
|
2201
|
-
logger10.info("[MITM] firstMatchOnly: \u547D\u4E2D\u9996\u4E2A\u8BF7\u6C42\uFF0C\u53D6\u6D88\u540E\u7EED\u62E6\u622A");
|
|
2202
|
-
page.unroute(urlPattern, routeHandler).catch(() => {
|
|
2203
|
-
});
|
|
2204
|
-
}
|
|
2205
|
-
const request = route.request();
|
|
2206
|
-
logger10.info(`[MITM] \u5DF2\u62E6\u622A\u8BF7\u6C42: ${request.url()}`);
|
|
2207
|
-
try {
|
|
2208
|
-
const headers = await request.allHeaders();
|
|
2209
|
-
const postData = request.postData();
|
|
2210
|
-
const urlObj = new import_url2.URL(request.url());
|
|
2211
|
-
delete headers["accept-encoding"];
|
|
2212
|
-
delete headers["content-length"];
|
|
2213
|
-
const reqOptions = {
|
|
2214
|
-
hostname: urlObj.hostname,
|
|
2215
|
-
port: 443,
|
|
2216
|
-
path: urlObj.pathname + urlObj.search,
|
|
2217
|
-
method: request.method(),
|
|
2218
|
-
headers,
|
|
2219
|
-
timeout: overallTimeout
|
|
2220
|
-
};
|
|
2221
|
-
const req = import_https.default.request(reqOptions, (res) => {
|
|
2222
|
-
const chunks = [];
|
|
2223
|
-
let accumulatedText = "";
|
|
2224
|
-
res.on("data", (chunk) => {
|
|
2225
|
-
if (!hasReceivedInitialData) {
|
|
2226
|
-
hasReceivedInitialData = true;
|
|
2227
|
-
if (initialTimer) {
|
|
2228
|
-
clearTimeout(initialTimer);
|
|
2229
|
-
initialTimer = null;
|
|
2230
|
-
}
|
|
2231
|
-
logger10.debug("[Intercept] \u5DF2\u63A5\u6536\u521D\u59CB\u6570\u636E");
|
|
2232
|
-
}
|
|
2233
|
-
chunks.push(chunk);
|
|
2234
|
-
const textChunk = chunk.toString("utf-8");
|
|
2235
|
-
accumulatedText += textChunk;
|
|
2236
|
-
if (onData) {
|
|
2237
|
-
try {
|
|
2238
|
-
onData(textChunk, safeResolve, accumulatedText);
|
|
2239
|
-
} catch (e) {
|
|
2240
|
-
logger10.fail(`onData \u9519\u8BEF`, e);
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
});
|
|
2244
|
-
res.on("end", () => {
|
|
2245
|
-
logger10.info("[MITM] \u4E0A\u6E38\u54CD\u5E94\u7ED3\u675F");
|
|
2246
|
-
clearAllTimers();
|
|
2247
|
-
if (onEnd) {
|
|
2248
|
-
try {
|
|
2249
|
-
onEnd(accumulatedText, safeResolve);
|
|
2250
|
-
} catch (e) {
|
|
2251
|
-
logger10.fail(`onEnd \u9519\u8BEF`, e);
|
|
2252
|
-
}
|
|
2253
|
-
} else if (!onData) {
|
|
2254
|
-
safeResolve(accumulatedText);
|
|
2255
|
-
}
|
|
2256
|
-
route.fulfill({
|
|
2257
|
-
status: res.statusCode,
|
|
2258
|
-
headers: res.headers,
|
|
2259
|
-
body: Buffer.concat(chunks)
|
|
2260
|
-
}).catch(() => {
|
|
2261
|
-
});
|
|
2262
|
-
});
|
|
2263
|
-
});
|
|
2264
|
-
req.on("error", (e) => {
|
|
2265
|
-
clearAllTimers();
|
|
2266
|
-
route.abort().catch(() => {
|
|
2267
|
-
});
|
|
2268
|
-
safeReject(e);
|
|
2269
|
-
});
|
|
2270
|
-
if (postData) req.write(postData);
|
|
2271
|
-
req.end();
|
|
2272
|
-
} catch (e) {
|
|
2273
|
-
clearAllTimers();
|
|
2274
|
-
route.continue().catch(() => {
|
|
2275
|
-
});
|
|
2276
|
-
safeReject(e);
|
|
2277
|
-
}
|
|
2278
|
-
};
|
|
2279
|
-
page.route(urlPattern, routeHandler).catch(safeReject);
|
|
2280
|
-
});
|
|
2281
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
2282
|
-
initialTimer = setTimeout(() => {
|
|
2283
|
-
if (!hasReceivedInitialData) {
|
|
2284
|
-
const error = new CrawlerError({
|
|
2285
|
-
message: `\u521D\u59CB\u6570\u636E\u63A5\u6536\u8D85\u65F6 (${initialTimeout}ms)`,
|
|
2286
|
-
code: Code.InitialTimeout,
|
|
2287
|
-
context: { timeout: initialTimeout }
|
|
2288
|
-
});
|
|
2289
|
-
clearAllTimers();
|
|
2290
|
-
if (onTimeout) {
|
|
2291
|
-
try {
|
|
2292
|
-
onTimeout(error, (err) => safeReject(err));
|
|
2293
|
-
} catch (e) {
|
|
2294
|
-
safeReject(e);
|
|
2295
|
-
}
|
|
2296
|
-
} else {
|
|
2297
|
-
safeReject(error);
|
|
2298
|
-
}
|
|
2299
|
-
}
|
|
2300
|
-
}, initialTimeout);
|
|
2301
|
-
overallTimer = setTimeout(() => {
|
|
2302
|
-
const error = new CrawlerError({
|
|
2303
|
-
message: `\u6574\u4F53\u8BF7\u6C42\u8D85\u65F6 (${overallTimeout}ms)`,
|
|
2304
|
-
code: Code.OverallTimeout,
|
|
2305
|
-
context: { timeout: overallTimeout }
|
|
2306
|
-
});
|
|
2307
|
-
clearAllTimers();
|
|
2308
|
-
if (onTimeout) {
|
|
2309
|
-
try {
|
|
2310
|
-
onTimeout(error, (err) => safeReject(err));
|
|
2311
|
-
} catch (e) {
|
|
2312
|
-
safeReject(e);
|
|
2313
|
-
}
|
|
2314
|
-
} else {
|
|
2315
|
-
safeReject(error);
|
|
2316
|
-
}
|
|
2317
|
-
}, overallTimeout);
|
|
2318
|
-
});
|
|
2319
|
-
workPromise.catch(() => {
|
|
2320
|
-
});
|
|
2321
|
-
timeoutPromise.catch(() => {
|
|
2322
|
-
});
|
|
2323
|
-
const racePromise = Promise.race([workPromise, timeoutPromise]);
|
|
2324
|
-
racePromise.catch(() => {
|
|
2325
|
-
});
|
|
2326
|
-
return racePromise;
|
|
2327
|
-
}
|
|
2328
|
-
};
|
|
2329
|
-
|
|
2330
2877
|
// src/mutation.js
|
|
2331
2878
|
var import_uuid2 = require("uuid");
|
|
2332
|
-
var
|
|
2879
|
+
var logger10 = createInternalLogger("Mutation");
|
|
2333
2880
|
var MUTATION_MONITOR_MODE = Object.freeze({
|
|
2334
2881
|
Added: "added",
|
|
2335
2882
|
Changed: "changed",
|
|
@@ -2362,14 +2909,14 @@ var Mutation = {
|
|
|
2362
2909
|
const stableTime = options.stableTime ?? 5 * 1e3;
|
|
2363
2910
|
const timeout = options.timeout ?? 120 * 1e3;
|
|
2364
2911
|
const onMutation = options.onMutation;
|
|
2365
|
-
|
|
2912
|
+
logger10.start("waitForStable", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, \u7A33\u5B9A\u65F6\u95F4=${stableTime}ms`);
|
|
2366
2913
|
if (initialTimeout > 0) {
|
|
2367
2914
|
const selectorQuery = selectorList.join(",");
|
|
2368
2915
|
try {
|
|
2369
2916
|
await page.waitForSelector(selectorQuery, { timeout: initialTimeout });
|
|
2370
|
-
|
|
2917
|
+
logger10.info(`waitForStable \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
|
|
2371
2918
|
} catch (e) {
|
|
2372
|
-
|
|
2919
|
+
logger10.warning(`waitForStable \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
|
|
2373
2920
|
throw e;
|
|
2374
2921
|
}
|
|
2375
2922
|
}
|
|
@@ -2385,7 +2932,7 @@ var Mutation = {
|
|
|
2385
2932
|
return "__CONTINUE__";
|
|
2386
2933
|
}
|
|
2387
2934
|
});
|
|
2388
|
-
|
|
2935
|
+
logger10.info("waitForStable \u5DF2\u542F\u7528 onMutation \u56DE\u8C03");
|
|
2389
2936
|
} catch (e) {
|
|
2390
2937
|
}
|
|
2391
2938
|
}
|
|
@@ -2500,9 +3047,9 @@ var Mutation = {
|
|
|
2500
3047
|
{ selectorList, stableTime, timeout, callbackName, hasCallback: !!onMutation }
|
|
2501
3048
|
);
|
|
2502
3049
|
if (result.mutationCount === 0 && result.stableTime === 0) {
|
|
2503
|
-
|
|
3050
|
+
logger10.warning("waitForStable \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
|
|
2504
3051
|
}
|
|
2505
|
-
|
|
3052
|
+
logger10.success("waitForStable", `DOM \u7A33\u5B9A, \u603B\u5171 ${result.mutationCount} \u6B21\u53D8\u5316${result.wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
|
|
2506
3053
|
return result;
|
|
2507
3054
|
},
|
|
2508
3055
|
/**
|
|
@@ -2620,22 +3167,22 @@ var Mutation = {
|
|
|
2620
3167
|
return "__CONTINUE__";
|
|
2621
3168
|
}
|
|
2622
3169
|
};
|
|
2623
|
-
|
|
3170
|
+
logger10.start(
|
|
2624
3171
|
"waitForStableAcrossRoots",
|
|
2625
3172
|
`\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668(\u8DE8 root), \u7A33\u5B9A\u65F6\u95F4=${waitForStableTime}ms`
|
|
2626
3173
|
);
|
|
2627
3174
|
if (initialTimeout > 0) {
|
|
2628
3175
|
try {
|
|
2629
3176
|
await page.waitForSelector(selectorQuery, { timeout: initialTimeout });
|
|
2630
|
-
|
|
3177
|
+
logger10.info(`waitForStableAcrossRoots \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
|
|
2631
3178
|
} catch (e) {
|
|
2632
|
-
|
|
3179
|
+
logger10.warning(`waitForStableAcrossRoots \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
|
|
2633
3180
|
throw e;
|
|
2634
3181
|
}
|
|
2635
3182
|
}
|
|
2636
3183
|
let state = await buildState();
|
|
2637
3184
|
if (!state?.hasMatched) {
|
|
2638
|
-
|
|
3185
|
+
logger10.warning("waitForStableAcrossRoots \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
|
|
2639
3186
|
return { mutationCount: 0, stableTime: 0, wasPaused: false };
|
|
2640
3187
|
}
|
|
2641
3188
|
let mutationCount = 0;
|
|
@@ -2672,7 +3219,7 @@ var Mutation = {
|
|
|
2672
3219
|
if (lastState.snapshotKey !== lastSnapshotKey) {
|
|
2673
3220
|
lastSnapshotKey = lastState.snapshotKey;
|
|
2674
3221
|
mutationCount += 1;
|
|
2675
|
-
|
|
3222
|
+
logger10.info(
|
|
2676
3223
|
`waitForStableAcrossRoots \u53D8\u5316#${mutationCount}, len=${lastState.snapshotLength}, path=${lastState.primaryPath || "unknown"}, preview="${truncate(lastState.text, 120)}"`
|
|
2677
3224
|
);
|
|
2678
3225
|
const signal = await invokeMutationCallback({
|
|
@@ -2685,7 +3232,7 @@ var Mutation = {
|
|
|
2685
3232
|
continue;
|
|
2686
3233
|
}
|
|
2687
3234
|
if (!isPaused && stableSince > 0 && Date.now() - stableSince >= waitForStableTime) {
|
|
2688
|
-
|
|
3235
|
+
logger10.success("waitForStableAcrossRoots", `DOM \u7A33\u5B9A, \u603B\u5171 ${mutationCount} \u6B21\u53D8\u5316${wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
|
|
2689
3236
|
return {
|
|
2690
3237
|
mutationCount,
|
|
2691
3238
|
stableTime: waitForStableTime,
|
|
@@ -2712,7 +3259,7 @@ var Mutation = {
|
|
|
2712
3259
|
const onMutation = options.onMutation;
|
|
2713
3260
|
const rawMode = String(options.mode || MUTATION_MONITOR_MODE.Added).toLowerCase();
|
|
2714
3261
|
const mode = [MUTATION_MONITOR_MODE.Added, MUTATION_MONITOR_MODE.Changed, MUTATION_MONITOR_MODE.All].includes(rawMode) ? rawMode : MUTATION_MONITOR_MODE.Added;
|
|
2715
|
-
|
|
3262
|
+
logger10.start("useMonitor", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, mode=${mode}`);
|
|
2716
3263
|
const monitorKey = generateKey("pk_mon");
|
|
2717
3264
|
const callbackName = generateKey("pk_mon_cb");
|
|
2718
3265
|
const cleanerName = generateKey("pk_mon_clean");
|
|
@@ -2855,7 +3402,7 @@ var Mutation = {
|
|
|
2855
3402
|
return total;
|
|
2856
3403
|
};
|
|
2857
3404
|
}, { selectorList, monitorKey, callbackName, cleanerName, hasCallback: !!onMutation, mode });
|
|
2858
|
-
|
|
3405
|
+
logger10.success("useMonitor", "\u76D1\u63A7\u5668\u5DF2\u542F\u52A8");
|
|
2859
3406
|
return {
|
|
2860
3407
|
stop: async () => {
|
|
2861
3408
|
let totalMutations = 0;
|
|
@@ -2868,7 +3415,7 @@ var Mutation = {
|
|
|
2868
3415
|
}, cleanerName);
|
|
2869
3416
|
} catch (e) {
|
|
2870
3417
|
}
|
|
2871
|
-
|
|
3418
|
+
logger10.success("useMonitor.stop", `\u76D1\u63A7\u5DF2\u505C\u6B62, \u5171 ${totalMutations} \u6B21\u53D8\u5316`);
|
|
2872
3419
|
return { totalMutations };
|
|
2873
3420
|
}
|
|
2874
3421
|
};
|
|
@@ -2879,28 +3426,28 @@ var Mutation = {
|
|
|
2879
3426
|
var Display = {
|
|
2880
3427
|
parseTokenDisplayName(value) {
|
|
2881
3428
|
if (!value) {
|
|
2882
|
-
return { owner: "",
|
|
3429
|
+
return { owner: "", envType: "", note: "" };
|
|
2883
3430
|
}
|
|
2884
3431
|
const parts = String(value).split(":");
|
|
2885
3432
|
const owner = (parts[0] || "").trim();
|
|
2886
|
-
const
|
|
3433
|
+
const envType = (parts[1] || "").trim();
|
|
2887
3434
|
const note = parts.length > 2 ? parts.slice(2).join(":").trim() : "";
|
|
2888
|
-
return { owner,
|
|
3435
|
+
return { owner, envType, note };
|
|
2889
3436
|
},
|
|
2890
|
-
|
|
3437
|
+
parseEnvDisplayName(value) {
|
|
2891
3438
|
if (!value) {
|
|
2892
|
-
return {
|
|
3439
|
+
return { env: "", owner: "", envType: "", note: "" };
|
|
2893
3440
|
}
|
|
2894
3441
|
const parts = String(value).split(":");
|
|
2895
|
-
const
|
|
3442
|
+
const env = (parts[0] || "").trim();
|
|
2896
3443
|
const owner = (parts[1] || "").trim();
|
|
2897
|
-
const
|
|
3444
|
+
const envType = (parts[2] || "").trim();
|
|
2898
3445
|
const note = parts.length > 3 ? parts.slice(3).join(":").trim() : "";
|
|
2899
|
-
return {
|
|
3446
|
+
return { env, owner, envType, note };
|
|
2900
3447
|
},
|
|
2901
|
-
buildTokenDisplayName({ owner,
|
|
3448
|
+
buildTokenDisplayName({ owner, envType, note } = {}) {
|
|
2902
3449
|
const trimmedOwner = owner?.trim() || "";
|
|
2903
|
-
const trimmedType =
|
|
3450
|
+
const trimmedType = envType?.trim() || "";
|
|
2904
3451
|
const trimmedNote = note?.trim() || "";
|
|
2905
3452
|
const parts = [trimmedOwner, trimmedType];
|
|
2906
3453
|
if (trimmedNote) {
|
|
@@ -2923,12 +3470,12 @@ var Display = {
|
|
|
2923
3470
|
const secondary = hasName && short ? short : "";
|
|
2924
3471
|
return { primary, secondary, fullId: cleanId, shortId: short };
|
|
2925
3472
|
},
|
|
2926
|
-
|
|
2927
|
-
const parts = this.
|
|
2928
|
-
const cleanId = String(
|
|
3473
|
+
resolveEnvIdentity(displayName, envId) {
|
|
3474
|
+
const parts = this.parseEnvDisplayName(displayName);
|
|
3475
|
+
const cleanId = String(envId || "").trim();
|
|
2929
3476
|
const short = this.shortId(cleanId, 8);
|
|
2930
|
-
const hasName = parts.
|
|
2931
|
-
const primary = hasName ? parts.
|
|
3477
|
+
const hasName = parts.env !== "" && parts.env !== cleanId;
|
|
3478
|
+
const primary = hasName ? parts.env : short || "-";
|
|
2932
3479
|
const secondary = hasName && short ? short : "";
|
|
2933
3480
|
return { primary, secondary, parts, fullId: cleanId, shortId: short };
|
|
2934
3481
|
}
|
|
@@ -3737,7 +4284,7 @@ var createTemplateLogger = (baseLogger = createBaseLogger()) => {
|
|
|
3737
4284
|
};
|
|
3738
4285
|
var getDefaultBaseLogger = () => createBaseLogger("");
|
|
3739
4286
|
var Logger = {
|
|
3740
|
-
setLogger: (
|
|
4287
|
+
setLogger: (logger12) => setDefaultLogger(logger12),
|
|
3741
4288
|
info: (message) => getDefaultBaseLogger().info(message),
|
|
3742
4289
|
success: (message) => getDefaultBaseLogger().success(message),
|
|
3743
4290
|
warning: (message) => getDefaultBaseLogger().warning(message),
|
|
@@ -3745,15 +4292,15 @@ var Logger = {
|
|
|
3745
4292
|
error: (message) => getDefaultBaseLogger().error(message),
|
|
3746
4293
|
debug: (message) => getDefaultBaseLogger().debug(message),
|
|
3747
4294
|
start: (message) => getDefaultBaseLogger().start(message),
|
|
3748
|
-
useTemplate: (
|
|
3749
|
-
if (
|
|
4295
|
+
useTemplate: (logger12) => {
|
|
4296
|
+
if (logger12) return createTemplateLogger(createBaseLogger("", logger12));
|
|
3750
4297
|
return createTemplateLogger();
|
|
3751
4298
|
}
|
|
3752
4299
|
};
|
|
3753
4300
|
|
|
3754
4301
|
// src/share.js
|
|
3755
4302
|
var import_delay2 = __toESM(require("delay"), 1);
|
|
3756
|
-
var
|
|
4303
|
+
var logger11 = createInternalLogger("Share");
|
|
3757
4304
|
var DEFAULT_TIMEOUT_MS2 = 50 * 1e3;
|
|
3758
4305
|
var DEFAULT_PAYLOAD_SNAPSHOT_MAX_LEN = 500;
|
|
3759
4306
|
var DEFAULT_POLL_INTERVAL_MS = 120;
|
|
@@ -3873,7 +4420,7 @@ var createDomShareMonitor = async (page, options = {}) => {
|
|
|
3873
4420
|
const onMatch = typeof options.onMatch === "function" ? options.onMatch : null;
|
|
3874
4421
|
const onTelemetry = typeof options.onTelemetry === "function" ? options.onTelemetry : null;
|
|
3875
4422
|
let matched = false;
|
|
3876
|
-
|
|
4423
|
+
logger11.info(`DOM \u76D1\u542C\u51C6\u5907\u6302\u8F7D: selectors=${toJsonInline(selectors, 120)}, mode=${mode}`);
|
|
3877
4424
|
const monitor = await Mutation.useMonitor(page, selectors, {
|
|
3878
4425
|
mode,
|
|
3879
4426
|
onMutation: (context = {}) => {
|
|
@@ -3891,12 +4438,12 @@ ${text}`;
|
|
|
3891
4438
|
});
|
|
3892
4439
|
}
|
|
3893
4440
|
if (mutationCount <= 5 || mutationCount % 50 === 0) {
|
|
3894
|
-
|
|
4441
|
+
logger11.info(`DOM \u53D8\u5316\u5DF2\u6355\u83B7: mutationCount=${mutationCount}, mutationNodes=${mutationNodes.length}`);
|
|
3895
4442
|
}
|
|
3896
4443
|
const [candidate] = Utils.parseLinks(rawDom, { prefix }) || [];
|
|
3897
4444
|
if (!candidate) return;
|
|
3898
4445
|
matched = true;
|
|
3899
|
-
|
|
4446
|
+
logger11.success("captureLink.domHit", `DOM \u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: mutationCount=${mutationCount}, link=${candidate}`);
|
|
3900
4447
|
if (onMatch) {
|
|
3901
4448
|
onMatch({
|
|
3902
4449
|
link: candidate,
|
|
@@ -3912,7 +4459,7 @@ ${text}`;
|
|
|
3912
4459
|
return {
|
|
3913
4460
|
stop: async () => {
|
|
3914
4461
|
const result = await monitor.stop();
|
|
3915
|
-
|
|
4462
|
+
logger11.info(`DOM \u76D1\u542C\u5DF2\u505C\u6B62: totalMutations=${result?.totalMutations || 0}`);
|
|
3916
4463
|
return result;
|
|
3917
4464
|
}
|
|
3918
4465
|
};
|
|
@@ -3952,8 +4499,8 @@ var Share = {
|
|
|
3952
4499
|
if (share.mode === "response" && apiMatchers.length === 0) {
|
|
3953
4500
|
throw new Error("Share.captureLink requires share.xurl[0] api matcher when mode=response");
|
|
3954
4501
|
}
|
|
3955
|
-
|
|
3956
|
-
|
|
4502
|
+
logger11.start("captureLink", `mode=${share.mode}, timeoutMs=${timeoutMs}, prefix=${share.prefix}`);
|
|
4503
|
+
logger11.info(`captureLink \u914D\u7F6E: xurl=${toJsonInline(share.xurl)}, domMode=${domMode}, domSelectors=${toJsonInline(domSelectors, 120)}`);
|
|
3957
4504
|
const stats = {
|
|
3958
4505
|
actionTimedOut: false,
|
|
3959
4506
|
domMutationCount: 0,
|
|
@@ -3978,7 +4525,7 @@ var Share = {
|
|
|
3978
4525
|
link: validated,
|
|
3979
4526
|
payloadText: String(payloadText || "")
|
|
3980
4527
|
};
|
|
3981
|
-
|
|
4528
|
+
logger11.info(`\u5019\u9009\u94FE\u63A5\u5DF2\u786E\u8BA4: source=${source}, link=${validated}`);
|
|
3982
4529
|
return true;
|
|
3983
4530
|
};
|
|
3984
4531
|
const resolveResponseCandidate = (responseText) => {
|
|
@@ -4013,7 +4560,7 @@ var Share = {
|
|
|
4013
4560
|
try {
|
|
4014
4561
|
await monitor.stop();
|
|
4015
4562
|
} catch (error) {
|
|
4016
|
-
|
|
4563
|
+
logger11.warning(`\u505C\u6B62 DOM \u76D1\u542C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
|
|
4017
4564
|
}
|
|
4018
4565
|
};
|
|
4019
4566
|
const onResponse = async (response) => {
|
|
@@ -4026,29 +4573,29 @@ var Share = {
|
|
|
4026
4573
|
stats.responseSampleUrls.push(url);
|
|
4027
4574
|
}
|
|
4028
4575
|
if (stats.responseObserved <= 5) {
|
|
4029
|
-
|
|
4576
|
+
logger11.info(`\u63A5\u53E3\u54CD\u5E94\u91C7\u6837(${stats.responseObserved}): ${url}`);
|
|
4030
4577
|
}
|
|
4031
4578
|
if (!apiMatchers.some((matcher) => url.includes(matcher))) return;
|
|
4032
4579
|
stats.responseMatched += 1;
|
|
4033
4580
|
stats.lastMatchedUrl = url;
|
|
4034
|
-
|
|
4581
|
+
logger11.info(`\u63A5\u53E3\u547D\u4E2D\u5339\u914D(${stats.responseMatched}): ${url}`);
|
|
4035
4582
|
const text = await response.text();
|
|
4036
4583
|
const hit = resolveResponseCandidate(text);
|
|
4037
4584
|
if (!hit?.link) {
|
|
4038
4585
|
if (stats.responseMatched <= 3) {
|
|
4039
|
-
|
|
4586
|
+
logger11.info(`\u63A5\u53E3\u89E3\u6790\u5B8C\u6210\u4F46\u672A\u63D0\u53D6\u5230\u5206\u4EAB\u94FE\u63A5: payloadSize=${text.length}`);
|
|
4040
4587
|
}
|
|
4041
4588
|
return;
|
|
4042
4589
|
}
|
|
4043
4590
|
stats.responseResolved += 1;
|
|
4044
|
-
|
|
4591
|
+
logger11.success("captureLink.responseHit", `\u63A5\u53E3\u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: url=${url}, link=${hit.link}`);
|
|
4045
4592
|
setCandidate("response", hit.link, hit.payloadText);
|
|
4046
4593
|
} catch (error) {
|
|
4047
|
-
|
|
4594
|
+
logger11.warning(`\u63A5\u53E3\u54CD\u5E94\u5904\u7406\u5F02\u5E38: ${error instanceof Error ? error.message : String(error)}`);
|
|
4048
4595
|
}
|
|
4049
4596
|
};
|
|
4050
4597
|
if (share.mode === "dom") {
|
|
4051
|
-
|
|
4598
|
+
logger11.info("\u5F53\u524D\u4E3A DOM \u6A21\u5F0F\uFF0C\u4EC5\u542F\u7528 DOM \u76D1\u542C");
|
|
4052
4599
|
domMonitor = await createDomShareMonitor(page, {
|
|
4053
4600
|
prefix: share.prefix,
|
|
4054
4601
|
selectors: domSelectors,
|
|
@@ -4063,14 +4610,14 @@ var Share = {
|
|
|
4063
4610
|
});
|
|
4064
4611
|
}
|
|
4065
4612
|
if (share.mode === "response") {
|
|
4066
|
-
|
|
4613
|
+
logger11.info(`\u5F53\u524D\u4E3A\u63A5\u53E3\u6A21\u5F0F\uFF0C\u6302\u8F7D response \u76D1\u542C: apiMatchers=${toJsonInline(apiMatchers, 160)}`);
|
|
4067
4614
|
page.on("response", onResponse);
|
|
4068
4615
|
}
|
|
4069
4616
|
const deadline = Date.now() + timeoutMs;
|
|
4070
4617
|
const getRemainingMs = () => Math.max(0, deadline - Date.now());
|
|
4071
4618
|
try {
|
|
4072
4619
|
const actionTimeout = getRemainingMs();
|
|
4073
|
-
|
|
4620
|
+
logger11.start("captureLink.performActions", `\u6267\u884C\u52A8\u4F5C\u9884\u7B97=${actionTimeout}ms`);
|
|
4074
4621
|
if (actionTimeout > 0) {
|
|
4075
4622
|
let timer = null;
|
|
4076
4623
|
let actionError = null;
|
|
@@ -4084,21 +4631,21 @@ var Share = {
|
|
|
4084
4631
|
const actionResult = await Promise.race([actionPromise, timeoutPromise]);
|
|
4085
4632
|
if (timer) clearTimeout(timer);
|
|
4086
4633
|
if (actionResult === "__ACTION_ERROR__") {
|
|
4087
|
-
|
|
4634
|
+
logger11.fail("captureLink.performActions", actionError);
|
|
4088
4635
|
throw actionError;
|
|
4089
4636
|
}
|
|
4090
4637
|
if (actionResult === "__ACTION_TIMEOUT__") {
|
|
4091
4638
|
stats.actionTimedOut = true;
|
|
4092
|
-
|
|
4639
|
+
logger11.warning(`performActions \u5DF2\u8D85\u65F6 (${actionTimeout}ms)\uFF0C\u52A8\u4F5C\u53EF\u80FD\u4ECD\u5728\u5F02\u6B65\u6267\u884C`);
|
|
4093
4640
|
} else {
|
|
4094
|
-
|
|
4641
|
+
logger11.success("captureLink.performActions", "\u6267\u884C\u52A8\u4F5C\u5B8C\u6210");
|
|
4095
4642
|
}
|
|
4096
4643
|
}
|
|
4097
4644
|
let nextProgressLogTs = Date.now() + 3e3;
|
|
4098
4645
|
while (true) {
|
|
4099
4646
|
const selected = share.mode === "dom" ? candidates.dom : candidates.response;
|
|
4100
4647
|
if (selected?.link) {
|
|
4101
|
-
|
|
4648
|
+
logger11.success("captureLink", `\u6355\u83B7\u6210\u529F: source=${share.mode}, link=${selected.link}`);
|
|
4102
4649
|
return {
|
|
4103
4650
|
link: selected.link,
|
|
4104
4651
|
payloadText: selected.payloadText,
|
|
@@ -4110,7 +4657,7 @@ var Share = {
|
|
|
4110
4657
|
if (remaining <= 0) break;
|
|
4111
4658
|
const now = Date.now();
|
|
4112
4659
|
if (now >= nextProgressLogTs) {
|
|
4113
|
-
|
|
4660
|
+
logger11.info(
|
|
4114
4661
|
`captureLink \u7B49\u5F85\u4E2D: remaining=${remaining}ms, domMutationCount=${stats.domMutationCount}, responseMatched=${stats.responseMatched}`
|
|
4115
4662
|
);
|
|
4116
4663
|
nextProgressLogTs = now + 5e3;
|
|
@@ -4118,11 +4665,11 @@ var Share = {
|
|
|
4118
4665
|
await (0, import_delay2.default)(Math.max(0, Math.min(DEFAULT_POLL_INTERVAL_MS, remaining)));
|
|
4119
4666
|
}
|
|
4120
4667
|
if (share.mode === "response" && stats.responseMatched === 0) {
|
|
4121
|
-
|
|
4668
|
+
logger11.warning(
|
|
4122
4669
|
`\u63A5\u53E3\u76D1\u542C\u672A\u547D\u4E2D: apiMatchers=${toJsonInline(apiMatchers, 220)}, \u54CD\u5E94\u6837\u672CURLs=${toJsonInline(stats.responseSampleUrls, 420)}`
|
|
4123
4670
|
);
|
|
4124
4671
|
}
|
|
4125
|
-
|
|
4672
|
+
logger11.warning(
|
|
4126
4673
|
`captureLink \u8D85\u65F6\u672A\u62FF\u5230\u94FE\u63A5: mode=${share.mode}, actionTimedOut=${stats.actionTimedOut}, domMutationCount=${stats.domMutationCount}, responseObserved=${stats.responseObserved}, responseMatched=${stats.responseMatched}, lastMatchedUrl=${stats.lastMatchedUrl || "none"}`
|
|
4127
4674
|
);
|
|
4128
4675
|
return {
|
|
@@ -4134,7 +4681,7 @@ var Share = {
|
|
|
4134
4681
|
} finally {
|
|
4135
4682
|
if (share.mode === "response") {
|
|
4136
4683
|
page.off("response", onResponse);
|
|
4137
|
-
|
|
4684
|
+
logger11.info("response \u76D1\u542C\u5DF2\u5378\u8F7D");
|
|
4138
4685
|
}
|
|
4139
4686
|
await stopDomMonitor();
|
|
4140
4687
|
}
|
|
@@ -4220,8 +4767,8 @@ var usePlaywrightToolKit = () => {
|
|
|
4220
4767
|
LiveView,
|
|
4221
4768
|
Constants: constants_exports,
|
|
4222
4769
|
Utils,
|
|
4770
|
+
RuntimeEnv,
|
|
4223
4771
|
Captcha,
|
|
4224
|
-
Sse,
|
|
4225
4772
|
Errors: errors_exports,
|
|
4226
4773
|
Mutation,
|
|
4227
4774
|
Display,
|