@wenathlan/extension 1.1.46 → 1.1.48

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.
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
4
4
  "name": "Devthink",
5
- "version": "1.1.46",
5
+ "version": "1.1.48",
6
6
  "description": "A consent-first bridge for reviewed browser-agent tasks.",
7
7
  "permissions": [
8
8
  "activeTab",
@@ -32,6 +32,106 @@
32
32
  return { urlpattern, source };
33
33
  }
34
34
 
35
+ // emulation.ts
36
+ var browserpermissions = ["geolocation", "notifications", "camera", "microphone", "clipboard-read", "clipboard-write", "midi", "persistent-storage"];
37
+ var permissionstates = ["granted", "denied", "prompt"];
38
+ function familyofkind(kind) {
39
+ if (kind === "emulatedevice") return "device";
40
+ if (kind === "emulatenetwork") return "network";
41
+ if (kind === "emulatelocate") return "location";
42
+ if (kind === "setuseragent") return "agent";
43
+ if (kind === "overridepermission") return "permission";
44
+ if (kind === "blackboxscripts") return "blackbox";
45
+ return void 0;
46
+ }
47
+ function devicepresetof(value) {
48
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
49
+ const entry = value;
50
+ const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
51
+ const width = typeof entry.width === "number" && Number.isInteger(entry.width) && entry.width > 0 ? entry.width : void 0;
52
+ const height = typeof entry.height === "number" && Number.isInteger(entry.height) && entry.height > 0 ? entry.height : void 0;
53
+ const pixelratio = typeof entry.pixelratio === "number" && Number.isFinite(entry.pixelratio) && entry.pixelratio > 0 ? entry.pixelratio : void 0;
54
+ if (name === void 0 || width === void 0 || height === void 0 || pixelratio === void 0) return void 0;
55
+ return { name, width, height, pixelratio, mobile: entry.mobile === true };
56
+ }
57
+ function networkpresetof(value) {
58
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
59
+ const entry = value;
60
+ const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
61
+ const latency = typeof entry.latency === "number" && Number.isFinite(entry.latency) && entry.latency >= 0 ? entry.latency : void 0;
62
+ const download = typeof entry.download === "number" && Number.isFinite(entry.download) && entry.download >= 0 ? entry.download : void 0;
63
+ const upload = typeof entry.upload === "number" && Number.isFinite(entry.upload) && entry.upload >= 0 ? entry.upload : void 0;
64
+ if (name === void 0 || latency === void 0 || download === void 0 || upload === void 0) return void 0;
65
+ return { name, latency, download, upload, offline: entry.offline === true };
66
+ }
67
+ function locationpresetof(value) {
68
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
69
+ const entry = value;
70
+ const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
71
+ const latitude = typeof entry.latitude === "number" && Number.isFinite(entry.latitude) ? entry.latitude : void 0;
72
+ const longitude = typeof entry.longitude === "number" && Number.isFinite(entry.longitude) ? entry.longitude : void 0;
73
+ const accuracy = typeof entry.accuracy === "number" && Number.isFinite(entry.accuracy) && entry.accuracy >= 0 ? entry.accuracy : void 0;
74
+ if (name === void 0 || latitude === void 0 || longitude === void 0 || accuracy === void 0) return void 0;
75
+ if (!locationrangevalid(latitude, longitude)) return void 0;
76
+ return { name, latitude, longitude, accuracy };
77
+ }
78
+ function agentpresetof(value) {
79
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
80
+ const entry = value;
81
+ const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
82
+ const useragent = typeof entry.useragent === "string" ? entry.useragent : void 0;
83
+ const platform = typeof entry.platform === "string" && entry.platform.trim() ? entry.platform.trim() : void 0;
84
+ const brands = Array.isArray(entry.brands) ? entry.brands.filter((brand) => typeof brand === "string" && brand.trim().length > 0) : [];
85
+ if (name === void 0 || useragent === void 0 || platform === void 0 || brands.length === 0) return void 0;
86
+ if (!agentgrammarvalid(useragent)) return void 0;
87
+ return { name, useragent, platform, brands: [...new Set(brands)] };
88
+ }
89
+ function permissiongrantof(value) {
90
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
91
+ const entry = value;
92
+ const name = typeof entry.name === "string" && browserpermissions.includes(entry.name) ? entry.name : void 0;
93
+ const state = typeof entry.state === "string" && permissionstates.includes(entry.state) ? entry.state : void 0;
94
+ if (name === void 0 || state === void 0) return void 0;
95
+ return { name, state, runscope: entry.runscope !== false };
96
+ }
97
+ function blackboxruleof(value) {
98
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
99
+ const entry = value;
100
+ const urlpatterns = Array.isArray(entry.urlpatterns) ? entry.urlpatterns.filter((pattern) => typeof pattern === "string" && /^https:\/\//.test(pattern)) : [];
101
+ const tracescope = entry.tracescope;
102
+ if (urlpatterns.length === 0) return void 0;
103
+ if (tracescope !== "profiles" && tracescope !== "traces" && tracescope !== "both") return void 0;
104
+ return { urlpatterns: [...new Set(urlpatterns)], tracescope };
105
+ }
106
+ function locationrangevalid(latitude, longitude) {
107
+ return Number.isFinite(latitude) && Number.isFinite(longitude) && latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180;
108
+ }
109
+ function agentgrammarvalid(useragent) {
110
+ const text = useragent.trim();
111
+ if (text.length === 0 || text.length > 512) return false;
112
+ if (/[\r\n]/.test(text)) return false;
113
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+\-()/:; ,]*$/.test(text)) return false;
114
+ return /\/\d/.test(text) || /\d+\.\d+/.test(text);
115
+ }
116
+ function blackboxmatches(urlpattern, url) {
117
+ const patternmatch = /^(https:\/\/[^/]+)(\/.*)?$/.exec(urlpattern);
118
+ const urlmatch = /^(https:\/\/[^/]+)(\/.*)?$/.exec(url);
119
+ if (!patternmatch || !urlmatch) return false;
120
+ if (patternmatch[1] !== urlmatch[1]) return false;
121
+ const patternpath = (patternmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
122
+ const urlpath = (urlmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
123
+ const walk = (patternindex, urlindex) => {
124
+ if (patternindex >= patternpath.length) return urlindex >= urlpath.length;
125
+ const segment = patternpath[patternindex];
126
+ if (segment === void 0) return false;
127
+ if (segment === "**") return walk(patternindex + 1, urlindex) || urlindex < urlpath.length && walk(patternindex, urlindex + 1);
128
+ if (urlindex >= urlpath.length) return false;
129
+ if (segment !== "*" && segment !== urlpath[urlindex]) return false;
130
+ return walk(patternindex + 1, urlindex + 1);
131
+ };
132
+ return walk(0, 0);
133
+ }
134
+
35
135
  // runtimeline.ts
36
136
  var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
37
137
  function levelrank(level) {
@@ -120,9 +220,9 @@
120
220
  }
121
221
 
122
222
  // policy.ts
123
- var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript"]);
223
+ var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission"]);
124
224
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr"]);
125
- var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp"]);
225
+ var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts"]);
126
226
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
127
227
  function parseoptions(step) {
128
228
  if (step.options === void 0) return {};
@@ -2477,6 +2577,185 @@
2477
2577
  }
2478
2578
  }
2479
2579
 
2580
+ // extension/pageemulate.ts
2581
+ function registry() {
2582
+ const holder = globalThis;
2583
+ if (holder.devthinkemulation === void 0) holder.devthinkemulation = {};
2584
+ return holder.devthinkemulation;
2585
+ }
2586
+ function priorsnapshot(family) {
2587
+ if (family === "device") return { pixelratio: window.devicePixelRatio, viewportwidth: window.innerWidth, viewportheight: window.innerHeight };
2588
+ if (family === "agent") return { useragent: navigator.userAgent, platform: navigator.platform };
2589
+ if (family === "permission") return { note: "the browser permission state stays untouched and the override restores by removing the page-side answer" };
2590
+ return { note: "the layer adds no prior page state to restore" };
2591
+ }
2592
+ function applydevice(width, height, pixelratio, mobile) {
2593
+ const state = registry();
2594
+ if (state.pixelratio === void 0) state.pixelratio = window.devicePixelRatio;
2595
+ Object.defineProperty(window, "devicePixelRatio", { configurable: true, get: () => pixelratio });
2596
+ document.documentElement.dataset.devthinkMobile = mobile ? "true" : "false";
2597
+ document.documentElement.dataset.devthinkViewport = `${width}x${height}`;
2598
+ }
2599
+ function revertdevice(prior) {
2600
+ const state = registry();
2601
+ const restored = typeof prior?.pixelratio === "number" ? prior.pixelratio : state.pixelratio ?? window.devicePixelRatio;
2602
+ Object.defineProperty(window, "devicePixelRatio", { configurable: true, get: () => restored });
2603
+ delete document.documentElement.dataset.devthinkMobile;
2604
+ delete document.documentElement.dataset.devthinkViewport;
2605
+ delete state.pixelratio;
2606
+ }
2607
+ function applylocation(latitude, longitude, accuracy) {
2608
+ const state = registry();
2609
+ if (state.geolocation === void 0) state.geolocation = navigator.geolocation;
2610
+ const position = () => ({
2611
+ coords: { latitude, longitude, accuracy, altitude: null, altitudeAccuracy: null, heading: null, speed: null },
2612
+ timestamp: Date.now()
2613
+ });
2614
+ const overridden = {
2615
+ getCurrentPosition: (success) => {
2616
+ success(position());
2617
+ },
2618
+ watchPosition: (success) => {
2619
+ success(position());
2620
+ return 0;
2621
+ },
2622
+ clearWatch: () => {
2623
+ }
2624
+ };
2625
+ Object.defineProperty(navigator, "geolocation", { configurable: true, get: () => overridden });
2626
+ }
2627
+ function revertlocation() {
2628
+ const state = registry();
2629
+ if (state.geolocation !== void 0) Object.defineProperty(navigator, "geolocation", { configurable: true, get: () => state.geolocation });
2630
+ delete state.geolocation;
2631
+ }
2632
+ function applyagent(useragent, platform, brands) {
2633
+ const state = registry();
2634
+ if (state.useragent === void 0) state.useragent = navigator.userAgent;
2635
+ if (state.platform === void 0) state.platform = navigator.platform;
2636
+ if (state.brands === void 0) state.brands = brands;
2637
+ Object.defineProperty(navigator, "userAgent", { configurable: true, get: () => useragent });
2638
+ Object.defineProperty(navigator, "platform", { configurable: true, get: () => platform });
2639
+ const branded = brands.map((brand, index) => ({ brand, version: `${index + 1}.0.0.0` }));
2640
+ const dataholder = navigator;
2641
+ if (dataholder.userAgentData !== void 0) Object.defineProperty(dataholder, "userAgentData", { configurable: true, get: () => ({ brands: branded }) });
2642
+ }
2643
+ function revertagent(prior) {
2644
+ const state = registry();
2645
+ const useragent = typeof prior?.useragent === "string" ? prior.useragent : state.useragent ?? navigator.userAgent;
2646
+ const platform = typeof prior?.platform === "string" ? prior.platform : state.platform ?? navigator.platform;
2647
+ Object.defineProperty(navigator, "userAgent", { configurable: true, get: () => useragent });
2648
+ Object.defineProperty(navigator, "platform", { configurable: true, get: () => platform });
2649
+ delete state.useragent;
2650
+ delete state.platform;
2651
+ delete state.brands;
2652
+ }
2653
+ function applypermission(name, state) {
2654
+ const holder = navigator;
2655
+ if (holder.devthinkpermission === void 0) holder.devthinkpermission = {};
2656
+ holder.devthinkpermission[name] = state;
2657
+ const state0 = registry();
2658
+ if (state0.permissions === void 0 && navigator.permissions !== void 0) state0.permissions = navigator.permissions;
2659
+ if (navigator.permissions === void 0) return;
2660
+ const overridden = {
2661
+ query: (description) => new Promise((resolve) => {
2662
+ const applied = holder.devthinkpermission?.[description.name];
2663
+ resolve({ state: applied ?? "prompt", name: description.name, onchange: null });
2664
+ })
2665
+ };
2666
+ Object.defineProperty(navigator, "permissions", { configurable: true, get: () => overridden });
2667
+ }
2668
+ function revertpermission() {
2669
+ const state = registry();
2670
+ if (state.permissions !== void 0) Object.defineProperty(navigator, "permissions", { configurable: true, get: () => state.permissions });
2671
+ delete state.permissions;
2672
+ delete navigator.devthinkpermission;
2673
+ }
2674
+ function applyblackbox(patterns) {
2675
+ registry().blackbox = patterns;
2676
+ }
2677
+ function activeblackboxpatterns() {
2678
+ return registry().blackbox ?? [];
2679
+ }
2680
+ async function runemulationstep(step) {
2681
+ const options = (() => {
2682
+ try {
2683
+ return parseoptions(step);
2684
+ } catch {
2685
+ return {};
2686
+ }
2687
+ })();
2688
+ const family = familyofkind(step.kind);
2689
+ const derivation = "The mask is a page-injected override through the scripting api; the browser device metrics, network stack, true location, request headers and permission state stay untouched because no debugger or platform permission exists in the manifest.";
2690
+ if (step.kind === "emulatedevice") {
2691
+ const preset = devicepresetof(options.device);
2692
+ if (!preset) return { ok: false, summary: "The reviewed device preset is absent or malformed." };
2693
+ const prior = priorsnapshot("device");
2694
+ applydevice(preset.width, preset.height, preset.pixelratio, preset.mobile);
2695
+ return { ok: true, summary: `Applied the device preset ${preset.name} of ${preset.width} by ${preset.height} css pixels, pixel ratio ${preset.pixelratio} and the ${preset.mobile ? "mobile" : "desktop"} hint to the run tab.`, details: { prior, preset: { name: preset.name, width: preset.width, height: preset.height, pixelratio: preset.pixelratio, mobile: preset.mobile }, derivation } };
2696
+ }
2697
+ if (step.kind === "emulatenetwork") {
2698
+ const preset = networkpresetof(options.network);
2699
+ if (!preset) return { ok: false, summary: "The reviewed network preset is absent or malformed." };
2700
+ const window0 = typeof options.window === "number" ? options.window : void 0;
2701
+ return { ok: true, summary: `Applied the network preset ${preset.name} with ${preset.latency} milliseconds latency, ${preset.download} and ${preset.upload} kilobit per second bounds${preset.offline ? ` and the offline flag${window0 !== void 0 ? ` for the reviewed window of ${window0} milliseconds` : ""}` : ""}; the bounds shape the traffic the extension itself initiates.`, details: { preset: { name: preset.name, latency: preset.latency, download: preset.download, upload: preset.upload, offline: preset.offline }, ...window0 !== void 0 ? { window: window0 } : {}, derivation } };
2702
+ }
2703
+ if (step.kind === "emulatelocate") {
2704
+ const preset = locationpresetof(options.location);
2705
+ if (!preset) return { ok: false, summary: "The reviewed location preset is absent or malformed." };
2706
+ applylocation(preset.latitude, preset.longitude, preset.accuracy);
2707
+ return { ok: true, summary: `Applied the location preset ${preset.name} of ${preset.latitude}, ${preset.longitude} with the ${preset.accuracy} meter accuracy radius to the run tab.`, details: { preset: { name: preset.name, latitude: preset.latitude, longitude: preset.longitude, accuracy: preset.accuracy }, derivation } };
2708
+ }
2709
+ if (step.kind === "setuseragent") {
2710
+ const preset = agentpresetof(options.agent);
2711
+ if (!preset) return { ok: false, summary: "The reviewed agent preset is absent or malformed." };
2712
+ const prior = priorsnapshot("agent");
2713
+ applyagent(preset.useragent, preset.platform, preset.brands);
2714
+ return { ok: true, summary: `Applied the agent preset ${preset.name} with the reviewed user agent string, platform ${preset.platform} and ${preset.brands.length} brand${preset.brands.length === 1 ? "" : "s"} together, scoped to the run tab only.`, details: { prior, preset: { name: preset.name, platform: preset.platform, brands: preset.brands }, derivation } };
2715
+ }
2716
+ if (step.kind === "overridepermission") {
2717
+ const grant = permissiongrantof(options.permission);
2718
+ if (!grant) return { ok: false, summary: "The reviewed permission override is absent or malformed." };
2719
+ const prior = priorsnapshot("permission");
2720
+ applypermission(grant.name, grant.state);
2721
+ return { ok: true, summary: `Answered the ${grant.name} permission queries of the run tab with the reviewed ${grant.state} state${grant.runscope ? " for the run scope" : ""}; the browser permission itself stays untouched.`, details: { prior, permission: { name: grant.name, state: grant.state, runscope: grant.runscope }, derivation } };
2722
+ }
2723
+ if (step.kind === "blackboxscripts") {
2724
+ const rules = (Array.isArray(options.rules) ? options.rules : []).flatMap((rule) => {
2725
+ const parsed = blackboxruleof(rule);
2726
+ return parsed !== void 0 ? [parsed] : [];
2727
+ });
2728
+ if (rules.length === 0) return { ok: false, summary: "The reviewed blackbox rule list is absent or malformed." };
2729
+ applyblackbox(rules.flatMap((rule) => rule.urlpatterns));
2730
+ return { ok: true, summary: `Marked ${rules.flatMap((rule) => rule.urlpatterns).length} third party url pattern${rules.flatMap((rule) => rule.urlpatterns).length === 1 ? "" : "s"} as blackboxed in the traces of the run; the rules read no page state.`, details: { rules, derivation: "Blackbox rules shape stack traces and profiles of the run only; they read no page state and touch no third party script." } };
2731
+ }
2732
+ void family;
2733
+ return { ok: false, summary: "The emulation step is not part of the mask family." };
2734
+ }
2735
+ function revertemulationlayer(family, prior) {
2736
+ if (family === "device") {
2737
+ revertdevice(prior);
2738
+ return { ok: true, summary: "Restored the prior pixel ratio and cleared the device hint of the run tab." };
2739
+ }
2740
+ if (family === "location") {
2741
+ revertlocation();
2742
+ return { ok: true, summary: "Restored the true navigator geolocation of the run tab." };
2743
+ }
2744
+ if (family === "agent") {
2745
+ revertagent(prior);
2746
+ return { ok: true, summary: "Restored the true navigator user agent, platform and brand list of the run tab." };
2747
+ }
2748
+ if (family === "permission") {
2749
+ revertpermission();
2750
+ return { ok: true, summary: "Removed the page-side permission answers so the browser permission state returns." };
2751
+ }
2752
+ if (family === "blackbox") {
2753
+ delete registry().blackbox;
2754
+ return { ok: true, summary: "Removed the blackbox pattern registry of the run." };
2755
+ }
2756
+ return { ok: true, summary: "The network layer holds no page state to restore; the transport bounds ended with the run." };
2757
+ }
2758
+
2480
2759
  // extension/pagedebug.ts
2481
2760
  var harnesskey = "__devthinkcdp";
2482
2761
  function readharness() {
@@ -2544,7 +2823,9 @@
2544
2823
  }
2545
2824
  }
