@skrillex1224/playwright-toolkit 2.1.166 → 2.1.168

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.js CHANGED
@@ -93,10 +93,10 @@ var createActorInfo = (info) => {
93
93
  xurl
94
94
  };
95
95
  };
96
- const buildLandingUrl = ({ protocol: protocol2, domain: domain2, path: path2 }) => {
96
+ const buildLandingUrl = ({ protocol: protocol2, domain: domain2, path: path3 }) => {
97
97
  const safeProtocol = String(protocol2).trim();
98
98
  const safeDomain = normalizeDomain(domain2);
99
- const safePath = normalizePath(path2);
99
+ const safePath = normalizePath(path3);
100
100
  return `${safeProtocol}://${safeDomain}${safePath}`;
101
101
  };
102
102
  const buildIcon = ({ key }) => {
@@ -104,13 +104,13 @@ var createActorInfo = (info) => {
104
104
  };
105
105
  const protocol = info.protocol || "https";
106
106
  const domain = normalizeDomain(info.domain);
107
- const path = normalizePath(info.path);
107
+ const path2 = normalizePath(info.path);
108
108
  const share = normalizeShare2(info.share);
109
109
  return {
110
110
  ...info,
111
111
  protocol,
112
112
  domain,
113
- path,
113
+ path: path2,
114
114
  share,
115
115
  get icon() {
116
116
  if (info.icon) return info.icon;
@@ -124,7 +124,7 @@ var createActorInfo = (info) => {
124
124
  var ActorInfo = {
125
125
  qbot: createActorInfo({
126
126
  key: "qbot",
127
- name: "\u641C\u72D7QBot",
127
+ name: "QQ\u6D4F\u89C8\u5668AI\u641C",
128
128
  domain: "sogou.com",
129
129
  path: "/web",
130
130
  share: {
@@ -546,20 +546,361 @@ var CrawlerError = class _CrawlerError extends Error {
546
546
  // src/apify-kit.js
547
547
  import { serializeError as serializeError2 } from "serialize-error";
548
548
 
549
- // src/traffic-runtime.js
550
- var trafficMeter = null;
551
- var setTrafficMeter = (meter) => {
552
- trafficMeter = meter || null;
549
+ // src/proxy-meter-runtime.js
550
+ import { spawn, spawnSync } from "child_process";
551
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
552
+ import { tmpdir } from "os";
553
+ import path from "path";
554
+ import { fileURLToPath } from "url";
555
+ var logger2 = createInternalLogger("ProxyMeter");
556
+ var MAX_TOP_DOMAINS = 20;
557
+ var FLUSH_INTERVAL_MS = 2e3;
558
+ var DEFAULT_DEBUG_MAX_EVENTS = 400;
559
+ var runtime = null;
560
+ var cleanupInstalled = false;
561
+ var observedDomainResourceTypes = /* @__PURE__ */ new Map();
562
+ var toSafeInt = (value) => {
563
+ const num = Number(value);
564
+ if (!Number.isFinite(num) || num <= 0) return 0;
565
+ return Math.round(num);
553
566
  };
554
- var getTrafficSnapshot = () => {
555
- if (!trafficMeter || typeof trafficMeter.snapshot !== "function") {
567
+ var toSafeFloat = (value) => {
568
+ const num = Number(value);
569
+ if (!Number.isFinite(num)) return 0;
570
+ return num;
571
+ };
572
+ var normalizeResourceType = (value) => {
573
+ const type = String(value || "").trim().toLowerCase();
574
+ if (!type) return "other";
575
+ return type;
576
+ };
577
+ var resolveHostname = (requestUrl) => {
578
+ const raw = String(requestUrl || "").trim();
579
+ if (!raw) return "";
580
+ try {
581
+ return String(new URL(raw).hostname || "").trim().toLowerCase();
582
+ } catch {
583
+ return "";
584
+ }
585
+ };
586
+ var recordDomainResourceType = (domain, resourceType) => {
587
+ const host = String(domain || "").trim().toLowerCase();
588
+ if (!host) return;
589
+ const type = normalizeResourceType(resourceType);
590
+ let byType = observedDomainResourceTypes.get(host);
591
+ if (!byType) {
592
+ byType = /* @__PURE__ */ new Map();
593
+ observedDomainResourceTypes.set(host, byType);
594
+ }
595
+ byType.set(type, (byType.get(type) || 0) + 1);
596
+ };
597
+ var resolveResourceTypeMeta = (domain) => {
598
+ const host = String(domain || "").trim().toLowerCase();
599
+ if (!host) return { resourceType: "", resourceTypeCounts: void 0 };
600
+ const byType = observedDomainResourceTypes.get(host);
601
+ if (!byType || byType.size === 0) {
602
+ return { resourceType: "", resourceTypeCounts: void 0 };
603
+ }
604
+ const entries = Array.from(byType.entries()).filter(([type, count]) => type && Number(count) > 0).sort((a, b) => {
605
+ if (Number(b[1]) !== Number(a[1])) return Number(b[1]) - Number(a[1]);
606
+ return String(a[0]).localeCompare(String(b[0]));
607
+ });
608
+ if (entries.length === 0) {
609
+ return { resourceType: "", resourceTypeCounts: void 0 };
610
+ }
611
+ return {
612
+ resourceType: entries[0][0],
613
+ resourceTypeCounts: Object.fromEntries(entries.map(([type, count]) => [type, toSafeInt(count)]))
614
+ };
615
+ };
616
+ var resolveScriptPath = () => {
617
+ const baseDir = typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url));
618
+ return path.join(baseDir, "proxy-meter.js");
619
+ };
620
+ var pickFreePort = () => {
621
+ const script = [
622
+ 'const net=require("net");',
623
+ "const server=net.createServer();",
624
+ 'server.listen(0,"127.0.0.1",()=>{',
625
+ "const port=server.address().port;",
626
+ "server.close(()=>{console.log(port);});",
627
+ "});"
628
+ ].join("");
629
+ const result = spawnSync(process.execPath, ["-e", script], { encoding: "utf8" });
630
+ const port = Number(String(result.stdout || "").trim());
631
+ if (result.status === 0 && Number.isFinite(port) && port > 0) {
632
+ return port;
633
+ }
634
+ return 2e4 + Math.floor(Math.random() * 2e4);
635
+ };
636
+ var resolveLogDir = () => {
637
+ const storageDir = process.env.APIFY_STORAGE_DIR || process.env.APIFY_LOCAL_STORAGE_DIR || "";
638
+ if (storageDir) {
639
+ return path.join(storageDir, "proxy-meter");
640
+ }
641
+ return path.join(tmpdir(), "proxy-meter");
642
+ };
643
+ var ensureLogPath = () => {
644
+ const baseDir = resolveLogDir();
645
+ if (!existsSync(baseDir)) {
646
+ try {
647
+ mkdirSync(baseDir, { recursive: true });
648
+ } catch {
649
+ }
650
+ }
651
+ const runId = String(process.env.APIFY_ACTOR_RUN_ID || "").trim();
652
+ const suffix = `${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
653
+ const label = runId ? `proxy-meter-${runId}-${suffix}.json` : `proxy-meter-${process.pid}-${suffix}.json`;
654
+ return path.join(baseDir, label);
655
+ };
656
+ var readSnapshot = (logPath) => {
657
+ if (!logPath || !existsSync(logPath)) return null;
658
+ try {
659
+ const raw = readFileSync(logPath, "utf8");
660
+ if (!raw) return null;
661
+ return JSON.parse(raw);
662
+ } catch {
556
663
  return null;
557
664
  }
558
- return trafficMeter.snapshot();
665
+ };
666
+ var normalizeDomainRows = (hosts) => {
667
+ const rows = [];
668
+ let requestCount = 0;
669
+ let connectCount = 0;
670
+ for (const [domain, stat] of Object.entries(hosts || {})) {
671
+ if (!stat || typeof stat !== "object") continue;
672
+ const inBytes = toSafeInt(stat.inBytes);
673
+ const outBytes = toSafeInt(stat.outBytes);
674
+ const requests = toSafeInt(stat.requests);
675
+ const connections = toSafeInt(stat.connections);
676
+ const totalBytes = inBytes + outBytes;
677
+ requestCount += requests;
678
+ connectCount += connections;
679
+ if (totalBytes <= 0 && requests <= 0 && connections <= 0) continue;
680
+ const resourceTypeMeta = resolveResourceTypeMeta(domain);
681
+ rows.push({
682
+ domain,
683
+ inBytes,
684
+ outBytes,
685
+ totalBytes,
686
+ requests,
687
+ connections,
688
+ resourceType: resourceTypeMeta.resourceType || void 0,
689
+ resourceTypeCounts: resourceTypeMeta.resourceTypeCounts || void 0
690
+ });
691
+ }
692
+ rows.sort((a, b) => {
693
+ if (b.totalBytes !== a.totalBytes) return b.totalBytes - a.totalBytes;
694
+ if (b.requests !== a.requests) return b.requests - a.requests;
695
+ return String(a.domain).localeCompare(String(b.domain));
696
+ });
697
+ return {
698
+ topDomains: rows.slice(0, MAX_TOP_DOMAINS),
699
+ requestCount,
700
+ connectCount
701
+ };
702
+ };
703
+ var normalizeDebugRows = (rows, type) => {
704
+ if (!Array.isArray(rows) || rows.length === 0) return void 0;
705
+ const normalized = rows.map((row) => {
706
+ if (!row || typeof row !== "object") return null;
707
+ return {
708
+ domain: String(row.domain || "").trim(),
709
+ status: String(row.status || "").trim(),
710
+ count: toSafeInt(row.count),
711
+ failedCount: toSafeInt(row.failedCount),
712
+ inBytes: toSafeInt(row.inBytes),
713
+ outBytes: toSafeInt(row.outBytes),
714
+ totalBytes: toSafeInt(row.totalBytes),
715
+ avgDurationMs: toSafeFloat(row.avgDurationMs),
716
+ reconnectCount: toSafeInt(row.reconnectCount),
717
+ largeResponseCount: toSafeInt(row.largeResponseCount),
718
+ failureRatePct: toSafeFloat(row.failureRatePct),
719
+ type
720
+ };
721
+ }).filter((row) => row && row.domain && row.count > 0);
722
+ return normalized.length > 0 ? normalized : void 0;
723
+ };
724
+ var normalizeDebugEvent = (row) => {
725
+ if (!row || typeof row !== "object") return null;
726
+ const host = String(row.host || "").trim();
727
+ if (!host) return null;
728
+ return {
729
+ ts: String(row.ts || ""),
730
+ runId: String(row.runId || ""),
731
+ channel: String(row.channel || ""),
732
+ host,
733
+ method: String(row.method || ""),
734
+ path: String(row.path || ""),
735
+ statusCode: toSafeInt(row.statusCode),
736
+ durationMs: toSafeInt(row.durationMs),
737
+ inBytes: toSafeInt(row.inBytes),
738
+ outBytes: toSafeInt(row.outBytes),
739
+ totalBytes: toSafeInt(row.totalBytes),
740
+ error: String(row.error || "")
741
+ };
742
+ };
743
+ var normalizeDebugSummary = (debug) => {
744
+ if (!debug || typeof debug !== "object" || !debug.summary || typeof debug.summary !== "object") {
745
+ return null;
746
+ }
747
+ const summary = debug.summary;
748
+ const domainStatus = normalizeDebugRows(summary.domainStatus, "domain_status");
749
+ const topFailureDomains = normalizeDebugRows(summary.topFailureDomains, "top_failure");
750
+ const topLargeDomains = normalizeDebugRows(summary.topLargeDomains, "top_large");
751
+ const topReconnectDomains = normalizeDebugRows(summary.topReconnectDomains, "top_reconnect");
752
+ return {
753
+ enabled: Boolean(debug.enabled),
754
+ sampledEvents: toSafeInt(debug.sampledEvents),
755
+ droppedEvents: toSafeInt(debug.droppedEvents),
756
+ totalEvents: toSafeInt(debug.totalEvents),
757
+ largeResponseThresholdBytes: toSafeInt(summary.largeResponseThresholdBytes),
758
+ requestCount: toSafeInt(summary.requestCount),
759
+ failedCount: toSafeInt(summary.failedCount),
760
+ failureRatePct: toSafeFloat(summary.failureRatePct),
761
+ largeResponseCount: toSafeInt(summary.largeResponseCount),
762
+ reconnectCount: toSafeInt(summary.reconnectCount),
763
+ domainStatus,
764
+ topFailureDomains,
765
+ topLargeDomains,
766
+ topReconnectDomains
767
+ };
768
+ };
769
+ var normalizeSnapshot = (raw) => {
770
+ if (!raw || typeof raw !== "object") return null;
771
+ const totalIn = toSafeInt(raw.totalInBytes);
772
+ const totalOut = toSafeInt(raw.totalOutBytes);
773
+ const domains = normalizeDomainRows(raw.hosts && typeof raw.hosts === "object" ? raw.hosts : {});
774
+ const normalized = {
775
+ meter: "proxy-meter-v1",
776
+ totalBytes: totalIn + totalOut,
777
+ uploadBytes: totalOut,
778
+ downloadBytes: totalIn,
779
+ requestCount: domains.requestCount,
780
+ connectCount: domains.connectCount,
781
+ topDomains: domains.topDomains
782
+ };
783
+ const debug = raw.debug && typeof raw.debug === "object" ? raw.debug : null;
784
+ const summary = normalizeDebugSummary(debug);
785
+ const events = Array.isArray(debug?.events) ? debug.events.map(normalizeDebugEvent).filter(Boolean) : [];
786
+ if (summary) {
787
+ normalized.trafficDebugSummary = summary;
788
+ }
789
+ if (events.length > 0) {
790
+ normalized.trafficDebugEvents = events;
791
+ }
792
+ const hasSignal = normalized.totalBytes > 0 || normalized.uploadBytes > 0 || normalized.downloadBytes > 0 || normalized.requestCount > 0 || normalized.connectCount > 0 || normalized.topDomains && normalized.topDomains.length > 0 || Boolean(summary) || events.length > 0;
793
+ if (!hasSignal) return null;
794
+ return normalized;
795
+ };
796
+ var registerCleanup = () => {
797
+ if (cleanupInstalled) return;
798
+ cleanupInstalled = true;
799
+ const shutdown = () => {
800
+ if (!runtime || !runtime.proc) return;
801
+ try {
802
+ runtime.proc.kill("SIGTERM");
803
+ } catch {
804
+ }
805
+ };
806
+ process.once("exit", shutdown);
807
+ process.once("SIGINT", shutdown);
808
+ process.once("SIGTERM", shutdown);
809
+ };
810
+ var startProxyMeter = (options = {}) => {
811
+ const upstreamUrl = String(options.proxyUrl || "").trim();
812
+ if (!upstreamUrl) return null;
813
+ if (runtime && runtime.proc) {
814
+ try {
815
+ runtime.proc.kill("SIGTERM");
816
+ } catch {
817
+ }
818
+ runtime = null;
819
+ }
820
+ observedDomainResourceTypes = /* @__PURE__ */ new Map();
821
+ const port = pickFreePort();
822
+ const logPath = ensureLogPath();
823
+ const scriptPath = resolveScriptPath();
824
+ const debugMode = Boolean(options.debugMode);
825
+ const debugMaxEvents = Math.max(10, toSafeInt(options.debugMaxEvents) || DEFAULT_DEBUG_MAX_EVENTS);
826
+ const env = {
827
+ ...process.env,
828
+ PROXY_METER_PORT: String(port),
829
+ PROXY_METER_LOG: logPath,
830
+ PROXY_METER_UPSTREAM: upstreamUrl,
831
+ PROXY_METER_FLUSH_MS: String(FLUSH_INTERVAL_MS),
832
+ PROXY_METER_DEBUG: debugMode ? "1" : "0",
833
+ PROXY_METER_DEBUG_MAX_EVENTS: String(debugMaxEvents)
834
+ };
835
+ const child = spawn(process.execPath, [scriptPath], {
836
+ env,
837
+ stdio: ["ignore", "ignore", "ignore"]
838
+ });
839
+ child.once("exit", (code) => {
840
+ if (code && code !== 0) {
841
+ logger2.warn(`[proxy-meter] exited with code ${code}`);
842
+ }
843
+ });
844
+ runtime = {
845
+ proc: child,
846
+ port,
847
+ logPath,
848
+ startedAt: Date.now()
849
+ };
850
+ registerCleanup();
851
+ return { server: `http://127.0.0.1:${port}` };
852
+ };
853
+ var recordProxyMeterResourceType = (requestUrl, resourceType) => {
854
+ if (!runtime) return;
855
+ const host = resolveHostname(requestUrl);
856
+ if (!host) return;
857
+ recordDomainResourceType(host, resourceType);
858
+ };
859
+ var stopProxyMeter = async () => {
860
+ if (!runtime) return null;
861
+ const { proc, logPath } = runtime;
862
+ if (!proc || proc.killed) {
863
+ runtime = null;
864
+ return logPath || null;
865
+ }
866
+ await new Promise((resolve) => {
867
+ const timeout = setTimeout(() => {
868
+ try {
869
+ proc.kill("SIGKILL");
870
+ } catch {
871
+ }
872
+ resolve();
873
+ }, 2e3);
874
+ proc.once("exit", () => {
875
+ clearTimeout(timeout);
876
+ resolve();
877
+ });
878
+ try {
879
+ proc.kill("SIGTERM");
880
+ } catch {
881
+ resolve();
882
+ }
883
+ });
884
+ runtime = null;
885
+ return logPath || null;
886
+ };
887
+ var getProxyMeterSnapshot = async (options = {}) => {
888
+ if (!runtime) return null;
889
+ const finalize = Boolean(options.finalize);
890
+ const logPath = finalize ? await stopProxyMeter() : runtime.logPath;
891
+ const raw = readSnapshot(logPath);
892
+ const snapshot = normalizeSnapshot(raw);
893
+ if (finalize && logPath) {
894
+ try {
895
+ rmSync(logPath, { force: true });
896
+ } catch {
897
+ }
898
+ }
899
+ return snapshot;
559
900
  };
560
901
 
561
902
  // src/apify-kit.js
562
- var logger2 = createInternalLogger("ApifyKit");
903
+ var logger3 = createInternalLogger("ApifyKit");
563
904
  async function createApifyKit() {
564
905
  let apify = null;
565
906
  try {
@@ -587,29 +928,29 @@ async function createApifyKit() {
587
928
  const { times: retryTimes = 0, mode: retryMode = "direct", before: beforeRetry } = retry;
588
929
  const executeAction = async (attemptNumber) => {
589
930
  const attemptLabel = attemptNumber > 0 ? ` (\u91CD\u8BD5 #${attemptNumber})` : "";
590
- logger2.start(`[Step] ${step}${attemptLabel}`);
931
+ logger3.start(`[Step] ${step}${attemptLabel}`);
591
932
  try {
592
933
  const result = await actionFn();
593
- logger2.success(`[Step] ${step}${attemptLabel}`);
934
+ logger3.success(`[Step] ${step}${attemptLabel}`);
594
935
  return { success: true, result };
595
936
  } catch (error) {
596
- logger2.fail(`[Step] ${step}${attemptLabel}`, error);
937
+ logger3.fail(`[Step] ${step}${attemptLabel}`, error);
597
938
  return { success: false, error };
598
939
  }
599
940
  };
600
941
  const prepareForRetry = async (attemptNumber) => {
601
942
  if (typeof beforeRetry === "function") {
602
- logger2.start(`[RetryStep] \u6267\u884C\u81EA\u5B9A\u4E49 before \u94A9\u5B50 (\u7B2C ${attemptNumber} \u6B21\u91CD\u8BD5)`);
943
+ logger3.start(`[RetryStep] \u6267\u884C\u81EA\u5B9A\u4E49 before \u94A9\u5B50 (\u7B2C ${attemptNumber} \u6B21\u91CD\u8BD5)`);
603
944
  await beforeRetry(page, attemptNumber);
604
- logger2.success(`[RetryStep] before \u94A9\u5B50\u5B8C\u6210`);
945
+ logger3.success(`[RetryStep] before \u94A9\u5B50\u5B8C\u6210`);
605
946
  } else if (retryMode === "refresh") {
606
- logger2.start(`[RetryStep] \u5237\u65B0\u9875\u9762 (\u7B2C ${attemptNumber} \u6B21\u91CD\u8BD5)`);
947
+ logger3.start(`[RetryStep] \u5237\u65B0\u9875\u9762 (\u7B2C ${attemptNumber} \u6B21\u91CD\u8BD5)`);
607
948
  await page.reload({ waitUntil: "commit" });
608
- logger2.success(`[RetryStep] \u9875\u9762\u5237\u65B0\u5B8C\u6210`);
949
+ logger3.success(`[RetryStep] \u9875\u9762\u5237\u65B0\u5B8C\u6210`);
609
950
  } else {
610
- logger2.start(`[RetryStep] \u7B49\u5F85 3 \u79D2 (\u7B2C ${attemptNumber} \u6B21\u91CD\u8BD5)`);
951
+ logger3.start(`[RetryStep] \u7B49\u5F85 3 \u79D2 (\u7B2C ${attemptNumber} \u6B21\u91CD\u8BD5)`);
611
952
  await new Promise((resolve) => setTimeout(resolve, 3e3));
612
- logger2.success(`[RetryStep] \u7B49\u5F85\u5B8C\u6210`);
953
+ logger3.success(`[RetryStep] \u7B49\u5F85\u5B8C\u6210`);
613
954
  }
614
955
  };
615
956
  let lastResult = await executeAction(0);
@@ -617,11 +958,11 @@ async function createApifyKit() {
617
958
  return lastResult.result;
618
959
  }
619
960
  for (let attempt = 1; attempt <= retryTimes; attempt++) {
620
- logger2.start(`[RetryStep] \u51C6\u5907\u7B2C ${attempt}/${retryTimes} \u6B21\u91CD\u8BD5: ${step}`);
961
+ logger3.start(`[RetryStep] \u51C6\u5907\u7B2C ${attempt}/${retryTimes} \u6B21\u91CD\u8BD5: ${step}`);
621
962
  try {
622
963
  await prepareForRetry(attempt);
623
964
  } catch (prepareError) {
624
- logger2.warn(`[RetryStep] \u91CD\u8BD5\u51C6\u5907\u5931\u8D25: ${prepareError.message}`);
965
+ logger3.warn(`[RetryStep] \u91CD\u8BD5\u51C6\u5907\u5931\u8D25: ${prepareError.message}`);
625
966
  continue;
626
967
  }
627
968
  lastResult = await executeAction(attempt);
@@ -642,7 +983,7 @@ async function createApifyKit() {
642
983
  base64 = `data:image/jpeg;base64,${buffer.toString("base64")}`;
643
984
  }
644
985
  } catch (snapErr) {
645
- logger2.warn(`\u622A\u56FE\u751F\u6210\u5931\u8D25: ${snapErr.message}`);
986
+ logger3.warn(`\u622A\u56FE\u751F\u6210\u5931\u8D25: ${snapErr.message}`);
646
987
  }
647
988
  await this.pushFailed(finalError, {
648
989
  step,
@@ -676,7 +1017,7 @@ async function createApifyKit() {
676
1017
  * @param {Object} data - 要推送的数据对象
677
1018
  */
678
1019
  async pushSuccess(data) {
679
- const traffic = getTrafficSnapshot();
1020
+ const traffic = await getProxyMeterSnapshot({ finalize: true });
680
1021
  await Actor2.pushData({
681
1022
  // 固定为0
682
1023
  code: Code.Success,
@@ -685,7 +1026,7 @@ async function createApifyKit() {
685
1026
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
686
1027
  data
687
1028
  });
688
- logger2.success("pushSuccess", "Data pushed");
1029
+ logger3.success("pushSuccess", "Data pushed");
689
1030
  },
690
1031
  /**
691
1032
  * 推送失败数据的通用方法(私有方法,仅供runStep内部使用)
@@ -698,7 +1039,7 @@ async function createApifyKit() {
698
1039
  const isCrawlerError = CrawlerError.isCrawlerError(error);
699
1040
  const code = isCrawlerError ? error.code : Code.UnknownError;
700
1041
  const context = isCrawlerError ? error.context : {};
701
- const traffic = getTrafficSnapshot();
1042
+ const traffic = await getProxyMeterSnapshot({ finalize: true });
702
1043
  await Actor2.pushData({
703
1044
  // 如果是 CrawlerError,使用其 code,否则使用默认 Failed code
704
1045
  code,
@@ -709,7 +1050,7 @@ async function createApifyKit() {
709
1050
  context,
710
1051
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
711
1052
  });
712
- logger2.success("pushFailed", "Error data pushed");
1053
+ logger3.success("pushFailed", "Error data pushed");
713
1054
  }
714
1055
  };
715
1056
  }
@@ -725,7 +1066,7 @@ var ApifyKit = {
725
1066
  };
726
1067
 
727
1068
  // src/internals/utils.js
728
- var logger3 = createInternalLogger("InternalUtils");
1069
+ var logger4 = createInternalLogger("InternalUtils");
729
1070
  var parseCookies = (cookieString, domain) => {
730
1071
  const cookies = [];
731
1072
  const pairs = cookieString.split(";").map((c) => c.trim());
@@ -743,7 +1084,7 @@ var parseCookies = (cookieString, domain) => {
743
1084
  cookies.push(cookie);
744
1085
  }
745
1086
  }
746
- logger3.success("parseCookies", `parsed ${cookies.length} cookies`);
1087
+ logger4.success("parseCookies", `parsed ${cookies.length} cookies`);
747
1088
  return cookies;
748
1089
  };
749
1090
 
@@ -786,7 +1127,7 @@ var Utils = {
786
1127
  };
787
1128
 
788
1129
  // src/anti-cheat.js
789
- var logger4 = createInternalLogger("AntiCheat");
1130
+ var logger5 = createInternalLogger("AntiCheat");
790
1131
  var BASE_CONFIG = Object.freeze({
791
1132
  locale: "zh-CN",
792
1133
  acceptLanguage: "zh-CN,zh;q=0.9",
@@ -848,7 +1189,7 @@ var AntiCheat = {
848
1189
  // src/humanize.js
849
1190
  import delay from "delay";
850
1191
  import { createCursor } from "ghost-cursor-playwright";
851
- var logger5 = createInternalLogger("Humanize");
1192
+ var logger6 = createInternalLogger("Humanize");
852
1193
  var $CursorWeakMap = /* @__PURE__ */ new WeakMap();
853
1194
  function $GetCursor(page) {
854
1195
  const cursor = $CursorWeakMap.get(page);
@@ -876,13 +1217,13 @@ var Humanize = {
876
1217
  */
877
1218
  async initializeCursor(page) {
878
1219
  if ($CursorWeakMap.has(page)) {
879
- logger5.debug("initializeCursor: cursor already exists, skipping");
1220
+ logger6.debug("initializeCursor: cursor already exists, skipping");
880
1221
  return;
881
1222
  }
882
- logger5.start("initializeCursor", "creating cursor");
1223
+ logger6.start("initializeCursor", "creating cursor");
883
1224
  const cursor = await createCursor(page);
884
1225
  $CursorWeakMap.set(page, cursor);
885
- logger5.success("initializeCursor", "cursor initialized");
1226
+ logger6.success("initializeCursor", "cursor initialized");
886
1227
  },
887
1228
  /**
888
1229
  * 人类化鼠标移动 - 使用 ghost-cursor 移动到指定位置或元素
@@ -892,17 +1233,17 @@ var Humanize = {
892
1233
  */
893
1234
  async humanMove(page, target) {
894
1235
  const cursor = $GetCursor(page);
895
- logger5.start("humanMove", `target=${typeof target === "string" ? target : "element/coords"}`);
1236
+ logger6.start("humanMove", `target=${typeof target === "string" ? target : "element/coords"}`);
896
1237
  try {
897
1238
  if (typeof target === "string") {
898
1239
  const element = await page.$(target);
899
1240
  if (!element) {
900
- logger5.warn(`humanMove: \u5143\u7D20\u4E0D\u5B58\u5728 ${target}`);
1241
+ logger6.warn(`humanMove: \u5143\u7D20\u4E0D\u5B58\u5728 ${target}`);
901
1242
  return false;
902
1243
  }
903
1244
  const box = await element.boundingBox();
904
1245
  if (!box) {
905
- logger5.warn(`humanMove: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E ${target}`);
1246
+ logger6.warn(`humanMove: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E ${target}`);
906
1247
  return false;
907
1248
  }
908
1249
  const x = box.x + box.width / 2 + (Math.random() - 0.5) * box.width * 0.2;
@@ -918,10 +1259,10 @@ var Humanize = {
918
1259
  await cursor.actions.move({ x, y });
919
1260
  }
920
1261
  }
921
- logger5.success("humanMove");
1262
+ logger6.success("humanMove");
922
1263
  return true;
923
1264
  } catch (error) {
924
- logger5.fail("humanMove", error);
1265
+ logger6.fail("humanMove", error);
925
1266
  throw error;
926
1267
  }
927
1268
  },
@@ -945,12 +1286,12 @@ var Humanize = {
945
1286
  maxDurationMs = maxSteps * 220 + 800
946
1287
  } = options;
947
1288
  const targetDesc = typeof target === "string" ? target : "ElementHandle";
948
- logger5.start("humanScroll", `target=${targetDesc}`);
1289
+ logger6.start("humanScroll", `target=${targetDesc}`);
949
1290
  let element;
950
1291
  if (typeof target === "string") {
951
1292
  element = await page.$(target);
952
1293
  if (!element) {
953
- logger5.warn(`humanScroll | \u5143\u7D20\u672A\u627E\u5230: ${target}`);
1294
+ logger6.warn(`humanScroll | \u5143\u7D20\u672A\u627E\u5230: ${target}`);
954
1295
  return { element: null, didScroll: false };
955
1296
  }
956
1297
  } else {
@@ -1025,26 +1366,26 @@ var Humanize = {
1025
1366
  try {
1026
1367
  for (let i = 0; i < maxSteps; i++) {
1027
1368
  if (Date.now() - startTime > maxDurationMs) {
1028
- logger5.warn(`humanScroll | \u8D85\u65F6\u4FDD\u62A4\u89E6\u53D1 (${maxDurationMs}ms)`);
1369
+ logger6.warn(`humanScroll | \u8D85\u65F6\u4FDD\u62A4\u89E6\u53D1 (${maxDurationMs}ms)`);
1029
1370
  return { element, didScroll };
1030
1371
  }
1031
1372
  const status = await checkVisibility();
1032
1373
  if (status.code === "VISIBLE") {
1033
1374
  if (status.isFixed) {
1034
- logger5.info("humanScroll | fixed \u5BB9\u5668\u5185\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
1375
+ logger6.info("humanScroll | fixed \u5BB9\u5668\u5185\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
1035
1376
  } else {
1036
- logger5.debug("humanScroll | \u5143\u7D20\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
1377
+ logger6.debug("humanScroll | \u5143\u7D20\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
1037
1378
  }
1038
- logger5.success("humanScroll", didScroll ? "\u5DF2\u6EDA\u52A8" : "\u65E0\u9700\u6EDA\u52A8");
1379
+ logger6.success("humanScroll", didScroll ? "\u5DF2\u6EDA\u52A8" : "\u65E0\u9700\u6EDA\u52A8");
1039
1380
  return { element, didScroll };
1040
1381
  }
1041
- logger5.debug(`humanScroll | \u6B65\u9AA4 ${i + 1}/${maxSteps}: ${status.reason} ${status.direction ? `(${status.direction})` : ""}`);
1382
+ logger6.debug(`humanScroll | \u6B65\u9AA4 ${i + 1}/${maxSteps}: ${status.reason} ${status.direction ? `(${status.direction})` : ""}`);
1042
1383
  if (status.code === "OBSTRUCTED" && status.obstruction) {
1043
- logger5.debug(`humanScroll | \u88AB\u4EE5\u4E0B\u5143\u7D20\u906E\u6321 <${status.obstruction.tag} id="${status.obstruction.id}">`);
1384
+ logger6.debug(`humanScroll | \u88AB\u4EE5\u4E0B\u5143\u7D20\u906E\u6321 <${status.obstruction.tag} id="${status.obstruction.id}">`);
1044
1385
  }
1045
1386
  const scrollRect = await getScrollableRect();
1046
1387
  if (!scrollRect && status.isFixed) {
1047
- logger5.warn("humanScroll | fixed \u5BB9\u5668\u5185\u4E14\u65E0\u53EF\u6EDA\u52A8\u7956\u5148\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
1388
+ logger6.warn("humanScroll | fixed \u5BB9\u5668\u5185\u4E14\u65E0\u53EF\u6EDA\u52A8\u7956\u5148\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
1048
1389
  return { element, didScroll };
1049
1390
  }
1050
1391
  const stepMin = scrollRect ? Math.min(minStep, Math.max(60, scrollRect.height * 0.4)) : minStep;
@@ -1080,10 +1421,10 @@ var Humanize = {
1080
1421
  didScroll = true;
1081
1422
  await delay(this.jitterMs(20 + Math.random() * 40, 0.2));
1082
1423
  }
1083
- logger5.warn(`humanScroll | \u5728 ${maxSteps} \u6B65\u540E\u65E0\u6CD5\u786E\u4FDD\u53EF\u89C1\u6027`);
1424
+ logger6.warn(`humanScroll | \u5728 ${maxSteps} \u6B65\u540E\u65E0\u6CD5\u786E\u4FDD\u53EF\u89C1\u6027`);
1084
1425
  return { element, didScroll };
1085
1426
  } catch (error) {
1086
- logger5.fail("humanScroll", error);
1427
+ logger6.fail("humanScroll", error);
1087
1428
  throw error;
1088
1429
  }
1089
1430
  },
@@ -1101,7 +1442,7 @@ var Humanize = {
1101
1442
  const cursor = $GetCursor(page);
1102
1443
  const { reactionDelay = 250, throwOnMissing = true, scrollIfNeeded = true, restore = false } = options;
1103
1444
  const targetDesc = target == null ? "Current Position" : typeof target === "string" ? target : "ElementHandle";
1104
- logger5.start("humanClick", `target=${targetDesc}`);
1445
+ logger6.start("humanClick", `target=${targetDesc}`);
1105
1446
  const restoreOnce = async () => {
1106
1447
  if (restoreOnce.restored) return;
1107
1448
  restoreOnce.restored = true;
@@ -1110,14 +1451,14 @@ var Humanize = {
1110
1451
  await delay(this.jitterMs(1e3));
1111
1452
  await restoreOnce.do();
1112
1453
  } catch (restoreError) {
1113
- logger5.warn(`humanClick: \u6062\u590D\u6EDA\u52A8\u4F4D\u7F6E\u5931\u8D25: ${restoreError.message}`);
1454
+ logger6.warn(`humanClick: \u6062\u590D\u6EDA\u52A8\u4F4D\u7F6E\u5931\u8D25: ${restoreError.message}`);
1114
1455
  }
1115
1456
  };
1116
1457
  try {
1117
1458
  if (target == null) {
1118
1459
  await delay(this.jitterMs(reactionDelay, 0.4));
1119
1460
  await cursor.actions.click();
1120
- logger5.success("humanClick", "Clicked current position");
1461
+ logger6.success("humanClick", "Clicked current position");
1121
1462
  return true;
1122
1463
  }
1123
1464
  let element;
@@ -1127,7 +1468,7 @@ var Humanize = {
1127
1468
  if (throwOnMissing) {
1128
1469
  throw new Error(`\u627E\u4E0D\u5230\u5143\u7D20 ${target}`);
1129
1470
  }
1130
- logger5.warn(`humanClick: \u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${target}`);
1471
+ logger6.warn(`humanClick: \u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${target}`);
1131
1472
  return false;
1132
1473
  }
1133
1474
  } else {
@@ -1143,7 +1484,7 @@ var Humanize = {
1143
1484
  if (throwOnMissing) {
1144
1485
  throw new Error("\u65E0\u6CD5\u83B7\u53D6\u5143\u7D20\u4F4D\u7F6E");
1145
1486
  }
1146
- logger5.warn("humanClick: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E\uFF0C\u8DF3\u8FC7\u70B9\u51FB");
1487
+ logger6.warn("humanClick: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E\uFF0C\u8DF3\u8FC7\u70B9\u51FB");
1147
1488
  return false;
1148
1489
  }
1149
1490
  const x = box.x + box.width / 2 + (Math.random() - 0.5) * box.width * 0.3;
@@ -1152,11 +1493,11 @@ var Humanize = {
1152
1493
  await delay(this.jitterMs(reactionDelay, 0.4));
1153
1494
  await cursor.actions.click();
1154
1495
  await restoreOnce();
1155
- logger5.success("humanClick");
1496
+ logger6.success("humanClick");
1156
1497
  return true;
1157
1498
  } catch (error) {
1158
1499
  await restoreOnce();
1159
- logger5.fail("humanClick", error);
1500
+ logger6.fail("humanClick", error);
1160
1501
  throw error;
1161
1502
  }
1162
1503
  },
@@ -1167,9 +1508,9 @@ var Humanize = {
1167
1508
  */
1168
1509
  async randomSleep(baseMs, jitterPercent = 0.3) {
1169
1510
  const ms = this.jitterMs(baseMs, jitterPercent);
1170
- logger5.start("randomSleep", `base=${baseMs}, actual=${ms}ms`);
1511
+ logger6.start("randomSleep", `base=${baseMs}, actual=${ms}ms`);
1171
1512
  await delay(ms);
1172
- logger5.success("randomSleep");
1513
+ logger6.success("randomSleep");
1173
1514
  },
1174
1515
  /**
1175
1516
  * 模拟人类"注视"或"阅读"行为:鼠标在页面上随机微动
@@ -1179,7 +1520,7 @@ var Humanize = {
1179
1520
  async simulateGaze(page, baseDurationMs = 2500) {
1180
1521
  const cursor = $GetCursor(page);
1181
1522
  const durationMs = this.jitterMs(baseDurationMs, 0.4);
1182
- logger5.start("simulateGaze", `duration=${durationMs}ms`);
1523
+ logger6.start("simulateGaze", `duration=${durationMs}ms`);
1183
1524
  const startTime = Date.now();
1184
1525
  const viewportSize = page.viewportSize() || { width: 1920, height: 1080 };
1185
1526
  while (Date.now() - startTime < durationMs) {
@@ -1188,7 +1529,7 @@ var Humanize = {
1188
1529
  await cursor.actions.move({ x, y });
1189
1530
  await delay(this.jitterMs(600, 0.5));
1190
1531
  }
1191
- logger5.success("simulateGaze");
1532
+ logger6.success("simulateGaze");
1192
1533
  },
1193
1534
  /**
1194
1535
  * 人类化输入 - 带节奏变化(快-慢-停顿-偶尔加速)
@@ -1201,7 +1542,7 @@ var Humanize = {
1201
1542
  * @param {number} [options.pauseBase=800] - 停顿时长基础值 (ms),实际 ±50% 抖动
1202
1543
  */
1203
1544
  async humanType(page, selector, text, options = {}) {
1204
- logger5.start("humanType", `selector=${selector}, textLen=${text.length}`);
1545
+ logger6.start("humanType", `selector=${selector}, textLen=${text.length}`);
1205
1546
  const {
1206
1547
  baseDelay = 180,
1207
1548
  pauseProbability = 0.08,
@@ -1225,13 +1566,13 @@ var Humanize = {
1225
1566
  await delay(charDelay);
1226
1567
  if (Math.random() < pauseProbability && i < text.length - 1) {
1227
1568
  const pauseTime = this.jitterMs(pauseBase, 0.5);
1228
- logger5.debug(`\u505C\u987F ${pauseTime}ms...`);
1569
+ logger6.debug(`\u505C\u987F ${pauseTime}ms...`);
1229
1570
  await delay(pauseTime);
1230
1571
  }
1231
1572
  }
1232
- logger5.success("humanType");
1573
+ logger6.success("humanType");
1233
1574
  } catch (error) {
1234
- logger5.fail("humanType", error);
1575
+ logger6.fail("humanType", error);
1235
1576
  throw error;
1236
1577
  }
1237
1578
  },
@@ -1241,22 +1582,22 @@ var Humanize = {
1241
1582
  * @param {string} selector - 输入框选择器
1242
1583
  */
1243
1584
  async humanClear(page, selector) {
1244
- logger5.start("humanClear", `selector=${selector}`);
1585
+ logger6.start("humanClear", `selector=${selector}`);
1245
1586
  try {
1246
1587
  const locator = page.locator(selector);
1247
1588
  await locator.click();
1248
1589
  await delay(this.jitterMs(200, 0.4));
1249
1590
  const currentValue = await locator.inputValue();
1250
1591
  if (!currentValue || currentValue.length === 0) {
1251
- logger5.success("humanClear", "already empty");
1592
+ logger6.success("humanClear", "already empty");
1252
1593
  return;
1253
1594
  }
1254
1595
  await page.keyboard.press("Meta+A");
1255
1596
  await delay(this.jitterMs(100, 0.4));
1256
1597
  await page.keyboard.press("Backspace");
1257
- logger5.success("humanClear");
1598
+ logger6.success("humanClear");
1258
1599
  } catch (error) {
1259
- logger5.fail("humanClear", error);
1600
+ logger6.fail("humanClear", error);
1260
1601
  throw error;
1261
1602
  }
1262
1603
  },
@@ -1268,7 +1609,7 @@ var Humanize = {
1268
1609
  async warmUpBrowsing(page, baseDuration = 3500) {
1269
1610
  const cursor = $GetCursor(page);
1270
1611
  const durationMs = this.jitterMs(baseDuration, 0.4);
1271
- logger5.start("warmUpBrowsing", `duration=${durationMs}ms`);
1612
+ logger6.start("warmUpBrowsing", `duration=${durationMs}ms`);
1272
1613
  const startTime = Date.now();
1273
1614
  const viewportSize = page.viewportSize() || { width: 1920, height: 1080 };
1274
1615
  try {
@@ -1287,9 +1628,9 @@ var Humanize = {
1287
1628
  await delay(this.jitterMs(800, 0.5));
1288
1629
  }
1289
1630
  }
1290
- logger5.success("warmUpBrowsing");
1631
+ logger6.success("warmUpBrowsing");
1291
1632
  } catch (error) {
1292
- logger5.fail("warmUpBrowsing", error);
1633
+ logger6.fail("warmUpBrowsing", error);
1293
1634
  throw error;
1294
1635
  }
1295
1636
  },
@@ -1303,7 +1644,7 @@ var Humanize = {
1303
1644
  async naturalScroll(page, direction = "down", distance = 300, baseSteps = 5) {
1304
1645
  const steps = Math.max(3, baseSteps + Math.floor(Math.random() * 3) - 1);
1305
1646
  const actualDistance = this.jitterMs(distance, 0.15);
1306
- logger5.start("naturalScroll", `dir=${direction}, dist=${actualDistance}, steps=${steps}`);
1647
+ logger6.start("naturalScroll", `dir=${direction}, dist=${actualDistance}, steps=${steps}`);
1307
1648
  const sign = direction === "down" ? 1 : -1;
1308
1649
  const stepDistance = actualDistance / steps;
1309
1650
  try {
@@ -1315,9 +1656,9 @@ var Humanize = {
1315
1656
  const baseDelay = 60 + i * 25;
1316
1657
  await delay(this.jitterMs(baseDelay, 0.3));
1317
1658
  }
1318
- logger5.success("naturalScroll");
1659
+ logger6.success("naturalScroll");
1319
1660
  } catch (error) {
1320
- logger5.fail("naturalScroll", error);
1661
+ logger6.fail("naturalScroll", error);
1321
1662
  throw error;
1322
1663
  }
1323
1664
  }
@@ -1366,693 +1707,10 @@ var findMatchedByPassRule = (rules = [], requestUrl = "") => {
1366
1707
  hostname
1367
1708
  };
1368
1709
  };
1369
- var resolveRouteByProxy = ({
1370
- requestUrl = "",
1371
- enableProxy = false,
1372
- byPassRules = []
1373
- }) => {
1374
- if (!enableProxy) {
1375
- return { route: "direct", matchedRule: null, hostname: "" };
1376
- }
1377
- const matched = findMatchedByPassRule(byPassRules, requestUrl);
1378
- if (!matched) {
1379
- return { route: "proxy", matchedRule: null, hostname: "" };
1380
- }
1381
- if (matched.rule) {
1382
- return { route: "direct", matchedRule: matched.rule, hostname: matched.hostname };
1383
- }
1384
- return { route: "proxy", matchedRule: null, hostname: matched.hostname };
1385
- };
1386
-
1387
- // src/traffic-meter.js
1388
- var logger6 = createInternalLogger("TrafficMeter");
1389
- var encoder = new TextEncoder();
1390
- var MAX_DOMAIN_BUCKETS = 160;
1391
- var MAX_REASON_BUCKETS = 64;
1392
- var MAX_TOP_ITEMS = 12;
1393
- var MAX_HINT_ITEMS = 8;
1394
- var UNKNOWN_DOMAIN = "(unknown)";
1395
- var OTHER_DOMAINS = "(other-domains)";
1396
- var OTHER_REASONS = "(other-reasons)";
1397
- var STATIC_RESOURCE_TYPES = /* @__PURE__ */ new Set([
1398
- "script",
1399
- "stylesheet",
1400
- "image",
1401
- "font",
1402
- "media",
1403
- "manifest"
1404
- ]);
1405
- var BACKEND_RESOURCE_TYPES = /* @__PURE__ */ new Set([
1406
- "xhr",
1407
- "fetch",
1408
- "websocket",
1409
- "eventsource"
1410
- ]);
1411
- var toSafeNumber = (value) => {
1412
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return 0;
1413
- return Math.round(value);
1414
- };
1415
- var byteLength = (value) => {
1416
- const text = String(value || "");
1417
- if (!text) return 0;
1418
- try {
1419
- return encoder.encode(text).length;
1420
- } catch {
1421
- return text.length;
1422
- }
1423
- };
1424
- var normalizeHeaderValue = (value) => {
1425
- if (Array.isArray(value)) return value.join(",");
1426
- if (value === null || value === void 0) return "";
1427
- return String(value);
1428
- };
1429
- var estimateRequestBytes = (request = {}) => {
1430
- const method = String(request.method || "GET");
1431
- const url = String(request.url || "");
1432
- let total = byteLength(`${method} ${url} HTTP/1.1\r
1433
- `);
1434
- const headers = request.headers && typeof request.headers === "object" ? request.headers : {};
1435
- Object.entries(headers).forEach(([key, value]) => {
1436
- total += byteLength(`${key}: ${normalizeHeaderValue(value)}\r
1437
- `);
1438
- });
1439
- total += 2;
1440
- if (typeof request.postData === "string" && request.postData) {
1441
- total += byteLength(request.postData);
1442
- }
1443
- return total;
1444
- };
1445
- var createTrafficState = () => ({
1446
- totalRequests: 0,
1447
- proxyRequests: 0,
1448
- directRequests: 0,
1449
- totalUploadBytes: 0,
1450
- proxyUploadBytes: 0,
1451
- directUploadBytes: 0,
1452
- totalDownloadBytes: 0,
1453
- proxyDownloadBytes: 0,
1454
- directDownloadBytes: 0,
1455
- totalFailedRequests: 0,
1456
- proxyFailedRequests: 0,
1457
- directFailedRequests: 0,
1458
- totalCanceledRequests: 0,
1459
- proxyCanceledRequests: 0,
1460
- directCanceledRequests: 0,
1461
- orphanDataReceivedBytes: 0,
1462
- orphanProxyDataReceivedBytes: 0,
1463
- orphanFinishDeltaBytes: 0,
1464
- orphanProxyFinishDeltaBytes: 0,
1465
- domainStats: /* @__PURE__ */ new Map(),
1466
- typeStats: /* @__PURE__ */ new Map(),
1467
- failedReasonStats: /* @__PURE__ */ new Map()
1468
- });
1469
- var ensureRoute = (route) => route === "proxy" ? "proxy" : "direct";
1470
- var normalizeResourceType = (value) => {
1471
- const type = String(value || "").trim().toLowerCase();
1472
- if (!type) return "other";
1473
- if (type === "ws") return "websocket";
1474
- return type;
1475
- };
1476
- var parseHostname = (url = "") => {
1477
- try {
1478
- const hostname = new URL(String(url || "")).hostname.toLowerCase();
1479
- return hostname || "";
1480
- } catch {
1481
- return "";
1482
- }
1483
- };
1484
- var normalizeDomainKey = (domain = "") => {
1485
- const key = String(domain || "").trim().toLowerCase();
1486
- return key || UNKNOWN_DOMAIN;
1487
- };
1488
- var isStaticType = (resourceType = "") => STATIC_RESOURCE_TYPES.has(normalizeResourceType(resourceType));
1489
- var isBackendType = (resourceType = "") => BACKEND_RESOURCE_TYPES.has(normalizeResourceType(resourceType));
1490
- var createDomainBucket = (domain) => ({
1491
- domain,
1492
- requests: 0,
1493
- proxyRequests: 0,
1494
- directRequests: 0,
1495
- uploadBytes: 0,
1496
- downloadBytes: 0,
1497
- totalBytes: 0,
1498
- proxyBytes: 0,
1499
- directBytes: 0,
1500
- failedRequests: 0,
1501
- canceledRequests: 0,
1502
- staticBytes: 0,
1503
- backendBytes: 0
1504
- });
1505
- var createTypeBucket = (resourceType) => ({
1506
- resourceType,
1507
- requests: 0,
1508
- proxyRequests: 0,
1509
- directRequests: 0,
1510
- uploadBytes: 0,
1511
- downloadBytes: 0,
1512
- totalBytes: 0,
1513
- proxyBytes: 0,
1514
- directBytes: 0,
1515
- failedRequests: 0,
1516
- canceledRequests: 0
1517
- });
1518
- var createReasonBucket = (reason) => ({
1519
- reason,
1520
- count: 0,
1521
- canceledCount: 0,
1522
- proxyCount: 0,
1523
- directCount: 0
1524
- });
1525
- var formatBytes = (bytes = 0) => {
1526
- const value = toSafeNumber(bytes);
1527
- if (value <= 0) return "0B";
1528
- const units = ["B", "KB", "MB", "GB", "TB"];
1529
- let size = value;
1530
- let unit = 0;
1531
- while (size >= 1024 && unit < units.length - 1) {
1532
- size /= 1024;
1533
- unit += 1;
1534
- }
1535
- const precision = size >= 100 || unit === 0 ? 0 : 2;
1536
- return `${size.toFixed(precision)}${units[unit]}`;
1537
- };
1538
- var addRequests = (state, route, count = 1) => {
1539
- const c = toSafeNumber(count);
1540
- if (c <= 0) return;
1541
- const normalizedRoute = ensureRoute(route);
1542
- state.totalRequests += c;
1543
- if (normalizedRoute === "proxy") {
1544
- state.proxyRequests += c;
1545
- return;
1546
- }
1547
- state.directRequests += c;
1548
- };
1549
- var addUploadBytes = (state, route, bytes = 0) => {
1550
- const b = toSafeNumber(bytes);
1551
- if (b <= 0) return;
1552
- const normalizedRoute = ensureRoute(route);
1553
- state.totalUploadBytes += b;
1554
- if (normalizedRoute === "proxy") {
1555
- state.proxyUploadBytes += b;
1556
- return;
1557
- }
1558
- state.directUploadBytes += b;
1559
- };
1560
- var addDownloadBytes = (state, route, bytes = 0) => {
1561
- const b = toSafeNumber(bytes);
1562
- if (b <= 0) return;
1563
- const normalizedRoute = ensureRoute(route);
1564
- state.totalDownloadBytes += b;
1565
- if (normalizedRoute === "proxy") {
1566
- state.proxyDownloadBytes += b;
1567
- return;
1568
- }
1569
- state.directDownloadBytes += b;
1570
- };
1571
- var incrementBucketRequests = (bucket, route) => {
1572
- if (!bucket) return;
1573
- bucket.requests += 1;
1574
- if (ensureRoute(route) === "proxy") {
1575
- bucket.proxyRequests += 1;
1576
- return;
1577
- }
1578
- bucket.directRequests += 1;
1579
- };
1580
- var incrementBucketBytes = (bucket, route, bytes = 0, direction = "download", resourceType = "other") => {
1581
- if (!bucket) return;
1582
- const b = toSafeNumber(bytes);
1583
- if (b <= 0) return;
1584
- if (direction === "upload") {
1585
- bucket.uploadBytes += b;
1586
- } else {
1587
- bucket.downloadBytes += b;
1588
- }
1589
- bucket.totalBytes += b;
1590
- if (ensureRoute(route) === "proxy") {
1591
- bucket.proxyBytes += b;
1592
- } else {
1593
- bucket.directBytes += b;
1594
- }
1595
- if (bucket.domain !== void 0) {
1596
- if (isStaticType(resourceType)) {
1597
- bucket.staticBytes += b;
1598
- } else if (isBackendType(resourceType)) {
1599
- bucket.backendBytes += b;
1600
- }
1601
- }
1602
- };
1603
- var incrementBucketFailed = (bucket, canceled = false) => {
1604
- if (!bucket) return;
1605
- bucket.failedRequests += 1;
1606
- if (canceled) {
1607
- bucket.canceledRequests += 1;
1608
- }
1609
- };
1610
- var ensureReasonText = (reason) => {
1611
- const normalized = String(reason || "").trim().toLowerCase();
1612
- return normalized || "unknown";
1613
- };
1614
- var pickDomainBucket = (state, domain = "") => {
1615
- const domainKey = normalizeDomainKey(domain);
1616
- const knownBucket = state.domainStats.get(domainKey);
1617
- if (knownBucket) return knownBucket;
1618
- if (state.domainStats.size < MAX_DOMAIN_BUCKETS) {
1619
- const bucket2 = createDomainBucket(domainKey);
1620
- state.domainStats.set(domainKey, bucket2);
1621
- return bucket2;
1622
- }
1623
- const overflow = state.domainStats.get(OTHER_DOMAINS);
1624
- if (overflow) return overflow;
1625
- const bucket = createDomainBucket(OTHER_DOMAINS);
1626
- state.domainStats.set(OTHER_DOMAINS, bucket);
1627
- return bucket;
1628
- };
1629
- var pickTypeBucket = (state, resourceType = "other") => {
1630
- const typeKey = normalizeResourceType(resourceType);
1631
- const knownBucket = state.typeStats.get(typeKey);
1632
- if (knownBucket) return knownBucket;
1633
- const bucket = createTypeBucket(typeKey);
1634
- state.typeStats.set(typeKey, bucket);
1635
- return bucket;
1636
- };
1637
- var pickReasonBucket = (state, reason = "") => {
1638
- const reasonKey = ensureReasonText(reason);
1639
- const knownBucket = state.failedReasonStats.get(reasonKey);
1640
- if (knownBucket) return knownBucket;
1641
- if (state.failedReasonStats.size < MAX_REASON_BUCKETS) {
1642
- const bucket2 = createReasonBucket(reasonKey);
1643
- state.failedReasonStats.set(reasonKey, bucket2);
1644
- return bucket2;
1645
- }
1646
- const overflow = state.failedReasonStats.get(OTHER_REASONS);
1647
- if (overflow) return overflow;
1648
- const bucket = createReasonBucket(OTHER_REASONS);
1649
- state.failedReasonStats.set(OTHER_REASONS, bucket);
1650
- return bucket;
1651
- };
1652
- var addRequestProfile = (state, route, domain, resourceType, uploadBytes = 0) => {
1653
- const domainBucket = pickDomainBucket(state, domain);
1654
- const typeBucket = pickTypeBucket(state, resourceType);
1655
- incrementBucketRequests(domainBucket, route);
1656
- incrementBucketRequests(typeBucket, route);
1657
- incrementBucketBytes(domainBucket, route, uploadBytes, "upload", resourceType);
1658
- incrementBucketBytes(typeBucket, route, uploadBytes, "upload", resourceType);
1659
- };
1660
- var addUploadProfile = (state, route, domain, resourceType, uploadBytes = 0) => {
1661
- const domainBucket = pickDomainBucket(state, domain);
1662
- const typeBucket = pickTypeBucket(state, resourceType);
1663
- incrementBucketBytes(domainBucket, route, uploadBytes, "upload", resourceType);
1664
- incrementBucketBytes(typeBucket, route, uploadBytes, "upload", resourceType);
1665
- };
1666
- var addDownloadProfile = (state, route, domain, resourceType, downloadBytes = 0) => {
1667
- const domainBucket = pickDomainBucket(state, domain);
1668
- const typeBucket = pickTypeBucket(state, resourceType);
1669
- incrementBucketBytes(domainBucket, route, downloadBytes, "download", resourceType);
1670
- incrementBucketBytes(typeBucket, route, downloadBytes, "download", resourceType);
1671
- };
1672
- var addFailedProfile = (state, route, domain, resourceType, reason = "unknown", canceled = false) => {
1673
- const domainBucket = pickDomainBucket(state, domain);
1674
- const typeBucket = pickTypeBucket(state, resourceType);
1675
- const reasonBucket = pickReasonBucket(state, reason);
1676
- incrementBucketFailed(domainBucket, canceled);
1677
- incrementBucketFailed(typeBucket, canceled);
1678
- reasonBucket.count += 1;
1679
- if (ensureRoute(route) === "proxy") {
1680
- reasonBucket.proxyCount += 1;
1681
- } else {
1682
- reasonBucket.directCount += 1;
1683
- }
1684
- if (canceled) {
1685
- reasonBucket.canceledCount += 1;
1686
- }
1687
- };
1688
- var toRoundedRatio = (numerator, denominator) => {
1689
- if (denominator <= 0) return 0;
1690
- return Number((numerator / denominator * 100).toFixed(2));
1691
- };
1692
- var sortByBytesAndRequests = (left, right) => {
1693
- if (right.totalBytes !== left.totalBytes) return right.totalBytes - left.totalBytes;
1694
- if (right.requests !== left.requests) return right.requests - left.requests;
1695
- return String(left.domain || left.resourceType || "").localeCompare(String(right.domain || right.resourceType || ""));
1696
- };
1697
- var buildTopDomains = (state) => {
1698
- return Array.from(state.domainStats.values()).filter((item) => item && item.totalBytes > 0).sort(sortByBytesAndRequests).slice(0, MAX_TOP_ITEMS).map((item) => ({
1699
- domain: item.domain,
1700
- requests: item.requests,
1701
- proxyRequests: item.proxyRequests,
1702
- directRequests: item.directRequests,
1703
- uploadBytes: item.uploadBytes,
1704
- downloadBytes: item.downloadBytes,
1705
- totalBytes: item.totalBytes,
1706
- proxyBytes: item.proxyBytes,
1707
- directBytes: item.directBytes,
1708
- failedRequests: item.failedRequests,
1709
- canceledRequests: item.canceledRequests,
1710
- staticBytes: item.staticBytes,
1711
- backendBytes: item.backendBytes
1712
- }));
1713
- };
1714
- var buildTopResourceTypes = (state) => {
1715
- return Array.from(state.typeStats.values()).filter((item) => item && (item.totalBytes > 0 || item.requests > 0)).sort(sortByBytesAndRequests).slice(0, MAX_TOP_ITEMS).map((item) => ({
1716
- resourceType: item.resourceType,
1717
- requests: item.requests,
1718
- proxyRequests: item.proxyRequests,
1719
- directRequests: item.directRequests,
1720
- uploadBytes: item.uploadBytes,
1721
- downloadBytes: item.downloadBytes,
1722
- totalBytes: item.totalBytes,
1723
- proxyBytes: item.proxyBytes,
1724
- directBytes: item.directBytes,
1725
- failedRequests: item.failedRequests,
1726
- canceledRequests: item.canceledRequests
1727
- }));
1728
- };
1729
- var buildFailedReasons = (state) => {
1730
- return Array.from(state.failedReasonStats.values()).filter((item) => item && item.count > 0).sort((left, right) => {
1731
- if (right.count !== left.count) return right.count - left.count;
1732
- return String(left.reason || "").localeCompare(String(right.reason || ""));
1733
- }).slice(0, MAX_TOP_ITEMS).map((item) => ({
1734
- reason: item.reason,
1735
- count: item.count,
1736
- canceledCount: item.canceledCount,
1737
- proxyCount: item.proxyCount,
1738
- directCount: item.directCount
1739
- }));
1740
- };
1741
- var buildProxyByPassHints = (state) => {
1742
- return Array.from(state.domainStats.values()).filter((item) => item && item.domain !== UNKNOWN_DOMAIN && item.domain !== OTHER_DOMAINS).filter((item) => item.proxyBytes >= 128 * 1024 && item.proxyRequests >= 2 && item.totalBytes > 0).filter((item) => item.staticBytes > item.backendBytes && toRoundedRatio(item.staticBytes, item.totalBytes) >= 80).sort((left, right) => {
1743
- if (right.proxyBytes !== left.proxyBytes) return right.proxyBytes - left.proxyBytes;
1744
- return right.proxyRequests - left.proxyRequests;
1745
- }).slice(0, MAX_HINT_ITEMS).map((item) => ({
1746
- domain: item.domain,
1747
- proxyBytes: item.proxyBytes,
1748
- proxyRequests: item.proxyRequests,
1749
- totalBytes: item.totalBytes,
1750
- staticBytes: item.staticBytes,
1751
- backendBytes: item.backendBytes,
1752
- staticRatioPct: toRoundedRatio(item.staticBytes, item.totalBytes)
1753
- }));
1754
- };
1755
- var createTrafficMeter = ({
1756
- enableProxy = false,
1757
- byPassRules = [],
1758
- debugMode = false
1759
- } = {}) => {
1760
- const state = createTrafficState();
1761
- const requestMap = /* @__PURE__ */ new Map();
1762
- const orphanReceivedMap = /* @__PURE__ */ new Map();
1763
- const wsRouteMap = /* @__PURE__ */ new Map();
1764
- const attachedPages = /* @__PURE__ */ new WeakSet();
1765
- const attachedContexts = /* @__PURE__ */ new WeakSet();
1766
- const resolveRoute = (url = "") => {
1767
- return resolveRouteByProxy({
1768
- requestUrl: url,
1769
- enableProxy,
1770
- byPassRules
1771
- }).route;
1772
- };
1773
- const fallbackRoute = () => enableProxy ? "proxy" : "direct";
1774
- const debugLog = (message) => {
1775
- if (!debugMode) return;
1776
- logger6.info(`[\u9010\u8BF7\u6C42\u8C03\u8BD5] ${message}`);
1777
- };
1778
- const addFailed = (route, canceled = false) => {
1779
- const normalizedRoute = ensureRoute(route);
1780
- state.totalFailedRequests += 1;
1781
- if (normalizedRoute === "proxy") {
1782
- state.proxyFailedRequests += 1;
1783
- } else {
1784
- state.directFailedRequests += 1;
1785
- }
1786
- if (!canceled) return;
1787
- state.totalCanceledRequests += 1;
1788
- if (normalizedRoute === "proxy") {
1789
- state.proxyCanceledRequests += 1;
1790
- } else {
1791
- state.directCanceledRequests += 1;
1792
- }
1793
- };
1794
- const finalizeByEncodedLength = (requestId, encodedDataLength, source = "finished") => {
1795
- const safeRequestId = String(requestId || "");
1796
- if (!safeRequestId) return;
1797
- const requestState = requestMap.get(safeRequestId);
1798
- const orphanReceived = toSafeNumber(orphanReceivedMap.get(safeRequestId));
1799
- const encoded = toSafeNumber(encodedDataLength);
1800
- if (requestState) {
1801
- const routed2 = ensureRoute(requestState.route);
1802
- const downloaded = toSafeNumber(requestState.downloadBytes);
1803
- const delta2 = Math.max(0, encoded - downloaded);
1804
- if (delta2 > 0) {
1805
- addDownloadBytes(state, routed2, delta2);
1806
- addDownloadProfile(state, routed2, requestState.domain, requestState.resourceType, delta2);
1807
- requestState.downloadBytes = downloaded + delta2;
1808
- }
1809
- const uploadBytes = toSafeNumber(requestState.uploadBytes);
1810
- const total = uploadBytes + toSafeNumber(requestState.downloadBytes);
1811
- debugLog(
1812
- `final id=${safeRequestId} source=${source} status=ok route=${routed2} type=${requestState.resourceType || "other"} upload=${formatBytes(uploadBytes)} (${uploadBytes}) download=${formatBytes(requestState.downloadBytes)} (${requestState.downloadBytes}) total=${formatBytes(total)} (${total}) url=${requestState.url || "-"}`
1813
- );
1814
- requestMap.delete(safeRequestId);
1815
- orphanReceivedMap.delete(safeRequestId);
1816
- return;
1817
- }
1818
- const routed = fallbackRoute();
1819
- const delta = Math.max(0, encoded - orphanReceived);
1820
- if (delta > 0) {
1821
- addDownloadBytes(state, routed, delta);
1822
- addDownloadProfile(state, routed, UNKNOWN_DOMAIN, "other", delta);
1823
- }
1824
- state.orphanFinishDeltaBytes += delta;
1825
- if (routed === "proxy") {
1826
- state.orphanProxyFinishDeltaBytes += delta;
1827
- }
1828
- debugLog(
1829
- `final id=${safeRequestId} source=${source} status=orphan route=${routed} encoded=${formatBytes(encoded)} (${encoded}) delta=${formatBytes(delta)} (${delta})`
1830
- );
1831
- orphanReceivedMap.delete(safeRequestId);
1832
- };
1833
- const recordRequest = (params = {}) => {
1834
- const requestId = String(params.requestId || "");
1835
- const request = params.request && typeof params.request === "object" ? params.request : {};
1836
- const url = String(request.url || "");
1837
- const route = resolveRoute(url);
1838
- const resourceType = normalizeResourceType(params.type || request.type || "other");
1839
- const domain = normalizeDomainKey(parseHostname(url));
1840
- if (requestId && requestMap.has(requestId)) {
1841
- const redirectResponse = params.redirectResponse && typeof params.redirectResponse === "object" ? params.redirectResponse : null;
1842
- finalizeByEncodedLength(
1843
- requestId,
1844
- redirectResponse ? redirectResponse.encodedDataLength : 0,
1845
- "redirect"
1846
- );
1847
- }
1848
- addRequests(state, route, 1);
1849
- const uploadBytes = estimateRequestBytes(request);
1850
- addUploadBytes(state, route, uploadBytes);
1851
- addRequestProfile(state, route, domain, resourceType, uploadBytes);
1852
- if (requestId) {
1853
- requestMap.set(requestId, {
1854
- route: ensureRoute(route),
1855
- resourceType,
1856
- domain,
1857
- url,
1858
- uploadBytes,
1859
- downloadBytes: 0
1860
- });
1861
- }
1862
- debugLog(
1863
- `request id=${requestId || "-"} route=${route} type=${resourceType} upload=${formatBytes(uploadBytes)} (${uploadBytes}) method=${String(request.method || "GET")} url=${url || "-"}`
1864
- );
1865
- };
1866
- const recordDataReceived = (params = {}) => {
1867
- const requestId = String(params.requestId || "");
1868
- const bytes = toSafeNumber(params.encodedDataLength);
1869
- if (bytes <= 0) return;
1870
- if (!requestId) {
1871
- const routed2 = fallbackRoute();
1872
- addDownloadBytes(state, routed2, bytes);
1873
- addDownloadProfile(state, routed2, UNKNOWN_DOMAIN, "other", bytes);
1874
- state.orphanDataReceivedBytes += bytes;
1875
- if (routed2 === "proxy") {
1876
- state.orphanProxyDataReceivedBytes += bytes;
1877
- }
1878
- return;
1879
- }
1880
- const requestState = requestMap.get(requestId);
1881
- if (requestState) {
1882
- requestState.downloadBytes = toSafeNumber(requestState.downloadBytes) + bytes;
1883
- addDownloadBytes(state, requestState.route, bytes);
1884
- addDownloadProfile(state, requestState.route, requestState.domain, requestState.resourceType, bytes);
1885
- return;
1886
- }
1887
- const prev = toSafeNumber(orphanReceivedMap.get(requestId));
1888
- orphanReceivedMap.set(requestId, prev + bytes);
1889
- const routed = fallbackRoute();
1890
- addDownloadBytes(state, routed, bytes);
1891
- addDownloadProfile(state, routed, UNKNOWN_DOMAIN, "other", bytes);
1892
- state.orphanDataReceivedBytes += bytes;
1893
- if (routed === "proxy") {
1894
- state.orphanProxyDataReceivedBytes += bytes;
1895
- }
1896
- };
1897
- const recordLoadingFinished = (params = {}) => {
1898
- finalizeByEncodedLength(params.requestId, params.encodedDataLength, "loadingFinished");
1899
- };
1900
- const recordRequestFailed = (params = {}) => {
1901
- const requestId = String(params.requestId || "");
1902
- const requestState = requestId ? requestMap.get(requestId) : null;
1903
- const canceled = Boolean(params.canceled);
1904
- const reason = ensureReasonText(params.errorText);
1905
- const routed = ensureRoute(requestState?.route || fallbackRoute());
1906
- const resourceType = normalizeResourceType(requestState?.resourceType || "other");
1907
- const domain = normalizeDomainKey(requestState?.domain || "");
1908
- if (requestState) {
1909
- addFailed(routed, canceled);
1910
- addFailedProfile(state, routed, domain, resourceType, reason, canceled);
1911
- const uploadBytes = toSafeNumber(requestState.uploadBytes);
1912
- const downloadBytes = toSafeNumber(requestState.downloadBytes);
1913
- const totalBytes = uploadBytes + downloadBytes;
1914
- debugLog(
1915
- `final id=${requestId || "-"} source=loadingFailed status=failed route=${routed} type=${requestState.resourceType || "other"} upload=${formatBytes(uploadBytes)} (${uploadBytes}) download=${formatBytes(downloadBytes)} (${downloadBytes}) total=${formatBytes(totalBytes)} (${totalBytes}) canceled=${canceled} reason=${reason} url=${requestState.url || "-"}`
1916
- );
1917
- } else {
1918
- const orphanDownload = toSafeNumber(orphanReceivedMap.get(requestId));
1919
- addFailed(routed, canceled);
1920
- addFailedProfile(state, routed, UNKNOWN_DOMAIN, "other", reason, canceled);
1921
- debugLog(
1922
- `final id=${requestId || "-"} source=loadingFailed status=orphan-failed route=${routed} upload=0B (0) download=${formatBytes(orphanDownload)} (${orphanDownload}) total=${formatBytes(orphanDownload)} (${orphanDownload}) canceled=${canceled} reason=${reason} url=-`
1923
- );
1924
- }
1925
- if (requestId) {
1926
- requestMap.delete(requestId);
1927
- orphanReceivedMap.delete(requestId);
1928
- }
1929
- };
1930
- const bindWebSocketRoute = (params = {}) => {
1931
- const requestId = String(params.requestId || "");
1932
- if (!requestId) return;
1933
- const url = String(params.url || "");
1934
- const route = resolveRoute(url);
1935
- wsRouteMap.set(requestId, { route, url, domain: normalizeDomainKey(parseHostname(url)) });
1936
- };
1937
- const clearWebSocketRoute = (params = {}) => {
1938
- const requestId = String(params.requestId || "");
1939
- if (!requestId) return;
1940
- wsRouteMap.delete(requestId);
1941
- };
1942
- const getWebSocketMeta = (requestId = "") => {
1943
- if (!requestId) return { route: fallbackRoute(), url: "", domain: UNKNOWN_DOMAIN };
1944
- const meta = wsRouteMap.get(requestId);
1945
- if (!meta || typeof meta !== "object") {
1946
- return { route: fallbackRoute(), url: "", domain: UNKNOWN_DOMAIN };
1947
- }
1948
- return {
1949
- route: ensureRoute(meta.route),
1950
- url: String(meta.url || ""),
1951
- domain: normalizeDomainKey(meta.domain)
1952
- };
1953
- };
1954
- const recordWebSocketFrameSent = (params = {}) => {
1955
- const requestId = String(params.requestId || "");
1956
- const { route, url, domain } = getWebSocketMeta(requestId);
1957
- const payload = params.response && typeof params.response === "object" ? params.response.payloadData : "";
1958
- const bytes = byteLength(payload || "");
1959
- addUploadBytes(state, route, bytes);
1960
- addUploadProfile(state, route, domain, "websocket", bytes);
1961
- debugLog(`ws-send id=${requestId || "-"} route=${route} bytes=${formatBytes(bytes)} (${bytes}) url=${url || "-"}`);
1962
- };
1963
- const recordWebSocketFrameReceived = (params = {}) => {
1964
- const requestId = String(params.requestId || "");
1965
- const { route, url, domain } = getWebSocketMeta(requestId);
1966
- const payload = params.response && typeof params.response === "object" ? params.response.payloadData : "";
1967
- const bytes = byteLength(payload || "");
1968
- addDownloadBytes(state, route, bytes);
1969
- addDownloadProfile(state, route, domain, "websocket", bytes);
1970
- debugLog(`ws-recv id=${requestId || "-"} route=${route} bytes=${formatBytes(bytes)} (${bytes}) url=${url || "-"}`);
1971
- };
1972
- const attachPage = async (page) => {
1973
- if (!page || typeof page.context !== "function") return;
1974
- if (attachedPages.has(page)) return;
1975
- attachedPages.add(page);
1976
- try {
1977
- const context = page.context();
1978
- if (!context || typeof context.newCDPSession !== "function") {
1979
- return;
1980
- }
1981
- if (!attachedContexts.has(context) && typeof context.on === "function") {
1982
- attachedContexts.add(context);
1983
- context.on("page", (nextPage) => {
1984
- if (!nextPage) return;
1985
- attachPage(nextPage).catch((error) => {
1986
- logger6.warn(`\u5B50\u9875\u9762 CDP \u76D1\u542C\u6CE8\u518C\u5931\u8D25: ${error?.message || error}`);
1987
- });
1988
- });
1989
- }
1990
- const session = await context.newCDPSession(page);
1991
- await session.send("Network.enable");
1992
- session.on("Network.requestWillBeSent", recordRequest);
1993
- session.on("Network.dataReceived", recordDataReceived);
1994
- session.on("Network.loadingFinished", recordLoadingFinished);
1995
- session.on("Network.loadingFailed", recordRequestFailed);
1996
- session.on("Network.webSocketCreated", bindWebSocketRoute);
1997
- session.on("Network.webSocketClosed", clearWebSocketRoute);
1998
- session.on("Network.webSocketFrameSent", recordWebSocketFrameSent);
1999
- session.on("Network.webSocketFrameReceived", recordWebSocketFrameReceived);
2000
- debugLog("CDP \u76D1\u542C\u5DF2\u6CE8\u518C");
2001
- } catch (error) {
2002
- logger6.warn(`CDP \u76D1\u542C\u6CE8\u518C\u5931\u8D25: ${error?.message || error}`);
2003
- }
2004
- };
2005
- const snapshot = () => {
2006
- const totalBytes = state.totalUploadBytes + state.totalDownloadBytes;
2007
- const proxyBytes = state.proxyUploadBytes + state.proxyDownloadBytes;
2008
- const directBytes = state.directUploadBytes + state.directDownloadBytes;
2009
- return {
2010
- meter: "cdp-data-received-v3",
2011
- totalRequests: state.totalRequests,
2012
- proxyRequests: state.proxyRequests,
2013
- directRequests: state.directRequests,
2014
- totalUploadBytes: state.totalUploadBytes,
2015
- proxyUploadBytes: state.proxyUploadBytes,
2016
- directUploadBytes: state.directUploadBytes,
2017
- totalDownloadBytes: state.totalDownloadBytes,
2018
- proxyDownloadBytes: state.proxyDownloadBytes,
2019
- directDownloadBytes: state.directDownloadBytes,
2020
- totalBytes,
2021
- proxyBytes,
2022
- directBytes,
2023
- totalFailedRequests: state.totalFailedRequests,
2024
- proxyFailedRequests: state.proxyFailedRequests,
2025
- directFailedRequests: state.directFailedRequests,
2026
- totalCanceledRequests: state.totalCanceledRequests,
2027
- proxyCanceledRequests: state.proxyCanceledRequests,
2028
- directCanceledRequests: state.directCanceledRequests,
2029
- orphanDataReceivedBytes: state.orphanDataReceivedBytes,
2030
- orphanProxyDataReceivedBytes: state.orphanProxyDataReceivedBytes,
2031
- orphanFinishDeltaBytes: state.orphanFinishDeltaBytes,
2032
- orphanProxyFinishDeltaBytes: state.orphanProxyFinishDeltaBytes,
2033
- openRequestCount: requestMap.size,
2034
- orphanOpenCount: orphanReceivedMap.size,
2035
- topDomains: buildTopDomains(state),
2036
- topResourceTypes: buildTopResourceTypes(state),
2037
- failedReasons: buildFailedReasons(state),
2038
- proxyBypassHints: buildProxyByPassHints(state)
2039
- };
2040
- };
2041
- const reset = () => {
2042
- Object.assign(state, createTrafficState());
2043
- requestMap.clear();
2044
- orphanReceivedMap.clear();
2045
- wsRouteMap.clear();
2046
- };
2047
- return {
2048
- attachPage,
2049
- snapshot,
2050
- reset
2051
- };
2052
- };
2053
1710
 
2054
1711
  // src/launch.js
2055
1712
  var logger7 = createInternalLogger("Launch");
1713
+ var REQUEST_HOOK_FLAG = Symbol("playwright-toolkit-request-hook");
2056
1714
  var DEFAULT_CRAWLER_BASE_OPTIONS = Object.freeze({
2057
1715
  maxConcurrency: 1,
2058
1716
  maxRequestRetries: 0,
@@ -2064,24 +1722,10 @@ var resolveProxyLaunchOptions = (proxyConfiguration = {}) => {
2064
1722
  const proxyUrl = String(config.proxy_url || "").trim();
2065
1723
  const enableProxy = typeof config.enable_proxy === "boolean" ? config.enable_proxy : proxyUrl !== "";
2066
1724
  if (!enableProxy || !proxyUrl) {
2067
- return { launchProxy: null, byPassDomains: [], enableProxy, proxyUrl };
1725
+ return { byPassDomains: [], enableProxy, proxyUrl };
2068
1726
  }
2069
1727
  const byPassDomains = normalizeByPassDomains(config.by_pass_domains);
2070
- let parsedProxyUrl;
2071
- parsedProxyUrl = new URL(proxyUrl);
2072
- const launchProxy = {
2073
- server: `${parsedProxyUrl.protocol}//${parsedProxyUrl.host}`
2074
- };
2075
- if (parsedProxyUrl.username) {
2076
- launchProxy.username = decodeURIComponent(parsedProxyUrl.username);
2077
- }
2078
- if (parsedProxyUrl.password) {
2079
- launchProxy.password = decodeURIComponent(parsedProxyUrl.password);
2080
- }
2081
- if (byPassDomains.length > 0) {
2082
- launchProxy.bypass = byPassDomains.join(",");
2083
- }
2084
- return { launchProxy, byPassDomains, enableProxy, proxyUrl };
1728
+ return { byPassDomains, enableProxy, proxyUrl };
2085
1729
  };
2086
1730
  var Launch = {
2087
1731
  getPlaywrightCrawlerOptions(options = {}) {
@@ -2097,14 +1741,13 @@ var Launch = {
2097
1741
  preNavigationHooks = [],
2098
1742
  postNavigationHooks = []
2099
1743
  } = normalizedOptions;
2100
- const { launchProxy, byPassDomains, enableProxy, proxyUrl } = resolveProxyLaunchOptions(proxyConfiguration);
1744
+ const { byPassDomains, enableProxy, proxyUrl } = resolveProxyLaunchOptions(proxyConfiguration);
2101
1745
  const byPassRules = buildByPassDomainRules(byPassDomains);
2102
- const trafficMeter2 = createTrafficMeter({
2103
- enableProxy: Boolean(launchProxy),
2104
- byPassRules,
2105
- debugMode: Boolean(debugMode)
2106
- });
2107
- setTrafficMeter(trafficMeter2);
1746
+ const proxyMeter = enableProxy && proxyUrl ? startProxyMeter({ proxyUrl, debugMode }) : null;
1747
+ const launchProxy = proxyMeter ? { server: proxyMeter.server } : null;
1748
+ if (launchProxy && byPassDomains.length > 0) {
1749
+ launchProxy.bypass = byPassDomains.join(",");
1750
+ }
2108
1751
  const launchOptions = {
2109
1752
  args: [
2110
1753
  ...AntiCheat.getLaunchArgs(),
@@ -2117,8 +1760,14 @@ var Launch = {
2117
1760
  }
2118
1761
  const enableByPassLogger = Boolean(logOptions && logOptions.enable);
2119
1762
  if (enableByPassLogger && launchProxy) {
1763
+ let upstreamLabel = "";
1764
+ try {
1765
+ const parsedProxyUrl = new URL(proxyUrl);
1766
+ upstreamLabel = `${parsedProxyUrl.protocol}//${parsedProxyUrl.host}`;
1767
+ } catch {
1768
+ }
2120
1769
  logger7.info(
2121
- `[\u4EE3\u7406\u5DF2\u542F\u7528] \u4EE3\u7406\u670D\u52A1=${launchProxy.server} \u76F4\u8FDE\u57DF\u540D=${(byPassDomains || []).join(",")}`
1770
+ `[\u4EE3\u7406\u5DF2\u542F\u7528] \u672C\u5730=${launchProxy.server} \u4E0A\u6E38=${upstreamLabel || "-"} \u76F4\u8FDE\u57DF\u540D=${(byPassDomains || []).join(",")}`
2122
1771
  );
2123
1772
  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`);
2124
1773
  } else if (enableByPassLogger && enableProxy && !launchProxy) {
@@ -2138,17 +1787,23 @@ var Launch = {
2138
1787
  const recommendedGotoOptions = {
2139
1788
  waitUntil: "commit"
2140
1789
  };
2141
- if (page && typeof page.on === "function") {
2142
- trafficMeter2.attachPage(page);
1790
+ if (!page || typeof page.on !== "function") {
1791
+ return recommendedGotoOptions;
2143
1792
  }
2144
- if (!enableByPassLogger || byPassDomains.length === 0 || !page || typeof page.on !== "function") {
1793
+ if (page[REQUEST_HOOK_FLAG]) {
2145
1794
  return recommendedGotoOptions;
2146
1795
  }
1796
+ page[REQUEST_HOOK_FLAG] = true;
2147
1797
  const requestHandler = (req) => {
2148
1798
  const requestUrl = req.url();
1799
+ const resourceType = req.resourceType();
1800
+ if (launchProxy) {
1801
+ recordProxyMeterResourceType(requestUrl, resourceType);
1802
+ }
1803
+ if (!enableByPassLogger || byPassDomains.length === 0) return;
2149
1804
  const matched = findMatchedByPassRule(byPassRules, requestUrl);
2150
1805
  if (!matched || !matched.rule) return;
2151
- logger7.info(`[\u76F4\u8FDE\u547D\u4E2D] \u89C4\u5219=${matched.rule.pattern} \u57DF\u540D=${matched.hostname} \u8D44\u6E90\u7C7B\u578B=${req.resourceType()} \u65B9\u6CD5=${req.method()} \u5730\u5740=${requestUrl}`);
1806
+ logger7.info(`[\u76F4\u8FDE\u547D\u4E2D] \u89C4\u5219=${matched.rule.pattern} \u57DF\u540D=${matched.hostname} \u8D44\u6E90\u7C7B\u578B=${resourceType} \u65B9\u6CD5=${req.method()} \u5730\u5740=${requestUrl}`);
2152
1807
  };
2153
1808
  page.on("request", requestHandler);
2154
1809
  return recommendedGotoOptions;
@@ -2833,10 +2488,10 @@ var Mutation = {
2833
2488
  let text = "";
2834
2489
  let html = "";
2835
2490
  let source = "main";
2836
- let path = `${selector}[${index}]`;
2491
+ let path2 = `${selector}[${index}]`;
2837
2492
  if (isIframe) {
2838
2493
  source = "iframe";
2839
- path = `${selector}[${index}]::iframe(${safeFrameId(node)})`;
2494
+ path2 = `${selector}[${index}]::iframe(${safeFrameId(node)})`;
2840
2495
  try {
2841
2496
  const frameDoc = node.contentDocument;
2842
2497
  const frameRoot = frameDoc?.body || frameDoc?.documentElement;
@@ -2854,7 +2509,7 @@ var Mutation = {
2854
2509
  items.push({
2855
2510
  selector,
2856
2511
  source,
2857
- path,
2512
+ path: path2,
2858
2513
  text,
2859
2514
  html,
2860
2515
  snapshot