@wenathlan/extension 1.1.40 → 1.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -196,6 +196,594 @@ 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
+
485
+ // media.ts
486
+ var mediakinds = ["capturepdf", "recordscreen", "captureaudio", "captureframe", "downloadimages", "shotcanvas", "probestream", "readmedia", "readassets", "timelapse", "convertimage", "makethumbs"];
487
+ var defaultpaperwidth = 8.5;
488
+ var defaultpaperheight = 11;
489
+ var defaultmargins = { top: 0.4, right: 0.4, bottom: 0.4, left: 0.4 };
490
+ var pdfpointsperinch = 72;
491
+ var basefontsize = 11;
492
+ function pdfoptionsof(value) {
493
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
494
+ const options = value;
495
+ const normalized = {};
496
+ if (typeof options.paperwidth === "number" && Number.isFinite(options.paperwidth)) normalized.paperwidth = options.paperwidth;
497
+ if (typeof options.paperheight === "number" && Number.isFinite(options.paperheight)) normalized.paperheight = options.paperheight;
498
+ if (options.margins && typeof options.margins === "object" && !Array.isArray(options.margins)) {
499
+ const margins = options.margins;
500
+ const top = typeof margins.top === "number" ? margins.top : defaultmargins.top;
501
+ const right = typeof margins.right === "number" ? margins.right : defaultmargins.right;
502
+ const bottom = typeof margins.bottom === "number" ? margins.bottom : defaultmargins.bottom;
503
+ const left = typeof margins.left === "number" ? margins.left : defaultmargins.left;
504
+ normalized.margins = { top, right, bottom, left };
505
+ }
506
+ if (typeof options.scale === "number" && Number.isFinite(options.scale)) normalized.scale = options.scale;
507
+ if (typeof options.landscape === "boolean") normalized.landscape = options.landscape;
508
+ if (typeof options.paginate === "boolean") normalized.paginate = options.paginate;
509
+ return normalized;
510
+ }
511
+ function pdfpagesize(options) {
512
+ const width = (options.paperwidth ?? defaultpaperwidth) * pdfpointsperinch;
513
+ const height = (options.paperheight ?? defaultpaperheight) * pdfpointsperinch;
514
+ return options.landscape === true ? { width: height, height: width } : { width, height };
515
+ }
516
+ function pdfmargins(options) {
517
+ const margins = options.margins ?? defaultmargins;
518
+ return { top: margins.top * pdfpointsperinch, right: margins.right * pdfpointsperinch, bottom: margins.bottom * pdfpointsperinch, left: margins.left * pdfpointsperinch };
519
+ }
520
+ function pdffontsize(options) {
521
+ return basefontsize * (options.scale ?? 1);
522
+ }
523
+ function pdftextlayout(text2, options) {
524
+ const size = pdfpagesize(options);
525
+ const margins = pdfmargins(options);
526
+ const fontsize = pdffontsize(options);
527
+ const leading = fontsize * 1.35;
528
+ const linesperpage = Math.max(1, Math.floor((size.height - margins.top - margins.bottom) / leading));
529
+ const columns = Math.max(1, Math.floor((size.width - margins.left - margins.right) / (fontsize * 0.5)));
530
+ const wrapped = [];
531
+ for (const paragraph of text2.split(/\r?\n/)) {
532
+ let line = "";
533
+ for (const word of paragraph.split(/\s+/).filter(Boolean)) {
534
+ const candidate = line ? `${line} ${word}` : word;
535
+ if (candidate.length <= columns) {
536
+ line = candidate;
537
+ continue;
538
+ }
539
+ if (line) wrapped.push(line);
540
+ if (word.length <= columns) {
541
+ line = word;
542
+ continue;
543
+ }
544
+ for (let index = 0; index < word.length; index += columns) wrapped.push(word.slice(index, index + columns));
545
+ line = "";
546
+ }
547
+ wrapped.push(line);
548
+ if (wrapped.length >= linesperpage) break;
549
+ }
550
+ return wrapped.slice(0, linesperpage);
551
+ }
552
+ function pdfsegments(scrollheight, viewportheight, breaks) {
553
+ if (scrollheight <= 0) return [];
554
+ const step = viewportheight > 0 ? viewportheight : scrollheight;
555
+ const cuts = [0, ...breaks.filter((top) => Number.isFinite(top) && top > 0 && top < scrollheight).map((top) => Math.round(top))].filter((top, index2, list) => list.indexOf(top) === index2).sort((left, right) => left - right);
556
+ const segments = [];
557
+ let index = 0;
558
+ let cursor = 0;
559
+ while (cursor < scrollheight) {
560
+ while (index < cuts.length && (cuts[index] ?? 0) <= cursor) index += 1;
561
+ const nextcut = index < cuts.length ? cuts[index] : void 0;
562
+ const next = nextcut !== void 0 ? Math.min(nextcut, scrollheight) : Math.min(cursor + step, scrollheight);
563
+ if (next <= cursor) break;
564
+ segments.push({ top: cursor, height: next - cursor });
565
+ cursor = next;
566
+ }
567
+ return segments.length > 0 ? segments : [{ top: 0, height: scrollheight }];
568
+ }
569
+ function pdfescape(text2) {
570
+ let escaped = "";
571
+ for (const character of text2) {
572
+ const code = character.charCodeAt(0);
573
+ if (character === "(" || character === ")" || character === "\\") escaped += `\\${character}`;
574
+ else if (code >= 32 && code <= 255) escaped += character;
575
+ else escaped += "?";
576
+ }
577
+ return escaped;
578
+ }
579
+ function buildpdf(pages, options) {
580
+ const size = pdfpagesize(options);
581
+ const margins = pdfmargins(options);
582
+ const fontsize = pdffontsize(options);
583
+ const leading = fontsize * 1.35;
584
+ const laidout = (pages.length > 0 ? pages : [""]).map((text2) => pdftextlayout(text2, options));
585
+ const objects = [];
586
+ const kids = laidout.map((_, index) => `${4 + index * 2} 0 R`).join(" ");
587
+ objects.push(`<< /Type /Catalog /Pages 2 0 R >>`);
588
+ objects.push(`<< /Type /Pages /Kids [${kids}] /Count ${laidout.length} >>`);
589
+ objects.push(`<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>`);
590
+ for (let pageindex = 0; pageindex < laidout.length; pageindex += 1) {
591
+ const lines = laidout[pageindex] ?? [];
592
+ const operators = ["BT", `/F1 ${fontsize} Tf`, `${leading.toFixed(2)} TL`, `${margins.left.toFixed(2)} ${(size.height - margins.top - fontsize).toFixed(2)} Td`];
593
+ for (let lineindex = 0; lineindex < lines.length; lineindex += 1) {
594
+ if (lineindex > 0) operators.push("T*");
595
+ operators.push(`(${pdfescape(lines[lineindex] ?? "")}) Tj`);
596
+ }
597
+ operators.push("ET");
598
+ const content = operators.join("\n");
599
+ objects.push(`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${size.width.toFixed(2)} ${size.height.toFixed(2)}] /Resources << /Font << /F1 3 0 R >> >> /Contents ${5 + pageindex * 2} 0 R >>`);
600
+ objects.push(`<< /Length ${content.length} >>
601
+ stream
602
+ ${content}
603
+ endstream`);
604
+ }
605
+ let document = "%PDF-1.4\n";
606
+ const offsets = [];
607
+ for (let index = 0; index < objects.length; index += 1) {
608
+ offsets.push(document.length);
609
+ document += `${index + 1} 0 obj
610
+ ${objects[index]}
611
+ endobj
612
+ `;
613
+ }
614
+ const xrefstart = document.length;
615
+ document += `xref
616
+ 0 ${objects.length + 1}
617
+ 0000000000 65535 f
618
+ `;
619
+ for (const offset of offsets) document += `${String(offset).padStart(10, "0")} 00000 n
620
+ `;
621
+ document += `trailer
622
+ << /Size ${objects.length + 1} /Root 1 0 R >>
623
+ startxref
624
+ ${xrefstart}
625
+ %%EOF
626
+ `;
627
+ return { document, bytes: document.length, pages: laidout.length, pagewidth: Math.round(size.width), pageheight: Math.round(size.height) };
628
+ }
629
+ function recordingoptionsof(value) {
630
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
631
+ const options = value;
632
+ const normalized = {};
633
+ if (options.scope === "tab" || options.scope === "run") normalized.scope = options.scope;
634
+ if (typeof options.fps === "number" && Number.isFinite(options.fps)) normalized.fps = options.fps;
635
+ if (typeof options.bitrate === "number" && Number.isFinite(options.bitrate)) normalized.bitrate = options.bitrate;
636
+ if (typeof options.audio === "boolean") normalized.audio = options.audio;
637
+ return normalized;
638
+ }
639
+ function newrecording(input) {
640
+ return {
641
+ id: input.id,
642
+ runid: input.runid,
643
+ stepid: input.stepid,
644
+ tabid: input.tabid,
645
+ kind: input.kind,
646
+ scope: input.options.scope ?? "tab",
647
+ format: input.kind === "audio" ? "evidence" : "frames",
648
+ startedat: input.at,
649
+ at: input.at,
650
+ ...input.options.fps !== void 0 ? { fps: input.options.fps } : {},
651
+ ...input.options.bitrate !== void 0 ? { bitrate: input.options.bitrate } : {},
652
+ ...input.options.audio !== void 0 ? { audio: input.options.audio } : {},
653
+ frames: []
654
+ };
655
+ }
656
+ function finishrecording(record2, endat) {
657
+ return { ...record2, endedat: endat, duration: Math.max(0, endat - record2.startedat) };
658
+ }
659
+ function frameinterval(fps) {
660
+ if (!Number.isFinite(fps) || fps <= 0) return 1e3;
661
+ return Math.max(1, Math.round(1e3 / fps));
662
+ }
663
+ function imagefilterof(value) {
664
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
665
+ const options = value;
666
+ const normalized = {};
667
+ if (typeof options.selector === "string" && options.selector.trim()) normalized.selector = options.selector.trim();
668
+ if (typeof options.minwidth === "number" && Number.isFinite(options.minwidth)) normalized.minwidth = options.minwidth;
669
+ if (typeof options.minheight === "number" && Number.isFinite(options.minheight)) normalized.minheight = options.minheight;
670
+ if (Array.isArray(options.formats) && options.formats.every((item) => typeof item === "string" && item.trim())) normalized.formats = options.formats;
671
+ return normalized;
672
+ }
673
+ function imagematches(image, filter) {
674
+ if (filter.minwidth !== void 0 && image.width < filter.minwidth) return false;
675
+ if (filter.minheight !== void 0 && image.height < filter.minheight) return false;
676
+ if (filter.formats !== void 0 && filter.formats.length > 0) {
677
+ const mime = image.mime.toLowerCase();
678
+ const matches = filter.formats.some((format) => {
679
+ const wanted = format.toLowerCase().trim();
680
+ return mime === wanted || mime === `image/${wanted}` || mime.endsWith(`/${wanted}`);
681
+ });
682
+ if (!matches) return false;
683
+ }
684
+ return true;
685
+ }
686
+ function dedupeimages(images) {
687
+ const seen = /* @__PURE__ */ new Set();
688
+ const unique = [];
689
+ for (const image of images) {
690
+ if (seen.has(image.url)) continue;
691
+ seen.add(image.url);
692
+ unique.push(image);
693
+ }
694
+ return unique;
695
+ }
696
+ function imagenames(rule, run, step, count, extension) {
697
+ const names = [];
698
+ for (let index = 1; index <= Math.max(0, Math.round(count)); index += 1) names.push(buildname(rule, { run, step, sequence: index, kind: "image" }, extension));
699
+ return names;
700
+ }
701
+ function lapseplanof(value) {
702
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
703
+ const options = value;
704
+ if (typeof options.interval !== "number" || !Number.isFinite(options.interval)) return void 0;
705
+ if (typeof options.duration !== "number" || !Number.isFinite(options.duration)) return void 0;
706
+ const format = options.format === "jpeg" || options.format === "webp" ? options.format : "png";
707
+ return { interval: options.interval, duration: options.duration, format };
708
+ }
709
+ function lapseframes(plan) {
710
+ if (!(plan.interval > 0) || !(plan.duration > 0)) return [];
711
+ const frames = [];
712
+ for (let time = 0; time < plan.duration; time += plan.interval) frames.push(Math.round(time));
713
+ return frames;
714
+ }
715
+ function convertdirectiveof(value) {
716
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
717
+ const options = value;
718
+ if (options.target !== "png" && options.target !== "jpeg" && options.target !== "webp") return void 0;
719
+ const normalized = { target: options.target };
720
+ if (options.source === "png" || options.source === "jpeg" || options.source === "webp") normalized.source = options.source;
721
+ if (typeof options.quality === "number" && Number.isFinite(options.quality)) normalized.quality = options.quality;
722
+ return normalized;
723
+ }
724
+ function thumbdirectiveof(value) {
725
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
726
+ const options = value;
727
+ if (typeof options.size !== "number" || !Number.isFinite(options.size) || options.size <= 0) return void 0;
728
+ if (options.fit !== "cover" && options.fit !== "contain") return void 0;
729
+ if (typeof options.suffix !== "string" || !options.suffix.trim()) return void 0;
730
+ return { size: options.size, fit: options.fit, suffix: options.suffix.trim() };
731
+ }
732
+ function thumbgeometry(source, directive) {
733
+ const size = Math.max(1, Math.round(directive.size));
734
+ if (directive.fit === "contain") {
735
+ const scale2 = Math.min(size / Math.max(1, source.width), size / Math.max(1, source.height));
736
+ const dw = Math.max(1, Math.round(source.width * scale2));
737
+ const dh = Math.max(1, Math.round(source.height * scale2));
738
+ return { sx: 0, sy: 0, sw: source.width, sh: source.height, dx: Math.floor((size - dw) / 2), dy: Math.floor((size - dh) / 2), dw, dh, width: size, height: size };
739
+ }
740
+ const scale = Math.max(size / Math.max(1, source.width), size / Math.max(1, source.height));
741
+ const sw = Math.min(source.width, Math.round(size / scale));
742
+ const sh = Math.min(source.height, Math.round(size / scale));
743
+ return { sx: Math.floor((source.width - sw) / 2), sy: Math.floor((source.height - sh) / 2), sw, sh, dx: 0, dy: 0, dw: size, dh: size, width: size, height: size };
744
+ }
745
+ function mediaentries(raw) {
746
+ return raw.map((entry) => ({
747
+ url: typeof entry.url === "string" ? entry.url : "",
748
+ mime: typeof entry.mime === "string" ? entry.mime : "",
749
+ duration: typeof entry.duration === "number" && Number.isFinite(entry.duration) ? entry.duration : 0,
750
+ width: typeof entry.width === "number" && Number.isFinite(entry.width) ? Math.round(entry.width) : 0,
751
+ height: typeof entry.height === "number" && Number.isFinite(entry.height) ? Math.round(entry.height) : 0,
752
+ codecs: typeof entry.codecs === "string" ? entry.codecs : "",
753
+ tracks: Array.isArray(entry.tracks) ? entry.tracks.filter((item) => typeof item === "string") : []
754
+ }));
755
+ }
756
+ function assetentries(raw) {
757
+ return raw.map((entry) => ({
758
+ kind: entry.kind === "logo" ? "logo" : "favicon",
759
+ url: typeof entry.url === "string" ? entry.url : "",
760
+ bytes: typeof entry.bytes === "number" && Number.isFinite(entry.bytes) ? entry.bytes : 0,
761
+ ...typeof entry.sizes === "string" && entry.sizes.trim() ? { sizes: entry.sizes.trim() } : {}
762
+ }));
763
+ }
764
+ function streamsummaries(raw) {
765
+ return raw.map((entry) => {
766
+ const tracks = Array.isArray(entry.tracks) ? entry.tracks : [];
767
+ return {
768
+ kind: typeof entry.kind === "string" ? entry.kind : "stream",
769
+ tracks: tracks.length,
770
+ label: typeof entry.label === "string" ? entry.label : "",
771
+ live: entry.live === true,
772
+ detail: tracks.map((track) => {
773
+ const item = track;
774
+ return {
775
+ kind: typeof item.kind === "string" ? item.kind : "",
776
+ label: typeof item.label === "string" ? item.label : "",
777
+ ...typeof item.width === "number" && Number.isFinite(item.width) ? { width: Math.round(item.width) } : {},
778
+ ...typeof item.height === "number" && Number.isFinite(item.height) ? { height: Math.round(item.height) } : {},
779
+ ...typeof item.framerate === "number" && Number.isFinite(item.framerate) ? { framerate: item.framerate } : {},
780
+ state: typeof item.state === "string" ? item.state : ""
781
+ };
782
+ })
783
+ };
784
+ });
785
+ }
786
+
199
787
  // memory.ts