2546
2825
  writeharness(harness);
2547
- return { ok: true, summary: `Attached the instrumented devtools harness with the reviewed domains ${options.domains.join(", ")} enabled.`, details: { attached: true, domains: [...harness.domains], derivation: "The chrome devtools protocol needs the debugger permission, which the manifest gate forbids; the session runs through the page-instrumented harness injected by the scripting api." } };
2826
+ const iframes = [...document.querySelectorAll("iframe[src]")].map((frame) => frame.src).filter((src) => src.startsWith("https://"));
2827
+ const serviceworker = "serviceWorker" in navigator && navigator.serviceWorker.controller ? navigator.serviceWorker.controller.scriptURL : void 0;
2828
+ return { ok: true, summary: `Attached the instrumented devtools harness with the reviewed domains ${options.domains.join(", ")} enabled.`, details: { attached: true, domains: [...harness.domains], targets: { iframes, ...serviceworker !== void 0 ? { serviceworker } : {} }, derivation: "The chrome devtools protocol needs the debugger permission, which the manifest gate forbids; the session runs through the page-instrumented harness injected by the scripting api and the iframe and service worker targets derive from the page frame list and controller state." } };
2548
2829
  }
2549
2830
  if (step.kind === "detachcdp") {
2550
2831
  const harness = readharness();
@@ -2734,14 +3015,16 @@
2734
3015
  }
