@wenathlan/extension 1.1.43 → 1.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1648,15 +1648,22 @@ var sessionmemory = class {
1648
1648
  async getfetchconsents() {
1649
1649
  return await this.adapter.get("fetchconsents") ?? [];
1650
1650
  }
1651
- /** Stores one api key reference record without any key material; the secret value stays behind its storage id. */
1652
- async setapikey(ref) {
1653
- const records = (await this.adapter.get("apikeys") ?? []).filter((item) => item.name !== ref.name);
1654
- await this.adapter.set("apikeys", [ref, ...records]);
1651
+ /** Stores one api key entry with its origin scope and created time, replacing the previous entry of that name; the key material stays behind its storage id. */
1652
+ async setapikey(entry) {
1653
+ const records = (await this.adapter.get("apikeys") ?? []).filter((item) => item.name !== entry.name);
1654
+ await this.adapter.set("apikeys", [entry, ...records]);
1655
1655
  }
1656
- /** Returns every stored api key reference with its origin scope, header name and storage id; key material never loads here. */
1656
+ /** Returns every stored api key entry with its origin scope, header name, storage id and last use timestamp; key material never loads here. */
1657
1657
  async getapikeys() {
1658
1658
  return await this.adapter.get("apikeys") ?? [];
1659
1659
  }
1660
+ /** Stamps the last use timestamp of one stored api key entry without ever loading the key material. */
1661
+ async touchapikey(name, at) {
1662
+ const records = await this.getapikeys();
1663
+ const entry = records.find((item) => item.name === name);
1664
+ if (!entry) return;
1665
+ await this.adapter.set("apikeys", [{ ...entry, lastuse: at }, ...records.filter((item) => item.name !== name)]);
1666
+ }
1660
1667
  /** Removes one api key reference and its stored secret together. */
1661
1668
  async removeapikey(name) {
1662
1669
  const records = await this.getapikeys();
@@ -1753,6 +1760,171 @@ var sessionmemory = class {
1753
1760
  async getsubscriptions() {
1754
1761
  return await this.adapter.get("subscriptions") ?? [];
1755
1762
  }
1763
+ /** Stores one registered request block rule of a run, replacing the previous rule of that id; every rule reverts and stays auditable after the run ends. */
1764
+ async addblockrule(rule) {
1765
+ const records = (await this.adapter.get("blockrules") ?? []).filter((item) => item.id !== rule.id);
1766
+ await this.adapter.set("blockrules", [rule, ...records]);
1767
+ }
1768
+ /** Returns every stored block rule, newest first. */
1769
+ async getblockrules() {
1770
+ return await this.adapter.get("blockrules") ?? [];
1771
+ }
1772
+ /** Stores one registered response mock fixture of a run, replacing the previous fixture of that id; the fixture body stays out of every audit trail. */
1773
+ async addmockspec(spec) {
1774
+ const records = (await this.adapter.get("mockspecs") ?? []).filter((item) => item.id !== spec.id);
1775
+ await this.adapter.set("mockspecs", [spec, ...records]);
1776
+ }
1777
+ /** Returns every stored mock fixture, newest first. */
1778
+ async getmockspecs() {
1779
+ return await this.adapter.get("mockspecs") ?? [];
1780
+ }
1781
+ /** Stores one registered header rewrite rule of a run, replacing the previous rule of that id; the provenance of every applied rule stays auditable. */
1782
+ async addheaderule(rule) {
1783
+ const records = (await this.adapter.get("headerules") ?? []).filter((item) => item.id !== rule.id);
1784
+ await this.adapter.set("headerules", [rule, ...records]);
1785
+ }
1786
+ /** Returns every stored header rewrite rule, newest first. */
1787
+ async getheaderules() {
1788
+ return await this.adapter.get("headerules") ?? [];
1789
+ }
1790
+ /** Stores one cookie operation of a run per domain with its timestamp; cookie values never enter the operation record. */
1791
+ async addcookieop(operation) {
1792
+ const records = await this.adapter.get("cookieops") ?? [];
1793
+ await this.adapter.set("cookieops", [operation, ...records]);
1794
+ }
1795
+ /** Returns every stored cookie operation, newest first, optionally filtered by domain. */
1796
+ async getcookieops(domain) {
1797
+ const records = await this.adapter.get("cookieops") ?? [];
1798
+ return records.filter((item) => domain === void 0 || item.domain === domain);
1799
+ }
1800
+ /** Stores one token record of a provider with scopes, origin scope and expiry; the token values stay behind their storage ids. */
1801
+ async addtoken(record2) {
1802
+ const records = (await this.adapter.get("tokens") ?? []).filter((item) => item.id !== record2.id);
1803
+ await this.adapter.set("tokens", [record2, ...records]);
1804
+ }
1805
+ /** Returns every stored token record, newest first, optionally filtered by provider; token values never load here. */
1806
+ async listtokens(provider) {
1807
+ const records = await this.adapter.get("tokens") ?? [];
1808
+ return records.filter((item) => provider === void 0 || item.provider === provider);
1809
+ }
1810
+ /** Stores one applied proxy route of a run with its apply time, replacing the previous route of that id; the history keeps the revert times. */
1811
+ async addproxyroute(route) {
1812
+ const records = (await this.adapter.get("proxyroutes") ?? []).filter((item) => item.id !== route.id);
1813
+ await this.adapter.set("proxyroutes", [route, ...records]);
1814
+ }
1815
+ /** Returns every stored proxy route with apply and revert times, newest first. */
1816
+ async getproxyroutes() {
1817
+ return await this.adapter.get("proxyroutes") ?? [];
1818
+ }
1819
+ /** Stores one parsed rate limit read per origin, replacing the previous read of that origin. */
1820
+ async setratelimit(read) {
1821
+ const records = (await this.adapter.get("ratelimits") ?? []).filter((item) => item.origin !== read.origin);
1822
+ await this.adapter.set("ratelimits", [read, ...records]);
1823
+ }
1824
+ /** Returns every stored rate limit read whose reset window has not passed yet; expired states drop out at their reset windows. */
1825
+ async getratelimits(now) {
1826
+ const records = await this.adapter.get("ratelimits") ?? [];
1827
+ const live = records.filter((item) => item.resetat > now);
1828
+ if (live.length !== records.length) await this.adapter.set("ratelimits", live);
1829
+ return live;
1830
+ }
1831
+ /** Stores one run timeline entry; the user configured timeline retention window expires the oldest entries while their level counts survive in the per run level summaries. */
1832
+ async addtimelineentry(entry) {
1833
+ const records = await this.gettimeline();
1834
+ const combined = [entry, ...records];
1835
+ const retention = (await this.getsettings())?.timelineretention;
1836
+ if (retention === void 0) {
1837
+ await this.adapter.set("timelineentries", combined);
1838
+ return;
1839
+ }
1840
+ const kept = combined.slice(0, retention);
1841
+ const expired = combined.slice(retention);
1842
+ if (expired.length > 0) {
1843
+ const expiredcounts = /* @__PURE__ */ new Map();
1844
+ for (const item of expired) {
1845
+ const counts = expiredcounts.get(item.runid) ?? {};
1846
+ counts[item.level] = (counts[item.level] ?? 0) + 1;
1847
+ expiredcounts.set(item.runid, counts);
1848
+ }
1849
+ for (const [runid, counts] of expiredcounts) await this.mergelevelsummary(runid, counts, Date.now());
1850
+ }
1851
+ await this.adapter.set("timelineentries", kept);
1852
+ }
1853
+ /** Returns every stored run timeline entry, newest first. */
1854
+ async gettimeline() {
1855
+ return await this.adapter.get("timelineentries") ?? [];
1856
+ }
1857
+ /** Returns the run timeline entries filtered by run, level and step id. */
1858
+ async listtimeline(filter) {
1859
+ const records = await this.gettimeline();
1860
+ return records.filter((item) => (filter.runid === void 0 || item.runid === filter.runid) && (filter.level === void 0 || item.level === filter.level) && (filter.stepid === void 0 || item.stepid === filter.stepid));
1861
+ }
1862
+ /** Stores one captured javascript error record with its stack frames, source url and line. */
1863
+ async adderrorrecord(record2) {
1864
+ const records = await this.adapter.get("errorrecords") ?? [];
1865
+ await this.adapter.set("errorrecords", [record2, ...records]);
1866
+ }
1867
+ /** Returns every stored error record, newest first. */
1868
+ async geterrorrecords() {
1869
+ return await this.adapter.get("errorrecords") ?? [];
1870
+ }
1871
+ /** Stores one captured unhandled rejection record with its reason and stack frames. */
1872
+ async addrejectionrecord(record2) {
1873
+ const records = await this.adapter.get("rejectionrecords") ?? [];
1874
+ await this.adapter.set("rejectionrecords", [record2, ...records]);
1875
+ }
1876
+ /** Returns every stored rejection record, newest first. */
1877
+ async getrejectionrecords() {
1878
+ return await this.adapter.get("rejectionrecords") ?? [];
1879
+ }
1880
+ /** Stores one captured long task entry with its duration, start time and attribution names. */
1881
+ async addlongtask(record2) {
1882
+ const records = await this.adapter.get("longtasks") ?? [];
1883
+ await this.adapter.set("longtasks", [record2, ...records]);
1884
+ }
1885
+ /** Returns every stored long task entry, newest first. */
1886
+ async getlongtasks() {
1887
+ return await this.adapter.get("longtasks") ?? [];
1888
+ }
1889
+ /** Stores one console diff result between two runs, replacing the previous one. */
1890
+ async addconsolediff(diff) {
1891
+ return this.adapter.set("consolediff", diff);
1892
+ }
1893
+ /** Returns the one stored console diff result. */
1894
+ async getdiff() {
1895
+ return this.adapter.get("consolediff");
1896
+ }
1897
+ /** Stores one log rotation target record with its overflow entry counts, replacing the previous record of that target and run. */
1898
+ async addrotationtarget(record2) {
1899
+ const records = (await this.adapter.get("rotationtargets") ?? []).filter((item) => !(item.target === record2.target && item.runid === record2.runid));
1900
+ await this.adapter.set("rotationtargets", [record2, ...records]);
1901
+ }
1902
+ /** Returns every stored rotation target record with its overflow entry counts, newest first. */
1903
+ async getrotationtargets() {
1904
+ return await this.adapter.get("rotationtargets") ?? [];
1905
+ }
1906
+ /** Stores one console capture consent decision per origin; the approved decision persists so console watching on that origin prompts once. */
1907
+ async setconsoleconsent(consent) {
1908
+ const records = (await this.adapter.get("consoleconsents") ?? []).filter((item) => item.id !== consent.id);
1909
+ await this.adapter.set("consoleconsents", [consent, ...records]);
1910
+ }
1911
+ /** Returns every console capture consent decision, newest first. */
1912
+ async getconsoleconsents() {
1913
+ return await this.adapter.get("consoleconsents") ?? [];
1914
+ }
1915
+ /** Merges expired entry counts into the per run level count summary that survives the retention window. */
1916
+ async mergelevelsummary(runid, counts, now) {
1917
+ const records = await this.getlevelsummaries();
1918
+ const existing = records.find((item) => item.runid === runid);
1919
+ const merged = { ...existing?.counts ?? {} };
1920
+ for (const [level, count] of Object.entries(counts)) merged[level] = (merged[level] ?? 0) + count;
1921
+ const updated = { runid, counts: merged, at: now };
1922
+ await this.adapter.set("levelsummaries", [updated, ...records.filter((item) => item.runid !== runid)]);
1923
+ }
1924
+ /** Returns every per run level count summary, newest first. */
1925
+ async getlevelsummaries() {
1926
+ return await this.adapter.get("levelsummaries") ?? [];
1927
+ }
1756
1928
  };
1757
1929
  function mediakindof(record2) {
1758
1930
  if ("pages" in record2) return "pdf";
@@ -1796,6 +1968,315 @@ function randomid() {
1796
1968
  return crypto.randomUUID();
1797
1969
  }
1798
1970
 
1971
+ // netauth.ts
1972
+ function oauthflowof(value) {
1973
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1974
+ const options = value;
1975
+ if (typeof options.provider !== "string" || !options.provider.trim()) return void 0;
1976
+ if (typeof options.authorizeurl !== "string" || !options.authorizeurl.trim()) return void 0;
1977
+ if (typeof options.tokenurl !== "string" || !options.tokenurl.trim()) return void 0;
1978
+ if (!Array.isArray(options.scopes) || options.scopes.length === 0 || !options.scopes.every((item) => typeof item === "string" && item.trim().length > 0)) return void 0;
1979
+ if (typeof options.redirectorigin !== "string" || !options.redirectorigin.trim()) return void 0;
1980
+ return { provider: options.provider.trim(), authorizeurl: options.authorizeurl.trim(), tokenurl: options.tokenurl.trim(), scopes: options.scopes.map((item) => item.trim()), redirectorigin: options.redirectorigin.trim() };
1981
+ }
1982
+ function authorizeurl(flow, state) {
1983
+ const url = new URL(flow.authorizeurl);
1984
+ url.searchParams.set("response_type", "code");
1985
+ url.searchParams.set("redirect_uri", flow.redirectorigin);
1986
+ url.searchParams.set("scope", flow.scopes.join(" "));
1987
+ url.searchParams.set("state", state);
1988
+ return url.toString();
1989
+ }
1990
+ function capturecode(url, redirectorigin, state) {
1991
+ let parsed;
1992
+ try {
1993
+ parsed = new URL(url);
1994
+ } catch {
1995
+ return { error: "The redirect url does not parse for the code capture." };
1996
+ }
1997
+ const granted = redirectorigin.includes("/", redirectorigin.indexOf("://") + 3) ? `${parsed.origin}${parsed.pathname}`.startsWith(redirectorigin) : parsed.origin === redirectorigin;
1998
+ if (!granted) return { error: `The redirect landed on ${parsed.origin} outside the granted redirect origin ${redirectorigin}.` };
1999
+ const returned = parsed.searchParams.get("state");
2000
+ if (returned !== state) return { error: "The redirect state token does not match the reviewed flow." };
2001
+ const error = parsed.searchParams.get("error");
2002
+ if (error) return { error: `The provider refused the flow: ${error}.` };
2003
+ const code = parsed.searchParams.get("code");
2004
+ if (!code) return { error: "The redirect carries no authorization code." };
2005
+ return { code };
2006
+ }
2007
+ function parsetokens(body) {
2008
+ let parsed;
2009
+ try {
2010
+ parsed = JSON.parse(body);
2011
+ } catch {
2012
+ return void 0;
2013
+ }
2014
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
2015
+ const record2 = parsed;
2016
+ const tokens = {};
2017
+ if (typeof record2.access_token === "string" && record2.access_token) tokens.accesstoken = record2.access_token;
2018
+ if (typeof record2.refresh_token === "string" && record2.refresh_token) tokens.refreshtoken = record2.refresh_token;
2019
+ if (typeof record2.expires_in === "number" && Number.isFinite(record2.expires_in) && record2.expires_in >= 0) tokens.expiresin = record2.expires_in;
2020
+ if (typeof record2.scope === "string" && record2.scope.trim()) tokens.scopes = record2.scope.trim().split(/\s+/);
2021
+ if (tokens.accesstoken === void 0 && tokens.refreshtoken === void 0) return void 0;
2022
+ return tokens;
2023
+ }
2024
+ function tokenrequest(flow, input) {
2025
+ if (input.refreshtoken !== void 0) return { url: flow.tokenurl, body: urlencodeform([{ name: "grant_type", value: "refresh_token" }, { name: "refresh_token", value: input.refreshtoken }]) };
2026
+ return { url: flow.tokenurl, body: urlencodeform([{ name: "grant_type", value: "authorization_code" }, { name: "code", value: input.code ?? "" }, { name: "redirect_uri", value: flow.redirectorigin }]) };
2027
+ }
2028
+ function revocationruleof(value) {
2029
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2030
+ const options = value;
2031
+ if (!Array.isArray(options.tokenids) || options.tokenids.length === 0 || !options.tokenids.every((item) => typeof item === "string" && item.trim().length > 0)) return void 0;
2032
+ if (typeof options.reason !== "string" || !options.reason.trim()) return void 0;
2033
+ return { tokenids: options.tokenids.map((item) => item.trim()), reason: options.reason.trim(), revokedat: Date.now() };
2034
+ }
2035
+ function formpayloadof(value) {
2036
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2037
+ const options = value;
2038
+ if (typeof options.url !== "string" || !options.url.trim()) return void 0;
2039
+ if (!Array.isArray(options.fields) || options.fields.length === 0) return void 0;
2040
+ const fields = [];
2041
+ for (const item of options.fields) {
2042
+ if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
2043
+ const field = item;
2044
+ if (typeof field.name !== "string" || !field.name.trim()) return void 0;
2045
+ if (typeof field.value !== "string") return void 0;
2046
+ fields.push({ name: field.name.trim(), value: field.value });
2047
+ }
2048
+ return { url: options.url.trim(), fields };
2049
+ }
2050
+ function urlencodeform(fields) {
2051
+ return fields.map((field) => `${formencode(field.name)}=${formencode(field.value)}`).join("&");
2052
+ }
2053
+ function formencode(value) {
2054
+ const bytes = [...new TextEncoder().encode(value)];
2055
+ return bytes.map((byte) => byte >= 65 && byte <= 90 || byte >= 97 && byte <= 122 || byte >= 48 && byte <= 57 || byte === 45 || byte === 95 || byte === 46 || byte === 126 ? String.fromCharCode(byte) : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`).join("");
2056
+ }
2057
+ function multipartpayloadof(value) {
2058
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2059
+ const options = value;
2060
+ if (typeof options.url !== "string" || !options.url.trim()) return void 0;
2061
+ if (!Array.isArray(options.files) || options.files.length === 0) return void 0;
2062
+ const fields = [];
2063
+ for (const item of Array.isArray(options.fields) ? options.fields : []) {
2064
+ if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
2065
+ const field = item;
2066
+ if (typeof field.name !== "string" || !field.name.trim()) return void 0;
2067
+ if (typeof field.value !== "string") return void 0;
2068
+ fields.push({ name: field.name.trim(), value: field.value });
2069
+ }
2070
+ const files = [];
2071
+ for (const item of options.files) {
2072
+ if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
2073
+ const file = item;
2074
+ if (typeof file.name !== "string" || !file.name.trim()) return void 0;
2075
+ if (typeof file.filename !== "string" || !file.filename.trim()) return void 0;
2076
+ if (typeof file.mime !== "string" || !file.mime.trim()) return void 0;
2077
+ if (typeof file.content !== "string") return void 0;
2078
+ if (file.reviewed !== true) return void 0;
2079
+ files.push({ name: file.name.trim(), filename: file.filename.trim(), mime: file.mime.trim(), content: file.content, reviewed: true });
2080
+ }
2081
+ const payload = { url: options.url.trim(), fields, files, ...typeof options.boundary === "string" && options.boundary.trim() ? { boundary: options.boundary.trim() } : {} };
2082
+ return payload;
2083
+ }
2084
+ function newboundary() {
2085
+ return `----devthink${Math.random().toString(16).slice(2)}${Date.now().toString(16)}`;
2086
+ }
2087
+ function multipartchunks(payload) {
2088
+ const boundary = payload.boundary ?? newboundary();
2089
+ const chunks = [];
2090
+ for (const field of payload.fields) chunks.push(`--${boundary}\r
2091
+ content-disposition: form-data; name="${field.name}"\r
2092
+ \r
2093
+ ${field.value}\r
2094
+ `);
2095
+ for (const file of payload.files) chunks.push(`--${boundary}\r
2096
+ content-disposition: form-data; name="${file.name}"; filename="${file.filename}"\r
2097
+ content-type: ${file.mime}\r
2098
+ \r
2099
+ ${file.content}\r
2100
+ `);
2101
+ chunks.push(`--${boundary}--\r
2102
+ `);
2103
+ return { chunks, boundary, bytes: chunks.reduce((total, chunk) => total + chunk.length, 0) };
2104
+ }
2105
+
2106
+ // netcontrol.ts
2107
+ var controlkinds = ["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"];
2108
+ function patternorigin(pattern) {
2109
+ const trimmed = pattern.trim();
2110
+ if (!trimmed.startsWith("https://")) return void 0;
2111
+ const rest = trimmed.slice("https://".length);
2112
+ const host = rest.split("/")[0] ?? "";
2113
+ if (!host.trim()) return void 0;
2114
+ return `https://${host.toLowerCase()}`;
2115
+ }
2116
+ function matchurlpattern(pattern, url) {
2117
+ const origin = patternorigin(pattern);
2118
+ if (!origin) return false;
2119
+ let parsed;
2120
+ try {
2121
+ parsed = new URL(url);
2122
+ } catch {
2123
+ return false;
2124
+ }
2125
+ if (parsed.origin !== origin) return false;
2126
+ const patternpath = pattern.trim().slice(origin.length);
2127
+ if (patternpath === "" || patternpath === "/") return true;
2128
+ const segments = patternpath.split("/").filter((segment) => segment !== "");
2129
+ if (segments.includes("**")) return true;
2130
+ const pathsegments = parsed.pathname.split("/").filter((segment) => segment !== "");
2131
+ if (segments.length !== pathsegments.length) return false;
2132
+ return segments.every((segment, index) => segment === pathsegments[index] || segment.includes("*") && new RegExp(`^${segment.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`).test(pathsegments[index] ?? ""));
2133
+ }
2134
+ function blockruleof(value) {
2135
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2136
+ const options = value;
2137
+ if (typeof options.urlpattern !== "string" || !options.urlpattern.trim()) return void 0;
2138
+ const rule = { urlpattern: options.urlpattern.trim() };
2139
+ if (Array.isArray(options.resourcetypes)) {
2140
+ const types = options.resourcetypes.filter((item) => typeof item === "string" && item.trim().length > 0);
2141
+ if (types.length === 0) return void 0;
2142
+ rule.resourcetypes = types;
2143
+ }
2144
+ return rule;
2145
+ }
2146
+ function newblockrule(input) {
2147
+ return { id: input.id, runid: input.runid, stepid: input.stepid, urlpattern: input.urlpattern, ...input.resourcetypes !== void 0 ? { resourcetypes: input.resourcetypes } : {}, hits: 0, registeredat: input.at };
2148
+ }
2149
+ function mockspecof(value) {
2150
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2151
+ const options = value;
2152
+ if (typeof options.urlpattern !== "string" || !options.urlpattern.trim()) return void 0;
2153
+ if (typeof options.status !== "number" || !Number.isInteger(options.status) || options.status < 100 || options.status > 599) return void 0;
2154
+ const hasbody = typeof options.body === "string";
2155
+ const bodyref = typeof options.bodyref === "string" ? options.bodyref.trim() : "";
2156
+ if (!hasbody && bodyref === "") return void 0;
2157
+ const spec = { urlpattern: options.urlpattern.trim(), status: options.status };
2158
+ if (hasbody) spec.body = options.body;
2159
+ if (bodyref !== "") spec.bodyref = bodyref;
2160
+ if (options.headers && typeof options.headers === "object" && !Array.isArray(options.headers)) spec.headers = options.headers;
2161
+ if (options.reviewed === true) spec.reviewed = true;
2162
+ return spec;
2163
+ }
2164
+ function newmockspec(input) {
2165
+ return { id: input.id, runid: input.runid, stepid: input.stepid, urlpattern: input.urlpattern, status: input.status, ...input.headers !== void 0 ? { headers: input.headers } : {}, ...input.body !== void 0 ? { body: input.body } : {}, ...input.bodyref !== void 0 ? { bodyref: input.bodyref } : {}, reviewed: input.reviewed, hits: 0, registeredat: input.at };
2166
+ }
2167
+ function mockfor(url, specs) {
2168
+ return specs.find((spec) => spec.revertedat === void 0 && matchurlpattern(spec.urlpattern, url));
2169
+ }
2170
+ function headeruleof(value) {
2171
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2172
+ const options = value;
2173
+ if (typeof options.urlpattern !== "string" || !options.urlpattern.trim()) return void 0;
2174
+ if (typeof options.name !== "string" || !options.name.trim()) return void 0;
2175
+ if (options.operation !== "set" && options.operation !== "append" && options.operation !== "remove") return void 0;
2176
+ if (options.operation === "remove" && options.value !== void 0) return void 0;
2177
+ if (options.operation !== "remove" && typeof options.value !== "string") return void 0;
2178
+ const rule = { urlpattern: options.urlpattern.trim(), name: options.name.trim(), operation: options.operation };
2179
+ if (options.operation !== "remove") rule.value = typeof options.value === "string" ? options.value : "";
2180
+ return rule;
2181
+ }
2182
+ function newheaderule(input) {
2183
+ return { id: input.id, runid: input.runid, stepid: input.stepid, urlpattern: input.urlpattern, name: input.name, operation: input.operation, ...input.value !== void 0 ? { value: input.value } : {}, hits: 0, registeredat: input.at };
2184
+ }
2185
+ function applyheaderules(url, headers, rules) {
2186
+ const rewritten = { ...headers };
2187
+ const applied = [];
2188
+ for (const rule of rules) {
2189
+ if (rule.revertedat !== void 0) continue;
2190
+ if (!matchurlpattern(rule.urlpattern, url)) continue;
2191
+ const name = rule.name;
2192
+ if (rule.operation === "remove") {
2193
+ delete rewritten[name];
2194
+ applied.push(rule);
2195
+ continue;
2196
+ }
2197
+ const value = rule.value ?? "";
2198
+ if (rule.operation === "set") rewritten[name] = value;
2199
+ else rewritten[name] = rewritten[name] !== void 0 ? `${rewritten[name]}, ${value}` : value;
2200
+ applied.push(rule);
2201
+ }
2202
+ return { headers: rewritten, applied };
2203
+ }
2204
+ function revertrule(rule, at) {
2205
+ if (rule.revertedat !== void 0) return rule;
2206
+ return { ...rule, revertedat: at };
2207
+ }
2208
+ function cookierecordof(value) {
2209
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2210
+ const options = value;
2211
+ if (typeof options.name !== "string" || !options.name.trim()) return void 0;
2212
+ if (typeof options.domain !== "string" || !options.domain.trim()) return void 0;
2213
+ if (typeof options.path !== "string" || !options.path.trim()) return void 0;
2214
+ if (typeof options.value !== "string") return void 0;
2215
+ const record2 = { name: options.name.trim(), domain: options.domain.trim().toLowerCase(), path: options.path.trim(), value: options.value };
2216
+ if (typeof options.expiresat === "number" && Number.isFinite(options.expiresat)) record2.expiresat = options.expiresat;
2217
+ return record2;
2218
+ }
2219
+ function cookiedomaingranted(domain, grants) {
2220
+ const host = domain.trim().toLowerCase().replace(/^\./, "");
2221
+ return grants.some((grant) => {
2222
+ let granthost = "";
2223
+ try {
2224
+ granthost = new URL(grant).hostname.toLowerCase();
2225
+ } catch {
2226
+ return false;
2227
+ }
2228
+ return host === granthost || host.endsWith(`.${granthost}`);
2229
+ });
2230
+ }
2231
+ function redactedcookies(records) {
2232
+ return records.map((record2) => ({ name: record2.name, domain: record2.domain, path: record2.path, ...record2.expiresat !== void 0 ? { expiresat: record2.expiresat } : {} }));
2233
+ }
2234
+ function proxyrouteof(value) {
2235
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2236
+ const options = value;
2237
+ if (options.scheme !== "http" && options.scheme !== "https" && options.scheme !== "socks4" && options.scheme !== "socks5") return void 0;
2238
+ if (typeof options.host !== "string" || !options.host.trim()) return void 0;
2239
+ if (typeof options.port !== "number" || !Number.isInteger(options.port) || options.port < 1 || options.port > 65535) return void 0;
2240
+ if (!Array.isArray(options.bypass) || options.bypass.length === 0 || !options.bypass.every((item) => typeof item === "string" && item.trim().length > 0)) return void 0;
2241
+ return { scheme: options.scheme, host: options.host.trim(), port: options.port, bypass: options.bypass.map((item) => item.trim()) };
2242
+ }
2243
+ function ratelimitreadof(headers, origin, now) {
2244
+ const pick = (name) => {
2245
+ for (const key of Object.keys(headers)) {
2246
+ if (key.toLowerCase() !== name) continue;
2247
+ const value = Number(headers[key]);
2248
+ return Number.isFinite(value) && value >= 0 ? value : void 0;
2249
+ }
2250
+ return void 0;
2251
+ };
2252
+ const remaining = pick("x-ratelimit-remaining");
2253
+ const limit = pick("x-ratelimit-limit");
2254
+ const reset = pick("x-ratelimit-reset");
2255
+ if (remaining === void 0 && limit === void 0 && reset === void 0) return void 0;
2256
+ const read = { origin, ...remaining !== void 0 ? { remaining } : {}, ...limit !== void 0 ? { limit } : {}, resetat: now, at: now };
2257
+ if (reset !== void 0) read.resetat = reset > Math.floor(now / 1e3) ? reset * 1e3 : now + reset * 1e3;
2258
+ return read;
2259
+ }
2260
+ function retryafterof(status, headers) {
2261
+ if (status !== 429 && status !== 503) return void 0;
2262
+ for (const key of Object.keys(headers)) {
2263
+ if (key.toLowerCase() !== "retry-after") continue;
2264
+ const raw = headers[key];
2265
+ if (raw === void 0) continue;
2266
+ const value = raw.trim();
2267
+ const seconds = Number(value);
2268
+ if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
2269
+ const date = Date.parse(value);
2270
+ if (Number.isFinite(date)) return Math.max(0, date - Date.now());
2271
+ return void 0;
2272
+ }
2273
+ return void 0;
2274
+ }
2275
+ function ratelimitwait(state, now) {
2276
+ if (!state) return 0;
2277
+ return Math.max(0, state.resetat - now);
2278
+ }
2279
+
1799
2280
  // netwatch.ts
1800
2281
  var netwatchkinds = ["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"];
1801
2282
  function resourcefacts(entries) {
@@ -1980,6 +2461,195 @@ function extractvalues(body, paths) {
1980
2461
  return fields.map((field) => ({ path: field.path, ...field.value !== void 0 ? { value: field.value } : {}, ...field.missing ? { missing: true } : {} }));
1981
2462
  }
1982
2463
 
2464
+ // runtimeline.ts
2465
+ var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
2466
+ var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
2467
+ var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network"];
2468
+ function levelrank(level) {
2469
+ return loglevels.indexOf(level);
2470
+ }
2471
+ function redactconsoletext(text2, patterns) {
2472
+ let redacted = text2;
2473
+ for (const pattern of patterns) {
2474
+ if (!pattern) continue;
2475
+ while (redacted.includes(pattern)) redacted = redacted.replace(pattern, "[redacted]");
2476
+ }
2477
+ return redacted;
2478
+ }
2479
+ function argkind(value) {
2480
+ if (value === null) return "null";
2481
+ if (Array.isArray(value)) return "array";
2482
+ if (value instanceof Error) return "error";
2483
+ switch (typeof value) {
2484
+ case "string":
2485
+ return "string";
2486
+ case "number":
2487
+ return "number";
2488
+ case "boolean":
2489
+ return "boolean";
2490
+ case "bigint":
2491
+ return "bigint";
2492
+ case "symbol":
2493
+ return "symbol";
2494
+ case "function":
2495
+ return "function";
2496
+ case "undefined":
2497
+ return "undefined";
2498
+ default:
2499
+ return "object";
2500
+ }
2501
+ }
2502
+ function serializearg(value, depth) {
2503
+ const render = (item, remaining) => {
2504
+ if (item instanceof Error) return `${item.name}: ${item.message}`;
2505
+ if (typeof item === "string") return item;
2506
+ if (typeof item === "function") return `[function ${item.name || "anonymous"}]`;
2507
+ if (typeof item === "bigint") return `${item}n`;
2508
+ if (typeof item === "symbol") return item.toString();
2509
+ if (item === null || item === void 0 || typeof item !== "object") return String(item);
2510
+ if (remaining <= 0) {
2511
+ const tag = Array.isArray(item) ? "Array" : item.constructor?.name ?? "Object";
2512
+ return `[${tag}]`;
2513
+ }
2514
+ if (Array.isArray(item)) return `[${item.map((entry) => render(entry, remaining - 1)).join(", ")}]`;
2515
+ const record2 = item;
2516
+ return `{${Object.keys(record2).map((key) => `${key}: ${render(record2[key], remaining - 1)}`).join(", ")}}`;
2517
+ };
2518
+ return render(value, Math.max(0, depth));
2519
+ }
2520
+ function consolecapture(input) {
2521
+ const parts = input.args.map((arg) => serializearg(arg, input.depth));
2522
+ return { level: input.level, text: redactconsoletext(parts.join(" "), input.redact), argkinds: input.args.map((arg) => argkind(arg)), repeat: 1 };
2523
+ }
2524
+ function stackframes(stacktext) {
2525
+ const frames = [];
2526
+ for (const row of stacktext.split("\n")) {
2527
+ const trimmed = row.trim();
2528
+ if (!trimmed.startsWith("at ")) continue;
2529
+ const body = trimmed.slice(3).trim();
2530
+ const location = body.match(/\(([^()]*:\d+:\d+)\)$/) ?? body.match(/^(.*:\d+:\d+)$/);
2531
+ const located = location?.[1];
2532
+ if (!located) continue;
2533
+ const segments = located.split(":");
2534
+ const column = Number.parseInt(segments.pop() ?? "", 10);
2535
+ const lineno = Number.parseInt(segments.pop() ?? "", 10);
2536
+ const url = segments.join(":");
2537
+ if (!Number.isFinite(lineno) || lineno < 0) continue;
2538
+ const name = body.endsWith(`(${located})`) ? body.slice(0, body.length - located.length - 2).trim() : "";
2539
+ frames.push({ ...name ? { functionname: name } : {}, url, line: lineno, ...Number.isFinite(column) ? { column } : {} });
2540
+ }
2541
+ return frames;
2542
+ }
2543
+ function errorcapture(input) {
2544
+ return { message: redactconsoletext(input.message, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [], sourceurl: input.sourceurl, line: input.line };
2545
+ }
2546
+ function rejectioncapture(input) {
2547
+ return { reason: redactconsoletext(input.reason, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [] };
2548
+ }
2549
+ function longtaskcapture(input) {
2550
+ return input.entries.filter((entry) => entry.duration >= input.threshold).map((entry) => ({ duration: Math.round(entry.duration), starttime: Math.round(entry.starttime), attributions: [...entry.attributions] }));
2551
+ }
2552
+ function attachtimeline(input) {
2553
+ return { runid: input.runid, origin: input.origin, stepids: [...input.stepids], attachedat: input.now, entries: [] };
2554
+ }
2555
+ function filterentries(entries, levelset) {
2556
+ return entries.filter((entry) => {
2557
+ const floor = levelset.floors?.[entry.stepid] ?? levelset.floors?.["*"];
2558
+ if (floor !== void 0 && levelrank(entry.level) > levelrank(floor)) return false;
2559
+ if (levelset.sources !== void 0 && levelset.sources.length > 0 && !levelset.sources.includes(entry.source)) return false;
2560
+ return true;
2561
+ });
2562
+ }
2563
+ function spamdetect(entries, rule) {
2564
+ const collapsed = [];
2565
+ const counts = /* @__PURE__ */ new Map();
2566
+ for (const entry of entries) {
2567
+ if (rule.pattern !== "" && !entry.message.includes(rule.pattern)) {
2568
+ collapsed.push({ ...entry, repeat: 1 });
2569
+ continue;
2570
+ }
2571
+ const key = `${entry.level}|${entry.source}|${entry.message}`;
2572
+ const previous = collapsed[collapsed.length - 1];
2573
+ if (previous && previous.repeat !== void 0 && `${previous.level}|${previous.source}|${previous.message}` === key && entry.time - previous.time <= rule.windowsize) {
2574
+ previous.repeat += 1;
2575
+ continue;
2576
+ }
2577
+ collapsed.push({ ...entry, repeat: 1 });
2578
+ }
2579
+ for (const entry of collapsed) {
2580
+ if (entry.repeat > 1) counts.set(`${entry.level}|${entry.source}|${entry.message}`, entry.repeat);
2581
+ }
2582
+ const flagged = [...counts.entries()].filter(([, count]) => count > rule.collapse).map(([key, count]) => ({ message: key.split("|").slice(2).join("|"), count }));
2583
+ return { entries: collapsed, flagged };
2584
+ }
2585
+ function rotatelogs(entries, rule) {
2586
+ if (entries.length <= rule.maxentries) return { kept: [...entries], overflow: [] };
2587
+ const kept = entries.slice(entries.length - rule.maxentries);
2588
+ const overflow = entries.slice(0, entries.length - rule.maxentries);
2589
+ return { kept, overflow };
2590
+ }
2591
+ function timelinecounts(entries) {
2592
+ const counts = {};
2593
+ for (const level of loglevels) counts[level] = 0;
2594
+ for (const entry of entries) counts[entry.level] = (counts[entry.level] ?? 0) + 1;
2595
+ return counts;
2596
+ }
2597
+ function blockingduration(tasks, stepid, window) {
2598
+ const inside = tasks.filter((task) => task.starttime >= window.startedat && task.starttime <= window.endedat);
2599
+ return { stepid, blocking: inside.reduce((total, task) => total + task.duration, 0), tasks: inside.length };
2600
+ }
2601
+ function netfailureentryof(input) {
2602
+ const exchange = input.exchange;
2603
+ if (exchange.errorclass === void 0 && exchange.status < 400) return null;
2604
+ return { id: input.id, runid: exchange.runid, stepid: exchange.stepid, url: exchange.url, status: exchange.status, errorclass: exchange.errorclass ?? "httperror", correlationid: exchange.correlationid, at: input.at };
2605
+ }
2606
+ function watcherdetached(input) {
2607
+ for (const navigation of input.navigations) {
2608
+ if (navigation >= input.startedat && navigation <= input.startedat + input.lifetime) return { detached: true, at: navigation };
2609
+ }
2610
+ return { detached: false };
2611
+ }
2612
+ function consolediff(input) {
2613
+ const base = input.baselines;
2614
+ const target = input.targetlines;
2615
+ const basemap = /* @__PURE__ */ new Map();
2616
+ for (const line of base) basemap.set(line, (basemap.get(line) ?? 0) + 1);
2617
+ const targetmap = /* @__PURE__ */ new Map();
2618
+ for (const line of target) targetmap.set(line, (targetmap.get(line) ?? 0) + 1);
2619
+ const lines = [];
2620
+ const added = [];
2621
+ const removed = [];
2622
+ const repeated = [];
2623
+ for (const [line, count] of targetmap) {
2624
+ const basecount = basemap.get(line) ?? 0;
2625
+ if (basecount === 0) {
2626
+ for (let index = 0; index < count; index += 1) {
2627
+ lines.push({ kind: "added", text: line });
2628
+ added.push(line);
2629
+ }
2630
+ continue;
2631
+ }
2632
+ const share = Math.min(basecount, count);
2633
+ for (let index = 0; index < share; index += 1) {
2634
+ lines.push({ kind: "repeated", text: line, count: share });
2635
+ repeated.push(line);
2636
+ }
2637
+ for (let index = share; index < count; index += 1) {
2638
+ lines.push({ kind: "added", text: line });
2639
+ added.push(line);
2640
+ }
2641
+ }
2642
+ for (const [line, count] of basemap) {
2643
+ const targetcount = targetmap.get(line) ?? 0;
2644
+ const missing = Math.max(0, count - targetcount);
2645
+ for (let index = 0; index < missing; index += 1) {
2646
+ lines.push({ kind: "removed", text: line });
2647
+ removed.push(line);
2648
+ }
2649
+ }
2650
+ return { base: input.baseid, target: input.targetid, lines, added: added.length, removed: removed.length, repeated: repeated.length, at: input.now };
2651
+ }
2652
+
1983
2653
  // socketbus.ts
1984
2654
  var socketkinds = ["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"];
1985
2655
  function channelorigin(url) {
@@ -2201,9 +2871,9 @@ function polldecision(input) {
2201
2871
  }
2202
2872
 
2203
2873
  // policy.ts
2204
- 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"]);
2874
+ 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"]);
2205
2875
  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"]);
2206
- 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"]);
2876
+ 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"]);
2207
2877
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
2208
2878
  var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
2209
2879
  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"]);
@@ -2218,6 +2888,8 @@ var mediaactions = /* @__PURE__ */ new Set(["capturepdf", "recordscreen", "captu
2218
2888
  var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"]);
2219
2889
  var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"]);
2220
2890
  var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
2891
+ var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
2892
+ var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
2221
2893
  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"]);
2222
2894
  var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
2223
2895
  var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
@@ -2236,8 +2908,11 @@ function hostpattern(origin) {
2236
2908
  function iswatchkind(kind) {
2237
2909
  return watchactions.has(kind);
2238
2910
  }
2911
+ function isdebugkind(kind) {
2912
+ return debugactions.has(kind);
2913
+ }
2239
2914
  function observationmodeof(kind) {
2240
- if (watchactions.has(kind) || kind === "waitquiet") return "watching";
2915
+ if (watchactions.has(kind) || debugactions.has(kind) || kind === "waitquiet") return "watching";
2241
2916
  if (kind === "diffsnapshots") return "diffing";
2242
2917
  return "passive";
2243
2918
  }
@@ -2932,6 +3607,9 @@ function issocketkind(kind) {
2932
3607
  function isnetwatchkind(kind) {
2933
3608
  return netwatchactions.has(kind);
2934
3609
  }
3610
+ function iscontrolkind(kind) {
3611
+ return controlactions.has(kind);
3612
+ }
2935
3613
  function resolvedrisk(step) {
2936
3614
  if (step.kind === "capturebodies") {
2937
3615
  let options = {};
@@ -3230,6 +3908,244 @@ function validatenetwatchgrammar(step, options) {
3230
3908
  }
3231
3909
  return { allowed: true };
3232
3910
  }
3911
+ function validatecontrolgrammar(step, options) {
3912
+ const kind = step.kind;
3913
+ if (kind === "blockrequest") {
3914
+ const rule = blockruleof(options.block);
3915
+ if (!rule) return { allowed: false, reason: "A reviewed block rule with a url pattern is required in options.block." };
3916
+ if (patternorigin(rule.urlpattern) === void 0) return { allowed: false, reason: "Block rules need an https origin pattern; patterns without a named origin are refused." };
3917
+ if (options.block.reviewed !== true) return { allowed: false, reason: "The block rule carries the explicit reviewed flag before any request is blocked." };
3918
+ }
3919
+ if (kind === "mockresponse") {
3920
+ const spec = mockspecof(options.mock);
3921
+ if (!spec) return { allowed: false, reason: "A reviewed mock fixture with a url pattern, status and its reviewed body or a captured body ref is required in options.mock." };
3922
+ if (patternorigin(spec.urlpattern) === void 0) return { allowed: false, reason: "Mock fixtures need an https origin pattern; patterns without a named origin are refused." };
3923
+ if (spec.reviewed !== true) return { allowed: false, reason: "Every mock fixture is reviewed with its full body or the referenced captured body through the explicit reviewed flag before it serves." };
3924
+ }
3925
+ if (kind === "rewriteheaders") {
3926
+ const rules = options.rules;
3927
+ if (!Array.isArray(rules) || rules.length === 0) return { allowed: false, reason: "A reviewed non-empty list of header rewrite rules is required in options.rules." };
3928
+ for (const item of rules) {
3929
+ const rule = headeruleof(item);
3930
+ if (!rule) return { allowed: false, reason: "Every header rewrite rule needs a url pattern, header name, a set, append or remove operation and its value." };
3931
+ if (patternorigin(rule.urlpattern) === void 0) return { allowed: false, reason: "Header rewrite rules must name their origin pattern explicitly; patterns without a named origin are refused." };
3932
+ }
3933
+ }
3934
+ if (kind === "setcookies") {
3935
+ const cookies = options.cookies;
3936
+ if (!Array.isArray(cookies) || cookies.length === 0) return { allowed: false, reason: "A reviewed non-empty list of cookie records is required in options.cookies." };
3937
+ for (const item of cookies) {
3938
+ if (!cookierecordof(item)) return { allowed: false, reason: "Every cookie record needs a name, domain, path and reviewed string value with an optional expiry." };
3939
+ }
3940
+ }
3941
+ if (kind === "readcookies" && options.domain !== void 0 && !isnonempty(options.domain)) return { allowed: false, reason: "The reviewed cookie read domain must be a non-empty host." };
3942
+ if (kind === "clearcookies") {
3943
+ if (!isnonempty(options.domain)) return { allowed: false, reason: "A reviewed cookie domain is required before cookies are cleared." };
3944
+ if (options.names !== void 0 && (!Array.isArray(options.names) || options.names.length === 0 || !options.names.every((name) => isnonempty(name)))) return { allowed: false, reason: "The reviewed cookie clear list must be a non-empty list of cookie names when present." };
3945
+ }
3946
+ if (kind === "authflow") {
3947
+ const flow = oauthflowof(options.oauth);
3948
+ if (!flow) return { allowed: false, reason: "A reviewed oauth flow with provider, authorize url, token url, scopes and redirect origin is required in options.oauth." };
3949
+ if (!ishttpsurl(flow.authorizeurl) || !ishttpsurl(flow.tokenurl)) return { allowed: false, reason: "The oauth authorize and token urls must use HTTPS." };
3950
+ if (!ishttpsurl(flow.redirectorigin) && !/^https:\/\/[^/]+\/?$/.test(flow.redirectorigin)) return { allowed: false, reason: "The oauth redirect origin must be an HTTPS origin inside the grants." };
3951
+ const consent = authconsentgranted(step);
3952
+ if (!consent.allowed) return consent;
3953
+ }
3954
+ if (kind === "saveapikey") {
3955
+ const key = options.key;
3956
+ if (!key || typeof key !== "object" || Array.isArray(key)) return { allowed: false, reason: "A reviewed api key entry with name, origin scopes and header is required in options.key." };
3957
+ const entry = key;
3958
+ if (!isnonempty(entry.name)) return { allowed: false, reason: "The api key entry needs a reviewed non-empty name." };
3959
+ if (!Array.isArray(entry.origins) || entry.origins.length === 0 || !entry.origins.every((item) => ishttpsurl(item))) return { allowed: false, reason: "The api key needs a reviewed non-empty list of HTTPS origin scopes." };
3960
+ if (!isnonempty(entry.header)) return { allowed: false, reason: "The api key entry needs a reviewed non-empty header name." };
3961
+ if (typeof entry.value !== "string" || !entry.value) return { allowed: false, reason: "The api key needs its secret value in the reviewed options; it never enters the audit trail." };
3962
+ const consent = apikeyconsentgranted(step);
3963
+ if (!consent.allowed) return consent;
3964
+ }
3965
+ if (kind === "routeproxy") {
3966
+ if (!proxyrouteof(options.proxy)) return { allowed: false, reason: "A reviewed proxy route with scheme, host, port and a non-empty bypass list is required in options.proxy." };
3967
+ if (!isnonempty(options.consentref)) return { allowed: false, reason: "Proxy routing needs the explicit reviewed consent ref before any route applies." };
3968
+ }
3969
+ if (kind === "postform") {
3970
+ const form = formpayloadof(options.form);
3971
+ if (!form) return { allowed: false, reason: "A reviewed form payload with a url and a non-empty field list is required in options.form." };
3972
+ if (!ishttpsurl(form.url)) return { allowed: false, reason: "The form submission target must use HTTPS." };
3973
+ if (options.wait !== void 0 && (typeof options.wait !== "number" || !Number.isFinite(options.wait) || options.wait < 0)) return { allowed: false, reason: "The reviewed rate limit wait budget must be zero or a positive number of milliseconds." };
3974
+ }
3975
+ if (kind === "postfiles") {
3976
+ const upload = multipartpayloadof(options.upload);
3977
+ if (!upload) return { allowed: false, reason: "A reviewed multipart upload with a url and reviewed files is required in options.upload; every file carries the explicit reviewed flag." };
3978
+ if (!ishttpsurl(upload.url)) return { allowed: false, reason: "The multipart upload target must use HTTPS." };
3979
+ if (options.wait !== void 0 && (typeof options.wait !== "number" || !Number.isFinite(options.wait) || options.wait < 0)) return { allowed: false, reason: "The reviewed rate limit wait budget must be zero or a positive number of milliseconds." };
3980
+ }
3981
+ return { allowed: true };
3982
+ }
3983
+ function blockgate(session, step, now) {
3984
+ if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the request block." };
3985
+ if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot block requests." };
3986
+ if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot block requests." };
3987
+ let options = {};
3988
+ try {
3989
+ options = parseoptions(step);
3990
+ } catch {
3991
+ options = {};
3992
+ }
3993
+ const rule = options.block;
3994
+ if (!rule || typeof rule !== "object" || Array.isArray(rule) || rule.reviewed !== true) return { allowed: false, reason: "Request blocking needs its reviewed block rule with the explicit reviewed flag before any rule applies." };
3995
+ if (!blockruleof(rule)) return { allowed: false, reason: "The block rule needs a url pattern and an optional resource type list." };
3996
+ return { allowed: true };
3997
+ }
3998
+ function cookiegate(session, domain, now) {
3999
+ if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the cookie operation." };
4000
+ if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot touch cookies." };
4001
+ if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot touch cookies." };
4002
+ const grants = session.grants ?? [session.origin];
4003
+ if (!cookiedomaingranted(domain, grants)) return { allowed: false, reason: `The cookie domain ${domain} stays outside the session origin grants; cookie control refuses domains beyond the grants.` };
4004
+ return { allowed: true };
4005
+ }
4006
+ function proxygate(session, step, now) {
4007
+ if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the proxy route." };
4008
+ if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot change routing." };
4009
+ if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot change routing." };
4010
+ let options = {};
4011
+ try {
4012
+ options = parseoptions(step);
4013
+ } catch {
4014
+ options = {};
4015
+ }
4016
+ if (!isnonempty(options.consentref)) return { allowed: false, reason: "Proxy routing needs the explicit reviewed consent ref before any route applies." };
4017
+ if (!proxyrouteof(options.proxy)) return { allowed: false, reason: "The proxy route needs a scheme, host, port and a non-empty bypass list of origins that stay direct." };
4018
+ return { allowed: true };
4019
+ }
4020
+ function authconsentgranted(step) {
4021
+ let options = {};
4022
+ try {
4023
+ options = parseoptions(step);
4024
+ } catch {
4025
+ options = {};
4026
+ }
4027
+ const consentref = options.consentref;
4028
+ if (typeof consentref !== "string" || !consentref.trim()) return { allowed: false, reason: "An oauth flow requires the reviewed provider consent prompt ref in options before it starts." };
4029
+ return { allowed: true };
4030
+ }
4031
+ function apikeyconsentgranted(step) {
4032
+ let options = {};
4033
+ try {
4034
+ options = parseoptions(step);
4035
+ } catch {
4036
+ options = {};
4037
+ }
4038
+ const consentref = options.consentref;
4039
+ if (typeof consentref !== "string" || !consentref.trim()) return { allowed: false, reason: "Storing an api key requires the explicit reviewed consent prompt ref in options before anything is stored." };
4040
+ return { allowed: true };
4041
+ }
4042
+ function ratelimitbudgetallowed(wait, budget) {
4043
+ if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The rate limit wait must be zero or a positive number of milliseconds." };
4044
+ if (budget !== void 0 && (typeof budget !== "number" || !Number.isFinite(budget) || budget < 0)) return { allowed: false, reason: "The reviewed rate limit budget must be zero or a positive number of milliseconds." };
4045
+ if (wait !== void 0 && budget !== void 0 && wait > budget) return { allowed: false, reason: `The rate limit wait of ${wait} milliseconds exceeds the reviewed budget of ${budget} milliseconds; review a wider budget or submit later.` };
4046
+ return { allowed: true };
4047
+ }
4048
+ function timelinegate(session, tabid, origin, now) {
4049
+ if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the timeline capture." };
4050
+ if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture the timeline." };
4051
+ if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot capture the timeline." };
4052
+ if (session.tabid !== tabid) return { allowed: false, reason: `The timeline capture needs the run tab ${session.tabid} and refuses tab ${tabid}.` };
4053
+ if (!origingranted(session, origin)) return { allowed: false, reason: `The timeline capture of ${origin} needs the session origin grants first.` };
4054
+ return { allowed: true };
4055
+ }
4056
+ function consoleconsentcovers(origin, consents) {
4057
+ if (consents.some((consent) => consent.origin === origin && consent.approved === true)) return { allowed: true };
4058
+ return { allowed: false, reason: `Console capture on ${origin} needs the reviewed console consent first; approve the prompt in the review panel.` };
4059
+ }
4060
+ function stackgate(session, origin) {
4061
+ if (!origingranted(session, origin)) return { allowed: false, reason: `Stack capture of ${origin} stays outside the session origin grants.` };
4062
+ return { allowed: true };
4063
+ }
4064
+ function debugwaitbudgetallowed(watchwindow, wait) {
4065
+ if (watchwindow !== void 0 && (typeof watchwindow !== "number" || !Number.isFinite(watchwindow) || watchwindow < 0)) return { allowed: false, reason: "The debug watch window must be zero or a positive number of milliseconds." };
4066
+ if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The reviewed debug wait budget must be zero or a positive number of milliseconds." };
4067
+ if (watchwindow !== void 0 && wait !== void 0 && watchwindow > wait) return { allowed: false, reason: `The debug watch window of ${watchwindow} milliseconds exceeds the reviewed wait budget of ${wait} milliseconds; review a wider budget or a shorter window.` };
4068
+ return { allowed: true };
4069
+ }
4070
+ function timelineretentionwindow(settings) {
4071
+ return settings?.timelineretention;
4072
+ }
4073
+ function diffreviewgrade() {
4074
+ return { risk: "read", mode: "diffing", evidence: "comparison" };
4075
+ }
4076
+ function validatetimelinegrammar(step, options) {
4077
+ const kind = step.kind;
4078
+ let watchwindow;
4079
+ if (options.watch !== void 0) {
4080
+ const watch = options.watch;
4081
+ if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed debug watch window must be an object." };
4082
+ const reviewed = watch;
4083
+ if (reviewed.window !== void 0) {
4084
+ if (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0) return { allowed: false, reason: "The reviewed debug watch window must be zero or a positive number of milliseconds." };
4085
+ watchwindow = reviewed.window;
4086
+ }
4087
+ }
4088
+ const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
4089
+ if (!budgetcheck.allowed) return budgetcheck;
4090
+ if (options.level !== void 0 && !loglevels.includes(options.level)) return { allowed: false, reason: `The reviewed level floor must be one of ${loglevels.join(", ")}.` };
4091
+ if (options.sources !== void 0) {
4092
+ if (!Array.isArray(options.sources) || options.sources.length === 0 || !options.sources.every((source) => timelinesources.includes(source))) return { allowed: false, reason: `The reviewed source filters must be a non-empty list of the reviewed timeline sources: ${timelinesources.join(", ")}.` };
4093
+ }
4094
+ if (kind === "watchconsole") {
4095
+ if (options.redact === void 0 || !Array.isArray(options.redact) || options.redact.length === 0 || !options.redact.every((pattern) => isnonempty(pattern))) return { allowed: false, reason: "Console capture requires a reviewed non-empty redaction pattern list before any console text is captured." };
4096
+ if (options.depth !== void 0 && (typeof options.depth !== "number" || !Number.isInteger(options.depth) || options.depth < 1)) return { allowed: false, reason: "The reviewed serialization depth bound must be a positive integer with no code ceiling." };
4097
+ if (options.spam !== void 0) {
4098
+ const rule = spamruleof(options.spam);
4099
+ if (!rule) return { allowed: false, reason: "The reviewed spam rule needs a pattern, a window size and a collapse threshold." };
4100
+ if (rule.collapse < 1) return { allowed: false, reason: "The reviewed spam collapse threshold must be a positive integer of user configured value with no code ceiling." };
4101
+ }
4102
+ if (options.rotation !== void 0) {
4103
+ const rule = rotationruleof(options.rotation);
4104
+ if (!rule) return { allowed: false, reason: "The reviewed rotation rule needs a max entry count and an overflow target." };
4105
+ }
4106
+ }
4107
+ if (kind === "watchtasks") {
4108
+ if (options.threshold !== void 0 && (typeof options.threshold !== "number" || !Number.isFinite(options.threshold) || options.threshold < 0)) return { allowed: false, reason: "The reviewed long task threshold must be zero or a positive number of milliseconds with no code ceiling." };
4109
+ }
4110
+ return { allowed: true };
4111
+ }
4112
+ function spamruleof(value) {
4113
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
4114
+ const entry = value;
4115
+ const pattern = typeof entry.pattern === "string" ? entry.pattern : "";
4116
+ const windowsize = typeof entry.windowsize === "number" && Number.isFinite(entry.windowsize) && entry.windowsize >= 0 ? entry.windowsize : void 0;
4117
+ const collapse = typeof entry.collapse === "number" && Number.isInteger(entry.collapse) ? entry.collapse : void 0;
4118
+ if (windowsize === void 0 || collapse === void 0) return void 0;
4119
+ return { pattern, windowsize, collapse };
4120
+ }
4121
+ function rotationruleof(value) {
4122
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
4123
+ const entry = value;
4124
+ const maxentries = typeof entry.maxentries === "number" && Number.isInteger(entry.maxentries) && entry.maxentries >= 1 ? entry.maxentries : void 0;
4125
+ const overflowtarget = typeof entry.overflowtarget === "string" && entry.overflowtarget.trim() ? entry.overflowtarget.trim() : void 0;
4126
+ if (maxentries === void 0 || overflowtarget === void 0) return void 0;
4127
+ return { maxentries, overflowtarget };
4128
+ }
4129
+ function controltarget(step) {
4130
+ let options = {};
4131
+ try {
4132
+ options = parseoptions(step);
4133
+ } catch {
4134
+ options = {};
4135
+ }
4136
+ for (const key of ["form", "upload"]) {
4137
+ const value = options[key];
4138
+ if (value && typeof value === "object" && !Array.isArray(value)) {
4139
+ const url = value.url;
4140
+ if (typeof url === "string" && url.trim()) return url.trim();
4141
+ }
4142
+ }
4143
+ if (step.kind === "authflow") {
4144
+ const flow = oauthflowof(options.oauth);
4145
+ if (flow) return flow.tokenurl;
4146
+ }
4147
+ return void 0;
4148
+ }
3233
4149
  function sockettarget(step) {
3234
4150
  let options = {};
3235
4151
  try {
@@ -3611,6 +4527,14 @@ function validatestep(step, origin) {
3611
4527
  const netwatchcheck = validatenetwatchgrammar(step, options);
3612
4528
  if (!netwatchcheck.allowed) return netwatchcheck;
3613
4529
  }
4530
+ if (iscontrolkind(step.kind)) {
4531
+ const controlcheck = validatecontrolgrammar(step, options);
4532
+ if (!controlcheck.allowed) return controlcheck;
4533
+ }
4534
+ if (isdebugkind(step.kind)) {
4535
+ const timelinecheck = validatetimelinegrammar(step, options);
4536
+ if (!timelinecheck.allowed) return timelinecheck;
4537
+ }
3614
4538
  if (step.kind === "tabcreate") {
3615
4539
  if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
3616
4540
  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." };
@@ -3712,6 +4636,59 @@ function canexecute(input) {
3712
4636
  const watchgatecheck = watchgate(input.session, input.settings, now);
3713
4637
  if (!watchgatecheck.allowed) return watchgatecheck;
3714
4638
  }
4639
+ if (isdebugkind(input.step.kind)) {
4640
+ const timelinegatecheck = timelinegate(input.session, input.tabid, input.origin, now);
4641
+ if (!timelinegatecheck.allowed) return timelinegatecheck;
4642
+ }
4643
+ if (iscontrolkind(input.step.kind)) {
4644
+ const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
4645
+ if (!controlgate.allowed) return controlgate;
4646
+ let controloptions = {};
4647
+ try {
4648
+ controloptions = parseoptions(input.step);
4649
+ } catch {
4650
+ controloptions = {};
4651
+ }
4652
+ if (input.step.kind === "blockrequest") {
4653
+ const blockgatecheck = blockgate(input.session, input.step, now);
4654
+ if (!blockgatecheck.allowed) return blockgatecheck;
4655
+ const rule = blockruleof(controloptions.block);
4656
+ if (rule) {
4657
+ const blockorigin = origincheck(input.session, rule.urlpattern);
4658
+ if (!blockorigin.allowed) return blockorigin;
4659
+ }
4660
+ }
4661
+ if (input.step.kind === "mockresponse" || input.step.kind === "rewriteheaders") {
4662
+ const patterns = input.step.kind === "mockresponse" ? [mockspecof(controloptions.mock)?.urlpattern ?? ""] : Array.isArray(controloptions.rules) ? controloptions.rules.map((item) => item && typeof item === "object" && !Array.isArray(item) ? String(item.urlpattern ?? "") : "") : [];
4663
+ for (const pattern of patterns) {
4664
+ const patterngate = origincheck(input.session, pattern);
4665
+ if (!patterngate.allowed) return patterngate;
4666
+ }
4667
+ }
4668
+ if (input.step.kind === "setcookies" || input.step.kind === "readcookies" || input.step.kind === "clearcookies") {
4669
+ const domain = typeof controloptions.domain === "string" && controloptions.domain.trim() ? controloptions.domain : Array.isArray(controloptions.cookies) ? String(controloptions.cookies[0]?.domain ?? "") : "";
4670
+ if (!domain) return { allowed: false, reason: "A reviewed cookie domain is required before cookie control runs." };
4671
+ const cookiegatecheck = cookiegate(input.session, domain, now);
4672
+ if (!cookiegatecheck.allowed) return cookiegatecheck;
4673
+ }
4674
+ if (input.step.kind === "authflow") {
4675
+ const authconsent = authconsentgranted(input.step);
4676
+ if (!authconsent.allowed) return authconsent;
4677
+ }
4678
+ if (input.step.kind === "saveapikey") {
4679
+ const keyconsent = apikeyconsentgranted(input.step);
4680
+ if (!keyconsent.allowed) return keyconsent;
4681
+ }
4682
+ if (input.step.kind === "routeproxy") {
4683
+ const proxygatecheck = proxygate(input.session, input.step, now);
4684
+ if (!proxygatecheck.allowed) return proxygatecheck;
4685
+ }
4686
+ const target = controltarget(input.step);
4687
+ if (target !== void 0) {
4688
+ const targetgate = origincheck(input.session, target);
4689
+ if (!targetgate.allowed) return targetgate;
4690
+ }
4691
+ }
3715
4692
  if (input.step.kind === "extractapi") {
3716
4693
  let replayoptions = {};
3717
4694
  try {
@@ -3746,7 +4723,7 @@ function canexecute(input) {
3746
4723
  }
3747
4724
 
3748
4725
  // version.ts
3749
- var packageversion = "1.1.43";
4726
+ var packageversion = "1.1.45";
3750
4727
 
3751
4728
  // types.ts
3752
4729
  var protocolversion = packageversion;
@@ -3783,6 +4760,44 @@ function parseproposal(value, origin, grants) {
3783
4760
  ...typeof candidate.value === "string" ? { value: candidate.value } : {},
3784
4761
  ...typeof candidate.options === "string" ? { options: candidate.options } : {}
3785
4762
  };
4763
+ if (step.kind === "blockrequest") {
4764
+ let blockoptions = {};
4765
+ try {
4766
+ blockoptions = parseoptions(step);
4767
+ } catch {
4768
+ blockoptions = {};
4769
+ }
4770
+ const rule = blockruleof(blockoptions.block);
4771
+ if (rule && patternorigin(rule.urlpattern) === void 0) throw new Error("Block rules without a named origin pattern are refused.");
4772
+ }
4773
+ if (step.kind === "routeproxy") {
4774
+ let proxyoptions = {};
4775
+ try {
4776
+ proxyoptions = parseoptions(step);
4777
+ } catch {
4778
+ proxyoptions = {};
4779
+ }
4780
+ const proxy = proxyoptions.proxy;
4781
+ const bypass = proxy && typeof proxy === "object" && !Array.isArray(proxy) ? proxy.bypass : void 0;
4782
+ if (!Array.isArray(bypass) || bypass.length === 0) throw new Error("Proxy routes without a bypass list are refused.");
4783
+ }
4784
+ if (step.kind === "watchconsole" || step.kind === "watcherrors" || step.kind === "watchtasks") {
4785
+ let debugoptions = {};
4786
+ try {
4787
+ debugoptions = parseoptions(step);
4788
+ } catch {
4789
+ debugoptions = {};
4790
+ }
4791
+ const granted = covered.some((pattern) => {
4792
+ try {
4793
+ return new URL(origin).origin === new URL(pattern).origin;
4794
+ } catch {
4795
+ return false;
4796
+ }
4797
+ });
4798
+ if (!granted) throw new Error(`The ${step.kind} capture of ${origin} targets an origin outside the grants.`);
4799
+ if (debugoptions.level !== void 0 && !loglevels.includes(debugoptions.level)) throw new Error(`The reviewed level floor must be one of ${loglevels.join(", ")}.`);
4800
+ }
3786
4801
  const evaluation = validatestep(step, origin);
3787
4802
  if (!evaluation.allowed) throw new Error(evaluation.reason);
3788
4803
  const target = outboundtarget(step);
@@ -3857,7 +4872,7 @@ function requestbody(input) {
3857
4872
  return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
3858
4873
  }
3859
4874
  function outcomeresponse(input) {
3860
- 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 } : {} });
4875
+ 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 } : {} });
3861
4876
  }
3862
4877
  function mapresponse(input) {
3863
4878
  return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
@@ -3951,12 +4966,45 @@ function callsreport(input) {
3951
4966
  function exchangesreport(input) {
3952
4967
  return { version: protocolversion, exchanges: input.exchanges, channels: input.channels, subscriptions: input.subscriptions, apimap: input.apimap };
3953
4968
  }
4969
+ function authreport(input) {
4970
+ const tokens = input.tokens.map((token) => {
4971
+ const { accessstorageid, refreshstorageid, ...metadata } = token;
4972
+ void accessstorageid;
4973
+ void refreshstorageid;
4974
+ return metadata;
4975
+ });
4976
+ return { version: protocolversion, tokens };
4977
+ }
4978
+ function controlreport(input) {
4979
+ const mocks = input.mocks.map((spec) => {
4980
+ const { body, ...metadata } = spec;
4981
+ void body;
4982
+ return metadata;
4983
+ });
4984
+ return { version: protocolversion, blocks: input.blocks, mocks, rewrites: input.rewrites, cookies: input.cookies, proxies: input.proxies, ratelimits: input.ratelimits };
4985
+ }
4986
+ function timelinereport(input) {
4987
+ return { version: protocolversion, entries: input.entries, errors: input.errors, rejections: input.rejections, longtasks: input.longtasks, levelcounts: input.levelcounts };
4988
+ }
4989
+ function consolediffreport(input) {
4990
+ return { version: protocolversion, diff: input.diff };
4991
+ }
3954
4992
  export {
3955
4993
  annotationplanof,
3956
4994
  apientries,
4995
+ apikeyconsentgranted,
3957
4996
  apireplayspecof,
4997
+ applyheaderules,
4998
+ argkind,
3958
4999
  assetentries,
5000
+ attachtimeline,
5001
+ authconsentgranted,
5002
+ authorizeurl,
5003
+ authreport,
3959
5004
  blendrows,
5005
+ blockgate,
5006
+ blockingduration,
5007
+ blockruleof,
3960
5008
  bodyfilterof,
3961
5009
  bodymatches,
3962
5010
  buildname,
@@ -3968,6 +5016,7 @@ export {
3968
5016
  callsreport,
3969
5017
  canexecute,
3970
5018
  capturebody,
5019
+ capturecode,
3971
5020
  capturedheaders,
3972
5021
  captureelement,
3973
5022
  captureformats,
@@ -3983,16 +5032,29 @@ export {
3983
5032
  channelorigin,
3984
5033
  closechannel,
3985
5034
  collectmessages,
5035
+ consolecapture,
5036
+ consoleconsentcovers,
5037
+ consolediff,
5038
+ consolediffreport,
5039
+ controlkinds,
5040
+ controlreport,
5041
+ controltarget,
3986
5042
  convertdirectiveof,
5043
+ cookiedomaingranted,
5044
+ cookiegate,
5045
+ cookierecordof,
3987
5046
  correlationid,
3988
5047
  croprect,
3989
5048
  crossesviewport,
3990
5049
  cursorfrom,
3991
5050
  datasetresponse,
5051
+ debugwaitbudgetallowed,
3992
5052
  dedupeimages,
3993
5053
  actionrisk as deriveactionrisk,
3994
5054
  diffresponse,
5055
+ diffreviewgrade,
3995
5056
  downloadreport,
5057
+ errorcapture,
3996
5058
  errorreportresponse,
3997
5059
  eventresponse,
3998
5060
  exchangesreport,
@@ -4001,15 +5063,18 @@ export {
4001
5063
  failureclass,
4002
5064
  fetchoptionsof,
4003
5065
  fetchrequestof,
5066
+ filterentries,
4004
5067
  filterexchanges,
4005
5068
  finishrecording,
4006
5069
  fixedheadermatch,
5070
+ formpayloadof,
4007
5071
  formreportresponse,
4008
5072
  frameinterval,
4009
5073
  generatedvalueallowed,
4010
5074
  graphqlopenvelope,
4011
5075
  graphqlrequestof,
4012
5076
  headerfilterof,
5077
+ headeruleof,
4013
5078
  heldkeysreport,
4014
5079
  hostpattern,
4015
5080
  htmlqueriesof,
@@ -4017,6 +5082,8 @@ export {
4017
5082
  imagefilterof,
4018
5083
  imagematches,
4019
5084
  imagenames,
5085
+ iscontrolkind,
5086
+ isdebugkind,
4020
5087
  isformkind,
4021
5088
  isnetwatchkind,
4022
5089
  issocketkind,
@@ -4025,19 +5092,32 @@ export {
4025
5092
  lapseframes,
4026
5093
  lapseplanof,
4027
5094
  layoutreport,
5095
+ levelrank,
5096
+ loglevels,
5097
+ longtaskcapture,
4028
5098
  mapresponse,
4029
5099
  matchmessage,
5100
+ matchurlpattern,
4030
5101
  mediaentries,
4031
5102
  mediakinds,
4032
5103
  mediareport,
4033
5104
  messagefilterof,
5105
+ mockfor,
5106
+ mockspecof,
5107
+ multipartchunks,
5108
+ multipartpayloadof,
4034
5109
  navstateresponse,
5110
+ netfailureentryof,
4035
5111
  netlogreport,
4036
5112
  netwatchkinds,
5113
+ newblockrule,
4037
5114
  newchannel,
4038
5115
  newexchange,
5116
+ newheaderule,
5117
+ newmockspec,
4039
5118
  newrecording,
4040
5119
  normalizeendpoint,
5120
+ oauthflowof,
4041
5121
  observationmodeof,
4042
5122
  observationresponse,
4043
5123
  openchannel,
@@ -4047,7 +5127,9 @@ export {
4047
5127
  parsehtmlbody,
4048
5128
  parseproposal,
4049
5129
  parsessetext,
5130
+ parsetokens,
4050
5131
  passwordconsentgranted,
5132
+ patternorigin,
4051
5133
  payloadshapeof,
4052
5134
  payloadvalid,
4053
5135
  payloadwithdefaults,
@@ -4062,32 +5144,50 @@ export {
4062
5144
  profilegrantgranted,
4063
5145
  protocolversion,
4064
5146
  provenancereport,
5147
+ proxygate,
5148
+ proxyrouteof,
4065
5149
  publishmessage,
4066
5150
  quarantinereport,
4067
5151
  randomid,
4068
5152
  rankapis,
5153
+ ratelimitbudgetallowed,
5154
+ ratelimitreadof,
5155
+ ratelimitwait,
4069
5156
  readpath,
4070
5157
  readstream,
4071
5158
  receivemessage,
4072
5159
  reconnectwaits,
4073
5160
  recordingoptionsof,
5161
+ redactconsoletext,
5162
+ redactedcookies,
4074
5163
  regionsteps,
5164
+ rejectioncapture,
4075
5165
  replayurl,
4076
5166
  requestbody,
4077
5167
  resolutionverdict,
4078
5168
  resolvedrisk,
4079
5169
  resourcefacts,
5170
+ retryafterof,
5171
+ revertrule,
5172
+ revocationruleof,
5173
+ rotatelogs,
5174
+ rotationruleof,
4080
5175
  safetyresponse,
4081
5176
  scaledrect,
4082
5177
  seamweights,
4083
5178
  selectorresponse,
4084
5179
  sendfetch,
4085
5180
  sequenceintegrity,
5181
+ serializearg,
4086
5182
  sessionmemory,
4087
5183
  signalsreport,
4088
5184
  socketgate,
4089
5185
  socketkinds,
5186
+ spamdetect,
5187
+ spamruleof,
4090
5188
  sserequestheaders,
5189
+ stackframes,
5190
+ stackgate,
4091
5191
  statusclassof,
4092
5192
  streamsummaries,
4093
5193
  streamwindowof,
@@ -4097,14 +5197,23 @@ export {
4097
5197
  templateurl,
4098
5198
  thumbdirectiveof,
4099
5199
  thumbgeometry,
5200
+ timelinecounts,
5201
+ timelinegate,
5202
+ timelinekinds,
5203
+ timelinereport,
5204
+ timelineretentionwindow,
5205
+ timelinesources,
5206
+ tokenrequest,
4100
5207
  trailreport,
4101
5208
  transformgrammar,
4102
5209
  unwrapgraphql,
5210
+ urlencodeform,
4103
5211
  validatefieldmatch,
4104
5212
  validateformrecord,
4105
5213
  validatestep,
4106
5214
  validatetargetref,
4107
5215
  validatevaluegen,
5216
+ watcherdetached,
4108
5217
  watchgate,
4109
5218
  wizardreport
4110
5219
  };