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

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.22";
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-X4X4KGMA.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,32 @@ 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 did not identify a single
546
+ * element. EXACTLY ONE MATCH WINS: a locator matching several is
547
+ * disqualified rather than obeyed, so choosing between two elements never
548
+ * happens — and where nothing identifies one, the run stops and names the
549
+ * ambiguity. A step that resolves by anything other than its first choice
550
+ * says so in the result: a repair nobody can see is a repair nobody can
551
+ * trust. */
552
+ candidates: z3.array(LocatorOverrideSchema).max(4).optional(),
528
553
  nth: z3.number().int().nonnegative().optional()
529
554
  });
530
555
  var RecordedAssertionKindSchema = z3.enum([
@@ -918,11 +943,57 @@ var FlowOccurrenceSchema = z22.object({
918
943
  startPath: z22.string().optional(),
919
944
  baseUrl: z22.string().url().optional(),
920
945
  /** GO SOMEWHERE FIRST, or carry on where the last row ended.
946
+ *
947
+ * SUPERSEDED BY `start`, and still read so that every case written before
948
+ * it keeps running unchanged. `startOf` answers for both shapes; nothing
949
+ * should consult this field directly.
921
950
  *
922
951
  * Off by default, because a case is a sequence in one browser: re-entering
923
952
  * each flow's original starting URL would sign the tester out between rows
924
953
  * and is exactly the behaviour composing flows is meant to avoid. */
925
954
  navigateFirst: z22.boolean().default(false),
955
+ /** WHICH BROWSER THIS ROW RUNS IN.
956
+ *
957
+ * Two answers, because there are two: carry on in the browser this
958
+ * participant already has, or open them a new one. Where the page goes is a
959
+ * different question with its own field — folding them together produced a
960
+ * three-way choice in which two of the answers differed by something most
961
+ * people never set.
962
+ *
963
+ * A case is a sequence of flows performed by participants, and until now
964
+ * the only question a row could answer was whether to navigate. Whether it
965
+ * ran in the participant's EXISTING browser or a fresh one could not be
966
+ * said at all — it was implied (always the existing one) and could only be
967
+ * overridden by burying a `resetActor` step inside the flow.
968
+ *
969
+ * continue — the participant's browser, exactly where they left it
970
+ * navigate — the participant's browser, back at this flow's start path
971
+ * fresh — a new browser for them, signed in again if they start signed
972
+ * in, then the start path
973
+ *
974
+ * A participant's FIRST row is always fresh in effect: there is nothing to
975
+ * continue from. That is not stored, because it is a fact about the order
976
+ * rather than about the row — moving the row changes the answer. */
977
+ start: z22.enum(["continue", "navigate", "fresh"]).optional(),
978
+ /** WHETHER THIS COPY DOES ITS OWN SIGNING IN.
979
+ *
980
+ * A flow recorded with "sign in before recording" keeps that sign-in as its
981
+ * first step, because that is how it readies ITSELF when it runs alone.
982
+ * Inside a case the identity belongs to the person, so the same step is
983
+ * usually a second answer to a question already answered.
984
+ *
985
+ * skip — the person's account is the one used; this copy's own sign-ins
986
+ * lie dormant, and the run confirms the session rather than
987
+ * assuming it
988
+ * run — perform them anyway, whoever they name. Signing in as somebody
989
+ * else on this person's browser is a real thing to want, and
990
+ * before this there was no way to say it.
991
+ *
992
+ * ABSENT IS NOT "run". It is "nobody has said", which behaves as skip where
993
+ * the two agree on the account and is refused where they do not — so a case
994
+ * written before this keeps its meaning, and a contradiction stays a
995
+ * contradiction until somebody actually resolves it. */
996
+ ownSignIn: z22.enum(["skip", "run"]).optional(),
926
997
  steps: z22.array(RecordedStepSchema),
927
998
  environment: ExecutionEnvironmentSchema.optional(),
928
999
  /** Where this copy came from, so the editor can say whether the source has
@@ -954,8 +1025,18 @@ var TestCaseSchema = z22.object({
954
1025
  slug: z22.string().regex(/^[a-z0-9][a-z0-9-]*$/),
955
1026
  name: z22.string().min(1),
956
1027
  description: z22.string().default(""),
957
- /** Where the case itself begins. The first row continues from here unless it
958
- * carries its own navigation. */
1028
+ /** NO LONGER USED, and kept only so that a case written while they were
1029
+ * parses.
1030
+ *
1031
+ * These overrode the FIRST row's start path — whichever row that happened
1032
+ * to be. A case is reordered by dragging, so the override silently moved to
1033
+ * whatever was dragged to the top, carrying a path chosen for a different
1034
+ * flow. Nothing said so, and the row it landed on looked like every other
1035
+ * row. A positional override of a list people reorder is a setting that
1036
+ * cannot stay correct.
1037
+ *
1038
+ * Every row now starts where its own flow says, which is the same answer
1039
+ * whatever the order. */
959
1040
  startPath: z22.string().default("/"),
960
1041
  useStartPath: z22.boolean().default(false),
961
1042
  baseUrl: z22.string().url().optional(),
@@ -984,6 +1065,11 @@ var TestCaseSchema = z22.object({
984
1065
  steps.add(step.id);
985
1066
  });
986
1067
  });
1068
+ }).transform((testCase) => {
1069
+ if (!testCase.defaultActorId || testCase.defaultActorId === "default") return testCase;
1070
+ if (testCase.actors?.some((actor) => actor.id === testCase.defaultActorId)) return testCase;
1071
+ const { defaultActorId: _gone, ...rest } = testCase;
1072
+ return rest;
987
1073
  });
