@wenathlan/extension 1.1.42 → 1.1.43

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
@@ -1672,6 +1672,87 @@ var sessionmemory = class {
1672
1672
  async getsecret(storageid) {
1673
1673
  return this.adapter.get(storageid);
1674
1674
  }
1675
+ /** Stores one channel record of a socket or event stream, replacing the previous record of that id. */
1676
+ async addchannel(record2) {
1677
+ const records = (await this.adapter.get("channels") ?? []).filter((item) => item.id !== record2.id);
1678
+ await this.adapter.set("channels", [record2, ...records]);
1679
+ }
1680
+ /** Returns every stored channel record, newest first. */
1681
+ async getchannels() {
1682
+ return await this.adapter.get("channels") ?? [];
1683
+ }
1684
+ /** Returns one channel record by its id. */
1685
+ async getchannel(id) {
1686
+ return (await this.getchannels()).find((item) => item.id === id);
1687
+ }
1688
+ /** Queues one message envelope of a channel stream, keeping the arrival order for the waitmessage matchers. */
1689
+ async addmessage(envelope) {
1690
+ const records = (await this.adapter.get("messages") ?? []).filter((item) => !(item.channelid === envelope.channelid && item.sequence === envelope.sequence));
1691
+ await this.adapter.set("messages", [...records, envelope]);
1692
+ }
1693
+ /** Returns every queued message envelope, oldest first, optionally filtered by channel and stream. */
1694
+ async getmessages(channelid, stream) {
1695
+ const records = await this.adapter.get("messages") ?? [];
1696
+ return records.filter((item) => (channelid === void 0 || item.channelid === channelid) && (stream === void 0 || item.stream === stream));
1697
+ }
1698
+ /** Drops the matched message envelopes of one channel from the queue once a waitmessage step consumed them. */
1699
+ async drainmessages(sequences) {
1700
+ const records = await this.adapter.get("messages") ?? [];
1701
+ const kept = records.filter((item) => !sequences.some((match) => match.channelid === item.channelid && match.sequence === item.sequence));
1702
+ await this.adapter.set("messages", kept);
1703
+ }
1704
+ /** Stores one observed exchange record, replacing the previous record of that id. */
1705
+ async addexchange(record2) {
1706
+ const records = (await this.adapter.get("exchanges") ?? []).filter((item) => item.id !== record2.id);
1707
+ await this.adapter.set("exchanges", [record2, ...records]);
1708
+ }
1709
+ /** Returns every stored exchange record, newest first. */
1710
+ async getexchanges() {
1711
+ return await this.adapter.get("exchanges") ?? [];
1712
+ }
1713
+ /** Returns one exchange record by its id. */
1714
+ async getexchange(id) {
1715
+ return (await this.getexchanges()).find((item) => item.id === id);
1716
+ }
1717
+ /** Returns the exchange records filtered by run, origin and status; the status filter accepts one code or the failed class of every exchange with an error class. */
1718
+ async listexchanges(filter) {
1719
+ const records = await this.getexchanges();
1720
+ return records.filter((item) => (filter.runid === void 0 || item.runid === filter.runid) && (filter.origin === void 0 || item.origin === filter.origin) && (filter.status === void 0 || (filter.status === "failed" ? item.errorclass !== void 0 : item.status === filter.status)));
1721
+ }
1722
+ /** Stores one captured response body with its mime type and byte size; the user configured body retention window expires the oldest bodies while the exchange metadata always survives. */
1723
+ async addbody(record2) {
1724
+ const records = await this.getbodies();
1725
+ const retention = (await this.getsettings())?.bodyretention;
1726
+ const combined = [record2, ...records.filter((item) => item.ref !== record2.ref)];
1727
+ const stored = retention === void 0 ? combined : combined.map((item, index) => index < retention ? item : expirebodybytes(item));
1728
+ await this.adapter.set("bodies", stored);
1729
+ }
1730
+ /** Returns every captured body record, newest first. */
1731
+ async getbodies() {
1732
+ return await this.adapter.get("bodies") ?? [];
1733
+ }
1734
+ /** Returns one captured body record with its stored text by its reference. */
1735
+ async getbody(ref) {
1736
+ return (await this.getbodies()).find((item) => item.ref === ref);
1737
+ }
1738
+ /** Stores the page api map of one origin, replacing the previous map of that origin. */
1739
+ async setapimap(origin, entries) {
1740
+ const records = (await this.adapter.get("apimap") ?? []).filter((item) => item.origin !== origin);
1741
+ await this.adapter.set("apimap", [...entries, ...records]);
1742
+ }
1743
+ /** Returns every stored page api map entry, newest first. */
1744
+ async getapimap() {
1745
+ return await this.adapter.get("apimap") ?? [];
1746
+ }
1747
+ /** Stores one event stream subscription record, replacing the previous record of that id. */
1748
+ async setsubscription(record2) {
1749
+ const records = (await this.adapter.get("subscriptions") ?? []).filter((item) => item.id !== record2.id);
1750
+ await this.adapter.set("subscriptions", [record2, ...records]);
1751
+ }
1752
+ /** Returns every stored event stream subscription, newest first. */
1753
+ async getsubscriptions() {
1754
+ return await this.adapter.get("subscriptions") ?? [];
1755
+ }
1675
1756
  };