200
788
  var sessionmemory = class {
201
789
  constructor(adapter) {
@@ -963,23 +1551,172 @@ var sessionmemory = class {
963
1551
  for (const capture of await this.getcaptures()) runs.set(capture.id, capture.runid);
964
1552
  return records.filter((item) => runs.get(item.beforeid) === runid);
965
1553
  }
1554
+ /** Stores one media record of the 1.1.41 family with its bytes and step linkage, replacing the previous record of that id; the user configured media retention window expires the oldest bytes while the metadata and the recording index always survive. */
1555
+ async addmedia(record2) {
1556
+ const records = await this.getmediarecords();
1557
+ const retention = (await this.getsettings())?.mediaretention;
1558
+ const combined = [record2, ...records.filter((item) => item.id !== record2.id)];
1559
+ const stored = retention === void 0 ? combined : combined.map((item, index) => index < retention ? item : expiremediabytes(item));
1560
+ await this.adapter.set("media", stored);
1561
+ }
1562
+ /** Returns every stored media record, newest first. */
1563
+ async getmediarecords() {
1564
+ return await this.adapter.get("media") ?? [];
1565
+ }
1566
+ /** Returns the media records filtered by run and kind; an absent filter returns every record. */
1567
+ async listmedia(filter) {
1568
+ const records = await this.getmediarecords();
1569
+ return records.filter((item) => (filter.runid === void 0 || item.runid === filter.runid) && (filter.kind === void 0 || mediakindof(item) === filter.kind));
1570
+ }
1571
+ /** Returns one media record by its id. */
1572
+ async getmediarecord(id) {
1573
+ return (await this.getmediarecords()).find((item) => item.id === id);
1574
+ }
1575
+ /** Returns one recording with its file reference and frame index by its id. */
1576
+ async getrecording(id) {
1577
+ const found = await this.getmediarecord(id);
1578
+ return found !== void 0 && "startedat" in found ? found : void 0;
1579
+ }
1580
+ /** Removes one media record by its id; the audit trail keeps its outcome evidence. */
1581
+ async removemedia(id) {
1582
+ await this.adapter.set("media", (await this.getmediarecords()).filter((item) => item.id !== id));
1583
+ }
1584
+ /** Stores one observed image batch of a downloadimages step, replacing the previous batch of that id. */
1585
+ async addimagebatch(batch) {
1586
+ const records = await this.adapter.get("imagebatches") ?? [];
1587
+ await this.adapter.set("imagebatches", [batch, ...records.filter((item) => item.id !== batch.id)]);
1588
+ }
1589
+ /** Returns every observed image batch with its filter match counts, newest first. */
1590
+ async getimagebatches() {
1591
+ return await this.adapter.get("imagebatches") ?? [];
1592
+ }
1593
+ /** Stores one recording consent decision of an origin, replacing the previous record of that id. */
1594
+ async setrecordingconsent(record2) {
1595
+ const records = (await this.adapter.get("recordingconsents") ?? []).filter((item) => item.id !== record2.id);
1596
+ await this.adapter.set("recordingconsents", [record2, ...records]);
1597
+ }
1598
+ /** Returns every recording consent decision with its prompt and origin, newest first. */
1599
+ async getrecordingconsents() {
1600
+ return await this.adapter.get("recordingconsents") ?? [];
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
+ }
966
1675
  };
1676
+ function mediakindof(record2) {
1677
+ if ("pages" in record2) return "pdf";
1678
+ if ("startedat" in record2) return "recording";
1679
+ if ("timestamp" in record2) return "frame";
1680
+ if ("context" in record2) return "canvas";
1681
+ if ("tracks" in record2) return "stream";
1682
+ return "asset";
1683
+ }
1684
+ function expiremediabytes(record2) {
1685
+ if ("dataurl" in record2) {
1686
+ const source = record2;
1687
+ const copy = { ...source };
1688
+ delete copy.dataurl;
1689
+ return { ...copy, bytesexpired: true };
1690
+ }
1691
+ if ("startedat" in record2) {
1692
+ const source = record2;
1693
+ const copy = { ...source };
1694
+ delete copy.bytes;
1695
+ return { ...copy, bytesexpired: true };
1696
+ }
1697
+ return record2;
1698
+ }
967
1699
  function expirecapturebytes(record2) {
968
1700
  const { bytes, ...metadata } = record2;
969
1701
  void bytes;
970
1702
  return { ...metadata, bytesexpired: true };
971
1703
  }
1704
+ function expirecallbody(record2) {
1705
+ const { body, ...metadata } = record2;
1706
+ void body;
1707
+ return { ...metadata, bodyexpired: true };
1708
+ }
972
1709
  function randomid() {
973
1710
  return crypto.randomUUID();
974
1711
  }
975
1712
 
976
1713
  // policy.ts
977
- 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"]);
1714
+ var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql"]);
978
1715
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
979
- 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"]);
1716
+ var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml"]);
980
1717
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
981
1718
  var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
982
- 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"]);
1719
+ 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"]);
983
1720
  var valueactions = /* @__PURE__ */ new Set(["presskey", "drag", "drop", "upload", "readattribute", "removeattribute", "waittext", "evaluate", "zoomset", "tabactivate", "tabclose", "tabreload", "windowclose", "windowresize", "tabcreate", "windowcreate", "downloadfile", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "chooseradio", "setslider", "setdate", "setcolor", "followlink", "setfragment", "handleauth", "navintent", "openclipboard", "checksafe", "reopentab", "spanav", "duplicatetab", "pintab", "mutetab", "movetab", "movetabwindow", "searchtabs", "badgetab", "attachmeta", "focuswindow", "maximizewindow", "minimizewindow", "restorewindow", "incognitowindow", "asksubmit", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "pausedownload", "resumedownload", "verifydownload", "writeclipboard", "quarantinedownload", "scanvirus"]);
984
1721
  var tabscommandactions = /* @__PURE__ */ new Set(["querytabs", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "watchtab", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "savelayout", "restorelayout", "findclones", "searchtabs", "badgetab", "attachmeta", "listaudio", "reopenrun", "snapshotsession"]);
985
1722
  var formactions = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "submitform", "readerrors", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "handoffcaptcha", "fillcard", "fillcode", "consentpassword", "skiphoneypot", "detectlogin", "detecttemplate"]);
