@wenathlan/extension 1.1.36 → 1.1.38

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
@@ -435,20 +435,208 @@ var sessionmemory = class {
435
435
  async setcontroltab(state) {
436
436
  return this.adapter.set("controltab", state);
437
437
  }
438
+ /** Stores one saved form profile under its reviewed name, replacing the previous profile of that name. */
439
+ async setprofile(profile) {
440
+ const records = (await this.getprofiles()).filter((item) => item.name !== profile.name);
441
+ await this.adapter.set("formprofiles", [profile, ...records]);
442
+ }
443
+ /** Returns one saved form profile by its reviewed name. */
444
+ async getprofile(name) {
445
+ return (await this.getprofiles()).find((item) => item.name === name);
446
+ }
447
+ /** Returns every saved form profile with its origin grants, newest first. */
448
+ async getprofiles() {
449
+ return await this.adapter.get("formprofiles") ?? [];
450
+ }
451
+ /** Removes one saved form profile by its reviewed name. */
452
+ async removeprofile(name) {
453
+ const records = (await this.getprofiles()).filter((item) => item.name !== name);
454
+ await this.adapter.set("formprofiles", records);
455
+ }
456
+ /** Records one wizard state with its step history. */
457
+ async addwizard(state) {
458
+ const records = await this.getwizards();
459
+ await this.adapter.set("wizards", [state, ...records]);
460
+ }
461
+ /** Returns every stored wizard state with its step history, newest first. */
462
+ async getwizards() {
463
+ return await this.adapter.get("wizards") ?? [];
464
+ }
465
+ /** Stores one submission ticket with its values hash, replacing the previous ticket of that id. */
466
+ async setticket(ticket) {
467
+ const records = (await this.gettickets()).filter((item) => item.id !== ticket.id);
468
+ await this.adapter.set("submittickets", [ticket, ...records]);
469
+ }
470
+ /** Returns every stored submission ticket with its values hash, newest first. */
471
+ async gettickets() {
472
+ return await this.adapter.get("submittickets") ?? [];
473
+ }
474
+ /** Records one collected error report for correction loops. */
475
+ async adderrorreport(report) {
476
+ const records = await this.geterrorreports();
477
+ await this.adapter.set("errorreports", [report, ...records]);
478
+ }
479
+ /** Returns every stored error report, newest first. */
480
+ async geterrorreports() {
481
+ return await this.adapter.get("errorreports") ?? [];
482
+ }
483
+ /** Records one typeahead pick observed when a reviewed suggestion entry was chosen. */
484
+ async addpick(pick) {
485
+ const records = await this.getpicks();
486
+ await this.adapter.set("typeaheadpicks", [pick, ...records]);
487
+ }
488
+ /** Returns every recorded typeahead pick, newest first. */
489
+ async getpicks() {
490
+ return await this.adapter.get("typeaheadpicks") ?? [];
491
+ }
492
+ /** Records one captcha handoff while the plan waits for the user. */
493
+ async addcaptcha(handoff) {
494
+ const records = await this.getcaptchas();
495
+ await this.adapter.set("captchas", [handoff, ...records]);
496
+ }
497
+ /** Returns every captcha handoff record with its resolution state, newest first. */
498
+ async getcaptchas() {
499
+ return await this.adapter.get("captchas") ?? [];
500
+ }
501
+ /** Resolves one captcha handoff by id once the user finished it. */
502
+ async resolvecaptcha(id, resolvedat) {
503
+ const records = await this.getcaptchas();
504
+ await this.adapter.set("captchas", records.map((handoff) => handoff.id === id && !handoff.resolved ? { ...handoff, resolved: true, resolvedat } : handoff));
505
+ }
506
+ /** Records one login or template detection for its origin. */
507
+ async adddetection(record2) {
508
+ const records = await this.getdetections();
509
+ await this.adapter.set("detections", [record2, ...records]);
510
+ }
511
+ /** Returns every stored login and template detection per origin, newest first. */
512
+ async getdetections() {
513
+ return await this.adapter.get("detections") ?? [];
514
+ }
515
+ /** Stores the reviewed one time code behind the consent gate of an active session. */
516
+ async setcodevalue(value) {
517
+ return this.adapter.set("codevalue", value);
518
+ }
519
+ /** Returns the reviewed one time code, if the user stored one behind the consent gate. */
520
+ async getcodevalue() {
521
+ return this.adapter.get("codevalue");
522
+ }
523
+ /** Stores one dataset with its column specs and rows, replacing the previous record of that id. */
524
+ async setdataset(value) {
525
+ await this.adapter.set(`dataset${value.id}`, value);
526
+ const ids = (await this.adapter.get("datasets") ?? []).filter((id) => id !== value.id);
527
+ await this.adapter.set("datasets", [value.id, ...ids]);
528
+ }
529
+ /** Returns one stored dataset by its id with its column specs and row count. */
530
+ async getdataset(id) {
531
+ return this.adapter.get(`dataset${id}`);
532
+ }
533
+ /** Returns every stored dataset id, newest first. */
534
+ async getdatasets() {
535
+ const ids = await this.adapter.get("datasets") ?? [];
536
+ const records = [];
537
+ for (const id of ids) {
538
+ const record2 = await this.adapter.get(`dataset${id}`);
539
+ if (record2) records.push(record2);
540
+ }
541
+ return records;
542
+ }
543
+ /** Stores one imported csv dataset for fill loops beside the dataset store. */
544
+ async addimport(value) {
545
+ await this.setdataset(value);
546
+ const ids = (await this.adapter.get("imports") ?? []).filter((id) => id !== value.id);
547
+ await this.adapter.set("imports", [value.id, ...ids]);
548
+ }
549
+ /** Returns every imported csv dataset for fill loops, newest first. */
550
+ async getimports() {
551
+ const ids = await this.adapter.get("imports") ?? [];
552
+ const records = [];
553
+ for (const id of ids) {
554
+ const record2 = await this.adapter.get(`dataset${id}`);
555
+ if (record2) records.push(record2);
556
+ }
557
+ return records;
558
+ }
559
+ /** Stores one extraction session with its cursor and page history, replacing the previous session of that id. */
560
+ async setextractsession(value) {
561
+ await this.adapter.set(`extract${value.id}`, value);
562
+ const ids = (await this.adapter.get("extracts") ?? []).filter((id) => id !== value.id);
563
+ await this.adapter.set("extracts", [value.id, ...ids]);
564
+ }
565
+ /** Returns every extraction session with its cursor and page history, newest first. */
566
+ async getextractsessions() {
567
+ const ids = await this.adapter.get("extracts") ?? [];
568
+ const records = [];
569
+ for (const id of ids) {
570
+ const record2 = await this.adapter.get(`extract${id}`);
571
+ if (record2) records.push(record2);
572
+ }
573
+ return records;
574
+ }
575
+ /** Records one provenance record of an exported artifact. */
576
+ async addprovenance(record2) {
577
+ const records = await this.getprovenances();
578
+ await this.adapter.set("provenances", [record2, ...records]);
579
+ }
580
+ /** Returns every provenance record per exported artifact, newest first. */
581
+ async getprovenances() {
582
+ return await this.adapter.get("provenances") ?? [];
583
+ }
584
+ /** Stores the transform rules and dedupe keys of one task, replacing the previous record of that task. */
585
+ async settaskrules(value) {
586
+ const records = (await this.adapter.get("taskrules") ?? []).filter((item) => item.taskid !== value.taskid);
587
+ await this.adapter.set("taskrules", [value, ...records]);
588
+ }
589
+ /** Returns the transform rules and dedupe keys per task, newest first. */
590
+ async gettaskrules() {
591
+ return await this.adapter.get("taskrules") ?? [];
592
+ }
593
+ /** Stores one stream chunk state for resume, replacing the previous state of that dataset. */
594
+ async setstream(state) {
595
+ const records = (await this.adapter.get("streams") ?? []).filter((item) => item.datasetid !== state.datasetid);
596
+ await this.adapter.set("streams", [state, ...records]);
597
+ }
598
+ /** Returns every stream chunk state persisted for resume, newest first. */
599
+ async getstreams() {
600
+ return await this.adapter.get("streams") ?? [];
601
+ }
602
+ /** Stores one reviewed sheet endpoint config behind its origin grant, replacing the previous config of that origin. */
603
+ async setsheetendpoint(config) {
604
+ const records = (await this.adapter.get("sheetendpoints") ?? []).filter((item) => item.origin !== config.origin);
605
+ await this.adapter.set("sheetendpoints", [config, ...records]);
606
+ }
607
+ /** Returns every reviewed sheet endpoint config, newest first. */
608
+ async getsheetendpoints() {
609
+ return await this.adapter.get("sheetendpoints") ?? [];
610
+ }
611
+ /** Records one exported data artifact; artifact retention is a user setting and an absent setting keeps every artifact. */
612
+ async addexport(artifact) {
613
+ const records = await this.getexports();
614
+ const combined = [artifact, ...records.filter((item) => item.id !== artifact.id)];
615
+ const retention = (await this.getsettings())?.artifactretention;
616
+ await this.adapter.set("exports", retention === void 0 ? combined : combined.slice(0, retention));
617
+ }
618
+ /** Returns every exported data artifact with its content and checksum, newest first. */
619
+ async getexports() {
620
+ return await this.adapter.get("exports") ?? [];
621
+ }
438
622
  };