988
1074
  var CaseFolderSchema = z22.object({
989
1075
  schemaVersion: z22.literal(1),
@@ -1035,13 +1121,66 @@ var SuiteSchema = z22.object({
1035
1121
  seen.add(entry.entryId);
1036
1122
  });
1037
1123
  });
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);
1124
+ function startOf(occurrence) {
1125
+ return occurrence.start === "fresh" ? "fresh" : "continue";
1126
+ }
1127
+ function navigatesFirst(occurrence) {
1128
+ return occurrence.start === "navigate" || occurrence.navigateFirst;
1129
+ }
1130
+ function sessionLanes(occurrences, defaultActorId) {
1131
+ const mine = /* @__PURE__ */ new Map();
1132
+ const owner = /* @__PURE__ */ new Map();
1133
+ const incarnations = /* @__PURE__ */ new Map();
1134
+ const lanes = /* @__PURE__ */ new Map();
1135
+ const last = /* @__PURE__ */ new Map();
1136
+ const rows = [];
1137
+ let previous = null;
1138
+ const open = (actorId) => {
1139
+ const next = (incarnations.get(actorId) ?? -1) + 1;
1140
+ incarnations.set(actorId, next);
1141
+ const sessionId = `${actorId}#${next}`;
1142
+ mine.set(actorId, sessionId);
1143
+ owner.set(sessionId, actorId);
1144
+ if (!lanes.has(sessionId)) lanes.set(sessionId, lanes.size);
1145
+ return sessionId;
1146
+ };
1147
+ for (const occurrence of occurrences) {
1148
+ const actorId = occurrence.actorId || defaultActorId || "default";
1149
+ const continues = startOf(occurrence) === "continue";
1150
+ const asked = occurrence.start === "continue";
1151
+ const theirs = mine.get(actorId);
1152
+ const adoptable = previous !== null && owner.get(previous) === "default" && actorId !== "default";
1153
+ let sessionId;
1154
+ if (continues && theirs) sessionId = theirs;
1155
+ else if (asked && adoptable) {
1156
+ sessionId = previous;
1157
+ owner.set(sessionId, actorId);
1158
+ mine.set(actorId, sessionId);
1159
+ mine.delete("default");
1160
+ } else sessionId = open(actorId);
1161
+ const continuesFrom = last.get(sessionId) ?? null;
1162
+ rows.push({
1163
+ occurrenceId: occurrence.occurrenceId,
1164
+ actorId,
1165
+ incarnation: Number(sessionId.split("#")[1]),
1166
+ sessionId,
1167
+ lane: lanes.get(sessionId),
1168
+ continuesFrom,
1169
+ opens: continuesFrom === null,
1170
+ // WHETHER THERE IS A CHOICE TO MAKE. A row with nothing to continue —
1171
+ // no browser of their own and no guest's to take over — is told so
1172
+ // rather than offered an answer that cannot mean anything.
1173
+ mayContinue: Boolean(theirs) || adoptable
1174
+ });
1175
+ last.set(sessionId, occurrence.occurrenceId);
1176
+ previous = sessionId;
1177
+ }
1178
+ return rows;
1040
1179
  }
