assuremind 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var Ut=Object.create;var Y=Object.defineProperty;var kt=Object.getOwnPropertyDescriptor;var Gt=Object.getOwnPropertyNames;var Mt=Object.getPrototypeOf,Bt=Object.prototype.hasOwnProperty;var Wt=(e,t)=>{for(var n in t)Y(e,n,{get:t[n],enumerable:!0})},Ve=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Gt(t))!Bt.call(e,i)&&i!==n&&Y(e,i,{get:()=>t[i],enumerable:!(r=kt(t,i))||r.enumerable});return e};var h=(e,t,n)=>(n=e!=null?Ut(Mt(e)):{},Ve(t||!e||!e.__esModule?Y(n,"default",{value:e,enumerable:!0}):n,e)),Jt=e=>Ve(Y({},"__esModule",{value:!0}),e);var Wn={};Wt(Wn,{AssuremindError:()=>v,ConfigError:()=>C,ExecutionError:()=>Z,HealingError:()=>Q,ProviderError:()=>X,StorageError:()=>g,ValidationError:()=>u,acceptHealingEvent:()=>Ct,appendPendingEvent:()=>jt,callFaker:()=>zt,clearEnvCache:()=>Dt,configExists:()=>ut,createCase:()=>et,createChildLogger:()=>b,createSuite:()=>Ke,defineConfig:()=>Bn,deleteCase:()=>nt,deleteGlobalVariable:()=>ct,deleteResult:()=>bt,deleteSuite:()=>qe,formatError:()=>Ue,generateCacheKey:()=>$t,getAvailableGenerators:()=>Ft,getCasePath:()=>it,getHealingStats:()=>_t,isAssuremindError:()=>ze,listCasePaths:()=>Se,listCases:()=>rt,listHealingReportIds:()=>Nt,listResultIds:()=>_e,listResults:()=>xt,listSuiteDirs:()=>ne,listSuites:()=>Xe,listSuitesWithCounts:()=>Ze,listVariableFiles:()=>lt,logger:()=>he,normalizeUrl:()=>$e,pruneResolvedEvents:()=>Pt,readCase:()=>ae,readConfig:()=>le,readEnvVariables:()=>ve,readGlobalVariables:()=>we,readHealingReport:()=>It,readPendingEvents:()=>V,readResult:()=>Pe,readSuite:()=>G,readVariables:()=>H,redactSecrets:()=>Be,rejectHealingEvent:()=>Tt,resolveVariables:()=>ot,sanitizeGeneratedCode:()=>Me,screenshotsDir:()=>St,setGlobalVariable:()=>st,sha256:()=>Oe,toSlug:()=>N,tracesDir:()=>Et,tryGetEnv:()=>Ht,updateCase:()=>tt,updateConfig:()=>pt,updateSuite:()=>Ye,validateConfig:()=>dt,validateEnv:()=>Le,videosDir:()=>yt,writeCase:()=>oe,writeConfig:()=>Ie,writeHealingReport:()=>Rt,writeResult:()=>ht,writeSuite:()=>te,writeVariables:()=>se});module.exports=Jt(Wn);var c=require("zod"),Kt=c.z.enum(["off","on","only-on-failure"]),Yt=c.z.enum(["off","on","on-first-retry","retain-on-failure"]),qt=c.z.enum(["off","on","on-first-retry","retain-on-failure"]),De=c.z.enum(["chromium","firefox","webkit"]),Xt=c.z.enum(["commit","domcontentloaded","load","networkidle"]),He=c.z.enum(["dev","stage","test","prod"]),Zt=c.z.object({dev:c.z.string().url().or(c.z.literal("")).default(""),stage:c.z.string().url().or(c.z.literal("")).default(""),test:c.z.string().url().or(c.z.literal("")).default(""),prod:c.z.string().url().or(c.z.literal("")).default("")}),Qt=c.z.object({enabled:c.z.boolean(),maxLevel:c.z.number().int().min(1).max(5),dailyBudget:c.z.number().positive(),autoPR:c.z.boolean()}),en=c.z.object({allure:c.z.boolean(),html:c.z.boolean(),json:c.z.boolean()}),tn=c.z.object({width:c.z.number().int().positive(),height:c.z.number().int().positive()}),nn=c.z.object({name:c.z.string().min(1),environment:He,baseUrl:c.z.string().url(),browsers:c.z.array(De).min(1),headless:c.z.boolean().optional()}),z=c.z.object({baseUrl:c.z.string().url(),environment:He.default("stage"),environmentUrls:Zt.default({dev:"",stage:"",test:"",prod:""}),browsers:c.z.array(De).min(1),headless:c.z.boolean(),viewport:tn.default({width:1280,height:720}),timeout:c.z.number().int().positive(),retries:c.z.number().int().min(0),parallel:c.z.number().int().positive(),pageLoad:Xt.default("domcontentloaded"),screenshot:Kt,video:Yt,trace:qt,healing:Qt,reporting:en,studioPort:c.z.number().int().min(1024).max(65535),profiles:c.z.array(nn).default([]),activeProfile:c.z.string().optional(),device:c.z.string().optional(),rag:c.z.object({enabled:c.z.boolean(),codeCorpus:c.z.object({enabled:c.z.boolean(),maxEntries:c.z.number().int().positive(),similarityThreshold:c.z.number().min(0).max(1),directUseThreshold:c.z.number().min(0).max(1)}),healingCorpus:c.z.object({enabled:c.z.boolean(),maxEntries:c.z.number().int().positive(),similarityThreshold:c.z.number().min(0).max(1)}),errorCatalog:c.z.object({enabled:c.z.boolean(),maxEntries:c.z.number().int().positive()}),embedder:c.z.literal("tfidf")}).default({enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"}),mcp:c.z.object({enabled:c.z.boolean(),headless:c.z.boolean(),actThenScript:c.z.boolean(),proactiveHealing:c.z.boolean(),actionTimeout:c.z.number().int().positive(),idleTimeout:c.z.number().int().positive()}).default({enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4})}),q={baseUrl:"http://localhost:3000",environment:"stage",environmentUrls:{dev:"",stage:"http://localhost:3000",test:"",prod:""},browsers:["chromium"],headless:!0,viewport:{width:1280,height:720},timeout:3e4,retries:1,parallel:1,pageLoad:"domcontentloaded",screenshot:"only-on-failure",video:"off",trace:"on-first-retry",healing:{enabled:!0,maxLevel:5,dailyBudget:5,autoPR:!1},reporting:{allure:!0,html:!0,json:!0},studioPort:4400,profiles:[],rag:{enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"},mcp:{enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4}};var S=h(require("path")),I=h(require("fs-extra")),Je=require("uuid");var l=require("zod"),U=l.z.object({id:l.z.string().min(1),order:l.z.number().int().positive(),instruction:l.z.string().min(1),generatedCode:l.z.string(),strategy:l.z.enum(["template","cache","rag","batch","fast","primary","recorder"]),stepType:l.z.enum(["ui","api","mock"]).default("ui"),lastHealed:l.z.string().nullable(),timeout:l.z.number().int().positive().optional(),retries:l.z.number().int().min(0).optional(),mockUrl:l.z.string().optional(),mockResponse:l.z.string().optional(),mockStatus:l.z.number().int().optional(),runAudit:l.z.boolean().optional(),soft:l.z.boolean().optional(),visualCheck:l.z.boolean().optional()}),rn=l.z.object({type:l.z.enum(["inline","json-file","csv-file"]),path:l.z.string().optional(),data:l.z.array(l.z.record(l.z.string())).optional()}).optional(),Fe=l.z.object({id:l.z.string(),instruction:l.z.string(),generatedCode:l.z.string().default(""),order:l.z.number().int().default(0)}),an=l.z.object({before:l.z.array(Fe).default([]),after:l.z.array(Fe).default([])}).default({before:[],after:[]}),de=l.z.object({id:l.z.string().min(1),name:l.z.string().min(1),description:l.z.string(),tags:l.z.array(l.z.string()),priority:l.z.enum(["critical","high","medium","low"]),timeout:l.z.number().int().positive().optional(),dataSource:rn,steps:l.z.array(U),caseHooks:an,lighthouseCategories:l.z.array(l.z.enum(["performance","accessibility","seo"])).default(["performance","accessibility","seo"]),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}),ge=l.z.object({id:l.z.string().min(1),name:l.z.string().min(1),description:l.z.string(),tags:l.z.array(l.z.string()),type:l.z.enum(["ui","api","audit","performance"]).default("ui"),timeout:l.z.number().int().positive().optional(),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}),qn=l.z.enum(["before_all","before_each","after_each","after_all"]),Xn=l.z.object({before_all:l.z.array(U).default([]),before_each:l.z.array(U).default([]),after_each:l.z.array(U).default([]),after_all:l.z.array(U).default([])});var v=class extends Error{code;constructor(t,n){super(t),this.name="AssuremindError",this.code=n,Object.setPrototypeOf(this,new.target.prototype)}},X=class extends v{provider;constructor(t,n,r="PROVIDER_ERROR"){super(t,r),this.name="ProviderError",this.provider=n}},Z=class extends v{stepId;constructor(t,n,r="EXECUTION_ERROR"){super(t,r),this.name="ExecutionError",this.stepId=n}},C=class extends v{constructor(t,n="CONFIG_ERROR"){super(t,n),this.name="ConfigError"}},u=class extends v{field;constructor(t,n,r="VALIDATION_ERROR"){super(t,r),this.name="ValidationError",this.field=n}},Q=class extends v{level;constructor(t,n,r="HEALING_ERROR"){super(t,r),this.name="HealingError",this.level=n}},g=class extends v{path;constructor(t,n,r="STORAGE_ERROR"){super(t,r),this.name="StorageError",this.path=n}};function ze(e){return e instanceof v}function Ue(e){return e instanceof v?`[${e.code}] ${e.message}`:e instanceof Error?e.message:String(e)}var fe=h(require("pino")),on=h(require("fs-extra"));var sn=process.env.NODE_ENV!=="production",ke=sn?{target:"pino-pretty",options:{colorize:!0,translateTime:"HH:MM:ss",ignore:"pid,hostname",messageFormat:"[assuremind] {msg}"}}:void 0,he=(0,fe.default)({level:process.env.LOG_LEVEL??"info",base:{name:"assuremind"}},ke?fe.default.transport(ke):void 0);function b(e){return he.child({component:e})}function cn(e){let t=/^```(?:typescript|javascript|ts|js)?\n?([\s\S]*?)```\s*$/m,n=e.match(t);return n?.[1]!==void 0?n[1].trim():e.trim()}var ln=[/\brequire\s*\(/,/\bimport\s*\(/,/\bprocess\b/,/\bchild_process\b/,/\bexec\s*\(/,/\bspawn\s*\(/,/\beval\s*\(/,/\bFunction\s*\(/,/\b__dirname\b/,/\b__filename\b/,/\bglobal\b/,/\bwindow\.location\.href\s*=/,/\bdocument\.cookie\b/,/\blocalStorage\b/,/\bsessionStorage\b/,/\bIndexedDB\b/,/\bXMLHttpRequest\b/,/\bfetch\s*\(/,/\bWebSocket\s*\(/];function mn(e){for(let t of ln)if(t.test(e))throw new u(`Generated code contains forbidden pattern: ${t.source}. This may be a prompt injection attempt. Please regenerate the step.`,"generatedCode","UNSAFE_CODE")}var pn=/\s+(?:field|input|box|textbox|text\s?box|drop-?\s?down|dropdown|combo\s?box|combobox|menu|selector|element|checkbox|radio\s?button|radio)$/i;function Ge(e){let t=e.replace(pn,"").trim();return t.length>0?t:e}function un(e){let t=e;return t=t.replace(/(getBy(?:Label|Placeholder)\(\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Ge(a)}${i}`),t=t.replace(/(name:\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Ge(a)}${i}`),t}function dn(e){let t=e;return t=t.replace(/\.or\((?:[^()]*|\([^()]*\))*\)(?:\.(?:first|last)\(\))?/g,""),t=t.replace(/\.(?:first|last)\(\)(?=\.\w)/g,""),t=un(t),t}function Me(e){let t=cn(e);if(!t)throw new u("AI returned an empty code response. Please try regenerating.","generatedCode","EMPTY_CODE");let n=dn(t);return mn(n),n}function N(e){return e.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function Be(e,t){let n=e;for(let r of t)r.length>0&&(n=n.replaceAll(r,"[REDACTED]"));return n}var xe=h(require("path")),R=h(require("fs-extra"));async function x(e,t){let n=xe.default.dirname(e);await R.default.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.default.writeJson(r,t,{spaces:2}),await R.default.rename(r,e)}catch(i){throw await R.default.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}async function E(e){try{return await R.default.readJson(e)}catch(t){throw new g(`Failed to read JSON file "${e}": ${t instanceof Error?t.message:String(t)}`,e,"READ_FAILED")}}async function We(e,t){let n=xe.default.dirname(e);await R.default.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.default.writeFile(r,t,"utf8"),await R.default.rename(r,e)}catch(i){throw await R.default.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}var k=b("suite-store"),ee="suite.json";async function G(e){let t=S.default.join(e,ee);if(!await I.default.pathExists(t))throw new g(`Suite file not found at "${t}". Ensure the suite directory exists and contains a suite.json file.`,t,"SUITE_NOT_FOUND");let n=await E(t),r=ge.safeParse(n);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
1
+ "use strict";var Ut=Object.create;var Y=Object.defineProperty;var kt=Object.getOwnPropertyDescriptor;var Gt=Object.getOwnPropertyNames;var Mt=Object.getPrototypeOf,Bt=Object.prototype.hasOwnProperty;var Wt=(e,t)=>{for(var n in t)Y(e,n,{get:t[n],enumerable:!0})},Ve=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Gt(t))!Bt.call(e,i)&&i!==n&&Y(e,i,{get:()=>t[i],enumerable:!(r=kt(t,i))||r.enumerable});return e};var h=(e,t,n)=>(n=e!=null?Ut(Mt(e)):{},Ve(t||!e||!e.__esModule?Y(n,"default",{value:e,enumerable:!0}):n,e)),Jt=e=>Ve(Y({},"__esModule",{value:!0}),e);var Jn={};Wt(Jn,{AssuremindError:()=>v,ConfigError:()=>C,ExecutionError:()=>Z,HealingError:()=>Q,ProviderError:()=>X,StorageError:()=>g,ValidationError:()=>u,acceptHealingEvent:()=>Ct,appendPendingEvent:()=>jt,callFaker:()=>zt,clearEnvCache:()=>Dt,configExists:()=>ut,createCase:()=>et,createChildLogger:()=>b,createSuite:()=>Ke,defineConfig:()=>Wn,deleteCase:()=>nt,deleteGlobalVariable:()=>ct,deleteResult:()=>bt,deleteSuite:()=>qe,formatError:()=>Ue,generateCacheKey:()=>$t,getAvailableGenerators:()=>Ft,getCasePath:()=>it,getHealingStats:()=>_t,isAssuremindError:()=>ze,listCasePaths:()=>Se,listCases:()=>rt,listHealingReportIds:()=>Nt,listResultIds:()=>_e,listResults:()=>xt,listSuiteDirs:()=>ne,listSuites:()=>Xe,listSuitesWithCounts:()=>Ze,listVariableFiles:()=>lt,logger:()=>he,normalizeUrl:()=>$e,pruneResolvedEvents:()=>Pt,readCase:()=>ae,readConfig:()=>le,readEnvVariables:()=>ve,readGlobalVariables:()=>we,readHealingReport:()=>It,readPendingEvents:()=>V,readResult:()=>Pe,readSuite:()=>G,readVariables:()=>H,redactSecrets:()=>Be,rejectHealingEvent:()=>Tt,resolveVariables:()=>ot,sanitizeGeneratedCode:()=>Me,screenshotsDir:()=>St,setGlobalVariable:()=>st,sha256:()=>Oe,toSlug:()=>N,tracesDir:()=>Et,tryGetEnv:()=>Ht,updateCase:()=>tt,updateConfig:()=>pt,updateSuite:()=>Ye,validateConfig:()=>dt,validateEnv:()=>Le,videosDir:()=>yt,writeCase:()=>oe,writeConfig:()=>Ie,writeHealingReport:()=>Rt,writeResult:()=>ht,writeSuite:()=>te,writeVariables:()=>se});module.exports=Jt(Jn);var c=require("zod"),Kt=c.z.enum(["off","on","only-on-failure"]),Yt=c.z.enum(["off","on","on-first-retry","retain-on-failure"]),qt=c.z.enum(["off","on","on-first-retry","retain-on-failure"]),De=c.z.enum(["chromium","firefox","webkit"]),Xt=c.z.enum(["commit","domcontentloaded","load","networkidle"]),He=c.z.enum(["dev","stage","test","prod"]),Zt=c.z.object({dev:c.z.string().url().or(c.z.literal("")).default(""),stage:c.z.string().url().or(c.z.literal("")).default(""),test:c.z.string().url().or(c.z.literal("")).default(""),prod:c.z.string().url().or(c.z.literal("")).default("")}),Qt=c.z.object({enabled:c.z.boolean(),maxLevel:c.z.number().int().min(1).max(5),dailyBudget:c.z.number().positive(),autoPR:c.z.boolean(),healAssertionLocators:c.z.boolean().default(!1)}),en=c.z.object({allure:c.z.boolean(),html:c.z.boolean(),json:c.z.boolean()}),tn=c.z.object({width:c.z.number().int().positive(),height:c.z.number().int().positive()}),nn=c.z.object({name:c.z.string().min(1),environment:He,baseUrl:c.z.string().url(),browsers:c.z.array(De).min(1),headless:c.z.boolean().optional()}),z=c.z.object({baseUrl:c.z.string().url(),environment:He.default("stage"),environmentUrls:Zt.default({dev:"",stage:"",test:"",prod:""}),browsers:c.z.array(De).min(1),headless:c.z.boolean(),viewport:tn.default({width:1280,height:720}),timeout:c.z.number().int().positive(),retries:c.z.number().int().min(0),parallel:c.z.number().int().positive(),pageLoad:Xt.default("domcontentloaded"),screenshot:Kt,video:Yt,trace:qt,healing:Qt,reporting:en,studioPort:c.z.number().int().min(1024).max(65535),profiles:c.z.array(nn).default([]),activeProfile:c.z.string().optional(),device:c.z.string().optional(),rag:c.z.object({enabled:c.z.boolean(),codeCorpus:c.z.object({enabled:c.z.boolean(),maxEntries:c.z.number().int().positive(),similarityThreshold:c.z.number().min(0).max(1),directUseThreshold:c.z.number().min(0).max(1)}),healingCorpus:c.z.object({enabled:c.z.boolean(),maxEntries:c.z.number().int().positive(),similarityThreshold:c.z.number().min(0).max(1)}),errorCatalog:c.z.object({enabled:c.z.boolean(),maxEntries:c.z.number().int().positive()}),embedder:c.z.literal("tfidf")}).default({enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"}),mcp:c.z.object({enabled:c.z.boolean(),headless:c.z.boolean(),actThenScript:c.z.boolean(),proactiveHealing:c.z.boolean(),actionTimeout:c.z.number().int().positive(),idleTimeout:c.z.number().int().positive()}).default({enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4}),ci:c.z.object({writeToRepo:c.z.boolean()}).default({writeToRepo:!1})}),q={baseUrl:"http://localhost:3000",environment:"stage",environmentUrls:{dev:"",stage:"http://localhost:3000",test:"",prod:""},browsers:["chromium"],headless:!0,viewport:{width:1280,height:720},timeout:3e4,retries:1,parallel:1,pageLoad:"domcontentloaded",screenshot:"only-on-failure",video:"off",trace:"on-first-retry",healing:{enabled:!0,maxLevel:5,dailyBudget:5,autoPR:!1,healAssertionLocators:!1},reporting:{allure:!0,html:!0,json:!0},studioPort:4400,profiles:[],rag:{enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"},mcp:{enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4},ci:{writeToRepo:!1}};var S=h(require("path")),I=h(require("fs-extra")),Je=require("uuid");var l=require("zod"),U=l.z.object({id:l.z.string().min(1),order:l.z.number().int().positive(),instruction:l.z.string().min(1),generatedCode:l.z.string(),strategy:l.z.enum(["template","cache","rag","batch","fast","primary","recorder"]),stepType:l.z.enum(["ui","api","mock"]).default("ui"),lastHealed:l.z.string().nullable(),timeout:l.z.number().int().positive().optional(),retries:l.z.number().int().min(0).optional(),mockUrl:l.z.string().optional(),mockResponse:l.z.string().optional(),mockStatus:l.z.number().int().optional(),runAudit:l.z.boolean().optional(),soft:l.z.boolean().optional(),visualCheck:l.z.boolean().optional()}),rn=l.z.object({type:l.z.enum(["inline","json-file","csv-file"]),path:l.z.string().optional(),data:l.z.array(l.z.record(l.z.string())).optional()}).optional(),Fe=l.z.object({id:l.z.string(),instruction:l.z.string(),generatedCode:l.z.string().default(""),order:l.z.number().int().default(0)}),an=l.z.object({before:l.z.array(Fe).default([]),after:l.z.array(Fe).default([])}).default({before:[],after:[]}),de=l.z.object({id:l.z.string().min(1),name:l.z.string().min(1),description:l.z.string(),tags:l.z.array(l.z.string()),priority:l.z.enum(["critical","high","medium","low"]),timeout:l.z.number().int().positive().optional(),dataSource:rn,steps:l.z.array(U),caseHooks:an,lighthouseCategories:l.z.array(l.z.enum(["performance","accessibility","seo"])).default(["performance","accessibility","seo"]),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}),ge=l.z.object({id:l.z.string().min(1),name:l.z.string().min(1),description:l.z.string(),tags:l.z.array(l.z.string()),type:l.z.enum(["ui","api","audit","performance"]).default("ui"),timeout:l.z.number().int().positive().optional(),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}),Xn=l.z.enum(["before_all","before_each","after_each","after_all"]),Zn=l.z.object({before_all:l.z.array(U).default([]),before_each:l.z.array(U).default([]),after_each:l.z.array(U).default([]),after_all:l.z.array(U).default([])});var v=class extends Error{code;constructor(t,n){super(t),this.name="AssuremindError",this.code=n,Object.setPrototypeOf(this,new.target.prototype)}},X=class extends v{provider;constructor(t,n,r="PROVIDER_ERROR"){super(t,r),this.name="ProviderError",this.provider=n}},Z=class extends v{stepId;constructor(t,n,r="EXECUTION_ERROR"){super(t,r),this.name="ExecutionError",this.stepId=n}},C=class extends v{constructor(t,n="CONFIG_ERROR"){super(t,n),this.name="ConfigError"}},u=class extends v{field;constructor(t,n,r="VALIDATION_ERROR"){super(t,r),this.name="ValidationError",this.field=n}},Q=class extends v{level;constructor(t,n,r="HEALING_ERROR"){super(t,r),this.name="HealingError",this.level=n}},g=class extends v{path;constructor(t,n,r="STORAGE_ERROR"){super(t,r),this.name="StorageError",this.path=n}};function ze(e){return e instanceof v}function Ue(e){return e instanceof v?`[${e.code}] ${e.message}`:e instanceof Error?e.message:String(e)}var fe=h(require("pino")),on=h(require("fs-extra"));var sn=process.env.NODE_ENV!=="production",ke=sn?{target:"pino-pretty",options:{colorize:!0,translateTime:"HH:MM:ss",ignore:"pid,hostname",messageFormat:"[assuremind] {msg}"}}:void 0,he=(0,fe.default)({level:process.env.LOG_LEVEL??"info",base:{name:"assuremind"}},ke?fe.default.transport(ke):void 0);function b(e){return he.child({component:e})}function cn(e){let t=/^```(?:typescript|javascript|ts|js)?\n?([\s\S]*?)```\s*$/m,n=e.match(t);return n?.[1]!==void 0?n[1].trim():e.trim()}var ln=[/\brequire\s*\(/,/\bimport\s*\(/,/\bprocess\b/,/\bchild_process\b/,/\bexec\s*\(/,/\bspawn\s*\(/,/\beval\s*\(/,/\bFunction\s*\(/,/\b__dirname\b/,/\b__filename\b/,/\bglobal\b/,/\bwindow\.location\.href\s*=/,/\bdocument\.cookie\b/,/\blocalStorage\b/,/\bsessionStorage\b/,/\bIndexedDB\b/,/\bXMLHttpRequest\b/,/\bfetch\s*\(/,/\bWebSocket\s*\(/];function mn(e){for(let t of ln)if(t.test(e))throw new u(`Generated code contains forbidden pattern: ${t.source}. This may be a prompt injection attempt. Please regenerate the step.`,"generatedCode","UNSAFE_CODE")}var pn=/\s+(?:field|input|box|textbox|text\s?box|drop-?\s?down|dropdown|combo\s?box|combobox|menu|selector|element|checkbox|radio\s?button|radio)$/i;function Ge(e){let t=e.replace(pn,"").trim();return t.length>0?t:e}function un(e){let t=e;return t=t.replace(/(getBy(?:Label|Placeholder)\(\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Ge(a)}${i}`),t=t.replace(/(name:\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Ge(a)}${i}`),t}function dn(e){return e.replace(/\blocator\((['"`])([\s\S]*?)\1\)/g,(t,n,r)=>{if(!r.includes(" >> "))return t;let i=r.split(" >> ").map(a=>a.trim()).filter(Boolean);return i.length<2?t:i.map(a=>`locator(${n}${a}${n})`).join(".")})}function gn(e){let t=e;return t=dn(t),t=t.replace(/\.or\((?:[^()]*|\([^()]*\))*\)(?:\.(?:first|last)\(\))?/g,""),t=t.replace(/\.(?:first|last)\(\)(?=\.\w)/g,""),t=un(t),t}function Me(e){let t=cn(e);if(!t)throw new u("AI returned an empty code response. Please try regenerating.","generatedCode","EMPTY_CODE");let n=gn(t);return mn(n),n}function N(e){return e.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function Be(e,t){let n=e;for(let r of t)r.length>0&&(n=n.replaceAll(r,"[REDACTED]"));return n}var xe=h(require("path")),R=h(require("fs-extra"));async function x(e,t){let n=xe.default.dirname(e);await R.default.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.default.writeJson(r,t,{spaces:2}),await R.default.rename(r,e)}catch(i){throw await R.default.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}async function E(e){try{return await R.default.readJson(e)}catch(t){throw new g(`Failed to read JSON file "${e}": ${t instanceof Error?t.message:String(t)}`,e,"READ_FAILED")}}async function We(e,t){let n=xe.default.dirname(e);await R.default.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.default.writeFile(r,t,"utf8"),await R.default.rename(r,e)}catch(i){throw await R.default.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}var k=b("suite-store"),ee="suite.json";async function G(e){let t=S.default.join(e,ee);if(!await I.default.pathExists(t))throw new g(`Suite file not found at "${t}". Ensure the suite directory exists and contains a suite.json file.`,t,"SUITE_NOT_FOUND");let n=await E(t),r=ge.safeParse(n);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
2
2
  `);throw new u(`Invalid suite.json at "${t}":
3
3
  ${a}`,"suite","INVALID_SUITE")}if(!n.type){let a=S.default.basename(S.default.dirname(e));a==="api"?r.data.type="api":a==="audit"||a==="performance"?r.data.type="audit":r.data.type="ui"}return r.data}async function te(e,t){await I.default.ensureDir(e);let n=S.default.join(e,ee),r=ge.safeParse(t);if(!r.success){let i=r.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
4
4
  `);throw new u(`Cannot write invalid suite to "${n}":
@@ -6,17 +6,17 @@ ${i}`,"suite","INVALID_SUITE")}await x(n,r.data),k.debug({suiteId:t.id,path:n},"
6
6
  `);throw new u(`Invalid test case file at "${e}":
7
7
  ${r}`,"case","INVALID_CASE")}return n.data}async function oe(e,t){let n=de.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
8
8
  `);throw new u(`Cannot write invalid test case to "${e}":
9
- ${r}`,"case","INVALID_CASE")}await x(e,n.data),ie.debug({caseId:t.id,path:e},"Test case written")}async function et(e,t){let n=new Date().toISOString(),r={...t,id:(0,Qe.v4)(),createdAt:n,updatedAt:n},i=re.default.join(e,`${N(r.name)}${be}`);return await oe(i,r),ie.info({caseId:r.id,path:i},"Test case created"),i}async function tt(e,t){let n=await ae(e),r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:new Date().toISOString()};return await oe(e,r),r}async function nt(e){if(!await D.default.pathExists(e))throw new g(`Test case file not found at "${e}".`,e,"CASE_NOT_FOUND");await D.default.remove(e),ie.info({path:e},"Test case deleted")}async function Se(e){return await D.default.pathExists(e)?(await D.default.readdir(e,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.endsWith(be)).map(n=>re.default.join(e,n.name)):[]}async function rt(e){let t=await Se(e),n=[];for(let r of t)try{n.push(await ae(r))}catch(i){ie.warn({path:r,err:i},"Failed to read test case \u2014 skipping")}return n.sort((r,i)=>r.name.localeCompare(i.name))}function it(e,t){return re.default.join(e,`${N(t)}${be}`)}var P=h(require("path")),M=h(require("fs-extra"));var T=require("zod"),gn=T.z.object({value:T.z.string(),secret:T.z.literal(!0)}),fn=T.z.union([T.z.string(),gn]),ye=T.z.record(T.z.string(),fn);function hn(e){return typeof e=="object"&&e.secret===!0}function at(e){return hn(e)?e.value:e}var xn=b("variable-store"),B="variables",Ee="global.json";function bn(e){return`${e}.env.json`}async function H(e){if(!await M.default.pathExists(e))return{};let t=await E(e),n=ye.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
9
+ ${r}`,"case","INVALID_CASE")}await x(e,n.data),ie.debug({caseId:t.id,path:e},"Test case written")}async function et(e,t){let n=new Date().toISOString(),r={...t,id:(0,Qe.v4)(),createdAt:n,updatedAt:n},i=re.default.join(e,`${N(r.name)}${be}`);return await oe(i,r),ie.info({caseId:r.id,path:i},"Test case created"),i}async function tt(e,t){let n=await ae(e),r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:new Date().toISOString()};return await oe(e,r),r}async function nt(e){if(!await D.default.pathExists(e))throw new g(`Test case file not found at "${e}".`,e,"CASE_NOT_FOUND");await D.default.remove(e),ie.info({path:e},"Test case deleted")}async function Se(e){return await D.default.pathExists(e)?(await D.default.readdir(e,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.endsWith(be)).map(n=>re.default.join(e,n.name)):[]}async function rt(e){let t=await Se(e),n=[];for(let r of t)try{n.push(await ae(r))}catch(i){ie.warn({path:r,err:i},"Failed to read test case \u2014 skipping")}return n.sort((r,i)=>r.name.localeCompare(i.name))}function it(e,t){return re.default.join(e,`${N(t)}${be}`)}var P=h(require("path")),M=h(require("fs-extra"));var T=require("zod"),fn=T.z.object({value:T.z.string(),secret:T.z.literal(!0)}),hn=T.z.union([T.z.string(),fn]),ye=T.z.record(T.z.string(),hn);function xn(e){return typeof e=="object"&&e.secret===!0}function at(e){return xn(e)?e.value:e}var bn=b("variable-store"),B="variables",Ee="global.json";function Sn(e){return`${e}.env.json`}async function H(e){if(!await M.default.pathExists(e))return{};let t=await E(e),n=ye.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
10
10
  `);throw new u(`Invalid variables file at "${e}":
11
11
  ${r}`,"variables","INVALID_VARIABLES")}return n.data}async function se(e,t){let n=ye.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
12
12
  `);throw new u(`Cannot write invalid variables to "${e}":
13
- ${r}`,"variables","INVALID_VARIABLES")}await x(e,n.data),xn.debug({path:e},"Variables written")}async function we(e){let t=P.default.join(e,B,Ee);return H(t)}async function ve(e,t){let n=P.default.join(e,B,bn(t));return H(n)}async function ot(e,t){let n=await we(e),r=t?await ve(e,t):{},i={...n,...r},a={};for(let[p,y]of Object.entries(i))a[p]=at(y);return a}async function st(e,t,n){let r=P.default.join(e,B,Ee);await M.default.ensureDir(P.default.dirname(r));let i=await H(r);i[t]=n,await se(r,i)}async function ct(e,t){let n=P.default.join(e,B,Ee),r=await H(n);if(!(t in r))throw new g(`Variable "${t}" not found in global variables.`,n,"VARIABLE_NOT_FOUND");delete r[t],await se(n,r)}async function lt(e){let t=P.default.join(e,B);return await M.default.pathExists(t)?(await M.default.readdir(t,{withFileTypes:!0})).filter(r=>r.isFile()&&r.name.endsWith(".json")).map(r=>P.default.join(t,r.name)):[]}var F=h(require("path")),ce=h(require("fs-extra"));var Ae=b("config-store"),Re="autotest.config.json",mt="autotest.config.ts";async function le(e){let t=F.default.join(e,Re);if(!await ce.default.pathExists(t))return Ae.debug({rootDir:e},"No autotest.config.json found \u2014 using defaults"),q;let n=await E(t),r=z.safeParse(n);if(!r.success){let i=r.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
13
+ ${r}`,"variables","INVALID_VARIABLES")}await x(e,n.data),bn.debug({path:e},"Variables written")}async function we(e){let t=P.default.join(e,B,Ee);return H(t)}async function ve(e,t){let n=P.default.join(e,B,Sn(t));return H(n)}async function ot(e,t){let n=await we(e),r=t?await ve(e,t):{},i={...n,...r},a={};for(let[p,y]of Object.entries(i))a[p]=at(y);return a}async function st(e,t,n){let r=P.default.join(e,B,Ee);await M.default.ensureDir(P.default.dirname(r));let i=await H(r);i[t]=n,await se(r,i)}async function ct(e,t){let n=P.default.join(e,B,Ee),r=await H(n);if(!(t in r))throw new g(`Variable "${t}" not found in global variables.`,n,"VARIABLE_NOT_FOUND");delete r[t],await se(n,r)}async function lt(e){let t=P.default.join(e,B);return await M.default.pathExists(t)?(await M.default.readdir(t,{withFileTypes:!0})).filter(r=>r.isFile()&&r.name.endsWith(".json")).map(r=>P.default.join(t,r.name)):[]}var F=h(require("path")),ce=h(require("fs-extra"));var Ae=b("config-store"),Re="autotest.config.json",mt="autotest.config.ts";async function le(e){let t=F.default.join(e,Re);if(!await ce.default.pathExists(t))return Ae.debug({rootDir:e},"No autotest.config.json found \u2014 using defaults"),q;let n=await E(t),r=z.safeParse(n);if(!r.success){let i=r.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
14
14
  `);throw new u(`Invalid autotest.config.json at "${t}":
15
15
  ${i}
16
16
 
17
17
  How to fix: Run "npx assuremind init" to reset to defaults, or manually correct the file using autotest.config.ts as reference.`,"config","INVALID_CONFIG")}return r.data}async function Ie(e,t){let n=z.safeParse(t);if(!n.success){let a=n.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
18
18
  `);throw new u(`Cannot write invalid config:
19
- ${a}`,"config","INVALID_CONFIG")}let r=F.default.join(e,Re);await x(r,n.data);let i=F.default.join(e,mt);await We(i,Sn(n.data)),Ae.info({rootDir:e},"Config saved")}async function pt(e,t){let n=await le(e),r={...n,...t,healing:{...n.healing,...t.healing??{}},reporting:{...n.reporting,...t.reporting??{}},environmentUrls:{...n.environmentUrls,...t.environmentUrls??{}}};return await Ie(e,r),r}async function ut(e){let t=F.default.join(e,Re),n=F.default.join(e,mt);return await ce.default.pathExists(t)||await ce.default.pathExists(n)}async function dt(e){let t=await le(e);if(!t.baseUrl)throw new C('baseUrl is required in autotest.config.ts. Set it to your application URL, e.g. "http://localhost:3000".',"CONFIG_BASE_URL_MISSING");Ae.debug({config:t},"Config validated successfully")}function Sn(e){return`import { defineConfig } from 'assuremind';
19
+ ${a}`,"config","INVALID_CONFIG")}let r=F.default.join(e,Re);await x(r,n.data);let i=F.default.join(e,mt);await We(i,yn(n.data)),Ae.info({rootDir:e},"Config saved")}async function pt(e,t){let n=await le(e),r={...n,...t,healing:{...n.healing,...t.healing??{}},reporting:{...n.reporting,...t.reporting??{}},environmentUrls:{...n.environmentUrls,...t.environmentUrls??{}}};return await Ie(e,r),r}async function ut(e){let t=F.default.join(e,Re),n=F.default.join(e,mt);return await ce.default.pathExists(t)||await ce.default.pathExists(n)}async function dt(e){let t=await le(e);if(!t.baseUrl)throw new C('baseUrl is required in autotest.config.ts. Set it to your application URL, e.g. "http://localhost:3000".',"CONFIG_BASE_URL_MISSING");Ae.debug({config:t},"Config validated successfully")}function yn(e){return`import { defineConfig } from 'assuremind';
20
20
 
21
21
  export default defineConfig({
22
22
  baseUrl: '${e.baseUrl}',
@@ -41,22 +41,22 @@ export default defineConfig({
41
41
  },
42
42
  studioPort: ${e.studioPort},
43
43
  });
44
- `}var j=h(require("path")),O=h(require("fs-extra"));var s=require("zod"),me=s.z.enum(["pending","running","passed","failed","skipped"]),yn=s.z.object({method:s.z.string(),url:s.z.string(),headers:s.z.record(s.z.string()).optional(),body:s.z.string().optional()}).optional(),En=s.z.object({status:s.z.number(),statusText:s.z.string(),headers:s.z.record(s.z.string()).optional(),body:s.z.string().optional(),duration:s.z.number()}).optional(),A=s.z.number().nullish().transform(e=>e??void 0),gt=s.z.object({id:s.z.string(),title:s.z.string(),passed:s.z.boolean(),partial:s.z.boolean().optional(),na:s.z.boolean().optional()}),wn=s.z.object({url:s.z.string(),stepIndex:s.z.number().int(),stepInstruction:s.z.string(),score:A,a11yScore:A,seoScore:A,fcp:A,lcp:A,cls:A,ttfb:A,tbt:A,si:A,tti:A,inp:A,a11yAudits:s.z.array(gt).optional(),seoAudits:s.z.array(gt).optional(),lighthouseError:s.z.string().optional()}),vn=s.z.object({stepId:s.z.string(),instruction:s.z.string(),status:me,code:s.z.string(),error:s.z.string().optional(),duration:s.z.number(),screenshotPath:s.z.string().optional(),healed:s.z.boolean().optional(),healedCode:s.z.string().optional(),stepType:s.z.enum(["ui","api","mock"]).optional(),apiRequest:yn,apiResponse:En,navigatedToUrl:s.z.string().optional(),auditUrl:s.z.string().optional()}),An=s.z.object({caseId:s.z.string(),caseName:s.z.string(),status:me,steps:s.z.array(vn),duration:s.z.number(),browser:s.z.string(),device:s.z.string().optional(),startedAt:s.z.string().datetime(),finishedAt:s.z.string().datetime(),videoPath:s.z.string().optional(),tracePath:s.z.string().optional(),dataRowIndex:s.z.number().int().optional(),dataRow:s.z.record(s.z.string()).optional(),pageLoads:s.z.array(wn).default([])}),Rn=s.z.object({suiteId:s.z.string(),suiteName:s.z.string(),suiteType:s.z.enum(["ui","api","audit","performance"]).optional(),status:me,cases:s.z.array(An),duration:s.z.number(),browser:s.z.string(),startedAt:s.z.string().datetime(),finishedAt:s.z.string().datetime()}),je=s.z.object({runId:s.z.string(),status:me,environment:s.z.string().optional(),suites:s.z.array(Rn),duration:s.z.number(),startedAt:s.z.string().datetime(),finishedAt:s.z.string().datetime(),totalTests:s.z.number().int(),passed:s.z.number().int(),failed:s.z.number().int(),skipped:s.z.number().int(),logFilePath:s.z.string().optional()});var Ce=b("result-store"),$="results",ft="runs";function Te(e,t){return j.default.join(e,ft,`${t}.json`)}async function ht(e,t){let n=je.safeParse(t);if(!n.success){let i=n.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
44
+ `}var j=h(require("path")),O=h(require("fs-extra"));var s=require("zod"),me=s.z.enum(["pending","running","passed","failed","skipped"]),En=s.z.object({method:s.z.string(),url:s.z.string(),headers:s.z.record(s.z.string()).optional(),body:s.z.string().optional()}).optional(),wn=s.z.object({status:s.z.number(),statusText:s.z.string(),headers:s.z.record(s.z.string()).optional(),body:s.z.string().optional(),duration:s.z.number()}).optional(),A=s.z.number().nullish().transform(e=>e??void 0),gt=s.z.object({id:s.z.string(),title:s.z.string(),passed:s.z.boolean(),partial:s.z.boolean().optional(),na:s.z.boolean().optional()}),vn=s.z.object({url:s.z.string(),stepIndex:s.z.number().int(),stepInstruction:s.z.string(),score:A,a11yScore:A,seoScore:A,fcp:A,lcp:A,cls:A,ttfb:A,tbt:A,si:A,tti:A,inp:A,a11yAudits:s.z.array(gt).optional(),seoAudits:s.z.array(gt).optional(),lighthouseError:s.z.string().optional()}),An=s.z.object({stepId:s.z.string(),instruction:s.z.string(),status:me,code:s.z.string(),error:s.z.string().optional(),duration:s.z.number(),screenshotPath:s.z.string().optional(),healed:s.z.boolean().optional(),healedCode:s.z.string().optional(),stepType:s.z.enum(["ui","api","mock"]).optional(),apiRequest:En,apiResponse:wn,navigatedToUrl:s.z.string().optional(),auditUrl:s.z.string().optional()}),Rn=s.z.object({caseId:s.z.string(),caseName:s.z.string(),status:me,steps:s.z.array(An),duration:s.z.number(),browser:s.z.string(),device:s.z.string().optional(),startedAt:s.z.string().datetime(),finishedAt:s.z.string().datetime(),videoPath:s.z.string().optional(),tracePath:s.z.string().optional(),downloadPaths:s.z.array(s.z.string()).default([]),dataRowIndex:s.z.number().int().optional(),dataRow:s.z.record(s.z.string()).optional(),pageLoads:s.z.array(vn).default([])}),In=s.z.object({suiteId:s.z.string(),suiteName:s.z.string(),suiteType:s.z.enum(["ui","api","audit","performance"]).optional(),status:me,cases:s.z.array(Rn),duration:s.z.number(),browser:s.z.string(),startedAt:s.z.string().datetime(),finishedAt:s.z.string().datetime()}),je=s.z.object({runId:s.z.string(),status:me,environment:s.z.string().optional(),suites:s.z.array(In),duration:s.z.number(),startedAt:s.z.string().datetime(),finishedAt:s.z.string().datetime(),totalTests:s.z.number().int(),passed:s.z.number().int(),failed:s.z.number().int(),skipped:s.z.number().int(),logFilePath:s.z.string().optional()});var Ce=b("result-store"),$="results",ft="runs";function Te(e,t){return j.default.join(e,ft,`${t}.json`)}async function ht(e,t){let n=je.safeParse(t);if(!n.success){let i=n.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
45
45
  `);throw new u(`Cannot write invalid run result:
46
46
  ${i}`,"result","INVALID_RESULT")}let r=Te(j.default.join(e,$),t.runId);await x(r,n.data),Ce.info({runId:t.runId,status:t.status},"Run result saved")}async function Pe(e,t){let n=Te(j.default.join(e,$),t);if(!await O.default.pathExists(n))throw new g(`Run result not found for runId "${t}".`,n,"RESULT_NOT_FOUND");let r=await E(n),i=je.safeParse(r);if(!i.success){let a=i.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
47
47
  `);throw new u(`Invalid run result at "${n}":
48
- ${a}`,"result","INVALID_RESULT")}return i.data}async function _e(e){let t=j.default.join(e,$,ft);if(!await O.default.pathExists(t))return[];let r=(await O.default.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.endsWith(".json")).map(a=>a.name.replace(".json",""));return(await Promise.all(r.map(async a=>{let p=await O.default.stat(j.default.join(t,`${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}async function xt(e,t=20){let n=await _e(e),r=[];for(let i of n.slice(0,t))try{let a=await Pe(e,i);r.push({runId:a.runId,status:a.status,environment:a.environment,startedAt:a.startedAt,finishedAt:a.finishedAt,totalTests:a.totalTests,passed:a.passed,failed:a.failed,skipped:a.skipped,duration:a.duration,suiteIds:a.suites.map(p=>p.suiteId)})}catch(a){Ce.warn({runId:i,err:a},"Failed to read run result \u2014 skipping")}return r}async function bt(e,t){let n=Te(j.default.join(e,$),t);if(!await O.default.pathExists(n))throw new g(`Run result not found for runId "${t}".`,n,"RESULT_NOT_FOUND");await O.default.remove(n),Ce.info({runId:t},"Run result deleted")}function St(e){return j.default.join(e,$,"screenshots")}function yt(e){return j.default.join(e,$,"videos")}function Et(e){return j.default.join(e,$,"traces")}var w=h(require("path")),L=h(require("fs-extra"));var d=require("zod"),In=d.z.enum(["pending","accepted","rejected"]),jn=d.z.enum(["retry","regenerate","multi-selector","visual","decompose","manual"]),pe=d.z.object({id:d.z.string(),runId:d.z.string(),suiteId:d.z.string(),caseId:d.z.string(),stepId:d.z.string(),stepInstruction:d.z.string(),failedCode:d.z.string(),healedCode:d.z.string(),error:d.z.string(),strategy:jn,level:d.z.number().int().min(1).max(6),status:In,pageUrl:d.z.string(),timestamp:d.z.string().datetime(),acceptedAt:d.z.string().datetime().optional(),rejectedAt:d.z.string().datetime().optional()}),Ne=d.z.object({runId:d.z.string(),generatedAt:d.z.string().datetime(),totalHeals:d.z.number().int(),accepted:d.z.number().int(),rejected:d.z.number().int(),pending:d.z.number().int(),events:d.z.array(pe)});var W=b("healing-store"),_=w.default.join("results","healing"),Cn="pending.json",ue="healing-report-";function At(e,t){return w.default.join(e,`${ue}${t}.json`)}function J(e){return w.default.join(e,Cn)}async function Rt(e,t){let n=Ne.safeParse(t);if(!n.success){let a=n.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
48
+ ${a}`,"result","INVALID_RESULT")}return i.data}async function _e(e){let t=j.default.join(e,$,ft);if(!await O.default.pathExists(t))return[];let r=(await O.default.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.endsWith(".json")).map(a=>a.name.replace(".json",""));return(await Promise.all(r.map(async a=>{let p=await O.default.stat(j.default.join(t,`${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}async function xt(e,t=20){let n=await _e(e),r=[];for(let i of n.slice(0,t))try{let a=await Pe(e,i);r.push({runId:a.runId,status:a.status,environment:a.environment,startedAt:a.startedAt,finishedAt:a.finishedAt,totalTests:a.totalTests,passed:a.passed,failed:a.failed,skipped:a.skipped,duration:a.duration,suiteIds:a.suites.map(p=>p.suiteId)})}catch(a){Ce.warn({runId:i,err:a},"Failed to read run result \u2014 skipping")}return r}async function bt(e,t){let n=Te(j.default.join(e,$),t);if(!await O.default.pathExists(n))throw new g(`Run result not found for runId "${t}".`,n,"RESULT_NOT_FOUND");await O.default.remove(n),Ce.info({runId:t},"Run result deleted")}function St(e){return j.default.join(e,$,"screenshots")}function yt(e){return j.default.join(e,$,"videos")}function Et(e){return j.default.join(e,$,"traces")}var w=h(require("path")),L=h(require("fs-extra"));var d=require("zod"),jn=d.z.enum(["pending","accepted","rejected"]),Cn=d.z.enum(["retry","regenerate","multi-selector","visual","decompose","manual"]),pe=d.z.object({id:d.z.string(),runId:d.z.string(),suiteId:d.z.string(),caseId:d.z.string(),stepId:d.z.string(),stepInstruction:d.z.string(),failedCode:d.z.string(),healedCode:d.z.string(),error:d.z.string(),strategy:Cn,level:d.z.number().int().min(1).max(6),status:jn,pageUrl:d.z.string(),timestamp:d.z.string().datetime(),acceptedAt:d.z.string().datetime().optional(),rejectedAt:d.z.string().datetime().optional()}),Ne=d.z.object({runId:d.z.string(),generatedAt:d.z.string().datetime(),totalHeals:d.z.number().int(),accepted:d.z.number().int(),rejected:d.z.number().int(),pending:d.z.number().int(),events:d.z.array(pe)});var W=b("healing-store"),_=w.default.join("results","healing"),Tn="pending.json",ue="healing-report-";function At(e,t){return w.default.join(e,`${ue}${t}.json`)}function J(e){return w.default.join(e,Tn)}async function Rt(e,t){let n=Ne.safeParse(t);if(!n.success){let a=n.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
49
49
  `);throw new u(`Cannot write invalid healing report:
50
50
  ${a}`,"healingReport","INVALID_HEALING_REPORT")}let r=w.default.join(e,_),i=At(r,t.runId);await x(i,n.data),W.info({runId:t.runId,totalHeals:t.totalHeals},"Healing report saved")}async function It(e,t){let n=w.default.join(e,_),r=At(n,t);if(!await L.default.pathExists(r))throw new g(`Healing report not found for runId "${t}".`,r,"HEALING_REPORT_NOT_FOUND");let i=await E(r),a=Ne.safeParse(i);if(!a.success){let p=a.error.issues.map(y=>` \u2022 ${y.path.join(".")}: ${y.message}`).join(`
51
51
  `);throw new u(`Invalid healing report at "${r}":
52
- ${p}`,"healingReport","INVALID_HEALING_REPORT")}return a.data}async function V(e){let t=w.default.join(e,_),n=J(t);if(!await L.default.pathExists(n))return[];let r=await E(n),i=pe.array().safeParse(r);return i.success?i.data:(W.warn({path:n},"Pending healing file is malformed \u2014 resetting"),[])}var wt=50,vt=["pending","rejected","accepted"];function Tn(e){if(e.length<=wt)return e;let t=[...e].sort((i,a)=>{let p=vt.indexOf(i.status),y=vt.indexOf(a.status);return p!==y?p-y:i.timestamp<a.timestamp?-1:1}),n=e.length-wt,r=new Set(t.slice(0,n).map(i=>i.id));return W.info({excess:n,deleted:r.size},"Auto-pruning healing events to enforce cap"),e.filter(i=>!r.has(i.id))}async function jt(e,t){let n=pe.safeParse(t);if(!n.success)throw new u(`Invalid healing event: ${n.error.message}`,"healingEvent","INVALID_HEALING_EVENT");let r=w.default.join(e,_);await L.default.ensureDir(r);let i=await V(e);i.push(n.data),await x(J(r),Tn(i))}async function Ct(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"accepted",acceptedAt:new Date().toISOString()};let i=w.default.join(e,_);return await x(J(i),n),W.info({eventId:t},"Healing event accepted"),n[r]}async function Tt(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"rejected",rejectedAt:new Date().toISOString()};let i=w.default.join(e,_);return await x(J(i),n),W.info({eventId:t},"Healing event rejected"),n[r]}async function Pt(e,t=30){let n=await V(e),r=Date.now()-t*24*60*60*1e3,i=n.length,a=n.filter(p=>{if(p.status==="pending")return!0;let y=p.acceptedAt??p.rejectedAt;return y?new Date(y).getTime()>r:!0});if(a.length<i){let p=w.default.join(e,_);await x(J(p),a)}return i-a.length}async function _t(e){let t=await V(e),n=t.filter(a=>a.status==="pending").length,r=t.filter(a=>a.status==="accepted").length,i=t.filter(a=>a.status==="rejected").length;return{pending:n,accepted:r,rejected:i,total:t.length}}async function Nt(e){let t=w.default.join(e,_);if(!await L.default.pathExists(t))return[];let r=(await L.default.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.startsWith(ue)&&a.name.endsWith(".json")).map(a=>a.name.slice(ue.length,-5));return(await Promise.all(r.map(async a=>{let p=await L.default.stat(w.default.join(t,`${ue}${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}var Ot=require("crypto");function Oe(e){return(0,Ot.createHash)("sha256").update(e,"utf8").digest("hex")}function $e(e){try{return new URL(e).pathname.split("/").map(r=>/^\d+$/.test(r)||/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(r)||/^[a-z0-9-]{8,}$/i.test(r)&&/-/.test(r)?"*":r).join("/")}catch{return e}}function $t(e,t){let n=e.toLowerCase().replace(/\s+/g," ").trim(),r=$e(t);return Oe(`${n}|${r}`).slice(0,16)}var Vt=require("dotenv"),m=require("zod");(0,Vt.config)();var Lt=["anthropic","openai","google","azure-openai","bedrock","deepseek","groq","together","qwen","perplexity","ollama","custom"],Pn=m.z.object({NODE_ENV:m.z.enum(["development","production","test"]).default("development"),LOG_LEVEL:m.z.enum(["trace","debug","info","warn","error","fatal"]).default("info"),AI_PROVIDER:m.z.enum(Lt),AI_TIERED_ENABLED:m.z.string().transform(e=>e==="true").default("false"),AI_TIERED_FAST_PROVIDER:m.z.enum(Lt).optional(),AI_TIERED_FAST_MODEL:m.z.string().optional(),AI_TIMEOUT:m.z.coerce.number().int().positive().default(30),AI_MAX_RETRIES:m.z.coerce.number().int().min(0).max(10).default(2)}),_n=m.z.object({ANTHROPIC_API_KEY:m.z.string().min(1),ANTHROPIC_MODEL:m.z.string().default("claude-sonnet-4-20250514")}),Nn=m.z.object({OPENAI_API_KEY:m.z.string().min(1),OPENAI_MODEL:m.z.string().default("gpt-4o")}),On=m.z.object({GOOGLE_API_KEY:m.z.string().min(1),GOOGLE_MODEL:m.z.string().default("gemini-2.5-pro")}),$n=m.z.object({AZURE_OPENAI_API_KEY:m.z.string().min(1),AZURE_OPENAI_ENDPOINT:m.z.string().url(),AZURE_OPENAI_DEPLOYMENT:m.z.string().min(1),AZURE_OPENAI_API_VERSION:m.z.string().default("2024-10-21")}),Ln=m.z.object({AWS_ACCESS_KEY_ID:m.z.string().min(1),AWS_SECRET_ACCESS_KEY:m.z.string().min(1),AWS_SESSION_TOKEN:m.z.string().optional(),AWS_REGION:m.z.string().default("us-east-1"),BEDROCK_MODEL:m.z.string().default("anthropic.claude-sonnet-4-20250514-v1:0")}),Vn=m.z.object({DEEPSEEK_API_KEY:m.z.string().min(1),DEEPSEEK_MODEL:m.z.string().default("deepseek-chat")}),Dn=m.z.object({GROQ_API_KEY:m.z.string().min(1),GROQ_MODEL:m.z.string().default("llama-3.3-70b-versatile")}),Hn=m.z.object({TOGETHER_API_KEY:m.z.string().min(1),TOGETHER_MODEL:m.z.string().default("meta-llama/Llama-3.3-70B-Instruct-Turbo")}),Fn=m.z.object({QWEN_API_KEY:m.z.string().min(1),QWEN_BASE_URL:m.z.string().url().default("https://dashscope-intl.aliyuncs.com/compatible-mode/v1"),QWEN_MODEL:m.z.string().default("qwen-max")}),zn=m.z.object({PERPLEXITY_API_KEY:m.z.string().min(1),PERPLEXITY_MODEL:m.z.string().default("sonar-pro")}),Un=m.z.object({OLLAMA_BASE_URL:m.z.string().url().default("http://localhost:11434"),OLLAMA_MODEL:m.z.string().default("llama3.3")}),kn=m.z.object({CUSTOM_API_KEY:m.z.string().min(1),CUSTOM_BASE_URL:m.z.string().url(),CUSTOM_MODEL:m.z.string().min(1)}),Gn={anthropic:_n,openai:Nn,google:On,"azure-openai":$n,bedrock:Ln,deepseek:Vn,groq:Dn,together:Hn,qwen:Fn,perplexity:zn,ollama:Un,custom:kn},K=null;function Le(){if(K!==null)return K;let e=Pn.safeParse(process.env);if(!e.success){let a=e.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
52
+ ${p}`,"healingReport","INVALID_HEALING_REPORT")}return a.data}async function V(e){let t=w.default.join(e,_),n=J(t);if(!await L.default.pathExists(n))return[];let r=await E(n),i=pe.array().safeParse(r);return i.success?i.data:(W.warn({path:n},"Pending healing file is malformed \u2014 resetting"),[])}var wt=50,vt=["pending","rejected","accepted"];function Pn(e){if(e.length<=wt)return e;let t=[...e].sort((i,a)=>{let p=vt.indexOf(i.status),y=vt.indexOf(a.status);return p!==y?p-y:i.timestamp<a.timestamp?-1:1}),n=e.length-wt,r=new Set(t.slice(0,n).map(i=>i.id));return W.info({excess:n,deleted:r.size},"Auto-pruning healing events to enforce cap"),e.filter(i=>!r.has(i.id))}async function jt(e,t){let n=pe.safeParse(t);if(!n.success)throw new u(`Invalid healing event: ${n.error.message}`,"healingEvent","INVALID_HEALING_EVENT");let r=w.default.join(e,_);await L.default.ensureDir(r);let i=await V(e);i.push(n.data),await x(J(r),Pn(i))}async function Ct(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"accepted",acceptedAt:new Date().toISOString()};let i=w.default.join(e,_);return await x(J(i),n),W.info({eventId:t},"Healing event accepted"),n[r]}async function Tt(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"rejected",rejectedAt:new Date().toISOString()};let i=w.default.join(e,_);return await x(J(i),n),W.info({eventId:t},"Healing event rejected"),n[r]}async function Pt(e,t=30){let n=await V(e),r=Date.now()-t*24*60*60*1e3,i=n.length,a=n.filter(p=>{if(p.status==="pending")return!0;let y=p.acceptedAt??p.rejectedAt;return y?new Date(y).getTime()>r:!0});if(a.length<i){let p=w.default.join(e,_);await x(J(p),a)}return i-a.length}async function _t(e){let t=await V(e),n=t.filter(a=>a.status==="pending").length,r=t.filter(a=>a.status==="accepted").length,i=t.filter(a=>a.status==="rejected").length;return{pending:n,accepted:r,rejected:i,total:t.length}}async function Nt(e){let t=w.default.join(e,_);if(!await L.default.pathExists(t))return[];let r=(await L.default.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.startsWith(ue)&&a.name.endsWith(".json")).map(a=>a.name.slice(ue.length,-5));return(await Promise.all(r.map(async a=>{let p=await L.default.stat(w.default.join(t,`${ue}${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}var Ot=require("crypto");function Oe(e){return(0,Ot.createHash)("sha256").update(e,"utf8").digest("hex")}function $e(e){try{return new URL(e).pathname.split("/").map(r=>/^\d+$/.test(r)||/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(r)||/^[a-z0-9-]{8,}$/i.test(r)&&/-/.test(r)?"*":r).join("/")}catch{return e}}function $t(e,t){let n=e.toLowerCase().replace(/\s+/g," ").trim(),r=$e(t);return Oe(`${n}|${r}`).slice(0,16)}var Vt=require("dotenv"),m=require("zod");(0,Vt.config)();var Lt=["anthropic","openai","google","azure-openai","bedrock","deepseek","groq","together","qwen","perplexity","ollama","custom"],_n=m.z.object({NODE_ENV:m.z.enum(["development","production","test"]).default("development"),LOG_LEVEL:m.z.enum(["trace","debug","info","warn","error","fatal"]).default("info"),AI_PROVIDER:m.z.enum(Lt),AI_TIERED_ENABLED:m.z.string().transform(e=>e==="true").default("false"),AI_TIERED_FAST_PROVIDER:m.z.enum(Lt).optional(),AI_TIERED_FAST_MODEL:m.z.string().optional(),AI_TIMEOUT:m.z.coerce.number().int().positive().default(30),AI_MAX_RETRIES:m.z.coerce.number().int().min(0).max(10).default(2)}),Nn=m.z.object({ANTHROPIC_API_KEY:m.z.string().min(1),ANTHROPIC_MODEL:m.z.string().default("claude-sonnet-4-20250514")}),On=m.z.object({OPENAI_API_KEY:m.z.string().min(1),OPENAI_MODEL:m.z.string().default("gpt-4o")}),$n=m.z.object({GOOGLE_API_KEY:m.z.string().min(1),GOOGLE_MODEL:m.z.string().default("gemini-2.5-pro")}),Ln=m.z.object({AZURE_OPENAI_API_KEY:m.z.string().min(1),AZURE_OPENAI_ENDPOINT:m.z.string().url(),AZURE_OPENAI_DEPLOYMENT:m.z.string().min(1),AZURE_OPENAI_API_VERSION:m.z.string().default("2024-10-21")}),Vn=m.z.object({AWS_ACCESS_KEY_ID:m.z.string().min(1),AWS_SECRET_ACCESS_KEY:m.z.string().min(1),AWS_SESSION_TOKEN:m.z.string().optional(),AWS_REGION:m.z.string().default("us-east-1"),BEDROCK_MODEL:m.z.string().default("anthropic.claude-sonnet-4-20250514-v1:0")}),Dn=m.z.object({DEEPSEEK_API_KEY:m.z.string().min(1),DEEPSEEK_MODEL:m.z.string().default("deepseek-chat")}),Hn=m.z.object({GROQ_API_KEY:m.z.string().min(1),GROQ_MODEL:m.z.string().default("llama-3.3-70b-versatile")}),Fn=m.z.object({TOGETHER_API_KEY:m.z.string().min(1),TOGETHER_MODEL:m.z.string().default("meta-llama/Llama-3.3-70B-Instruct-Turbo")}),zn=m.z.object({QWEN_API_KEY:m.z.string().min(1),QWEN_BASE_URL:m.z.string().url().default("https://dashscope-intl.aliyuncs.com/compatible-mode/v1"),QWEN_MODEL:m.z.string().default("qwen-max")}),Un=m.z.object({PERPLEXITY_API_KEY:m.z.string().min(1),PERPLEXITY_MODEL:m.z.string().default("sonar-pro")}),kn=m.z.object({OLLAMA_BASE_URL:m.z.string().url().default("http://localhost:11434"),OLLAMA_MODEL:m.z.string().default("llama3.3")}),Gn=m.z.object({CUSTOM_API_KEY:m.z.string().min(1),CUSTOM_BASE_URL:m.z.string().url(),CUSTOM_MODEL:m.z.string().min(1)}),Mn={anthropic:Nn,openai:On,google:$n,"azure-openai":Ln,bedrock:Vn,deepseek:Dn,groq:Hn,together:Fn,qwen:zn,perplexity:Un,ollama:kn,custom:Gn},K=null;function Le(){if(K!==null)return K;let e=_n.safeParse(process.env);if(!e.success){let a=e.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
53
53
  `);throw new C(`Missing or invalid environment variables:
54
54
  ${a}
55
55
 
56
- How to fix: Copy .env.example to .env and fill in your AI provider credentials.`,"ENV_VALIDATION_FAILED")}let t=e.data.AI_PROVIDER,r=Gn[t].safeParse(process.env);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
56
+ How to fix: Copy .env.example to .env and fill in your AI provider credentials.`,"ENV_VALIDATION_FAILED")}let t=e.data.AI_PROVIDER,r=Mn[t].safeParse(process.env);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
57
57
  `);throw new C(`Provider "${t}" is missing required environment variables:
58
58
  ${a}
59
59
 
60
- How to fix: Check .env.example for the ${t} section and add the required keys to .env.`,"PROVIDER_ENV_MISSING")}return K={...e.data,...r.data},K}function Dt(){K=null}function Ht(){try{return Le()}catch{return null}}var o=require("@faker-js/faker");function Ft(){return[{category:"person",methods:[{name:"fullName",example:o.faker.person.fullName()},{name:"firstName",example:o.faker.person.firstName()},{name:"lastName",example:o.faker.person.lastName()},{name:"middleName",example:o.faker.person.middleName()},{name:"sex",example:o.faker.person.sex()},{name:"prefix",example:o.faker.person.prefix()},{name:"suffix",example:o.faker.person.suffix()},{name:"jobTitle",example:o.faker.person.jobTitle()},{name:"jobArea",example:o.faker.person.jobArea()},{name:"jobType",example:o.faker.person.jobType()},{name:"bio",example:o.faker.person.bio()}]},{category:"internet",methods:[{name:"email",example:o.faker.internet.email()},{name:"userName",example:o.faker.internet.username()},{name:"password",example:o.faker.internet.password()},{name:"url",example:o.faker.internet.url()},{name:"domainName",example:o.faker.internet.domainName()},{name:"domainWord",example:o.faker.internet.domainWord()},{name:"ip",example:o.faker.internet.ip()},{name:"ipv6",example:o.faker.internet.ipv6()},{name:"mac",example:o.faker.internet.mac()},{name:"userAgent",example:o.faker.internet.userAgent()},{name:"emoji",example:o.faker.internet.emoji()}]},{category:"phone",methods:[{name:"number",example:o.faker.phone.number()},{name:"imei",example:o.faker.phone.imei()}]},{category:"location",methods:[{name:"streetAddress",example:o.faker.location.streetAddress()},{name:"street",example:o.faker.location.street()},{name:"city",example:o.faker.location.city()},{name:"state",example:o.faker.location.state()},{name:"zipCode",example:o.faker.location.zipCode()},{name:"country",example:o.faker.location.country()},{name:"countryCode",example:o.faker.location.countryCode()},{name:"county",example:o.faker.location.county()},{name:"latitude",example:String(o.faker.location.latitude())},{name:"longitude",example:String(o.faker.location.longitude())},{name:"timeZone",example:o.faker.location.timeZone()},{name:"buildingNumber",example:o.faker.location.buildingNumber()}]},{category:"company",methods:[{name:"name",example:o.faker.company.name()},{name:"catchPhrase",example:o.faker.company.catchPhrase()},{name:"buzzPhrase",example:o.faker.company.buzzPhrase()},{name:"buzzNoun",example:o.faker.company.buzzNoun()},{name:"buzzVerb",example:o.faker.company.buzzVerb()},{name:"buzzAdjective",example:o.faker.company.buzzAdjective()}]},{category:"finance",methods:[{name:"accountNumber",example:o.faker.finance.accountNumber()},{name:"accountName",example:o.faker.finance.accountName()},{name:"creditCardNumber",example:o.faker.finance.creditCardNumber()},{name:"creditCardCVV",example:o.faker.finance.creditCardCVV()},{name:"currencyCode",example:o.faker.finance.currencyCode()},{name:"currencyName",example:o.faker.finance.currencyName()},{name:"amount",example:o.faker.finance.amount()},{name:"iban",example:o.faker.finance.iban()},{name:"bic",example:o.faker.finance.bic()},{name:"bitcoinAddress",example:o.faker.finance.bitcoinAddress()},{name:"transactionType",example:o.faker.finance.transactionType()}]},{category:"date",methods:[{name:"past",example:o.faker.date.past().toISOString()},{name:"future",example:o.faker.date.future().toISOString()},{name:"recent",example:o.faker.date.recent().toISOString()},{name:"soon",example:o.faker.date.soon().toISOString()},{name:"birthdate",example:o.faker.date.birthdate().toISOString()},{name:"month",example:o.faker.date.month()},{name:"weekday",example:o.faker.date.weekday()}]},{category:"lorem",methods:[{name:"word",example:o.faker.lorem.word()},{name:"words",example:o.faker.lorem.words()},{name:"sentence",example:o.faker.lorem.sentence()},{name:"sentences",example:o.faker.lorem.sentences()},{name:"paragraph",example:o.faker.lorem.paragraph()},{name:"slug",example:o.faker.lorem.slug()},{name:"lines",example:o.faker.lorem.lines(1)}]},{category:"string",methods:[{name:"uuid",example:o.faker.string.uuid()},{name:"alphanumeric",example:o.faker.string.alphanumeric(10)},{name:"numeric",example:o.faker.string.numeric(6)},{name:"alpha",example:o.faker.string.alpha(8)},{name:"nanoid",example:o.faker.string.nanoid()},{name:"hexadecimal",example:o.faker.string.hexadecimal({length:8})},{name:"sample",example:o.faker.string.sample(12)}]},{category:"number",methods:[{name:"int",example:String(o.faker.number.int({max:9999}))},{name:"float",example:String(o.faker.number.float({max:100,fractionDigits:2}))}]},{category:"datatype",methods:[{name:"boolean",example:String(o.faker.datatype.boolean())}]},{category:"image",methods:[{name:"avatar",example:o.faker.image.avatar()},{name:"url",example:o.faker.image.url()}]},{category:"color",methods:[{name:"human",example:o.faker.color.human()},{name:"rgb",example:o.faker.color.rgb()},{name:"hex",example:o.faker.color.rgb({format:"hex"})}]},{category:"commerce",methods:[{name:"productName",example:o.faker.commerce.productName()},{name:"price",example:o.faker.commerce.price()},{name:"department",example:o.faker.commerce.department()},{name:"productDescription",example:o.faker.commerce.productDescription().slice(0,60)+"..."},{name:"isbn",example:o.faker.commerce.isbn()}]},{category:"vehicle",methods:[{name:"vehicle",example:o.faker.vehicle.vehicle()},{name:"manufacturer",example:o.faker.vehicle.manufacturer()},{name:"model",example:o.faker.vehicle.model()},{name:"vin",example:o.faker.vehicle.vin()},{name:"vrm",example:o.faker.vehicle.vrm()}]},{category:"system",methods:[{name:"fileName",example:o.faker.system.fileName()},{name:"mimeType",example:o.faker.system.mimeType()},{name:"fileExt",example:o.faker.system.fileExt()},{name:"filePath",example:o.faker.system.filePath()},{name:"semver",example:o.faker.system.semver()}]}]}var Mn={"internet.userName":"username"};function zt(e,t){let n=o.faker[e],r=Mn[`${e}.${t}`]??t;if(!n||typeof n[r]!="function")return`[unknown: ${e}.${t}]`;let i=n[r]();return i instanceof Date?i.toISOString():String(i)}function Bn(e){let t={...q,...e},n=z.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
60
+ How to fix: Check .env.example for the ${t} section and add the required keys to .env.`,"PROVIDER_ENV_MISSING")}return K={...e.data,...r.data},K}function Dt(){K=null}function Ht(){try{return Le()}catch{return null}}var o=require("@faker-js/faker");function Ft(){return[{category:"person",methods:[{name:"fullName",example:o.faker.person.fullName()},{name:"firstName",example:o.faker.person.firstName()},{name:"lastName",example:o.faker.person.lastName()},{name:"middleName",example:o.faker.person.middleName()},{name:"sex",example:o.faker.person.sex()},{name:"prefix",example:o.faker.person.prefix()},{name:"suffix",example:o.faker.person.suffix()},{name:"jobTitle",example:o.faker.person.jobTitle()},{name:"jobArea",example:o.faker.person.jobArea()},{name:"jobType",example:o.faker.person.jobType()},{name:"bio",example:o.faker.person.bio()}]},{category:"internet",methods:[{name:"email",example:o.faker.internet.email()},{name:"userName",example:o.faker.internet.username()},{name:"password",example:o.faker.internet.password()},{name:"url",example:o.faker.internet.url()},{name:"domainName",example:o.faker.internet.domainName()},{name:"domainWord",example:o.faker.internet.domainWord()},{name:"ip",example:o.faker.internet.ip()},{name:"ipv6",example:o.faker.internet.ipv6()},{name:"mac",example:o.faker.internet.mac()},{name:"userAgent",example:o.faker.internet.userAgent()},{name:"emoji",example:o.faker.internet.emoji()}]},{category:"phone",methods:[{name:"number",example:o.faker.phone.number()},{name:"imei",example:o.faker.phone.imei()}]},{category:"location",methods:[{name:"streetAddress",example:o.faker.location.streetAddress()},{name:"street",example:o.faker.location.street()},{name:"city",example:o.faker.location.city()},{name:"state",example:o.faker.location.state()},{name:"zipCode",example:o.faker.location.zipCode()},{name:"country",example:o.faker.location.country()},{name:"countryCode",example:o.faker.location.countryCode()},{name:"county",example:o.faker.location.county()},{name:"latitude",example:String(o.faker.location.latitude())},{name:"longitude",example:String(o.faker.location.longitude())},{name:"timeZone",example:o.faker.location.timeZone()},{name:"buildingNumber",example:o.faker.location.buildingNumber()}]},{category:"company",methods:[{name:"name",example:o.faker.company.name()},{name:"catchPhrase",example:o.faker.company.catchPhrase()},{name:"buzzPhrase",example:o.faker.company.buzzPhrase()},{name:"buzzNoun",example:o.faker.company.buzzNoun()},{name:"buzzVerb",example:o.faker.company.buzzVerb()},{name:"buzzAdjective",example:o.faker.company.buzzAdjective()}]},{category:"finance",methods:[{name:"accountNumber",example:o.faker.finance.accountNumber()},{name:"accountName",example:o.faker.finance.accountName()},{name:"creditCardNumber",example:o.faker.finance.creditCardNumber()},{name:"creditCardCVV",example:o.faker.finance.creditCardCVV()},{name:"currencyCode",example:o.faker.finance.currencyCode()},{name:"currencyName",example:o.faker.finance.currencyName()},{name:"amount",example:o.faker.finance.amount()},{name:"iban",example:o.faker.finance.iban()},{name:"bic",example:o.faker.finance.bic()},{name:"bitcoinAddress",example:o.faker.finance.bitcoinAddress()},{name:"transactionType",example:o.faker.finance.transactionType()}]},{category:"date",methods:[{name:"past",example:o.faker.date.past().toISOString()},{name:"future",example:o.faker.date.future().toISOString()},{name:"recent",example:o.faker.date.recent().toISOString()},{name:"soon",example:o.faker.date.soon().toISOString()},{name:"birthdate",example:o.faker.date.birthdate().toISOString()},{name:"month",example:o.faker.date.month()},{name:"weekday",example:o.faker.date.weekday()}]},{category:"lorem",methods:[{name:"word",example:o.faker.lorem.word()},{name:"words",example:o.faker.lorem.words()},{name:"sentence",example:o.faker.lorem.sentence()},{name:"sentences",example:o.faker.lorem.sentences()},{name:"paragraph",example:o.faker.lorem.paragraph()},{name:"slug",example:o.faker.lorem.slug()},{name:"lines",example:o.faker.lorem.lines(1)}]},{category:"string",methods:[{name:"uuid",example:o.faker.string.uuid()},{name:"alphanumeric",example:o.faker.string.alphanumeric(10)},{name:"numeric",example:o.faker.string.numeric(6)},{name:"alpha",example:o.faker.string.alpha(8)},{name:"nanoid",example:o.faker.string.nanoid()},{name:"hexadecimal",example:o.faker.string.hexadecimal({length:8})},{name:"sample",example:o.faker.string.sample(12)}]},{category:"number",methods:[{name:"int",example:String(o.faker.number.int({max:9999}))},{name:"float",example:String(o.faker.number.float({max:100,fractionDigits:2}))}]},{category:"datatype",methods:[{name:"boolean",example:String(o.faker.datatype.boolean())}]},{category:"image",methods:[{name:"avatar",example:o.faker.image.avatar()},{name:"url",example:o.faker.image.url()}]},{category:"color",methods:[{name:"human",example:o.faker.color.human()},{name:"rgb",example:o.faker.color.rgb()},{name:"hex",example:o.faker.color.rgb({format:"hex"})}]},{category:"commerce",methods:[{name:"productName",example:o.faker.commerce.productName()},{name:"price",example:o.faker.commerce.price()},{name:"department",example:o.faker.commerce.department()},{name:"productDescription",example:o.faker.commerce.productDescription().slice(0,60)+"..."},{name:"isbn",example:o.faker.commerce.isbn()}]},{category:"vehicle",methods:[{name:"vehicle",example:o.faker.vehicle.vehicle()},{name:"manufacturer",example:o.faker.vehicle.manufacturer()},{name:"model",example:o.faker.vehicle.model()},{name:"vin",example:o.faker.vehicle.vin()},{name:"vrm",example:o.faker.vehicle.vrm()}]},{category:"system",methods:[{name:"fileName",example:o.faker.system.fileName()},{name:"mimeType",example:o.faker.system.mimeType()},{name:"fileExt",example:o.faker.system.fileExt()},{name:"filePath",example:o.faker.system.filePath()},{name:"semver",example:o.faker.system.semver()}]}]}var Bn={"internet.userName":"username"};function zt(e,t){let n=o.faker[e],r=Bn[`${e}.${t}`]??t;if(!n||typeof n[r]!="function")return`[unknown: ${e}.${t}]`;let i=n[r]();return i instanceof Date?i.toISOString():String(i)}function Wn(e){let t={...q,...e},n=z.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
61
61
  `);throw new Error(`Invalid assuremind config:
62
62
  ${r}`)}return n.data}0&&(module.exports={AssuremindError,ConfigError,ExecutionError,HealingError,ProviderError,StorageError,ValidationError,acceptHealingEvent,appendPendingEvent,callFaker,clearEnvCache,configExists,createCase,createChildLogger,createSuite,defineConfig,deleteCase,deleteGlobalVariable,deleteResult,deleteSuite,formatError,generateCacheKey,getAvailableGenerators,getCasePath,getHealingStats,isAssuremindError,listCasePaths,listCases,listHealingReportIds,listResultIds,listResults,listSuiteDirs,listSuites,listSuitesWithCounts,listVariableFiles,logger,normalizeUrl,pruneResolvedEvents,readCase,readConfig,readEnvVariables,readGlobalVariables,readHealingReport,readPendingEvents,readResult,readSuite,readVariables,redactSecrets,rejectHealingEvent,resolveVariables,sanitizeGeneratedCode,screenshotsDir,setGlobalVariable,sha256,toSlug,tracesDir,tryGetEnv,updateCase,updateConfig,updateSuite,validateConfig,validateEnv,videosDir,writeCase,writeConfig,writeHealingReport,writeResult,writeSuite,writeVariables});
package/dist/index.mjs CHANGED
@@ -1,22 +1,22 @@
1
- import{z as c}from"zod";var Ye=c.enum(["off","on","only-on-failure"]),qe=c.enum(["off","on","on-first-retry","retain-on-failure"]),Xe=c.enum(["off","on","on-first-retry","retain-on-failure"]),ve=c.enum(["chromium","firefox","webkit"]),Ze=c.enum(["commit","domcontentloaded","load","networkidle"]),Ae=c.enum(["dev","stage","test","prod"]),Qe=c.object({dev:c.string().url().or(c.literal("")).default(""),stage:c.string().url().or(c.literal("")).default(""),test:c.string().url().or(c.literal("")).default(""),prod:c.string().url().or(c.literal("")).default("")}),et=c.object({enabled:c.boolean(),maxLevel:c.number().int().min(1).max(5),dailyBudget:c.number().positive(),autoPR:c.boolean()}),tt=c.object({allure:c.boolean(),html:c.boolean(),json:c.boolean()}),nt=c.object({width:c.number().int().positive(),height:c.number().int().positive()}),rt=c.object({name:c.string().min(1),environment:Ae,baseUrl:c.string().url(),browsers:c.array(ve).min(1),headless:c.boolean().optional()}),D=c.object({baseUrl:c.string().url(),environment:Ae.default("stage"),environmentUrls:Qe.default({dev:"",stage:"",test:"",prod:""}),browsers:c.array(ve).min(1),headless:c.boolean(),viewport:nt.default({width:1280,height:720}),timeout:c.number().int().positive(),retries:c.number().int().min(0),parallel:c.number().int().positive(),pageLoad:Ze.default("domcontentloaded"),screenshot:Ye,video:qe,trace:Xe,healing:et,reporting:tt,studioPort:c.number().int().min(1024).max(65535),profiles:c.array(rt).default([]),activeProfile:c.string().optional(),device:c.string().optional(),rag:c.object({enabled:c.boolean(),codeCorpus:c.object({enabled:c.boolean(),maxEntries:c.number().int().positive(),similarityThreshold:c.number().min(0).max(1),directUseThreshold:c.number().min(0).max(1)}),healingCorpus:c.object({enabled:c.boolean(),maxEntries:c.number().int().positive(),similarityThreshold:c.number().min(0).max(1)}),errorCatalog:c.object({enabled:c.boolean(),maxEntries:c.number().int().positive()}),embedder:c.literal("tfidf")}).default({enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"}),mcp:c.object({enabled:c.boolean(),headless:c.boolean(),actThenScript:c.boolean(),proactiveHealing:c.boolean(),actionTimeout:c.number().int().positive(),idleTimeout:c.number().int().positive()}).default({enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4})}),J={baseUrl:"http://localhost:3000",environment:"stage",environmentUrls:{dev:"",stage:"http://localhost:3000",test:"",prod:""},browsers:["chromium"],headless:!0,viewport:{width:1280,height:720},timeout:3e4,retries:1,parallel:1,pageLoad:"domcontentloaded",screenshot:"only-on-failure",video:"off",trace:"on-first-retry",healing:{enabled:!0,maxLevel:5,dailyBudget:5,autoPR:!1},reporting:{allure:!0,html:!0,json:!0},studioPort:4400,profiles:[],rag:{enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"},mcp:{enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4}};import E from"path";import I from"fs-extra";import{v4 as xt}from"uuid";import{z as l}from"zod";var H=l.object({id:l.string().min(1),order:l.number().int().positive(),instruction:l.string().min(1),generatedCode:l.string(),strategy:l.enum(["template","cache","rag","batch","fast","primary","recorder"]),stepType:l.enum(["ui","api","mock"]).default("ui"),lastHealed:l.string().nullable(),timeout:l.number().int().positive().optional(),retries:l.number().int().min(0).optional(),mockUrl:l.string().optional(),mockResponse:l.string().optional(),mockStatus:l.number().int().optional(),runAudit:l.boolean().optional(),soft:l.boolean().optional(),visualCheck:l.boolean().optional()}),it=l.object({type:l.enum(["inline","json-file","csv-file"]),path:l.string().optional(),data:l.array(l.record(l.string())).optional()}).optional(),Re=l.object({id:l.string(),instruction:l.string(),generatedCode:l.string().default(""),order:l.number().int().default(0)}),at=l.object({before:l.array(Re).default([]),after:l.array(Re).default([])}).default({before:[],after:[]}),te=l.object({id:l.string().min(1),name:l.string().min(1),description:l.string(),tags:l.array(l.string()),priority:l.enum(["critical","high","medium","low"]),timeout:l.number().int().positive().optional(),dataSource:it,steps:l.array(H),caseHooks:at,lighthouseCategories:l.array(l.enum(["performance","accessibility","seo"])).default(["performance","accessibility","seo"]),createdAt:l.string().datetime(),updatedAt:l.string().datetime()}),ne=l.object({id:l.string().min(1),name:l.string().min(1),description:l.string(),tags:l.array(l.string()),type:l.enum(["ui","api","audit","performance"]).default("ui"),timeout:l.number().int().positive().optional(),createdAt:l.string().datetime(),updatedAt:l.string().datetime()}),Fn=l.enum(["before_all","before_each","after_each","after_all"]),zn=l.object({before_all:l.array(H).default([]),before_each:l.array(H).default([]),after_each:l.array(H).default([]),after_all:l.array(H).default([])});var v=class extends Error{code;constructor(t,n){super(t),this.name="AssuremindError",this.code=n,Object.setPrototypeOf(this,new.target.prototype)}},re=class extends v{provider;constructor(t,n,r="PROVIDER_ERROR"){super(t,r),this.name="ProviderError",this.provider=n}},ie=class extends v{stepId;constructor(t,n,r="EXECUTION_ERROR"){super(t,r),this.name="ExecutionError",this.stepId=n}},C=class extends v{constructor(t,n="CONFIG_ERROR"){super(t,n),this.name="ConfigError"}},u=class extends v{field;constructor(t,n,r="VALIDATION_ERROR"){super(t,r),this.name="ValidationError",this.field=n}},ae=class extends v{level;constructor(t,n,r="HEALING_ERROR"){super(t,r),this.name="HealingError",this.level=n}},g=class extends v{path;constructor(t,n,r="STORAGE_ERROR"){super(t,r),this.name="StorageError",this.path=n}};function ot(e){return e instanceof v}function st(e){return e instanceof v?`[${e.code}] ${e.message}`:e instanceof Error?e.message:String(e)}import Ie from"pino";import Jn from"fs-extra";var ct=process.env.NODE_ENV!=="production",je=ct?{target:"pino-pretty",options:{colorize:!0,translateTime:"HH:MM:ss",ignore:"pid,hostname",messageFormat:"[assuremind] {msg}"}}:void 0,Ce=Ie({level:process.env.LOG_LEVEL??"info",base:{name:"assuremind"}},je?Ie.transport(je):void 0);function S(e){return Ce.child({component:e})}function lt(e){let t=/^```(?:typescript|javascript|ts|js)?\n?([\s\S]*?)```\s*$/m,n=e.match(t);return n?.[1]!==void 0?n[1].trim():e.trim()}var mt=[/\brequire\s*\(/,/\bimport\s*\(/,/\bprocess\b/,/\bchild_process\b/,/\bexec\s*\(/,/\bspawn\s*\(/,/\beval\s*\(/,/\bFunction\s*\(/,/\b__dirname\b/,/\b__filename\b/,/\bglobal\b/,/\bwindow\.location\.href\s*=/,/\bdocument\.cookie\b/,/\blocalStorage\b/,/\bsessionStorage\b/,/\bIndexedDB\b/,/\bXMLHttpRequest\b/,/\bfetch\s*\(/,/\bWebSocket\s*\(/];function pt(e){for(let t of mt)if(t.test(e))throw new u(`Generated code contains forbidden pattern: ${t.source}. This may be a prompt injection attempt. Please regenerate the step.`,"generatedCode","UNSAFE_CODE")}var ut=/\s+(?:field|input|box|textbox|text\s?box|drop-?\s?down|dropdown|combo\s?box|combobox|menu|selector|element|checkbox|radio\s?button|radio)$/i;function Te(e){let t=e.replace(ut,"").trim();return t.length>0?t:e}function dt(e){let t=e;return t=t.replace(/(getBy(?:Label|Placeholder)\(\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Te(a)}${i}`),t=t.replace(/(name:\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Te(a)}${i}`),t}function gt(e){let t=e;return t=t.replace(/\.or\((?:[^()]*|\([^()]*\))*\)(?:\.(?:first|last)\(\))?/g,""),t=t.replace(/\.(?:first|last)\(\)(?=\.\w)/g,""),t=dt(t),t}function ft(e){let t=lt(e);if(!t)throw new u("AI returned an empty code response. Please try regenerating.","generatedCode","EMPTY_CODE");let n=gt(t);return pt(n),n}function O(e){return e.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function ht(e,t){let n=e;for(let r of t)r.length>0&&(n=n.replaceAll(r,"[REDACTED]"));return n}import Pe from"path";import R from"fs-extra";async function x(e,t){let n=Pe.dirname(e);await R.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.writeJson(r,t,{spaces:2}),await R.rename(r,e)}catch(i){throw await R.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}async function y(e){try{return await R.readJson(e)}catch(t){throw new g(`Failed to read JSON file "${e}": ${t instanceof Error?t.message:String(t)}`,e,"READ_FAILED")}}async function _e(e,t){let n=Pe.dirname(e);await R.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.writeFile(r,t,"utf8"),await R.rename(r,e)}catch(i){throw await R.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}var F=S("suite-store"),K="suite.json";async function Y(e){let t=E.join(e,K);if(!await I.pathExists(t))throw new g(`Suite file not found at "${t}". Ensure the suite directory exists and contains a suite.json file.`,t,"SUITE_NOT_FOUND");let n=await y(t),r=ne.safeParse(n);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
1
+ import{z as c}from"zod";var Ye=c.enum(["off","on","only-on-failure"]),qe=c.enum(["off","on","on-first-retry","retain-on-failure"]),Xe=c.enum(["off","on","on-first-retry","retain-on-failure"]),ve=c.enum(["chromium","firefox","webkit"]),Ze=c.enum(["commit","domcontentloaded","load","networkidle"]),Ae=c.enum(["dev","stage","test","prod"]),Qe=c.object({dev:c.string().url().or(c.literal("")).default(""),stage:c.string().url().or(c.literal("")).default(""),test:c.string().url().or(c.literal("")).default(""),prod:c.string().url().or(c.literal("")).default("")}),et=c.object({enabled:c.boolean(),maxLevel:c.number().int().min(1).max(5),dailyBudget:c.number().positive(),autoPR:c.boolean(),healAssertionLocators:c.boolean().default(!1)}),tt=c.object({allure:c.boolean(),html:c.boolean(),json:c.boolean()}),nt=c.object({width:c.number().int().positive(),height:c.number().int().positive()}),rt=c.object({name:c.string().min(1),environment:Ae,baseUrl:c.string().url(),browsers:c.array(ve).min(1),headless:c.boolean().optional()}),D=c.object({baseUrl:c.string().url(),environment:Ae.default("stage"),environmentUrls:Qe.default({dev:"",stage:"",test:"",prod:""}),browsers:c.array(ve).min(1),headless:c.boolean(),viewport:nt.default({width:1280,height:720}),timeout:c.number().int().positive(),retries:c.number().int().min(0),parallel:c.number().int().positive(),pageLoad:Ze.default("domcontentloaded"),screenshot:Ye,video:qe,trace:Xe,healing:et,reporting:tt,studioPort:c.number().int().min(1024).max(65535),profiles:c.array(rt).default([]),activeProfile:c.string().optional(),device:c.string().optional(),rag:c.object({enabled:c.boolean(),codeCorpus:c.object({enabled:c.boolean(),maxEntries:c.number().int().positive(),similarityThreshold:c.number().min(0).max(1),directUseThreshold:c.number().min(0).max(1)}),healingCorpus:c.object({enabled:c.boolean(),maxEntries:c.number().int().positive(),similarityThreshold:c.number().min(0).max(1)}),errorCatalog:c.object({enabled:c.boolean(),maxEntries:c.number().int().positive()}),embedder:c.literal("tfidf")}).default({enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"}),mcp:c.object({enabled:c.boolean(),headless:c.boolean(),actThenScript:c.boolean(),proactiveHealing:c.boolean(),actionTimeout:c.number().int().positive(),idleTimeout:c.number().int().positive()}).default({enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4}),ci:c.object({writeToRepo:c.boolean()}).default({writeToRepo:!1})}),J={baseUrl:"http://localhost:3000",environment:"stage",environmentUrls:{dev:"",stage:"http://localhost:3000",test:"",prod:""},browsers:["chromium"],headless:!0,viewport:{width:1280,height:720},timeout:3e4,retries:1,parallel:1,pageLoad:"domcontentloaded",screenshot:"only-on-failure",video:"off",trace:"on-first-retry",healing:{enabled:!0,maxLevel:5,dailyBudget:5,autoPR:!1,healAssertionLocators:!1},reporting:{allure:!0,html:!0,json:!0},studioPort:4400,profiles:[],rag:{enabled:!0,codeCorpus:{enabled:!0,maxEntries:500,similarityThreshold:.65,directUseThreshold:.9},healingCorpus:{enabled:!0,maxEntries:300,similarityThreshold:.6},errorCatalog:{enabled:!0,maxEntries:200},embedder:"tfidf"},mcp:{enabled:!0,headless:!0,actThenScript:!1,proactiveHealing:!1,actionTimeout:15e3,idleTimeout:3e4},ci:{writeToRepo:!1}};import E from"path";import I from"fs-extra";import{v4 as bt}from"uuid";import{z as l}from"zod";var H=l.object({id:l.string().min(1),order:l.number().int().positive(),instruction:l.string().min(1),generatedCode:l.string(),strategy:l.enum(["template","cache","rag","batch","fast","primary","recorder"]),stepType:l.enum(["ui","api","mock"]).default("ui"),lastHealed:l.string().nullable(),timeout:l.number().int().positive().optional(),retries:l.number().int().min(0).optional(),mockUrl:l.string().optional(),mockResponse:l.string().optional(),mockStatus:l.number().int().optional(),runAudit:l.boolean().optional(),soft:l.boolean().optional(),visualCheck:l.boolean().optional()}),it=l.object({type:l.enum(["inline","json-file","csv-file"]),path:l.string().optional(),data:l.array(l.record(l.string())).optional()}).optional(),Re=l.object({id:l.string(),instruction:l.string(),generatedCode:l.string().default(""),order:l.number().int().default(0)}),at=l.object({before:l.array(Re).default([]),after:l.array(Re).default([])}).default({before:[],after:[]}),te=l.object({id:l.string().min(1),name:l.string().min(1),description:l.string(),tags:l.array(l.string()),priority:l.enum(["critical","high","medium","low"]),timeout:l.number().int().positive().optional(),dataSource:it,steps:l.array(H),caseHooks:at,lighthouseCategories:l.array(l.enum(["performance","accessibility","seo"])).default(["performance","accessibility","seo"]),createdAt:l.string().datetime(),updatedAt:l.string().datetime()}),ne=l.object({id:l.string().min(1),name:l.string().min(1),description:l.string(),tags:l.array(l.string()),type:l.enum(["ui","api","audit","performance"]).default("ui"),timeout:l.number().int().positive().optional(),createdAt:l.string().datetime(),updatedAt:l.string().datetime()}),zn=l.enum(["before_all","before_each","after_each","after_all"]),Un=l.object({before_all:l.array(H).default([]),before_each:l.array(H).default([]),after_each:l.array(H).default([]),after_all:l.array(H).default([])});var v=class extends Error{code;constructor(t,n){super(t),this.name="AssuremindError",this.code=n,Object.setPrototypeOf(this,new.target.prototype)}},re=class extends v{provider;constructor(t,n,r="PROVIDER_ERROR"){super(t,r),this.name="ProviderError",this.provider=n}},ie=class extends v{stepId;constructor(t,n,r="EXECUTION_ERROR"){super(t,r),this.name="ExecutionError",this.stepId=n}},C=class extends v{constructor(t,n="CONFIG_ERROR"){super(t,n),this.name="ConfigError"}},u=class extends v{field;constructor(t,n,r="VALIDATION_ERROR"){super(t,r),this.name="ValidationError",this.field=n}},ae=class extends v{level;constructor(t,n,r="HEALING_ERROR"){super(t,r),this.name="HealingError",this.level=n}},g=class extends v{path;constructor(t,n,r="STORAGE_ERROR"){super(t,r),this.name="StorageError",this.path=n}};function ot(e){return e instanceof v}function st(e){return e instanceof v?`[${e.code}] ${e.message}`:e instanceof Error?e.message:String(e)}import Ie from"pino";import Kn from"fs-extra";var ct=process.env.NODE_ENV!=="production",je=ct?{target:"pino-pretty",options:{colorize:!0,translateTime:"HH:MM:ss",ignore:"pid,hostname",messageFormat:"[assuremind] {msg}"}}:void 0,Ce=Ie({level:process.env.LOG_LEVEL??"info",base:{name:"assuremind"}},je?Ie.transport(je):void 0);function S(e){return Ce.child({component:e})}function lt(e){let t=/^```(?:typescript|javascript|ts|js)?\n?([\s\S]*?)```\s*$/m,n=e.match(t);return n?.[1]!==void 0?n[1].trim():e.trim()}var mt=[/\brequire\s*\(/,/\bimport\s*\(/,/\bprocess\b/,/\bchild_process\b/,/\bexec\s*\(/,/\bspawn\s*\(/,/\beval\s*\(/,/\bFunction\s*\(/,/\b__dirname\b/,/\b__filename\b/,/\bglobal\b/,/\bwindow\.location\.href\s*=/,/\bdocument\.cookie\b/,/\blocalStorage\b/,/\bsessionStorage\b/,/\bIndexedDB\b/,/\bXMLHttpRequest\b/,/\bfetch\s*\(/,/\bWebSocket\s*\(/];function pt(e){for(let t of mt)if(t.test(e))throw new u(`Generated code contains forbidden pattern: ${t.source}. This may be a prompt injection attempt. Please regenerate the step.`,"generatedCode","UNSAFE_CODE")}var ut=/\s+(?:field|input|box|textbox|text\s?box|drop-?\s?down|dropdown|combo\s?box|combobox|menu|selector|element|checkbox|radio\s?button|radio)$/i;function Te(e){let t=e.replace(ut,"").trim();return t.length>0?t:e}function dt(e){let t=e;return t=t.replace(/(getBy(?:Label|Placeholder)\(\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Te(a)}${i}`),t=t.replace(/(name:\s*)(['"`])([\s\S]*?)\2/g,(n,r,i,a)=>`${r}${i}${Te(a)}${i}`),t}function gt(e){return e.replace(/\blocator\((['"`])([\s\S]*?)\1\)/g,(t,n,r)=>{if(!r.includes(" >> "))return t;let i=r.split(" >> ").map(a=>a.trim()).filter(Boolean);return i.length<2?t:i.map(a=>`locator(${n}${a}${n})`).join(".")})}function ft(e){let t=e;return t=gt(t),t=t.replace(/\.or\((?:[^()]*|\([^()]*\))*\)(?:\.(?:first|last)\(\))?/g,""),t=t.replace(/\.(?:first|last)\(\)(?=\.\w)/g,""),t=dt(t),t}function ht(e){let t=lt(e);if(!t)throw new u("AI returned an empty code response. Please try regenerating.","generatedCode","EMPTY_CODE");let n=ft(t);return pt(n),n}function O(e){return e.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function xt(e,t){let n=e;for(let r of t)r.length>0&&(n=n.replaceAll(r,"[REDACTED]"));return n}import Pe from"path";import R from"fs-extra";async function x(e,t){let n=Pe.dirname(e);await R.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.writeJson(r,t,{spaces:2}),await R.rename(r,e)}catch(i){throw await R.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}async function y(e){try{return await R.readJson(e)}catch(t){throw new g(`Failed to read JSON file "${e}": ${t instanceof Error?t.message:String(t)}`,e,"READ_FAILED")}}async function _e(e,t){let n=Pe.dirname(e);await R.ensureDir(n);let r=`${e}.${process.pid}.tmp`;try{await R.writeFile(r,t,"utf8"),await R.rename(r,e)}catch(i){throw await R.remove(r).catch(()=>{}),new g(`Failed to write file "${e}": ${i instanceof Error?i.message:String(i)}`,e,"WRITE_FAILED")}}var F=S("suite-store"),K="suite.json";async function Y(e){let t=E.join(e,K);if(!await I.pathExists(t))throw new g(`Suite file not found at "${t}". Ensure the suite directory exists and contains a suite.json file.`,t,"SUITE_NOT_FOUND");let n=await y(t),r=ne.safeParse(n);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
2
2
  `);throw new u(`Invalid suite.json at "${t}":
3
3
  ${a}`,"suite","INVALID_SUITE")}if(!n.type){let a=E.basename(E.dirname(e));a==="api"?r.data.type="api":a==="audit"||a==="performance"?r.data.type="audit":r.data.type="ui"}return r.data}async function oe(e,t){await I.ensureDir(e);let n=E.join(e,K),r=ne.safeParse(t);if(!r.success){let i=r.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
4
4
  `);throw new u(`Cannot write invalid suite to "${n}":
5
- ${i}`,"suite","INVALID_SUITE")}await x(n,r.data),F.debug({suiteId:t.id,path:n},"Suite written")}async function bt(e,t){let n=new Date().toISOString(),r=t.type??"ui",i={...t,type:r,id:xt(),createdAt:n,updatedAt:n},a=E.join(e,r),p=E.join(a,O(i.name));if(await I.pathExists(E.join(p,K)))throw new g(`Suite directory already exists at "${p}". Choose a different name or delete the existing suite first.`,p,"SUITE_ALREADY_EXISTS");return await oe(p,i),F.info({suiteId:i.id,path:p},"Suite created"),{suiteDir:p,suiteId:i.id}}async function St(e,t){let n=await Y(e),r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:new Date().toISOString()};return await oe(e,r),r}async function yt(e){if(!await I.pathExists(e))throw new g(`Suite directory not found at "${e}".`,e,"SUITE_NOT_FOUND");await I.remove(e),F.info({path:e},"Suite deleted")}async function se(e){let t=[],n=[E.join(e,"ui"),E.join(e,"api"),E.join(e,"audit"),E.join(e,"performance"),e];for(let r of n){if(!await I.pathExists(r))continue;let i=await I.readdir(r,{withFileTypes:!0});for(let a of i){if(!a.isDirectory()||r===e&&(a.name==="ui"||a.name==="api"||a.name==="audit"||a.name==="performance"))continue;let p=E.join(r,a.name,K);await I.pathExists(p)&&t.push(E.join(r,a.name))}}return t}async function Et(e){let t=await se(e),n=[];for(let r of t)try{n.push(await Y(r))}catch(i){F.warn({path:r,err:i},"Failed to read suite \u2014 skipping")}return n}async function wt(e){let t=await se(e),n=[];for(let r of t)try{let i=await Y(r),p=(await I.readdir(r,{withFileTypes:!0})).filter(b=>b.isFile()&&b.name.endsWith(".test.json")).length;n.push({...i,caseCount:p})}catch(i){F.warn({path:r,err:i},"Failed to read suite \u2014 skipping")}return n}import ce from"path";import z from"fs-extra";import{v4 as vt}from"uuid";var q=S("case-store"),le=".test.json";async function me(e){if(!await z.pathExists(e))throw new g(`Test case file not found at "${e}".`,e,"CASE_NOT_FOUND");let t=await y(e),n=te.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
5
+ ${i}`,"suite","INVALID_SUITE")}await x(n,r.data),F.debug({suiteId:t.id,path:n},"Suite written")}async function St(e,t){let n=new Date().toISOString(),r=t.type??"ui",i={...t,type:r,id:bt(),createdAt:n,updatedAt:n},a=E.join(e,r),p=E.join(a,O(i.name));if(await I.pathExists(E.join(p,K)))throw new g(`Suite directory already exists at "${p}". Choose a different name or delete the existing suite first.`,p,"SUITE_ALREADY_EXISTS");return await oe(p,i),F.info({suiteId:i.id,path:p},"Suite created"),{suiteDir:p,suiteId:i.id}}async function yt(e,t){let n=await Y(e),r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:new Date().toISOString()};return await oe(e,r),r}async function Et(e){if(!await I.pathExists(e))throw new g(`Suite directory not found at "${e}".`,e,"SUITE_NOT_FOUND");await I.remove(e),F.info({path:e},"Suite deleted")}async function se(e){let t=[],n=[E.join(e,"ui"),E.join(e,"api"),E.join(e,"audit"),E.join(e,"performance"),e];for(let r of n){if(!await I.pathExists(r))continue;let i=await I.readdir(r,{withFileTypes:!0});for(let a of i){if(!a.isDirectory()||r===e&&(a.name==="ui"||a.name==="api"||a.name==="audit"||a.name==="performance"))continue;let p=E.join(r,a.name,K);await I.pathExists(p)&&t.push(E.join(r,a.name))}}return t}async function wt(e){let t=await se(e),n=[];for(let r of t)try{n.push(await Y(r))}catch(i){F.warn({path:r,err:i},"Failed to read suite \u2014 skipping")}return n}async function vt(e){let t=await se(e),n=[];for(let r of t)try{let i=await Y(r),p=(await I.readdir(r,{withFileTypes:!0})).filter(b=>b.isFile()&&b.name.endsWith(".test.json")).length;n.push({...i,caseCount:p})}catch(i){F.warn({path:r,err:i},"Failed to read suite \u2014 skipping")}return n}import ce from"path";import z from"fs-extra";import{v4 as At}from"uuid";var q=S("case-store"),le=".test.json";async function me(e){if(!await z.pathExists(e))throw new g(`Test case file not found at "${e}".`,e,"CASE_NOT_FOUND");let t=await y(e),n=te.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
6
6
  `);throw new u(`Invalid test case file at "${e}":
7
7
  ${r}`,"case","INVALID_CASE")}return n.data}async function pe(e,t){let n=te.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
8
8
  `);throw new u(`Cannot write invalid test case to "${e}":
9
- ${r}`,"case","INVALID_CASE")}await x(e,n.data),q.debug({caseId:t.id,path:e},"Test case written")}async function At(e,t){let n=new Date().toISOString(),r={...t,id:vt(),createdAt:n,updatedAt:n},i=ce.join(e,`${O(r.name)}${le}`);return await pe(i,r),q.info({caseId:r.id,path:i},"Test case created"),i}async function Rt(e,t){let n=await me(e),r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:new Date().toISOString()};return await pe(e,r),r}async function It(e){if(!await z.pathExists(e))throw new g(`Test case file not found at "${e}".`,e,"CASE_NOT_FOUND");await z.remove(e),q.info({path:e},"Test case deleted")}async function Ne(e){return await z.pathExists(e)?(await z.readdir(e,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.endsWith(le)).map(n=>ce.join(e,n.name)):[]}async function jt(e){let t=await Ne(e),n=[];for(let r of t)try{n.push(await me(r))}catch(i){q.warn({path:r,err:i},"Failed to read test case \u2014 skipping")}return n.sort((r,i)=>r.name.localeCompare(i.name))}function Ct(e,t){return ce.join(e,`${O(t)}${le}`)}import _ from"path";import X from"fs-extra";import{z as P}from"zod";var Tt=P.object({value:P.string(),secret:P.literal(!0)}),Pt=P.union([P.string(),Tt]),ue=P.record(P.string(),Pt);function _t(e){return typeof e=="object"&&e.secret===!0}function Oe(e){return _t(e)?e.value:e}var Nt=S("variable-store"),U="variables",de="global.json";function Ot(e){return`${e}.env.json`}async function k(e){if(!await X.pathExists(e))return{};let t=await y(e),n=ue.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
9
+ ${r}`,"case","INVALID_CASE")}await x(e,n.data),q.debug({caseId:t.id,path:e},"Test case written")}async function Rt(e,t){let n=new Date().toISOString(),r={...t,id:At(),createdAt:n,updatedAt:n},i=ce.join(e,`${O(r.name)}${le}`);return await pe(i,r),q.info({caseId:r.id,path:i},"Test case created"),i}async function It(e,t){let n=await me(e),r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:new Date().toISOString()};return await pe(e,r),r}async function jt(e){if(!await z.pathExists(e))throw new g(`Test case file not found at "${e}".`,e,"CASE_NOT_FOUND");await z.remove(e),q.info({path:e},"Test case deleted")}async function Ne(e){return await z.pathExists(e)?(await z.readdir(e,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.endsWith(le)).map(n=>ce.join(e,n.name)):[]}async function Ct(e){let t=await Ne(e),n=[];for(let r of t)try{n.push(await me(r))}catch(i){q.warn({path:r,err:i},"Failed to read test case \u2014 skipping")}return n.sort((r,i)=>r.name.localeCompare(i.name))}function Tt(e,t){return ce.join(e,`${O(t)}${le}`)}import _ from"path";import X from"fs-extra";import{z as P}from"zod";var Pt=P.object({value:P.string(),secret:P.literal(!0)}),_t=P.union([P.string(),Pt]),ue=P.record(P.string(),_t);function Nt(e){return typeof e=="object"&&e.secret===!0}function Oe(e){return Nt(e)?e.value:e}var Ot=S("variable-store"),U="variables",de="global.json";function $t(e){return`${e}.env.json`}async function k(e){if(!await X.pathExists(e))return{};let t=await y(e),n=ue.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
10
10
  `);throw new u(`Invalid variables file at "${e}":
11
11
  ${r}`,"variables","INVALID_VARIABLES")}return n.data}async function ge(e,t){let n=ue.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
12
12
  `);throw new u(`Cannot write invalid variables to "${e}":
13
- ${r}`,"variables","INVALID_VARIABLES")}await x(e,n.data),Nt.debug({path:e},"Variables written")}async function $e(e){let t=_.join(e,U,de);return k(t)}async function Le(e,t){let n=_.join(e,U,Ot(t));return k(n)}async function $t(e,t){let n=await $e(e),r=t?await Le(e,t):{},i={...n,...r},a={};for(let[p,b]of Object.entries(i))a[p]=Oe(b);return a}async function Lt(e,t,n){let r=_.join(e,U,de);await X.ensureDir(_.dirname(r));let i=await k(r);i[t]=n,await ge(r,i)}async function Vt(e,t){let n=_.join(e,U,de),r=await k(n);if(!(t in r))throw new g(`Variable "${t}" not found in global variables.`,n,"VARIABLE_NOT_FOUND");delete r[t],await ge(n,r)}async function Dt(e){let t=_.join(e,U);return await X.pathExists(t)?(await X.readdir(t,{withFileTypes:!0})).filter(r=>r.isFile()&&r.name.endsWith(".json")).map(r=>_.join(t,r.name)):[]}import G from"path";import fe from"fs-extra";var he=S("config-store"),xe="autotest.config.json",Ve="autotest.config.ts";async function be(e){let t=G.join(e,xe);if(!await fe.pathExists(t))return he.debug({rootDir:e},"No autotest.config.json found \u2014 using defaults"),J;let n=await y(t),r=D.safeParse(n);if(!r.success){let i=r.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
13
+ ${r}`,"variables","INVALID_VARIABLES")}await x(e,n.data),Ot.debug({path:e},"Variables written")}async function $e(e){let t=_.join(e,U,de);return k(t)}async function Le(e,t){let n=_.join(e,U,$t(t));return k(n)}async function Lt(e,t){let n=await $e(e),r=t?await Le(e,t):{},i={...n,...r},a={};for(let[p,b]of Object.entries(i))a[p]=Oe(b);return a}async function Vt(e,t,n){let r=_.join(e,U,de);await X.ensureDir(_.dirname(r));let i=await k(r);i[t]=n,await ge(r,i)}async function Dt(e,t){let n=_.join(e,U,de),r=await k(n);if(!(t in r))throw new g(`Variable "${t}" not found in global variables.`,n,"VARIABLE_NOT_FOUND");delete r[t],await ge(n,r)}async function Ht(e){let t=_.join(e,U);return await X.pathExists(t)?(await X.readdir(t,{withFileTypes:!0})).filter(r=>r.isFile()&&r.name.endsWith(".json")).map(r=>_.join(t,r.name)):[]}import G from"path";import fe from"fs-extra";var he=S("config-store"),xe="autotest.config.json",Ve="autotest.config.ts";async function be(e){let t=G.join(e,xe);if(!await fe.pathExists(t))return he.debug({rootDir:e},"No autotest.config.json found \u2014 using defaults"),J;let n=await y(t),r=D.safeParse(n);if(!r.success){let i=r.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
14
14
  `);throw new u(`Invalid autotest.config.json at "${t}":
15
15
  ${i}
16
16
 
17
17
  How to fix: Run "npx assuremind init" to reset to defaults, or manually correct the file using autotest.config.ts as reference.`,"config","INVALID_CONFIG")}return r.data}async function De(e,t){let n=D.safeParse(t);if(!n.success){let a=n.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
18
18
  `);throw new u(`Cannot write invalid config:
19
- ${a}`,"config","INVALID_CONFIG")}let r=G.join(e,xe);await x(r,n.data);let i=G.join(e,Ve);await _e(i,Ut(n.data)),he.info({rootDir:e},"Config saved")}async function Ht(e,t){let n=await be(e),r={...n,...t,healing:{...n.healing,...t.healing??{}},reporting:{...n.reporting,...t.reporting??{}},environmentUrls:{...n.environmentUrls,...t.environmentUrls??{}}};return await De(e,r),r}async function Ft(e){let t=G.join(e,xe),n=G.join(e,Ve);return await fe.pathExists(t)||await fe.pathExists(n)}async function zt(e){let t=await be(e);if(!t.baseUrl)throw new C('baseUrl is required in autotest.config.ts. Set it to your application URL, e.g. "http://localhost:3000".',"CONFIG_BASE_URL_MISSING");he.debug({config:t},"Config validated successfully")}function Ut(e){return`import { defineConfig } from 'assuremind';
19
+ ${a}`,"config","INVALID_CONFIG")}let r=G.join(e,xe);await x(r,n.data);let i=G.join(e,Ve);await _e(i,kt(n.data)),he.info({rootDir:e},"Config saved")}async function Ft(e,t){let n=await be(e),r={...n,...t,healing:{...n.healing,...t.healing??{}},reporting:{...n.reporting,...t.reporting??{}},environmentUrls:{...n.environmentUrls,...t.environmentUrls??{}}};return await De(e,r),r}async function zt(e){let t=G.join(e,xe),n=G.join(e,Ve);return await fe.pathExists(t)||await fe.pathExists(n)}async function Ut(e){let t=await be(e);if(!t.baseUrl)throw new C('baseUrl is required in autotest.config.ts. Set it to your application URL, e.g. "http://localhost:3000".',"CONFIG_BASE_URL_MISSING");he.debug({config:t},"Config validated successfully")}function kt(e){return`import { defineConfig } from 'assuremind';
20
20
 
21
21
  export default defineConfig({
22
22
  baseUrl: '${e.baseUrl}',
@@ -41,22 +41,22 @@ export default defineConfig({
41
41
  },
42
42
  studioPort: ${e.studioPort},
43
43
  });
44
- `}import j from"path";import $ from"fs-extra";import{z as s}from"zod";var Z=s.enum(["pending","running","passed","failed","skipped"]),kt=s.object({method:s.string(),url:s.string(),headers:s.record(s.string()).optional(),body:s.string().optional()}).optional(),Gt=s.object({status:s.number(),statusText:s.string(),headers:s.record(s.string()).optional(),body:s.string().optional(),duration:s.number()}).optional(),A=s.number().nullish().transform(e=>e??void 0),He=s.object({id:s.string(),title:s.string(),passed:s.boolean(),partial:s.boolean().optional(),na:s.boolean().optional()}),Mt=s.object({url:s.string(),stepIndex:s.number().int(),stepInstruction:s.string(),score:A,a11yScore:A,seoScore:A,fcp:A,lcp:A,cls:A,ttfb:A,tbt:A,si:A,tti:A,inp:A,a11yAudits:s.array(He).optional(),seoAudits:s.array(He).optional(),lighthouseError:s.string().optional()}),Bt=s.object({stepId:s.string(),instruction:s.string(),status:Z,code:s.string(),error:s.string().optional(),duration:s.number(),screenshotPath:s.string().optional(),healed:s.boolean().optional(),healedCode:s.string().optional(),stepType:s.enum(["ui","api","mock"]).optional(),apiRequest:kt,apiResponse:Gt,navigatedToUrl:s.string().optional(),auditUrl:s.string().optional()}),Wt=s.object({caseId:s.string(),caseName:s.string(),status:Z,steps:s.array(Bt),duration:s.number(),browser:s.string(),device:s.string().optional(),startedAt:s.string().datetime(),finishedAt:s.string().datetime(),videoPath:s.string().optional(),tracePath:s.string().optional(),dataRowIndex:s.number().int().optional(),dataRow:s.record(s.string()).optional(),pageLoads:s.array(Mt).default([])}),Jt=s.object({suiteId:s.string(),suiteName:s.string(),suiteType:s.enum(["ui","api","audit","performance"]).optional(),status:Z,cases:s.array(Wt),duration:s.number(),browser:s.string(),startedAt:s.string().datetime(),finishedAt:s.string().datetime()}),Se=s.object({runId:s.string(),status:Z,environment:s.string().optional(),suites:s.array(Jt),duration:s.number(),startedAt:s.string().datetime(),finishedAt:s.string().datetime(),totalTests:s.number().int(),passed:s.number().int(),failed:s.number().int(),skipped:s.number().int(),logFilePath:s.string().optional()});var ye=S("result-store"),N="results",Fe="runs";function Ee(e,t){return j.join(e,Fe,`${t}.json`)}async function Kt(e,t){let n=Se.safeParse(t);if(!n.success){let i=n.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
44
+ `}import j from"path";import $ from"fs-extra";import{z as s}from"zod";var Z=s.enum(["pending","running","passed","failed","skipped"]),Gt=s.object({method:s.string(),url:s.string(),headers:s.record(s.string()).optional(),body:s.string().optional()}).optional(),Mt=s.object({status:s.number(),statusText:s.string(),headers:s.record(s.string()).optional(),body:s.string().optional(),duration:s.number()}).optional(),A=s.number().nullish().transform(e=>e??void 0),He=s.object({id:s.string(),title:s.string(),passed:s.boolean(),partial:s.boolean().optional(),na:s.boolean().optional()}),Bt=s.object({url:s.string(),stepIndex:s.number().int(),stepInstruction:s.string(),score:A,a11yScore:A,seoScore:A,fcp:A,lcp:A,cls:A,ttfb:A,tbt:A,si:A,tti:A,inp:A,a11yAudits:s.array(He).optional(),seoAudits:s.array(He).optional(),lighthouseError:s.string().optional()}),Wt=s.object({stepId:s.string(),instruction:s.string(),status:Z,code:s.string(),error:s.string().optional(),duration:s.number(),screenshotPath:s.string().optional(),healed:s.boolean().optional(),healedCode:s.string().optional(),stepType:s.enum(["ui","api","mock"]).optional(),apiRequest:Gt,apiResponse:Mt,navigatedToUrl:s.string().optional(),auditUrl:s.string().optional()}),Jt=s.object({caseId:s.string(),caseName:s.string(),status:Z,steps:s.array(Wt),duration:s.number(),browser:s.string(),device:s.string().optional(),startedAt:s.string().datetime(),finishedAt:s.string().datetime(),videoPath:s.string().optional(),tracePath:s.string().optional(),downloadPaths:s.array(s.string()).default([]),dataRowIndex:s.number().int().optional(),dataRow:s.record(s.string()).optional(),pageLoads:s.array(Bt).default([])}),Kt=s.object({suiteId:s.string(),suiteName:s.string(),suiteType:s.enum(["ui","api","audit","performance"]).optional(),status:Z,cases:s.array(Jt),duration:s.number(),browser:s.string(),startedAt:s.string().datetime(),finishedAt:s.string().datetime()}),Se=s.object({runId:s.string(),status:Z,environment:s.string().optional(),suites:s.array(Kt),duration:s.number(),startedAt:s.string().datetime(),finishedAt:s.string().datetime(),totalTests:s.number().int(),passed:s.number().int(),failed:s.number().int(),skipped:s.number().int(),logFilePath:s.string().optional()});var ye=S("result-store"),N="results",Fe="runs";function Ee(e,t){return j.join(e,Fe,`${t}.json`)}async function Yt(e,t){let n=Se.safeParse(t);if(!n.success){let i=n.error.issues.map(a=>` \u2022 ${a.path.join(".")}: ${a.message}`).join(`
45
45
  `);throw new u(`Cannot write invalid run result:
46
46
  ${i}`,"result","INVALID_RESULT")}let r=Ee(j.join(e,N),t.runId);await x(r,n.data),ye.info({runId:t.runId,status:t.status},"Run result saved")}async function ze(e,t){let n=Ee(j.join(e,N),t);if(!await $.pathExists(n))throw new g(`Run result not found for runId "${t}".`,n,"RESULT_NOT_FOUND");let r=await y(n),i=Se.safeParse(r);if(!i.success){let a=i.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
47
47
  `);throw new u(`Invalid run result at "${n}":
48
- ${a}`,"result","INVALID_RESULT")}return i.data}async function Ue(e){let t=j.join(e,N,Fe);if(!await $.pathExists(t))return[];let r=(await $.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.endsWith(".json")).map(a=>a.name.replace(".json",""));return(await Promise.all(r.map(async a=>{let p=await $.stat(j.join(t,`${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}async function Yt(e,t=20){let n=await Ue(e),r=[];for(let i of n.slice(0,t))try{let a=await ze(e,i);r.push({runId:a.runId,status:a.status,environment:a.environment,startedAt:a.startedAt,finishedAt:a.finishedAt,totalTests:a.totalTests,passed:a.passed,failed:a.failed,skipped:a.skipped,duration:a.duration,suiteIds:a.suites.map(p=>p.suiteId)})}catch(a){ye.warn({runId:i,err:a},"Failed to read run result \u2014 skipping")}return r}async function qt(e,t){let n=Ee(j.join(e,N),t);if(!await $.pathExists(n))throw new g(`Run result not found for runId "${t}".`,n,"RESULT_NOT_FOUND");await $.remove(n),ye.info({runId:t},"Run result deleted")}function Xt(e){return j.join(e,N,"screenshots")}function Zt(e){return j.join(e,N,"videos")}function Qt(e){return j.join(e,N,"traces")}import w from"path";import L from"fs-extra";import{z as d}from"zod";var en=d.enum(["pending","accepted","rejected"]),tn=d.enum(["retry","regenerate","multi-selector","visual","decompose","manual"]),Q=d.object({id:d.string(),runId:d.string(),suiteId:d.string(),caseId:d.string(),stepId:d.string(),stepInstruction:d.string(),failedCode:d.string(),healedCode:d.string(),error:d.string(),strategy:tn,level:d.number().int().min(1).max(6),status:en,pageUrl:d.string(),timestamp:d.string().datetime(),acceptedAt:d.string().datetime().optional(),rejectedAt:d.string().datetime().optional()}),we=d.object({runId:d.string(),generatedAt:d.string().datetime(),totalHeals:d.number().int(),accepted:d.number().int(),rejected:d.number().int(),pending:d.number().int(),events:d.array(Q)});var M=S("healing-store"),T=w.join("results","healing"),nn="pending.json",ee="healing-report-";function Me(e,t){return w.join(e,`${ee}${t}.json`)}function B(e){return w.join(e,nn)}async function rn(e,t){let n=we.safeParse(t);if(!n.success){let a=n.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
48
+ ${a}`,"result","INVALID_RESULT")}return i.data}async function Ue(e){let t=j.join(e,N,Fe);if(!await $.pathExists(t))return[];let r=(await $.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.endsWith(".json")).map(a=>a.name.replace(".json",""));return(await Promise.all(r.map(async a=>{let p=await $.stat(j.join(t,`${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}async function qt(e,t=20){let n=await Ue(e),r=[];for(let i of n.slice(0,t))try{let a=await ze(e,i);r.push({runId:a.runId,status:a.status,environment:a.environment,startedAt:a.startedAt,finishedAt:a.finishedAt,totalTests:a.totalTests,passed:a.passed,failed:a.failed,skipped:a.skipped,duration:a.duration,suiteIds:a.suites.map(p=>p.suiteId)})}catch(a){ye.warn({runId:i,err:a},"Failed to read run result \u2014 skipping")}return r}async function Xt(e,t){let n=Ee(j.join(e,N),t);if(!await $.pathExists(n))throw new g(`Run result not found for runId "${t}".`,n,"RESULT_NOT_FOUND");await $.remove(n),ye.info({runId:t},"Run result deleted")}function Zt(e){return j.join(e,N,"screenshots")}function Qt(e){return j.join(e,N,"videos")}function en(e){return j.join(e,N,"traces")}import w from"path";import L from"fs-extra";import{z as d}from"zod";var tn=d.enum(["pending","accepted","rejected"]),nn=d.enum(["retry","regenerate","multi-selector","visual","decompose","manual"]),Q=d.object({id:d.string(),runId:d.string(),suiteId:d.string(),caseId:d.string(),stepId:d.string(),stepInstruction:d.string(),failedCode:d.string(),healedCode:d.string(),error:d.string(),strategy:nn,level:d.number().int().min(1).max(6),status:tn,pageUrl:d.string(),timestamp:d.string().datetime(),acceptedAt:d.string().datetime().optional(),rejectedAt:d.string().datetime().optional()}),we=d.object({runId:d.string(),generatedAt:d.string().datetime(),totalHeals:d.number().int(),accepted:d.number().int(),rejected:d.number().int(),pending:d.number().int(),events:d.array(Q)});var M=S("healing-store"),T=w.join("results","healing"),rn="pending.json",ee="healing-report-";function Me(e,t){return w.join(e,`${ee}${t}.json`)}function B(e){return w.join(e,rn)}async function an(e,t){let n=we.safeParse(t);if(!n.success){let a=n.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
49
49
  `);throw new u(`Cannot write invalid healing report:
50
- ${a}`,"healingReport","INVALID_HEALING_REPORT")}let r=w.join(e,T),i=Me(r,t.runId);await x(i,n.data),M.info({runId:t.runId,totalHeals:t.totalHeals},"Healing report saved")}async function an(e,t){let n=w.join(e,T),r=Me(n,t);if(!await L.pathExists(r))throw new g(`Healing report not found for runId "${t}".`,r,"HEALING_REPORT_NOT_FOUND");let i=await y(r),a=we.safeParse(i);if(!a.success){let p=a.error.issues.map(b=>` \u2022 ${b.path.join(".")}: ${b.message}`).join(`
50
+ ${a}`,"healingReport","INVALID_HEALING_REPORT")}let r=w.join(e,T),i=Me(r,t.runId);await x(i,n.data),M.info({runId:t.runId,totalHeals:t.totalHeals},"Healing report saved")}async function on(e,t){let n=w.join(e,T),r=Me(n,t);if(!await L.pathExists(r))throw new g(`Healing report not found for runId "${t}".`,r,"HEALING_REPORT_NOT_FOUND");let i=await y(r),a=we.safeParse(i);if(!a.success){let p=a.error.issues.map(b=>` \u2022 ${b.path.join(".")}: ${b.message}`).join(`
51
51
  `);throw new u(`Invalid healing report at "${r}":
52
- ${p}`,"healingReport","INVALID_HEALING_REPORT")}return a.data}async function V(e){let t=w.join(e,T),n=B(t);if(!await L.pathExists(n))return[];let r=await y(n),i=Q.array().safeParse(r);return i.success?i.data:(M.warn({path:n},"Pending healing file is malformed \u2014 resetting"),[])}var ke=50,Ge=["pending","rejected","accepted"];function on(e){if(e.length<=ke)return e;let t=[...e].sort((i,a)=>{let p=Ge.indexOf(i.status),b=Ge.indexOf(a.status);return p!==b?p-b:i.timestamp<a.timestamp?-1:1}),n=e.length-ke,r=new Set(t.slice(0,n).map(i=>i.id));return M.info({excess:n,deleted:r.size},"Auto-pruning healing events to enforce cap"),e.filter(i=>!r.has(i.id))}async function sn(e,t){let n=Q.safeParse(t);if(!n.success)throw new u(`Invalid healing event: ${n.error.message}`,"healingEvent","INVALID_HEALING_EVENT");let r=w.join(e,T);await L.ensureDir(r);let i=await V(e);i.push(n.data),await x(B(r),on(i))}async function cn(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"accepted",acceptedAt:new Date().toISOString()};let i=w.join(e,T);return await x(B(i),n),M.info({eventId:t},"Healing event accepted"),n[r]}async function ln(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"rejected",rejectedAt:new Date().toISOString()};let i=w.join(e,T);return await x(B(i),n),M.info({eventId:t},"Healing event rejected"),n[r]}async function mn(e,t=30){let n=await V(e),r=Date.now()-t*24*60*60*1e3,i=n.length,a=n.filter(p=>{if(p.status==="pending")return!0;let b=p.acceptedAt??p.rejectedAt;return b?new Date(b).getTime()>r:!0});if(a.length<i){let p=w.join(e,T);await x(B(p),a)}return i-a.length}async function pn(e){let t=await V(e),n=t.filter(a=>a.status==="pending").length,r=t.filter(a=>a.status==="accepted").length,i=t.filter(a=>a.status==="rejected").length;return{pending:n,accepted:r,rejected:i,total:t.length}}async function un(e){let t=w.join(e,T);if(!await L.pathExists(t))return[];let r=(await L.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.startsWith(ee)&&a.name.endsWith(".json")).map(a=>a.name.slice(ee.length,-5));return(await Promise.all(r.map(async a=>{let p=await L.stat(w.join(t,`${ee}${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}import{createHash as dn}from"crypto";function Be(e){return dn("sha256").update(e,"utf8").digest("hex")}function We(e){try{return new URL(e).pathname.split("/").map(r=>/^\d+$/.test(r)||/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(r)||/^[a-z0-9-]{8,}$/i.test(r)&&/-/.test(r)?"*":r).join("/")}catch{return e}}function gn(e,t){let n=e.toLowerCase().replace(/\s+/g," ").trim(),r=We(t);return Be(`${n}|${r}`).slice(0,16)}import{config as fn}from"dotenv";import{z as m}from"zod";fn();var Je=["anthropic","openai","google","azure-openai","bedrock","deepseek","groq","together","qwen","perplexity","ollama","custom"],hn=m.object({NODE_ENV:m.enum(["development","production","test"]).default("development"),LOG_LEVEL:m.enum(["trace","debug","info","warn","error","fatal"]).default("info"),AI_PROVIDER:m.enum(Je),AI_TIERED_ENABLED:m.string().transform(e=>e==="true").default("false"),AI_TIERED_FAST_PROVIDER:m.enum(Je).optional(),AI_TIERED_FAST_MODEL:m.string().optional(),AI_TIMEOUT:m.coerce.number().int().positive().default(30),AI_MAX_RETRIES:m.coerce.number().int().min(0).max(10).default(2)}),xn=m.object({ANTHROPIC_API_KEY:m.string().min(1),ANTHROPIC_MODEL:m.string().default("claude-sonnet-4-20250514")}),bn=m.object({OPENAI_API_KEY:m.string().min(1),OPENAI_MODEL:m.string().default("gpt-4o")}),Sn=m.object({GOOGLE_API_KEY:m.string().min(1),GOOGLE_MODEL:m.string().default("gemini-2.5-pro")}),yn=m.object({AZURE_OPENAI_API_KEY:m.string().min(1),AZURE_OPENAI_ENDPOINT:m.string().url(),AZURE_OPENAI_DEPLOYMENT:m.string().min(1),AZURE_OPENAI_API_VERSION:m.string().default("2024-10-21")}),En=m.object({AWS_ACCESS_KEY_ID:m.string().min(1),AWS_SECRET_ACCESS_KEY:m.string().min(1),AWS_SESSION_TOKEN:m.string().optional(),AWS_REGION:m.string().default("us-east-1"),BEDROCK_MODEL:m.string().default("anthropic.claude-sonnet-4-20250514-v1:0")}),wn=m.object({DEEPSEEK_API_KEY:m.string().min(1),DEEPSEEK_MODEL:m.string().default("deepseek-chat")}),vn=m.object({GROQ_API_KEY:m.string().min(1),GROQ_MODEL:m.string().default("llama-3.3-70b-versatile")}),An=m.object({TOGETHER_API_KEY:m.string().min(1),TOGETHER_MODEL:m.string().default("meta-llama/Llama-3.3-70B-Instruct-Turbo")}),Rn=m.object({QWEN_API_KEY:m.string().min(1),QWEN_BASE_URL:m.string().url().default("https://dashscope-intl.aliyuncs.com/compatible-mode/v1"),QWEN_MODEL:m.string().default("qwen-max")}),In=m.object({PERPLEXITY_API_KEY:m.string().min(1),PERPLEXITY_MODEL:m.string().default("sonar-pro")}),jn=m.object({OLLAMA_BASE_URL:m.string().url().default("http://localhost:11434"),OLLAMA_MODEL:m.string().default("llama3.3")}),Cn=m.object({CUSTOM_API_KEY:m.string().min(1),CUSTOM_BASE_URL:m.string().url(),CUSTOM_MODEL:m.string().min(1)}),Tn={anthropic:xn,openai:bn,google:Sn,"azure-openai":yn,bedrock:En,deepseek:wn,groq:vn,together:An,qwen:Rn,perplexity:In,ollama:jn,custom:Cn},W=null;function Ke(){if(W!==null)return W;let e=hn.safeParse(process.env);if(!e.success){let a=e.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
52
+ ${p}`,"healingReport","INVALID_HEALING_REPORT")}return a.data}async function V(e){let t=w.join(e,T),n=B(t);if(!await L.pathExists(n))return[];let r=await y(n),i=Q.array().safeParse(r);return i.success?i.data:(M.warn({path:n},"Pending healing file is malformed \u2014 resetting"),[])}var ke=50,Ge=["pending","rejected","accepted"];function sn(e){if(e.length<=ke)return e;let t=[...e].sort((i,a)=>{let p=Ge.indexOf(i.status),b=Ge.indexOf(a.status);return p!==b?p-b:i.timestamp<a.timestamp?-1:1}),n=e.length-ke,r=new Set(t.slice(0,n).map(i=>i.id));return M.info({excess:n,deleted:r.size},"Auto-pruning healing events to enforce cap"),e.filter(i=>!r.has(i.id))}async function cn(e,t){let n=Q.safeParse(t);if(!n.success)throw new u(`Invalid healing event: ${n.error.message}`,"healingEvent","INVALID_HEALING_EVENT");let r=w.join(e,T);await L.ensureDir(r);let i=await V(e);i.push(n.data),await x(B(r),sn(i))}async function ln(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"accepted",acceptedAt:new Date().toISOString()};let i=w.join(e,T);return await x(B(i),n),M.info({eventId:t},"Healing event accepted"),n[r]}async function mn(e,t){let n=await V(e),r=n.findIndex(a=>a.id===t);if(r===-1)throw new g(`Healing event "${t}" not found in pending list.`,t,"HEALING_EVENT_NOT_FOUND");n[r]={...n[r],status:"rejected",rejectedAt:new Date().toISOString()};let i=w.join(e,T);return await x(B(i),n),M.info({eventId:t},"Healing event rejected"),n[r]}async function pn(e,t=30){let n=await V(e),r=Date.now()-t*24*60*60*1e3,i=n.length,a=n.filter(p=>{if(p.status==="pending")return!0;let b=p.acceptedAt??p.rejectedAt;return b?new Date(b).getTime()>r:!0});if(a.length<i){let p=w.join(e,T);await x(B(p),a)}return i-a.length}async function un(e){let t=await V(e),n=t.filter(a=>a.status==="pending").length,r=t.filter(a=>a.status==="accepted").length,i=t.filter(a=>a.status==="rejected").length;return{pending:n,accepted:r,rejected:i,total:t.length}}async function dn(e){let t=w.join(e,T);if(!await L.pathExists(t))return[];let r=(await L.readdir(t,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.startsWith(ee)&&a.name.endsWith(".json")).map(a=>a.name.slice(ee.length,-5));return(await Promise.all(r.map(async a=>{let p=await L.stat(w.join(t,`${ee}${a}.json`));return{id:a,mtime:p.mtimeMs}}))).sort((a,p)=>p.mtime-a.mtime).map(a=>a.id)}import{createHash as gn}from"crypto";function Be(e){return gn("sha256").update(e,"utf8").digest("hex")}function We(e){try{return new URL(e).pathname.split("/").map(r=>/^\d+$/.test(r)||/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(r)||/^[a-z0-9-]{8,}$/i.test(r)&&/-/.test(r)?"*":r).join("/")}catch{return e}}function fn(e,t){let n=e.toLowerCase().replace(/\s+/g," ").trim(),r=We(t);return Be(`${n}|${r}`).slice(0,16)}import{config as hn}from"dotenv";import{z as m}from"zod";hn();var Je=["anthropic","openai","google","azure-openai","bedrock","deepseek","groq","together","qwen","perplexity","ollama","custom"],xn=m.object({NODE_ENV:m.enum(["development","production","test"]).default("development"),LOG_LEVEL:m.enum(["trace","debug","info","warn","error","fatal"]).default("info"),AI_PROVIDER:m.enum(Je),AI_TIERED_ENABLED:m.string().transform(e=>e==="true").default("false"),AI_TIERED_FAST_PROVIDER:m.enum(Je).optional(),AI_TIERED_FAST_MODEL:m.string().optional(),AI_TIMEOUT:m.coerce.number().int().positive().default(30),AI_MAX_RETRIES:m.coerce.number().int().min(0).max(10).default(2)}),bn=m.object({ANTHROPIC_API_KEY:m.string().min(1),ANTHROPIC_MODEL:m.string().default("claude-sonnet-4-20250514")}),Sn=m.object({OPENAI_API_KEY:m.string().min(1),OPENAI_MODEL:m.string().default("gpt-4o")}),yn=m.object({GOOGLE_API_KEY:m.string().min(1),GOOGLE_MODEL:m.string().default("gemini-2.5-pro")}),En=m.object({AZURE_OPENAI_API_KEY:m.string().min(1),AZURE_OPENAI_ENDPOINT:m.string().url(),AZURE_OPENAI_DEPLOYMENT:m.string().min(1),AZURE_OPENAI_API_VERSION:m.string().default("2024-10-21")}),wn=m.object({AWS_ACCESS_KEY_ID:m.string().min(1),AWS_SECRET_ACCESS_KEY:m.string().min(1),AWS_SESSION_TOKEN:m.string().optional(),AWS_REGION:m.string().default("us-east-1"),BEDROCK_MODEL:m.string().default("anthropic.claude-sonnet-4-20250514-v1:0")}),vn=m.object({DEEPSEEK_API_KEY:m.string().min(1),DEEPSEEK_MODEL:m.string().default("deepseek-chat")}),An=m.object({GROQ_API_KEY:m.string().min(1),GROQ_MODEL:m.string().default("llama-3.3-70b-versatile")}),Rn=m.object({TOGETHER_API_KEY:m.string().min(1),TOGETHER_MODEL:m.string().default("meta-llama/Llama-3.3-70B-Instruct-Turbo")}),In=m.object({QWEN_API_KEY:m.string().min(1),QWEN_BASE_URL:m.string().url().default("https://dashscope-intl.aliyuncs.com/compatible-mode/v1"),QWEN_MODEL:m.string().default("qwen-max")}),jn=m.object({PERPLEXITY_API_KEY:m.string().min(1),PERPLEXITY_MODEL:m.string().default("sonar-pro")}),Cn=m.object({OLLAMA_BASE_URL:m.string().url().default("http://localhost:11434"),OLLAMA_MODEL:m.string().default("llama3.3")}),Tn=m.object({CUSTOM_API_KEY:m.string().min(1),CUSTOM_BASE_URL:m.string().url(),CUSTOM_MODEL:m.string().min(1)}),Pn={anthropic:bn,openai:Sn,google:yn,"azure-openai":En,bedrock:wn,deepseek:vn,groq:An,together:Rn,qwen:In,perplexity:jn,ollama:Cn,custom:Tn},W=null;function Ke(){if(W!==null)return W;let e=xn.safeParse(process.env);if(!e.success){let a=e.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
53
53
  `);throw new C(`Missing or invalid environment variables:
54
54
  ${a}
55
55
 
56
- How to fix: Copy .env.example to .env and fill in your AI provider credentials.`,"ENV_VALIDATION_FAILED")}let t=e.data.AI_PROVIDER,r=Tn[t].safeParse(process.env);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
56
+ How to fix: Copy .env.example to .env and fill in your AI provider credentials.`,"ENV_VALIDATION_FAILED")}let t=e.data.AI_PROVIDER,r=Pn[t].safeParse(process.env);if(!r.success){let a=r.error.issues.map(p=>` \u2022 ${p.path.join(".")}: ${p.message}`).join(`
57
57
  `);throw new C(`Provider "${t}" is missing required environment variables:
58
58
  ${a}
59
59
 
60
- How to fix: Check .env.example for the ${t} section and add the required keys to .env.`,"PROVIDER_ENV_MISSING")}return W={...e.data,...r.data},W}function Pn(){W=null}function _n(){try{return Ke()}catch{return null}}import{faker as o}from"@faker-js/faker";function Nn(){return[{category:"person",methods:[{name:"fullName",example:o.person.fullName()},{name:"firstName",example:o.person.firstName()},{name:"lastName",example:o.person.lastName()},{name:"middleName",example:o.person.middleName()},{name:"sex",example:o.person.sex()},{name:"prefix",example:o.person.prefix()},{name:"suffix",example:o.person.suffix()},{name:"jobTitle",example:o.person.jobTitle()},{name:"jobArea",example:o.person.jobArea()},{name:"jobType",example:o.person.jobType()},{name:"bio",example:o.person.bio()}]},{category:"internet",methods:[{name:"email",example:o.internet.email()},{name:"userName",example:o.internet.username()},{name:"password",example:o.internet.password()},{name:"url",example:o.internet.url()},{name:"domainName",example:o.internet.domainName()},{name:"domainWord",example:o.internet.domainWord()},{name:"ip",example:o.internet.ip()},{name:"ipv6",example:o.internet.ipv6()},{name:"mac",example:o.internet.mac()},{name:"userAgent",example:o.internet.userAgent()},{name:"emoji",example:o.internet.emoji()}]},{category:"phone",methods:[{name:"number",example:o.phone.number()},{name:"imei",example:o.phone.imei()}]},{category:"location",methods:[{name:"streetAddress",example:o.location.streetAddress()},{name:"street",example:o.location.street()},{name:"city",example:o.location.city()},{name:"state",example:o.location.state()},{name:"zipCode",example:o.location.zipCode()},{name:"country",example:o.location.country()},{name:"countryCode",example:o.location.countryCode()},{name:"county",example:o.location.county()},{name:"latitude",example:String(o.location.latitude())},{name:"longitude",example:String(o.location.longitude())},{name:"timeZone",example:o.location.timeZone()},{name:"buildingNumber",example:o.location.buildingNumber()}]},{category:"company",methods:[{name:"name",example:o.company.name()},{name:"catchPhrase",example:o.company.catchPhrase()},{name:"buzzPhrase",example:o.company.buzzPhrase()},{name:"buzzNoun",example:o.company.buzzNoun()},{name:"buzzVerb",example:o.company.buzzVerb()},{name:"buzzAdjective",example:o.company.buzzAdjective()}]},{category:"finance",methods:[{name:"accountNumber",example:o.finance.accountNumber()},{name:"accountName",example:o.finance.accountName()},{name:"creditCardNumber",example:o.finance.creditCardNumber()},{name:"creditCardCVV",example:o.finance.creditCardCVV()},{name:"currencyCode",example:o.finance.currencyCode()},{name:"currencyName",example:o.finance.currencyName()},{name:"amount",example:o.finance.amount()},{name:"iban",example:o.finance.iban()},{name:"bic",example:o.finance.bic()},{name:"bitcoinAddress",example:o.finance.bitcoinAddress()},{name:"transactionType",example:o.finance.transactionType()}]},{category:"date",methods:[{name:"past",example:o.date.past().toISOString()},{name:"future",example:o.date.future().toISOString()},{name:"recent",example:o.date.recent().toISOString()},{name:"soon",example:o.date.soon().toISOString()},{name:"birthdate",example:o.date.birthdate().toISOString()},{name:"month",example:o.date.month()},{name:"weekday",example:o.date.weekday()}]},{category:"lorem",methods:[{name:"word",example:o.lorem.word()},{name:"words",example:o.lorem.words()},{name:"sentence",example:o.lorem.sentence()},{name:"sentences",example:o.lorem.sentences()},{name:"paragraph",example:o.lorem.paragraph()},{name:"slug",example:o.lorem.slug()},{name:"lines",example:o.lorem.lines(1)}]},{category:"string",methods:[{name:"uuid",example:o.string.uuid()},{name:"alphanumeric",example:o.string.alphanumeric(10)},{name:"numeric",example:o.string.numeric(6)},{name:"alpha",example:o.string.alpha(8)},{name:"nanoid",example:o.string.nanoid()},{name:"hexadecimal",example:o.string.hexadecimal({length:8})},{name:"sample",example:o.string.sample(12)}]},{category:"number",methods:[{name:"int",example:String(o.number.int({max:9999}))},{name:"float",example:String(o.number.float({max:100,fractionDigits:2}))}]},{category:"datatype",methods:[{name:"boolean",example:String(o.datatype.boolean())}]},{category:"image",methods:[{name:"avatar",example:o.image.avatar()},{name:"url",example:o.image.url()}]},{category:"color",methods:[{name:"human",example:o.color.human()},{name:"rgb",example:o.color.rgb()},{name:"hex",example:o.color.rgb({format:"hex"})}]},{category:"commerce",methods:[{name:"productName",example:o.commerce.productName()},{name:"price",example:o.commerce.price()},{name:"department",example:o.commerce.department()},{name:"productDescription",example:o.commerce.productDescription().slice(0,60)+"..."},{name:"isbn",example:o.commerce.isbn()}]},{category:"vehicle",methods:[{name:"vehicle",example:o.vehicle.vehicle()},{name:"manufacturer",example:o.vehicle.manufacturer()},{name:"model",example:o.vehicle.model()},{name:"vin",example:o.vehicle.vin()},{name:"vrm",example:o.vehicle.vrm()}]},{category:"system",methods:[{name:"fileName",example:o.system.fileName()},{name:"mimeType",example:o.system.mimeType()},{name:"fileExt",example:o.system.fileExt()},{name:"filePath",example:o.system.filePath()},{name:"semver",example:o.system.semver()}]}]}var On={"internet.userName":"username"};function $n(e,t){let n=o[e],r=On[`${e}.${t}`]??t;if(!n||typeof n[r]!="function")return`[unknown: ${e}.${t}]`;let i=n[r]();return i instanceof Date?i.toISOString():String(i)}function Fi(e){let t={...J,...e},n=D.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
60
+ How to fix: Check .env.example for the ${t} section and add the required keys to .env.`,"PROVIDER_ENV_MISSING")}return W={...e.data,...r.data},W}function _n(){W=null}function Nn(){try{return Ke()}catch{return null}}import{faker as o}from"@faker-js/faker";function On(){return[{category:"person",methods:[{name:"fullName",example:o.person.fullName()},{name:"firstName",example:o.person.firstName()},{name:"lastName",example:o.person.lastName()},{name:"middleName",example:o.person.middleName()},{name:"sex",example:o.person.sex()},{name:"prefix",example:o.person.prefix()},{name:"suffix",example:o.person.suffix()},{name:"jobTitle",example:o.person.jobTitle()},{name:"jobArea",example:o.person.jobArea()},{name:"jobType",example:o.person.jobType()},{name:"bio",example:o.person.bio()}]},{category:"internet",methods:[{name:"email",example:o.internet.email()},{name:"userName",example:o.internet.username()},{name:"password",example:o.internet.password()},{name:"url",example:o.internet.url()},{name:"domainName",example:o.internet.domainName()},{name:"domainWord",example:o.internet.domainWord()},{name:"ip",example:o.internet.ip()},{name:"ipv6",example:o.internet.ipv6()},{name:"mac",example:o.internet.mac()},{name:"userAgent",example:o.internet.userAgent()},{name:"emoji",example:o.internet.emoji()}]},{category:"phone",methods:[{name:"number",example:o.phone.number()},{name:"imei",example:o.phone.imei()}]},{category:"location",methods:[{name:"streetAddress",example:o.location.streetAddress()},{name:"street",example:o.location.street()},{name:"city",example:o.location.city()},{name:"state",example:o.location.state()},{name:"zipCode",example:o.location.zipCode()},{name:"country",example:o.location.country()},{name:"countryCode",example:o.location.countryCode()},{name:"county",example:o.location.county()},{name:"latitude",example:String(o.location.latitude())},{name:"longitude",example:String(o.location.longitude())},{name:"timeZone",example:o.location.timeZone()},{name:"buildingNumber",example:o.location.buildingNumber()}]},{category:"company",methods:[{name:"name",example:o.company.name()},{name:"catchPhrase",example:o.company.catchPhrase()},{name:"buzzPhrase",example:o.company.buzzPhrase()},{name:"buzzNoun",example:o.company.buzzNoun()},{name:"buzzVerb",example:o.company.buzzVerb()},{name:"buzzAdjective",example:o.company.buzzAdjective()}]},{category:"finance",methods:[{name:"accountNumber",example:o.finance.accountNumber()},{name:"accountName",example:o.finance.accountName()},{name:"creditCardNumber",example:o.finance.creditCardNumber()},{name:"creditCardCVV",example:o.finance.creditCardCVV()},{name:"currencyCode",example:o.finance.currencyCode()},{name:"currencyName",example:o.finance.currencyName()},{name:"amount",example:o.finance.amount()},{name:"iban",example:o.finance.iban()},{name:"bic",example:o.finance.bic()},{name:"bitcoinAddress",example:o.finance.bitcoinAddress()},{name:"transactionType",example:o.finance.transactionType()}]},{category:"date",methods:[{name:"past",example:o.date.past().toISOString()},{name:"future",example:o.date.future().toISOString()},{name:"recent",example:o.date.recent().toISOString()},{name:"soon",example:o.date.soon().toISOString()},{name:"birthdate",example:o.date.birthdate().toISOString()},{name:"month",example:o.date.month()},{name:"weekday",example:o.date.weekday()}]},{category:"lorem",methods:[{name:"word",example:o.lorem.word()},{name:"words",example:o.lorem.words()},{name:"sentence",example:o.lorem.sentence()},{name:"sentences",example:o.lorem.sentences()},{name:"paragraph",example:o.lorem.paragraph()},{name:"slug",example:o.lorem.slug()},{name:"lines",example:o.lorem.lines(1)}]},{category:"string",methods:[{name:"uuid",example:o.string.uuid()},{name:"alphanumeric",example:o.string.alphanumeric(10)},{name:"numeric",example:o.string.numeric(6)},{name:"alpha",example:o.string.alpha(8)},{name:"nanoid",example:o.string.nanoid()},{name:"hexadecimal",example:o.string.hexadecimal({length:8})},{name:"sample",example:o.string.sample(12)}]},{category:"number",methods:[{name:"int",example:String(o.number.int({max:9999}))},{name:"float",example:String(o.number.float({max:100,fractionDigits:2}))}]},{category:"datatype",methods:[{name:"boolean",example:String(o.datatype.boolean())}]},{category:"image",methods:[{name:"avatar",example:o.image.avatar()},{name:"url",example:o.image.url()}]},{category:"color",methods:[{name:"human",example:o.color.human()},{name:"rgb",example:o.color.rgb()},{name:"hex",example:o.color.rgb({format:"hex"})}]},{category:"commerce",methods:[{name:"productName",example:o.commerce.productName()},{name:"price",example:o.commerce.price()},{name:"department",example:o.commerce.department()},{name:"productDescription",example:o.commerce.productDescription().slice(0,60)+"..."},{name:"isbn",example:o.commerce.isbn()}]},{category:"vehicle",methods:[{name:"vehicle",example:o.vehicle.vehicle()},{name:"manufacturer",example:o.vehicle.manufacturer()},{name:"model",example:o.vehicle.model()},{name:"vin",example:o.vehicle.vin()},{name:"vrm",example:o.vehicle.vrm()}]},{category:"system",methods:[{name:"fileName",example:o.system.fileName()},{name:"mimeType",example:o.system.mimeType()},{name:"fileExt",example:o.system.fileExt()},{name:"filePath",example:o.system.filePath()},{name:"semver",example:o.system.semver()}]}]}var $n={"internet.userName":"username"};function Ln(e,t){let n=o[e],r=$n[`${e}.${t}`]??t;if(!n||typeof n[r]!="function")return`[unknown: ${e}.${t}]`;let i=n[r]();return i instanceof Date?i.toISOString():String(i)}function zi(e){let t={...J,...e},n=D.safeParse(t);if(!n.success){let r=n.error.issues.map(i=>` \u2022 ${i.path.join(".")}: ${i.message}`).join(`
61
61
  `);throw new Error(`Invalid assuremind config:
62
- ${r}`)}return n.data}export{v as AssuremindError,C as ConfigError,ie as ExecutionError,ae as HealingError,re as ProviderError,g as StorageError,u as ValidationError,cn as acceptHealingEvent,sn as appendPendingEvent,$n as callFaker,Pn as clearEnvCache,Ft as configExists,At as createCase,S as createChildLogger,bt as createSuite,Fi as defineConfig,It as deleteCase,Vt as deleteGlobalVariable,qt as deleteResult,yt as deleteSuite,st as formatError,gn as generateCacheKey,Nn as getAvailableGenerators,Ct as getCasePath,pn as getHealingStats,ot as isAssuremindError,Ne as listCasePaths,jt as listCases,un as listHealingReportIds,Ue as listResultIds,Yt as listResults,se as listSuiteDirs,Et as listSuites,wt as listSuitesWithCounts,Dt as listVariableFiles,Ce as logger,We as normalizeUrl,mn as pruneResolvedEvents,me as readCase,be as readConfig,Le as readEnvVariables,$e as readGlobalVariables,an as readHealingReport,V as readPendingEvents,ze as readResult,Y as readSuite,k as readVariables,ht as redactSecrets,ln as rejectHealingEvent,$t as resolveVariables,ft as sanitizeGeneratedCode,Xt as screenshotsDir,Lt as setGlobalVariable,Be as sha256,O as toSlug,Qt as tracesDir,_n as tryGetEnv,Rt as updateCase,Ht as updateConfig,St as updateSuite,zt as validateConfig,Ke as validateEnv,Zt as videosDir,pe as writeCase,De as writeConfig,rn as writeHealingReport,Kt as writeResult,oe as writeSuite,ge as writeVariables};
62
+ ${r}`)}return n.data}export{v as AssuremindError,C as ConfigError,ie as ExecutionError,ae as HealingError,re as ProviderError,g as StorageError,u as ValidationError,ln as acceptHealingEvent,cn as appendPendingEvent,Ln as callFaker,_n as clearEnvCache,zt as configExists,Rt as createCase,S as createChildLogger,St as createSuite,zi as defineConfig,jt as deleteCase,Dt as deleteGlobalVariable,Xt as deleteResult,Et as deleteSuite,st as formatError,fn as generateCacheKey,On as getAvailableGenerators,Tt as getCasePath,un as getHealingStats,ot as isAssuremindError,Ne as listCasePaths,Ct as listCases,dn as listHealingReportIds,Ue as listResultIds,qt as listResults,se as listSuiteDirs,wt as listSuites,vt as listSuitesWithCounts,Ht as listVariableFiles,Ce as logger,We as normalizeUrl,pn as pruneResolvedEvents,me as readCase,be as readConfig,Le as readEnvVariables,$e as readGlobalVariables,on as readHealingReport,V as readPendingEvents,ze as readResult,Y as readSuite,k as readVariables,xt as redactSecrets,mn as rejectHealingEvent,Lt as resolveVariables,ht as sanitizeGeneratedCode,Zt as screenshotsDir,Vt as setGlobalVariable,Be as sha256,O as toSlug,en as tracesDir,Nn as tryGetEnv,It as updateCase,Ft as updateConfig,yt as updateSuite,Ut as validateConfig,Ke as validateEnv,Qt as videosDir,pe as writeCase,De as writeConfig,an as writeHealingReport,Yt as writeResult,oe as writeSuite,ge as writeVariables};
package/docs/STUDIO.md CHANGED
@@ -48,7 +48,7 @@ The sidebar on the left has these pages:
48
48
  | 📋 | Reports | Browse historical run results |
49
49
  | 🔧 | Variables | Manage test variables and secrets |
50
50
  | 🩺 | Self-Healing | Review and apply AI-generated selector fixes |
51
- | ⚙️ | Settings | Environment, browsers, healing, capture settings, MCP integration |
51
+ | ⚙️ | Settings | Environment, browsers, healing, capture settings, MCP integration, CI/CD integration |
52
52
  | 🌿 | Git Control Center | Branch management, AI commits, push/pull, conflict resolution |
53
53
 
54
54
  ---
@@ -74,6 +74,8 @@ While data is loading, animated skeleton cards are shown so the layout never jum
74
74
 
75
75
  ## Test Editor
76
76
 
77
+ > **Writing steps:** for a full catalog of plain-English step phrasings (UI **and** API) with examples, see [WRITING-STEPS.md](WRITING-STEPS.md).
78
+
77
79
  ### Suite Types
78
80
 
79
81
  Assuremind supports three suite types:
@@ -466,6 +468,19 @@ The **Device Emulation** card appears below the Browsers card. It lets you emula
466
468
 
467
469
  **Equivalent CLI flag:** `--device "iPhone 15 Pro"`
468
470
 
471
+ ### CI Config Generator
472
+
473
+ Click **Generate CI Config** to produce a pipeline file from your current run settings (browsers, parallel workers, device). Pick a platform tab — **GitHub Actions**, **GitLab CI**, or **Jenkins**.
474
+
475
+ - The YAML opens in a **preview** (read-only). Click **✎ Edit** to customise it — change branches, Node version, add steps, env vars, etc. Your edits apply to **Copy**, **Download**, and **Write to repo**. **↺ Reset** discards edits and regenerates from the run config.
476
+ - **Copy** and **Download** are always available.
477
+ - **Write to repo** writes the file into your project at its standard path:
478
+ - GitHub Actions → `.github/workflows/assuremind.yml`
479
+ - GitLab CI → `.gitlab-ci.yml`
480
+ - Jenkins → `Jenkinsfile`
481
+
482
+ This button only appears when **Settings → CI/CD Integration → "Write CI files to repo"** is enabled (off by default). It **only writes the file** — it does **not** commit or push. Review the change and commit it from the **Git Control Center** when you're ready.
483
+
469
484
  ---
470
485
 
471
486
  ## Reports
@@ -476,6 +491,7 @@ Browse all historical run results:
476
491
  - Click a run to see the **full result tree**: Suite → Case → Step
477
492
  - Each step shows: status, duration, generated code, and screenshot (on failure)
478
493
  - Failed steps show the error message and a screenshot thumbnail
494
+ - A **Downloads** panel appears for runs that captured files (see [File Upload & Download](#file-upload--download)) — click a file to save it locally
479
495
 
480
496
  ### Lighthouse Results (Audit Suites)
481
497
 
@@ -700,6 +716,85 @@ The Faker Data modal (accessible from the Test Editor) lets you browse 100+ dyna
700
716
 
701
717
  ---
702
718
 
719
+ ## File Upload & Download
720
+
721
+ ### Uploading files (for upload steps)
722
+
723
+ In the **Test Editor**, click the **Files** button (next to *Fake Data*) to open the file picker:
724
+
725
+ 1. Click **Choose a file to upload** and pick a file from your machine.
726
+ 2. It's saved into your project at **`fixtures/uploads/`** (committed with the repo, so it works in CI) and appears in the list.
727
+ 3. Click the file to insert a **`{{FILE:name}}`** token into your step. Delete files with the trash icon.
728
+
729
+ The token resolves to the file's relative path at runtime, so it works with `setInputFiles`:
730
+
731
+ | Step (plain English) | Generated code |
732
+ |----------------------|----------------|
733
+ | `Upload {{FILE:resume.pdf}} to the Resume field` | `await page.getByLabel('Resume').setInputFiles('{{FILE:resume.pdf}}');` |
734
+ | `Upload {{FILE:resume.pdf}}` | `await page.locator('input[type="file"]').setInputFiles('{{FILE:resume.pdf}}');` |
735
+
736
+ For a button that opens a native file chooser, keep the listener and the click in **one step**:
737
+
738
+ ```js
739
+ const fileChooserPromise = page.waitForEvent('filechooser');
740
+ await page.getByRole('button', { name: 'Choose File' }).click();
741
+ (await fileChooserPromise).setFiles('{{FILE:resume.pdf}}');
742
+ ```
743
+
744
+ Files inside an iframe still need `frameLocator(...)`, e.g.
745
+ `await page.frameLocator('#frame').locator('input[type="file"]').setInputFiles('{{FILE:doc.pdf}}');`
746
+
747
+ **Waiting for the upload to complete** — `setInputFiles` only attaches the file; there is no universal "upload done" event. The step waits for a completion signal:
748
+
749
+ | Step (plain English) | Wait behavior |
750
+ |----------------------|---------------|
751
+ | `Upload {{FILE:resume.pdf}} to the Resume field` | waits for the network to settle (`waitForLoadState('networkidle')`) |
752
+ | `Upload {{FILE:resume.pdf}} to the Resume field and wait for 'Upload complete'` | waits for the success text — **most reliable** |
753
+ | `Upload {{FILE:photo.png}}` | uploads to the page's file input, then waits for network |
754
+
755
+ **Example upload steps:**
756
+
757
+ ```text
758
+ Upload {{FILE:resume.pdf}} to the Resume field
759
+ Upload {{FILE:resume.pdf}} to the Resume field and wait for 'Upload complete'
760
+ Upload {{FILE:photo.png}}
761
+ Upload {{FILE:invoice.pdf}} into the Attachment input
762
+ ```
763
+
764
+ ### Downloads (auto-captured)
765
+
766
+ You do **not** need any special code to handle downloads — just trigger them with a click. The step
767
+ **waits until the file finishes downloading** (`waitForEvent('download')` → `await download.path()`),
768
+ and the runner **automatically saves every download** to `results/downloads/<runId>/`. Avoid
769
+ `download.saveAs()` / `fs` in steps — the sandbox blocks them and it isn't needed.
770
+
771
+ **Example download steps:**
772
+
773
+ ```text
774
+ Download the "Export CSV" link
775
+ Click the "Export" button to download
776
+ Download the file by clicking "Report PDF"
777
+ Click "Download invoice" and wait for download
778
+ ```
779
+
780
+ Each generates:
781
+ ```js
782
+ const downloadPromise = page.waitForEvent('download');
783
+ await page.getByRole('link', { name: 'Export CSV' }).click();
784
+ const download = await downloadPromise;
785
+ await download.path(); // resolves once the download is complete
786
+ ```
787
+
788
+ To view and retrieve captured files:
789
+
790
+ 1. Go to **Reports → Run Reports** and click the run to expand it.
791
+ 2. A **Downloads** panel at the top lists every file captured during that run (name + size).
792
+ 3. Click any file to download it to your local machine.
793
+
794
+ The panel only appears when the run produced downloads.
795
+
796
+ ---
797
+
703
798
  ## Visual Regression
704
799
 
705
800
  Visual regression is configured in **Settings** → Visual Regression and reviewed in **Reports** → Visual Diffs / Baselines tabs.