2735
3016
  }
2736
3017
  if (step.kind === "watcherrors") {
3018
+ const blackbox = activeblackboxpatterns();
3019
+ const hideframes = (record) => ({ ...record, frames: record.frames.filter((frame) => !blackbox.some((pattern) => blackboxmatches(pattern, frame.url))) });
2737
3020
  const onerror = (event) => {
2738
- const record = errorcapture({ message: event.message, sourceurl: event.filename, line: event.lineno, ...event.error instanceof Error ? { stacktext: event.error.stack } : {}, redact: options.redact });
3021
+ const record = hideframes(errorcapture({ message: event.message, sourceurl: event.filename, line: event.lineno, ...event.error instanceof Error ? { stacktext: event.error.stack } : {}, redact: options.redact }));
2739
3022
  errors.push({ ...record, stepid: step.id, at: Date.now() });
2740
3023
  capture("error", "error", record.message, Date.now());
2741
3024
  };
2742
3025
  const onrejection = (event) => {
2743
3026
  const reason = event.reason instanceof Error ? `${event.reason.name}: ${event.reason.message}` : String(event.reason);
2744
- const record = rejectioncapture({ reason, ...event.reason instanceof Error ? { stacktext: event.reason.stack } : {}, redact: options.redact });
3027
+ const record = hideframes(rejectioncapture({ reason, ...event.reason instanceof Error ? { stacktext: event.reason.stack } : {}, redact: options.redact }));
2745
3028
  rejections.push({ ...record, stepid: step.id, at: Date.now() });
2746
3029
  capture("error", "rejection", record.reason, Date.now());
2747
3030
  };
@@ -2786,6 +3069,143 @@
2786
3069
  return { ok: true, summary, details: { entries, console: consoleentries, errors, rejections, resources, longtasks, watchwindow: options.window, depth: options.depth, derivation: "Console, error and task watching derives from page-injected listeners and the performance buffers through the scripting api; no debugger permission exists in the manifest." } };
2787
3070
  }