1676
1757
  function mediakindof(record2) {
1677
1758
  if ("pages" in record2) return "pdf";
@@ -1706,14 +1787,423 @@ function expirecallbody(record2) {
1706
1787
  void body;
1707
1788
  return { ...metadata, bodyexpired: true };
1708
1789
  }
1790
+ function expirebodybytes(record2) {
1791
+ const { body, ...metadata } = record2;
1792
+ void body;
1793
+ return { ...metadata, bodyexpired: true };
1794
+ }
1709
1795
  function randomid() {
1710
1796
  return crypto.randomUUID();
1711
1797
  }
1712
1798
 
1799
+ // netwatch.ts
1800
+ var netwatchkinds = ["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"];
1801
+ function resourcefacts(entries) {
1802
+ const facts = [];
1803
+ for (const entry of entries) {
1804
+ const url = typeof entry.name === "string" ? entry.name : "";
1805
+ if (!url) continue;
1806
+ const entrytype = typeof entry.entryType === "string" ? entry.entryType : "resource";
1807
+ if (entrytype !== "resource" && entrytype !== "navigation") continue;
1808
+ facts.push({
1809
+ url,
1810
+ initiator: typeof entry.initiatorType === "string" ? entry.initiatorType : "",
1811
+ entrytype,
1812
+ start: typeof entry.startTime === "number" && Number.isFinite(entry.startTime) ? entry.startTime : 0,
1813
+ duration: typeof entry.duration === "number" && Number.isFinite(entry.duration) ? entry.duration : 0,
1814
+ transfer: typeof entry.transferSize === "number" && Number.isFinite(entry.transferSize) ? entry.transferSize : 0,
1815
+ protocol: typeof entry.nextHopProtocol === "string" ? entry.nextHopProtocol : "",
1816
+ ...typeof entry.responseStatus === "number" && Number.isInteger(entry.responseStatus) ? { status: entry.responseStatus } : {},
1817
+ ...entry.failed === true ? { failed: true } : {}
1818
+ });
1819
+ }
1820
+ return facts;
1821
+ }
1822
+ function failureclass(fact) {
1823
+ if (fact.status !== void 0 && fact.status >= 400) return { errorclass: "httperror", status: fact.status };
1824
+ if (fact.failed === true) return { errorclass: "networkerror", status: 0 };
1825
+ if ((fact.initiator === "fetch" || fact.initiator === "xmlhttprequest") && fact.duration > 0 && fact.transfer === 0 && fact.protocol === "") return { errorclass: "networkerror", status: 0 };
1826
+ return { status: fact.status ?? 0 };
1827
+ }
1828
+ function correlationid(runid, index) {
1829
+ return `${runid}-${index + 1}`;
1830
+ }
1831
+ function newexchange(input) {
1832
+ const verdict = failureclass(input.fact);
1833
+ let origin = "";
1834
+ try {
1835
+ origin = new URL(input.fact.url).origin;
1836
+ } catch {
1837
+ origin = "";
1838
+ }
1839
+ const method = input.fact.initiator === "fetch" || input.fact.initiator === "xmlhttprequest" ? "?" : "GET";
1840
+ const statusclass = verdict.status >= 100 && verdict.status < 600 ? verdict.status >= 200 && verdict.status < 300 ? "success" : verdict.status >= 300 && verdict.status < 400 ? "redirect" : verdict.status >= 400 && verdict.status < 500 ? "clienterror" : verdict.status >= 500 ? "servererror" : "informational" : "unknown";
1841
+ return { id: input.id, runid: input.runid, stepid: input.stepid, correlationid: input.correlationid, url: input.fact.url, origin, method, status: verdict.status, statusclass, ...verdict.errorclass !== void 0 ? { errorclass: verdict.errorclass } : {}, source: "page", ...input.fact.initiator ? { initiator: input.fact.initiator } : {}, timing: Math.round(input.fact.duration), bytes: input.fact.transfer, at: input.at };
1842
+ }
1843
+ function pairexchange(exchange, response) {
1844
+ if (exchange.correlationid !== response.correlationid) throw new Error(`The response ${response.correlationid} does not pair with the exchange ${exchange.correlationid}.`);
1845
+ return { ...exchange, status: response.status, statusclass: response.status >= 200 && response.status < 300 ? "success" : response.status >= 400 && response.status < 500 ? "clienterror" : response.status >= 500 ? "servererror" : response.status >= 300 && response.status < 400 ? "redirect" : "unknown", bytes: response.bytes, ...response.mime !== void 0 ? { mime: response.mime } : {}, ...response.bodyref !== void 0 ? { bodyref: response.bodyref } : {}, ...Object.keys(response.headers).length > 0 ? { responseheaders: response.headers } : {} };
1846
+ }
1847
+ function filterexchanges(exchanges, filter) {
1848
+ return exchanges.filter((exchange) => (filter.runid === void 0 || exchange.runid === filter.runid) && (filter.origin === void 0 || exchange.origin === filter.origin) && (filter.status === void 0 || (filter.status === "failed" ? exchange.errorclass !== void 0 : exchange.status === filter.status)));
1849
+ }
1850
+ function headerfilterof(value) {
1851
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { allow: [], redact: [] };
1852
+ const entry = value;
1853
+ const names = (source) => Array.isArray(source) ? source.filter((name) => typeof name === "string" && name.trim().length > 0).map((name) => name.trim().toLowerCase()) : [];
1854
+ return { allow: names(entry.allow), redact: names(entry.redact) };
1855
+ }
1856
+ function capturedheaders(headers, filter) {
1857
+ const result = {};
1858
+ for (const [name, value] of Object.entries(headers)) {
1859
+ const key = name.trim().toLowerCase();
1860
+ if (filter.allow.length > 0 && !filter.allow.includes(key)) continue;
1861
+ result[key] = filter.redact.includes(key) ? "[redacted]" : value;
1862
+ }
1863
+ return result;
1864
+ }
1865
+ function bodyfilterof(value) {
1866
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1867
+ const entry = value;
1868
+ const filter = {};
1869
+ if (typeof entry.urlpattern === "string" && entry.urlpattern.trim()) filter.urlpattern = entry.urlpattern.trim();
1870
+ if (Array.isArray(entry.mimes)) filter.mimes = entry.mimes.filter((mime) => typeof mime === "string" && mime.trim().length > 0).map((mime) => mime.trim().toLowerCase());
1871
+ if (typeof entry.ceiling === "number" && Number.isFinite(entry.ceiling) && entry.ceiling >= 0) filter.ceiling = entry.ceiling;
1872
+ return filter;
1873
+ }
1874
+ function bodymatches(filter, exchange) {
1875
+ if (filter.urlpattern !== void 0 && !exchange.url.includes(filter.urlpattern)) return false;
1876
+ if (filter.mimes !== void 0 && filter.mimes.length > 0) {
1877
+ const mime = ((exchange.mime ?? "").split(";")[0] ?? "").trim().toLowerCase();
1878
+ if (!filter.mimes.includes(mime)) return false;
1879
+ }
1880
+ return true;
1881
+ }
1882
+ var privatemimes = /* @__PURE__ */ new Set(["text/html", "text/plain", "text/xml", "application/xml", "application/json", "text/json", "application/x-www-form-urlencoded", "application/graphql", "multipart/form-data"]);
1883
+ function privatemime(mime) {
1884
+ return privatemimes.has((mime.split(";")[0] ?? "").trim().toLowerCase());
1885
+ }
1886
+ function capturebody(input) {
1887
+ if (!bodymatches(input.filter, input.exchange)) return { refused: `The exchange ${input.exchange.correlationid} does not match the reviewed body filter.` };
1888
+ const ceiling = input.filter.ceiling;
1889
+ const stored = ceiling !== void 0 && input.body.length > ceiling ? input.body.slice(0, ceiling) : input.body;
1890
+ return { record: { ref: input.ref, runid: input.runid, correlationid: input.exchange.correlationid, url: input.exchange.url, mime: input.mime, bytes: stored.length, body: stored, at: input.at }, truncated: stored.length < input.body.length };
1891
+ }
1892
+ function payloadshapeof(body) {
1893
+ if (body === void 0) return [];
1894
+ try {
1895
+ const parsed = JSON.parse(body);
1896
+ const shape = (value) => value && typeof value === "object" && !Array.isArray(value) ? Object.keys(value) : [];
1897
+ if (Array.isArray(parsed)) return parsed.length > 0 ? shape(parsed[0]) : [];
1898
+ return shape(parsed);
1899
+ } catch {
1900
+ return [];
1901
+ }
1902
+ }
1903
+ function isapicandidate(exchange) {
1904
+ if (exchange.initiator === "fetch" || exchange.initiator === "xmlhttprequest") return true;
1905
+ if (exchange.bodyref !== void 0) return true;
1906
+ try {
1907
+ return /\/api\/|\/graphql|\.json($|\?)|\/v\d+\//i.test(new URL(exchange.url).pathname);
1908
+ } catch {
1909
+ return false;
1910
+ }
1911
+ }
1912
+ function apientries(exchanges, bodies) {
1913
+ const bodybyref = new Map(bodies.map((body) => [body.correlationid, body]));
1914
+ const groups = /* @__PURE__ */ new Map();
1915
+ for (const exchange of exchanges) {
1916
+ if (!isapicandidate(exchange)) continue;
1917
+ let endpoint = exchange.url;
1918
+ let origin = exchange.origin;
1919
+ try {
1920
+ const parsed = new URL(exchange.url);
1921
+ endpoint = `${parsed.origin}${parsed.pathname}`;
1922
+ origin = parsed.origin;
1923
+ } catch {
1924
+ }
1925
+ const key = `${exchange.method} ${endpoint}`;
1926
+ const group = groups.get(key) ?? { endpoint, method: exchange.method, origin, mimes: /* @__PURE__ */ new Map(), frequency: 0, json: 0, captured: 0, shapes: /* @__PURE__ */ new Map(), correlationids: [] };
1927
+ group.frequency += 1;
1928
+ group.correlationids.push(exchange.correlationid);
1929
+ const body = exchange.bodyref !== void 0 ? bodybyref.get(exchange.correlationid) : void 0;
1930
+ const mime = body?.mime ?? exchange.mime ?? "";
1931
+ group.mimes.set(mime, (group.mimes.get(mime) ?? 0) + 1);
1932
+ if (body !== void 0) {
1933
+ group.captured += 1;
1934
+ const shape = payloadshapeof(body.body);
1935
+ if (shape.length > 0) group.json += 1;
1936
+ const shapekey = shape.join(",");
1937
+ group.shapes.set(shapekey, (group.shapes.get(shapekey) ?? 0) + 1);
1938
+ }
1939
+ groups.set(key, group);
1940
+ }
1941
+ return [...groups.values()].map((group) => {
1942
+ const mime = [...group.mimes.entries()].sort((left, right) => right[1] - left[1])[0]?.[0] ?? "";
1943
+ const modalshape = [...group.shapes.entries()].sort((left, right) => right[1] - left[1])[0];
1944
+ return { endpoint: group.endpoint, method: group.method, mime, frequency: group.frequency, payloadshape: (modalshape?.[0] ?? "").split(",").filter(Boolean), jsonshare: group.captured > 0 ? group.json / group.captured : 0, stability: group.captured > 0 ? (modalshape?.[1] ?? 0) / group.captured : 0, origin: group.origin, correlationids: group.correlationids };
1945
+ });
1946
+ }
1947
+ function rankapis(entries) {
1948
+ const score = (entry) => entry.frequency * (1 + entry.jsonshare + entry.stability);
1949
+ return [...entries].sort((left, right) => score(right) - score(left) || right.frequency - left.frequency);
1950
+ }
1951
+ function apireplayspecof(value) {
1952
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1953
+ const entry = value;
1954
+ if (typeof entry.endpoint !== "string" || !entry.endpoint.trim()) return void 0;
1955
+ const spec = { endpoint: entry.endpoint.trim() };
1956
+ if (typeof entry.verb === "string" && entry.verb.trim()) spec.verb = entry.verb.trim().toUpperCase();
1957
+ if (entry.overrides !== void 0 && entry.overrides !== null && typeof entry.overrides === "object" && !Array.isArray(entry.overrides)) {
1958
+ const overrides = {};
1959
+ for (const [name, override] of Object.entries(entry.overrides)) {
1960
+ if (typeof override === "string") overrides[name] = override;
1961
+ }
1962
+ spec.overrides = overrides;
1963
+ }
1964
+ if (Array.isArray(entry.paths)) spec.paths = entry.paths.filter((path) => typeof path === "string" && path.trim().length > 0);
1965
+ return spec;
1966
+ }
1967
+ function replayurl(spec) {
1968
+ const url = new URL(spec.endpoint);
1969
+ for (const [name, value] of Object.entries(spec.overrides ?? {})) url.searchParams.set(name, value);
1970
+ return url.toString();
1971
+ }
1972
+ function extractvalues(body, paths) {
1973
+ let parsed;
1974
+ try {
1975
+ parsed = JSON.parse(body);
1976
+ } catch {
1977
+ return paths.map((path) => ({ path, missing: true }));
1978
+ }
1979
+ const fields = readpath(parsed, paths.map((path) => ({ name: path, path, kind: "json" })));
1980
+ return fields.map((field) => ({ path: field.path, ...field.value !== void 0 ? { value: field.value } : {}, ...field.missing ? { missing: true } : {} }));
1981
+ }
1982
+
1983
+ // socketbus.ts
1984
+ var socketkinds = ["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"];
1985
+ function channelorigin(url) {
1986
+ try {
1987
+ const parsed = new URL(url);
1988
+ const protocol = parsed.protocol === "wss:" ? "https:" : parsed.protocol === "ws:" ? "http:" : parsed.protocol;
1989
+ return `${protocol}//${parsed.host}`;
1990
+ } catch {
1991
+ return "";
1992
+ }
1993
+ }
1994
+ function channeloptionsof(value) {
1995
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1996
+ const entry = value;
1997
+ if (typeof entry.url !== "string" || !entry.url.trim()) return void 0;
1998
+ const options = {};
1999
+ if (Array.isArray(entry.protocols)) options.protocols = entry.protocols.filter((item) => typeof item === "string" && item.trim().length > 0);
2000
+ if (typeof entry.reconnect === "number" && Number.isFinite(entry.reconnect)) options.reconnect = entry.reconnect;
2001
+ if (typeof entry.backoff === "number" && Number.isFinite(entry.backoff)) options.backoff = entry.backoff;
2002
+ if (typeof entry.backoffceiling === "number" && Number.isFinite(entry.backoffceiling)) options.backoffceiling = entry.backoffceiling;
2003
+ if (typeof entry.lifetime === "number" && Number.isFinite(entry.lifetime)) options.lifetime = entry.lifetime;
2004
+ return { url: entry.url.trim(), options };
2005
+ }
2006
+ function newchannel(input) {
2007
+ return { id: input.id, runid: input.runid, stepid: input.stepid, kind: input.kind, url: input.url, origin: channelorigin(input.url), state: "connecting", openedat: input.at, sent: 0, received: 0, reconnects: 0, ...input.protocols !== void 0 && input.protocols.length > 0 ? { protocols: [...input.protocols] } : {} };
2008
+ }
2009
+ function reconnectwaits(attempts, base, ceiling) {
2010
+ const count = Math.max(0, Math.floor(attempts));
2011
+ const waits = [];
2012
+ let wait = Math.max(0, base);
2013
+ for (let index = 0; index < count; index += 1) {
2014
+ waits.push(wait);
2015
+ const next = wait * 2;
2016
+ wait = ceiling !== void 0 && Number.isFinite(ceiling) && ceiling >= 0 ? Math.min(next, ceiling) : next;
2017
+ }
2018
+ return waits;
2019
+ }
2020
+ async function openchannel(input) {
2021
+ const sleep = input.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, Math.max(0, milliseconds))));
2022
+ const now = input.now ?? Date.now;
2023
+ const attempts = Math.max(1, Math.floor(input.options.reconnect ?? 0) + 1);
2024
+ const waits = reconnectwaits(attempts - 1, input.options.backoff ?? 0, input.options.backoffceiling);
2025
+ let record2 = { ...input.record, state: "connecting" };
2026
+ let lasterror = "";
2027
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
2028
+ try {
2029
+ const result = await input.connect(record2.url, record2.protocols ?? []);
2030
+ if (result.open) return { ...record2, state: "open", openedat: now() };
2031
+ lasterror = result.error ?? `closed with code ${result.code ?? 0}`;
2032
+ } catch (error) {
2033
+ lasterror = error instanceof Error ? error.message : String(error);
2034
+ }
2035
+ if (attempt < attempts - 1) {
2036
+ const wait = waits[attempt] ?? 0;
2037
+ if (wait > 0) await sleep(wait);
2038
+ record2 = { ...record2, reconnects: record2.reconnects + 1 };
2039
+ }
2040
+ }
2041
+ return { ...record2, state: "failed", error: lasterror };
2042
+ }
2043
+ function closechannel(record2, at, error) {
2044
+ const state = error !== void 0 ? "failed" : "closed";
2045
+ return { ...record2, state, closedat: at, ...error !== void 0 ? { error } : {} };
2046
+ }
2047
+ function tagmessage(state, channelid, stream, payload, at) {
2048
+ const sequence = (state.sequences[channelid] ?? 0) + 1;
2049
+ const envelope = { channelid, stream, payload, sequence, at };
2050
+ return { state: { sequences: { ...state.sequences, [channelid]: sequence }, queue: state.queue }, envelope };
2051
+ }
2052
+ function publishmessage(state, channelid, stream, payload, at) {
2053
+ return tagmessage(state, channelid, stream, payload, at);
2054
+ }
2055
+ function receivemessage(state, channelid, stream, payload, at) {
2056
+ const tagged = tagmessage(state, channelid, stream, payload, at);
2057
+ return { state: { ...tagged.state, queue: [...state.queue, tagged.envelope] }, envelope: tagged.envelope };
2058
+ }
2059
+ function pathstep2(current, segment) {
2060
+ if (Array.isArray(current) && /^\d+$/.test(segment)) return current[Number.parseInt(segment, 10)];
2061
+ if (current && typeof current === "object" && !Array.isArray(current)) return current[segment];
2062
+ return void 0;
2063
+ }
2064
+ function matchmessage(filter, envelope) {
2065
+ if (!filter) return true;
2066
+ if (filter.stream !== void 0 && filter.stream !== envelope.stream) return false;
2067
+ if (filter.path !== void 0) {
2068
+ try {
2069
+ const parsed = JSON.parse(envelope.payload);
2070
+ let current = parsed;
2071
+ let missing = false;
2072
+ for (const segment of filter.path.split(".")) {
2073
+ const next = pathstep2(current, segment);
2074
+ if (next === void 0) {
2075
+ missing = true;
2076
+ break;
2077
+ }
2078
+ current = next;
2079
+ }
2080
+ if (missing) return false;
2081
+ } catch {
2082
+ return false;
2083
+ }
2084
+ }
2085
+ return true;
2086
+ }
2087
+ function collectmessages(state, channelid, filter) {
2088
+ const limit = filter?.limit !== void 0 && Number.isFinite(filter.limit) && filter.limit >= 1 ? Math.floor(filter.limit) : Number.POSITIVE_INFINITY;
2089
+ const matched = [];
2090
+ const queue = [];
2091
+ for (const envelope of state.queue) {
2092
+ if (envelope.channelid === channelid && matched.length < limit && matchmessage(filter, envelope)) matched.push(envelope);
2093
+ else queue.push(envelope);
2094
+ }
2095
+ return { state: { sequences: state.sequences, queue }, matched };
2096
+ }
2097
+ function sequenceintegrity(envelopes) {
2098
+ const last = /* @__PURE__ */ new Map();
2099
+ const gaps = [];
2100
+ for (const envelope of envelopes) {
2101
+ const expected = (last.get(envelope.channelid) ?? 0) + 1;
2102
+ if (envelope.sequence !== expected) gaps.push({ channelid: envelope.channelid, expected, found: envelope.sequence });
2103
+ last.set(envelope.channelid, Math.max(envelope.sequence, expected));
2104
+ }
2105
+ return { ok: gaps.length === 0, gaps };
2106
+ }
2107
+ function messagefilterof(value) {
2108
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2109
+ const entry = value;
2110
+ const filter = {};
2111
+ if (typeof entry.stream === "string" && entry.stream.trim()) filter.stream = entry.stream.trim();
2112
+ if (typeof entry.path === "string" && entry.path.trim()) filter.path = entry.path.trim();
2113
+ if (typeof entry.limit === "number" && Number.isFinite(entry.limit) && entry.limit >= 1) filter.limit = Math.floor(entry.limit);
2114
+ return filter;
2115
+ }
2116
+ function parsessetext(text2) {
2117
+ const separator = text2.lastIndexOf("\n\n");
2118
+ const complete = separator === -1 ? "" : text2.slice(0, separator + 2);
2119
+ const rest = separator === -1 ? text2 : text2.slice(separator + 2);
2120
+ const events = [];
2121
+ for (const block of complete.split(/\n\n/)) {
2122
+ const id = [];
2123
+ const names = [];
2124
+ const data = [];
2125
+ let retry;
2126
+ for (const line of block.split("\n")) {
2127
+ if (line === "" || line.startsWith(":")) continue;
2128
+ const colon = line.indexOf(":");
2129
+ const field = colon === -1 ? line : line.slice(0, colon);
2130
+ let value = colon === -1 ? "" : line.slice(colon + 1);
2131
+ if (value.startsWith(" ")) value = value.slice(1);
2132
+ if (field === "id" && value !== "") id.push(value);
2133
+ if (field === "event" && value !== "") names.push(value);
2134
+ if (field === "data") data.push(value);
2135
+ if (field === "retry" && /^\d+$/.test(value)) retry = Number.parseInt(value, 10);
2136
+ }
2137
+ if (id.length === 0 && names.length === 0 && data.length === 0) continue;
2138
+ events.push({ ...id.length > 0 ? { id: id[id.length - 1] } : {}, ...names.length > 0 ? { event: names[names.length - 1] } : {}, data: data.join("\n"), ...retry !== void 0 ? { retry } : {} });
2139
+ }
2140
+ return { events, rest };
2141
+ }
2142
+ function sserequestheaders(record2) {
2143
+ return { accept: "text/event-stream", ...record2.lasteventid !== void 0 && record2.lasteventid !== "" ? { "last-event-id": record2.lasteventid } : {} };
2144
+ }
2145
+ function subscriptionoptionsof(value) {
2146
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2147
+ const entry = value;
2148
+ if (typeof entry.url !== "string" || !entry.url.trim()) return void 0;
2149
+ const cancel = entry.cancel;
2150
+ if (!cancel || typeof cancel !== "object" || Array.isArray(cancel)) return void 0;
2151
+ const cancelrecord = cancel;
2152
+ if (cancelrecord.kind !== "stop" && cancelrecord.kind !== "lifetime") return void 0;
2153
+ if (typeof cancelrecord.value !== "string" && typeof cancelrecord.value !== "number") return void 0;
2154
+ const result = { url: entry.url.trim(), cancel: { kind: cancelrecord.kind, value: cancelrecord.value } };
2155
+ if (typeof entry.lifetime === "number" && Number.isFinite(entry.lifetime) && entry.lifetime > 0) result.lifetime = entry.lifetime;
2156
+ if (typeof entry.lasteventid === "string" && entry.lasteventid.trim()) result.lasteventid = entry.lasteventid.trim();
2157
+ return result;
2158
+ }
2159
+ function pollcursorof(value) {
2160
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2161
+ const entry = value;
2162
+ if (typeof entry.url !== "string" || !entry.url.trim()) return void 0;
2163
+ if (typeof entry.cursorfield !== "string" || !entry.cursorfield.trim()) return void 0;
2164
+ if (typeof entry.interval !== "number" || !Number.isFinite(entry.interval) || entry.interval <= 0) return void 0;
2165
+ const stop = entry.stop;
2166
+ if (!stop || typeof stop !== "object" || Array.isArray(stop)) return void 0;
2167
+ const stoprecord = stop;
2168
+ if (typeof stoprecord.field !== "string" || !stoprecord.field.trim()) return void 0;
2169
+ if (typeof stoprecord.equals !== "string") return void 0;
2170
+ const cursor = { url: entry.url.trim(), cursorfield: entry.cursorfield.trim(), interval: entry.interval, stop: { field: stoprecord.field.trim(), equals: stoprecord.equals } };
2171
+ if (typeof entry.maxpolls === "number" && Number.isFinite(entry.maxpolls) && entry.maxpolls >= 1) cursor.maxpolls = Math.floor(entry.maxpolls);
2172
+ if (typeof entry.param === "string" && entry.param.trim()) cursor.param = entry.param.trim();
2173
+ return cursor;
2174
+ }
2175
+ function cursorfrom(response, field) {
2176
+ let current = response;
2177
+ for (const segment of field.split(".")) {
2178
+ const next = pathstep2(current, segment);
2179
+ if (next === void 0) return void 0;
2180
+ current = next;
2181
+ }
2182
+ return current === void 0 || current === null ? void 0 : String(current);
2183
+ }
2184
+ function pollurl(cursor, value) {
2185
+ if (cursor.param === void 0 || value === void 0) {
2186
+ return { url: cursor.url, ...value !== void 0 ? { body: JSON.stringify({ [cursor.cursorfield]: value }) } : {} };
2187
+ }
2188
+ const url = new URL(cursor.url);
2189
+ url.searchParams.set(cursor.param, value);
2190
+ return { url: url.toString() };
2191
+ }
2192
+ function polldecision(input) {
2193
+ if (input.cancelled?.() === true) return { continue: false, reason: "The long poll loop was cancelled." };
2194
+ if (input.expiresat !== void 0 && input.now >= input.expiresat) return { continue: false, reason: "The long poll loop stopped at the reviewed plan expiry." };
2195
+ const stopvalue = cursorfrom(input.response, input.cursor.stop.field);
2196
+ if (stopvalue !== void 0 && stopvalue === input.cursor.stop.equals) return { continue: false, reason: `The stop condition matched ${input.cursor.stop.field} ${stopvalue}.` };
2197
+ if (input.cursor.maxpolls !== void 0 && input.polls + 1 >= input.cursor.maxpolls) return { continue: false, reason: `The long poll loop reached the reviewed poll ceiling of ${input.cursor.maxpolls}.` };
2198
+ const value = cursorfrom(input.response, input.cursor.cursorfield);
2199
+ const next = pollurl(input.cursor, value);
2200
+ return { continue: true, reason: "The long poll loop continues.", ...value !== void 0 ? { cursor: value } : {}, next: { ...next, wait: input.cursor.interval } };
2201
+ }
2202
+
1713
2203
  // policy.ts