@@ -987,6 +1724,9 @@ var datasetactions = /* @__PURE__ */ new Set(["scrapetable", "exportcsv", "expor
987
1724
  var exportactions = /* @__PURE__ */ new Set(["exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk"]);
988
1725
  var filesactions = /* @__PURE__ */ new Set(["batchdownload", "pausedownload", "resumedownload", "verifydownload", "interceptmime", "exportnetlog", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "namecaptures", "cleanupartifacts"]);
989
1726
  var captureactions = /* @__PURE__ */ new Set(["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"]);
1727
+ var mediaactions = /* @__PURE__ */ new Set(["capturepdf", "recordscreen", "captureaudio", "captureframe", "downloadimages", "shotcanvas", "probestream", "readmedia", "readassets", "timelapse", "convertimage", "makethumbs"]);
1728
+ var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"]);
1729
+ 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"]);
990
1730
  var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
991
1731
  var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
992
1732
  var groupcolors = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
@@ -1688,6 +2428,305 @@ function validatetabsgrammar(step, options) {
1688
2428
  if (kind === "reopenrun" && !isnonempty(options.run)) return { allowed: false, reason: "A reviewed run id is required in options to reopen its tabs." };
1689
2429
  return { allowed: true };
1690
2430
  }
2431
+ function ismediakind(kind) {
2432
+ return mediaactions.has(kind);
2433
+ }
2434
+ function ishttpkind(kind) {
2435
+ return httpactions.has(kind);
2436
+ }
2437
+ function origincheck(session, url) {
2438
+ let parsed;
2439
+ try {
2440
+ parsed = new URL(url);
2441
+ } catch {
2442
+ return { allowed: false, reason: "The outbound request needs a valid url before it can be reviewed." };
2443
+ }
2444
+ if (parsed.protocol !== "https:") return { allowed: false, reason: "Outbound requests use HTTPS urls only." };
2445
+ if (parsed.username || parsed.password) return { allowed: false, reason: "Endpoint credentials are not allowed in the url." };
2446
+ if (!origingranted(session, parsed.origin)) return { allowed: false, reason: `The outbound request to ${parsed.origin} stays outside the session origin grants.` };
2447
+ return { allowed: true };
2448
+ }
2449
+ function credentialheadername(name) {
2450
+ return credentialheaders.has(name.trim().toLowerCase());
2451
+ }
2452
+ function fetchconsentrefgranted(step) {
2453
+ let options = {};
2454
+ try {
2455
+ options = parseoptions(step);
2456
+ } catch {
2457
+ options = {};
2458
+ }
2459
+ const request = options.fetch;
2460
+ const headers = request && typeof request === "object" && !Array.isArray(request) ? request.headers : void 0;
2461
+ const names = headers && typeof headers === "object" && !Array.isArray(headers) ? Object.keys(headers) : [];
2462
+ if (names.length === 0) return { allowed: true };
2463
+ const empty = names.some((name) => !name.trim());
2464
+ if (empty) return { allowed: false, reason: "Header allowlists with empty names are refused." };
2465
+ const credential = names.find((name) => credentialheadername(name));
2466
+ 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.` };
2467
+ 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.` };
2468
+ return { allowed: true };
2469
+ }
2470
+ function fetchbudgetallowed(timeout, retries, backoff, wait) {
2471
+ for (const [label, value] of [["timeout", timeout], ["retries", retries], ["backoff", backoff]]) {
2472
+ 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.` };
2473
+ }
2474
+ 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." };
2475
+ if (wait === void 0 || timeout === void 0) return { allowed: true };
2476
+ const attempts = Math.max(1, Math.floor(retries ?? 0) + 1);
2477
+ const waits = (backoff ?? 0) * (attempts * (attempts - 1)) / 2;
2478
+ const worstcase = timeout * attempts + waits;
2479
+ 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.` };
2480
+ return { allowed: true };
2481
+ }
2482
+ function outboundtarget(step) {
2483
+ let options = {};
2484
+ try {
2485
+ options = parseoptions(step);
2486
+ } catch {
2487
+ options = {};
2488
+ }
2489
+ const request = options.fetch;
2490
+ if (request && typeof request === "object" && !Array.isArray(request)) {
2491
+ const url = request.url;
2492
+ if (typeof url === "string" && url.trim()) return url.trim();
2493
+ }
2494
+ return void 0;
2495
+ }
2496
+ function validpath(path) {
2497
+ return path.split(".").every((segment) => /^[A-Za-z0-9_-]+$/.test(segment));
2498
+ }
2499
+ function validatehttpgrammar(step, options) {
2500
+ const kind = step.kind;
2501
+ if (kind === "fetchurl") {
2502
+ const request = options.fetch;
2503
+ if (!request || typeof request !== "object" || Array.isArray(request)) return { allowed: false, reason: "A reviewed fetch request with a url is required in options.fetch." };
2504
+ const fetchrequest = request;
2505
+ if (typeof fetchrequest.url !== "string" || !fetchrequest.url.trim()) return { allowed: false, reason: "The reviewed fetch request needs a non-empty url." };
2506
+ 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." };
2507
+ if (fetchrequest.headers !== void 0) {
2508
+ 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." };
2509
+ for (const name of Object.keys(fetchrequest.headers)) {
2510
+ if (!name.trim()) return { allowed: false, reason: "Header allowlists with empty names are refused." };
2511
+ if (typeof fetchrequest.headers[name] !== "string") return { allowed: false, reason: `The reviewed header ${name} needs a string value.` };
2512
+ }
2513
+ }
2514
+ if (fetchrequest.body !== void 0 && typeof fetchrequest.body !== "string") return { allowed: false, reason: "The reviewed fetch body must be a string." };
2515
+ 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." };
2516
+ const consentgate = fetchconsentrefgranted(step);
2517
+ if (!consentgate.allowed) return consentgate;
2518
+ const policycheck = validatefetchoptions(options.fetchoptions);
2519
+ if (!policycheck.allowed) return policycheck;
2520
+ const fetchpolicy = fetchoptionsvalues(options.fetchoptions);
2521
+ const budget = fetchbudgetallowed(fetchpolicy.timeout, fetchpolicy.retries, fetchpolicy.backoff, fetchnumeric(options, "wait"));
2522
+ if (!budget.allowed) return budget;
2523
+ if (options.stream !== void 0) {
2524
+ 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." };
2525
+ const streambudget = options.stream.budget;
2526
+ 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." };
2527
+ }
2528
+ }
2529
+ if (kind === "parsejson") {
2530
+ if (!isnonempty(options.call)) return { allowed: false, reason: "A reviewed stored call id is required in options.call before the body parses." };
2531
+ const fields = options.fields;
2532
+ 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." };
2533
+ for (const item of fields) {
2534
+ if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every json path rule must be an object." };
2535
+ const rule = item;
2536
+ if (!isnonempty(rule.name)) return { allowed: false, reason: "Every json path rule needs a non-empty field name." };
2537
+ 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.` };
2538
+ 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.` };
2539
+ }
2540
+ }
2541
+ if (kind === "parsehtml") {
2542
+ if (!isnonempty(options.call)) return { allowed: false, reason: "A reviewed stored call id is required in options.call before the markup parses." };
2543
+ const queries = options.queries;
2544
+ if (!Array.isArray(queries) || queries.length === 0) return { allowed: false, reason: "A reviewed non-empty list of html queries is required in options.queries." };
2545
+ for (const item of queries) {
2546
+ if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every html query must be an object." };
2547
+ const query = item;
2548
+ if (!isnonempty(query.selector)) return { allowed: false, reason: "Every html query needs a selector from the reviewed selector grammar." };
2549
+ if (query.attribute !== void 0 && !isnonempty(query.attribute)) return { allowed: false, reason: "The reviewed html query attribute must be a non-empty attribute name." };
2550
+ if (query.multi !== void 0 && typeof query.multi !== "boolean") return { allowed: false, reason: "The reviewed html query multi flag must be a boolean." };
2551
+ }
2552
+ }
2553
+ if (kind === "callrest" || kind === "callgraphql") {
2554
+ if (!isnonempty(options.endpoint)) return { allowed: false, reason: "A reviewed typed endpoint name is required in options.endpoint." };
2555
+ if (kind === "callrest") {
2556
+ 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." };
2557
+ 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." };
2558
+ 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." };
2559
+ }
2560
+ if (kind === "callgraphql") {
2561
+ const request = options.graphql;
2562
+ if (!request || typeof request !== "object" || Array.isArray(request)) return { allowed: false, reason: "A reviewed graphql request with an operation is required in options.graphql." };
2563
+ const graphql = request;
2564
+ if (typeof graphql.query !== "string" || !graphql.query.trim()) return { allowed: false, reason: "The reviewed graphql operation text must be a non-empty string." };
2565
+ 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." };
2566
+ 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." };
2567
+ if (graphql.operationname !== void 0 && !isnonempty(graphql.operationname)) return { allowed: false, reason: "The reviewed graphql operation name must be a non-empty string." };
2568
+ }
2569
+ 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." };
2570
+ const policycheck = validatefetchoptions(options.fetchoptions);
2571
+ if (!policycheck.allowed) return policycheck;
2572
+ const fetchpolicy = fetchoptionsvalues(options.fetchoptions);
2573
+ const budget = fetchbudgetallowed(fetchpolicy.timeout, fetchpolicy.retries, fetchpolicy.backoff, fetchnumeric(options, "wait"));
2574
+ if (!budget.allowed) return budget;
2575
+ }
2576
+ return { allowed: true };
2577
+ }
2578
+ function validatefetchoptions(value) {
2579
+ if (value === void 0) return { allowed: true };
2580
+ 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." };
2581
+ const options = value;
2582
+ for (const key of ["timeout", "backoff"]) {
2583
+ 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.` };
2584
+ }
2585
+ for (const key of ["retries", "follow"]) {
2586
+ 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.` };
2587
+ }
2588
+ return { allowed: true };
2589
+ }
2590
+ function fetchoptionsvalues(value) {
2591
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2592
+ const options = value;
2593
+ return { timeout: fetchnumeric(options, "timeout"), retries: fetchnumeric(options, "retries"), backoff: fetchnumeric(options, "backoff") };
2594
+ }
2595
+ function fetchnumeric(options, key) {
2596
+ const value = options[key];
2597
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2598
+ }
2599
+ function isrecordingkind(kind) {
2600
+ return kind === "recordscreen" || kind === "captureaudio";
2601
+ }
2602
+ function mediagate(session, tabid, origin, now) {
2603
+ if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the media capture." };
2604
+ if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture media." };
2605
+ if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot capture media." };
2606
+ if (session.tabid !== tabid) return { allowed: false, reason: `The media capture needs the active tab grant of session tab ${session.tabid} and refuses tab ${tabid}.` };
2607
+ if (!origingranted(session, origin)) return { allowed: false, reason: `The media capture of ${origin} needs the session origin grants first.` };
2608
+ return { allowed: true };
2609
+ }
2610
+ function recordingconsentgranted(step) {
2611
+ let options = {};
2612
+ try {
2613
+ options = parseoptions(step);
2614
+ } catch {
2615
+ options = {};
2616
+ }
2617
+ const consentref = options.consentref;
2618
+ if (typeof consentref !== "string" || !consentref.trim()) return { allowed: false, reason: "A recording of user activity requires a reviewed consent ref in options before it starts." };
2619
+ return { allowed: true };
2620
+ }
2621
+ function lapsebudgetallowed(interval, duration, wait) {
2622
+ if (!(interval > 0)) return { allowed: false, reason: "The reviewed lapse interval must be a positive number of milliseconds." };
2623
+ if (!(duration > 0)) return { allowed: false, reason: "The reviewed lapse duration must be a positive number of milliseconds." };
2624
+ if (wait !== void 0 && !(wait >= 0)) return { allowed: false, reason: "The reviewed wait budget must be zero or a positive number of milliseconds." };
2625
+ if (wait !== void 0 && duration > wait) return { allowed: false, reason: `The lapse duration of ${duration} milliseconds exceeds the reviewed wait budget of ${wait} milliseconds; review a wider budget or a shorter duration.` };
2626
+ return { allowed: true };
2627
+ }
2628
+ function validatemediagrammar(step, options) {
2629
+ const kind = step.kind;
2630
+ if (kind === "capturepdf") {
2631
+ const pdf = options.pdf;
2632
+ if (pdf !== void 0) {
2633
+ if (!pdf || typeof pdf !== "object" || Array.isArray(pdf)) return { allowed: false, reason: "The reviewed pdf options must be an object in options.pdf." };
2634
+ const pdfoptions = pdf;
2635
+ if (pdfoptions.paperwidth !== void 0 && (typeof pdfoptions.paperwidth !== "number" || !Number.isFinite(pdfoptions.paperwidth) || pdfoptions.paperwidth <= 0)) return { allowed: false, reason: "The reviewed pdf paper width must be a positive number of inches with no code cap." };
2636
+ if (pdfoptions.paperheight !== void 0 && (typeof pdfoptions.paperheight !== "number" || !Number.isFinite(pdfoptions.paperheight) || pdfoptions.paperheight <= 0)) return { allowed: false, reason: "The reviewed pdf paper height must be a positive number of inches with no code cap." };
2637
+ if (pdfoptions.margins !== void 0) {
2638
+ const margins = pdfoptions.margins;
2639
+ if (!margins || typeof margins !== "object" || Array.isArray(margins)) return { allowed: false, reason: "The reviewed pdf margins must be an object with top, right, bottom and left inches." };
2640
+ for (const side of ["top", "right", "bottom", "left"]) {
2641
+ const value = margins[side];
2642
+ if (value === void 0) continue;
2643
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return { allowed: false, reason: `The reviewed pdf ${side} margin must be zero or a positive number of inches; negative margins are refused.` };
2644
+ }
2645
+ }
2646
+ if (pdfoptions.scale !== void 0 && (typeof pdfoptions.scale !== "number" || !Number.isFinite(pdfoptions.scale) || pdfoptions.scale <= 0)) return { allowed: false, reason: "The reviewed pdf scale must be a positive number with no code cap." };
2647
+ if (pdfoptions.landscape !== void 0 && typeof pdfoptions.landscape !== "boolean") return { allowed: false, reason: "The reviewed pdf landscape flag must be a boolean." };
2648
+ if (pdfoptions.paginate !== void 0 && typeof pdfoptions.paginate !== "boolean") return { allowed: false, reason: "The reviewed pdf paginate flag must be a boolean." };
2649
+ }
2650
+ if (options.breakpoints !== void 0 && (!Array.isArray(options.breakpoints) || options.breakpoints.length === 0 || !options.breakpoints.every((item) => isnonempty(item)))) return { allowed: false, reason: "The reviewed pdf break points must be a non-empty list of selectors when present." };
2651
+ if (options.exporttarget !== void 0 && options.exporttarget !== "memory" && options.exporttarget !== "download") return { allowed: false, reason: "The reviewed pdf export target must be memory or download; pdf documents do not route to the clipboard." };
2652
+ if (options.name !== void 0 && !isnonempty(options.name)) return { allowed: false, reason: "The reviewed pdf artifact name must be a non-empty string." };
2653
+ }
2654
+ if (kind === "recordscreen" || kind === "captureaudio") {
2655
+ const recording = options.recording;
2656
+ if (recording !== void 0) {
2657
+ if (!recording || typeof recording !== "object" || Array.isArray(recording)) return { allowed: false, reason: "The reviewed recording options must be an object in options.recording." };
2658
+ const recordoptions = recording;
2659
+ if (recordoptions.scope !== void 0 && recordoptions.scope !== "tab" && recordoptions.scope !== "run") return { allowed: false, reason: "The reviewed recording scope must be tab or run." };
2660
+ if (recordoptions.fps !== void 0 && (typeof recordoptions.fps !== "number" || !Number.isFinite(recordoptions.fps) || recordoptions.fps <= 0)) return { allowed: false, reason: "The reviewed recording fps must be a positive number with no code ceiling." };
2661
+ if (recordoptions.bitrate !== void 0 && (typeof recordoptions.bitrate !== "number" || !Number.isFinite(recordoptions.bitrate) || recordoptions.bitrate <= 0)) return { allowed: false, reason: "The reviewed recording bitrate must be a positive number with no code ceiling." };
2662
+ if (recordoptions.audio !== void 0 && typeof recordoptions.audio !== "boolean") return { allowed: false, reason: "The reviewed recording audio flag must be a boolean." };
2663
+ }
2664
+ if (options.duration !== void 0 && (typeof options.duration !== "number" || !Number.isFinite(options.duration) || options.duration <= 0)) return { allowed: false, reason: "The reviewed recording duration must be a positive number of milliseconds with no code ceiling." };
2665
+ const consent = recordingconsentgranted(step);
2666
+ if (!consent.allowed) return consent;
2667
+ }
2668
+ if (kind === "captureframe") {
2669
+ if (options.timestamp !== void 0 && (typeof options.timestamp !== "number" || !Number.isFinite(options.timestamp) || options.timestamp < 0)) return { allowed: false, reason: "The reviewed frame timestamp must be zero or a positive number of seconds." };
2670
+ if (options.poster !== void 0 && typeof options.poster !== "boolean") return { allowed: false, reason: "The reviewed poster flag must be a boolean." };
2671
+ const capturecheck = validatecaptureoptions(options.capture);
2672
+ if (!capturecheck.allowed) return capturecheck;
2673
+ }
2674
+ if (kind === "downloadimages") {
2675
+ const filter = options.imagefilter;
2676
+ if (!filter || typeof filter !== "object" || Array.isArray(filter)) return { allowed: false, reason: "A reviewed imagefilter is required in options before any image downloads." };
2677
+ const imagefilter = filter;
2678
+ if (imagefilter.selector !== void 0 && !isnonempty(imagefilter.selector)) return { allowed: false, reason: "The reviewed imagefilter selector must be a non-empty selector from the reviewed selector grammar." };
2679
+ if (imagefilter.minwidth !== void 0 && (typeof imagefilter.minwidth !== "number" || !Number.isFinite(imagefilter.minwidth) || imagefilter.minwidth < 0)) return { allowed: false, reason: "The reviewed imagefilter minimum width must be zero or a positive number of pixels." };
2680
+ if (imagefilter.minheight !== void 0 && (typeof imagefilter.minheight !== "number" || !Number.isFinite(imagefilter.minheight) || imagefilter.minheight < 0)) return { allowed: false, reason: "The reviewed imagefilter minimum height must be zero or a positive number of pixels." };
2681
+ if (imagefilter.formats !== void 0 && (!Array.isArray(imagefilter.formats) || imagefilter.formats.length === 0 || !imagefilter.formats.every((item) => isnonempty(item)))) return { allowed: false, reason: "The reviewed imagefilter format list must be a non-empty list of mime or extension patterns when present." };
2682
+ if (options.naming !== void 0) {
2683
+ const namingcheck = validatecapturenaming(options.naming);
2684
+ if (!namingcheck.allowed) return namingcheck;
2685
+ }
2686
+ }
2687
+ if (kind === "shotcanvas") {
2688
+ const capturecheck = validatecaptureoptions(options.capture);
2689
+ if (!capturecheck.allowed) return capturecheck;
2690
+ }
2691
+ if (kind === "probestream" && options.selector !== void 0 && !isnonempty(options.selector)) return { allowed: false, reason: "The reviewed stream probe scope selector must be a non-empty string." };
2692
+ if (kind === "timelapse") {
2693
+ const lapse = options.lapse;
2694
+ if (!lapse || typeof lapse !== "object" || Array.isArray(lapse)) return { allowed: false, reason: "A reviewed lapse plan with interval, duration and format is required in options." };
2695
+ const plan = lapse;
2696
+ if (typeof plan.interval !== "number" || !Number.isFinite(plan.interval) || plan.interval <= 0) return { allowed: false, reason: "The reviewed lapse interval must be a positive number of milliseconds with no code ceiling." };
2697
+ if (typeof plan.duration !== "number" || !Number.isFinite(plan.duration) || plan.duration <= 0) return { allowed: false, reason: "The reviewed lapse duration must be a positive number of milliseconds with no code ceiling." };
2698
+ if (plan.format !== void 0 && plan.format !== "png" && plan.format !== "jpeg" && plan.format !== "webp") return { allowed: false, reason: "The reviewed lapse format must be png, jpeg or webp." };
2699
+ const budget = lapsebudgetallowed(plan.interval, plan.duration, typeof options.wait === "number" ? options.wait : void 0);
2700
+ if (!budget.allowed) return budget;
2701
+ const capturecheck = validatecaptureoptions(options.capture);
2702
+ if (!capturecheck.allowed) return capturecheck;
2703
+ }
2704
+ if (kind === "convertimage" || kind === "makethumbs") {
2705
+ const single = options.capture;
2706
+ const list = options.captures;
2707
+ const hasone = isnonempty(single);
2708
+ const haslist = Array.isArray(list) && list.length > 0 && list.every((item) => isnonempty(item));
2709
+ if (!hasone && !haslist) return { allowed: false, reason: "A reviewed capture id or a reviewed non-empty capture id list is required in options." };
2710
+ if (hasone && haslist) return { allowed: false, reason: "The reviewed step needs one capture id or a capture id list, not both." };
2711
+ }
2712
+ if (kind === "convertimage") {
2713
+ const convert = options.convert;
2714
+ if (!convert || typeof convert !== "object" || Array.isArray(convert)) return { allowed: false, reason: "A reviewed convert directive with a target format is required in options." };
2715
+ const directive = convert;
2716
+ if (directive.target !== "png" && directive.target !== "jpeg" && directive.target !== "webp") return { allowed: false, reason: "The reviewed conversion target must be png, jpeg or webp." };
2717
+ if (directive.source !== void 0 && directive.source !== "png" && directive.source !== "jpeg" && directive.source !== "webp") return { allowed: false, reason: "The reviewed conversion source must be png, jpeg or webp." };
2718
+ if (directive.quality !== void 0 && (typeof directive.quality !== "number" || !Number.isFinite(directive.quality) || directive.quality < 0 || directive.quality > 100)) return { allowed: false, reason: "The reviewed conversion quality must stay between zero and one hundred with no code cap inside that range." };
2719
+ }
2720
+ if (kind === "makethumbs") {
2721
+ const thumb = options.thumb;
2722
+ if (!thumb || typeof thumb !== "object" || Array.isArray(thumb)) return { allowed: false, reason: "A reviewed thumb directive with size, fit and suffix is required in options." };
2723
+ const directive = thumb;
2724
+ if (typeof directive.size !== "number" || !Number.isFinite(directive.size) || directive.size <= 0) return { allowed: false, reason: "The reviewed thumbnail size must be a positive number of pixels with no fixed set." };
2725
+ if (directive.fit !== "cover" && directive.fit !== "contain") return { allowed: false, reason: "The reviewed thumbnail fit must be cover or contain." };
2726
+ if (!isnonempty(directive.suffix)) return { allowed: false, reason: "The reviewed thumbnail naming suffix must be a non-empty string." };
2727
+ }
2728
+ return { allowed: true };
2729
+ }
1691
2730
  function validatestep(step, origin) {
1692
2731
  if (!allowedactions.has(step.kind)) return { allowed: false, reason: "Unsupported action kind." };
1693
2732
  if (!step.summary.trim()) return { allowed: false, reason: "A human-readable action summary is required." };
@@ -1909,6 +2948,14 @@ function validatestep(step, origin) {
1909
2948
  const capturecheck = validatecapturegrammar(step, options);
1910
2949
  if (!capturecheck.allowed) return capturecheck;
1911
2950
  }
2951
+ if (ismediakind(step.kind)) {
2952
+ const mediacheck = validatemediagrammar(step, options);
2953
+ if (!mediacheck.allowed) return mediacheck;
2954
+ }
2955
+ if (ishttpkind(step.kind)) {
2956
+ const httpcheck = validatehttpgrammar(step, options);
2957
+ if (!httpcheck.allowed) return httpcheck;
2958
+ }
1912
2959
  if (step.kind === "tabcreate") {
1913
2960
  if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
1914
2961
  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." };
@@ -1980,6 +3027,25 @@ function canexecute(input) {
1980
3027
  const target = captureoptions.capture?.exporttarget;
1981
3028
  if (target !== void 0 && target !== "memory" && target !== "download" && target !== "clipboard") return { allowed: false, reason: "The capture export target must be memory, download or clipboard." };
1982
3029
  }
3030
+ if (ismediakind(input.step.kind)) {
3031
+ const mediagatecheck = mediagate(input.session, input.tabid, input.origin, now);
3032
+ if (!mediagatecheck.allowed) return mediagatecheck;
3033
+ }
3034
+ if (isrecordingkind(input.step.kind)) {
3035
+ const recordinggate = recordingconsentgranted(input.step);
3036
+ if (!recordinggate.allowed) return recordinggate;
3037
+ }
3038
+ if (ishttpkind(input.step.kind)) {
3039
+ const target = outboundtarget(input.step);
3040
+ if (target !== void 0) {
3041
+ const outboundgate = origincheck(input.session, target);
3042
+ if (!outboundgate.allowed) return outboundgate;
3043
+ }
3044
+ if (input.step.kind === "fetchurl" || input.step.kind === "callrest" || input.step.kind === "callgraphql") {
3045
+ const consentgate = fetchconsentrefgranted(input.step);
3046
+ if (!consentgate.allowed) return consentgate;
3047
+ }
3048
+ }
1983
3049
  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") {
1984
3050
  let options = {};
1985
3051
  try {
@@ -2001,7 +3067,7 @@ function canexecute(input) {
2001
3067
  }
2002
3068
 
2003
3069
  // version.ts
2004
- var packageversion = "1.1.40";
3070
+ var packageversion = "1.1.42";
2005
3071
 
2006
3072
  // types.ts
2007
3073
  var protocolversion = packageversion;
@@ -2015,9 +3081,10 @@ function text(value, field) {
2015
3081
  if (typeof value !== "string" || !value.trim()) throw new Error(`${field} must be a non-empty string.`);
2016
3082
  return value.trim();
2017
3083
  }
2018
- function parseproposal(value, origin) {
3084
+ function parseproposal(value, origin, grants) {
2019
3085
  const root = record(value);
2020
3086
  if (root.version !== protocolversion) throw new Error("Unsupported protocol version.");
3087
+ const covered = grants !== void 0 && grants.length > 0 ? grants : [origin];
2021
3088
  const planinput = record(root.plan);
2022
3089
  const stepsinput = planinput.steps;
2023
3090
  if (!Array.isArray(stepsinput) || stepsinput.length === 0) throw new Error("A plan needs at least one step.");
@@ -2035,6 +3102,17 @@ function parseproposal(value, origin) {
2035
3102
  };
2036
3103
  const evaluation = validatestep(step, origin);
2037
3104
  if (!evaluation.allowed) throw new Error(evaluation.reason);
3105
+ const target = outboundtarget(step);
3106
+ if (target !== void 0) {
3107
+ const granted = covered.some((pattern) => {
3108
+ try {
3109
+ return new URL(target).origin === new URL(pattern).origin;
3110
+ } catch {
3111
+ return false;
3112
+ }
3113
+ });
3114
+ if (!granted) throw new Error(`The fetch request to ${target} targets an origin outside the grants.`);
3115
+ }
2038
3116
  return step;
2039
3117
  });
2040
3118
  for (const step of steps) {
@@ -2065,7 +3143,7 @@ function requestbody(input) {
2065
3143
  return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
2066
3144
  }
2067
3145
  function outcomeresponse(input) {
2068
- 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 } : {} });
3146
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {} });
2069
3147
  }