2788
3071
 
3072
+ // extension/pageprofile.ts
3073
+ function profilestepoptions(step) {
3074
+ let options = {};
3075
+ try {
3076
+ options = parseoptions(step);
3077
+ } catch {
3078
+ options = {};
3079
+ }
3080
+ const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
3081
+ const flow = options.flow && typeof options.flow === "object" && !Array.isArray(options.flow) ? options.flow : void 0;
3082
+ const heap = options.heap && typeof options.heap === "object" && !Array.isArray(options.heap) ? options.heap : {};
3083
+ const growth = options.growth && typeof options.growth === "object" && !Array.isArray(options.growth) ? options.growth : void 0;
3084
+ const profile = options.profile && typeof options.profile === "object" && !Array.isArray(options.profile) ? options.profile : {};
3085
+ const trace = options.trace && typeof options.trace === "object" && !Array.isArray(options.trace) ? options.trace : {};
3086
+ return {
3087
+ ...flow !== void 0 && typeof flow.prefix === "string" && Array.isArray(flow.steps) && Array.isArray(flow.metrics) ? { flow: { prefix: flow.prefix, steps: flow.steps.filter((item) => typeof item === "string"), metrics: flow.metrics.filter((item) => typeof item === "string") } } : {},
3088
+ watchwindow: typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0,
3089
+ heapinterval: typeof heap.interval === "number" && Number.isFinite(heap.interval) && heap.interval >= 0 ? heap.interval : 0,
3090
+ ...growth !== void 0 && typeof growth.slope === "number" ? { growth: { slope: growth.slope, interval: typeof growth.interval === "number" && Number.isFinite(growth.interval) && growth.interval >= 0 ? growth.interval : 0 } } : {},
3091
+ duration: typeof profile.duration === "number" && Number.isFinite(profile.duration) && profile.duration >= 0 ? profile.duration : 0,
3092
+ threshold: typeof options.threshold === "number" && Number.isFinite(options.threshold) && options.threshold >= 0 ? options.threshold : 0,
3093
+ categories: Array.isArray(trace.categories) ? trace.categories.filter((category) => typeof category === "string") : [],
3094
+ ...typeof trace.exporttarget === "string" ? { exporttarget: trace.exporttarget } : {},
3095
+ ...typeof trace.traceid === "string" ? { traceid: trace.traceid } : {},
3096
+ scripts: Array.isArray(options.scripts) ? options.scripts.filter((url) => typeof url === "string") : []
3097
+ };
3098
+ }
3099
+ function categoryof(entry, initiator) {
3100
+ if (entry.entryType === "navigation") return "navigation";
3101
+ if (entry.entryType === "paint" || entry.entryType === "largest-contentful-paint" || entry.entryType === "layout-shift") return "painting";
3102
+ if (entry.entryType === "resource") return initiator === "fetch" || initiator === "xmlhttprequest" ? "network" : "loading";
3103
+ return "scripting";
3104
+ }
3105
+ async function collectentries(watchwindow, types) {
3106
+ const rows = [];
3107
+ for (const type of ["navigation", "paint", "mark", "measure", "resource", "longtask"]) {
3108
+ for (const entry of performance.getEntriesByType(type)) {
3109
+ rows.push({ name: entry.name, type: entry.entryType, start: entry.startTime, duration: entry.duration, ...type === "resource" ? { initiator: entry.initiatorType } : {} });
3110
+ }
3111
+ }
3112
+ if (types.length > 0) {
3113
+ await new Promise((resolve) => {
3114
+ const observer = new PerformanceObserver((list) => {
3115
+ for (const entry of list.getEntries()) rows.push({ name: entry.name, type: entry.entryType, start: entry.startTime, duration: entry.duration });
3116
+ });
3117
+ observer.observe({ entryTypes: types, buffered: true });
3118
+ window.setTimeout(() => {
3119
+ observer.disconnect();
3120
+ resolve();
3121
+ }, Math.max(0, watchwindow));
3122
+ });
3123
+ } else {
3124
+ await new Promise((resolve) => window.setTimeout(resolve, Math.max(0, watchwindow)));
3125
+ }
3126
+ return rows;
3127
+ }
3128
+ function heapsample() {
3129
+ const memory = performance.memory;
3130
+ return { usedbytes: memory?.usedJSHeapSize ?? 0, limitbytes: memory?.jsHeapSizeLimit ?? memory?.totalJSHeapSize ?? 0, nodecount: document.querySelectorAll("*").length };
3131
+ }
3132
+ async function runprofilestep(step) {
3133
+ const options = profilestepoptions(step);
3134
+ if (step.kind === "measureflow") {
3135
+ if (!options.flow || options.watchwindow <= 0) return { ok: false, summary: "The reviewed flow spec with its watch window is absent." };
3136
+ const started = performance.now();
3137
+ for (const stepid of options.flow.steps) performance.mark(`${options.flow.prefix}:${stepid}:start`);
3138
+ const entries = await collectentries(options.watchwindow, ["largest-contentful-paint", "first-input", "event", "longtask"]);
3139
+ for (const stepid of options.flow.steps) performance.mark(`${options.flow.prefix}:${stepid}:end`);
3140
+ for (const stepid of options.flow.steps) performance.measure(`${options.flow.prefix}:${stepid}`, `${options.flow.prefix}:${stepid}:start`, `${options.flow.prefix}:${stepid}:end`);
3141
+ return { ok: true, summary: `Marked the start and end of ${options.flow.steps.length} step${options.flow.steps.length === 1 ? "" : "s"} of the flow ${options.flow.prefix} and collected ${entries.length} performance entr${entries.length === 1 ? "y" : "ies"} for the reviewed window of ${options.watchwindow} milliseconds.`, details: { entries, watchwindow: options.watchwindow, started, derivation: "Flow measurement derives from the performance timeline buffers and the injected marks through the scripting api; no debugger permission exists in the manifest." } };
3142
+ }
3143
+ if (step.kind === "heapshot") {
3144
+ const sample = heapsample();
3145
+ return { ok: true, summary: `Captured the on demand heap sample of ${sample.usedbytes} used bytes against the ${sample.limitbytes} byte limit with ${sample.nodecount} dom node${sample.nodecount === 1 ? "" : "s"}.`, details: { ...sample, derivation: "Heap bytes derive from the page performance memory buffer and the node count from the dom because no heap profiler exists without the debugger permission." } };
3146
+ }
3147
+ if (step.kind === "trackmemory") {
3148
+ if (!options.growth) return { ok: false, summary: "The reviewed growth slope is absent." };
3149
+ const sample = heapsample();
3150
+ return { ok: true, summary: `Took the heap sample of ${sample.usedbytes} used bytes beside the step for the growth tracking of slope ${options.growth.slope} bytes per millisecond.`, details: { ...sample, slope: options.growth.slope, interval: options.growth.interval, derivation: "Growth samples derive from the page performance memory buffer beside every step of the run." } };
3151
+ }
3152
+ if (step.kind === "profilecpu") {
3153
+ if (options.duration <= 0) return { ok: false, summary: "The reviewed cpu profile duration is absent." };
3154
+ const started = performance.now();
3155
+ const entries = await collectentries(options.duration, ["longtask", "event", "first-input"]);
3156
+ const samples = entries.filter((entry) => entry.type === "longtask" || entry.type === "event" || entry.type === "first-input").map((entry) => ({ name: entry.name || entry.type, time: entry.duration }));
3157
+ return { ok: true, summary: `Profiled the cpu window of ${options.duration} milliseconds with ${samples.length} sample${samples.length === 1 ? "" : "s"} from the long task and event timing buffers.`, details: { samples, duration: options.duration, started, derivation: "Cpu samples derive from the long task attribution and event timing buffers because no sampling profiler exists without the debugger permission." } };
3158
+ }
3159
+ if (step.kind === "watchshifts") {
3160
+ if (options.watchwindow <= 0) return { ok: false, summary: "The reviewed layout shift window is absent." };
3161
+ const shifts = [];
3162
+ await new Promise((resolve) => {
3163
+ const observer = new PerformanceObserver((list) => {
3164
+ for (const entry of list.getEntries()) {
3165
+ const shift = entry;
3166
+ const selectors = (shift.sources ?? []).flatMap((source) => source.node instanceof Element ? [source.node.tagName.toLowerCase() + (source.node.id ? `#${source.node.id}` : "")] : []);
3167
+ const score = typeof shift.value === "number" ? shift.value : 0;
3168
+ if (options.threshold > 0 && score < options.threshold) continue;
3169
+ shifts.push({ score, starttime: shift.startTime, selectors });
3170
+ }
3171
+ });
3172
+ observer.observe({ entryTypes: ["layout-shift"], buffered: true });
3173
+ window.setTimeout(() => {
3174
+ observer.disconnect();
3175
+ resolve();
3176
+ }, options.watchwindow);
3177
+ });
3178
+ return { ok: true, summary: `Watched ${shifts.length} layout shift${shifts.length === 1 ? "" : "s"} for the reviewed window of ${options.watchwindow} milliseconds${options.threshold > 0 ? ` with the score threshold ${options.threshold}` : ""}.`, details: { shifts, watchwindow: options.watchwindow, derivation: "Layout shifts derive from the performance layout-shift buffer with the impacted element selectors of the shift sources." } };
3179
+ }
3180
+ if (step.kind === "traceload") {
3181
+ if (options.categories.length === 0 || options.watchwindow <= 0) return { ok: false, summary: "The reviewed trace categories or window are absent." };
3182
+ const started = performance.now();
3183
+ const entries = await collectentries(options.watchwindow, ["largest-contentful-paint", "first-input", "event", "longtask", "layout-shift"]);
3184
+ const events4 = entries.filter((entry) => options.categories.includes(categoryof({ name: entry.name, entryType: entry.type, startTime: entry.start, duration: entry.duration }, entry.initiator))).map((entry) => ({ name: entry.name, category: categoryof({ name: entry.name, entryType: entry.type, startTime: entry.start, duration: entry.duration }, entry.initiator), offset: Math.round(entry.start - started) }));
3185
+ return { ok: true, summary: `Recorded ${events4.length} trace event${events4.length === 1 ? "" : "s"} of the reviewed categories ${options.categories.join(", ")} for the window of ${options.watchwindow} milliseconds and derived the exportable trace file.`, details: { events: events4, categories: options.categories, watchwindow: options.watchwindow, started, ...options.exporttarget !== void 0 ? { exporttarget: options.exporttarget } : {}, derivation: "The trace file derives from the performance timeline entries of the reviewed categories; it is not the devtools binary trace format because no debugger permission exists in the manifest." } };
3186
+ }
3187
+ if (step.kind === "capturesourcemaps") {
3188
+ const scripts = [];
3189
+ for (const element of document.querySelectorAll("script[src]")) {
3190
+ const src = element.src;
3191
+ if (!src.startsWith(location.origin)) continue;
3192
+ if (options.scripts.length > 0 && !options.scripts.includes(src)) continue;
3193
+ let mapurl;
3194
+ try {
3195
+ const response = await fetch(src, { credentials: "same-origin" });
3196
+ const source = await response.text();
3197
+ const match = /[#@]\s*sourceMappingURL=(\S+)/.exec(source);
3198
+ if (match !== null && match[1] !== void 0) mapurl = new URL(match[1], src).toString();
3199
+ } catch {
3200
+ }
3201
+ scripts.push({ url: src, ...mapurl !== void 0 ? { mapurl } : {} });
3202
+ }
3203
+ const withmaps = scripts.filter((script) => script.mapurl !== void 0);
3204
+ return { ok: true, summary: `Read the sourceMappingURL declarations of ${scripts.length} same origin script${scripts.length === 1 ? "" : "s"} of ${location.origin} and found ${withmaps.length} map declaration${withmaps.length === 1 ? "" : "s"}; the script sources stay in the page bridge and only the map urls leave it.`, details: { scripts, origin: location.origin, derivation: "Source map declarations are read by re-fetching the loaded same origin scripts of the page; cross origin scripts stay outside the capture and no map content enters the page bridge." } };
3205
+ }
3206
+ return { ok: false, summary: "The profiling step is not part of the instrumented family." };
3207
+ }
3208
+
2789
3209
  // extension/pageforms.ts
2790
3210
  function matchfield(fields, match) {
2791
3211
  const key = match.mode === "label" ? "label" : match.mode === "placeholder" ? "placeholder" : match.mode === "arialabel" ? "arialabel" : "name";
@@ -3660,6 +4080,8 @@
3660
4080
  var watchstepkinds = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "waitquiet", "readjson", "diffsnapshots", "deriveselector"]);
