@wenathlan/extension 1.1.47 → 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.
- package/README.md +5 -4
- package/dist/emulation.d.ts +89 -0
- package/dist/emulation.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +423 -4
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +38 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +22 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +37 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +110 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +576 -7
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +288 -5
- package/extension/dist/pagebridge.js.map +3 -3
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +14 -2
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +162 -2
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +2 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -224,6 +224,156 @@ function expireprofilerecords(input) {
|
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
// emulation.ts
|
|
228
|
+
var emulationkinds = ["emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "blackboxscripts"];
|
|
229
|
+
var browserpermissions = ["geolocation", "notifications", "camera", "microphone", "clipboard-read", "clipboard-write", "midi", "persistent-storage"];
|
|
230
|
+
var permissionstates = ["granted", "denied", "prompt"];
|
|
231
|
+
function familyofkind(kind) {
|
|
232
|
+
if (kind === "emulatedevice") return "device";
|
|
233
|
+
if (kind === "emulatenetwork") return "network";
|
|
234
|
+
if (kind === "emulatelocate") return "location";
|
|
235
|
+
if (kind === "setuseragent") return "agent";
|
|
236
|
+
if (kind === "overridepermission") return "permission";
|
|
237
|
+
if (kind === "blackboxscripts") return "blackbox";
|
|
238
|
+
return void 0;
|
|
239
|
+
}
|
|
240
|
+
function devicepresetof(value) {
|
|
241
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
242
|
+
const entry = value;
|
|
243
|
+
const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
|
|
244
|
+
const width = typeof entry.width === "number" && Number.isInteger(entry.width) && entry.width > 0 ? entry.width : void 0;
|
|
245
|
+
const height = typeof entry.height === "number" && Number.isInteger(entry.height) && entry.height > 0 ? entry.height : void 0;
|
|
246
|
+
const pixelratio = typeof entry.pixelratio === "number" && Number.isFinite(entry.pixelratio) && entry.pixelratio > 0 ? entry.pixelratio : void 0;
|
|
247
|
+
if (name === void 0 || width === void 0 || height === void 0 || pixelratio === void 0) return void 0;
|
|
248
|
+
return { name, width, height, pixelratio, mobile: entry.mobile === true };
|
|
249
|
+
}
|
|
250
|
+
function networkpresetof(value) {
|
|
251
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
252
|
+
const entry = value;
|
|
253
|
+
const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
|
|
254
|
+
const latency = typeof entry.latency === "number" && Number.isFinite(entry.latency) && entry.latency >= 0 ? entry.latency : void 0;
|
|
255
|
+
const download = typeof entry.download === "number" && Number.isFinite(entry.download) && entry.download >= 0 ? entry.download : void 0;
|
|
256
|
+
const upload = typeof entry.upload === "number" && Number.isFinite(entry.upload) && entry.upload >= 0 ? entry.upload : void 0;
|
|
257
|
+
if (name === void 0 || latency === void 0 || download === void 0 || upload === void 0) return void 0;
|
|
258
|
+
return { name, latency, download, upload, offline: entry.offline === true };
|
|
259
|
+
}
|
|
260
|
+
function locationpresetof(value) {
|
|
261
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
262
|
+
const entry = value;
|
|
263
|
+
const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
|
|
264
|
+
const latitude = typeof entry.latitude === "number" && Number.isFinite(entry.latitude) ? entry.latitude : void 0;
|
|
265
|
+
const longitude = typeof entry.longitude === "number" && Number.isFinite(entry.longitude) ? entry.longitude : void 0;
|
|
266
|
+
const accuracy = typeof entry.accuracy === "number" && Number.isFinite(entry.accuracy) && entry.accuracy >= 0 ? entry.accuracy : void 0;
|
|
267
|
+
if (name === void 0 || latitude === void 0 || longitude === void 0 || accuracy === void 0) return void 0;
|
|
268
|
+
if (!locationrangevalid(latitude, longitude)) return void 0;
|
|
269
|
+
return { name, latitude, longitude, accuracy };
|
|
270
|
+
}
|
|
271
|
+
function agentpresetof(value) {
|
|
272
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
273
|
+
const entry = value;
|
|
274
|
+
const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : void 0;
|
|
275
|
+
const useragent = typeof entry.useragent === "string" ? entry.useragent : void 0;
|
|
276
|
+
const platform = typeof entry.platform === "string" && entry.platform.trim() ? entry.platform.trim() : void 0;
|
|
277
|
+
const brands = Array.isArray(entry.brands) ? entry.brands.filter((brand) => typeof brand === "string" && brand.trim().length > 0) : [];
|
|
278
|
+
if (name === void 0 || useragent === void 0 || platform === void 0 || brands.length === 0) return void 0;
|
|
279
|
+
if (!agentgrammarvalid(useragent)) return void 0;
|
|
280
|
+
return { name, useragent, platform, brands: [...new Set(brands)] };
|
|
281
|
+
}
|
|
282
|
+
function permissiongrantof(value) {
|
|
283
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
284
|
+
const entry = value;
|
|
285
|
+
const name = typeof entry.name === "string" && browserpermissions.includes(entry.name) ? entry.name : void 0;
|
|
286
|
+
const state = typeof entry.state === "string" && permissionstates.includes(entry.state) ? entry.state : void 0;
|
|
287
|
+
if (name === void 0 || state === void 0) return void 0;
|
|
288
|
+
return { name, state, runscope: entry.runscope !== false };
|
|
289
|
+
}
|
|
290
|
+
function blackboxruleof(value) {
|
|
291
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
292
|
+
const entry = value;
|
|
293
|
+
const urlpatterns = Array.isArray(entry.urlpatterns) ? entry.urlpatterns.filter((pattern) => typeof pattern === "string" && /^https:\/\//.test(pattern)) : [];
|
|
294
|
+
const tracescope = entry.tracescope;
|
|
295
|
+
if (urlpatterns.length === 0) return void 0;
|
|
296
|
+
if (tracescope !== "profiles" && tracescope !== "traces" && tracescope !== "both") return void 0;
|
|
297
|
+
return { urlpatterns: [...new Set(urlpatterns)], tracescope };
|
|
298
|
+
}
|
|
299
|
+
function revertplanof(value) {
|
|
300
|
+
const steps = Array.isArray(value) ? value.filter((step) => typeof step === "string" && step.trim().length > 0) : [];
|
|
301
|
+
return steps.length > 0 ? steps : void 0;
|
|
302
|
+
}
|
|
303
|
+
function newlayer(input) {
|
|
304
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, family: input.family, name: input.name, originscope: input.originscope, appliedat: input.at, ...input.prior !== void 0 ? { prior: input.prior } : {}, revertplan: [...input.revertplan] };
|
|
305
|
+
}
|
|
306
|
+
function emulationstateof(input) {
|
|
307
|
+
return { runid: input.runid, tabid: input.tabid, origin: input.origin, layers: [], updatedat: input.now };
|
|
308
|
+
}
|
|
309
|
+
function applylayer(state, layer, at) {
|
|
310
|
+
const layers = [...state.layers.filter((item) => item.id !== layer.id), layer];
|
|
311
|
+
return { ...state, layers, updatedat: at };
|
|
312
|
+
}
|
|
313
|
+
function revertalllayers(state, at) {
|
|
314
|
+
const reverted = [...state.layers].reverse().filter((layer) => layer.revertedat === void 0);
|
|
315
|
+
const layers = state.layers.map((layer) => layer.revertedat === void 0 ? { ...layer, revertedat: at } : layer);
|
|
316
|
+
return { state: { ...state, layers, updatedat: at }, reverted };
|
|
317
|
+
}
|
|
318
|
+
function activelayers(state) {
|
|
319
|
+
return state ? state.layers.filter((layer) => layer.revertedat === void 0) : [];
|
|
320
|
+
}
|
|
321
|
+
function layernames(state) {
|
|
322
|
+
return activelayers(state).map((layer) => layer.name);
|
|
323
|
+
}
|
|
324
|
+
function locationrangevalid(latitude, longitude) {
|
|
325
|
+
return Number.isFinite(latitude) && Number.isFinite(longitude) && latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180;
|
|
326
|
+
}
|
|
327
|
+
function agentgrammarvalid(useragent) {
|
|
328
|
+
const text2 = useragent.trim();
|
|
329
|
+
if (text2.length === 0 || text2.length > 512) return false;
|
|
330
|
+
if (/[\r\n]/.test(text2)) return false;
|
|
331
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._+\-()/:; ,]*$/.test(text2)) return false;
|
|
332
|
+
return /\/\d/.test(text2) || /\d+\.\d+/.test(text2);
|
|
333
|
+
}
|
|
334
|
+
function permissiongrade(name) {
|
|
335
|
+
return name === "geolocation" || name === "camera" || name === "microphone" || name === "notifications" ? "powerful" : "standard";
|
|
336
|
+
}
|
|
337
|
+
function expirelayers(state, retention, now) {
|
|
338
|
+
if (retention === void 0) return state;
|
|
339
|
+
const layers = state.layers.map((layer) => {
|
|
340
|
+
if (layer.revertedat === void 0 || layer.prior === void 0 || layer.priorexpired === true) return layer;
|
|
341
|
+
if (now - layer.revertedat <= retention) return layer;
|
|
342
|
+
const { prior, ...metadata } = layer;
|
|
343
|
+
void prior;
|
|
344
|
+
return { ...metadata, priorexpired: true };
|
|
345
|
+
});
|
|
346
|
+
return { ...state, layers, updatedat: now };
|
|
347
|
+
}
|
|
348
|
+
function exportpresetlibrary(input) {
|
|
349
|
+
return { version: 1, devices: [...input.devices], networks: [...input.networks], locations: [...input.locations], agents: [...input.agents], exportedat: input.now };
|
|
350
|
+
}
|
|
351
|
+
function importpresetlibrary(value) {
|
|
352
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
353
|
+
const entry = value;
|
|
354
|
+
const devices = (Array.isArray(entry.devices) ? entry.devices : []).flatMap((preset) => {
|
|
355
|
+
const parsed = devicepresetof(preset);
|
|
356
|
+
return parsed !== void 0 ? [parsed] : [];
|
|
357
|
+
});
|
|
358
|
+
const networks = (Array.isArray(entry.networks) ? entry.networks : []).flatMap((preset) => {
|
|
359
|
+
const parsed = networkpresetof(preset);
|
|
360
|
+
return parsed !== void 0 ? [parsed] : [];
|
|
361
|
+
});
|
|
362
|
+
const locations = (Array.isArray(entry.locations) ? entry.locations : []).flatMap((preset) => {
|
|
363
|
+
const parsed = locationpresetof(preset);
|
|
364
|
+
return parsed !== void 0 ? [parsed] : [];
|
|
365
|
+
});
|
|
366
|
+
const agents = (Array.isArray(entry.agents) ? entry.agents : []).flatMap((preset) => {
|
|
367
|
+
const parsed = agentpresetof(preset);
|
|
368
|
+
return parsed !== void 0 ? [parsed] : [];
|
|
369
|
+
});
|
|
370
|
+
if (devices.length + networks.length + locations.length + agents.length === 0) return void 0;
|
|
371
|
+
return { version: typeof entry.version === "number" && Number.isInteger(entry.version) && entry.version >= 1 ? entry.version : 1, devices, networks, locations, agents, exportedat: typeof entry.exportedat === "number" ? entry.exportedat : Date.now() };
|
|
372
|
+
}
|
|
373
|
+
function locationconsentcovers(origin, latitude, longitude, consents) {
|
|
374
|
+
return consents.some((consent) => consent.origin === origin && consent.approved === true && consent.revokedat === void 0 && consent.latitude === latitude && consent.longitude === longitude);
|
|
375
|
+
}
|
|
376
|
+
|
|
227
377
|
// memory.ts
|
|
228
378
|
var sessionmemory = class {
|
|
229
379
|
constructor(adapter) {
|
|
@@ -1594,6 +1744,83 @@ var sessionmemory = class {
|
|
|
1594
1744
|
await this.adapter.set("sourcemapconsents", updated);
|
|
1595
1745
|
return revoked;
|
|
1596
1746
|
}
|
|
1747
|
+
/** Stores the emulation state of one run keyed by its run id; the reverted layer prior states expire after the user configured retention window while the layer history always survives. */
|
|
1748
|
+
async setemulationstate(state) {
|
|
1749
|
+
const retention = (await this.getsettings())?.emulationretention;
|
|
1750
|
+
await this.adapter.set(`emulationstate${state.runid}`, expirelayers(state, retention, Date.now()));
|
|
1751
|
+
}
|
|
1752
|
+
/** Returns the persisted emulation state of one run so the layers survive service worker restarts. */
|
|
1753
|
+
async getemulationstate(runid) {
|
|
1754
|
+
return this.adapter.get(`emulationstate${runid}`);
|
|
1755
|
+
}
|
|
1756
|
+
/** Returns the active and past layers of one run, newest last in apply order; the listlayers accessor of the emulation memory. */
|
|
1757
|
+
async listlayers(runid) {
|
|
1758
|
+
const state = await this.getemulationstate(runid);
|
|
1759
|
+
return state?.layers ?? [];
|
|
1760
|
+
}
|
|
1761
|
+
/** Stores one user curated device preset by its name so the preset library stays user data instead of a hardcoded list. */
|
|
1762
|
+
async setdevicepreset(preset) {
|
|
1763
|
+
const records = (await this.adapter.get("devicepresets") ?? []).filter((item) => item.name !== preset.name);
|
|
1764
|
+
await this.adapter.set("devicepresets", [...records, preset]);
|
|
1765
|
+
}
|
|
1766
|
+
/** Returns every user curated device preset. */
|
|
1767
|
+
async getdevicepresets() {
|
|
1768
|
+
return await this.adapter.get("devicepresets") ?? [];
|
|
1769
|
+
}
|
|
1770
|
+
/** Stores one user curated network preset by its name with editable values. */
|
|
1771
|
+
async setnetworkpreset(preset) {
|
|
1772
|
+
const records = (await this.adapter.get("networkpresets") ?? []).filter((item) => item.name !== preset.name);
|
|
1773
|
+
await this.adapter.set("networkpresets", [...records, preset]);
|
|
1774
|
+
}
|
|
1775
|
+
/** Returns every user curated network preset. */
|
|
1776
|
+
async getnetworkpresets() {
|
|
1777
|
+
return await this.adapter.get("networkpresets") ?? [];
|
|
1778
|
+
}
|
|
1779
|
+
/** Stores one user curated location preset by its name. */
|
|
1780
|
+
async setlocationpreset(preset) {
|
|
1781
|
+
const records = (await this.adapter.get("locationpresets") ?? []).filter((item) => item.name !== preset.name);
|
|
1782
|
+
await this.adapter.set("locationpresets", [...records, preset]);
|
|
1783
|
+
}
|
|
1784
|
+
/** Returns every user curated location preset. */
|
|
1785
|
+
async getlocationpresets() {
|
|
1786
|
+
return await this.adapter.get("locationpresets") ?? [];
|
|
1787
|
+
}
|
|
1788
|
+
/** Stores one user curated agent preset by its name. */
|
|
1789
|
+
async setagentpreset(preset) {
|
|
1790
|
+
const records = (await this.adapter.get("agentpresets") ?? []).filter((item) => item.name !== preset.name);
|
|
1791
|
+
await this.adapter.set("agentpresets", [...records, preset]);
|
|
1792
|
+
}
|
|
1793
|
+
/** Returns every user curated agent preset. */
|
|
1794
|
+
async getagentpresets() {
|
|
1795
|
+
return await this.adapter.get("agentpresets") ?? [];
|
|
1796
|
+
}
|
|
1797
|
+
/** Replaces the blackbox rule set of one origin so third party script blackboxing stays scoped per origin. */
|
|
1798
|
+
async setblackboxrules(origin, rules) {
|
|
1799
|
+
const records = (await this.adapter.get("blackboxrules") ?? []).filter((item) => item.origin !== origin);
|
|
1800
|
+
await this.adapter.set("blackboxrules", [...records, { origin, rules }]);
|
|
1801
|
+
}
|
|
1802
|
+
/** Returns every stored blackbox rule set with its origin. */
|
|
1803
|
+
async getblackboxrules() {
|
|
1804
|
+
return await this.adapter.get("blackboxrules") ?? [];
|
|
1805
|
+
}
|
|
1806
|
+
/** Records one permission override of a run with its prior state captured for the exact restore. */
|
|
1807
|
+
async addpermissionoverride(record2) {
|
|
1808
|
+
const records = (await this.adapter.get("permissionoverrides") ?? []).filter((item) => item.id !== record2.id);
|
|
1809
|
+
await this.adapter.set("permissionoverrides", [record2, ...records]);
|
|
1810
|
+
}
|
|
1811
|
+
/** Returns the permission override history with restore states, newest first. */
|
|
1812
|
+
async getpermissionoverrides() {
|
|
1813
|
+
return await this.adapter.get("permissionoverrides") ?? [];
|
|
1814
|
+
}
|
|
1815
|
+
/** Stores one location consent decision per origin, replacing the previous decision of its id. */
|
|
1816
|
+
async setlocationconsent(consent) {
|
|
1817
|
+
const records = (await this.adapter.get("locationconsents") ?? []).filter((item) => item.id !== consent.id);
|
|
1818
|
+
await this.adapter.set("locationconsents", [consent, ...records]);
|
|
1819
|
+
}
|
|
1820
|
+
/** Returns every location consent decision, newest first. */
|
|
1821
|
+
async getlocationconsents() {
|
|
1822
|
+
return await this.adapter.get("locationconsents") ?? [];
|
|
1823
|
+
}
|
|
1597
1824
|
};
|
|
1598
1825
|
function mediakindof(record2) {
|
|
1599
1826
|
if ("pages" in record2) return "pdf";
|
|
@@ -2813,9 +3040,9 @@ function consolediff(input) {
|
|
|
2813
3040
|
}
|
|
2814
3041
|
|
|
2815
3042
|
// policy.ts
|
|
2816
|
-
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"]);
|
|
3043
|
+
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"]);
|
|
2817
3044
|
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"]);
|
|
2818
|
-
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"]);
|
|
3045
|
+
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"]);
|
|
2819
3046
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
2820
3047
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
2821
3048
|
var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
|
|
@@ -2834,6 +3061,7 @@ var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "r
|
|
|
2834
3061
|
var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
|
|
2835
3062
|
var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
|
|
2836
3063
|
var profileractions = /* @__PURE__ */ new Set(["measureflow", "heapshot", "trackmemory", "profilecpu", "watchshifts", "traceload", "annotatetrace", "replaytrace", "capturesourcemaps"]);
|
|
3064
|
+
var emulationactions = /* @__PURE__ */ new Set(["emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "blackboxscripts"]);
|
|
2837
3065
|
var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
|
|
2838
3066
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
2839
3067
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
@@ -2858,6 +3086,9 @@ function iscdpkind(kind) {
|
|
|
2858
3086
|
function isprofilekind(kind) {
|
|
2859
3087
|
return profileractions.has(kind);
|
|
2860
3088
|
}
|
|
3089
|
+
function isemulationkind(kind) {
|
|
3090
|
+
return emulationactions.has(kind);
|
|
3091
|
+
}
|
|
2861
3092
|
function actionrisk(kind) {
|
|
2862
3093
|
if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
|
|
2863
3094
|
if (sensitiveactions.has(kind)) return "sensitive";
|
|
@@ -4221,6 +4452,74 @@ function breakpointbudgetallowed(active, ceiling) {
|
|
|
4221
4452
|
function breakpointceilingof(settings) {
|
|
4222
4453
|
return settings?.breakpointceiling;
|
|
4223
4454
|
}
|
|
4455
|
+
function emugate(input) {
|
|
4456
|
+
const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now: input.now, action: "emulate the run tab" });
|
|
4457
|
+
if (!gate.allowed) return gate;
|
|
4458
|
+
if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "Emulation layers need an approved plan before they apply." };
|
|
4459
|
+
let options = {};
|
|
4460
|
+
try {
|
|
4461
|
+
options = parseoptions(input.step);
|
|
4462
|
+
} catch {
|
|
4463
|
+
options = {};
|
|
4464
|
+
}
|
|
4465
|
+
if (options.reviewed !== true) return { allowed: false, reason: `The ${input.step.kind} layer needs the explicit reviewed flag before any mask applies.` };
|
|
4466
|
+
if (revertplanof(options.revertplan) === void 0) return { allowed: false, reason: `Every ${input.step.kind} layer needs a reviewed revert plan beside it before any mask applies.` };
|
|
4467
|
+
return { allowed: true };
|
|
4468
|
+
}
|
|
4469
|
+
function emulationstackallowed(plan, kind, active) {
|
|
4470
|
+
if (!plan) return { allowed: false, reason: "Layer stacking needs the reviewed plan first." };
|
|
4471
|
+
const listed = plan.steps.filter((step) => step.kind === kind).length;
|
|
4472
|
+
if (active >= listed) return { allowed: false, reason: `The plan lists ${listed} reviewed ${kind} step${listed === 1 ? "" : "s"} and ${active} layer${active === 1 ? "" : "s"} of that family are already active; stacking beyond the reviewed plan is refused.` };
|
|
4473
|
+
return { allowed: true };
|
|
4474
|
+
}
|
|
4475
|
+
function locationconsentgate(origin, latitude, longitude, consents) {
|
|
4476
|
+
if (consents.some((consent) => consent.origin === origin && consent.revokedat !== void 0)) return { allowed: false, reason: `The location consent on ${origin} was revoked; approve a new prompt before the location override runs again.` };
|
|
4477
|
+
if (locationconsentcovers(origin, latitude, longitude, consents)) return { allowed: true };
|
|
4478
|
+
return { allowed: false, reason: `The location override of ${latitude}, ${longitude} on ${origin} needs the reviewed location consent first; approve the prompt with the coordinates shown in the review panel.` };
|
|
4479
|
+
}
|
|
4480
|
+
function validateemulationgrammar(step, options) {
|
|
4481
|
+
const kind = step.kind;
|
|
4482
|
+
if (revertplanof(options.revertplan) === void 0) return { allowed: false, reason: `Every ${kind} layer needs a reviewed revert plan before any mask applies.` };
|
|
4483
|
+
if (kind === "emulatedevice") {
|
|
4484
|
+
const preset = devicepresetof(options.device);
|
|
4485
|
+
if (!preset) return { allowed: false, reason: "The device layer needs a reviewed preset with a name, positive integer width and height and a positive pixel ratio." };
|
|
4486
|
+
if (options.reload !== void 0 && typeof options.reload !== "boolean") return { allowed: false, reason: "The reviewed reload flag must be a boolean; the page reloads only when the reviewed plan asks." };
|
|
4487
|
+
return { allowed: true };
|
|
4488
|
+
}
|
|
4489
|
+
if (kind === "emulatenetwork") {
|
|
4490
|
+
const preset = networkpresetof(options.network);
|
|
4491
|
+
if (!preset) return { allowed: false, reason: "The network layer needs a reviewed preset with a name and zero or positive latency, download and upload bounds." };
|
|
4492
|
+
if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isFinite(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed offline window must be zero or a positive number of milliseconds with no code ceiling." };
|
|
4493
|
+
return { allowed: true };
|
|
4494
|
+
}
|
|
4495
|
+
if (kind === "emulatelocate") {
|
|
4496
|
+
const preset = locationpresetof(options.location);
|
|
4497
|
+
if (!preset) return { allowed: false, reason: "The location layer needs a reviewed preset with a name, a latitude inside -90 and 90, a longitude inside -180 and 180 and a zero or positive accuracy radius." };
|
|
4498
|
+
if (!locationrangevalid(preset.latitude, preset.longitude)) return { allowed: false, reason: "The reviewed latitude must stay inside -90 and 90 degrees and the longitude inside -180 and 180 degrees." };
|
|
4499
|
+
return { allowed: true };
|
|
4500
|
+
}
|
|
4501
|
+
if (kind === "setuseragent") {
|
|
4502
|
+
const preset = agentpresetof(options.agent);
|
|
4503
|
+
if (!preset) return { allowed: false, reason: "The agent layer needs a reviewed preset with a user agent string of the reviewed grammar, a platform and a non-empty brand list." };
|
|
4504
|
+
if (!agentgrammarvalid(preset.useragent)) return { allowed: false, reason: "The reviewed user agent string must use the reviewed grammar of tokens, separators and version marks without line breaks." };
|
|
4505
|
+
return { allowed: true };
|
|
4506
|
+
}
|
|
4507
|
+
if (kind === "overridepermission") {
|
|
4508
|
+
const grant = permissiongrantof(options.permission);
|
|
4509
|
+
if (!grant) return { allowed: false, reason: `The permission override needs a reviewed name of the browser permission set (${browserpermissions.join(", ")}) and a state of ${permissionstates.join(", ")}.` };
|
|
4510
|
+
void permissiongrade(grant.name);
|
|
4511
|
+
return { allowed: true };
|
|
4512
|
+
}
|
|
4513
|
+
if (kind === "blackboxscripts") {
|
|
4514
|
+
const rules = Array.isArray(options.rules) ? options.rules.flatMap((rule) => {
|
|
4515
|
+
const parsed = blackboxruleof(rule);
|
|
4516
|
+
return parsed !== void 0 ? [parsed] : [];
|
|
4517
|
+
}) : [];
|
|
4518
|
+
if (rules.length === 0) return { allowed: false, reason: "The blackbox layer needs a reviewed non-empty rule list where every pattern names its origin explicitly and carries a trace scope." };
|
|
4519
|
+
return { allowed: true };
|
|
4520
|
+
}
|
|
4521
|
+
return { allowed: true };
|
|
4522
|
+
}
|
|
4224
4523
|
function validatecdpgrammar(step, options) {
|
|
4225
4524
|
const kind = step.kind;
|
|
4226
4525
|
if (kind === "attachcdp") {
|
|
@@ -4782,6 +5081,10 @@ function validatestep(step, origin) {
|
|
|
4782
5081
|
const profilecheck = validateprofilegrammar(step, options);
|
|
4783
5082
|
if (!profilecheck.allowed) return profilecheck;
|
|
4784
5083
|
}
|
|
5084
|
+
if (isemulationkind(step.kind)) {
|
|
5085
|
+
const emulationcheck = validateemulationgrammar(step, options);
|
|
5086
|
+
if (!emulationcheck.allowed) return emulationcheck;
|
|
5087
|
+
}
|
|
4785
5088
|
if (step.kind === "tabcreate") {
|
|
4786
5089
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
4787
5090
|
if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isInteger(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed target window id must be a non-negative integer." };
|
|
@@ -4951,6 +5254,10 @@ function canexecute(input) {
|
|
|
4951
5254
|
}
|
|
4952
5255
|
}
|
|
4953
5256
|
}
|
|
5257
|
+
if (isemulationkind(input.step.kind)) {
|
|
5258
|
+
const emugatecheck = emugate({ session: input.session, plan: input.plan, step: input.step, tabid: input.tabid, origin: input.origin, now });
|
|
5259
|
+
if (!emugatecheck.allowed) return emugatecheck;
|
|
5260
|
+
}
|
|
4954
5261
|
if (iscontrolkind(input.step.kind)) {
|
|
4955
5262
|
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
4956
5263
|
if (!controlgate.allowed) return controlgate;
|
|
@@ -5196,9 +5503,14 @@ function recordprofile(progress, planid, stepid, entry, now) {
|
|
|
5196
5503
|
const outcome = { stepid, ok: true, summary: `The profiling ${entry.family} capture ran${counts.length > 0 ? ` with ${counts}` : ""}.`, details: { profile: entry }, at: now };
|
|
5197
5504
|
return recordoutcome(base, planid, outcome, now);
|
|
5198
5505
|
}
|
|
5506
|
+
function recordemulation(progress, planid, stepid, entry, now) {
|
|
5507
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
5508
|
+
const outcome = { stepid, ok: true, summary: `${entry.reason}: ${entry.applied.length} applied layer${entry.applied.length === 1 ? "" : "s"}${entry.applied.length > 0 ? ` (${entry.applied.join(", ")})` : ""} and ${entry.reverted.length} reverted layer${entry.reverted.length === 1 ? "" : "s"}${entry.reverted.length > 0 ? ` (${entry.reverted.join(", ")})` : ""}.`, details: { emulation: entry }, at: now };
|
|
5509
|
+
return recordoutcome(base, planid, outcome, now);
|
|
5510
|
+
}
|
|
5199
5511
|
|
|
5200
5512
|
// version.ts
|
|
5201
|
-
var packageversion = "1.1.
|
|
5513
|
+
var packageversion = "1.1.48";
|
|
5202
5514
|
|
|
5203
5515
|
// types.ts
|
|
5204
5516
|
var protocolversion = packageversion;
|
|
@@ -5370,6 +5682,28 @@ function parseproposal(value, origin, grants) {
|
|
|
5370
5682
|
}
|
|
5371
5683
|
if (step.kind === "annotatetrace" && (!Array.isArray(profileoptions.annotations) || profileoptions.annotations.length === 0 || !profileoptions.annotations.every((annotation) => annotationof(annotation) !== void 0))) throw new Error("Trace annotation steps without reviewed step annotations are refused.");
|
|
5372
5684
|
}
|
|
5685
|
+
if (isemulationkind(step.kind)) {
|
|
5686
|
+
const granted = covered.some((pattern) => {
|
|
5687
|
+
try {
|
|
5688
|
+
return new URL(origin).origin === new URL(pattern).origin;
|
|
5689
|
+
} catch {
|
|
5690
|
+
return false;
|
|
5691
|
+
}
|
|
5692
|
+
});
|
|
5693
|
+
if (!granted) throw new Error(`The ${step.kind} step of ${origin} targets an origin outside the grants.`);
|
|
5694
|
+
let emulationoptions = {};
|
|
5695
|
+
try {
|
|
5696
|
+
emulationoptions = parseoptions(step);
|
|
5697
|
+
} catch {
|
|
5698
|
+
emulationoptions = {};
|
|
5699
|
+
}
|
|
5700
|
+
if (revertplanof(emulationoptions.revertplan) === void 0) throw new Error("Emulation steps without a reviewed revert plan are refused.");
|
|
5701
|
+
if (step.kind === "emulatelocate") {
|
|
5702
|
+
const preset = locationpresetof(emulationoptions.location);
|
|
5703
|
+
if (preset === void 0) throw new Error("Location emulation needs a reviewed preset with coordinates inside the latitude and longitude ranges.");
|
|
5704
|
+
}
|
|
5705
|
+
if (step.kind === "overridepermission" && permissiongrantof(emulationoptions.permission) === void 0) throw new Error("Permission overrides of unknown permission names are refused.");
|
|
5706
|
+
}
|
|
5373
5707
|
const evaluation = validatestep(step, origin);
|
|
5374
5708
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
5375
5709
|
const target = outboundtarget(step);
|
|
@@ -5444,7 +5778,7 @@ function requestbody(input) {
|
|
|
5444
5778
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
5445
5779
|
}
|
|
5446
5780
|
function outcomeresponse(input) {
|
|
5447
|
-
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {} });
|
|
5781
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {} });
|
|
5448
5782
|
}
|
|
5449
5783
|
function mapresponse(input) {
|
|
5450
5784
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -5567,6 +5901,14 @@ function profilereport(input) {
|
|
|
5567
5901
|
});
|
|
5568
5902
|
return { version: protocolversion, flows: input.flows, heaps: input.heaps, samples: input.samples, trends: input.trends, profiles: input.profiles, shifts: input.shifts, traces: input.traces, sourcemaps: input.sourcemaps, consents };
|
|
5569
5903
|
}
|
|
5904
|
+
function emulationreport(input) {
|
|
5905
|
+
const consents = input.consents.map((consent) => {
|
|
5906
|
+
const { prompt, ...metadata } = consent;
|
|
5907
|
+
void prompt;
|
|
5908
|
+
return metadata;
|
|
5909
|
+
});
|
|
5910
|
+
return { version: protocolversion, ...input.state !== void 0 ? { state: input.state } : {}, layers: input.state?.layers ?? [], devices: input.devices, networks: input.networks, locations: input.locations, agents: input.agents, blackbox: input.blackbox, permissions: input.permissions, consents };
|
|
5911
|
+
}
|
|
5570
5912
|
|
|
5571
5913
|
// capture.ts
|
|
5572
5914
|
var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
|
|
@@ -7255,7 +7597,7 @@ function stepoptions2(step) {
|
|
|
7255
7597
|
}
|
|
7256
7598
|
async function refreshcapabilities() {
|
|
7257
7599
|
const report = await readcapabilities();
|
|
7258
|
-
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds, ...cdpkinds], profile: [...profilerkinds] };
|
|
7600
|
+
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds, ...cdpkinds], profile: [...profilerkinds], emulation: [...emulationkinds] };
|
|
7259
7601
|
await memory.setcapabilities(withmedia);
|
|
7260
7602
|
return withmedia;
|
|
7261
7603
|
}
|
|
@@ -7696,6 +8038,8 @@ async function tracktabupdate(tabid2, changeinfo) {
|
|
|
7696
8038
|
});
|
|
7697
8039
|
await stopprofileinstrumentsforrun(runid, `the run tab navigated to ${url}`).catch(() => {
|
|
7698
8040
|
});
|
|
8041
|
+
await revertemulationforrun(runid, `the run tab navigated to ${url}`).catch(() => {
|
|
8042
|
+
});
|
|
7699
8043
|
}
|
|
7700
8044
|
return;
|
|
7701
8045
|
}
|
|
@@ -7721,6 +8065,10 @@ chrome.tabs.onActivated.addListener((activeinfo) => {
|
|
|
7721
8065
|
void recordtabwatchevent("activated", activeinfo.tabId);
|
|
7722
8066
|
});
|
|
7723
8067
|
chrome.tabs.onRemoved.addListener((tabid2) => {
|
|
8068
|
+
for (const [runid, state] of [...activeemulation.entries()]) {
|
|
8069
|
+
if (state.tabid === tabid2) void revertemulationforrun(runid, `the run tab ${tabid2} dropped`, tabid2).catch(() => {
|
|
8070
|
+
});
|
|
8071
|
+
}
|
|
7724
8072
|
const url = lastknownurls.get(tabid2);
|
|
7725
8073
|
const title = lastknowntitles.get(tabid2) ?? "";
|
|
7726
8074
|
const windowid = 0;
|
|
@@ -11360,11 +11708,116 @@ async function refreshbadge() {
|
|
|
11360
11708
|
const observedrequests = (await memory.getexchanges()).length;
|
|
11361
11709
|
const livechannels = (await memory.getchannels()).filter((channel) => channel.state === "open" || channel.state === "connecting").length;
|
|
11362
11710
|
const activerulescount = [...activerules.values()].reduce((total2, ruleset) => total2 + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0);
|
|
11711
|
+
const locationprompts = (await memory.getlocationconsents()).filter((consent) => consent.approved === void 0).length;
|
|
11712
|
+
const emulatedlayers = [...activeemulation.values()].reduce((total2, state) => total2 + activelayers(state).length, 0);
|
|
11363
11713
|
const tasktabs2 = new Set(badges.map((badge) => badge.tabid)).size;
|
|
11364
|
-
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + debuggerprompts + observedrequests + livechannels + activerulescount;
|
|
11714
|
+
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + debuggerprompts + locationprompts + emulatedlayers + observedrequests + livechannels + activerulescount;
|
|
11365
11715
|
await chrome.action.setBadgeText({ text: total > 0 ? String(total) : "" }).catch(() => {
|
|
11366
11716
|
});
|
|
11367
11717
|
}
|
|
11718
|
+
var activeemulation = /* @__PURE__ */ new Map();
|
|
11719
|
+
async function loademulationstate(runid) {
|
|
11720
|
+
const existing = activeemulation.get(runid);
|
|
11721
|
+
if (existing) return existing;
|
|
11722
|
+
const stored = await memory.getemulationstate(runid);
|
|
11723
|
+
if (stored) activeemulation.set(runid, stored);
|
|
11724
|
+
return stored;
|
|
11725
|
+
}
|
|
11726
|
+
async function revertemulationforrun(runid, reason, tabid2) {
|
|
11727
|
+
const state = await loademulationstate(runid);
|
|
11728
|
+
if (!state) return;
|
|
11729
|
+
const now = Date.now();
|
|
11730
|
+
const outcome = revertalllayers(state, now);
|
|
11731
|
+
const target = tabid2 ?? state.tabid;
|
|
11732
|
+
if (outcome.reverted.length > 0 && target !== void 0) {
|
|
11733
|
+
for (const layer of outcome.reverted) {
|
|
11734
|
+
await chrome.scripting.executeScript({ target: { tabId: target }, func: (family, prior) => {
|
|
11735
|
+
const bridge = globalThis.devthinkbridge;
|
|
11736
|
+
if (bridge) bridge.revertemulationlayer(family, prior);
|
|
11737
|
+
}, args: [layer.family, layer.prior] }).catch(() => {
|
|
11738
|
+
});
|
|
11739
|
+
}
|
|
11740
|
+
}
|
|
11741
|
+
activeemulation.set(runid, outcome.state);
|
|
11742
|
+
await memory.setemulationstate(outcome.state);
|
|
11743
|
+
const session = await memory.getsession();
|
|
11744
|
+
for (const layer of outcome.reverted) {
|
|
11745
|
+
await audit("emulation", `Reverted the ${layer.family} layer ${layer.name} of run ${runid} on ${reason}; the prior state${layer.prior !== void 0 ? " restored exactly" : " needed no page state"} through the revert plan of ${layer.revertplan.join(", ")}.`, { ...session ? { sessionid: session.id } : {}, planid: runid, stepid: layer.stepid });
|
|
11746
|
+
}
|
|
11747
|
+
if (outcome.reverted.length > 0) {
|
|
11748
|
+
const plan = await memory.getplan();
|
|
11749
|
+
if (plan) await memory.setprogress(recordemulation(await memory.getprogress(), plan.id, outcome.reverted[0]?.stepid ?? "", { applied: [], reverted: outcome.reverted.map((layer) => layer.name), reason: `Emulation reverted on ${reason}` }, now));
|
|
11750
|
+
await refreshbadge();
|
|
11751
|
+
}
|
|
11752
|
+
}
|
|
11753
|
+
async function executeemulationstep(step, session, plan, tabid2, origin) {
|
|
11754
|
+
const options = stepoptions2(step);
|
|
11755
|
+
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
11756
|
+
const revertplan = revertplanof(options.revertplan) ?? [];
|
|
11757
|
+
const family = familyofkind(step.kind) ?? "device";
|
|
11758
|
+
const state = await loademulationstate(plan.id) ?? emulationstateof({ runid: plan.id, tabid: tabid2, origin, now: Date.now() });
|
|
11759
|
+
const stacked = activelayers(state).filter((layer2) => layer2.family === family).length;
|
|
11760
|
+
const stackgate2 = emulationstackallowed(plan, step.kind, stacked);
|
|
11761
|
+
if (!stackgate2.allowed) throw new Error(stackgate2.reason);
|
|
11762
|
+
if (step.kind === "emulatelocate") {
|
|
11763
|
+
const preset = locationpresetof(options.location);
|
|
11764
|
+
if (!preset) throw new Error("A reviewed location preset is required before the location override applies.");
|
|
11765
|
+
const consents = await memory.getlocationconsents();
|
|
11766
|
+
const consentgate = locationconsentgate(origin, preset.latitude, preset.longitude, consents);
|
|
11767
|
+
if (!consentgate.allowed) {
|
|
11768
|
+
const pending = consents.find((consent) => consent.origin === origin && consent.approved === void 0 && consent.latitude === preset.latitude && consent.longitude === preset.longitude);
|
|
11769
|
+
if (!pending) {
|
|
11770
|
+
await memory.setlocationconsent({ id: randomid(), prompt: `Location override of ${preset.latitude}, ${preset.longitude} on ${origin} for run ${plan.id} through a page-injected geolocation override; the true browser location stays untouched.`, origin, latitude: preset.latitude, longitude: preset.longitude, consentedat: Date.now() });
|
|
11771
|
+
await refreshbadge();
|
|
11772
|
+
}
|
|
11773
|
+
throw new Error(`${consentgate.reason} The prompt is open in the review panel with the coordinates shown; approve it and run the step again.`);
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
if (step.kind === "blackboxscripts") {
|
|
11777
|
+
const rules = (Array.isArray(options.rules) ? options.rules : []).flatMap((rule) => {
|
|
11778
|
+
const parsed = blackboxruleof(rule);
|
|
11779
|
+
return parsed !== void 0 ? [parsed] : [];
|
|
11780
|
+
});
|
|
11781
|
+
if (rules.length === 0) throw new Error("A reviewed non-empty blackbox rule list is required.");
|
|
11782
|
+
const output2 = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The blackbox registration returned no result." };
|
|
11783
|
+
if (!output2.ok) return output2;
|
|
11784
|
+
await memory.setblackboxrules(origin, rules);
|
|
11785
|
+
const name2 = `${rules.length} blackbox rule${rules.length === 1 ? "" : "s"}`;
|
|
11786
|
+
const layer2 = newlayer({ id: randomid(), runid: plan.id, stepid: step.id, family: "blackbox", name: name2, originscope: origin, revertplan, at: Date.now() });
|
|
11787
|
+
const updated2 = applylayer(state, layer2, Date.now());
|
|
11788
|
+
activeemulation.set(plan.id, updated2);
|
|
11789
|
+
await memory.setemulationstate(updated2);
|
|
11790
|
+
await memory.setprogress(recordemulation(await memory.getprogress(), plan.id, step.id, { applied: [name2], reverted: [], reason: "Blackbox rules registered" }, Date.now()));
|
|
11791
|
+
await audit("emulation", `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 ${origin} with the ${rules.map((rule) => rule.tracescope).join(", ")} scope${revertplan.length > 0 ? ` and the revert plan of ${revertplan.join(", ")}` : ""}; the rules stay read only trace shaping.`, extra);
|
|
11792
|
+
await refreshbadge();
|
|
11793
|
+
return { ok: true, summary: output2.summary, details: { ...output2.details ?? {}, emulation: { applied: [name2], reverted: [] } } };
|
|
11794
|
+
}
|
|
11795
|
+
const priorpermission = step.kind === "overridepermission" ? await chrome.scripting.executeScript({ target: { tabId: tabid2 }, func: (name2) => navigator.permissions?.query({ name: name2 }).then((status) => status.state).catch(() => "prompt"), args: [permissiongrantof(options.permission)?.name ?? ""] }).then((result) => result[0]?.result).catch(() => "prompt") : void 0;
|
|
11796
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The emulation step returned no result." };
|
|
11797
|
+
if (!output.ok) return output;
|
|
11798
|
+
const prior = output.details?.prior;
|
|
11799
|
+
let name = family;
|
|
11800
|
+
if (step.kind === "emulatedevice") name = devicepresetof(options.device)?.name ?? "device";
|
|
11801
|
+
if (step.kind === "emulatenetwork") name = networkpresetof(options.network)?.name ?? "network";
|
|
11802
|
+
if (step.kind === "emulatelocate") name = locationpresetof(options.location)?.name ?? "location";
|
|
11803
|
+
if (step.kind === "setuseragent") name = agentpresetof(options.agent)?.name ?? "agent";
|
|
11804
|
+
if (step.kind === "overridepermission") name = `${permissiongrantof(options.permission)?.name ?? "permission"} ${permissiongrantof(options.permission)?.state ?? ""}`.trim();
|
|
11805
|
+
const layer = newlayer({ id: randomid(), runid: plan.id, stepid: step.id, family, name, originscope: origin, revertplan, ...prior !== void 0 ? { prior } : {}, at: Date.now() });
|
|
11806
|
+
const updated = applylayer(state, layer, Date.now());
|
|
11807
|
+
activeemulation.set(plan.id, updated);
|
|
11808
|
+
await memory.setemulationstate(updated);
|
|
11809
|
+
if (step.kind === "overridepermission") {
|
|
11810
|
+
const grant = permissiongrantof(options.permission);
|
|
11811
|
+
if (grant) await memory.addpermissionoverride({ id: layer.id, runid: plan.id, stepid: step.id, origin, name: grant.name, state: grant.state, priorstate: priorpermission === "granted" || priorpermission === "denied" || priorpermission === "prompt" ? priorpermission : "prompt", appliedat: Date.now() });
|
|
11812
|
+
}
|
|
11813
|
+
if (step.kind === "emulatedevice" && options.reload === true) await chrome.tabs.reload(tabid2).catch(() => {
|
|
11814
|
+
});
|
|
11815
|
+
const grade = step.kind === "overridepermission" ? ` graded ${permissiongrade(permissiongrantof(options.permission)?.name ?? "")} by the reviewed permission name` : "";
|
|
11816
|
+
await memory.setprogress(recordemulation(await memory.getprogress(), plan.id, step.id, { applied: [name], reverted: [], reason: `Emulation layer applied${grade}` }, Date.now()));
|
|
11817
|
+
await audit("emulation", `Applied the ${family} layer ${name} of run ${plan.id} on ${origin}${grade}${prior !== void 0 ? " with the prior page state captured for the exact revert" : ""} and the revert plan of ${revertplan.join(", ")}; the mask is a page-injected override through the scripting api because no debugger or platform permission exists in the manifest.`, extra);
|
|
11818
|
+
await refreshbadge();
|
|
11819
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, emulation: { applied: [name], reverted: [] } } };
|
|
11820
|
+
}
|
|
11368
11821
|
async function executestep(stepid) {
|
|
11369
11822
|
const session = await memory.getsession();
|
|
11370
11823
|
const plan = await memory.getplan();
|
|
@@ -11438,6 +11891,9 @@ async function executestep(stepid) {
|
|
|
11438
11891
|
} else if (isprofilekind(step.kind)) {
|
|
11439
11892
|
if (!session || !plan || plan.state !== "approved") throw new Error("Profiling kinds refuse to run outside an approved session plan.");
|
|
11440
11893
|
output = await executeprofilestep(step, session, plan, tab.id, origin);
|
|
11894
|
+
} else if (isemulationkind(step.kind)) {
|
|
11895
|
+
if (!session || !plan || plan.state !== "approved") throw new Error("Emulation kinds refuse to run outside an approved session plan.");
|
|
11896
|
+
output = await executeemulationstep(step, session, plan, tab.id, origin);
|
|
11441
11897
|
} else {
|
|
11442
11898
|
if (step.target && freshcheckkinds.has(step.kind)) {
|
|
11443
11899
|
const fresh = await snapshot(tab.id);
|
|
@@ -11502,6 +11958,8 @@ async function executestep(stepid) {
|
|
|
11502
11958
|
});
|
|
11503
11959
|
await stopprofileinstrumentsforrun(plan.id, "plan completion").catch(() => {
|
|
11504
11960
|
});
|
|
11961
|
+
await revertemulationforrun(plan.id, "plan completion").catch(() => {
|
|
11962
|
+
});
|
|
11505
11963
|
const done = { ...plan, state: "completed", completedat: Date.now() };
|
|
11506
11964
|
await memory.setplan(done);
|
|
11507
11965
|
await audit("complete", "Every reviewed step of the approved plan has executed.", { ...session ? { sessionid: session.id } : {}, planid: done.id });
|
|
@@ -11678,7 +12136,7 @@ async function handlerequest(message, sender) {
|
|
|
11678
12136
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
11679
12137
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
11680
12138
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
11681
|
-
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
12139
|
+
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
11682
12140
|
}
|
|
11683
12141
|
case "capabilities":
|
|
11684
12142
|
return refreshcapabilities();
|
|
@@ -12396,6 +12854,103 @@ async function handlerequest(message, sender) {
|
|
|
12396
12854
|
await refreshbadge();
|
|
12397
12855
|
return { revoked, tokenids };
|
|
12398
12856
|
}
|
|
12857
|
+
case "emulationreport": {
|
|
12858
|
+
const plan = await memory.getplan();
|
|
12859
|
+
const storedemulation = plan ? await loademulationstate(plan.id) : void 0;
|
|
12860
|
+
return emulationreport({ ...storedemulation !== void 0 ? { state: storedemulation } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() });
|
|
12861
|
+
}
|
|
12862
|
+
case "revertemulation": {
|
|
12863
|
+
const plan = await memory.getplan();
|
|
12864
|
+
if (!plan) throw new Error("No plan is available for an emulation revert.");
|
|
12865
|
+
const state = await loademulationstate(plan.id);
|
|
12866
|
+
if (!state || activelayers(state).length === 0) throw new Error("No active emulation layer covers this run.");
|
|
12867
|
+
await revertemulationforrun(plan.id, "review panel demand");
|
|
12868
|
+
return { reverted: true, layers: await memory.listlayers(plan.id) };
|
|
12869
|
+
}
|
|
12870
|
+
case "restoreemulation": {
|
|
12871
|
+
const plan = await memory.getplan();
|
|
12872
|
+
if (!plan) throw new Error("No plan is available for an emulation restore.");
|
|
12873
|
+
const stored = await memory.getemulationstate(plan.id);
|
|
12874
|
+
if (!stored || stored.layers.length === 0) throw new Error("No stored emulation layer history covers this run.");
|
|
12875
|
+
activeemulation.set(plan.id, stored);
|
|
12876
|
+
await refreshbadge();
|
|
12877
|
+
await audit("emulation", `Restored the stored emulation state of run ${plan.id} with ${activelayers(stored).length} active layer${activelayers(stored).length === 1 ? "" : "s"} on user demand after the service worker restart; the layer history stayed persisted through the run record.`, { planid: plan.id });
|
|
12878
|
+
return { restored: true, layers: stored.layers };
|
|
12879
|
+
}
|
|
12880
|
+
case "approvelocationconsent": {
|
|
12881
|
+
const inputapprove = message;
|
|
12882
|
+
const records = await memory.getlocationconsents();
|
|
12883
|
+
const record2 = records.find((item) => item.id === inputapprove.id);
|
|
12884
|
+
if (!record2) throw new Error("No location consent prompt matches the id.");
|
|
12885
|
+
const decided = { ...record2, approved: true, usedat: Date.now() };
|
|
12886
|
+
await memory.setlocationconsent(decided);
|
|
12887
|
+
await audit("consent", `Location override consent on ${record2.origin} for ${record2.latitude}, ${record2.longitude} approved from the review panel; the decision persists for those coordinates of that origin.`, { planid: record2.id });
|
|
12888
|
+
await refreshbadge();
|
|
12889
|
+
return { approved: true, origin: record2.origin, latitude: record2.latitude, longitude: record2.longitude };
|
|
12890
|
+
}
|
|
12891
|
+
case "setdevicepreset": {
|
|
12892
|
+
const inputpreset = message;
|
|
12893
|
+
const preset = devicepresetof(inputpreset.device);
|
|
12894
|
+
if (!preset) throw new Error("A reviewed device preset needs a name, positive integer width and height and a positive pixel ratio.");
|
|
12895
|
+
await memory.setdevicepreset(preset);
|
|
12896
|
+
await audit("emulation", `Stored the device preset ${preset.name} of ${preset.width} by ${preset.height} css pixels with pixel ratio ${preset.pixelratio} and the ${preset.mobile ? "mobile" : "desktop"} hint in the user curated library.`, {});
|
|
12897
|
+
return preset;
|
|
12898
|
+
}
|
|
12899
|
+
case "setnetworkpreset": {
|
|
12900
|
+
const inputpreset = message;
|
|
12901
|
+
const preset = networkpresetof(inputpreset.network);
|
|
12902
|
+
if (!preset) throw new Error("A reviewed network preset needs a name and zero or positive latency, download and upload bounds.");
|
|
12903
|
+
await memory.setnetworkpreset(preset);
|
|
12904
|
+
await audit("emulation", `Stored 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" : ""} in the user curated library.`, {});
|
|
12905
|
+
return preset;
|
|
12906
|
+
}
|
|
12907
|
+
case "setlocationpreset": {
|
|
12908
|
+
const inputpreset = message;
|
|
12909
|
+
const preset = locationpresetof(inputpreset.location);
|
|
12910
|
+
if (!preset) throw new Error("A reviewed location preset needs a name, a latitude inside -90 and 90, a longitude inside -180 and 180 and a zero or positive accuracy radius.");
|
|
12911
|
+
await memory.setlocationpreset(preset);
|
|
12912
|
+
await audit("emulation", `Stored the location preset ${preset.name} of ${preset.latitude}, ${preset.longitude} with the ${preset.accuracy} meter accuracy radius in the user curated library.`, {});
|
|
12913
|
+
return preset;
|
|
12914
|
+
}
|
|
12915
|
+
case "setagentpreset": {
|
|
12916
|
+
const inputpreset = message;
|
|
12917
|
+
const preset = agentpresetof(inputpreset.agent);
|
|
12918
|
+
if (!preset) throw new Error("A reviewed agent preset needs a user agent string of the reviewed grammar, a platform and a non-empty brand list.");
|
|
12919
|
+
await memory.setagentpreset(preset);
|
|
12920
|
+
await audit("emulation", `Stored the agent preset ${preset.name} with platform ${preset.platform} and ${preset.brands.length} brand${preset.brands.length === 1 ? "" : "s"} in the user curated library.`, {});
|
|
12921
|
+
return preset;
|
|
12922
|
+
}
|
|
12923
|
+
case "exportpresets": {
|
|
12924
|
+
const session = await memory.getsession();
|
|
12925
|
+
if (!session || session.stoppedat || session.expiresat <= Date.now()) throw new Error("Preset exports stay behind the consent gate of an active session.");
|
|
12926
|
+
const granted = await chrome.permissions.contains({ permissions: ["downloads"] }).catch(() => false);
|
|
12927
|
+
if (!granted) throw new Error("The preset export needs the downloads capability; request it from the review panel.");
|
|
12928
|
+
const file = exportpresetlibrary({ devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), now: Date.now() });
|
|
12929
|
+
const dataurl = `data:application/json;base64,${btoa(JSON.stringify(file, null, 2))}`;
|
|
12930
|
+
await chrome.downloads.download({ url: dataurl, filename: `devthink-presets-${Date.now()}.json` });
|
|
12931
|
+
await audit("emulation", `The review panel exported the versioned preset library of ${file.devices.length + file.networks.length + file.locations.length + file.agents.length} preset${file.devices.length + file.networks.length + file.locations.length + file.agents.length === 1 ? "" : "s"} through the reviewed download flow.`, { sessionid: session.id });
|
|
12932
|
+
return { exported: file.devices.length + file.networks.length + file.locations.length + file.agents.length, version: file.version };
|
|
12933
|
+
}
|
|
12934
|
+
case "importpresets": {
|
|
12935
|
+
const inputimport = message;
|
|
12936
|
+
const library = importpresetlibrary(inputimport.file);
|
|
12937
|
+
if (!library) throw new Error("The reviewed preset file carries no valid preset of any family; the import is refused.");
|
|
12938
|
+
for (const preset of library.devices) await memory.setdevicepreset(preset);
|
|
12939
|
+
for (const preset of library.networks) await memory.setnetworkpreset(preset);
|
|
12940
|
+
for (const preset of library.locations) await memory.setlocationpreset(preset);
|
|
12941
|
+
for (const preset of library.agents) await memory.setagentpreset(preset);
|
|
12942
|
+
const session = await memory.getsession();
|
|
12943
|
+
await audit("emulation", `Imported the reviewed preset library version ${library.version} with ${library.devices.length} device, ${library.networks.length} network, ${library.locations.length} location and ${library.agents.length} agent preset${library.devices.length + library.networks.length + library.locations.length + library.agents.length === 1 ? "" : "s"} through review.`, { ...session ? { sessionid: session.id } : {} });
|
|
12944
|
+
return { imported: library.devices.length + library.networks.length + library.locations.length + library.agents.length, version: library.version };
|
|
12945
|
+
}
|
|
12946
|
+
case "setemulationretention": {
|
|
12947
|
+
const inputretention = message;
|
|
12948
|
+
const settings = await memory.getsettings();
|
|
12949
|
+
const retention = typeof inputretention.retention === "number" && Number.isInteger(inputretention.retention) && inputretention.retention >= 0 ? inputretention.retention : void 0;
|
|
12950
|
+
await memory.setsettings({ ...settings, ...retention !== void 0 ? { emulationretention: retention } : {} });
|
|
12951
|
+
await audit("configure", `The user set the reverted emulation layer state retention to ${retention === void 0 ? "keep every prior state" : retention} layer${retention === 1 ? "" : "s"}; the layer history itself always survives.`);
|
|
12952
|
+
return { emulationretention: retention };
|
|
12953
|
+
}
|
|
12399
12954
|
case "revertproxyroute": {
|
|
12400
12955
|
const inputrevert = message;
|
|
12401
12956
|
const plan = await memory.getplan();
|
|
@@ -12607,11 +13162,15 @@ async function handlerequest(message, sender) {
|
|
|
12607
13162
|
});
|
|
12608
13163
|
await stopprofileinstrumentsforrun(stoppedplan.id, "run cancel").catch(() => {
|
|
12609
13164
|
});
|
|
13165
|
+
await revertemulationforrun(stoppedplan.id, "run cancel").catch(() => {
|
|
13166
|
+
});
|
|
12610
13167
|
} else {
|
|
12611
13168
|
await closechannelsforrun("none").catch(() => {
|
|
12612
13169
|
});
|
|
12613
13170
|
await revertcontrolsforrun("none", "run cancel").catch(() => {
|
|
12614
13171
|
});
|
|
13172
|
+
await revertemulationforrun("none", "run cancel").catch(() => {
|
|
13173
|
+
});
|
|
12615
13174
|
}
|
|
12616
13175
|
for (const [runid, active] of [...activecdpsessions.entries()]) {
|
|
12617
13176
|
active.cancelled = true;
|
|
@@ -12620,6 +13179,10 @@ async function handlerequest(message, sender) {
|
|
|
12620
13179
|
await stopprofileinstrumentsforrun(runid, "run cancel").catch(() => {
|
|
12621
13180
|
});
|
|
12622
13181
|
}
|
|
13182
|
+
for (const runid of [...activeemulation.keys()]) {
|
|
13183
|
+
await revertemulationforrun(runid, "run cancel").catch(() => {
|
|
13184
|
+
});
|
|
13185
|
+
}
|
|
12623
13186
|
for (const [id, active] of [...activerecordings.entries()]) {
|
|
12624
13187
|
const finished = finishrecording(active.record, Date.now());
|
|
12625
13188
|
await memory.addmedia(finished).catch(() => {
|
|
@@ -12650,6 +13213,12 @@ async function reconcilewatches() {
|
|
|
12650
13213
|
}
|
|
12651
13214
|
reconcilewatches().catch(() => {
|
|
12652
13215
|
});
|
|
13216
|
+
async function restoreemulationstate() {
|
|
13217
|
+
const plan = await memory.getplan();
|
|
13218
|
+
if (plan) await loademulationstate(plan.id);
|
|
13219
|
+
}
|
|
13220
|
+
restoreemulationstate().catch(() => {
|
|
13221
|
+
});
|
|
12653
13222
|
chrome.runtime.onConnect.addListener((port) => {
|
|
12654
13223
|
if (port.name !== "devthinksidepanel" || port.sender?.id !== chrome.runtime.id || !port.sender.url?.startsWith(chrome.runtime.getURL(""))) return port.disconnect();
|
|
12655
13224
|
port.onMessage.addListener((message) => {
|