@wenathlan/extension 1.1.41 → 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/README.md +6 -4
- package/dist/httpclient.d.ts +158 -0
- package/dist/httpclient.d.ts.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1358 -10
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +73 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/netwatch.d.ts +92 -0
- package/dist/netwatch.d.ts.map +1 -0
- package/dist/policy.d.ts +40 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +41 -4
- package/dist/protocol.d.ts.map +1 -1
- package/dist/socketbus.d.ts +134 -0
- package/dist/socketbus.d.ts.map +1 -0
- package/dist/types.d.ts +311 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +2090 -16
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +29 -4
- package/extension/dist/pagebridge.js.map +2 -2
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +19 -3
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +281 -3
- package/extension/dist/sidepanel.js.map +3 -3
- package/extension/dist/style.css +2 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -196,6 +196,292 @@ function annotationplanof(input) {
|
|
|
196
196
|
return plan;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
// httpclient.ts
|
|
200
|
+
var httpkinds = ["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"];
|
|
201
|
+
var redirectstatuses = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
202
|
+
var bodilessmethods = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
203
|
+
function statusclassof(status) {
|
|
204
|
+
if (status >= 100 && status < 200) return "informational";
|
|
205
|
+
if (status >= 200 && status < 300) return "success";
|
|
206
|
+
if (status >= 300 && status < 400) return "redirect";
|
|
207
|
+
if (status >= 400 && status < 500) return "clienterror";
|
|
208
|
+
if (status >= 500 && status < 600) return "servererror";
|
|
209
|
+
return "unknown";
|
|
210
|
+
}
|
|
211
|
+
function templateurl(template, values) {
|
|
212
|
+
return template.replace(/\{([a-z0-9_]+)\}/gi, (whole, name) => values[name] === void 0 ? whole : String(values[name]));
|
|
213
|
+
}
|
|
214
|
+
function fetchrequestof(value) {
|
|
215
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
216
|
+
const options = value;
|
|
217
|
+
if (typeof options.url !== "string" || !options.url.trim()) return void 0;
|
|
218
|
+
const request = { url: options.url.trim() };
|
|
219
|
+
if (typeof options.method === "string" && options.method.trim()) request.method = options.method.trim().toUpperCase();
|
|
220
|
+
if (options.headers && typeof options.headers === "object" && !Array.isArray(options.headers)) {
|
|
221
|
+
const headers = {};
|
|
222
|
+
for (const [name, headervalue] of Object.entries(options.headers)) {
|
|
223
|
+
if (typeof headervalue === "string") headers[name] = headervalue;
|
|
224
|
+
}
|
|
225
|
+
request.headers = headers;
|
|
226
|
+
}
|
|
227
|
+
if (typeof options.body === "string") request.body = options.body;
|
|
228
|
+
if (options.mode === "cors" || options.mode === "no-cors" || options.mode === "same-origin") request.mode = options.mode;
|
|
229
|
+
return request;
|
|
230
|
+
}
|
|
231
|
+
function fetchoptionsof(value) {
|
|
232
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
233
|
+
const options = value;
|
|
234
|
+
const normalized = {};
|
|
235
|
+
if (typeof options.timeout === "number" && Number.isFinite(options.timeout)) normalized.timeout = options.timeout;
|
|
236
|
+
if (typeof options.retries === "number" && Number.isFinite(options.retries)) normalized.retries = options.retries;
|
|
237
|
+
if (typeof options.backoff === "number" && Number.isFinite(options.backoff)) normalized.backoff = options.backoff;
|
|
238
|
+
if (typeof options.follow === "number" && Number.isFinite(options.follow)) normalized.follow = options.follow;
|
|
239
|
+
return normalized;
|
|
240
|
+
}
|
|
241
|
+
function streamwindowof(value) {
|
|
242
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
243
|
+
const options = value;
|
|
244
|
+
const window = {};
|
|
245
|
+
if (typeof options.budget === "number" && Number.isFinite(options.budget)) window.budget = options.budget;
|
|
246
|
+
return window;
|
|
247
|
+
}
|
|
248
|
+
function jsonpathrulesof(value) {
|
|
249
|
+
if (!Array.isArray(value)) return [];
|
|
250
|
+
const rules = [];
|
|
251
|
+
for (const item of value) {
|
|
252
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
253
|
+
const entry = item;
|
|
254
|
+
if (typeof entry.name !== "string" || !entry.name.trim()) continue;
|
|
255
|
+
if (typeof entry.path !== "string" || !entry.path.trim()) continue;
|
|
256
|
+
const rule = { name: entry.name.trim(), path: entry.path.trim() };
|
|
257
|
+
if (entry.kind === "text" || entry.kind === "number" || entry.kind === "boolean" || entry.kind === "json") rule.kind = entry.kind;
|
|
258
|
+
if (entry.default !== void 0) rule.default = entry.default;
|
|
259
|
+
rules.push(rule);
|
|
260
|
+
}
|
|
261
|
+
return rules;
|
|
262
|
+
}
|
|
263
|
+
function htmlqueriesof(value) {
|
|
264
|
+
if (!Array.isArray(value)) return [];
|
|
265
|
+
const queries = [];
|
|
266
|
+
for (const item of value) {
|
|
267
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
268
|
+
const entry = item;
|
|
269
|
+
if (typeof entry.selector !== "string" || !entry.selector.trim()) continue;
|
|
270
|
+
const query = { selector: entry.selector.trim() };
|
|
271
|
+
if (typeof entry.attribute === "string" && entry.attribute.trim()) query.attribute = entry.attribute.trim();
|
|
272
|
+
if (entry.multi === true) query.multi = true;
|
|
273
|
+
queries.push(query);
|
|
274
|
+
}
|
|
275
|
+
return queries;
|
|
276
|
+
}
|
|
277
|
+
function graphqlrequestof(value) {
|
|
278
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
279
|
+
const options = value;
|
|
280
|
+
if (typeof options.query !== "string" || !options.query.trim()) return void 0;
|
|
281
|
+
if (options.operationkind !== "query" && options.operationkind !== "mutation") return void 0;
|
|
282
|
+
const request = { query: options.query, operationkind: options.operationkind };
|
|
283
|
+
if (options.variables && typeof options.variables === "object" && !Array.isArray(options.variables)) request.variables = options.variables;
|
|
284
|
+
if (typeof options.operationname === "string" && options.operationname.trim()) request.operationname = options.operationname.trim();
|
|
285
|
+
return request;
|
|
286
|
+
}
|
|
287
|
+
var realsleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, Math.max(0, milliseconds)));
|
|
288
|
+
async function sendfetch(input) {
|
|
289
|
+
const options = input.options ?? {};
|
|
290
|
+
const sleep = input.sleep ?? realsleep;
|
|
291
|
+
const now = input.now ?? Date.now;
|
|
292
|
+
const attempts = Math.max(1, Math.floor(options.retries ?? 0) + 1);
|
|
293
|
+
const backoff = options.backoff ?? 0;
|
|
294
|
+
const follow = options.follow ?? Number.POSITIVE_INFINITY;
|
|
295
|
+
let url = input.request.url;
|
|
296
|
+
let method = (input.request.method ?? "GET").toUpperCase();
|
|
297
|
+
let retries = 0;
|
|
298
|
+
let redirects = 0;
|
|
299
|
+
let lastreason = "";
|
|
300
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
301
|
+
let hops = 0;
|
|
302
|
+
const startedat = now();
|
|
303
|
+
let response;
|
|
304
|
+
try {
|
|
305
|
+
const init = { method, headers: { ...input.request.headers ?? {} }, ...input.request.body !== void 0 && !bodilessmethods.has(method) ? { body: input.request.body } : {}, ...input.request.mode !== void 0 ? { mode: input.request.mode } : {}, redirect: follow <= 0 ? "error" : "follow" };
|
|
306
|
+
const sent = input.transport(url, init);
|
|
307
|
+
if (options.timeout !== void 0 && Number.isFinite(options.timeout) && options.timeout >= 0) {
|
|
308
|
+
let timedout = false;
|
|
309
|
+
response = await Promise.race([sent, sleep(options.timeout).then(() => {
|
|
310
|
+
timedout = true;
|
|
311
|
+
return void 0;
|
|
312
|
+
})]).then((value) => value ?? (timedout ? (() => {
|
|
313
|
+
throw new Error(`The request timed out after ${options.timeout} milliseconds.`);
|
|
314
|
+
})() : value));
|
|
315
|
+
} else {
|
|
316
|
+
response = await sent;
|
|
317
|
+
}
|
|
318
|
+
while (response !== void 0 && redirectstatuses.has(response.status) && typeof response.location === "string" && response.location) {
|
|
319
|
+
hops += 1;
|
|
320
|
+
if (hops > follow) throw new Error(`The redirect chain exceeded the reviewed follow limit of ${follow}.`);
|
|
321
|
+
url = new URL(response.location, url).toString();
|
|
322
|
+
if (method === "POST" && [301, 302, 303].includes(response.status)) method = "GET";
|
|
323
|
+
response = await input.transport(url, { ...init, method });
|
|
324
|
+
}
|
|
325
|
+
redirects = hops;
|
|
326
|
+
} catch (error) {
|
|
327
|
+
lastreason = error instanceof Error ? error.message : String(error);
|
|
328
|
+
response = void 0;
|
|
329
|
+
}
|
|
330
|
+
if (response !== void 0) {
|
|
331
|
+
const body = response.body;
|
|
332
|
+
return { url, status: response.status, statusclass: statusclassof(response.status), headernames: Object.keys(response.headers), body, bytes: body.length, duration: now() - startedat, retries, redirects };
|
|
333
|
+
}
|
|
334
|
+
if (attempt < attempts) {
|
|
335
|
+
const wait = backoff * attempt;
|
|
336
|
+
if (wait > 0) await sleep(wait);
|
|
337
|
+
input.onretry?.(attempt, wait, lastreason);
|
|
338
|
+
retries = attempt;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
throw new Error(`The request failed after ${attempts} attempt${attempts === 1 ? "" : "s"} with ${retries} retr${retries === 1 ? "y" : "ies"}: ${lastreason}`);
|
|
342
|
+
}
|
|
343
|
+
async function readstream(input) {
|
|
344
|
+
const pull = typeof input.chunks === "function" ? input.chunks : /* @__PURE__ */ ((source) => {
|
|
345
|
+
let index = 0;
|
|
346
|
+
return async () => source[index++];
|
|
347
|
+
})(input.chunks);
|
|
348
|
+
let count = 0;
|
|
349
|
+
let total = 0;
|
|
350
|
+
for (; ; ) {
|
|
351
|
+
if (input.window.abort?.() === true) return { chunks: count, bytes: total, aborted: true, reason: "The reviewed abort flag stopped the stream." };
|
|
352
|
+
const chunk = await pull();
|
|
353
|
+
if (chunk === void 0) return { chunks: count, bytes: total, aborted: false };
|
|
354
|
+
const next = total + chunk.length;
|
|
355
|
+
if (input.window.budget !== void 0 && next > input.window.budget) return { chunks: count, bytes: total, aborted: true, reason: `The stream aborted at ${next} bytes past the reviewed byte budget of ${input.window.budget}.` };
|
|
356
|
+
total = next;
|
|
357
|
+
count += 1;
|
|
358
|
+
input.window.onchunk?.(chunk, total);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function pathstep(current, segment) {
|
|
362
|
+
if (Array.isArray(current) && /^\d+$/.test(segment)) return current[Number.parseInt(segment, 10)];
|
|
363
|
+
if (current && typeof current === "object" && !Array.isArray(current)) return current[segment];
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
function coerce(value, kind, fallback) {
|
|
367
|
+
if (value === void 0 || value === null) return { value: fallback, missing: true };
|
|
368
|
+
if (kind === "text") return { value: String(value), missing: false };
|
|
369
|
+
if (kind === "number") {
|
|
370
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
371
|
+
return Number.isFinite(numeric) ? { value: numeric, missing: false } : { value: fallback, missing: true };
|
|
372
|
+
}
|
|
373
|
+
if (kind === "boolean") return { value: value === true || value === "true", missing: false };
|
|
374
|
+
return { value, missing: false };
|
|
375
|
+
}
|
|
376
|
+
function readpath(parsed, rules) {
|
|
377
|
+
const fields = [];
|
|
378
|
+
for (const rule of rules) {
|
|
379
|
+
const kind = rule.kind ?? "text";
|
|
380
|
+
let current = parsed;
|
|
381
|
+
let missing = false;
|
|
382
|
+
for (const segment of rule.path.split(".")) {
|
|
383
|
+
const next = pathstep(current, segment);
|
|
384
|
+
if (next === void 0) {
|
|
385
|
+
missing = true;
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
current = next;
|
|
389
|
+
}
|
|
390
|
+
if (missing) fields.push({ name: rule.name, path: rule.path, kind, ...rule.default !== void 0 ? { value: rule.default } : {}, missing: true });
|
|
391
|
+
else {
|
|
392
|
+
const resolved = coerce(current, kind, rule.default);
|
|
393
|
+
fields.push({ name: rule.name, path: rule.path, kind, ...resolved.value !== void 0 ? { value: resolved.value } : {}, ...resolved.missing ? { missing: true } : {} });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return fields;
|
|
397
|
+
}
|
|
398
|
+
function defaultparse() {
|
|
399
|
+
const parser = globalThis.DOMParser;
|
|
400
|
+
if (!parser) throw new Error("parsehtml needs a domparser seam outside the page context.");
|
|
401
|
+
return (markup) => {
|
|
402
|
+
const document = new parser().parseFromString(markup, "text/html");
|
|
403
|
+
return { query: (selector) => Array.from(document.querySelectorAll(selector)).map((element) => ({ text: element.textContent ?? "", attributes: Object.fromEntries(Array.from(element.attributes).map((attribute) => [attribute.name, attribute.value])) })) };
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function parsehtmlbody(input) {
|
|
407
|
+
const parse = input.parse ?? defaultparse();
|
|
408
|
+
const document = parse(input.body);
|
|
409
|
+
return input.queries.map((query) => {
|
|
410
|
+
const matches = document.query(query.selector);
|
|
411
|
+
const chosen = query.multi === true ? matches : matches.slice(0, 1);
|
|
412
|
+
const values = chosen.map((match) => query.attribute !== void 0 ? match.attributes[query.attribute] ?? "" : match.text);
|
|
413
|
+
return { selector: query.selector, ...query.attribute !== void 0 ? { attribute: query.attribute } : {}, multi: query.multi === true, count: matches.length, values };
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
function payloadvalid(payload, schema) {
|
|
417
|
+
if (!schema) return { ok: false, errors: ["The typed endpoint call needs a reviewed payload schema before it runs."] };
|
|
418
|
+
const errors = [];
|
|
419
|
+
for (const field of schema.fields) {
|
|
420
|
+
const value = payload[field.name];
|
|
421
|
+
if (value === void 0 || value === null) {
|
|
422
|
+
if (field.required === true) errors.push(`The required field ${field.name} of kind ${field.kind} is missing.`);
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (field.kind === "string" && typeof value !== "string") errors.push(`The field ${field.name} must be a string.`);
|
|
426
|
+
if (field.kind === "number" && (typeof value !== "number" || !Number.isFinite(value))) errors.push(`The field ${field.name} must be a finite number.`);
|
|
427
|
+
if (field.kind === "boolean" && typeof value !== "boolean") errors.push(`The field ${field.name} must be a boolean.`);
|
|
428
|
+
}
|
|
429
|
+
return { ok: errors.length === 0, errors };
|
|
430
|
+
}
|
|
431
|
+
function payloadwithdefaults(payload, schema) {
|
|
432
|
+
if (!schema) return payload;
|
|
433
|
+
const merged = { ...payload };
|
|
434
|
+
for (const field of schema.fields) {
|
|
435
|
+
if (merged[field.name] === void 0 && field.default !== void 0) merged[field.name] = field.default;
|
|
436
|
+
}
|
|
437
|
+
return merged;
|
|
438
|
+
}
|
|
439
|
+
function errorsof(body) {
|
|
440
|
+
try {
|
|
441
|
+
const parsed = JSON.parse(body);
|
|
442
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return [body];
|
|
443
|
+
const record2 = parsed;
|
|
444
|
+
for (const key of ["errors", "messages", "error", "message"]) {
|
|
445
|
+
const value = record2[key];
|
|
446
|
+
if (Array.isArray(value)) return value.map((item) => typeof item === "string" ? item : item && typeof item === "object" && typeof item.message === "string" ? item.message : String(item));
|
|
447
|
+
if (typeof value === "string") return [value];
|
|
448
|
+
}
|
|
449
|
+
return [body];
|
|
450
|
+
} catch {
|
|
451
|
+
return [body];
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async function callrest(input) {
|
|
455
|
+
const check = payloadvalid(input.payload, input.endpoint.schema);
|
|
456
|
+
if (!check.ok) throw new Error(check.errors.join(" "));
|
|
457
|
+
const payload = payloadwithdefaults(input.payload, input.endpoint.schema);
|
|
458
|
+
const url = templateurl(input.endpoint.url, payload);
|
|
459
|
+
const method = input.endpoint.method.toUpperCase();
|
|
460
|
+
const request = { url, method, ...input.endpoint.headers !== void 0 ? { headers: input.endpoint.headers } : {}, ...bodilessmethods.has(method) ? {} : { body: JSON.stringify(payload) } };
|
|
461
|
+
const transport = await sendfetch({ request, ...input.options !== void 0 ? { options: input.options } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.onretry !== void 0 ? { onretry: input.onretry } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
|
|
462
|
+
const ok = input.success !== void 0 ? input.success.includes(transport.status) : transport.statusclass === "success";
|
|
463
|
+
return { transport, url, payload, ok, errors: ok ? [] : errorsof(transport.body) };
|
|
464
|
+
}
|
|
465
|
+
function graphqlopenvelope(request) {
|
|
466
|
+
return JSON.stringify({ query: request.query, ...request.variables !== void 0 ? { variables: request.variables } : {}, ...request.operationname !== void 0 ? { operationName: request.operationname } : {} });
|
|
467
|
+
}
|
|
468
|
+
function unwrapgraphql(value) {
|
|
469
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { errors: ["The graphql response is not a json object."] };
|
|
470
|
+
const record2 = value;
|
|
471
|
+
const errors = Array.isArray(record2.errors) ? record2.errors.map((item) => typeof item === "string" ? item : item && typeof item === "object" && typeof item.message === "string" ? item.message : String(item)) : [];
|
|
472
|
+
return { ...record2.data !== void 0 ? { data: record2.data } : {}, errors };
|
|
473
|
+
}
|
|
474
|
+
async function callgraphql(input) {
|
|
475
|
+
const request = { url: input.endpoint.url, method: "POST", ...input.endpoint.headers !== void 0 ? { headers: input.endpoint.headers } : {}, body: graphqlopenvelope(input.request) };
|
|
476
|
+
const transport = await sendfetch({ request, ...input.options !== void 0 ? { options: input.options } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.onretry !== void 0 ? { onretry: input.onretry } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
|
|
477
|
+
try {
|
|
478
|
+
const unwrapped = unwrapgraphql(JSON.parse(transport.body));
|
|
479
|
+
return { transport, ...unwrapped.data !== void 0 ? { data: unwrapped.data } : {}, errors: unwrapped.errors };
|
|
480
|
+
} catch {
|
|
481
|
+
return { transport, errors: [transport.body] };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
199
485
|
// media.ts
|
|
200
486
|
var mediakinds = ["capturepdf", "recordscreen", "captureaudio", "captureframe", "downloadimages", "shotcanvas", "probestream", "readmedia", "readassets", "timelapse", "convertimage", "makethumbs"];
|
|
201
487
|
var defaultpaperwidth = 8.5;
|
|
@@ -1313,6 +1599,160 @@ var sessionmemory = class {
|
|
|
1313
1599
|
async getrecordingconsents() {
|
|
1314
1600
|
return await this.adapter.get("recordingconsents") ?? [];
|
|
1315
1601
|
}
|
|
1602
|
+
/** Stores one outbound call record with its transport facts and body, replacing the previous record of that id; the user configured call retention window expires the oldest bodies while the metadata always survives. */
|
|
1603
|
+
async addcall(record2) {
|
|
1604
|
+
const records = await this.getcalls();
|
|
1605
|
+
const retention = (await this.getsettings())?.callretention;
|
|
1606
|
+
const combined = [record2, ...records.filter((item) => item.id !== record2.id)];
|
|
1607
|
+
const stored = retention === void 0 ? combined : combined.map((item, index) => index < retention ? item : expirecallbody(item));
|
|
1608
|
+
await this.adapter.set("calls", stored);
|
|
1609
|
+
}
|
|
1610
|
+
/** Returns every stored outbound call record, newest first. */
|
|
1611
|
+
async getcalls() {
|
|
1612
|
+
return await this.adapter.get("calls") ?? [];
|
|
1613
|
+
}
|
|
1614
|
+
/** Returns one outbound call record with its body by its id. */
|
|
1615
|
+
async getcall(id) {
|
|
1616
|
+
return (await this.getcalls()).find((item) => item.id === id);
|
|
1617
|
+
}
|
|
1618
|
+
/** Returns the outbound call records filtered by run and origin; an absent filter returns every call. */
|
|
1619
|
+
async listcalls(filter) {
|
|
1620
|
+
const records = await this.getcalls();
|
|
1621
|
+
return records.filter((item) => (filter.runid === void 0 || item.runid === filter.runid) && (filter.origin === void 0 || item.origin === filter.origin));
|
|
1622
|
+
}
|
|
1623
|
+
/** Stores one typed endpoint definition version, appending to the version history of that endpoint name. */
|
|
1624
|
+
async setendpoint(record2) {
|
|
1625
|
+
const records = await this.adapter.get("endpoints") ?? [];
|
|
1626
|
+
const prior = records.filter((item) => item.name === record2.name);
|
|
1627
|
+
const version = prior.length > 0 ? Math.max(...prior.map((item) => item.version)) + 1 : 1;
|
|
1628
|
+
await this.adapter.set("endpoints", [{ ...record2, version, at: record2.at }, ...records]);
|
|
1629
|
+
}
|
|
1630
|
+
/** Returns the newest endpointrecord definition of one name with its payload schema and version. */
|
|
1631
|
+
async getendpoint(name) {
|
|
1632
|
+
const records = await this.adapter.get("endpoints") ?? [];
|
|
1633
|
+
return records.find((item) => item.name === name);
|
|
1634
|
+
}
|
|
1635
|
+
/** Returns the newest definition of every typed endpoint name with its schema and version history. */
|
|
1636
|
+
async getendpoints() {
|
|
1637
|
+
const records = await this.adapter.get("endpoints") ?? [];
|
|
1638
|
+
const latest = /* @__PURE__ */ new Map();
|
|
1639
|
+
for (const record2 of records) if (!latest.has(record2.name)) latest.set(record2.name, record2);
|
|
1640
|
+
return [...latest.values()];
|
|
1641
|
+
}
|
|
1642
|
+
/** Stores one fetch consent decision per origin with its reviewed header names and values and its expiry window. */
|
|
1643
|
+
async setfetchconsent(consent) {
|
|
1644
|
+
const records = (await this.adapter.get("fetchconsents") ?? []).filter((item) => item.id !== consent.id);
|
|
1645
|
+
await this.adapter.set("fetchconsents", [consent, ...records]);
|
|
1646
|
+
}
|
|
1647
|
+
/** Returns every fetch consent decision with its origin, header names and expiry window, newest first. */
|
|
1648
|
+
async getfetchconsents() {
|
|
1649
|
+
return await this.adapter.get("fetchconsents") ?? [];
|
|
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]);
|
|
1655
|
+
}
|
|
1656
|
+
/** Returns every stored api key reference with its origin scope, header name and storage id; key material never loads here. */
|
|
1657
|
+
async getapikeys() {
|
|
1658
|
+
return await this.adapter.get("apikeys") ?? [];
|
|
1659
|
+
}
|
|
1660
|
+
/** Removes one api key reference and its stored secret together. */
|
|
1661
|
+
async removeapikey(name) {
|
|
1662
|
+
const records = await this.getapikeys();
|
|
1663
|
+
const ref = records.find((item) => item.name === name);
|
|
1664
|
+
if (ref) await this.adapter.set(ref.storageid, void 0);
|
|
1665
|
+
await this.adapter.set("apikeys", records.filter((item) => item.name !== name));
|
|
1666
|
+
}
|
|
1667
|
+
/** Stores one api key secret under its storage id; the value never appears in reports, outcomes or the audit trail. */
|
|
1668
|
+
async setsecret(storageid, value) {
|
|
1669
|
+
return this.adapter.set(storageid, value);
|
|
1670
|
+
}
|
|
1671
|
+
/** Loads one api key secret under its storage id for the executor only. */
|
|
1672
|
+
async getsecret(storageid) {
|
|
1673
|
+
return this.adapter.get(storageid);
|
|
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
|
+
}
|
|
1316
1756
|
};
|
|
1317
1757
|
function mediakindof(record2) {
|
|
1318
1758
|
if ("pages" in record2) return "pdf";
|
|
@@ -1342,14 +1782,428 @@ function expirecapturebytes(record2) {
|
|
|
1342
1782
|
void bytes;
|
|
1343
1783
|
return { ...metadata, bytesexpired: true };
|
|
1344
1784
|
}
|
|
1785
|
+
function expirecallbody(record2) {
|
|
1786
|
+
const { body, ...metadata } = record2;
|
|
1787
|
+
void body;
|
|
1788
|
+
return { ...metadata, bodyexpired: true };
|
|
1789
|
+
}
|
|
1790
|
+
function expirebodybytes(record2) {
|
|
1791
|
+
const { body, ...metadata } = record2;
|
|
1792
|
+
void body;
|
|
1793
|
+
return { ...metadata, bodyexpired: true };
|
|
1794
|
+
}
|
|
1345
1795
|
function randomid() {
|
|
1346
1796
|
return crypto.randomUUID();
|
|
1347
1797
|
}
|
|
1348
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
|
+
|
|
1349
2203
|
// policy.ts
|
|
1350
|
-
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"]);
|
|
1351
|
-
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
|
|
1352
|
-
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"]);
|
|
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"]);
|
|
1353
2207
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
1354
2208
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
1355
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"]);
|
|
@@ -1361,6 +2215,10 @@ var exportactions = /* @__PURE__ */ new Set(["exportcsv", "exportjson", "exporte
|
|
|
1361
2215
|
var filesactions = /* @__PURE__ */ new Set(["batchdownload", "pausedownload", "resumedownload", "verifydownload", "interceptmime", "exportnetlog", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "namecaptures", "cleanupartifacts"]);
|
|
1362
2216
|
var captureactions = /* @__PURE__ */ new Set(["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"]);
|
|
1363
2217
|
var mediaactions = /* @__PURE__ */ new Set(["capturepdf", "recordscreen", "captureaudio", "captureframe", "downloadimages", "shotcanvas", "probestream", "readmedia", "readassets", "timelapse", "convertimage", "makethumbs"]);
|
|
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"]);
|
|
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"]);
|
|
1364
2222
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
1365
2223
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
1366
2224
|
var groupcolors = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
|
|
@@ -2065,9 +2923,329 @@ function validatetabsgrammar(step, options) {
|
|
|
2065
2923
|
function ismediakind(kind) {
|
|
2066
2924
|
return mediaactions.has(kind);
|
|
2067
2925
|
}
|
|
2926
|
+
function ishttpkind(kind) {
|
|
2927
|
+
return httpactions.has(kind);
|
|
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
|
+
}
|
|
2982
|
+
function origincheck(session, url) {
|
|
2983
|
+
let parsed;
|
|
2984
|
+
try {
|
|
2985
|
+
parsed = new URL(url);
|
|
2986
|
+
} catch {
|
|
2987
|
+
return { allowed: false, reason: "The outbound request needs a valid url before it can be reviewed." };
|
|
2988
|
+
}
|
|
2989
|
+
if (parsed.protocol !== "https:") return { allowed: false, reason: "Outbound requests use HTTPS urls only." };
|
|
2990
|
+
if (parsed.username || parsed.password) return { allowed: false, reason: "Endpoint credentials are not allowed in the url." };
|
|
2991
|
+
if (!origingranted(session, parsed.origin)) return { allowed: false, reason: `The outbound request to ${parsed.origin} stays outside the session origin grants.` };
|
|
2992
|
+
return { allowed: true };
|
|
2993
|
+
}
|
|
2994
|
+
function credentialheadername(name) {
|
|
2995
|
+
return credentialheaders.has(name.trim().toLowerCase());
|
|
2996
|
+
}
|
|
2997
|
+
function fetchconsentrefgranted(step) {
|
|
2998
|
+
let options = {};
|
|
2999
|
+
try {
|
|
3000
|
+
options = parseoptions(step);
|
|
3001
|
+
} catch {
|
|
3002
|
+
options = {};
|
|
3003
|
+
}
|
|
3004
|
+
const request = options.fetch;
|
|
3005
|
+
const headers = request && typeof request === "object" && !Array.isArray(request) ? request.headers : void 0;
|
|
3006
|
+
const names = headers && typeof headers === "object" && !Array.isArray(headers) ? Object.keys(headers) : [];
|
|
3007
|
+
if (names.length === 0) return { allowed: true };
|
|
3008
|
+
const empty = names.some((name) => !name.trim());
|
|
3009
|
+
if (empty) return { allowed: false, reason: "Header allowlists with empty names are refused." };
|
|
3010
|
+
const credential = names.find((name) => credentialheadername(name));
|
|
3011
|
+
if (credential !== void 0 && !isnonempty(options.consentref)) return { allowed: false, reason: `The credential bearing header ${credential} needs the explicit reviewed consent that names it before it is sent.` };
|
|
3012
|
+
if (!isnonempty(options.consentref)) return { allowed: false, reason: `The ${names.length} reviewed custom header${names.length === 1 ? "" : "s"} need a reviewed consent ref in options before any send.` };
|
|
3013
|
+
return { allowed: true };
|
|
3014
|
+
}
|
|
3015
|
+
function fetchbudgetallowed(timeout, retries, backoff, wait) {
|
|
3016
|
+
for (const [label, value] of [["timeout", timeout], ["retries", retries], ["backoff", backoff]]) {
|
|
3017
|
+
if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < 0)) return { allowed: false, reason: `The reviewed fetch ${label} must be zero or a positive number with no code ceiling.` };
|
|
3018
|
+
}
|
|
3019
|
+
if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The reviewed fetch wait budget must be zero or a positive number of milliseconds." };
|
|
3020
|
+
if (wait === void 0 || timeout === void 0) return { allowed: true };
|
|
3021
|
+
const attempts = Math.max(1, Math.floor(retries ?? 0) + 1);
|
|
3022
|
+
const waits = (backoff ?? 0) * (attempts * (attempts - 1)) / 2;
|
|
3023
|
+
const worstcase = timeout * attempts + waits;
|
|
3024
|
+
if (worstcase > wait) return { allowed: false, reason: `The fetch worst case of ${worstcase} milliseconds exceeds the reviewed wait budget of ${wait} milliseconds; review a wider budget or fewer retries.` };
|
|
3025
|
+
return { allowed: true };
|
|
3026
|
+
}
|
|
3027
|
+
function outboundtarget(step) {
|
|
3028
|
+
let options = {};
|
|
3029
|
+
try {
|
|
3030
|
+
options = parseoptions(step);
|
|
3031
|
+
} catch {
|
|
3032
|
+
options = {};
|
|
3033
|
+
}
|
|
3034
|
+
const request = options.fetch;
|
|
3035
|
+
if (request && typeof request === "object" && !Array.isArray(request)) {
|
|
3036
|
+
const url = request.url;
|
|
3037
|
+
if (typeof url === "string" && url.trim()) return url.trim();
|
|
3038
|
+
}
|
|
3039
|
+
return void 0;
|
|
3040
|
+
}
|
|
3041
|
+
function validpath(path) {
|
|
3042
|
+
return path.split(".").every((segment) => /^[A-Za-z0-9_-]+$/.test(segment));
|
|
3043
|
+
}
|
|
3044
|
+
function validatehttpgrammar(step, options) {
|
|
3045
|
+
const kind = step.kind;
|
|
3046
|
+
if (kind === "fetchurl") {
|
|
3047
|
+
const request = options.fetch;
|
|
3048
|
+
if (!request || typeof request !== "object" || Array.isArray(request)) return { allowed: false, reason: "A reviewed fetch request with a url is required in options.fetch." };
|
|
3049
|
+
const fetchrequest = request;
|
|
3050
|
+
if (typeof fetchrequest.url !== "string" || !fetchrequest.url.trim()) return { allowed: false, reason: "The reviewed fetch request needs a non-empty url." };
|
|
3051
|
+
if (fetchrequest.method !== void 0 && (typeof fetchrequest.method !== "string" || !["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"].includes(fetchrequest.method.trim().toUpperCase()))) return { allowed: false, reason: "The reviewed fetch method must be a known HTTP verb." };
|
|
3052
|
+
if (fetchrequest.headers !== void 0) {
|
|
3053
|
+
if (!fetchrequest.headers || typeof fetchrequest.headers !== "object" || Array.isArray(fetchrequest.headers)) return { allowed: false, reason: "The reviewed header allowlist must be an object of custom headers." };
|
|
3054
|
+
for (const name of Object.keys(fetchrequest.headers)) {
|
|
3055
|
+
if (!name.trim()) return { allowed: false, reason: "Header allowlists with empty names are refused." };
|
|
3056
|
+
if (typeof fetchrequest.headers[name] !== "string") return { allowed: false, reason: `The reviewed header ${name} needs a string value.` };
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
if (fetchrequest.body !== void 0 && typeof fetchrequest.body !== "string") return { allowed: false, reason: "The reviewed fetch body must be a string." };
|
|
3060
|
+
if (fetchrequest.mode !== void 0 && fetchrequest.mode !== "cors" && fetchrequest.mode !== "no-cors" && fetchrequest.mode !== "same-origin") return { allowed: false, reason: "The reviewed fetch mode must be cors, no-cors or same-origin." };
|
|
3061
|
+
const consentgate = fetchconsentrefgranted(step);
|
|
3062
|
+
if (!consentgate.allowed) return consentgate;
|
|
3063
|
+
const policycheck = validatefetchoptions(options.fetchoptions);
|
|
3064
|
+
if (!policycheck.allowed) return policycheck;
|
|
3065
|
+
const fetchpolicy = fetchoptionsvalues(options.fetchoptions);
|
|
3066
|
+
const budget = fetchbudgetallowed(fetchpolicy.timeout, fetchpolicy.retries, fetchpolicy.backoff, fetchnumeric(options, "wait"));
|
|
3067
|
+
if (!budget.allowed) return budget;
|
|
3068
|
+
if (options.stream !== void 0) {
|
|
3069
|
+
if (!options.stream || typeof options.stream !== "object" || Array.isArray(options.stream)) return { allowed: false, reason: "The reviewed stream window must be an object with an optional byte budget." };
|
|
3070
|
+
const streambudget = options.stream.budget;
|
|
3071
|
+
if (streambudget !== void 0 && (typeof streambudget !== "number" || !Number.isFinite(streambudget) || streambudget < 0)) return { allowed: false, reason: "The reviewed stream byte budget must be zero or a positive number of bytes with no code ceiling." };
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
if (kind === "parsejson") {
|
|
3075
|
+
if (!isnonempty(options.call)) return { allowed: false, reason: "A reviewed stored call id is required in options.call before the body parses." };
|
|
3076
|
+
const fields = options.fields;
|
|
3077
|
+
if (!Array.isArray(fields) || fields.length === 0) return { allowed: false, reason: "A reviewed non-empty list of json path rules is required in options.fields." };
|
|
3078
|
+
for (const item of fields) {
|
|
3079
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every json path rule must be an object." };
|
|
3080
|
+
const rule = item;
|
|
3081
|
+
if (!isnonempty(rule.name)) return { allowed: false, reason: "Every json path rule needs a non-empty field name." };
|
|
3082
|
+
if (typeof rule.path !== "string" || !rule.path.trim() || !validpath(rule.path.trim())) return { allowed: false, reason: `The json path of ${rule.name} must be a dotted path of non-empty segments.` };
|
|
3083
|
+
if (rule.kind !== void 0 && rule.kind !== "text" && rule.kind !== "number" && rule.kind !== "boolean" && rule.kind !== "json") return { allowed: false, reason: `The json path kind of ${rule.name} must be text, number, boolean or json.` };
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
if (kind === "parsehtml") {
|
|
3087
|
+
if (!isnonempty(options.call)) return { allowed: false, reason: "A reviewed stored call id is required in options.call before the markup parses." };
|
|
3088
|
+
const queries = options.queries;
|
|
3089
|
+
if (!Array.isArray(queries) || queries.length === 0) return { allowed: false, reason: "A reviewed non-empty list of html queries is required in options.queries." };
|
|
3090
|
+
for (const item of queries) {
|
|
3091
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every html query must be an object." };
|
|
3092
|
+
const query = item;
|
|
3093
|
+
if (!isnonempty(query.selector)) return { allowed: false, reason: "Every html query needs a selector from the reviewed selector grammar." };
|
|
3094
|
+
if (query.attribute !== void 0 && !isnonempty(query.attribute)) return { allowed: false, reason: "The reviewed html query attribute must be a non-empty attribute name." };
|
|
3095
|
+
if (query.multi !== void 0 && typeof query.multi !== "boolean") return { allowed: false, reason: "The reviewed html query multi flag must be a boolean." };
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
if (kind === "callrest" || kind === "callgraphql") {
|
|
3099
|
+
if (!isnonempty(options.endpoint)) return { allowed: false, reason: "A reviewed typed endpoint name is required in options.endpoint." };
|
|
3100
|
+
if (kind === "callrest") {
|
|
3101
|
+
if (options.payload !== void 0 && (!options.payload || typeof options.payload !== "object" || Array.isArray(options.payload))) return { allowed: false, reason: "The reviewed rest payload must be an object of reviewed values." };
|
|
3102
|
+
if (options.method !== void 0 && (typeof options.method !== "string" || !["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"].includes(options.method.trim().toUpperCase()))) return { allowed: false, reason: "The reviewed endpoint method override must be a known HTTP verb." };
|
|
3103
|
+
if (options.success !== void 0 && (!Array.isArray(options.success) || !options.success.every((code) => typeof code === "number" && Number.isInteger(code)))) return { allowed: false, reason: "The reviewed success status list must be a list of integer status codes." };
|
|
3104
|
+
}
|
|
3105
|
+
if (kind === "callgraphql") {
|
|
3106
|
+
const request = options.graphql;
|
|
3107
|
+
if (!request || typeof request !== "object" || Array.isArray(request)) return { allowed: false, reason: "A reviewed graphql request with an operation is required in options.graphql." };
|
|
3108
|
+
const graphql = request;
|
|
3109
|
+
if (typeof graphql.query !== "string" || !graphql.query.trim()) return { allowed: false, reason: "The reviewed graphql operation text must be a non-empty string." };
|
|
3110
|
+
if (graphql.operationkind !== "query" && graphql.operationkind !== "mutation") return { allowed: false, reason: "The reviewed graphql operation kind must be query or mutation; unknown operation kinds are refused." };
|
|
3111
|
+
if (graphql.variables !== void 0 && (!graphql.variables || typeof graphql.variables !== "object" || Array.isArray(graphql.variables))) return { allowed: false, reason: "The reviewed graphql variables must be an object of reviewed values." };
|
|
3112
|
+
if (graphql.operationname !== void 0 && !isnonempty(graphql.operationname)) return { allowed: false, reason: "The reviewed graphql operation name must be a non-empty string." };
|
|
3113
|
+
}
|
|
3114
|
+
if (options.apikeys !== void 0 && (!Array.isArray(options.apikeys) || !options.apikeys.every((name) => isnonempty(name)))) return { allowed: false, reason: "The reviewed api key reference list must be a list of non-empty stored names." };
|
|
3115
|
+
const policycheck = validatefetchoptions(options.fetchoptions);
|
|
3116
|
+
if (!policycheck.allowed) return policycheck;
|
|
3117
|
+
const fetchpolicy = fetchoptionsvalues(options.fetchoptions);
|
|
3118
|
+
const budget = fetchbudgetallowed(fetchpolicy.timeout, fetchpolicy.retries, fetchpolicy.backoff, fetchnumeric(options, "wait"));
|
|
3119
|
+
if (!budget.allowed) return budget;
|
|
3120
|
+
}
|
|
3121
|
+
return { allowed: true };
|
|
3122
|
+
}
|
|
3123
|
+
function validatefetchoptions(value) {
|
|
3124
|
+
if (value === void 0) return { allowed: true };
|
|
3125
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "The reviewed fetch options must be an object with timeout, retries, backoff and follow." };
|
|
3126
|
+
const options = value;
|
|
3127
|
+
for (const key of ["timeout", "backoff"]) {
|
|
3128
|
+
if (options[key] !== void 0 && (typeof options[key] !== "number" || !Number.isFinite(options[key]) || options[key] < 0)) return { allowed: false, reason: `The reviewed fetch ${key} must be zero or a positive number with no code ceiling.` };
|
|
3129
|
+
}
|
|
3130
|
+
for (const key of ["retries", "follow"]) {
|
|
3131
|
+
if (options[key] !== void 0 && (typeof options[key] !== "number" || !Number.isInteger(options[key]) || options[key] < 0)) return { allowed: false, reason: `The reviewed fetch ${key} must be zero or a positive integer with no code ceiling.` };
|
|
3132
|
+
}
|
|
3133
|
+
return { allowed: true };
|
|
3134
|
+
}
|
|
3135
|
+
function fetchoptionsvalues(value) {
|
|
3136
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
3137
|
+
const options = value;
|
|
3138
|
+
return { timeout: fetchnumeric(options, "timeout"), retries: fetchnumeric(options, "retries"), backoff: fetchnumeric(options, "backoff") };
|
|
3139
|
+
}
|
|
3140
|
+
function fetchnumeric(options, key) {
|
|
3141
|
+
const value = options[key];
|
|
3142
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3143
|
+
}
|
|
2068
3144
|
function isrecordingkind(kind) {
|
|
2069
3145
|
return kind === "recordscreen" || kind === "captureaudio";
|
|
2070
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
|
+
}
|
|
2071
3249
|
function mediagate(session, tabid, origin, now) {
|
|
2072
3250
|
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the media capture." };
|
|
2073
3251
|
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture media." };
|
|
@@ -2421,6 +3599,18 @@ function validatestep(step, origin) {
|
|
|
2421
3599
|
const mediacheck = validatemediagrammar(step, options);
|
|
2422
3600
|
if (!mediacheck.allowed) return mediacheck;
|
|
2423
3601
|
}
|
|
3602
|
+
if (ishttpkind(step.kind)) {
|
|
3603
|
+
const httpcheck = validatehttpgrammar(step, options);
|
|
3604
|
+
if (!httpcheck.allowed) return httpcheck;
|
|
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
|
+
}
|
|
2424
3614
|
if (step.kind === "tabcreate") {
|
|
2425
3615
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
2426
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." };
|
|
@@ -2500,6 +3690,41 @@ function canexecute(input) {
|
|
|
2500
3690
|
const recordinggate = recordingconsentgranted(input.step);
|
|
2501
3691
|
if (!recordinggate.allowed) return recordinggate;
|
|
2502
3692
|
}
|
|
3693
|
+
if (ishttpkind(input.step.kind)) {
|
|
3694
|
+
const target = outboundtarget(input.step);
|
|
3695
|
+
if (target !== void 0) {
|
|
3696
|
+
const outboundgate = origincheck(input.session, target);
|
|
3697
|
+
if (!outboundgate.allowed) return outboundgate;
|
|
3698
|
+
}
|
|
3699
|
+
if (input.step.kind === "fetchurl" || input.step.kind === "callrest" || input.step.kind === "callgraphql") {
|
|
3700
|
+
const consentgate = fetchconsentrefgranted(input.step);
|
|
3701
|
+
if (!consentgate.allowed) return consentgate;
|
|
3702
|
+
}
|
|
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
|
+
}
|
|
2503
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") {
|
|
2504
3729
|
let options = {};
|
|
2505
3730
|
try {
|
|
@@ -2521,7 +3746,7 @@ function canexecute(input) {
|
|
|
2521
3746
|
}
|
|
2522
3747
|
|
|
2523
3748
|
// version.ts
|
|
2524
|
-
var packageversion = "1.1.
|
|
3749
|
+
var packageversion = "1.1.43";
|
|
2525
3750
|
|
|
2526
3751
|
// types.ts
|
|
2527
3752
|
var protocolversion = packageversion;
|
|
@@ -2535,12 +3760,17 @@ function text(value, field) {
|
|
|
2535
3760
|
if (typeof value !== "string" || !value.trim()) throw new Error(`${field} must be a non-empty string.`);
|
|
2536
3761
|
return value.trim();
|
|
2537
3762
|
}
|
|
2538
|
-
function parseproposal(value, origin) {
|
|
3763
|
+
function parseproposal(value, origin, grants) {
|
|
2539
3764
|
const root = record(value);
|
|
2540
3765
|
if (root.version !== protocolversion) throw new Error("Unsupported protocol version.");
|
|
3766
|
+
const covered = grants !== void 0 && grants.length > 0 ? grants : [origin];
|
|
2541
3767
|
const planinput = record(root.plan);
|
|
2542
3768
|
const stepsinput = planinput.steps;
|
|
2543
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;
|
|
2544
3774
|
const steps = stepsinput.map((input, index) => {
|
|
2545
3775
|
const candidate = record(input);
|
|
2546
3776
|
const kind = text(candidate.kind, `step ${index + 1} kind`);
|
|
@@ -2548,13 +3778,47 @@ function parseproposal(value, origin) {
|
|
|
2548
3778
|
id: typeof candidate.id === "string" ? candidate.id : crypto.randomUUID(),
|
|
2549
3779
|
kind,
|
|
2550
3780
|
summary: text(candidate.summary, `step ${index + 1} summary`),
|
|
2551
|
-
risk:
|
|
3781
|
+
risk: resolvedrisk(stepof(kind, candidate, index)),
|
|
2552
3782
|
...typeof candidate.target === "string" ? { target: candidate.target } : {},
|
|
2553
3783
|
...typeof candidate.value === "string" ? { value: candidate.value } : {},
|
|
2554
3784
|
...typeof candidate.options === "string" ? { options: candidate.options } : {}
|
|
2555
3785
|
};
|
|
2556
3786
|
const evaluation = validatestep(step, origin);
|
|
2557
3787
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
3788
|
+
const target = outboundtarget(step);
|
|
3789
|
+
if (target !== void 0) {
|
|
3790
|
+
const granted = covered.some((pattern) => {
|
|
3791
|
+
try {
|
|
3792
|
+
return new URL(target).origin === new URL(pattern).origin;
|
|
3793
|
+
} catch {
|
|
3794
|
+
return false;
|
|
3795
|
+
}
|
|
3796
|
+
});
|
|
3797
|
+
if (!granted) throw new Error(`The fetch request to ${target} targets an origin outside the grants.`);
|
|
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.`);
|
|
2558
3822
|
return step;
|
|
2559
3823
|
});
|
|
2560
3824
|
for (const step of steps) {
|
|
@@ -2567,8 +3831,6 @@ function parseproposal(value, origin) {
|
|
|
2567
3831
|
const review = submitreviewgranted(steps, step.id);
|
|
2568
3832
|
if (!review.allowed) throw new Error(review.reason);
|
|
2569
3833
|
}
|
|
2570
|
-
const createdat = Date.now();
|
|
2571
|
-
const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
|
|
2572
3834
|
const plan = {
|
|
2573
3835
|
id: typeof planinput.id === "string" ? planinput.id : crypto.randomUUID(),
|
|
2574
3836
|
objective: text(planinput.objective, "objective"),
|
|
@@ -2578,14 +3840,24 @@ function parseproposal(value, origin) {
|
|
|
2578
3840
|
expiresat,
|
|
2579
3841
|
state: "pending"
|
|
2580
3842
|
};
|
|
2581
|
-
if (plan.expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
|
|
2582
3843
|
return { version: protocolversion, plan };
|
|
2583
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
|
+
}
|
|
2584
3856
|
function requestbody(input) {
|
|
2585
3857
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
2586
3858
|
}
|
|
2587
3859
|
function outcomeresponse(input) {
|
|
2588
|
-
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 } : {} });
|
|
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 } : {} });
|
|
2589
3861
|
}
|
|
2590
3862
|
function mapresponse(input) {
|
|
2591
3863
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -2668,15 +3940,35 @@ function capturereport(input) {
|
|
|
2668
3940
|
function mediareport(input) {
|
|
2669
3941
|
return { version: protocolversion, records: input.records, images: input.images };
|
|
2670
3942
|
}
|
|
3943
|
+
function callsreport(input) {
|
|
3944
|
+
const calls = input.calls.map((call) => {
|
|
3945
|
+
const { body, ...metadata } = call;
|
|
3946
|
+
void body;
|
|
3947
|
+
return metadata;
|
|
3948
|
+
});
|
|
3949
|
+
return { version: protocolversion, calls };
|
|
3950
|
+
}
|
|
3951
|
+
function exchangesreport(input) {
|
|
3952
|
+
return { version: protocolversion, exchanges: input.exchanges, channels: input.channels, subscriptions: input.subscriptions, apimap: input.apimap };
|
|
3953
|
+
}
|
|
2671
3954
|
export {
|
|
2672
3955
|
annotationplanof,
|
|
3956
|
+
apientries,
|
|
3957
|
+
apireplayspecof,
|
|
2673
3958
|
assetentries,
|
|
2674
3959
|
blendrows,
|
|
3960
|
+
bodyfilterof,
|
|
3961
|
+
bodymatches,
|
|
2675
3962
|
buildname,
|
|
2676
3963
|
buildpdf,
|
|
2677
3964
|
buildsheet,
|
|
2678
3965
|
buildstitchplan,
|
|
3966
|
+
callgraphql,
|
|
3967
|
+
callrest,
|
|
3968
|
+
callsreport,
|
|
2679
3969
|
canexecute,
|
|
3970
|
+
capturebody,
|
|
3971
|
+
capturedheaders,
|
|
2680
3972
|
captureelement,
|
|
2681
3973
|
captureformats,
|
|
2682
3974
|
capturekinds,
|
|
@@ -2687,9 +3979,15 @@ export {
|
|
|
2687
3979
|
capturestitched,
|
|
2688
3980
|
capturetargets,
|
|
2689
3981
|
capturevisible,
|
|
3982
|
+
channeloptionsof,
|
|
3983
|
+
channelorigin,
|
|
3984
|
+
closechannel,
|
|
3985
|
+
collectmessages,
|
|
2690
3986
|
convertdirectiveof,
|
|
3987
|
+
correlationid,
|
|
2691
3988
|
croprect,
|
|
2692
3989
|
crossesviewport,
|
|
3990
|
+
cursorfrom,
|
|
2693
3991
|
datasetresponse,
|
|
2694
3992
|
dedupeimages,
|
|
2695
3993
|
actionrisk as deriveactionrisk,
|
|
@@ -2697,67 +3995,117 @@ export {
|
|
|
2697
3995
|
downloadreport,
|
|
2698
3996
|
errorreportresponse,
|
|
2699
3997
|
eventresponse,
|
|
3998
|
+
exchangesreport,
|
|
2700
3999
|
extractionreport,
|
|
4000
|
+
extractvalues,
|
|
4001
|
+
failureclass,
|
|
4002
|
+
fetchoptionsof,
|
|
4003
|
+
fetchrequestof,
|
|
4004
|
+
filterexchanges,
|
|
2701
4005
|
finishrecording,
|
|
2702
4006
|
fixedheadermatch,
|
|
2703
4007
|
formreportresponse,
|
|
2704
4008
|
frameinterval,
|
|
2705
4009
|
generatedvalueallowed,
|
|
4010
|
+
graphqlopenvelope,
|
|
4011
|
+
graphqlrequestof,
|
|
4012
|
+
headerfilterof,
|
|
2706
4013
|
heldkeysreport,
|
|
2707
4014
|
hostpattern,
|
|
4015
|
+
htmlqueriesof,
|
|
4016
|
+
httpkinds,
|
|
2708
4017
|
imagefilterof,
|
|
2709
4018
|
imagematches,
|
|
2710
4019
|
imagenames,
|
|
2711
4020
|
isformkind,
|
|
4021
|
+
isnetwatchkind,
|
|
4022
|
+
issocketkind,
|
|
2712
4023
|
iswatchkind,
|
|
4024
|
+
jsonpathrulesof,
|
|
2713
4025
|
lapseframes,
|
|
2714
4026
|
lapseplanof,
|
|
2715
4027
|
layoutreport,
|
|
2716
4028
|
mapresponse,
|
|
4029
|
+
matchmessage,
|
|
2717
4030
|
mediaentries,
|
|
2718
4031
|
mediakinds,
|
|
2719
4032
|
mediareport,
|
|
4033
|
+
messagefilterof,
|
|
2720
4034
|
navstateresponse,
|
|
2721
4035
|
netlogreport,
|
|
4036
|
+
netwatchkinds,
|
|
4037
|
+
newchannel,
|
|
4038
|
+
newexchange,
|
|
2722
4039
|
newrecording,
|
|
2723
4040
|
normalizeendpoint,
|
|
2724
4041
|
observationmodeof,
|
|
2725
4042
|
observationresponse,
|
|
4043
|
+
openchannel,
|
|
2726
4044
|
outcomeresponse,
|
|
4045
|
+
pairexchange,
|
|
2727
4046
|
pairstates,
|
|
4047
|
+
parsehtmlbody,
|
|
2728
4048
|
parseproposal,
|
|
4049
|
+
parsessetext,
|
|
2729
4050
|
passwordconsentgranted,
|
|
4051
|
+
payloadshapeof,
|
|
4052
|
+
payloadvalid,
|
|
4053
|
+
payloadwithdefaults,
|
|
2730
4054
|
pdfoptionsof,
|
|
2731
4055
|
pdfpagesize,
|
|
2732
4056
|
pdfsegments,
|
|
2733
4057
|
pdftextlayout,
|
|
4058
|
+
pollcursorof,
|
|
4059
|
+
polldecision,
|
|
4060
|
+
pollurl,
|
|
4061
|
+
privatemime,
|
|
2734
4062
|
profilegrantgranted,
|
|
2735
4063
|
protocolversion,
|
|
2736
4064
|
provenancereport,
|
|
4065
|
+
publishmessage,
|
|
2737
4066
|
quarantinereport,
|
|
2738
4067
|
randomid,
|
|
4068
|
+
rankapis,
|
|
4069
|
+
readpath,
|
|
4070
|
+
readstream,
|
|
4071
|
+
receivemessage,
|
|
4072
|
+
reconnectwaits,
|
|
2739
4073
|
recordingoptionsof,
|
|
2740
4074
|
regionsteps,
|
|
4075
|
+
replayurl,
|
|
2741
4076
|
requestbody,
|
|
2742
4077
|
resolutionverdict,
|
|
4078
|
+
resolvedrisk,
|
|
4079
|
+
resourcefacts,
|
|
2743
4080
|
safetyresponse,
|
|
2744
4081
|
scaledrect,
|
|
2745
4082
|
seamweights,
|
|
2746
4083
|
selectorresponse,
|
|
4084
|
+
sendfetch,
|
|
4085
|
+
sequenceintegrity,
|
|
2747
4086
|
sessionmemory,
|
|
2748
4087
|
signalsreport,
|
|
4088
|
+
socketgate,
|
|
4089
|
+
socketkinds,
|
|
4090
|
+
sserequestheaders,
|
|
4091
|
+
statusclassof,
|
|
2749
4092
|
streamsummaries,
|
|
4093
|
+
streamwindowof,
|
|
2750
4094
|
submitreviewgranted,
|
|
4095
|
+
subscriptionoptionsof,
|
|
2751
4096
|
tabreportresponse,
|
|
4097
|
+
templateurl,
|
|
2752
4098
|
thumbdirectiveof,
|
|
2753
4099
|
thumbgeometry,
|
|
2754
4100
|
trailreport,
|
|
2755
4101
|
transformgrammar,
|
|
4102
|
+
unwrapgraphql,
|
|
2756
4103
|
validatefieldmatch,
|
|
2757
4104
|
validateformrecord,
|
|
2758
4105
|
validatestep,
|
|
2759
4106
|
validatetargetref,
|
|
2760
4107
|
validatevaluegen,
|
|
4108
|
+
watchgate,
|
|
2761
4109
|
wizardreport
|
|
2762
4110
|
};
|
|
2763
4111
|
//# sourceMappingURL=index.js.map
|