3661
4081
  var debugstepkinds = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
3662
4082
  var cdpstepkinds = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
4083
+ var profilestepkinds = /* @__PURE__ */ new Set(["measureflow", "heapshot", "trackmemory", "profilecpu", "watchshifts", "traceload", "capturesourcemaps"]);
4084
+ var emulationstepkinds = /* @__PURE__ */ new Set(["emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "blackboxscripts"]);
3663
4085
  var navstepkinds = /* @__PURE__ */ new Set(["waitload", "waiturl", "followlink", "spanav", "spawait", "rewritequery", "setfragment", "stopnav", "prefetch", "preconnect", "printpdf"]);
3664
4086
  var formkinds = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "asksubmit", "submitform", "consentpassword", "attachfile"]);
3665
4087
  var wizardkinds = /* @__PURE__ */ new Set(["runwizard", "selectchain", "picktypeahead", "pickdate", "fillcard", "fillcode"]);
@@ -3727,6 +4149,8 @@
3727
4149
  else if (watchstepkinds.has(step.kind)) result = runpagewatch(step, element, rootdocument);
3728
4150
  else if (debugstepkinds.has(step.kind)) return await rundebugwatch(step);
3729
4151
  else if (cdpstepkinds.has(step.kind)) return await runcdpstep(step);
