@flowriki/agent 0.1.0-beta.20 → 0.1.0-beta.21

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.
@@ -1 +1 @@
1
- "use strict";(()=>{function U(e,t,i){function u(r){let o=e.__flAccName;if(o){let a=o(r);return a||t(r.placeholder)||t(r.title)||""}return b(r)}function b(r){let o=r.getAttribute&&r.getAttribute("aria-labelledby");if(o){let d=o.split(/\s+/).map(g=>document.getElementById(g)).filter(Boolean).map(g=>t(g.textContent)).filter(Boolean).join(" ");if(d)return d}let a=t(r.getAttribute&&r.getAttribute("aria-label"));if(a)return a;if(r.id){let d=document.querySelector('label[for="'+CSS.escape(r.id)+'"]');if(d&&t(d.textContent))return t(d.textContent)}let l=r.closest&&r.closest("label");if(l&&t(l.textContent))return t(l.textContent);let s=r.tagName.toLowerCase(),p=(r.type||"").toLowerCase();return(["button","a","summary"].includes(s)||r.getAttribute&&r.getAttribute("role")==="button")&&t(r.textContent)?t(r.textContent):s==="input"&&["submit","button","reset"].includes(p)&&r.value?t(r.value):t(r.placeholder)||t(r.title)||""}let S="input,select,textarea,button,meter,output,progress,[contenteditable]";function y(r){let o=r.parentElement;if(!o||!r.matches?.(S))return"";for(let a of Array.from(o.querySelectorAll("label"))){let l=a.getAttribute("for");if(l&&document.getElementById(l))continue;let s=t(a.textContent);if(s)return s}return""}let E=new WeakMap,P=crypto.randomUUID(),_=0;function T(r){let o=E.get(r);o||(o=`${P}:${++_}`,E.set(r,o));let a={__targetId:o},l=r.getRootNode(),s=r.getAttribute("data-testid");if(s&&l.querySelectorAll(`[data-testid=${JSON.stringify(s)}]`).length===1)return{...a,name:u(r),locator:{strategy:"testId",value:s}};if(r.type==="password"&&u(r))return{...a,name:u(r),locator:{strategy:"label",value:u(r)}};let p=r.getAttribute("title");if(p&&l.querySelectorAll(`[title=${JSON.stringify(p)}]`).length===1)return{...a,name:u(r),locator:{strategy:"title",value:p}};let d=u(r);if(d)return{...a,name:d};let g=y(r);if(g)return{...a,name:g,nameFrom:"adjacent-label"};let v=t(Array.from(r.childNodes||[]).filter(A=>A.nodeType===3).map(A=>A.textContent).join(" "));return v&&v.length<=80?{...a,name:v,nameFrom:"text"}:r.id&&!/:r|[a-f0-9]{12}|\d{6}/i.test(r.id)&&l.querySelectorAll(`#${CSS.escape(r.id)}`).length===1?{...a,name:"",locator:{strategy:"css",value:`#${CSS.escape(r.id)}`}}:{...a,name:""}}function N(r){let o=r.getAttribute&&r.getAttribute("role");if(o)return o;let a=r.tagName.toLowerCase(),l=(r.type||"").toLowerCase();return a==="button"?"button":a==="a"&&r.href?"link":a==="select"?r.multiple||r.size>1?"listbox":"combobox":a==="textarea"?"textbox":a==="input"?["submit","button","reset","file"].includes(l)?"button":l==="checkbox"||l==="radio"?l:l==="number"?"spinbutton":l==="search"?"searchbox":l==="range"?"slider":"textbox":r.isContentEditable?"textbox":H[a]??""}let H={li:"listitem",ul:"list",ol:"list",tr:"row",td:"cell",th:"columnheader",table:"table",nav:"navigation",main:"main",header:"banner",footer:"contentinfo",aside:"complementary",form:"form",dialog:"dialog",details:"group",fieldset:"group",img:"img",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading"};function j(r){let o=r,a=null;for(let l=0;o&&l<6&&!(o===document.body||o===document.documentElement);l++){let s="";try{s=getComputedStyle(o).cursor}catch{break}if(s==="pointer")a=o;else if(a)break;o=o.parentElement}return a}let C="tr, [role=row], li, [role=listitem], [role=article], [role=gridcell]";function L(r){try{let o=r.cloneNode(!0);for(let a of Array.from(o.querySelectorAll(i+", input, select, textarea")))a.remove();for(let a of Array.from(o.querySelectorAll("td, th, li, p, div, span, tr")))a.append(" ");return t(o.textContent).slice(0,120)}catch{return""}}function D(r,o,a){if(!a)return!1;let l;try{l=Array.from(document.querySelectorAll(i+", input, select, textarea"))}catch{return!1}if(l.length>400)return!0;let s=0;for(let p of l)if(p!==r&&N(p)===o&&T(p).name===a&&++s>0)return!0;return!1}function V(r,o,a){if(!D(r,o,a))return null;let l=r.closest&&r.closest(C);if(!l)return null;let s=L(l);if(!s)return null;let p=l.getAttribute&&l.getAttribute("role"),d=l.tagName.toLowerCase();return{scope:s,scopeRole:p||(d==="tr"?"row":d==="li"?"listitem":d)}}function x(r,o,a){let l=V(r,o,a);return l||{scope:n(r)}}function n(r){let o=r.closest&&r.closest("form, fieldset, section, [role=group], [role=region], [role=dialog], [role=alertdialog], dialog");return o?u(o):""}function f(r){if(r.id)return"#"+CSS.escape(r.id);let o=[],a=r;for(;a&&a.nodeType===1&&a!==document.body;){let l=Array.from(a.parentNode?a.parentNode.children:[]).filter(s=>s.tagName===a.tagName);o.unshift(l.length>1?`${a.tagName.toLowerCase()}:nth-of-type(${l.indexOf(a)+1})`:a.tagName.toLowerCase()),a=a.parentElement}return"body > "+o.join(" > ")}return{accName:u,identify:T,roleOf:N,pointerTarget:j,scoping:x,cssPath:f}}var ge=Object.prototype.toString;function Q(e){return typeof e=="function"||ge.call(e)==="[object Function]"}function ye(e){var t=Number(e);return isNaN(t)?0:t===0||!isFinite(t)?t:(t>0?1:-1)*Math.floor(Math.abs(t))}var he=Math.pow(2,53)-1;function ve(e){var t=ye(e);return Math.min(Math.max(t,0),he)}function h(e,t){var i=Array,u=Object(e);if(e==null)throw new TypeError("Array.from requires an array-like object - not null or undefined");if(typeof t<"u"&&!Q(t))throw new TypeError("Array.from: when provided, the second argument must be a function");for(var b=ve(u.length),S=Q(i)?Object(new i(b)):new Array(b),y=0,E;y<b;)E=u[y],t?S[y]=t(E,y):S[y]=E,y+=1;return S.length=b,S}function I(e){"@babel/helpers - typeof";return I=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},I(e)}function we(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function X(e,t){for(var i=0;i<t.length;i++){var u=t[i];u.enumerable=u.enumerable||!1,u.configurable=!0,"value"in u&&(u.writable=!0),Object.defineProperty(e,Y(u.key),u)}}function Se(e,t,i){return t&&X(e.prototype,t),i&&X(e,i),Object.defineProperty(e,"prototype",{writable:!1}),e}function Ee(e,t,i){return t=Y(t),t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function Y(e){var t=xe(e,"string");return I(t)=="symbol"?t:t+""}function xe(e,t){if(I(e)!="object"||!e)return e;var i=e[Symbol.toPrimitive];if(i!==void 0){var u=i.call(e,t||"default");if(I(u)!="object")return u;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var Ae=(function(){function e(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[];we(this,e),Ee(this,"items",void 0),this.items=t}return Se(e,[{key:"add",value:function(i){return this.has(i)===!1&&this.items.push(i),this}},{key:"clear",value:function(){this.items=[]}},{key:"delete",value:function(i){var u=this.items.length;return this.items=this.items.filter(function(b){return b!==i}),u!==this.items.length}},{key:"forEach",value:function(i){var u=this;this.items.forEach(function(b){i(b,b,u)})}},{key:"has",value:function(i){return this.items.indexOf(i)!==-1}},{key:"size",get:function(){return this.items.length}}])})(),Z=typeof Set>"u"?Set:Ae;function m(e){var t;return(t=e.localName)!==null&&t!==void 0?t:e.tagName.toLowerCase()}var Le={article:"article",aside:"complementary",button:"button",datalist:"listbox",dd:"definition",details:"group",dialog:"dialog",dt:"term",fieldset:"group",figure:"figure",form:"form",footer:"contentinfo",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading",header:"banner",hr:"separator",html:"document",legend:"legend",li:"listitem",math:"math",main:"main",menu:"list",nav:"navigation",ol:"list",optgroup:"group",option:"option",output:"status",progress:"progressbar",section:"region",summary:"button",table:"table",tbody:"rowgroup",textarea:"textbox",tfoot:"rowgroup",td:"cell",th:"columnheader",thead:"rowgroup",tr:"row",ul:"list"},Te={caption:new Set(["aria-label","aria-labelledby"]),code:new Set(["aria-label","aria-labelledby"]),deletion:new Set(["aria-label","aria-labelledby"]),emphasis:new Set(["aria-label","aria-labelledby"]),generic:new Set(["aria-label","aria-labelledby","aria-roledescription"]),insertion:new Set(["aria-label","aria-labelledby"]),none:new Set(["aria-label","aria-labelledby"]),paragraph:new Set(["aria-label","aria-labelledby"]),presentation:new Set(["aria-label","aria-labelledby"]),strong:new Set(["aria-label","aria-labelledby"]),subscript:new Set(["aria-label","aria-labelledby"]),superscript:new Set(["aria-label","aria-labelledby"])};function Ne(e,t){return["aria-atomic","aria-busy","aria-controls","aria-current","aria-description","aria-describedby","aria-details","aria-dropeffect","aria-flowto","aria-grabbed","aria-hidden","aria-keyshortcuts","aria-label","aria-labelledby","aria-live","aria-owns","aria-relevant","aria-roledescription"].some(function(i){var u;return e.hasAttribute(i)&&!((u=Te[t])!==null&&u!==void 0&&u.has(i))})}function ee(e,t){return Ne(e,t)}function k(e){var t=Ie(e);if(t===null||O.indexOf(t)!==-1){var i=Ce(e);if(O.indexOf(t||"")===-1||ee(e,i||""))return i}return t}function Ce(e){var t=Le[m(e)];if(t!==void 0)return t;switch(m(e)){case"a":case"area":case"link":if(e.hasAttribute("href"))return"link";break;case"img":return e.getAttribute("alt")===""&&!ee(e,"img")?"presentation":"img";case"input":{var i=e,u=i.type;switch(u){case"button":case"image":case"reset":case"submit":return"button";case"checkbox":case"radio":return u;case"range":return"slider";case"email":case"tel":case"text":case"url":return e.hasAttribute("list")?"combobox":"textbox";case"search":return e.hasAttribute("list")?"combobox":"searchbox";case"number":return"spinbutton";default:return null}}case"select":return e.hasAttribute("multiple")||e.size>1?"listbox":"combobox"}return null}function Ie(e){var t=e.getAttribute("role");if(t!==null){var i=t.trim().split(" ")[0];if(i.length>0)return i}return null}var O=["presentation","none"];function c(e){return e!==null&&e.nodeType===e.ELEMENT_NODE}function G(e){return c(e)&&m(e)==="caption"}function R(e){return c(e)&&m(e)==="input"}function te(e){return c(e)&&m(e)==="optgroup"}function re(e){return c(e)&&m(e)==="select"}function ne(e){return c(e)&&m(e)==="table"}function ie(e){return c(e)&&m(e)==="textarea"}function ae(e){var t=e.ownerDocument===null?e:e.ownerDocument,i=t.defaultView;if(i===null)throw new TypeError("no window available");return i}function oe(e){return c(e)&&m(e)==="fieldset"}function ue(e){return c(e)&&m(e)==="legend"}function le(e){return c(e)&&m(e)==="slot"}function Oe(e){return c(e)&&e.ownerSVGElement!==void 0}function se(e){return c(e)&&m(e)==="svg"}function fe(e){return Oe(e)&&m(e)==="title"}function M(e,t){if(c(e)&&e.hasAttribute(t)){var i=e.getAttribute(t).split(" "),u=e.getRootNode?e.getRootNode():e.ownerDocument;return i.map(function(b){return u.getElementById(b)}).filter(function(b){return b!==null})}return[]}function w(e,t){return c(e)?t.indexOf(k(e))!==-1:!1}function Re(e){return e.trim().replace(/\s\s+/g," ")}function Me(e,t){if(!c(e))return!1;if(e.hasAttribute("hidden")||e.getAttribute("aria-hidden")==="true")return!0;var i=t(e);return i.getPropertyValue("display")==="none"||i.getPropertyValue("visibility")==="hidden"}function Pe(e){return w(e,["button","combobox","listbox","textbox"])||me(e,"range")}function me(e,t){if(!c(e))return!1;if(t==="range")return w(e,["meter","progressbar","scrollbar","slider","spinbutton"]);throw new TypeError("No knowledge about abstract role '".concat(t,"'. This is likely a bug :("))}function ce(e,t){var i=h(e.querySelectorAll(t));return M(e,"aria-owns").forEach(function(u){i.push.apply(i,h(u.querySelectorAll(t)))}),i}function _e(e){return re(e)?e.selectedOptions||ce(e,"[selected]"):ce(e,'[aria-selected="true"]')}function je(e){return w(e,O)}function ke(e){return G(e)}function He(e){return w(e,["button","cell","checkbox","columnheader","gridcell","heading","label","legend","link","menuitem","menuitemcheckbox","menuitemradio","option","radio","row","rowheader","switch","tab","tooltip","treeitem"])}function De(e){return!1}function Ve(e){return R(e)||ie(e)?e.value:e.textContent||""}function de(e){var t=e.getPropertyValue("content");return/^["'].*["']$/.test(t)?t.slice(1,-1):""}function pe(e){var t=m(e);return t==="button"||t==="input"&&e.getAttribute("type")!=="hidden"||t==="meter"||t==="output"||t==="progress"||t==="select"||t==="textarea"}function be(e){if(pe(e))return e;var t=null;return e.childNodes.forEach(function(i){if(t===null&&c(i)){var u=be(i);u!==null&&(t=u)}}),t}function $e(e){if(e.control!==void 0)return e.control;var t=e.getAttribute("for");return t!==null?e.ownerDocument.getElementById(t):be(e)}function qe(e){var t=e.labels;if(t===null)return t;if(t!==void 0)return h(t);if(!pe(e))return null;var i=e.ownerDocument;return h(i.querySelectorAll("label")).filter(function(u){return $e(u)===e})}function Ge(e){var t=e.assignedNodes();return t.length===0?h(e.childNodes):t}function B(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=new Z,u=typeof Map>"u"?void 0:new Map,b=ae(e),S=t.compute,y=S===void 0?"name":S,E=t.computedStyleSupportsPseudoElements,P=E===void 0?t.getComputedStyle!==void 0:E,_=t.getComputedStyle,T=_===void 0?b.getComputedStyle.bind(b):_,N=t.hidden,H=N===void 0?!1:N,j=function(f,r){if(r!==void 0)throw new Error("use uncachedGetComputedStyle directly for pseudo elements");if(u===void 0)return T(f);var o=u.get(f);if(o)return o;var a=T(f,r);return u.set(f,a),a};function C(n,f){var r="";if(c(n)&&P){var o=T(n,"::before"),a=de(o);r="".concat(a," ").concat(r)}var l=le(n)?Ge(n):h(n.childNodes).concat(M(n,"aria-owns"));if(l.forEach(function(d){var g=x(d,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0}),v=c(d)?j(d).getPropertyValue("display"):"inline",A=v!=="inline"?" ":"";r+="".concat(A).concat(g).concat(A)}),c(n)&&P){var s=T(n,"::after"),p=de(s);r="".concat(r," ").concat(p)}return r.trim()}function L(n,f){var r=n.getAttributeNode(f);return r!==null&&!i.has(r)&&r.value.trim()!==""?(i.add(r),r.value):null}function D(n){return c(n)?L(n,"title"):null}function V(n){if(!c(n))return null;if(oe(n)){i.add(n);for(var f=h(n.childNodes),r=0;r<f.length;r+=1){var o=f[r];if(ue(o))return x(o,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(ne(n)){i.add(n);for(var a=h(n.childNodes),l=0;l<a.length;l+=1){var s=a[l];if(G(s))return x(s,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(se(n)){i.add(n);for(var p=h(n.childNodes),d=0;d<p.length;d+=1){var g=p[d];if(fe(g))return g.textContent}return null}else if(m(n)==="img"||m(n)==="area"){var v=L(n,"alt");if(v!==null)return v}else if(te(n)){var A=L(n,"label");if(A!==null)return A}if(R(n)&&(n.type==="button"||n.type==="submit"||n.type==="reset")){var z=L(n,"value");if(z!==null)return z;if(n.type==="submit")return"Submit";if(n.type==="reset")return"Reset"}var $=qe(n);if($!==null&&$.length!==0)return i.add(n),h($).map(function(q){return x(q,{isEmbeddedInLabel:!0,isReferenced:!1,recursion:!0})}).filter(function(q){return q.length>0}).join(" ");if(R(n)&&n.type==="image"){var F=L(n,"alt");if(F!==null)return F;var J=L(n,"title");return J!==null?J:"Submit Query"}if(w(n,["button"])){var K=C(n,{isEmbeddedInLabel:!1,isReferenced:!1});if(K!=="")return K}return null}function x(n,f){if(i.has(n))return"";if(!H&&Me(n,j)&&!f.isReferenced)return i.add(n),"";var r=c(n)?n.getAttributeNode("aria-labelledby"):null,o=r!==null&&!i.has(r)?M(n,"aria-labelledby"):[];if(y==="name"&&!f.isReferenced&&o.length>0)return i.add(r),o.map(function(v){return x(v,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!0,recursion:!1})}).join(" ");var a=f.recursion&&Pe(n)&&y==="name";if(!a){var l=(c(n)&&n.getAttribute("aria-label")||"").trim();if(l!==""&&y==="name")return i.add(n),l;if(!je(n)){var s=V(n);if(s!==null)return i.add(n),s}}if(w(n,["menu"]))return i.add(n),"";if(a||f.isEmbeddedInLabel||f.isReferenced){if(w(n,["combobox","listbox"])){i.add(n);var p=_e(n);return p.length===0?R(n)?n.value:"":h(p).map(function(v){return x(v,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0})}).join(" ")}if(me(n,"range"))return i.add(n),n.hasAttribute("aria-valuetext")?n.getAttribute("aria-valuetext"):n.hasAttribute("aria-valuenow")?n.getAttribute("aria-valuenow"):n.getAttribute("value")||"";if(w(n,["textbox"]))return i.add(n),Ve(n)}if(He(n)||c(n)&&f.isReferenced||ke(n)||De(n)){var d=C(n,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});if(d!=="")return i.add(n),d}if(n.nodeType===n.TEXT_NODE)return i.add(n),n.textContent||"";if(f.recursion)return i.add(n),C(n,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});var g=D(n);return g!==null?(i.add(n),g):(i.add(n),"")}return Re(x(e,{isEmbeddedInLabel:!1,isReferenced:y==="description",recursion:!1}))}function Be(e){return w(e,["caption","code","deletion","emphasis","generic","insertion","none","paragraph","presentation","strong","subscript","superscript"])}function W(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return Be(e)?"":B(e,t)}window.__flAccName=e=>{try{return W(e).replace(/\s+/g," ").trim()}catch{return""}};window.__flCreateTargetIdentity=U;})();
1
+ "use strict";(()=>{function U(e,t,i){function l(n){let u=e.__flAccName;if(u){let a=u(n);return a||t(n.placeholder)||t(n.title)||""}return y(n)}function y(n){let u=n.getAttribute&&n.getAttribute("aria-labelledby");if(u){let m=u.split(/\s+/).map(b=>document.getElementById(b)).filter(Boolean).map(b=>t(b.textContent)).filter(Boolean).join(" ");if(m)return m}let a=t(n.getAttribute&&n.getAttribute("aria-label"));if(a)return a;if(n.id){let m=document.querySelector('label[for="'+CSS.escape(n.id)+'"]');if(m&&t(m.textContent))return t(m.textContent)}let o=n.closest&&n.closest("label");if(o&&t(o.textContent))return t(o.textContent);let s=n.tagName.toLowerCase(),p=(n.type||"").toLowerCase();return(["button","a","summary"].includes(s)||n.getAttribute&&n.getAttribute("role")==="button")&&t(n.textContent)?t(n.textContent):s==="input"&&["submit","button","reset"].includes(p)&&n.value?t(n.value):t(n.placeholder)||t(n.title)||""}let E="input,select,textarea,button,meter,output,progress,[contenteditable]";function v(n){let u=n.parentElement;if(!u||!n.matches?.(E))return"";for(let a of Array.from(u.querySelectorAll("label"))){let o=a.getAttribute("for");if(o&&document.getElementById(o))continue;let s=t(a.textContent);if(s)return s}return""}let x=new WeakMap,_=crypto.randomUUID(),j=0;function L(n){let u=n.getRootNode(),a=h=>u.querySelectorAll(h).length===1,o=[],s=n.getAttribute?.("data-testid");s&&a(`[data-testid=${JSON.stringify(s)}]`)&&o.push({strategy:"testId",value:s});let p=t(n.placeholder);p&&a(`[placeholder=${JSON.stringify(n.placeholder)}]`)&&o.push({strategy:"placeholder",value:p});let m=t(n.getAttribute?.("alt"));m&&a(`[alt=${JSON.stringify(n.getAttribute("alt"))}]`)&&o.push({strategy:"altText",value:m});let b=n.getAttribute?.("title");return b&&a(`[title=${JSON.stringify(b)}]`)&&o.push({strategy:"title",value:t(b)}),n.id&&!/:r|[a-f0-9]{12}|\d{6}/i.test(n.id)&&a(`#${CSS.escape(n.id)}`)&&o.push({strategy:"css",value:`#${CSS.escape(n.id)}`}),o.slice(0,4)}function C(n){let u=x.get(n);u||(u=`${_}:${++j}`,x.set(n,u));let a=L(n),o={__targetId:u,...a.length?{candidates:a}:{}},s=n.getRootNode(),p=l(n);if(p)return{...o,name:p};let m=v(n);if(m)return{...o,name:m,nameFrom:"adjacent-label"};let b=t(Array.from(n.childNodes||[]).filter(N=>N.nodeType===3).map(N=>N.textContent).join(" "));if(b&&b.length<=80)return{...o,name:b,nameFrom:"text"};let h=a[0];return h?{...o,name:"",locator:h.strategy==="placeholder"||h.strategy==="altText"?{strategy:"css",value:`[${h.strategy==="altText"?"alt":"placeholder"}=${JSON.stringify(h.value)}]`}:h}:{...o,name:""}}function k(n){let u=n.getAttribute&&n.getAttribute("role");if(u)return u;let a=n.tagName.toLowerCase(),o=(n.type||"").toLowerCase();return a==="button"?"button":a==="a"&&n.href?"link":a==="select"?n.multiple||n.size>1?"listbox":"combobox":a==="textarea"?"textbox":a==="input"?["submit","button","reset","file"].includes(o)?"button":o==="checkbox"||o==="radio"?o:o==="number"?"spinbutton":o==="search"?"searchbox":o==="range"?"slider":"textbox":n.isContentEditable?"textbox":H[a]??""}let H={li:"listitem",ul:"list",ol:"list",tr:"row",td:"cell",th:"columnheader",table:"table",nav:"navigation",main:"main",header:"banner",footer:"contentinfo",aside:"complementary",form:"form",dialog:"dialog",details:"group",fieldset:"group",img:"img",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading"};function I(n){let u=n,a=null;for(let o=0;u&&o<6&&!(u===document.body||u===document.documentElement);o++){let s="";try{s=getComputedStyle(u).cursor}catch{break}if(s==="pointer")a=u;else if(a)break;u=u.parentElement}return a}let T="tr, [role=row], li, [role=listitem], [role=article], [role=gridcell]";function $(n){try{let u=n.cloneNode(!0);for(let a of Array.from(u.querySelectorAll(i+", input, select, textarea")))a.remove();for(let a of Array.from(u.querySelectorAll("td, th, li, p, div, span, tr")))a.append(" ");return t(u.textContent).slice(0,120)}catch{return""}}function V(n,u,a){if(!a)return!1;let o;try{o=Array.from(document.querySelectorAll(i+", input, select, textarea"))}catch{return!1}if(o.length>400)return!0;let s=0;for(let p of o)if(p!==n&&k(p)===u&&C(p).name===a&&++s>0)return!0;return!1}function A(n,u,a){if(!V(n,u,a))return null;let o=n.closest&&n.closest(T);if(!o)return null;let s=$(o);if(!s)return null;let p=o.getAttribute&&o.getAttribute("role"),m=o.tagName.toLowerCase();return{scope:s,scopeRole:p||(m==="tr"?"row":m==="li"?"listitem":m)}}function r(n,u,a){let o=A(n,u,a);return o||{scope:f(n)}}function f(n){let u=n.closest&&n.closest("form, fieldset, section, [role=group], [role=region], [role=dialog], [role=alertdialog], dialog");return u?l(u):""}function c(n){if(n.id)return"#"+CSS.escape(n.id);let u=[],a=n;for(;a&&a.nodeType===1&&a!==document.body;){let o=Array.from(a.parentNode?a.parentNode.children:[]).filter(s=>s.tagName===a.tagName);u.unshift(o.length>1?`${a.tagName.toLowerCase()}:nth-of-type(${o.indexOf(a)+1})`:a.tagName.toLowerCase()),a=a.parentElement}return"body > "+u.join(" > ")}return{accName:l,identify:C,roleOf:k,pointerTarget:I,scoping:r,cssPath:c}}var ge=Object.prototype.toString;function Q(e){return typeof e=="function"||ge.call(e)==="[object Function]"}function ye(e){var t=Number(e);return isNaN(t)?0:t===0||!isFinite(t)?t:(t>0?1:-1)*Math.floor(Math.abs(t))}var he=Math.pow(2,53)-1;function ve(e){var t=ye(e);return Math.min(Math.max(t,0),he)}function w(e,t){var i=Array,l=Object(e);if(e==null)throw new TypeError("Array.from requires an array-like object - not null or undefined");if(typeof t<"u"&&!Q(t))throw new TypeError("Array.from: when provided, the second argument must be a function");for(var y=ve(l.length),E=Q(i)?Object(new i(y)):new Array(y),v=0,x;v<y;)x=l[v],t?E[v]=t(x,v):E[v]=x,v+=1;return E.length=y,E}function O(e){"@babel/helpers - typeof";return O=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},O(e)}function we(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function X(e,t){for(var i=0;i<t.length;i++){var l=t[i];l.enumerable=l.enumerable||!1,l.configurable=!0,"value"in l&&(l.writable=!0),Object.defineProperty(e,Y(l.key),l)}}function Se(e,t,i){return t&&X(e.prototype,t),i&&X(e,i),Object.defineProperty(e,"prototype",{writable:!1}),e}function Ee(e,t,i){return t=Y(t),t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function Y(e){var t=xe(e,"string");return O(t)=="symbol"?t:t+""}function xe(e,t){if(O(e)!="object"||!e)return e;var i=e[Symbol.toPrimitive];if(i!==void 0){var l=i.call(e,t||"default");if(O(l)!="object")return l;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var Ae=(function(){function e(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[];we(this,e),Ee(this,"items",void 0),this.items=t}return Se(e,[{key:"add",value:function(i){return this.has(i)===!1&&this.items.push(i),this}},{key:"clear",value:function(){this.items=[]}},{key:"delete",value:function(i){var l=this.items.length;return this.items=this.items.filter(function(y){return y!==i}),l!==this.items.length}},{key:"forEach",value:function(i){var l=this;this.items.forEach(function(y){i(y,y,l)})}},{key:"has",value:function(i){return this.items.indexOf(i)!==-1}},{key:"size",get:function(){return this.items.length}}])})(),Z=typeof Set>"u"?Set:Ae;function g(e){var t;return(t=e.localName)!==null&&t!==void 0?t:e.tagName.toLowerCase()}var Te={article:"article",aside:"complementary",button:"button",datalist:"listbox",dd:"definition",details:"group",dialog:"dialog",dt:"term",fieldset:"group",figure:"figure",form:"form",footer:"contentinfo",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading",header:"banner",hr:"separator",html:"document",legend:"legend",li:"listitem",math:"math",main:"main",menu:"list",nav:"navigation",ol:"list",optgroup:"group",option:"option",output:"status",progress:"progressbar",section:"region",summary:"button",table:"table",tbody:"rowgroup",textarea:"textbox",tfoot:"rowgroup",td:"cell",th:"columnheader",thead:"rowgroup",tr:"row",ul:"list"},Le={caption:new Set(["aria-label","aria-labelledby"]),code:new Set(["aria-label","aria-labelledby"]),deletion:new Set(["aria-label","aria-labelledby"]),emphasis:new Set(["aria-label","aria-labelledby"]),generic:new Set(["aria-label","aria-labelledby","aria-roledescription"]),insertion:new Set(["aria-label","aria-labelledby"]),none:new Set(["aria-label","aria-labelledby"]),paragraph:new Set(["aria-label","aria-labelledby"]),presentation:new Set(["aria-label","aria-labelledby"]),strong:new Set(["aria-label","aria-labelledby"]),subscript:new Set(["aria-label","aria-labelledby"]),superscript:new Set(["aria-label","aria-labelledby"])};function Ne(e,t){return["aria-atomic","aria-busy","aria-controls","aria-current","aria-description","aria-describedby","aria-details","aria-dropeffect","aria-flowto","aria-grabbed","aria-hidden","aria-keyshortcuts","aria-label","aria-labelledby","aria-live","aria-owns","aria-relevant","aria-roledescription"].some(function(i){var l;return e.hasAttribute(i)&&!((l=Le[t])!==null&&l!==void 0&&l.has(i))})}function ee(e,t){return Ne(e,t)}function D(e){var t=Ie(e);if(t===null||R.indexOf(t)!==-1){var i=Ce(e);if(R.indexOf(t||"")===-1||ee(e,i||""))return i}return t}function Ce(e){var t=Te[g(e)];if(t!==void 0)return t;switch(g(e)){case"a":case"area":case"link":if(e.hasAttribute("href"))return"link";break;case"img":return e.getAttribute("alt")===""&&!ee(e,"img")?"presentation":"img";case"input":{var i=e,l=i.type;switch(l){case"button":case"image":case"reset":case"submit":return"button";case"checkbox":case"radio":return l;case"range":return"slider";case"email":case"tel":case"text":case"url":return e.hasAttribute("list")?"combobox":"textbox";case"search":return e.hasAttribute("list")?"combobox":"searchbox";case"number":return"spinbutton";default:return null}}case"select":return e.hasAttribute("multiple")||e.size>1?"listbox":"combobox"}return null}function Ie(e){var t=e.getAttribute("role");if(t!==null){var i=t.trim().split(" ")[0];if(i.length>0)return i}return null}var R=["presentation","none"];function d(e){return e!==null&&e.nodeType===e.ELEMENT_NODE}function B(e){return d(e)&&g(e)==="caption"}function M(e){return d(e)&&g(e)==="input"}function te(e){return d(e)&&g(e)==="optgroup"}function re(e){return d(e)&&g(e)==="select"}function ne(e){return d(e)&&g(e)==="table"}function ie(e){return d(e)&&g(e)==="textarea"}function ae(e){var t=e.ownerDocument===null?e:e.ownerDocument,i=t.defaultView;if(i===null)throw new TypeError("no window available");return i}function oe(e){return d(e)&&g(e)==="fieldset"}function ue(e){return d(e)&&g(e)==="legend"}function le(e){return d(e)&&g(e)==="slot"}function Oe(e){return d(e)&&e.ownerSVGElement!==void 0}function se(e){return d(e)&&g(e)==="svg"}function fe(e){return Oe(e)&&g(e)==="title"}function P(e,t){if(d(e)&&e.hasAttribute(t)){var i=e.getAttribute(t).split(" "),l=e.getRootNode?e.getRootNode():e.ownerDocument;return i.map(function(y){return l.getElementById(y)}).filter(function(y){return y!==null})}return[]}function S(e,t){return d(e)?t.indexOf(D(e))!==-1:!1}function Re(e){return e.trim().replace(/\s\s+/g," ")}function Me(e,t){if(!d(e))return!1;if(e.hasAttribute("hidden")||e.getAttribute("aria-hidden")==="true")return!0;var i=t(e);return i.getPropertyValue("display")==="none"||i.getPropertyValue("visibility")==="hidden"}function Pe(e){return S(e,["button","combobox","listbox","textbox"])||pe(e,"range")}function pe(e,t){if(!d(e))return!1;if(t==="range")return S(e,["meter","progressbar","scrollbar","slider","spinbutton"]);throw new TypeError("No knowledge about abstract role '".concat(t,"'. This is likely a bug :("))}function ce(e,t){var i=w(e.querySelectorAll(t));return P(e,"aria-owns").forEach(function(l){i.push.apply(i,w(l.querySelectorAll(t)))}),i}function _e(e){return re(e)?e.selectedOptions||ce(e,"[selected]"):ce(e,'[aria-selected="true"]')}function je(e){return S(e,R)}function ke(e){return B(e)}function He(e){return S(e,["button","cell","checkbox","columnheader","gridcell","heading","label","legend","link","menuitem","menuitemcheckbox","menuitemradio","option","radio","row","rowheader","switch","tab","tooltip","treeitem"])}function De(e){return!1}function $e(e){return M(e)||ie(e)?e.value:e.textContent||""}function de(e){var t=e.getPropertyValue("content");return/^["'].*["']$/.test(t)?t.slice(1,-1):""}function me(e){var t=g(e);return t==="button"||t==="input"&&e.getAttribute("type")!=="hidden"||t==="meter"||t==="output"||t==="progress"||t==="select"||t==="textarea"}function be(e){if(me(e))return e;var t=null;return e.childNodes.forEach(function(i){if(t===null&&d(i)){var l=be(i);l!==null&&(t=l)}}),t}function Ve(e){if(e.control!==void 0)return e.control;var t=e.getAttribute("for");return t!==null?e.ownerDocument.getElementById(t):be(e)}function qe(e){var t=e.labels;if(t===null)return t;if(t!==void 0)return w(t);if(!me(e))return null;var i=e.ownerDocument;return w(i.querySelectorAll("label")).filter(function(l){return Ve(l)===e})}function Ge(e){var t=e.assignedNodes();return t.length===0?w(e.childNodes):t}function J(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=new Z,l=typeof Map>"u"?void 0:new Map,y=ae(e),E=t.compute,v=E===void 0?"name":E,x=t.computedStyleSupportsPseudoElements,_=x===void 0?t.getComputedStyle!==void 0:x,j=t.getComputedStyle,L=j===void 0?y.getComputedStyle.bind(y):j,C=t.hidden,k=C===void 0?!1:C,H=function(f,c){if(c!==void 0)throw new Error("use uncachedGetComputedStyle directly for pseudo elements");if(l===void 0)return L(f);var n=l.get(f);if(n)return n;var u=L(f,c);return l.set(f,u),u};function I(r,f){var c="";if(d(r)&&_){var n=L(r,"::before"),u=de(n);c="".concat(u," ").concat(c)}var a=le(r)?Ge(r):w(r.childNodes).concat(P(r,"aria-owns"));if(a.forEach(function(p){var m=A(p,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0}),b=d(p)?H(p).getPropertyValue("display"):"inline",h=b!=="inline"?" ":"";c+="".concat(h).concat(m).concat(h)}),d(r)&&_){var o=L(r,"::after"),s=de(o);c="".concat(c," ").concat(s)}return c.trim()}function T(r,f){var c=r.getAttributeNode(f);return c!==null&&!i.has(c)&&c.value.trim()!==""?(i.add(c),c.value):null}function $(r){return d(r)?T(r,"title"):null}function V(r){if(!d(r))return null;if(oe(r)){i.add(r);for(var f=w(r.childNodes),c=0;c<f.length;c+=1){var n=f[c];if(ue(n))return A(n,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(ne(r)){i.add(r);for(var u=w(r.childNodes),a=0;a<u.length;a+=1){var o=u[a];if(B(o))return A(o,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(se(r)){i.add(r);for(var s=w(r.childNodes),p=0;p<s.length;p+=1){var m=s[p];if(fe(m))return m.textContent}return null}else if(g(r)==="img"||g(r)==="area"){var b=T(r,"alt");if(b!==null)return b}else if(te(r)){var h=T(r,"label");if(h!==null)return h}if(M(r)&&(r.type==="button"||r.type==="submit"||r.type==="reset")){var N=T(r,"value");if(N!==null)return N;if(r.type==="submit")return"Submit";if(r.type==="reset")return"Reset"}var q=qe(r);if(q!==null&&q.length!==0)return i.add(r),w(q).map(function(G){return A(G,{isEmbeddedInLabel:!0,isReferenced:!1,recursion:!0})}).filter(function(G){return G.length>0}).join(" ");if(M(r)&&r.type==="image"){var z=T(r,"alt");if(z!==null)return z;var F=T(r,"title");return F!==null?F:"Submit Query"}if(S(r,["button"])){var K=I(r,{isEmbeddedInLabel:!1,isReferenced:!1});if(K!=="")return K}return null}function A(r,f){if(i.has(r))return"";if(!k&&Me(r,H)&&!f.isReferenced)return i.add(r),"";var c=d(r)?r.getAttributeNode("aria-labelledby"):null,n=c!==null&&!i.has(c)?P(r,"aria-labelledby"):[];if(v==="name"&&!f.isReferenced&&n.length>0)return i.add(c),n.map(function(b){return A(b,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!0,recursion:!1})}).join(" ");var u=f.recursion&&Pe(r)&&v==="name";if(!u){var a=(d(r)&&r.getAttribute("aria-label")||"").trim();if(a!==""&&v==="name")return i.add(r),a;if(!je(r)){var o=V(r);if(o!==null)return i.add(r),o}}if(S(r,["menu"]))return i.add(r),"";if(u||f.isEmbeddedInLabel||f.isReferenced){if(S(r,["combobox","listbox"])){i.add(r);var s=_e(r);return s.length===0?M(r)?r.value:"":w(s).map(function(b){return A(b,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0})}).join(" ")}if(pe(r,"range"))return i.add(r),r.hasAttribute("aria-valuetext")?r.getAttribute("aria-valuetext"):r.hasAttribute("aria-valuenow")?r.getAttribute("aria-valuenow"):r.getAttribute("value")||"";if(S(r,["textbox"]))return i.add(r),$e(r)}if(He(r)||d(r)&&f.isReferenced||ke(r)||De(r)){var p=I(r,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});if(p!=="")return i.add(r),p}if(r.nodeType===r.TEXT_NODE)return i.add(r),r.textContent||"";if(f.recursion)return i.add(r),I(r,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});var m=$(r);return m!==null?(i.add(r),m):(i.add(r),"")}return Re(A(e,{isEmbeddedInLabel:!1,isReferenced:v==="description",recursion:!1}))}function Be(e){return S(e,["caption","code","deletion","emphasis","generic","insertion","none","paragraph","presentation","strong","subscript","superscript"])}function W(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return Be(e)?"":J(e,t)}window.__flAccName=e=>{try{return W(e).replace(/\s+/g," ").trim()}catch{return""}};window.__flCreateTargetIdentity=U;})();
@@ -6,7 +6,7 @@ import os from "os";
6
6
  import path from "path";
7
7
  import { createRequire } from "module";
8
8
  import { spawnSync } from "child_process";
9
- var AGENT_VERSION = "0.1.0-beta.20";
9
+ var AGENT_VERSION = "0.1.0-beta.21";
10
10
  async function prepareBrowsers() {
11
11
  if (browsersReady()) {
12
12
  return { ok: true, already: true, message: "chromium is already installed" };
@@ -458,7 +458,7 @@ import path7 from "path";
458
458
  // ../engine-playwright/dist/index.js
459
459
  import path3 from "path";
460
460
 
461
- // ../test-model/dist/chunk-ZRUZU4TI.js
461
+ // ../test-model/dist/chunk-RQ5E3H3R.js
462
462
  import { z as z3 } from "zod";
463
463
  var LocatorOverrideSchema = z3.object({
464
464
  strategy: z3.enum(["css", "xpath", "id", "testId", "text", "label", "placeholder", "altText", "title"]),
@@ -524,7 +524,30 @@ var RecordedTargetSchema = z3.object({
524
524
  frame: z3.array(z3.string()).optional(),
525
525
  pageId: z3.string().optional(),
526
526
  nameFrom: z3.enum(["adjacent-label", "text"]).optional(),
527
+ /** SET BY A PERSON, and exclusive.
528
+ *
529
+ * Somebody opened the editor and said "find it this way instead", which is
530
+ * a decision about semantics that were wrong for this element. Falling back
531
+ * to those semantics would undo it, so this replaces resolution entirely.
532
+ *
533
+ * Steps recorded before `candidates` existed also carry the recorder's own
534
+ * pick here, and are treated the same way: nothing in the document says
535
+ * which of the two put it there, and guessing wrong in the other direction
536
+ * would change what somebody's test does. */
527
537
  locator: LocatorOverrideSchema.optional(),
538
+ /** WHAT THE RECORDER SAW, best first — preferences, not assertions.
539
+ *
540
+ * One locator is one way to be wrong. A test id is removed, a placeholder
541
+ * is reworded, a wrapper is added, and a step that had exactly one way to
542
+ * find its element has none — although the field is sitting in plain sight
543
+ * and two other things about it are unchanged.
544
+ *
545
+ * Tried in order, and ONLY where the one before found nothing. Two matches
546
+ * is a question the test has to answer, never one to guess at, so ambiguity
547
+ * stops the walk rather than passing it along. A step that resolves by
548
+ * anything other than its first choice says so in the result — a repair
549
+ * nobody can see is a repair nobody can trust. */
550
+ candidates: z3.array(LocatorOverrideSchema).max(4).optional(),
528
551
  nth: z3.number().int().nonnegative().optional()
529
552
  });
530
553
  var RecordedAssertionKindSchema = z3.enum([
@@ -918,11 +941,57 @@ var FlowOccurrenceSchema = z22.object({
918
941
  startPath: z22.string().optional(),
919
942
  baseUrl: z22.string().url().optional(),
920
943
  /** GO SOMEWHERE FIRST, or carry on where the last row ended.
944
+ *
945
+ * SUPERSEDED BY `start`, and still read so that every case written before
946
+ * it keeps running unchanged. `startOf` answers for both shapes; nothing
947
+ * should consult this field directly.
921
948
  *
922
949
  * Off by default, because a case is a sequence in one browser: re-entering
923
950
  * each flow's original starting URL would sign the tester out between rows
924
951
  * and is exactly the behaviour composing flows is meant to avoid. */
925
952
  navigateFirst: z22.boolean().default(false),
953
+ /** WHICH BROWSER THIS ROW RUNS IN.
954
+ *
955
+ * Two answers, because there are two: carry on in the browser this
956
+ * participant already has, or open them a new one. Where the page goes is a
957
+ * different question with its own field — folding them together produced a
958
+ * three-way choice in which two of the answers differed by something most
959
+ * people never set.
960
+ *
961
+ * A case is a sequence of flows performed by participants, and until now
962
+ * the only question a row could answer was whether to navigate. Whether it
963
+ * ran in the participant's EXISTING browser or a fresh one could not be
964
+ * said at all — it was implied (always the existing one) and could only be
965
+ * overridden by burying a `resetActor` step inside the flow.
966
+ *
967
+ * continue — the participant's browser, exactly where they left it
968
+ * navigate — the participant's browser, back at this flow's start path
969
+ * fresh — a new browser for them, signed in again if they start signed
970
+ * in, then the start path
971
+ *
972
+ * A participant's FIRST row is always fresh in effect: there is nothing to
973
+ * continue from. That is not stored, because it is a fact about the order
974
+ * rather than about the row — moving the row changes the answer. */
975
+ start: z22.enum(["continue", "navigate", "fresh"]).optional(),
976
+ /** WHETHER THIS COPY DOES ITS OWN SIGNING IN.
977
+ *
978
+ * A flow recorded with "sign in before recording" keeps that sign-in as its
979
+ * first step, because that is how it readies ITSELF when it runs alone.
980
+ * Inside a case the identity belongs to the person, so the same step is
981
+ * usually a second answer to a question already answered.
982
+ *
983
+ * skip — the person's account is the one used; this copy's own sign-ins
984
+ * lie dormant, and the run confirms the session rather than
985
+ * assuming it
986
+ * run — perform them anyway, whoever they name. Signing in as somebody
987
+ * else on this person's browser is a real thing to want, and
988
+ * before this there was no way to say it.
989
+ *
990
+ * ABSENT IS NOT "run". It is "nobody has said", which behaves as skip where
991
+ * the two agree on the account and is refused where they do not — so a case
992
+ * written before this keeps its meaning, and a contradiction stays a
993
+ * contradiction until somebody actually resolves it. */
994
+ ownSignIn: z22.enum(["skip", "run"]).optional(),
926
995
  steps: z22.array(RecordedStepSchema),
927
996
  environment: ExecutionEnvironmentSchema.optional(),
928
997
  /** Where this copy came from, so the editor can say whether the source has
@@ -954,8 +1023,18 @@ var TestCaseSchema = z22.object({
954
1023
  slug: z22.string().regex(/^[a-z0-9][a-z0-9-]*$/),
955
1024
  name: z22.string().min(1),
956
1025
  description: z22.string().default(""),
957
- /** Where the case itself begins. The first row continues from here unless it
958
- * carries its own navigation. */
1026
+ /** NO LONGER USED, and kept only so that a case written while they were
1027
+ * parses.
1028
+ *
1029
+ * These overrode the FIRST row's start path — whichever row that happened
1030
+ * to be. A case is reordered by dragging, so the override silently moved to
1031
+ * whatever was dragged to the top, carrying a path chosen for a different
1032
+ * flow. Nothing said so, and the row it landed on looked like every other
1033
+ * row. A positional override of a list people reorder is a setting that
1034
+ * cannot stay correct.
1035
+ *
1036
+ * Every row now starts where its own flow says, which is the same answer
1037
+ * whatever the order. */
959
1038
  startPath: z22.string().default("/"),
960
1039
  useStartPath: z22.boolean().default(false),
961
1040
  baseUrl: z22.string().url().optional(),
@@ -984,6 +1063,11 @@ var TestCaseSchema = z22.object({
984
1063
  steps.add(step.id);
985
1064
  });
986
1065
  });
1066
+ }).transform((testCase) => {
1067
+ if (!testCase.defaultActorId || testCase.defaultActorId === "default") return testCase;
1068
+ if (testCase.actors?.some((actor) => actor.id === testCase.defaultActorId)) return testCase;
1069
+ const { defaultActorId: _gone, ...rest } = testCase;
1070
+ return rest;
987
1071
  });
988
1072
  var CaseFolderSchema = z22.object({
989
1073
  schemaVersion: z22.literal(1),
@@ -1035,13 +1119,66 @@ var SuiteSchema = z22.object({
1035
1119
  seen.add(entry.entryId);
1036
1120
  });
1037
1121
  });
1038
- function caseOccurrences(testCase) {
1039
- return testCase.occurrences.map((item, index) => index === 0 && testCase.useStartPath && !item.navigateFirst ? { ...item, startPath: testCase.startPath, ...testCase.baseUrl ? { baseUrl: testCase.baseUrl } : {} } : item);
1122
+ function startOf(occurrence) {
1123
+ return occurrence.start === "fresh" ? "fresh" : "continue";
1124
+ }
1125
+ function navigatesFirst(occurrence) {
1126
+ return occurrence.start === "navigate" || occurrence.navigateFirst;
1127
+ }
1128
+ function sessionLanes(occurrences, defaultActorId) {
1129
+ const mine = /* @__PURE__ */ new Map();
1130
+ const owner = /* @__PURE__ */ new Map();
1131
+ const incarnations = /* @__PURE__ */ new Map();
1132
+ const lanes = /* @__PURE__ */ new Map();
1133
+ const last = /* @__PURE__ */ new Map();
1134
+ const rows = [];
1135
+ let previous = null;
1136
+ const open = (actorId) => {
1137
+ const next = (incarnations.get(actorId) ?? -1) + 1;
1138
+ incarnations.set(actorId, next);
1139
+ const sessionId = `${actorId}#${next}`;
1140
+ mine.set(actorId, sessionId);
1141
+ owner.set(sessionId, actorId);
1142
+ if (!lanes.has(sessionId)) lanes.set(sessionId, lanes.size);
1143
+ return sessionId;
1144
+ };
1145
+ for (const occurrence of occurrences) {
1146
+ const actorId = occurrence.actorId || defaultActorId || "default";
1147
+ const continues = startOf(occurrence) === "continue";
1148
+ const asked = occurrence.start === "continue";
1149
+ const theirs = mine.get(actorId);
1150
+ const adoptable = previous !== null && owner.get(previous) === "default" && actorId !== "default";
1151
+ let sessionId;
1152
+ if (continues && theirs) sessionId = theirs;
1153
+ else if (asked && adoptable) {
1154
+ sessionId = previous;
1155
+ owner.set(sessionId, actorId);
1156
+ mine.set(actorId, sessionId);
1157
+ mine.delete("default");
1158
+ } else sessionId = open(actorId);
1159
+ const continuesFrom = last.get(sessionId) ?? null;
1160
+ rows.push({
1161
+ occurrenceId: occurrence.occurrenceId,
1162
+ actorId,
1163
+ incarnation: Number(sessionId.split("#")[1]),
1164
+ sessionId,
1165
+ lane: lanes.get(sessionId),
1166
+ continuesFrom,
1167
+ opens: continuesFrom === null,
1168
+ // WHETHER THERE IS A CHOICE TO MAKE. A row with nothing to continue —
1169
+ // no browser of their own and no guest's to take over — is told so
1170
+ // rather than offered an answer that cannot mean anything.
1171
+ mayContinue: Boolean(theirs) || adoptable
1172
+ });
1173
+ last.set(sessionId, occurrence.occurrenceId);
1174
+ previous = sessionId;
1175
+ }
1176
+ return rows;
1040
1177
  }
1041
1178
  function caseDebugPlan(testCase, baseUrl) {
1042
1179
  const owners = {};
1043
1180
  const steps = [];
1044
- const occurrences = caseOccurrences(testCase);
1181
+ const occurrences = testCase.occurrences;
1045
1182
  for (const [index, item] of occurrences.entries()) {
1046
1183
  if (testCase.actors?.length) {
1047
1184
  const id = `case-actor:${encodeURIComponent(item.occurrenceId)}`;
@@ -1244,14 +1381,19 @@ function validateActors(actors = [], defaultId = "default", used = []) {
1244
1381
  ids.add(actor.id);
1245
1382
  }
1246
1383
  for (const id of [defaultId, ...used]) {
1247
- if (id === UNSET_ACTOR) throw new Error("A participant step does not say which participant \u2014 choose one before running");
1248
- if (id && !ids.has(id)) throw new Error(`Unknown actor: ${id} \u2014 configure this participant before running`);
1384
+ if (id === UNSET_ACTOR) throw new Error("A step does not say who it is for \u2014 choose somebody before running");
1385
+ if (id && !ids.has(id)) throw new Error(`This test names somebody it does not have (${id}) \u2014 choose who runs that row before running`);
1249
1386
  }
1250
1387
  }
1251
1388
  var UNSET_ACTOR = "\0unset";
1252
1389
  function actorIdsUsed(steps) {
1253
1390
  return steps.flatMap((step) => step.disabled ? [] : step.type === "useActor" ? [step.actorId ?? UNSET_ACTOR] : step.type === "resetActor" && step.actorId ? [step.actorId] : []);
1254
1391
  }
1392
+ function sameIdentity(a, b2) {
1393
+ if (a.profileId !== b2.profileId) return false;
1394
+ if (a.accountId && b2.accountId) return a.accountId === b2.accountId;
1395
+ return true;
1396
+ }
1255
1397
 
1256
1398
  // ../engine-playwright/dist/index.js
1257
1399
  import { createHmac, randomBytes } from "crypto";
@@ -1328,34 +1470,36 @@ function resolveStartUrl(baseUrl, startPath) {
1328
1470
  }
1329
1471
  var INPUT_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox", "checkbox", "radio", "spinbutton", "searchbox"]);
1330
1472
  var NAVIGATIONS = /* @__PURE__ */ new Set(["back", "forward"]);
1331
- function locatorPlan(step) {
1332
- const o = step.locator;
1473
+ function planForOverride(o, overridden) {
1333
1474
  if (o && o.value.trim()) {
1334
1475
  const value = o.value.trim();
1335
1476
  switch (o.strategy) {
1336
1477
  // Playwright reads a leading `xpath=` prefix, and also bare paths that
1337
1478
  // start with `/` or `//`. Normalising here means the QA can paste either.
1338
1479
  case "xpath":
1339
- return { method: "locator", arg: value.startsWith("xpath=") ? value : `xpath=${value}`, overridden: true };
1480
+ return { method: "locator", arg: value.startsWith("xpath=") ? value : `xpath=${value}`, overridden };
1340
1481
  // Typed as a bare id, so nobody has to remember the `#`.
1341
1482
  case "id":
1342
- return { method: "locator", arg: /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value.replace(/^#/, "")) ? `#${value.replace(/^#/, "")}` : `[id=${JSON.stringify(value.replace(/^#/, ""))}]`, overridden: true };
1483
+ return { method: "locator", arg: /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value.replace(/^#/, "")) ? `#${value.replace(/^#/, "")}` : `[id=${JSON.stringify(value.replace(/^#/, ""))}]`, overridden };
1343
1484
  case "css":
1344
- return { method: "locator", arg: value, overridden: true };
1485
+ return { method: "locator", arg: value, overridden };
1345
1486
  case "testId":
1346
- return { method: "getByTestId", arg: value, overridden: true };
1487
+ return { method: "getByTestId", arg: value, overridden };
1347
1488
  case "text":
1348
- return { method: "getByText", arg: value, exact: true, overridden: true };
1489
+ return { method: "getByText", arg: value, exact: true, overridden };
1349
1490
  case "label":
1350
- return { method: "getByLabel", arg: value, exact: true, overridden: true };
1491
+ return { method: "getByLabel", arg: value, exact: true, overridden };
1351
1492
  case "placeholder":
1352
- return { method: "getByPlaceholder", arg: value, exact: true, overridden: true };
1493
+ return { method: "getByPlaceholder", arg: value, exact: true, overridden };
1353
1494
  case "altText":
1354
- return { method: "getByAltText", arg: value, exact: true, overridden: true };
1495
+ return { method: "getByAltText", arg: value, exact: true, overridden };
1355
1496
  case "title":
1356
- return { method: "getByTitle", arg: value, exact: true, overridden: true };
1497
+ return { method: "getByTitle", arg: value, exact: true, overridden };
1357
1498
  }
1358
1499
  }
1500
+ return null;
1501
+ }
1502
+ function semanticPlan(step) {
1359
1503
  if (NAVIGATIONS.has(step.type)) {
1360
1504
  return { method: step.type === "back" ? "goBack" : "goForward", arg: "", overridden: false };
1361
1505
  }
@@ -1380,6 +1524,49 @@ function locatorPlan(step) {
1380
1524
  }
1381
1525
  return INPUT_ROLES.has(step.role) ? { method: "getByLabel", arg: step.name, exact: true, overridden: false, inputRole: step.role } : step.role ? { method: "getByRole", arg: step.role, name: step.name, exact: true, overridden: false } : { method: "getByText", arg: step.name, exact: step.type !== "hover", overridden: false, visible: true };
1382
1526
  }
1527
+ function locatorPlans(step) {
1528
+ const chosen = planForOverride(step.locator, true);
1529
+ if (chosen) return [chosen];
1530
+ const candidates = step.candidates ?? [];
1531
+ const plans = [
1532
+ ...candidates.filter((each) => each.strategy === "testId"),
1533
+ ...candidates.filter((each) => each.strategy !== "testId")
1534
+ ];
1535
+ const first = plans.findIndex((each) => each.strategy !== "testId");
1536
+ const before = first === -1 ? plans : plans.slice(0, first);
1537
+ const after = first === -1 ? [] : plans.slice(first);
1538
+ const semantic = semanticPlan(step);
1539
+ const ordered = [
1540
+ ...before.map((each) => planForOverride(each, false)),
1541
+ // Below a test id and above everything else. A name is what somebody
1542
+ // reading the test understands the step to be about, so a step that
1543
+ // resolves by it is the step still meaning what it says.
1544
+ semantic.method === "none" ? null : semantic,
1545
+ ...after.map((each) => planForOverride(each, false))
1546
+ ].filter((plan) => plan !== null);
1547
+ return ordered.length ? ordered : [semantic];
1548
+ }
1549
+ var quote = (s2) => `'${s2.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
1550
+ function locatorPlan(step) {
1551
+ return locatorPlans(step)[0];
1552
+ }
1553
+ function describeLocators(step) {
1554
+ return locatorPlans(step).map((plan) => describePlan(step, plan));
1555
+ }
1556
+ function describePlan(step, p) {
1557
+ if (p.method === "none") return "(nothing to resolve \u2014 no accessible name)";
1558
+ if (p.method === "goBack") return "page.goBack()";
1559
+ if (p.method === "goForward") return "page.goForward()";
1560
+ let root = "page";
1561
+ for (const frame of step.frame ?? []) root += `.frameLocator(${quote(frameSelector(frame))})`;
1562
+ if (!p.overridden && step.scope) root = scopeExpression(root, step.scope, step.scopeRole);
1563
+ let base = p.viaAdjacentLabel ? `${root}.getByText(${quote(p.arg)}, { exact: true }).locator('xpath=..').getByRole(${quote(step.role || "textbox")})` : p.method === "getByRole" ? `${root}.getByRole(${quote(p.arg)}, { name: ${quote(p.name ?? "")}, exact: true })` : `${root}.${p.method}(${quote(p.arg)}${p.exact !== void 0 ? `, { exact: ${p.exact} }` : ""})`;
1564
+ if (p.inputRole) base = `${root}.getByRole(${quote(p.inputRole)}, { name: ${quote(p.arg)}, exact: true }).or(${base}.and(${root}.locator(${quote(inputSelector(p.inputRole))})))`;
1565
+ if (p.visible) base += ".filter({ visible: true })";
1566
+ if (step.nth !== void 0) base += `.nth(${step.nth})`;
1567
+ if (step.pageId && step.pageId !== "main") base += ` // page ${quote(step.pageId)}`;
1568
+ return base;
1569
+ }
1383
1570
  function frameSelector(name) {
1384
1571
  const positional = /^nth:(\d+)$/.exec(name);
1385
1572
  if (positional) return `iframe, frame >> nth=${positional[1]}`;
@@ -1390,6 +1577,10 @@ function inputSelector(role) {
1390
1577
  return role === "textbox" ? 'input, textarea, [contenteditable="true"], [role="textbox"]' : `input, select, [role="${role}"]`;
1391
1578
  }
1392
1579
  var SCOPE_ROLES = ["form", "group", "region", "dialog", "alertdialog"];
1580
+ function scopeExpression(root, name, role) {
1581
+ const parts = (role ? [role] : SCOPE_ROLES).map((r) => `${root}.getByRole(${quote(r)}, { name: ${quote(name)}, exact: ${!role} })`);
1582
+ return parts.reduce((a, b2) => `${a}.or(${b2})`);
1583
+ }
1393
1584
  var CHECKS = [
1394
1585
  { id: "visible", label: "is visible", kind: "visible", group: "state" },
1395
1586
  { id: "not-visible", label: "is not visible", kind: "visible", not: true, group: "state" },
@@ -3105,6 +3296,30 @@ var RunActors = class {
3105
3296
  if (!id || id === "default") return this.opts.actors?.find((a) => a.id === (id ?? "default"))?.name ?? "the main participant";
3106
3297
  return this.opts.actors?.find((a) => a.id === id)?.name ?? id;
3107
3298
  }
3299
+ /** WHAT THIS PARTICIPANT STARTS AS, if anything.
3300
+ *
3301
+ * Asked by a `signIn` step before it runs: a flow carries its own sign-in
3302
+ * because that is how it gets itself ready when it runs ALONE, and in a
3303
+ * case the person is already that identity. Running it again logs in on top
3304
+ * of a session that is already signed in, which most applications answer by
3305
+ * redirecting away from the login page — so the form is not there and a
3306
+ * correctly signed-in run goes red. */
3307
+ identityOf(id) {
3308
+ return this.opts.actors?.find((a) => a.id === (id ?? "default"))?.startSignedIn;
3309
+ }
3310
+ /** SIGN THIS PERSON IN AGAIN, in the browser they already have.
3311
+ *
3312
+ * Not `freshGuest`, which is the opposite: that throws the context away to
3313
+ * start somebody over as a stranger, and leaves them one. This is for a
3314
+ * session that has simply gone — expired, or dropped by the application —
3315
+ * where the browser is the right browser and only the sign-in is missing.
3316
+ *
3317
+ * Un-initialising is what makes `select` do the preparation again rather
3318
+ * than assume it already happened. */
3319
+ async prepareAgain(id) {
3320
+ this.initialized.delete(id);
3321
+ await this.select(id);
3322
+ }
3108
3323
  async select(id, startUrl = this.opts.startUrl) {
3109
3324
  const actor = this.opts.actors?.find((a) => a.id === id);
3110
3325
  if (id !== "default" && !actor) throw new Error(`Unknown actor: ${id}`);
@@ -3170,6 +3385,42 @@ var RunActors = class {
3170
3385
  }
3171
3386
  this.initialized.add(id);
3172
3387
  }
3388
+ /** START THIS PARTICIPANT AGAIN, as themselves.
3389
+ *
3390
+ * `freshGuest` gives a browser that has never been anywhere, which is the
3391
+ * right answer for "start over as a stranger" and the wrong one for "Alice
3392
+ * on a second device": it would leave Alice signed out for the rest of the
3393
+ * case. So the context is replaced AND the participant is un-initialised,
3394
+ * which is what makes `select` navigate and run their sign-in again.
3395
+ *
3396
+ * The distinction is the whole difference between a row that says `fresh`
3397
+ * and a `resetActor` step, and both are worth having. */
3398
+ async restart(id, startUrl) {
3399
+ await this.freshGuest(id);
3400
+ this.initialized.delete(id);
3401
+ await this.select(id, startUrl);
3402
+ }
3403
+ /** HAND A GUEST'S BROWSER TO SOMEBODY, and sign them in there.
3404
+ *
3405
+ * Browsing as a guest and then signing in is one browser, not two: the
3406
+ * basket filled before signing in is the whole point of testing it, and a
3407
+ * participant given a browser of their own would never see it.
3408
+ *
3409
+ * The context is moved rather than copied — there is one of it — and the
3410
+ * participant is left un-initialised so `select` navigates and runs their
3411
+ * sign-in inside it. The guest keeps no claim on it; a later guest row
3412
+ * opens a browser of its own, because this one is signed in now. */
3413
+ async adopt(from, to, startUrl) {
3414
+ const session = this.sessions.get(from);
3415
+ if (!session) return this.select(to, startUrl);
3416
+ this.sessions.set(to, session);
3417
+ this.sessions.delete(from);
3418
+ this.initialized.delete(from);
3419
+ this.initialized.delete(to);
3420
+ this.current = session;
3421
+ this.currentId = to;
3422
+ await this.select(to, startUrl);
3423
+ }
3173
3424
  /** Replace a participant's context without disturbing anyone else's. */
3174
3425
  async replaceWithGuest(id, previous) {
3175
3426
  const session = await openRunSession({ ...this.opts, browser: this.main.browser, ...this.evidenceIn(`actor-${id}`) });
@@ -3189,6 +3440,10 @@ var RunActors = class {
3189
3440
  preparedHow() {
3190
3441
  return Object.fromEntries(this.preparations);
3191
3442
  }
3443
+ /** Whether this participant already has a browser open. */
3444
+ has(id) {
3445
+ return this.sessions.has(id);
3446
+ }
3192
3447
  /** Throw this participant's session away and give them a new signed-out one.
3193
3448
  *
3194
3449
  * NOT SIGNING OUT. An application can be signed out of while the browser
@@ -3553,8 +3808,15 @@ async function locate(page, step, opts = {}) {
3553
3808
  const loc = await locateAll(page, step, opts);
3554
3809
  return step.nth === void 0 ? loc : loc.nth(step.nth);
3555
3810
  }
3811
+ async function locateCandidates(page, step, opts = {}) {
3812
+ const built = [];
3813
+ for (const plan of locatorPlans(step)) built.push(await fromPlan(page, step, plan, opts));
3814
+ return step.nth === void 0 ? built : built.map((each) => each.nth(step.nth));
3815
+ }
3556
3816
  async function locateAll(page, step, opts = {}) {
3557
- const plan = locatorPlan(step);
3817
+ return fromPlan(page, step, locatorPlan(step), opts);
3818
+ }
3819
+ async function fromPlan(page, step, plan, opts = {}) {
3558
3820
  let root = rootFor(page, step);
3559
3821
  if (step.scope && !plan.overridden) {
3560
3822
  const roles = step.scopeRole ? [step.scopeRole] : [...SCOPE_ROLES];
@@ -4036,6 +4298,7 @@ async function performSignIn(session, step, opts) {
4036
4298
  }
4037
4299
  return { ok: true, label, detail: null, kind: null, checks: checked?.checks ?? [] };
4038
4300
  }
4301
+ var SIGNED_IN_CHECK_MS = 3e3;
4039
4302
  async function confirmSignedIn(page, auth, timeoutMs) {
4040
4303
  for (const check of auth.success) {
4041
4304
  if (check.enabled === false) continue;
@@ -4150,6 +4413,67 @@ async function runStepsIn(session, steps, opts) {
4150
4413
  error = "the run was cancelled";
4151
4414
  break;
4152
4415
  }
4416
+ if (step.type === "signIn" && !step.disabled && step.auth && opts.actors && opts.ownSignIn !== "run") {
4417
+ const identity = opts.actors.identityOf(opts.actors.currentId);
4418
+ const auth = opts.resolveAuth?.(step.auth);
4419
+ const dormant = opts.ownSignIn === "skip" || identity !== void 0 && sameIdentity(identity, step.auth);
4420
+ const expected = identity && !sameIdentity(identity, step.auth) ? opts.resolveAuth?.(identity) : auth;
4421
+ if (identity && auth && expected && hasSuccessCheck(expected) && dormant) {
4422
+ const started = Date.now();
4423
+ if ((await confirmSignedIn(session.page, expected, SIGNED_IN_CHECK_MS)).ok) {
4424
+ const provided = {
4425
+ stepId: step.id,
4426
+ name: `Sign in as ${expected.accountName}`,
4427
+ type: step.type,
4428
+ value: null,
4429
+ secret: false,
4430
+ status: "skipped",
4431
+ detail: `${opts.actors.nameOf(opts.actors.currentId)} already starts signed in as ${expected.accountName}`,
4432
+ failureKind: null,
4433
+ durationMs: Date.now() - started,
4434
+ checks: []
4435
+ };
4436
+ results.push(provided);
4437
+ onStepResult?.(provided);
4438
+ continue;
4439
+ }
4440
+ if (!sameIdentity(identity, step.auth)) {
4441
+ const started2 = Date.now();
4442
+ const who = opts.actors.currentId;
4443
+ let recovered = null;
4444
+ try {
4445
+ await opts.actors.prepareAgain(who);
4446
+ session = opts.actors.current;
4447
+ ({ page, pages, config, artifacts } = session);
4448
+ if (!(await confirmSignedIn(session.page, expected, SIGNED_IN_CHECK_MS)).ok) {
4449
+ recovered = "signing them in again did not take";
4450
+ }
4451
+ } catch (failure) {
4452
+ recovered = firstLine2(failure);
4453
+ }
4454
+ const outcome = {
4455
+ stepId: step.id,
4456
+ name: `Sign in as ${expected.accountName}`,
4457
+ type: step.type,
4458
+ value: null,
4459
+ secret: false,
4460
+ status: recovered ? "failed" : "skipped",
4461
+ detail: recovered ? `this row uses ${opts.actors.nameOf(who)}'s own sign-in rather than its own, and ${recovered}` : `${opts.actors.nameOf(who)} was signed in again as ${expected.accountName}`,
4462
+ failureKind: recovered ? "unknown" : null,
4463
+ durationMs: Date.now() - started2,
4464
+ checks: []
4465
+ };
4466
+ results.push(outcome);
4467
+ onStepResult?.(outcome);
4468
+ if (recovered) {
4469
+ status2 = "failed";
4470
+ error = `${outcome.name}: ${outcome.detail}`;
4471
+ break;
4472
+ }
4473
+ continue;
4474
+ }
4475
+ }
4476
+ }
4153
4477
  if ((step.type === "signIn" || step.type === "signOut") && !step.disabled) {
4154
4478
  const started = Date.now();
4155
4479
  const outcome = await performSignIn(session, step, opts);
@@ -4217,7 +4541,13 @@ async function runStepsIn(session, steps, opts) {
4217
4541
  baseUrl: opts.baseUrl,
4218
4542
  ...opts.actors ? { actors: opts.actors } : {},
4219
4543
  timeoutMs: config.timeoutMs,
4220
- navigationTimeoutMs: config.navigationTimeoutMs
4544
+ navigationTimeoutMs: config.navigationTimeoutMs,
4545
+ // SAID OUT LOUD, on a step that passed. The element was found, but
4546
+ // not the way this step says it should be — something has moved,
4547
+ // and the run is the only place anybody would learn it.
4548
+ onFallback: (_step, foundBy) => {
4549
+ result.foundBy = foundBy;
4550
+ }
4221
4551
  });
4222
4552
  for (const a of step.proposals ?? []) {
4223
4553
  if (a.enabled === false) continue;
@@ -4424,8 +4754,9 @@ async function executeStep(main, input, options = {}) {
4424
4754
  if (step.scroll && step.type !== "scroll") {
4425
4755
  await applyScroll(page, step, Math.min(2e3, remaining()));
4426
4756
  }
4427
- const loc = await locate(page, step);
4428
- await ensureTarget(page, loc, step, remaining());
4757
+ const target = await ensureTarget(page, step, remaining());
4758
+ const loc = target.loc;
4759
+ if (target.foundBy) options.onFallback?.(step, target.foundBy);
4429
4760
  page.setDefaultTimeout(remaining());
4430
4761
  await applyAction(loc, step, value, { resolveFile: options.resolveFile, dropTarget: await locateDrop(page, step) });
4431
4762
  };
@@ -4477,21 +4808,36 @@ async function applyScroll(page, step, timeout) {
4477
4808
  } catch {
4478
4809
  }
4479
4810
  }
4480
- async function ensureTarget(page, loc, step, timeout) {
4811
+ async function ensureTarget(page, step, timeout) {
4812
+ const candidates = await locateCandidates(page, step);
4813
+ const described = describeLocators(step);
4814
+ const primary = candidates[0];
4481
4815
  const firstWait = Math.max(500, Math.floor(timeout * 0.45));
4482
4816
  const recovery = Math.max(1e3, Math.floor(timeout * 0.35));
4483
4817
  try {
4484
- await loc.waitFor({ state: "attached", timeout: firstWait });
4485
- return;
4818
+ await primary.waitFor({ state: "attached", timeout: firstWait });
4819
+ return { loc: primary };
4486
4820
  } catch (error) {
4487
4821
  if (/strict mode violation/.test(String(error))) {
4488
4822
  throw Object.assign(error, { kind: "ambiguous" });
4489
4823
  }
4490
4824
  }
4491
- if (await scrollUntilFound(page, loc, recovery)) return;
4825
+ for (let at = 1; at < candidates.length; at += 1) {
4826
+ const found = await candidates[at].count().catch(() => 0);
4827
+ if (found === 1) return { loc: candidates[at], foundBy: described[at] ?? "another locator" };
4828
+ if (found > 1) {
4829
+ throw Object.assign(
4830
+ new Error(
4831
+ `"${step.name}" could not be found the way this step records it, and ${found} elements match ${described[at]} \u2014 narrow it in the editor`
4832
+ ),
4833
+ { kind: "ambiguous" }
4834
+ );
4835
+ }
4836
+ }
4837
+ if (await scrollUntilFound(page, primary, recovery)) return { loc: primary };
4492
4838
  throw Object.assign(
4493
4839
  new Error(
4494
- `no element named "${step.name}" \u2014 nothing on this page matches the target or its recorded scope`
4840
+ candidates.length > 1 ? `no element named "${step.name}" \u2014 none of the ${candidates.length} ways this step records it match anything on the page` : `no element named "${step.name}" \u2014 nothing on this page matches the target or its recorded scope`
4495
4841
  ),
4496
4842
  { kind: "not_found" }
4497
4843
  );
@@ -4533,6 +4879,7 @@ async function replayCase(opts) {
4533
4879
  const outcomes = [];
4534
4880
  let status2 = "passed";
4535
4881
  let error = null;
4882
+ const lanes = sessionLanes(occurrences, opts.defaultActorId);
4536
4883
  try {
4537
4884
  for (const [index, occurrence] of occurrences.entries()) {
4538
4885
  if (status2 !== "passed" || opts.signal?.aborted) {
@@ -4555,9 +4902,15 @@ async function replayCase(opts) {
4555
4902
  opts.onOccurrenceResult?.(skipped);
4556
4903
  continue;
4557
4904
  }
4558
- const navigates = occurrence.navigateFirst;
4905
+ const navigates = navigatesFirst(occurrence);
4906
+ const who = occurrence.actorId ?? opts.defaultActorId ?? "default";
4907
+ const entry = resolveStartUrl(occurrence.baseUrl ?? baseUrl, occurrence.startPath ?? "/");
4908
+ const plan = lanes[index];
4559
4909
  try {
4560
- await actors.select(occurrence.actorId ?? opts.defaultActorId ?? "default", resolveStartUrl(occurrence.baseUrl ?? baseUrl, occurrence.startPath ?? "/"));
4910
+ if (plan.opens && startOf(occurrence) === "fresh") await actors.restart(who, entry);
4911
+ else if (plan.opens) await actors.select(who, entry);
4912
+ else if (actors.currentId !== who && !actors.has(who)) await actors.adopt("default", who, entry);
4913
+ else await actors.select(who, entry);
4561
4914
  } catch (failure) {
4562
4915
  status2 = "failed";
4563
4916
  error = failure.message;
@@ -4570,6 +4923,7 @@ async function replayCase(opts) {
4570
4923
  baseUrl: occurrence.baseUrl ?? baseUrl,
4571
4924
  ...opts.resolveAuth ? { resolveAuth: opts.resolveAuth } : {},
4572
4925
  stepOffset: outcomes.reduce((count, previous) => count + previous.stepsTotal, 0),
4926
+ ...occurrence.ownSignIn ? { ownSignIn: occurrence.ownSignIn } : {},
4573
4927
  ...opts.resolveSecret === void 0 ? {} : { resolveSecret: opts.resolveSecret },
4574
4928
  ...opts.resolveFile === void 0 ? {} : { resolveFile: opts.resolveFile },
4575
4929
  ...opts.evidenceDir === void 0 ? {} : { evidenceDir: opts.evidenceDir },
@@ -4833,6 +5187,7 @@ async function startRecordingSession(opts) {
4833
5187
  ...payload.frame?.length ? { frame: payload.frame } : {},
4834
5188
  ...payload.to ? { to: payload.to } : {},
4835
5189
  ...payload.locator ? { locator: payload.locator } : {},
5190
+ ...payload.candidates?.length ? { candidates: payload.candidates } : {},
4836
5191
  ...payload.values ? { values: payload.values } : {},
4837
5192
  ...payload.warning ? { warning: payload.warning } : {},
4838
5193
  ...payload.pageId ? { pageId: payload.pageId } : {},
@@ -5045,6 +5400,7 @@ async function startRecordingSession(opts) {
5045
5400
  ...parsed.target?.scopeRole ? { scopeRole: parsed.target.scopeRole } : {},
5046
5401
  ...parsed.target?.frame?.length ? { frame: parsed.target.frame } : {},
5047
5402
  ...parsed.target?.locator ? { locator: parsed.target.locator } : {},
5403
+ ...parsed.target?.candidates?.length ? { candidates: parsed.target.candidates } : {},
5048
5404
  ...parsed.target?.pageId ? { pageId: parsed.target.pageId } : {},
5049
5405
  value: "",
5050
5406
  secret: false,
@@ -5676,7 +6032,7 @@ async function startDebugSession(opts) {
5676
6032
  return session;
5677
6033
  }
5678
6034
 
5679
- // ../results/dist/chunk-WEBNFZEW.js
6035
+ // ../results/dist/chunk-LYCTVHPR.js
5680
6036
  import { z as z5 } from "zod";
5681
6037
  var RunStatusSchema = z5.enum([
5682
6038
  "queued",
@@ -5744,6 +6100,13 @@ var RunStepSchema = z5.object({
5744
6100
  status: z5.enum(["passed", "failed", "skipped"]),
5745
6101
  failureKind: FailureKindSchema.nullable(),
5746
6102
  detail: z5.string().nullable(),
6103
+ /** WHICH WAY FOUND IT, when the first way did not.
6104
+ *
6105
+ * A step carries several routes to its element and takes the first that
6106
+ * matches. Passing on a later one is not a failure, but it does mean the
6107
+ * page has moved under the step — and a suite that repairs itself quietly
6108
+ * drifts away from the application it tests. Null is the ordinary case. */
6109
+ foundBy: z5.string().nullable().default(null),
5747
6110
  durationMs: z5.number().int().nonnegative().nullable(),
5748
6111
  /** Where the browser was when this step ran. Recorded on failure, and
5749
6112
  * frequently the whole answer — the element is missing because a step
@@ -6004,6 +6367,16 @@ var MIGRATIONS2 = [
6004
6367
  `
6005
6368
  ALTER TABLE run_step ADD COLUMN value TEXT;
6006
6369
  ALTER TABLE run_step ADD COLUMN secret INTEGER NOT NULL DEFAULT 0;
6370
+ `,
6371
+ // 5 — a step that found its element some way other than the first it tried.
6372
+ //
6373
+ // Nullable, and NULL is the ordinary case: the step was found the way it
6374
+ // says it should be. It is recorded because the alternative is a suite that
6375
+ // repairs itself quietly and drifts away from the application it tests —
6376
+ // and because "which steps are limping?" is a question about history, not
6377
+ // about the run somebody happens to be watching.
6378
+ `
6379
+ ALTER TABLE run_step ADD COLUMN found_by TEXT;
6007
6380
  `
6008
6381
  ];
6009
6382
  var SCHEMA_VERSION = MIGRATIONS2.length;
@@ -6216,8 +6589,8 @@ var SqliteStorage = class {
6216
6589
  this.handle.prepare(
6217
6590
  `INSERT INTO run_step (id, run_task_id, ordinal, step_id, action, target_role,
6218
6591
  target_name, target_scope, status, failure_kind, detail, duration_ms,
6219
- page_url, page_title, value, secret)
6220
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6592
+ page_url, page_title, value, secret, found_by)
6593
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6221
6594
  ).run(
6222
6595
  id,
6223
6596
  taskId,
@@ -6235,7 +6608,8 @@ var SqliteStorage = class {
6235
6608
  input.pageTitle ?? null,
6236
6609
  // Never the plaintext. A secret arrives here already withheld.
6237
6610
  input.secret ? null : input.value ?? null,
6238
- b(input.secret ?? false)
6611
+ b(input.secret ?? false),
6612
+ input.foundBy ?? null
6239
6613
  );
6240
6614
  const stmt = this.handle.prepare(
6241
6615
  `INSERT INTO run_check (id, run_step_id, assertion_id, kind, expected, actual, ok, soft, detail)
@@ -6379,6 +6753,7 @@ var SqliteStorage = class {
6379
6753
  durationMs: n(st.duration_ms),
6380
6754
  pageUrl: s(st.page_url),
6381
6755
  pageTitle: s(st.page_title),
6756
+ foundBy: s(st.found_by),
6382
6757
  checks: checksByStep.get(String(st.id)) ?? []
6383
6758
  });
6384
6759
  stepsByTask.set(String(st.run_task_id), list);
@@ -7519,6 +7894,9 @@ function toStepRow(ordinal, result, step) {
7519
7894
  durationMs: result.durationMs,
7520
7895
  pageUrl: result.evidence?.url ?? null,
7521
7896
  pageTitle: result.evidence?.title ?? null,
7897
+ // Kept, because "which steps are limping?" is a question about history
7898
+ // rather than about the run somebody happens to be watching.
7899
+ foundBy: result.foundBy ?? null,
7522
7900
  checks
7523
7901
  };
7524
7902
  }
@@ -7585,7 +7963,7 @@ async function executeCase(opts) {
7585
7963
  let outcome;
7586
7964
  try {
7587
7965
  outcome = await replayCase({
7588
- occurrences: caseOccurrences(testCase),
7966
+ occurrences: testCase.occurrences,
7589
7967
  ...testCase.actors ? { actors: testCase.actors } : {},
7590
7968
  ...testCase.defaultActorId ? { defaultActorId: testCase.defaultActorId } : {},
7591
7969
  resolveAuth: authenticationResolver(opts.authentications),
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  resolveCheckouts,
9
9
  start,
10
10
  status
11
- } from "./chunk-ZVX26LV7.js";
11
+ } from "./chunk-U3QYIAXF.js";
12
12
 
13
13
  // src/cli.ts
14
14
  import { parseArgs } from "util";
package/dist/engine.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveCheckouts,
10
10
  start,
11
11
  status
12
- } from "./chunk-ZVX26LV7.js";
12
+ } from "./chunk-U3QYIAXF.js";
13
13
  export {
14
14
  AGENT_VERSION,
15
15
  browsersReady,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowriki/agent",
3
- "version": "0.1.0-beta.20",
3
+ "version": "0.1.0-beta.21",
4
4
  "description": "Run Flowriki UI tests on your own computer. Connects this machine to your Flowriki account.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",