439
623
  function randomid() {
440
624
  return crypto.randomUUID();
441
625
  }
442
626
 
443
627
  // policy.ts
444
- 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"]);
628
+ 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"]);
445
629
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
446
- 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"]);
630
+ 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"]);
447
631
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
448
632
  var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
449
- 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"]);
450
- 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"]);
633
+ 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"]);
634
+ 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"]);
451
635
  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"]);
636
+ 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"]);
637
+ var datasetactions = /* @__PURE__ */ new Set(["scrapetable", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "importcsv", "looprows", "transformvalues", "deduperows", "paginateextract", "mergepages", "stamplerows", "previewgrid", "streamdisk", "resumeextract", "logprovenance"]);
638
+ var exportactions = /* @__PURE__ */ new Set(["exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk"]);
639
+ var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
452
640
  var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
453
641
  var groupcolors = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
454
642
  function normalizeendpoint(value) {
@@ -492,6 +680,240 @@ function istabscommandkind(kind) {
492
680
  function islayoutkind(kind) {
493
681
  return layoutmutationactions.has(kind);
494
682
  }
683
+ function isformkind(kind) {
684
+ return formactions.has(kind);
685
+ }
686
+ function isdatasetkind(kind) {
687
+ return datasetactions.has(kind);
688
+ }
689
+ function isexportkind(kind) {
690
+ return exportactions.has(kind);
691
+ }
692
+ function exportgranted(session, origin) {
693
+ if (!origingranted(session, origin)) return { allowed: false, reason: `The export of extracted data from ${origin} needs the session origin grants before it leaves local memory.` };
694
+ return { allowed: true };
695
+ }
696
+ function validatefieldmatch(value) {
697
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed field match is required in options." };
698
+ const match = value;
699
+ if (match.mode !== "label" && match.mode !== "placeholder" && match.mode !== "arialabel" && match.mode !== "name") return { allowed: false, reason: "The reviewed field match mode must be label, placeholder, arialabel or name." };
700
+ const key = match.mode === "label" ? "label" : match.mode === "placeholder" ? "placeholder" : match.mode === "arialabel" ? "arialabel" : "name";
701
+ if (!isnonempty(match[key])) return { allowed: false, reason: `The reviewed ${match.mode} field match needs a non-empty ${key}.` };
702
+ return { allowed: true };
703
+ }
704
+ function validateformrecord(value) {
705
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed form record with entries is required in options." };
706
+ const record2 = value;
707
+ if (record2.form !== void 0 && !isnonempty(record2.form)) return { allowed: false, reason: "The reviewed form record form selector must be a non-empty string." };
708
+ if (!Array.isArray(record2.entries) || record2.entries.length === 0) return { allowed: false, reason: "The reviewed form record needs a non-empty list of entries." };
709
+ for (const item of record2.entries) {
710
+ if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every reviewed form record entry must be an object." };
711
+ const entry = item;
712
+ const matchcheck = validatefieldmatch(entry.match);
713
+ if (!matchcheck.allowed) return matchcheck;
714
+ if (typeof entry.kind !== "string" || !fieldkinds.includes(entry.kind)) return { allowed: false, reason: "Every reviewed form record entry needs a known field kind." };
715
+ if (typeof entry.value !== "string") return { allowed: false, reason: "Every reviewed form record entry needs a string value." };
716
+ if (entry.kind === "password") return { allowed: false, reason: "Password entries are refused inside form records; use consentpassword with a reviewed consent ref." };
717
+ }
718
+ return { allowed: true };
719
+ }
720
+ function validatevaluegen(value) {
721
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed valuegen rule with a field kind is required in options." };
722
+ const rule = value;
723
+ if (typeof rule.kind !== "string" || !fieldkinds.includes(rule.kind)) return { allowed: false, reason: "The reviewed valuegen kind must be a known field kind." };
724
+ if (rule.locale !== void 0 && !isnonempty(rule.locale)) return { allowed: false, reason: "The reviewed valuegen locale must be a non-empty string." };
725
+ if (rule.seed !== void 0 && (typeof rule.seed !== "number" || !Number.isFinite(rule.seed))) return { allowed: false, reason: "The reviewed valuegen seed must be a finite number." };
726
+ return { allowed: true };
727
+ }
728
+ function validatefieldpairs(options, mode) {
729
+ const pairs = options.fields;
730
+ if (!Array.isArray(pairs) || pairs.length === 0) return { allowed: false, reason: "A reviewed non-empty list of field pairs is required in options." };
731
+ for (const item of pairs) {
732
+ if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every reviewed field pair must be an object." };
733
+ const pair = item;
734
+ if (!isnonempty(pair[mode])) return { allowed: false, reason: `Every reviewed field pair needs a non-empty ${mode}.` };
735
+ if (typeof pair.value !== "string" || !pair.value.trim()) return { allowed: false, reason: "Every reviewed field pair needs a non-empty value." };
736
+ }
737
+ return { allowed: true };
738
+ }
739
+ function validatecardsegments(value) {
740
+ if (!Array.isArray(value) || value.length === 0) return { allowed: false, reason: "A reviewed non-empty list of card segments is required in options." };
741
+ for (const item of value) {
742
+ if (!item || typeof item !== "object" || Array.isArray(item)) return { allowed: false, reason: "Every reviewed card segment must be an object." };
743
+ const segment = item;
744
+ const matchcheck = validatefieldmatch(segment.match);
745
+ if (!matchcheck.allowed) return matchcheck;
746
+ if (typeof segment.value !== "string" || !segment.value.trim()) return { allowed: false, reason: "Every reviewed card segment needs a non-empty value." };
747
+ }
748
+ return { allowed: true };
749
+ }
750
+ function validateformgrammar(step, options) {
751
+ const kind = step.kind;
752
+ if (kind === "fillform" || kind === "saveprofiles" && options.formrecord !== void 0) {
753
+ const recordcheck = validateformrecord(options.formrecord);
754
+ if (!recordcheck.allowed) return recordcheck;
755
+ }
756
+ if (kind === "filllabel" || kind === "fillplaceholder") {
757
+ const paircheck = validatefieldpairs(options, kind === "filllabel" ? "label" : "placeholder");
758
+ if (!paircheck.allowed) return paircheck;
759
+ }
760
+ if (kind === "generatevalues" && options.valuegen !== void 0) {
761
+ const rulecheck = validatevaluegen(options.valuegen);
762
+ if (!rulecheck.allowed) return rulecheck;
763
+ }
764
+ if (kind === "saveprofiles" && !isnonempty(options.name)) return { allowed: false, reason: "A reviewed profile name is required in options." };
765
+ if (kind === "submitform" && !isnonempty(options.consentref)) return { allowed: false, reason: "A reviewed consent ref of an approved asksubmit ticket is required in options." };
766
+ if (kind === "retryform") {
767
+ const backoff = options.backoff;
768
+ if (!backoff || typeof backoff !== "object" || Array.isArray(backoff)) return { allowed: false, reason: "A reviewed backoff rule with wait and factor is required in options." };
769
+ const rule = backoff;
770
+ if (typeof rule.wait !== "number" || !Number.isFinite(rule.wait) || rule.wait <= 0) return { allowed: false, reason: "The reviewed retry backoff wait must be a positive number of milliseconds with no code ceiling." };
771
+ if (typeof rule.factor !== "number" || !Number.isFinite(rule.factor) || rule.factor < 1) return { allowed: false, reason: "The reviewed retry backoff factor must be one or greater with no code ceiling." };
772
+ if (options.attempts !== void 0 && (typeof options.attempts !== "number" || !Number.isInteger(options.attempts) || options.attempts < 1)) return { allowed: false, reason: "The reviewed retry attempts must be a positive integer with no code ceiling." };
773
+ }
774
+ if (kind === "runwizard" && options.steps !== void 0 && (typeof options.steps !== "number" || !Number.isInteger(options.steps) || options.steps < 1)) return { allowed: false, reason: "The reviewed wizard step count must be a positive integer with no code ceiling." };
775
+ if (kind === "selectchain") {
776
+ if (!isnonempty(options.child)) return { allowed: false, reason: "A reviewed child selector of the dependent control is required in options." };
777
+ if (!nonnegativeoption(options, "wait")) return { allowed: false, reason: "The reviewed dependent wait must be zero or a positive number of milliseconds." };
778
+ }
779
+ if (kind === "picktypeahead") {
780
+ if (!isnonempty(options.pick)) return { allowed: false, reason: "A reviewed suggestion entry to pick is required in options." };
781
+ if (!nonnegativeoption(options, "timeout")) return { allowed: false, reason: "The reviewed typeahead timeout must be zero or a positive number of milliseconds." };
782
+ }
783
+ if (kind === "pickdate" && !/^\d{4}-\d{2}-\d{2}$/.test(step.value ?? "")) return { allowed: false, reason: "The reviewed date must use the yyyy-mm-dd form." };
784
+ if (kind === "fillcard") {
785
+ const segmentcheck = validatecardsegments(options.segments);
786
+ if (!segmentcheck.allowed) return segmentcheck;
787
+ if (!nonnegativeoption(options, "pause")) return { allowed: false, reason: "The reviewed card typing pause must be zero or a positive number of milliseconds." };
788
+ }
789
+ if (kind === "fillcode" && !isnonempty(options.source)) return { allowed: false, reason: "A reviewed one time code source is required in options." };
790
+ if (kind === "consentpassword" && !isnonempty(options.consentref)) return { allowed: false, reason: "A reviewed consent ref is required in options before any password is filled." };
791
+ return { allowed: true };
792
+ }
793
+ function validatetransformrule(value) {
794
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed transform rule with an expression, sources and a target is required in options." };
795
+ const rule = value;
796
+ const expression = rule.expression;
797
+ if (typeof expression !== "string" || !/^(trim|upper|lower|number|prefix|suffix|replace)(?::.+)?$/.test(expression)) return { allowed: false, reason: "The reviewed transform expression must be trim, upper, lower, number, prefix, suffix or replace with an optional argument." };
798
+ if (expression.startsWith("replace") && !expression.slice("replace".length).includes("=>")) return { allowed: false, reason: "The reviewed replace expression needs the from=>to separator." };
799
+ if (expression.startsWith("replace") && expression.slice("replace:".length).split("=>")[0] === "") return { allowed: false, reason: "The reviewed replace expression needs a non-empty from part." };
800
+ if (!Array.isArray(rule.sources) || rule.sources.length === 0 || !rule.sources.every((source) => isnonempty(source))) return { allowed: false, reason: "Every reviewed transform rule needs a non-empty list of source columns." };
801
+ if (!isnonempty(rule.target)) return { allowed: false, reason: "Every reviewed transform rule needs a non-empty target column." };
802
+ return { allowed: true };
803
+ }
804
+ function validatedatasetids(options, key) {
805
+ const ids = options[key];
806
+ if (!Array.isArray(ids) || ids.length === 0 || !ids.every((id) => isnonempty(id))) return { allowed: false, reason: `A reviewed non-empty list of dataset ids is required in options as ${key}.` };
807
+ return { allowed: true };
808
+ }
809
+ function validatedatagrammar(step, options, origin) {
810
+ const kind = step.kind;
811
+ if (kind === "scrapetable") {
812
+ if (options.name !== void 0 && !isnonempty(options.name)) return { allowed: false, reason: "The reviewed dataset name must be a non-empty string." };
813
+ if (options.rowlimit !== void 0 && (typeof options.rowlimit !== "number" || !Number.isInteger(options.rowlimit) || options.rowlimit < 1)) return { allowed: false, reason: "The reviewed row limit must be a positive integer with no code ceiling." };
814
+ }
815
+ if (kind === "paginateextract") {
816
+ if (!isnonempty(options.next)) return { allowed: false, reason: "A reviewed next control selector is required in options." };
817
+ if (options.pages !== void 0 && (typeof options.pages !== "number" || !Number.isInteger(options.pages) || options.pages < 1)) return { allowed: false, reason: "The reviewed page count must be a positive integer with no code ceiling." };
818
+ if (!nonnegativeoption(options, "wait")) return { allowed: false, reason: "The reviewed row freshness wait must be zero or a positive number of milliseconds." };
819
+ }
820
+ if (kind === "exportcsv" || kind === "exportjson" || kind === "exportexcel" || kind === "copytable" || kind === "streamdisk") {
821
+ if (!isnonempty(options.dataset)) return { allowed: false, reason: "A reviewed dataset id is required in options." };
822
+ if (options.name !== void 0 && !isnonempty(options.name)) return { allowed: false, reason: "The reviewed artifact name must be a non-empty string." };
823
+ }
824
+ if (kind === "exportcsv" && options.delimiter !== void 0 && (typeof options.delimiter !== "string" || options.delimiter.length !== 1)) return { allowed: false, reason: "The reviewed csv delimiter must be a single character." };
825
+ if (kind === "streamdisk" && (typeof options.chunk !== "number" || !Number.isInteger(options.chunk) || options.chunk < 1)) return { allowed: false, reason: "The reviewed streaming chunk size must be a positive integer with no code ceiling." };
826
+ if (kind === "pushsheets") {
827
+ if (!isnonempty(options.dataset)) return { allowed: false, reason: "A reviewed dataset id is required in options." };
828
+ if (!isnonempty(options.sheet)) return { allowed: false, reason: "A reviewed sheet endpoint url is required in options." };
829
+ if (!ishttpsurl(options.sheet)) return { allowed: false, reason: "The reviewed sheet endpoint url must use HTTPS." };
830
+ if (options.reviewed !== true) return { allowed: false, reason: "The sheet push needs the explicit reviewed flag before any data leaves local memory." };
831
+ }
832
+ if (kind === "importcsv") {
833
+ if (typeof options.csv !== "string" || !options.csv.trim()) return { allowed: false, reason: "Reviewed csv content is required in options." };
834
+ if (options.name !== void 0 && !isnonempty(options.name)) return { allowed: false, reason: "The reviewed dataset name must be a non-empty string." };
835
+ if (options.mapping !== void 0) {
836
+ const mapping = options.mapping;
837
+ if (!mapping || typeof mapping !== "object" || Array.isArray(mapping) || !Object.values(mapping).every((item) => typeof item === "string")) return { allowed: false, reason: "The reviewed csv column mapping must be an object of string values." };
838
+ }
839
+ }
840
+ if (kind === "looprows") {
841
+ if (!isnonempty(options.dataset)) return { allowed: false, reason: "A reviewed dataset id is required in options." };
842
+ if (options.variable !== void 0 && !isnonempty(options.variable)) return { allowed: false, reason: "The reviewed row variable name must be a non-empty string." };
843
+ const inner = validateinnerstep(options, origin);
844
+ if (!inner.allowed) return inner;
845
+ }
846
+ if (kind === "transformvalues") {
847
+ if (!isnonempty(options.dataset)) return { allowed: false, reason: "A reviewed dataset id is required in options." };
848
+ const rules = options.rules;
849
+ if (!Array.isArray(rules) || rules.length === 0) return { allowed: false, reason: "A reviewed non-empty list of transform rules is required in options." };
850
+ for (const item of rules) {
851
+ const rulecheck = validatetransformrule(item);
852
+ if (!rulecheck.allowed) return rulecheck;
853
+ }
854
+ }
855
+ if (kind === "deduperows") {
856
+ if (!isnonempty(options.dataset)) return { allowed: false, reason: "A reviewed dataset id is required in options." };
857
+ const keys = options.keys;
858
+ if (!Array.isArray(keys) || keys.length === 0 || !keys.every((key) => isnonempty(key))) return { allowed: false, reason: "A reviewed non-empty list of dedupe column keys is required in options." };
859
+ }
860
+ if (kind === "mergepages") {
861
+ const listcheck = validatedatasetids(options, "datasets");
862
+ if (!listcheck.allowed) return listcheck;
863
+ }
864
+ if (kind === "stamplerows") {
865
+ if (!isnonempty(options.dataset)) return { allowed: false, reason: "A reviewed dataset id is required in options." };
866
+ if (options.url !== void 0 && !ishttpsurl(options.url)) return { allowed: false, reason: "The reviewed source url must use HTTPS." };
867
+ }
868
+ if (kind === "previewgrid") {
869
+ if (!isnonempty(options.dataset)) return { allowed: false, reason: "A reviewed dataset id is required in options." };
870
+ if (options.sample !== void 0 && (typeof options.sample !== "number" || !Number.isInteger(options.sample) || options.sample < 1)) return { allowed: false, reason: "The reviewed sample row count must be a positive integer with no code ceiling." };
871
+ }
872
+ if (kind === "resumeextract" && !isnonempty(options.session)) return { allowed: false, reason: "A reviewed extract session id is required in options." };
873
+ if (kind === "logprovenance" && !isnonempty(options.artifact)) return { allowed: false, reason: "A reviewed artifact id or name is required in options." };
874
+ return { allowed: true };
875
+ }
876
+ function submitreviewgranted(steps, submitid) {
877
+ const position = steps.findIndex((candidate) => candidate.id === submitid);
878
+ const asked = steps.some((candidate, index) => candidate.kind === "asksubmit" && (position === -1 || index < position));
879
+ return asked ? { allowed: true } : { allowed: false, reason: "Form submission requires an asksubmit review step before it." };
880
+ }
881
+ function passwordconsentgranted(step) {
882
+ let options = {};
883
+ try {
884
+ options = parseoptions(step);
885
+ } catch {
886
+ options = {};
887
+ }
888
+ const consentref = options.consentref;
889
+ if (typeof consentref !== "string" || !consentref.trim()) return { allowed: false, reason: "A password fill requires a reviewed consent ref in options." };
890
+ return { allowed: true };
891
+ }
892
+ function luhnvalid(digits) {
893
+ let sum = 0;
894
+ let double = false;
895
+ for (let index = digits.length - 1; index >= 0; index -= 1) {
896
+ let value = Number.parseInt(digits[index] ?? "", 10);
897
+ if (!Number.isFinite(value)) return false;
898
+ if (double) {
899
+ value *= 2;
900
+ if (value > 9) value -= 9;
901
+ }
902
+ sum += value;
903
+ double = !double;
904
+ }
905
+ return sum % 10 === 0;
906
+ }
907
+ function generatedvalueallowed(value) {
908
+ const compact = value.replace(/[\s-]/g, "");
909
+ if (/^\d{13,19}$/.test(compact) && luhnvalid(compact) && !compact.startsWith("4111")) return { allowed: false, reason: "The generated value looks like a real card number and is refused; generated card values use the 4111 test prefix." };
910
+ if (/^\d{3}-\d{2}-\d{4}$/.test(value.trim())) return { allowed: false, reason: "The generated value looks like a personal identifier and is refused." };
911
+ return { allowed: true };
912
+ }
913
+ function profilegrantgranted(profile, origin) {
914
+ if (!profile.grants.includes(origin)) return { allowed: false, reason: `The saved profile ${profile.name} is not granted to ${origin}; add the origin to the profile grants first.` };
915
+ return { allowed: true };
916
+ }
495
917
  function layoutmutationgranted(session, now) {
496
918
  if (!session || session.stoppedat || session.expiresat <= now) return { allowed: false, reason: "Group and layout mutations stay inside the active session." };
497
919
  return { allowed: true };
@@ -585,7 +1007,7 @@ function validateinnerstep(options, origin) {
585
1007
  return { allowed: true };
586
1008
  }
587
1009
  if (typeof kind !== "string" || !kind.trim()) return { allowed: false, reason: "A reviewed step id or inline step kind is required in options." };
588
- if (kind === "retryaction" || kind === "enterframe") return { allowed: false, reason: "The reviewed inner step cannot be another wrapper kind." };
1010
+ if (kind === "retryaction" || kind === "enterframe" || kind === "looprows") return { allowed: false, reason: "The reviewed inner step cannot be another wrapper kind." };
589
1011
  if (!allowedactions.has(kind)) return { allowed: false, reason: "The reviewed inner step kind is unsupported." };
590
1012
  const inneroptions = options.options;
591
1013
  if (inneroptions !== void 0 && (!inneroptions || typeof inneroptions !== "object" || Array.isArray(inneroptions))) return { allowed: false, reason: "The reviewed inner step options must be an object." };
@@ -965,6 +1387,14 @@ function validatestep(step, origin) {
965
1387
  const tabscheck = validatetabsgrammar(step, options);
966
1388
  if (!tabscheck.allowed) return tabscheck;
967
1389
  }
1390
+ if (isformkind(step.kind)) {
1391
+ const formcheck = validateformgrammar(step, options);
1392
+ if (!formcheck.allowed) return formcheck;
1393
+ }
1394
+ if (isdatasetkind(step.kind)) {
1395
+ const datacheck = validatedatagrammar(step, options, origin);
1396
+ if (!datacheck.allowed) return datacheck;
1397
+ }
968
1398
  if (step.kind === "tabcreate") {
969
1399
  if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
970
1400
  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." };
@@ -992,6 +1422,10 @@ function canexecute(input) {
992
1422
  if (input.plan.expiresat <= now) return { allowed: false, reason: "The approved plan has expired." };
993
1423
  if ((input.step.kind === "pierceshadow" || input.step.kind === "enterframe") && !origingranted(input.session, input.origin)) return { allowed: false, reason: "The shadow or frame step is outside the session origin grants." };
994
1424
  if (input.step.kind === "readjson" && !origingranted(input.session, input.origin)) return { allowed: false, reason: "The json state read is outside the session origin grants." };
1425
+ if (isexportkind(input.step.kind)) {
1426
+ const exportgate = exportgranted(input.session, input.origin);
1427
+ if (!exportgate.allowed) return exportgate;
1428
+ }
995
1429
  if (input.step.kind === "navlist") {
996
1430
  let options = {};
997
1431
  try {
@@ -1006,6 +1440,15 @@ function canexecute(input) {
1006
1440
  }
1007
1441
  }
1008
1442
  if (islayoutkind(input.step.kind) && !layoutmutationgranted(input.session, now).allowed) return { allowed: false, reason: "Group and layout mutations stay inside the active session." };
1443
+ if (input.step.kind === "submitform" || input.step.kind === "retryform") {
1444
+ if (!input.plan) return { allowed: false, reason: "Form submission requires an asksubmit review step before it." };
1445
+ const reviewgate = submitreviewgranted(input.plan.steps, input.step.id);
1446
+ if (!reviewgate.allowed) return reviewgate;
1447
+ }
1448
+ if (input.step.kind === "consentpassword") {
1449
+ const consentgate = passwordconsentgranted(input.step);
1450
+ if (!consentgate.allowed) return consentgate;
1451
+ }
1009
1452
  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") {
1010
1453
  let options = {};
1011
1454
  try {
@@ -1027,7 +1470,7 @@ function canexecute(input) {
1027
1470
  }
1028
1471
 
1029
1472
  // version.ts
1030
- var packageversion = "1.1.36";
1473
+ var packageversion = "1.1.38";
1031
1474
 
1032
1475
  // types.ts
1033
1476
  var protocolversion = packageversion;
@@ -1064,9 +1507,14 @@ function parseproposal(value, origin) {
1064
1507
  return step;
1065
1508
  });
1066
1509
  for (const step of steps) {
1067
- if (step.kind !== "retryaction" && step.kind !== "enterframe") continue;
1510
+ if (step.kind !== "retryaction" && step.kind !== "enterframe" && step.kind !== "looprows") continue;
1068
1511
  const options = parseoptions(step);
1069
- if (typeof options.stepid === "string" && !steps.some((candidate) => candidate.id === options.stepid)) throw new Error("A retry or frame wrapper references an unknown step id.");
1512
+ if (typeof options.stepid === "string" && !steps.some((candidate) => candidate.id === options.stepid)) throw new Error("A retry, frame or loop wrapper references an unknown step id.");
1513
+ }
1514
+ for (const step of steps) {
1515
+ if (step.kind !== "submitform" && step.kind !== "retryform") continue;
1516
+ const review = submitreviewgranted(steps, step.id);
1517
+ if (!review.allowed) throw new Error(review.reason);
1070
1518
  }
1071
1519
  const createdat = Date.now();
1072
1520
  const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
@@ -1131,13 +1579,42 @@ function tabreportresponse(input) {
1131
1579
  function layoutreport(input) {
1132
1580
  return { version: protocolversion, layouts: input.layouts };
1133
1581
  }
1582
+ function formreportresponse(input) {
1583
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, report: input.report });
1584
+ }
1585
+ function errorreportresponse(input) {
1586
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, report: input.report });
1587
+ }
1588
+ function wizardreport(input) {
1589
+ return { version: protocolversion, ...input.sessionid ? { sessionid: input.sessionid } : {}, wizards: input.wizards, picks: input.picks };
1590
+ }
1591
+ function datasetresponse(input) {
1592
+ const sample = Math.max(0, Math.floor(input.sample ?? 10));
1593
+ const payload = { ...input.dataset, rows: input.dataset.rows.slice(0, sample), totalrows: input.dataset.rows.length };
1594
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, dataset: payload });
1595
+ }
1596
+ function extractionreport(input) {
1597
+ return { version: protocolversion, sessions: input.sessions };
1598
+ }
1599
+ function provenancereport(input) {
1600
+ return { version: protocolversion, records: input.records };
1601
+ }
1602
+ function transformgrammar(rules) {
1603
+ return JSON.stringify({ rules: rules.map((rule) => ({ expression: rule.expression, sources: rule.sources, target: rule.target })) });
1604
+ }
1134
1605
  export {
1135
1606
  canexecute,
1607
+ datasetresponse,
1136
1608
  actionrisk as deriveactionrisk,
1137
1609
  diffresponse,
1610
+ errorreportresponse,
1138
1611
  eventresponse,
1612
+ extractionreport,
1613
+ formreportresponse,
1614
+ generatedvalueallowed,
1139
1615
  heldkeysreport,
1140
1616
  hostpattern,
1617
+ isformkind,
1141
1618
  iswatchkind,
1142
1619
  layoutreport,
1143
1620
  mapresponse,
@@ -1147,7 +1624,10 @@ export {
1147
1624
  observationresponse,
1148
1625
  outcomeresponse,
1149
1626
  parseproposal,
1627
+ passwordconsentgranted,
1628
+ profilegrantgranted,
1150
1629
  protocolversion,
1630
+ provenancereport,
1151
1631
  randomid,
1152
1632
  requestbody,
1153
1633
  resolutionverdict,
@@ -1155,9 +1635,15 @@ export {
1155
1635
  selectorresponse,
1156
1636
  sessionmemory,
1157
1637
  signalsreport,
1638
+ submitreviewgranted,
1158
1639
  tabreportresponse,
1159
1640
  trailreport,
1641
+ transformgrammar,
1642
+ validatefieldmatch,
1643
+ validateformrecord,
1160
1644
  validatestep,
1161
- validatetargetref
1645
+ validatetargetref,
1646
+ validatevaluegen,
1647
+ wizardreport
1162
1648
  };
1163
1649
  //# sourceMappingURL=index.js.map