4152
+ else if (profilestepkinds.has(step.kind)) return await runprofilestep(step);
4153
+ else if (emulationstepkinds.has(step.kind)) return await runemulationstep(step);
3730
4154
  else if (mutatingkinds.has(step.kind)) result = runpageaction(step, element);
3731
4155
  else {
3732
4156
  if (!element) return { ok: false, summary: "Action target is no longer available." };
@@ -4048,6 +4472,6 @@
4048
4472
  for (const cookie of targets) document.cookie = `${cookie.name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
4049
4473
  return { cleared: targets.length, summary: `Cleared ${targets.length} cookie${targets.length === 1 ? "" : "s"} through the page cookie jar of ${location.origin}.` };
4050
4474
  }
4051
- Object.assign(globalThis, { devthinkbridge: { capturesnapshot, previewtarget, performstep, readdialogs, measurepage, elementrect, preparecapture, scrollcapture, restorecapture, scrollcontainercapture, waitsettle, pdfsegment, pdfbreaks, videoframe, canvasdata, streamelements, mediaelements, pageassets, pageimages, parsehtmlmarkup, resourcerecords, writecookies, readcookies, clearcookies } });
4475
+ Object.assign(globalThis, { devthinkbridge: { capturesnapshot, previewtarget, performstep, readdialogs, measurepage, elementrect, preparecapture, scrollcapture, restorecapture, scrollcontainercapture, waitsettle, pdfsegment, pdfbreaks, videoframe, canvasdata, streamelements, mediaelements, pageassets, pageimages, parsehtmlmarkup, resourcerecords, writecookies, readcookies, clearcookies, revertemulationlayer } });
4052
4476
  })();
4053
4477
  //# sourceMappingURL=pagebridge.js.map