1714
- 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"]);
1715
- var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
1716
- 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"]);
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"]);
2205
+ 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"]);
1717
2207
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
1718
2208
  var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
1719
2209
  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"]);
@@ -1726,6 +2216,8 @@ var filesactions = /* @__PURE__ */ new Set(["batchdownload", "pausedownload", "r
1726
2216
  var captureactions = /* @__PURE__ */ new Set(["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"]);
1727
2217
  var mediaactions = /* @__PURE__ */ new Set(["capturepdf", "recordscreen", "captureaudio", "captureframe", "downloadimages", "shotcanvas", "probestream", "readmedia", "readassets", "timelapse", "convertimage", "makethumbs"]);
1728
2218
  var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"]);
2219
+ var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"]);
2220
+ var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
1729
2221
  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"]);
1730
2222
  var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
1731
2223
  var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
@@ -2434,6 +2926,59 @@ function ismediakind(kind) {
2434
2926
  function ishttpkind(kind) {
2435
2927
  return httpactions.has(kind);
2436
2928
  }
2929
+ function issocketkind(kind) {
2930
+ return socketactions.has(kind);
2931
+ }
2932
+ function isnetwatchkind(kind) {
2933
+ return netwatchactions.has(kind);
2934
+ }
2935
+ function resolvedrisk(step) {
2936
+ if (step.kind === "capturebodies") {
2937
+ let options = {};
2938
+ try {
2939
+ options = parseoptions(step);
2940
+ } catch {
2941
+ options = {};
2942
+ }
2943
+ const body = options.body;
2944
+ const mimes = body && typeof body === "object" && !Array.isArray(body) ? body.mimes : void 0;
2945
+ if (Array.isArray(mimes) && mimes.some((mime) => typeof mime === "string" && privatemime(mime))) return "sensitive";
2946
+ return "interaction";
2947
+ }
2948
+ if (step.kind === "extractapi") {
2949
+ let options = {};
2950
+ try {
2951
+ options = parseoptions(step);
2952
+ } catch {
2953
+ options = {};
2954
+ }
2955
+ const replay = options.replay;
2956
+ const verb = replay && typeof replay === "object" && !Array.isArray(replay) ? replay.verb : void 0;
2957
+ if (typeof verb === "string" && !["GET", "HEAD", "OPTIONS"].includes(verb.trim().toUpperCase())) return "sensitive";
2958
+ return "read";
2959
+ }
2960
+ return actionrisk(step.kind);
2961
+ }
2962
+ function socketgate(session, url) {
2963
+ let parsed;
2964
+ try {
2965
+ parsed = new URL(url);
2966
+ } catch {
2967
+ return { allowed: false, reason: "The channel needs a valid url before it can be reviewed." };
2968
+ }
2969
+ if (parsed.protocol !== "wss:" && parsed.protocol !== "https:") return { allowed: false, reason: "Channels use wss websocket urls or https event stream urls only." };
2970
+ if (parsed.username || parsed.password) return { allowed: false, reason: "Channel credentials are not allowed in the url." };
2971
+ const origin = channelorigin(url);
2972
+ if (!origingranted(session, origin)) return { allowed: false, reason: `The channel to ${origin} stays outside the session origin grants.` };
2973
+ return { allowed: true };
2974
+ }
2975
+ function watchgate(session, settings, now) {
2976
+ if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the request watch." };
2977
+ if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot watch requests." };
2978
+ if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot watch requests." };
2979
+ if (settings?.webrequestgrant !== true) return { allowed: false, reason: "Request watching needs the webrequest grant in the review panel first; the observation derives from the page timing buffers and adds no manifest permission." };
2980
+ return { allowed: true };
2981
+ }
2437
2982
  function origincheck(session, url) {
2438
2983
  let parsed;
2439
2984
  try {
@@ -2599,6 +3144,108 @@ function fetchnumeric(options, key) {
2599
3144
  function isrecordingkind(kind) {
2600
3145
  return kind === "recordscreen" || kind === "captureaudio";
2601
3146
  }
3147
+ function validatesocketgrammar(step, options) {
3148
+ const kind = step.kind;
3149
+ if (kind === "opensocket") {
3150
+ const channel = channeloptionsof(options.socket);
3151
+ if (!channel) return { allowed: false, reason: "A reviewed socket with a url is required in options.socket." };
3152
+ if (channel.options.reconnect !== void 0 && !Number.isInteger(channel.options.reconnect)) return { allowed: false, reason: "The reviewed socket reconnect budget must be an integer attempt count with no code ceiling." };
3153
+ for (const label of ["backoff", "backoffceiling"]) {
3154
+ const value = channel.options[label];
3155
+ if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < 0)) return { allowed: false, reason: `The reviewed socket ${label} must be zero or a positive number of milliseconds with no code ceiling.` };
3156
+ }
3157
+ if (channel.options.lifetime !== void 0 && (typeof channel.options.lifetime !== "number" || !Number.isFinite(channel.options.lifetime) || channel.options.lifetime <= 0)) return { allowed: false, reason: "The reviewed socket lifetime window must be a positive number of milliseconds." };
3158
+ }
3159
+ if (kind === "sendmessage") {
3160
+ const message = options.message;
3161
+ if (!message || typeof message !== "object" || Array.isArray(message)) return { allowed: false, reason: "A reviewed message with a channel, stream and payload is required in options.message." };
3162
+ const envelope = message;
3163
+ if (!isnonempty(envelope.channel)) return { allowed: false, reason: "The reviewed message needs the open channel id in options.message.channel." };
3164
+ if (envelope.stream !== void 0 && !isnonempty(envelope.stream)) return { allowed: false, reason: "The reviewed message stream name must be a non-empty string." };
3165
+ if (typeof envelope.payload !== "string") return { allowed: false, reason: "The reviewed message payload must be a string." };
3166
+ }
3167
+ if (kind === "waitmessage") {
3168
+ if (options.filter !== void 0) {
3169
+ const filter = options.filter;
3170
+ if (!filter || typeof filter !== "object" || Array.isArray(filter)) return { allowed: false, reason: "The reviewed message filter must be an object of stream, path and limit." };
3171
+ const reviewed = filter;
3172
+ if (reviewed.stream !== void 0 && !isnonempty(reviewed.stream)) return { allowed: false, reason: "The reviewed message filter stream name must be a non-empty string." };
3173
+ if (reviewed.path !== void 0 && (typeof reviewed.path !== "string" || !validpath(reviewed.path.trim()))) return { allowed: false, reason: "The reviewed message filter path must be a dotted path of non-empty segments." };
3174
+ if (reviewed.limit !== void 0 && (typeof reviewed.limit !== "number" || !Number.isInteger(reviewed.limit) || reviewed.limit < 1)) return { allowed: false, reason: "The reviewed message match limit must be a positive integer with no code ceiling." };
3175
+ }
3176
+ if (options.wait !== void 0 && (typeof options.wait !== "number" || !Number.isFinite(options.wait) || options.wait < 0)) return { allowed: false, reason: "The reviewed message wait budget must be zero or a positive number of milliseconds." };
3177
+ }
3178
+ if (kind === "subscribesse") {
3179
+ const subscription = subscriptionoptionsof(options.subscription);
3180
+ if (!subscription) return { allowed: false, reason: "A reviewed subscription with an event stream url and a cancellation path is required in options.subscription." };
3181
+ const rawlifetime = options.subscription && typeof options.subscription === "object" && !Array.isArray(options.subscription) ? options.subscription.lifetime : void 0;
3182
+ if (rawlifetime !== void 0 && (typeof rawlifetime !== "number" || !Number.isFinite(rawlifetime) || rawlifetime <= 0)) return { allowed: false, reason: "The reviewed subscription lifetime window must be a positive number of milliseconds." };
3183
+ }
3184
+ if (kind === "longpoll") {
3185
+ const cursor = pollcursorof(options.poll);
3186
+ if (!cursor) return { allowed: false, reason: "A reviewed poll cursor with a url, cursor field, interval and stop condition is required in options.poll." };
3187
+ const wait = options.wait;
3188
+ if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The reviewed long poll wait budget must be zero or a positive number of milliseconds." };
3189
+ if (wait !== void 0 && cursor.interval > wait) return { allowed: false, reason: `The long poll interval of ${cursor.interval} milliseconds exceeds the reviewed wait budget of ${wait} milliseconds; review a wider budget or a shorter interval.` };
3190
+ }
3191
+ return { allowed: true };
3192
+ }
3193
+ function validatenetwatchgrammar(step, options) {
3194
+ const kind = step.kind;
3195
+ if (kind === "watchrequests") {
3196
+ if (options.watch !== void 0) {
3197
+ const watch = options.watch;
3198
+ if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed watch window must be an object." };
3199
+ const reviewed = watch;
3200
+ if (reviewed.window !== void 0 && (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0)) return { allowed: false, reason: "The reviewed watch window must be zero or a positive number of milliseconds." };
3201
+ }
3202
+ if (options.limit !== void 0 && (typeof options.limit !== "number" || !Number.isInteger(options.limit) || options.limit < 1)) return { allowed: false, reason: "The reviewed watch match limit must be a positive integer with no code ceiling." };
3203
+ }
3204
+ if (kind === "readheaders") {
3205
+ const headers = options.headers;
3206
+ if (!headers || typeof headers !== "object" || Array.isArray(headers)) return { allowed: false, reason: "A reviewed header filter with a name allowlist and a redaction list is required in options.headers." };
3207
+ const reviewed = headers;
3208
+ if (!Array.isArray(reviewed.allow) || reviewed.allow.length === 0 || !reviewed.allow.every((name) => isnonempty(name))) return { allowed: false, reason: "The reviewed header allowlist must be a non-empty list of header names." };
3209
+ if (!Array.isArray(reviewed.redact) || reviewed.redact.length === 0 || !reviewed.redact.every((name) => isnonempty(name))) return { allowed: false, reason: "Header capture requires a reviewed redaction list before any header value is stored." };
3210
+ }
3211
+ if (kind === "capturebodies") {
3212
+ const body = options.body;
3213
+ if (!body || typeof body !== "object" || Array.isArray(body)) return { allowed: false, reason: "A reviewed body filter with a url pattern, mime list and byte ceiling is required in options.body." };
3214
+ const reviewed = body;
3215
+ if (reviewed.urlpattern !== void 0 && !isnonempty(reviewed.urlpattern)) return { allowed: false, reason: "The reviewed body url pattern must be a non-empty string." };
3216
+ if (reviewed.mimes !== void 0 && (!Array.isArray(reviewed.mimes) || reviewed.mimes.length === 0 || !reviewed.mimes.every((mime) => isnonempty(mime)))) return { allowed: false, reason: "The reviewed body mime list must be a non-empty list of mime types." };
3217
+ if (reviewed.ceiling !== void 0 && (typeof reviewed.ceiling !== "number" || !Number.isFinite(reviewed.ceiling) || reviewed.ceiling < 0)) return { allowed: false, reason: "The reviewed body byte ceiling must be zero or a positive number of bytes with no code ceiling." };
3218
+ }
3219
+ if (kind === "mapapi") {
3220
+ if (options.limit !== void 0 && (typeof options.limit !== "number" || !Number.isInteger(options.limit) || options.limit < 1)) return { allowed: false, reason: "The reviewed mapapi match limit must be a positive integer with no code ceiling." };
3221
+ }
3222
+ if (kind === "extractapi") {
3223
+ const replay = apireplayspecof(options.replay);
3224
+ if (!replay) return { allowed: false, reason: "A reviewed replay spec with an endpoint is required in options.replay." };
3225
+ if (!ishttpsurl(replay.endpoint)) return { allowed: false, reason: "The reviewed replay endpoint must be an HTTPS url." };
3226
+ if (replay.verb !== void 0 && !["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"].includes(replay.verb)) return { allowed: false, reason: "The reviewed replay verb must be a known HTTP verb." };
3227
+ for (const path of replay.paths ?? []) {
3228
+ if (!validpath(path.trim())) return { allowed: false, reason: `The reviewed replay extraction path ${path} must be a dotted path of non-empty segments.` };
3229
+ }
3230
+ }
3231
+ return { allowed: true };
3232
+ }
3233
+ function sockettarget(step) {
3234
+ let options = {};
3235
+ try {
3236
+ options = parseoptions(step);
3237
+ } catch {
3238
+ options = {};
3239
+ }
3240
+ for (const key of ["socket", "subscription", "poll"]) {
3241
+ const value = options[key];
3242
+ if (value && typeof value === "object" && !Array.isArray(value)) {
3243
+ const url = value.url;
3244
+ if (typeof url === "string" && url.trim()) return url.trim();
3245
+ }
3246
+ }
3247
+ return void 0;
3248
+ }
2602
3249
  function mediagate(session, tabid, origin, now) {
2603
3250
  if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the media capture." };
2604
3251
  if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture media." };
@@ -2956,6 +3603,14 @@ function validatestep(step, origin) {
2956
3603
  const httpcheck = validatehttpgrammar(step, options);
2957
3604
  if (!httpcheck.allowed) return httpcheck;
2958
3605
  }
3606
+ if (issocketkind(step.kind)) {
3607
+ const socketcheck = validatesocketgrammar(step, options);
3608
+ if (!socketcheck.allowed) return socketcheck;
3609
+ }
3610
+ if (isnetwatchkind(step.kind)) {
3611
+ const netwatchcheck = validatenetwatchgrammar(step, options);
3612
+ if (!netwatchcheck.allowed) return netwatchcheck;
3613
+ }
2959
3614
  if (step.kind === "tabcreate") {
2960
3615
  if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
2961
3616
  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." };
@@ -3046,6 +3701,30 @@ function canexecute(input) {
3046
3701
  if (!consentgate.allowed) return consentgate;
3047
3702
  }
3048
3703
  }
3704
+ if (issocketkind(input.step.kind)) {
3705
+ const channelurl = sockettarget(input.step);
3706
+ if (channelurl !== void 0) {
3707
+ const channelgate = socketgate(input.session, channelurl);
3708
+ if (!channelgate.allowed) return channelgate;
3709
+ }
3710
+ }
3711
+ if (input.step.kind === "watchrequests") {
3712
+ const watchgatecheck = watchgate(input.session, input.settings, now);
3713
+ if (!watchgatecheck.allowed) return watchgatecheck;
3714
+ }
3715
+ if (input.step.kind === "extractapi") {
3716
+ let replayoptions = {};
3717
+ try {
3718
+ replayoptions = parseoptions(input.step);
3719
+ } catch {
3720
+ replayoptions = {};
3721
+ }
3722
+ const replay = apireplayspecof(replayoptions.replay);
3723
+ if (replay !== void 0) {
3724
+ const replaygate = origincheck(input.session, replay.endpoint);
3725
+ if (!replaygate.allowed) return replaygate;
3726
+ }
3727
+ }
3049
3728
  if (input.step.kind === "openlink" || input.step.kind === "openprivate" || input.step.kind === "batchopen" || input.step.kind === "prefetch" || input.step.kind === "deeplink" || input.step.kind === "reopentab") {
3050
3729
  let options = {};
3051
3730
  try {
@@ -3067,7 +3746,7 @@ function canexecute(input) {
3067
3746
  }
3068
3747
 
3069
3748
  // version.ts
3070
- var packageversion = "1.1.42";
3749
+ var packageversion = "1.1.43";
3071
3750
 
3072
3751
  // types.ts
3073
3752
  var protocolversion = packageversion;
@@ -3088,6 +3767,10 @@ function parseproposal(value, origin, grants) {
3088
3767
  const planinput = record(root.plan);
3089
3768
  const stepsinput = planinput.steps;
3090
3769
  if (!Array.isArray(stepsinput) || stepsinput.length === 0) throw new Error("A plan needs at least one step.");
3770
+ const createdat = Date.now();
3771
+ const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
3772
+ if (expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
3773
+ const planwindow = expiresat - createdat;
3091
3774
  const steps = stepsinput.map((input, index) => {
3092
3775
  const candidate = record(input);
3093
3776
  const kind = text(candidate.kind, `step ${index + 1} kind`);
@@ -3095,7 +3778,7 @@ function parseproposal(value, origin, grants) {
3095
3778
  id: typeof candidate.id === "string" ? candidate.id : crypto.randomUUID(),
3096
3779
  kind,
3097
3780
  summary: text(candidate.summary, `step ${index + 1} summary`),
3098
- risk: actionrisk(kind),
3781
+ risk: resolvedrisk(stepof(kind, candidate, index)),
3099
3782
  ...typeof candidate.target === "string" ? { target: candidate.target } : {},
3100
3783
  ...typeof candidate.value === "string" ? { value: candidate.value } : {},
3101
3784
  ...typeof candidate.options === "string" ? { options: candidate.options } : {}
@@ -3113,6 +3796,29 @@ function parseproposal(value, origin, grants) {
3113
3796
  });
3114
3797
  if (!granted) throw new Error(`The fetch request to ${target} targets an origin outside the grants.`);
3115
3798
  }
3799
+ const channelurl = sockettarget(step);
3800
+ if (channelurl !== void 0) {
3801
+ const channeloriginvalue = channeloriginof(channelurl);
3802
+ const granted = covered.some((pattern) => {
3803
+ try {
3804
+ return new URL(channelurl).origin === new URL(pattern).origin || channeloriginvalue === new URL(pattern).origin;
3805
+ } catch {
3806
+ return false;
3807
+ }
3808
+ });
3809
+ if (!granted) throw new Error(`The channel to ${channelurl} targets an origin outside the grants.`);
3810
+ }
3811
+ let lifetime;
3812
+ try {
3813
+ const options = parseoptions(step);
3814
+ for (const key of ["socket", "subscription"]) {
3815
+ const value2 = options[key];
3816
+ if (value2 && typeof value2 === "object" && !Array.isArray(value2) && typeof value2.lifetime === "number") lifetime = value2.lifetime;
3817
+ }
3818
+ } catch {
3819
+ lifetime = void 0;
3820
+ }
3821
+ if (lifetime !== void 0 && lifetime > planwindow) throw new Error(`The channel lifetime of ${lifetime} milliseconds exceeds the reviewed plan window of ${planwindow} milliseconds.`);
3116
3822
  return step;
3117
3823
  });
3118
3824
  for (const step of steps) {
@@ -3125,8 +3831,6 @@ function parseproposal(value, origin, grants) {
3125
3831
  const review = submitreviewgranted(steps, step.id);
3126
3832
  if (!review.allowed) throw new Error(review.reason);
3127
3833
  }
3128
- const createdat = Date.now();
3129
- const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
3130
3834
  const plan = {
3131
3835
  id: typeof planinput.id === "string" ? planinput.id : crypto.randomUUID(),
3132
3836
  objective: text(planinput.objective, "objective"),
@@ -3136,14 +3840,24 @@ function parseproposal(value, origin, grants) {
3136
3840
  expiresat,
3137
3841
  state: "pending"
3138
3842
  };
3139
- if (plan.expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
3140
3843
  return { version: protocolversion, plan };
3141
3844
  }
3845
+ function stepof(kind, candidate, index) {
3846
+ return { id: typeof candidate.id === "string" ? candidate.id : `candidate${index + 1}`, kind, summary: typeof candidate.summary === "string" ? candidate.summary : "", risk: "read", ...typeof candidate.options === "string" ? { options: candidate.options } : {} };
3847
+ }
3848
+ function channeloriginof(url) {
3849
+ try {
3850
+ const parsed = new URL(url);
3851
+ return `${parsed.protocol === "wss:" ? "https:" : parsed.protocol}//${parsed.host}`;
3852
+ } catch {
3853
+ return "";
3854
+ }
3855
+ }
3142
3856
  function requestbody(input) {
3143
3857
  return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
3144
3858
  }
3145
3859
  function outcomeresponse(input) {
3146
- 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 } : {} });
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 } : {} });
3147
3861
  }
3148
3862
  function mapresponse(input) {
3149
3863
  return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
@@ -3234,10 +3948,17 @@ function callsreport(input) {
3234
3948
  });
3235
3949
  return { version: protocolversion, calls };
3236
3950
  }
3951
+ function exchangesreport(input) {
3952
+ return { version: protocolversion, exchanges: input.exchanges, channels: input.channels, subscriptions: input.subscriptions, apimap: input.apimap };
3953
+ }
3237
3954
  export {
3238
3955
  annotationplanof,
3956
+ apientries,
3957
+ apireplayspecof,
3239
3958
  assetentries,
3240
3959
  blendrows,
3960
+ bodyfilterof,
3961
+ bodymatches,
3241
3962
  buildname,
3242
3963
  buildpdf,
3243
3964
  buildsheet,
@@ -3246,6 +3967,8 @@ export {
3246
3967
  callrest,
3247
3968
  callsreport,
3248
3969
  canexecute,
3970
+ capturebody,
3971
+ capturedheaders,
3249
3972
  captureelement,
3250
3973
  captureformats,
3251
3974
  capturekinds,
@@ -3256,9 +3979,15 @@ export {
3256
3979
  capturestitched,
3257
3980
  capturetargets,
3258
3981
  capturevisible,
3982
+ channeloptionsof,
3983
+ channelorigin,
3984
+ closechannel,
3985
+ collectmessages,
3259
3986
  convertdirectiveof,
3987
+ correlationid,
3260
3988
  croprect,
3261
3989
  crossesviewport,
3990
+ cursorfrom,
3262
3991
  datasetresponse,
3263
3992
  dedupeimages,
3264
3993
  actionrisk as deriveactionrisk,
@@ -3266,9 +3995,13 @@ export {
3266
3995
  downloadreport,
3267
3996
  errorreportresponse,
3268
3997
  eventresponse,
3998
+ exchangesreport,
3269
3999
  extractionreport,
4000
+ extractvalues,
4001
+ failureclass,
3270
4002
  fetchoptionsof,
3271
4003
  fetchrequestof,
4004
+ filterexchanges,
3272
4005
  finishrecording,
3273
4006
  fixedheadermatch,
3274
4007
  formreportresponse,
@@ -3276,6 +4009,7 @@ export {
3276
4009
  generatedvalueallowed,
3277
4010
  graphqlopenvelope,
3278
4011
  graphqlrequestof,
4012
+ headerfilterof,
3279
4013
  heldkeysreport,
3280
4014
  hostpattern,
3281
4015
  htmlqueriesof,
@@ -3284,54 +4018,81 @@ export {
3284
4018
  imagematches,
3285
4019
  imagenames,
3286
4020
  isformkind,
4021
+ isnetwatchkind,
4022
+ issocketkind,
3287
4023
  iswatchkind,
3288
4024
  jsonpathrulesof,
3289
4025
  lapseframes,
3290
4026
  lapseplanof,
3291
4027
  layoutreport,
3292
4028
  mapresponse,
4029
+ matchmessage,
3293
4030
  mediaentries,
3294
4031
  mediakinds,
3295
4032
  mediareport,
4033
+ messagefilterof,
3296
4034
  navstateresponse,
3297
4035
  netlogreport,
4036
+ netwatchkinds,
4037
+ newchannel,
4038
+ newexchange,
3298
4039
  newrecording,
3299
4040
  normalizeendpoint,
3300
4041
  observationmodeof,
3301
4042
  observationresponse,
4043
+ openchannel,
3302
4044
  outcomeresponse,
4045
+ pairexchange,
3303
4046
  pairstates,
3304
4047
  parsehtmlbody,
3305
4048
  parseproposal,
4049
+ parsessetext,
3306
4050
  passwordconsentgranted,
4051
+ payloadshapeof,
3307
4052
  payloadvalid,
3308
4053
  payloadwithdefaults,
3309
4054
  pdfoptionsof,
3310
4055
  pdfpagesize,
3311
4056
  pdfsegments,
3312
4057
  pdftextlayout,
4058
+ pollcursorof,
4059
+ polldecision,
4060
+ pollurl,
4061
+ privatemime,
3313
4062
  profilegrantgranted,
3314
4063
  protocolversion,
3315
4064
  provenancereport,
4065
+ publishmessage,
3316
4066
  quarantinereport,
3317
4067
  randomid,
4068
+ rankapis,
3318
4069
  readpath,
3319
4070
  readstream,
4071
+ receivemessage,
4072
+ reconnectwaits,
3320
4073
  recordingoptionsof,
3321
4074
  regionsteps,
4075
+ replayurl,
3322
4076
  requestbody,
3323
4077
  resolutionverdict,
4078
+ resolvedrisk,
4079
+ resourcefacts,
3324
4080
  safetyresponse,
3325
4081
  scaledrect,
3326
4082
  seamweights,
3327
4083
  selectorresponse,
3328
4084
  sendfetch,
4085
+ sequenceintegrity,
3329
4086
  sessionmemory,
3330
4087
  signalsreport,
4088
+ socketgate,
4089
+ socketkinds,
4090
+ sserequestheaders,
3331
4091
  statusclassof,
3332
4092
  streamsummaries,
3333
4093
  streamwindowof,
3334
4094
  submitreviewgranted,
4095
+ subscriptionoptionsof,
3335
4096
  tabreportresponse,
3336
4097
  templateurl,
3337
4098
  thumbdirectiveof,
@@ -3344,6 +4105,7 @@ export {
3344
4105
  validatestep,
3345
4106
  validatetargetref,
3346
4107
  validatevaluegen,
4108
+ watchgate,
3347
4109
  wizardreport
3348
4110
  };
3349
4111
  //# sourceMappingURL=index.js.map