1041
1180
  function caseDebugPlan(testCase, baseUrl) {
1042
1181
  const owners = {};
1043
1182
  const steps = [];
1044
- const occurrences = caseOccurrences(testCase);
1183
+ const occurrences = testCase.occurrences;
1045
1184
  for (const [index, item] of occurrences.entries()) {
1046
1185
  if (testCase.actors?.length) {
1047
1186
  const id = `case-actor:${encodeURIComponent(item.occurrenceId)}`;
@@ -1244,14 +1383,19 @@ function validateActors(actors = [], defaultId = "default", used = []) {
1244
1383
  ids.add(actor.id);
1245
1384
  }
1246
1385
  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`);
1386
+ if (id === UNSET_ACTOR) throw new Error("A step does not say who it is for \u2014 choose somebody before running");
1387
+ 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
1388
  }
1250
1389
  }
1251
1390
  var UNSET_ACTOR = "\0unset";
1252
1391
  function actorIdsUsed(steps) {
1253
1392
  return steps.flatMap((step) => step.disabled ? [] : step.type === "useActor" ? [step.actorId ?? UNSET_ACTOR] : step.type === "resetActor" && step.actorId ? [step.actorId] : []);
1254
1393
  }
1394
+ function sameIdentity(a, b2) {
1395
+ if (a.profileId !== b2.profileId) return false;
1396
+ if (a.accountId && b2.accountId) return a.accountId === b2.accountId;
1397
+ return true;
1398
+ }
1255
1399
 
1256
1400
  // ../engine-playwright/dist/index.js
1257
1401
  import { createHmac, randomBytes } from "crypto";
@@ -1328,34 +1472,36 @@ function resolveStartUrl(baseUrl, startPath) {
1328
1472
  }
1329
1473
  var INPUT_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox", "checkbox", "radio", "spinbutton", "searchbox"]);
1330
1474
  var NAVIGATIONS = /* @__PURE__ */ new Set(["back", "forward"]);
1331
- function locatorPlan(step) {
1332
- const o = step.locator;
1475
+ function planForOverride(o, overridden) {
1333
1476
  if (o && o.value.trim()) {
1334
1477
  const value = o.value.trim();
1335
1478
  switch (o.strategy) {
1336
1479
  // Playwright reads a leading `xpath=` prefix, and also bare paths that
1337
1480
  // start with `/` or `//`. Normalising here means the QA can paste either.
1338
1481
  case "xpath":
1339
- return { method: "locator", arg: value.startsWith("xpath=") ? value : `xpath=${value}`, overridden: true };
1482
+ return { method: "locator", arg: value.startsWith("xpath=") ? value : `xpath=${value}`, overridden };
1340
1483
  // Typed as a bare id, so nobody has to remember the `#`.
1341
1484
  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 };
1485
+ return { method: "locator", arg: /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value.replace(/^#/, "")) ? `#${value.replace(/^#/, "")}` : `[id=${JSON.stringify(value.replace(/^#/, ""))}]`, overridden };
1343
1486
  case "css":
1344
- return { method: "locator", arg: value, overridden: true };
1487
+ return { method: "locator", arg: value, overridden };
1345
1488
  case "testId":
1346
- return { method: "getByTestId", arg: value, overridden: true };
1489
+ return { method: "getByTestId", arg: value, overridden };
1347
1490
  case "text":
1348
- return { method: "getByText", arg: value, exact: true, overridden: true };
1491
+ return { method: "getByText", arg: value, exact: true, overridden };
1349
1492
  case "label":
1350
- return { method: "getByLabel", arg: value, exact: true, overridden: true };
1493
+ return { method: "getByLabel", arg: value, exact: true, overridden };
1351
1494
  case "placeholder":
1352
- return { method: "getByPlaceholder", arg: value, exact: true, overridden: true };
1495
+ return { method: "getByPlaceholder", arg: value, exact: true, overridden };
1353
1496
  case "altText":
1354
- return { method: "getByAltText", arg: value, exact: true, overridden: true };
1497
+ return { method: "getByAltText", arg: value, exact: true, overridden };
1355
1498
  case "title":
1356
- return { method: "getByTitle", arg: value, exact: true, overridden: true };
1499
+ return { method: "getByTitle", arg: value, exact: true, overridden };
1357
1500
  }
1358
1501
  }
1502
+ return null;
1503
+ }
1504
+ function semanticPlan(step) {
1359
1505
  if (NAVIGATIONS.has(step.type)) {
1360
1506
  return { method: step.type === "back" ? "goBack" : "goForward", arg: "", overridden: false };
1361
1507
  }
@@ -1380,6 +1526,49 @@ function locatorPlan(step) {
1380
1526
  }
1381
1527
  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
1528
  }
1529
+ function locatorPlans(step) {
1530
+ const chosen = planForOverride(step.locator, true);
1531
+ if (chosen) return [chosen];
1532
+ const candidates = step.candidates ?? [];
1533
+ const plans = [
1534
+ ...candidates.filter((each) => each.strategy === "testId"),
1535
+ ...candidates.filter((each) => each.strategy !== "testId")
1536
+ ];
1537
+ const first = plans.findIndex((each) => each.strategy !== "testId");
1538
+ const before = first === -1 ? plans : plans.slice(0, first);
1539
+ const after = first === -1 ? [] : plans.slice(first);
1540
+ const semantic = semanticPlan(step);
1541
+ const ordered = [
1542
+ ...before.map((each) => planForOverride(each, false)),
1543
+ // Below a test id and above everything else. A name is what somebody
1544
+ // reading the test understands the step to be about, so a step that
1545
+ // resolves by it is the step still meaning what it says.
1546
+ semantic.method === "none" ? null : semantic,
1547
+ ...after.map((each) => planForOverride(each, false))
1548
+ ].filter((plan) => plan !== null);
1549
+ return ordered.length ? ordered : [semantic];
1550
+ }
1551
+ var quote = (s2) => `'${s2.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
1552
+ function locatorPlan(step) {
1553
+ return locatorPlans(step)[0];
1554
+ }
1555
+ function describeLocators(step) {
1556
+ return locatorPlans(step).map((plan) => describePlan(step, plan));
1557
+ }
1558
+ function describePlan(step, p) {
1559
+ if (p.method === "none") return "(nothing to resolve \u2014 no accessible name)";
1560
+ if (p.method === "goBack") return "page.goBack()";
1561
+ if (p.method === "goForward") return "page.goForward()";
1562
+ let root = "page";
1563
+ for (const frame of step.frame ?? []) root += `.frameLocator(${quote(frameSelector(frame))})`;
1564
+ if (!p.overridden && step.scope) root = scopeExpression(root, step.scope, step.scopeRole);
1565
+ 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} }` : ""})`;
1566
+ if (p.inputRole) base = `${root}.getByRole(${quote(p.inputRole)}, { name: ${quote(p.arg)}, exact: true }).or(${base}.and(${root}.locator(${quote(inputSelector(p.inputRole))})))`;
1567
+ if (p.visible) base += ".filter({ visible: true })";
1568
+ if (step.nth !== void 0) base += `.nth(${step.nth})`;
1569
+ if (step.pageId && step.pageId !== "main") base += ` // page ${quote(step.pageId)}`;
1570
+ return base;
1571
+ }
1383
1572
  function frameSelector(name) {
1384
1573
  const positional = /^nth:(\d+)$/.exec(name);
1385
1574
  if (positional) return `iframe, frame >> nth=${positional[1]}`;
@@ -1390,6 +1579,10 @@ function inputSelector(role) {
1390
1579
  return role === "textbox" ? 'input, textarea, [contenteditable="true"], [role="textbox"]' : `input, select, [role="${role}"]`;
1391
1580
  }
1392
1581
  var SCOPE_ROLES = ["form", "group", "region", "dialog", "alertdialog"];
1582
+ function scopeExpression(root, name, role) {
1583
+ const parts = (role ? [role] : SCOPE_ROLES).map((r) => `${root}.getByRole(${quote(r)}, { name: ${quote(name)}, exact: ${!role} })`);
1584
+ return parts.reduce((a, b2) => `${a}.or(${b2})`);
1585
+ }
1393
1586
  var CHECKS = [
1394
1587
  { id: "visible", label: "is visible", kind: "visible", group: "state" },
1395
1588
  { id: "not-visible", label: "is not visible", kind: "visible", not: true, group: "state" },
@@ -3105,6 +3298,30 @@ var RunActors = class {
3105
3298
  if (!id || id === "default") return this.opts.actors?.find((a) => a.id === (id ?? "default"))?.name ?? "the main participant";
3106
3299
  return this.opts.actors?.find((a) => a.id === id)?.name ?? id;
3107
3300
  }
3301
+ /** WHAT THIS PARTICIPANT STARTS AS, if anything.
3302
+ *
3303
+ * Asked by a `signIn` step before it runs: a flow carries its own sign-in
3304
+ * because that is how it gets itself ready when it runs ALONE, and in a
3305
+ * case the person is already that identity. Running it again logs in on top
3306
+ * of a session that is already signed in, which most applications answer by
3307
+ * redirecting away from the login page — so the form is not there and a
3308
+ * correctly signed-in run goes red. */
3309
+ identityOf(id) {
3310
+ return this.opts.actors?.find((a) => a.id === (id ?? "default"))?.startSignedIn;
3311
+ }
3312
+ /** SIGN THIS PERSON IN AGAIN, in the browser they already have.
3313
+ *
3314
+ * Not `freshGuest`, which is the opposite: that throws the context away to
3315
+ * start somebody over as a stranger, and leaves them one. This is for a
3316
+ * session that has simply gone — expired, or dropped by the application —
3317
+ * where the browser is the right browser and only the sign-in is missing.
3318
+ *
3319
+ * Un-initialising is what makes `select` do the preparation again rather
3320
+ * than assume it already happened. */
3321
+ async prepareAgain(id) {
3322
+ this.initialized.delete(id);
3323
+ await this.select(id);
3324
+ }
3108
3325
  async select(id, startUrl = this.opts.startUrl) {
3109
3326
  const actor = this.opts.actors?.find((a) => a.id === id);
3110
3327
  if (id !== "default" && !actor) throw new Error(`Unknown actor: ${id}`);
@@ -3170,6 +3387,42 @@ var RunActors = class {
3170
3387
  }
3171
3388
  this.initialized.add(id);
3172
3389
  }
3390
+ /** START THIS PARTICIPANT AGAIN, as themselves.
3391
+ *
3392
+ * `freshGuest` gives a browser that has never been anywhere, which is the
3393
+ * right answer for "start over as a stranger" and the wrong one for "Alice
3394
+ * on a second device": it would leave Alice signed out for the rest of the
3395
+ * case. So the context is replaced AND the participant is un-initialised,
3396
+ * which is what makes `select` navigate and run their sign-in again.
3397
+ *
3398
+ * The distinction is the whole difference between a row that says `fresh`
3399
+ * and a `resetActor` step, and both are worth having. */
3400
+ async restart(id, startUrl) {
3401
+ await this.freshGuest(id);
3402
+ this.initialized.delete(id);
3403
+ await this.select(id, startUrl);
3404
+ }
3405
+ /** HAND A GUEST'S BROWSER TO SOMEBODY, and sign them in there.
3406
+ *
3407
+ * Browsing as a guest and then signing in is one browser, not two: the
3408
+ * basket filled before signing in is the whole point of testing it, and a
3409
+ * participant given a browser of their own would never see it.
3410
+ *
3411
+ * The context is moved rather than copied — there is one of it — and the
3412
+ * participant is left un-initialised so `select` navigates and runs their
3413
+ * sign-in inside it. The guest keeps no claim on it; a later guest row
3414
+ * opens a browser of its own, because this one is signed in now. */
3415
+ async adopt(from, to, startUrl) {
3416
+ const session = this.sessions.get(from);
3417
+ if (!session) return this.select(to, startUrl);
3418
+ this.sessions.set(to, session);
3419
+ this.sessions.delete(from);
3420
+ this.initialized.delete(from);
3421
+ this.initialized.delete(to);
3422
+ this.current = session;
3423
+ this.currentId = to;
3424
+ await this.select(to, startUrl);
3425
+ }
3173
3426
  /** Replace a participant's context without disturbing anyone else's. */
3174
3427
  async replaceWithGuest(id, previous) {
3175
3428
  const session = await openRunSession({ ...this.opts, browser: this.main.browser, ...this.evidenceIn(`actor-${id}`) });
@@ -3189,6 +3442,10 @@ var RunActors = class {
3189
3442
  preparedHow() {
3190
3443
  return Object.fromEntries(this.preparations);
3191
3444
  }
3445
+ /** Whether this participant already has a browser open. */
3446
+ has(id) {
3447
+ return this.sessions.has(id);
3448
+ }
3192
3449
  /** Throw this participant's session away and give them a new signed-out one.
3193
3450
  *
3194
3451
  * NOT SIGNING OUT. An application can be signed out of while the browser
@@ -3553,8 +3810,15 @@ async function locate(page, step, opts = {}) {
3553
3810
  const loc = await locateAll(page, step, opts);
3554
3811
  return step.nth === void 0 ? loc : loc.nth(step.nth);
3555
3812
  }
3813
+ async function locateCandidates(page, step, opts = {}) {
3814
+ const built = [];
3815
+ for (const plan of locatorPlans(step)) built.push(await fromPlan(page, step, plan, opts));
3816
+ return step.nth === void 0 ? built : built.map((each) => each.nth(step.nth));
3817
+ }
3556
3818
  async function locateAll(page, step, opts = {}) {
3557
- const plan = locatorPlan(step);
3819
+ return fromPlan(page, step, locatorPlan(step), opts);
3820
+ }
3821
+ async function fromPlan(page, step, plan, opts = {}) {
3558
3822
  let root = rootFor(page, step);
3559
3823
  if (step.scope && !plan.overridden) {
3560
3824
  const roles = step.scopeRole ? [step.scopeRole] : [...SCOPE_ROLES];
@@ -4036,6 +4300,7 @@ async function performSignIn(session, step, opts) {
4036
4300
  }
4037
4301
  return { ok: true, label, detail: null, kind: null, checks: checked?.checks ?? [] };
4038
4302
  }
4303
+ var SIGNED_IN_CHECK_MS = 3e3;
4039
4304
  async function confirmSignedIn(page, auth, timeoutMs) {
4040
4305
  for (const check of auth.success) {
4041
4306
  if (check.enabled === false) continue;
@@ -4150,6 +4415,67 @@ async function runStepsIn(session, steps, opts) {
4150
4415
  error = "the run was cancelled";
4151
4416
  break;
4152
4417
  }
4418
+ if (step.type === "signIn" && !step.disabled && step.auth && opts.actors && opts.ownSignIn !== "run") {
4419
+ const identity = opts.actors.identityOf(opts.actors.currentId);
4420
+ const auth = opts.resolveAuth?.(step.auth);
4421
+ const dormant = opts.ownSignIn === "skip" || identity !== void 0 && sameIdentity(identity, step.auth);
4422
+ const expected = identity && !sameIdentity(identity, step.auth) ? opts.resolveAuth?.(identity) : auth;
4423
+ if (identity && auth && expected && hasSuccessCheck(expected) && dormant) {
4424
+ const started = Date.now();
4425
+ if ((await confirmSignedIn(session.page, expected, SIGNED_IN_CHECK_MS)).ok) {
4426
+ const provided = {
4427
+ stepId: step.id,
4428
+ name: `Sign in as ${expected.accountName}`,
4429
+ type: step.type,
4430
+ value: null,
4431
+ secret: false,
4432
+ status: "skipped",
4433
+ detail: `${opts.actors.nameOf(opts.actors.currentId)} already starts signed in as ${expected.accountName}`,
4434
+ failureKind: null,
4435
+ durationMs: Date.now() - started,
4436
+ checks: []
4437
+ };
4438
+ results.push(provided);
4439
+ onStepResult?.(provided);
4440
+ continue;
4441
+ }
4442
+ if (!sameIdentity(identity, step.auth)) {
4443
+ const started2 = Date.now();
4444
+ const who = opts.actors.currentId;
4445
+ let recovered = null;
4446
+ try {
4447
+ await opts.actors.prepareAgain(who);
4448
+ session = opts.actors.current;
4449
+ ({ page, pages, config, artifacts } = session);
4450
+ if (!(await confirmSignedIn(session.page, expected, SIGNED_IN_CHECK_MS)).ok) {
4451
+ recovered = "signing them in again did not take";
4452
+ }
4453
+ } catch (failure) {
4454
+ recovered = firstLine2(failure);
4455
+ }
4456
+ const outcome = {
4457
+ stepId: step.id,
4458
+ name: `Sign in as ${expected.accountName}`,
4459
+ type: step.type,
4460
+ value: null,
4461
+ secret: false,
4462
+ status: recovered ? "failed" : "skipped",
4463
+ 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}`,
4464
+ failureKind: recovered ? "unknown" : null,
4465
+ durationMs: Date.now() - started2,
4466
+ checks: []
4467
+ };
4468
+ results.push(outcome);
4469
+ onStepResult?.(outcome);
4470
+ if (recovered) {
4471
+ status2 = "failed";
4472
+ error = `${outcome.name}: ${outcome.detail}`;
4473
+ break;
4474
+ }
4475
+ continue;
4476
+ }
4477
+ }
4478
+ }
4153
4479
  if ((step.type === "signIn" || step.type === "signOut") && !step.disabled) {
4154
4480
  const started = Date.now();
4155
4481
  const outcome = await performSignIn(session, step, opts);
@@ -4217,7 +4543,13 @@ async function runStepsIn(session, steps, opts) {
4217
4543
  baseUrl: opts.baseUrl,
4218
4544
  ...opts.actors ? { actors: opts.actors } : {},
4219
4545
  timeoutMs: config.timeoutMs,
4220
- navigationTimeoutMs: config.navigationTimeoutMs
4546
+ navigationTimeoutMs: config.navigationTimeoutMs,
4547
+ // SAID OUT LOUD, on a step that passed. The element was found, but
4548
+ // not the way this step says it should be — something has moved,
4549
+ // and the run is the only place anybody would learn it.
4550
+ onFallback: (_step, foundBy) => {
4551
+ result.foundBy = foundBy;
4552
+ }
4221
4553
  });
4222
4554
  for (const a of step.proposals ?? []) {
4223
4555
  if (a.enabled === false) continue;
@@ -4424,8 +4756,9 @@ async function executeStep(main, input, options = {}) {
4424
4756
  if (step.scroll && step.type !== "scroll") {
4425
4757
  await applyScroll(page, step, Math.min(2e3, remaining()));
4426
4758
  }
4427
- const loc = await locate(page, step);
4428
- await ensureTarget(page, loc, step, remaining());
4759
+ const target = await ensureTarget(page, step, remaining());
4760
+ const loc = target.loc;
4761
+ if (target.foundBy) options.onFallback?.(step, target.foundBy);
4429
4762
  page.setDefaultTimeout(remaining());
4430
4763
  await applyAction(loc, step, value, { resolveFile: options.resolveFile, dropTarget: await locateDrop(page, step) });
4431
4764
  };
@@ -4477,21 +4810,38 @@ async function applyScroll(page, step, timeout) {
4477
4810
  } catch {
4478
4811
  }
4479
4812
  }
4480
- async function ensureTarget(page, loc, step, timeout) {
4813
+ async function ensureTarget(page, step, timeout) {
4814
+ const candidates = await locateCandidates(page, step);
4815
+ const described = describeLocators(step);
4816
+ const primary = candidates[0];
4481
4817
  const firstWait = Math.max(500, Math.floor(timeout * 0.45));
4482
4818
  const recovery = Math.max(1e3, Math.floor(timeout * 0.35));
4819
+ let several = null;
4483
4820
  try {
4484
- await loc.waitFor({ state: "attached", timeout: firstWait });
4485
- return;
4821
+ await primary.waitFor({ state: "attached", timeout: firstWait });
4822
+ return { loc: primary };
4486
4823
  } catch (error) {
4487
- if (/strict mode violation/.test(String(error))) {
4488
- throw Object.assign(error, { kind: "ambiguous" });
4489
- }
4824
+ if (/strict mode violation/.test(String(error))) several = error;
4825
+ }
4826
+ let crowded = null;
4827
+ for (let at = 1; at < candidates.length; at += 1) {
4828
+ const found = await candidates[at].count().catch(() => 0);
4829
+ if (found === 1) return { loc: candidates[at], foundBy: described[at] ?? "another locator" };
4830
+ if (found > 1 && !crowded) crowded = { where: described[at] ?? "another locator", count: found };
4831
+ }
4832
+ if (several) throw Object.assign(several, { kind: "ambiguous" });
4833
+ if (crowded) {
4834
+ throw Object.assign(
4835
+ new Error(
4836
+ `"${step.name}" could not be found the way this step records it, and ${crowded.count} elements match ${crowded.where} \u2014 nothing this step knows tells them apart, so narrow it in the editor`
4837
+ ),
4838
+ { kind: "ambiguous" }
4839
+ );
4490
4840
  }
4491
- if (await scrollUntilFound(page, loc, recovery)) return;
4841
+ if (await scrollUntilFound(page, primary, recovery)) return { loc: primary };
4492
4842
  throw Object.assign(
4493
4843
  new Error(
4494
- `no element named "${step.name}" \u2014 nothing on this page matches the target or its recorded scope`
4844
+ 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
4845
  ),
4496
4846
  { kind: "not_found" }
4497
4847
  );
@@ -4533,6 +4883,7 @@ async function replayCase(opts) {
4533
4883
  const outcomes = [];
4534
4884
  let status2 = "passed";
4535
4885
  let error = null;
4886
+ const lanes = sessionLanes(occurrences, opts.defaultActorId);
4536
4887
  try {
4537
4888
  for (const [index, occurrence] of occurrences.entries()) {
4538
4889
  if (status2 !== "passed" || opts.signal?.aborted) {
@@ -4555,9 +4906,15 @@ async function replayCase(opts) {
4555
4906
  opts.onOccurrenceResult?.(skipped);
4556
4907
  continue;
4557
4908
  }
4558
- const navigates = occurrence.navigateFirst;
4909
+ const navigates = navigatesFirst(occurrence);
4910
+ const who = occurrence.actorId ?? opts.defaultActorId ?? "default";
4911
+ const entry = resolveStartUrl(occurrence.baseUrl ?? baseUrl, occurrence.startPath ?? "/");
4912
+ const plan = lanes[index];
4559
4913
  try {
4560
- await actors.select(occurrence.actorId ?? opts.defaultActorId ?? "default", resolveStartUrl(occurrence.baseUrl ?? baseUrl, occurrence.startPath ?? "/"));
4914
+ if (plan.opens && startOf(occurrence) === "fresh") await actors.restart(who, entry);
4915
+ else if (plan.opens) await actors.select(who, entry);
4916
+ else if (actors.currentId !== who && !actors.has(who)) await actors.adopt("default", who, entry);
4917
+ else await actors.select(who, entry);
4561
4918
  } catch (failure) {
4562
4919
  status2 = "failed";
4563
4920
  error = failure.message;
@@ -4570,6 +4927,7 @@ async function replayCase(opts) {
4570
4927
  baseUrl: occurrence.baseUrl ?? baseUrl,
4571
4928
  ...opts.resolveAuth ? { resolveAuth: opts.resolveAuth } : {},
4572
4929
  stepOffset: outcomes.reduce((count, previous) => count + previous.stepsTotal, 0),
4930
+ ...occurrence.ownSignIn ? { ownSignIn: occurrence.ownSignIn } : {},
4573
4931
  ...opts.resolveSecret === void 0 ? {} : { resolveSecret: opts.resolveSecret },
4574
4932
  ...opts.resolveFile === void 0 ? {} : { resolveFile: opts.resolveFile },
4575
4933
  ...opts.evidenceDir === void 0 ? {} : { evidenceDir: opts.evidenceDir },
@@ -4833,6 +5191,7 @@ async function startRecordingSession(opts) {
4833
5191
  ...payload.frame?.length ? { frame: payload.frame } : {},
4834
5192
  ...payload.to ? { to: payload.to } : {},
4835
5193
  ...payload.locator ? { locator: payload.locator } : {},
5194
+ ...payload.candidates?.length ? { candidates: payload.candidates } : {},
4836
5195
  ...payload.values ? { values: payload.values } : {},
4837
5196
  ...payload.warning ? { warning: payload.warning } : {},
4838
5197
  ...payload.pageId ? { pageId: payload.pageId } : {},
@@ -5045,6 +5404,7 @@ async function startRecordingSession(opts) {
5045
5404
  ...parsed.target?.scopeRole ? { scopeRole: parsed.target.scopeRole } : {},
5046
5405
  ...parsed.target?.frame?.length ? { frame: parsed.target.frame } : {},
5047
5406
  ...parsed.target?.locator ? { locator: parsed.target.locator } : {},
5407
+ ...parsed.target?.candidates?.length ? { candidates: parsed.target.candidates } : {},
5048
5408
  ...parsed.target?.pageId ? { pageId: parsed.target.pageId } : {},
5049
5409
  value: "",
5050
5410
  secret: false,
@@ -5676,7 +6036,7 @@ async function startDebugSession(opts) {
5676
6036
  return session;
5677
6037
  }
5678
6038
 
5679
- // ../results/dist/chunk-WEBNFZEW.js
6039
+ // ../results/dist/chunk-LYCTVHPR.js
5680
6040
  import { z as z5 } from "zod";
5681
6041
  var RunStatusSchema = z5.enum([
5682
6042
  "queued",
@@ -5744,6 +6104,13 @@ var RunStepSchema = z5.object({
5744
6104
  status: z5.enum(["passed", "failed", "skipped"]),
5745
6105
  failureKind: FailureKindSchema.nullable(),
5746
6106
  detail: z5.string().nullable(),
6107
+ /** WHICH WAY FOUND IT, when the first way did not.
6108
+ *
6109
+ * A step carries several routes to its element and takes the first that
6110
+ * matches. Passing on a later one is not a failure, but it does mean the
6111
+ * page has moved under the step — and a suite that repairs itself quietly
6112
+ * drifts away from the application it tests. Null is the ordinary case. */
6113
+ foundBy: z5.string().nullable().default(null),
5747
6114
  durationMs: z5.number().int().nonnegative().nullable(),
5748
6115
  /** Where the browser was when this step ran. Recorded on failure, and
5749
6116
  * frequently the whole answer — the element is missing because a step
@@ -6004,6 +6371,16 @@ var MIGRATIONS2 = [
6004
6371
  `
6005
6372
  ALTER TABLE run_step ADD COLUMN value TEXT;
6006
6373
  ALTER TABLE run_step ADD COLUMN secret INTEGER NOT NULL DEFAULT 0;
6374
+ `,
6375
+ // 5 — a step that found its element some way other than the first it tried.
6376
+ //
6377
+ // Nullable, and NULL is the ordinary case: the step was found the way it
6378
+ // says it should be. It is recorded because the alternative is a suite that
6379
+ // repairs itself quietly and drifts away from the application it tests —
6380
+ // and because "which steps are limping?" is a question about history, not
6381
+ // about the run somebody happens to be watching.
6382
+ `
6383
+ ALTER TABLE run_step ADD COLUMN found_by TEXT;
6007
6384
  `
6008
6385
  ];
6009
6386
  var SCHEMA_VERSION = MIGRATIONS2.length;
@@ -6216,8 +6593,8 @@ var SqliteStorage = class {
6216
6593
  this.handle.prepare(
6217
6594
  `INSERT INTO run_step (id, run_task_id, ordinal, step_id, action, target_role,
6218
6595
  target_name, target_scope, status, failure_kind, detail, duration_ms,
6219
- page_url, page_title, value, secret)
6220
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6596
+ page_url, page_title, value, secret, found_by)
6597
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
6221
6598
  ).run(
6222
6599
  id,
6223
6600
  taskId,
@@ -6235,7 +6612,8 @@ var SqliteStorage = class {
6235
6612
  input.pageTitle ?? null,
6236
6613
  // Never the plaintext. A secret arrives here already withheld.
6237
6614
  input.secret ? null : input.value ?? null,
6238
- b(input.secret ?? false)
6615
+ b(input.secret ?? false),
6616
+ input.foundBy ?? null
6239
6617
  );
6240
6618
  const stmt = this.handle.prepare(
6241
6619
  `INSERT INTO run_check (id, run_step_id, assertion_id, kind, expected, actual, ok, soft, detail)
@@ -6379,6 +6757,7 @@ var SqliteStorage = class {
6379
6757
  durationMs: n(st.duration_ms),
6380
6758
  pageUrl: s(st.page_url),
6381
6759
  pageTitle: s(st.page_title),
6760
+ foundBy: s(st.found_by),
6382
6761
  checks: checksByStep.get(String(st.id)) ?? []
6383
6762
  });
6384
6763
  stepsByTask.set(String(st.run_task_id), list);
@@ -7519,6 +7898,9 @@ function toStepRow(ordinal, result, step) {
7519
7898
  durationMs: result.durationMs,
7520
7899
  pageUrl: result.evidence?.url ?? null,
7521
7900
  pageTitle: result.evidence?.title ?? null,
7901
+ // Kept, because "which steps are limping?" is a question about history
7902
+ // rather than about the run somebody happens to be watching.
7903
+ foundBy: result.foundBy ?? null,
7522
7904
  checks
7523
7905
  };
7524
7906
  }
@@ -7585,7 +7967,7 @@ async function executeCase(opts) {
7585
7967
  let outcome;
7586
7968
  try {
7587
7969
  outcome = await replayCase({
7588
- occurrences: caseOccurrences(testCase),
7970
+ occurrences: testCase.occurrences,
7589
7971
  ...testCase.actors ? { actors: testCase.actors } : {},
7590
7972
  ...testCase.defaultActorId ? { defaultActorId: testCase.defaultActorId } : {},
7591
7973
  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-O3S4QSN4.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-O3S4QSN4.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.22",
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",