@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/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 = (logger13, name) => {
339
- if (logger13 && typeof logger13[name] === "function") {
340
- return logger13[name].bind(logger13);
338
+ var resolveLogMethod = (logger12, name) => {
339
+ if (logger12 && typeof logger12[name] === "function") {
340
+ return logger12[name].bind(logger12);
341
341
  }
342
- if (name === "warning" && logger13 && typeof logger13.warn === "function") {
343
- return logger13.warn.bind(logger13);
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 = (logger13) => {
349
- defaultLogger = logger13;
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 logger13 = resolveLogger(explicitLogger);
377
- const logFn = resolveLogMethod(logger13, methodName);
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(finalError, {
1027
- step,
1028
- page,
1029
- options,
1030
- base64,
1031
- retryAttempts: retryTimes
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
- await Actor2.pushData({
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
- * 自动解析 CrawlerError code 和 context
1679
+ * 推送失败数据的通用方法(私有方法,仅供 runStep 内部使用)。
1680
+ * 即便任务失败,这里也会自动回收 env_patch,确保 cookies/storage 的最新状态不会丢。
1072
1681
  * @param {Error|CrawlerError} error - 错误对象
1073
- * @param {Object} [meta] - 额外的数据(如failedStep, screenshotBase64等)
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
- await Actor2.pushData({
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: [{ name: "chrome", minVersion: 110 }],
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(BASE_CONFIG.locale);
1828
+ getFingerprintGeneratorOptions(options = {}) {
1829
+ return buildFingerprintOptions(options);
1203
1830
  },
1204
1831
  /**
1205
1832
  * 获取基础启动参数。
1206
1833
  */
1207
- getLaunchArgs() {
1208
- return [...DEFAULT_LAUNCH_ARGS];
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(BASE_CONFIG.locale);
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
- browserPoolOptions: {
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 logger11 = createInternalLogger("Mutation");
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
- logger11.start("waitForStable", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, \u7A33\u5B9A\u65F6\u95F4=${stableTime}ms`);
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
- logger11.info(`waitForStable \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
2917
+ logger10.info(`waitForStable \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
2371
2918
  } catch (e) {
2372
- logger11.warning(`waitForStable \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
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
- logger11.info("waitForStable \u5DF2\u542F\u7528 onMutation \u56DE\u8C03");
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
- logger11.warning("waitForStable \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
3050
+ logger10.warning("waitForStable \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
2504
3051
  }
2505
- logger11.success("waitForStable", `DOM \u7A33\u5B9A, \u603B\u5171 ${result.mutationCount} \u6B21\u53D8\u5316${result.wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
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
- logger11.start(
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
- logger11.info(`waitForStableAcrossRoots \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
3177
+ logger10.info(`waitForStableAcrossRoots \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
2631
3178
  } catch (e) {
2632
- logger11.warning(`waitForStableAcrossRoots \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
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
- logger11.warning("waitForStableAcrossRoots \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
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
- logger11.info(
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
- logger11.success("waitForStableAcrossRoots", `DOM \u7A33\u5B9A, \u603B\u5171 ${mutationCount} \u6B21\u53D8\u5316${wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
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
- logger11.start("useMonitor", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, mode=${mode}`);
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
- logger11.success("useMonitor", "\u76D1\u63A7\u5668\u5DF2\u542F\u52A8");
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
- logger11.success("useMonitor.stop", `\u76D1\u63A7\u5DF2\u505C\u6B62, \u5171 ${totalMutations} \u6B21\u53D8\u5316`);
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: "", accountType: "", note: "" };
3429
+ return { owner: "", envType: "", note: "" };
2883
3430
  }
2884
3431
  const parts = String(value).split(":");
2885
3432
  const owner = (parts[0] || "").trim();
2886
- const accountType = (parts[1] || "").trim();
3433
+ const envType = (parts[1] || "").trim();
2887
3434
  const note = parts.length > 2 ? parts.slice(2).join(":").trim() : "";
2888
- return { owner, accountType, note };
3435
+ return { owner, envType, note };
2889
3436
  },
2890
- parseAccountDisplayName(value) {
3437
+ parseEnvDisplayName(value) {
2891
3438
  if (!value) {
2892
- return { account: "", owner: "", accountType: "", note: "" };
3439
+ return { env: "", owner: "", envType: "", note: "" };
2893
3440
  }
2894
3441
  const parts = String(value).split(":");
2895
- const account = (parts[0] || "").trim();
3442
+ const env = (parts[0] || "").trim();
2896
3443
  const owner = (parts[1] || "").trim();
2897
- const accountType = (parts[2] || "").trim();
3444
+ const envType = (parts[2] || "").trim();
2898
3445
  const note = parts.length > 3 ? parts.slice(3).join(":").trim() : "";
2899
- return { account, owner, accountType, note };
3446
+ return { env, owner, envType, note };
2900
3447
  },
2901
- buildTokenDisplayName({ owner, accountType, note } = {}) {
3448
+ buildTokenDisplayName({ owner, envType, note } = {}) {
2902
3449
  const trimmedOwner = owner?.trim() || "";
2903
- const trimmedType = accountType?.trim() || "";
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
- resolveAccountIdentity(displayName, accountId) {
2927
- const parts = this.parseAccountDisplayName(displayName);
2928
- const cleanId = String(accountId || "").trim();
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.account !== "" && parts.account !== cleanId;
2931
- const primary = hasName ? parts.account : short || "-";
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: (logger13) => setDefaultLogger(logger13),
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: (logger13) => {
3749
- if (logger13) return createTemplateLogger(createBaseLogger("", logger13));
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 logger12 = createInternalLogger("Share");
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
- logger12.info(`DOM \u76D1\u542C\u51C6\u5907\u6302\u8F7D: selectors=${toJsonInline(selectors, 120)}, mode=${mode}`);
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
- logger12.info(`DOM \u53D8\u5316\u5DF2\u6355\u83B7: mutationCount=${mutationCount}, mutationNodes=${mutationNodes.length}`);
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
- logger12.success("captureLink.domHit", `DOM \u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: mutationCount=${mutationCount}, link=${candidate}`);
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
- logger12.info(`DOM \u76D1\u542C\u5DF2\u505C\u6B62: totalMutations=${result?.totalMutations || 0}`);
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
- logger12.start("captureLink", `mode=${share.mode}, timeoutMs=${timeoutMs}, prefix=${share.prefix}`);
3956
- logger12.info(`captureLink \u914D\u7F6E: xurl=${toJsonInline(share.xurl)}, domMode=${domMode}, domSelectors=${toJsonInline(domSelectors, 120)}`);
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
- logger12.info(`\u5019\u9009\u94FE\u63A5\u5DF2\u786E\u8BA4: source=${source}, link=${validated}`);
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
- logger12.warning(`\u505C\u6B62 DOM \u76D1\u542C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
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
- logger12.info(`\u63A5\u53E3\u54CD\u5E94\u91C7\u6837(${stats.responseObserved}): ${url}`);
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
- logger12.info(`\u63A5\u53E3\u547D\u4E2D\u5339\u914D(${stats.responseMatched}): ${url}`);
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
- logger12.info(`\u63A5\u53E3\u89E3\u6790\u5B8C\u6210\u4F46\u672A\u63D0\u53D6\u5230\u5206\u4EAB\u94FE\u63A5: payloadSize=${text.length}`);
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
- logger12.success("captureLink.responseHit", `\u63A5\u53E3\u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: url=${url}, link=${hit.link}`);
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
- logger12.warning(`\u63A5\u53E3\u54CD\u5E94\u5904\u7406\u5F02\u5E38: ${error instanceof Error ? error.message : String(error)}`);
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
- logger12.info("\u5F53\u524D\u4E3A DOM \u6A21\u5F0F\uFF0C\u4EC5\u542F\u7528 DOM \u76D1\u542C");
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
- logger12.info(`\u5F53\u524D\u4E3A\u63A5\u53E3\u6A21\u5F0F\uFF0C\u6302\u8F7D response \u76D1\u542C: apiMatchers=${toJsonInline(apiMatchers, 160)}`);
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
- logger12.start("captureLink.performActions", `\u6267\u884C\u52A8\u4F5C\u9884\u7B97=${actionTimeout}ms`);
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
- logger12.fail("captureLink.performActions", actionError);
4634
+ logger11.fail("captureLink.performActions", actionError);
4088
4635
  throw actionError;
4089
4636
  }
4090
4637
  if (actionResult === "__ACTION_TIMEOUT__") {
4091
4638
  stats.actionTimedOut = true;
4092
- logger12.warning(`performActions \u5DF2\u8D85\u65F6 (${actionTimeout}ms)\uFF0C\u52A8\u4F5C\u53EF\u80FD\u4ECD\u5728\u5F02\u6B65\u6267\u884C`);
4639
+ logger11.warning(`performActions \u5DF2\u8D85\u65F6 (${actionTimeout}ms)\uFF0C\u52A8\u4F5C\u53EF\u80FD\u4ECD\u5728\u5F02\u6B65\u6267\u884C`);
4093
4640
  } else {
4094
- logger12.success("captureLink.performActions", "\u6267\u884C\u52A8\u4F5C\u5B8C\u6210");
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
- logger12.success("captureLink", `\u6355\u83B7\u6210\u529F: source=${share.mode}, link=${selected.link}`);
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
- logger12.info(
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
- logger12.warning(
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
- logger12.warning(
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
- logger12.info("response \u76D1\u542C\u5DF2\u5378\u8F7D");
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,