2070
3148
  function mapresponse(input) {
2071
3149
  return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
@@ -2145,12 +3223,28 @@ function quarantinereport(input) {
2145
3223
  function capturereport(input) {
2146
3224
  return { version: protocolversion, records: input.records, pairs: input.pairs };
2147
3225
  }
3226
+ function mediareport(input) {
3227
+ return { version: protocolversion, records: input.records, images: input.images };
3228
+ }
3229
+ function callsreport(input) {
3230
+ const calls = input.calls.map((call) => {
3231
+ const { body, ...metadata } = call;
3232
+ void body;
3233
+ return metadata;
3234
+ });
3235
+ return { version: protocolversion, calls };
3236
+ }
2148
3237
  export {
2149
3238
  annotationplanof,
3239
+ assetentries,
2150
3240
  blendrows,
2151
3241
  buildname,
3242
+ buildpdf,
2152
3243
  buildsheet,
2153
3244
  buildstitchplan,
3245
+ callgraphql,
3246
+ callrest,
3247
+ callsreport,
2154
3248
  canexecute,
2155
3249
  captureelement,
2156
3250
  captureformats,
@@ -2162,38 +3256,68 @@ export {
2162
3256
  capturestitched,
2163
3257
  capturetargets,
2164
3258
  capturevisible,
3259
+ convertdirectiveof,
2165
3260
  croprect,
2166
3261
  crossesviewport,
2167
3262
  datasetresponse,
3263
+ dedupeimages,
2168
3264
  actionrisk as deriveactionrisk,
2169
3265
  diffresponse,
2170
3266
  downloadreport,
2171
3267
  errorreportresponse,
2172
3268
  eventresponse,
2173
3269
  extractionreport,
3270
+ fetchoptionsof,
3271
+ fetchrequestof,
3272
+ finishrecording,
2174
3273
  fixedheadermatch,
2175
3274
  formreportresponse,
3275
+ frameinterval,
2176
3276
  generatedvalueallowed,
3277
+ graphqlopenvelope,
3278
+ graphqlrequestof,
2177
3279
  heldkeysreport,
2178
3280
  hostpattern,
3281
+ htmlqueriesof,
3282
+ httpkinds,
3283
+ imagefilterof,
3284
+ imagematches,
3285
+ imagenames,
2179
3286
  isformkind,
2180
3287
  iswatchkind,
3288
+ jsonpathrulesof,
3289
+ lapseframes,
3290
+ lapseplanof,
2181
3291
  layoutreport,
2182
3292
  mapresponse,
3293
+ mediaentries,
3294
+ mediakinds,
3295
+ mediareport,
2183
3296
  navstateresponse,
2184
3297
  netlogreport,
3298
+ newrecording,
2185
3299
  normalizeendpoint,
2186
3300
  observationmodeof,
2187
3301
  observationresponse,
2188
3302
  outcomeresponse,
2189
3303
  pairstates,
3304
+ parsehtmlbody,
2190
3305
  parseproposal,
2191
3306
  passwordconsentgranted,
3307
+ payloadvalid,
3308
+ payloadwithdefaults,
3309
+ pdfoptionsof,
3310
+ pdfpagesize,
3311
+ pdfsegments,
3312
+ pdftextlayout,
2192
3313
  profilegrantgranted,
2193
3314
  protocolversion,
2194
3315
  provenancereport,
2195
3316
  quarantinereport,
2196
3317
  randomid,
3318
+ readpath,
3319
+ readstream,
3320
+ recordingoptionsof,
2197
3321
  regionsteps,
2198
3322
  requestbody,
2199
3323
  resolutionverdict,
@@ -2201,12 +3325,20 @@ export {
2201
3325
  scaledrect,
2202
3326
  seamweights,
2203
3327
  selectorresponse,
3328
+ sendfetch,
2204
3329
  sessionmemory,
2205
3330
  signalsreport,
3331
+ statusclassof,
3332
+ streamsummaries,
3333
+ streamwindowof,
2206
3334
  submitreviewgranted,
2207
3335
  tabreportresponse,
3336
+ templateurl,
3337
+ thumbdirectiveof,
3338
+ thumbgeometry,
2208
3339
  trailreport,
2209
3340
  transformgrammar,
3341
+ unwrapgraphql,
2210
3342
  validatefieldmatch,
2211
3343
  validateformrecord,
2212
3344
  validatestep,