shiplightai 0.1.63 → 0.1.64
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/cjs/debugger-manager.cjs +12 -12
- package/dist/cjs/debugger-pw.cjs +56 -56
- package/dist/cjs/debugger-server.cjs +2 -2
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/reporter.cjs +1 -1
- package/dist/cli.js +69 -69
- package/dist/debugger-manager.d.ts +18 -8
- package/dist/debugger-manager.js +12 -12
- package/dist/debugger-pw.d.ts +20 -2
- package/dist/debugger-pw.js +55 -55
- package/dist/debugger-server.d.ts +13 -0
- package/dist/debugger-server.js +2 -2
- package/dist/index.js +1 -1
- package/dist/reporter.js +1 -1
- package/dist/static/assets/{index-CKKo90Kh.js → index-BQYw0-8-.js} +88 -135
- package/dist/static/index.html +1 -1
- package/dist/static-embedded/assets/{index-CVIbsKcG.js → index-BRGTQwWu.js} +1502 -3481
- package/dist/static-embedded/assets/{index-BzKjxa3K.js → index-BuU8-IZQ.js} +2 -2
- package/dist/static-embedded/index.html +1 -1
- package/package.json +4 -4
|
@@ -50,6 +50,19 @@ interface ServerRoutesOptions {
|
|
|
50
50
|
* `ws://host/ws/debugger/:sessionId/cdp-browser`.
|
|
51
51
|
*/
|
|
52
52
|
liveviewUrlBuilder?: (sessionId: string, req: Request) => string;
|
|
53
|
+
/**
|
|
54
|
+
* Fallback router for idle sessions (no sandbox process). When the SPA
|
|
55
|
+
* makes file-related API calls (`/api/test-flow`, `/api/files`, etc.)
|
|
56
|
+
* through the session proxy but the sandbox isn't running, the proxy
|
|
57
|
+
* delegates to this router instead of forwarding to the inner process.
|
|
58
|
+
*/
|
|
59
|
+
fallbackRouter?: Router;
|
|
60
|
+
/**
|
|
61
|
+
* Absolute path to the artifacts directory (screenshots, videos).
|
|
62
|
+
* When set, `/api/report-assets/*` requests inside a session are served
|
|
63
|
+
* from this directory instead of being proxied to the inner process.
|
|
64
|
+
*/
|
|
65
|
+
artifactsDir?: string;
|
|
53
66
|
}
|
|
54
67
|
declare function createDebuggerHttpRoutes(opts: ServerRoutesOptions): Router;
|
|
55
68
|
/**
|
package/dist/debugger-server.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{Router as
|
|
1
|
+
import{Router as R}from"express";import y from"express";import*as c from"fs";import*as l from"path";function P(i){return i?l.isAbsolute(i)?l.normalize(i):l.resolve(i):null}function S(i,t){let u=t.headers.host??"127.0.0.1";return`${(t.headers["x-forwarded-proto"]??"ws")==="https"?"wss":"ws"}://${u}/ws/debugger/${i}/cdp-browser`}function j(i){let t=l.basename(i);return t.endsWith(".test.yaml")||t.endsWith(".test.yml")}function I(i){let{manager:t,staticDir:u,resolveYamlPath:f=P,liveviewUrlBuilder:b=S,fallbackRouter:g,artifactsDir:m}=i,n=R(),w=m?y.static(m):null;n.post("/api/debugger/sessions",y.json(),async(e,s)=>{let r=e.body?.yamlPath;if(typeof r!="string"||!r){s.status(400).json({error:"yamlPath is required"});return}let o=f(r);if(!o){s.status(403).json({error:"Path outside allowed roots"});return}if(!j(o)){s.status(400).json({error:"Not a Shiplight test file (expected *.test.yaml or *.test.yml)"});return}if(!c.existsSync(o)){s.status(404).json({error:"File not found"});return}let d=t.listSessions().find(a=>a.yamlPath===o&&a.status!=="ended");try{let a=t.openSession(o);s.status(d?200:201).json({sessionId:a.sessionId,yamlPath:a.yamlPath,startedAt:a.startedAt,status:a.status})}catch(a){s.status(500).json({error:a.message})}}),n.get("/api/debugger/sessions",(e,s)=>{s.setHeader("Cache-Control","no-store"),s.json({sessions:t.listSessions().map(r=>({sessionId:r.sessionId,yamlPath:r.yamlPath,startedAt:r.startedAt,status:r.status}))})}),n.delete("/api/debugger/sessions/:sessionId",async(e,s)=>{let r=e.params.sessionId,o=!!t.getSession(r);await t.closeSession(r),s.json({deleted:!0,alreadyGone:!o})}),c.existsSync(u)?n.use("/debugger/static",y.static(u)):console.error(`[debugger] WARNING: debugger static dir missing at ${u} \u2014 iframe routes will 404`),n.get("/debugger/:sessionId/",(e,s)=>{let r=e.params.sessionId;if(!t.getSession(r)){s.status(404).send("Debugger session not found");return}let d=l.join(u,"index.html");if(!c.existsSync(d)){s.status(500).send(`Debugger SPA bundle missing at ${d}`);return}let h=c.readFileSync(d,"utf-8").replace(/(src|href)="\/assets\//g,'$1="/debugger/static/assets/').replace(/(src|href)="\/index\.html/g,'$1="/debugger/static/index.html');s.type("html").send(h)});let x=async(e,s,r)=>{let o=e.params.sessionId;if(o==="static")return r();let d=t.getSession(o);if(!d){s.status(404).json({status:"error",message:"Session not found"});return}let a=e.originalUrl,h=`/debugger/${o}`;if(a.startsWith(h)){let p=a.slice(h.length)||"/";e.url=p,Object.defineProperty(e,"originalUrl",{value:p,configurable:!0})}if(w&&e.path.startsWith("/api/report-assets/")){e.url=e.url.replace(/^\/api\/report-assets/,""),w(e,s,r);return}if(d.status==="idle"||d.status==="starting"){let p=`${e.method} ${e.path}`;if(p==="POST /api/int-runner/create-session")try{await t.startSandbox(o)}catch(v){s.status(500).json({status:"error",message:v.message});return}else if(p==="POST /api/int-runner/liveview-url"){s.json({liveviewUrl:"",browserWsUrl:""});return}else if(e.path.startsWith("/api/int-runner/")){s.status(503).json({status:"error",message:"Sandbox not started"});return}else if(g){g(e,s,r);return}else{s.status(503).json({status:"error",message:"Sandbox not started"});return}}t.httpProxyFor(o,{liveviewUrlBuilder:()=>b(o,e)})(e,s,r)};return n.use("/debugger/:sessionId",x),n}function D(i,t,u,f){let g=(t.url??"").match(/^\/ws\/debugger\/([^/]+)(.*)$/);if(!g){u.write(`HTTP/1.1 404 Not Found\r
|
|
2
2
|
\r
|
|
3
|
-
`),u.destroy();return}let m=
|
|
3
|
+
`),u.destroy();return}let m=g[1],n=g[2]||"";n.startsWith("/cdp-browser/page/")?n=n.slice(12):(n==="/cdp-browser"||n==="/cdp-browser/")&&(n=""),i.wsUpgradeFor(m)(t,u,f,n||void 0)}export{I as createDebuggerHttpRoutes,D as handleDebuggerUpgrade};
|
package/dist/index.js
CHANGED
|
@@ -4364,7 +4364,7 @@ ${p.join(`
|
|
|
4364
4364
|
`)}function xc(e,t){let i=[],a=t?.version||"unknown";i.push(`// @generated by shiplightai v${a}`),i.push(...bo()),i.push(""),t?.use&&Object.keys(t.use).length>0&&(i.push(`test.use(${JSON.stringify(t.use,null,2)});`),i.push(""));let n=new Set,o={imports:n,actionEntityStore:t?.actionEntityStore},r=t?.testName||"Test Suite",s=Ln(t?.tags);i.push(`test.describe.serial('${s}${_e(r)}', () => {`),e.beforeAll&&e.beforeAll.length>0&&(i.push(...bi("beforeAll",e.beforeAll,o,1)),i.push("")),e.beforeEach&&e.beforeEach.length>0&&(i.push(...bi("beforeEach",e.beforeEach,o,1)),i.push(""));for(let c=0;c<e.tests.length;c++){let d=e.tests[c],h=d.timeout||d.skip!==void 0||d.fail!==void 0||d.only||d.slow?{timeout:d.timeout,skip:d.skip,fail:d.fail,only:d.only,slow:d.slow}:void 0;if(d.parameters&&d.parameters.length>0)for(let p of d.parameters){let g=wo(d.testFlow,p.values);i.push(...vi(g,`${_e(d.name)} [${_e(p.name)}]`,o,1,h)),i.push("")}else i.push(...vi(d.testFlow,_e(d.name),o,1,h)),(c<e.tests.length-1||e.afterEach||e.afterAll)&&i.push("")}return e.afterEach&&e.afterEach.length>0&&(i.push(...bi("afterEach",e.afterEach,o,1)),i.push("")),e.afterAll&&e.afterAll.length>0&&i.push(...bi("afterAll",e.afterAll,o,1)),i.push("});"),yo(i,n),i.join(`
|
|
4365
4365
|
`)}function Ln(e){return e&&e.length>0?e.map(t=>`@${t}`).join(" ")+" ":""}var vc=["testContext","request"];function xi(e){let t=new Set;function i(a){for(let n of a)switch(n.type){case ue.ACTION:{let r=n.action_entity?.action_data?.kwargs;if(r?.args&&Array.isArray(r.args))for(let s of r.args)typeof s=="string"&&vc.includes(s)&&t.add(s);break}case ue.STEP:i(n.statements);break;case ue.IF_ELSE:{let o=n;i(o.then),o.else&&i(o.else);break}case ue.WHILE_LOOP:i(n.body);break}}return i(e),t}function _c(e){let t=xi(e.statements??[]);if(e.teardown)for(let i of xi(e.teardown))t.add(i);return t}function Rn(e){return`{ ${["page","agent",...Array.from(e).sort()].join(", ")} }`}function vi(e,t,i,a=0,n){let o=" ".repeat(a),r=[],s=_c(e),l=Rn(s),c=n?.only?"test.only":"test";r.push(`${o}${c}('${t}', async (${l}) => {`),n?.skip===!0?r.push(`${o} test.skip();`):typeof n?.skip=="string"&&r.push(`${o} test.skip(true, '${_e(n.skip)}');`),n?.fail===!0?r.push(`${o} test.fail();`):typeof n?.fail=="string"&&r.push(`${o} test.fail(true, '${_e(n.fail)}');`),n?.slow&&r.push(`${o} test.slow();`),n?.timeout&&r.push(`${o} test.setTimeout(${n.timeout});`);let d=e.teardown&&e.teardown.length>0,h=a+1;if(d){if(r.push(`${o} try {`),e.statements&&e.statements.length>0){r.push(`${o} // Test steps`);let g=pe(e.statements,h+1,i);r.push(...g)}r.push(`${o} } finally {`),r.push(`${o} // Teardown`);let p=pe(e.teardown,h+1,i,"teardown");r.push(...p),r.push(`${o} }`)}else if(e.statements&&e.statements.length>0){r.push(`${o} // Test steps`);let p=pe(e.statements,h,i);r.push(...p)}return r.push(`${o}});`),r}function mo(e,t,i){let a=[],n=fo(t),o=xi(n),r=Rn(o);return a.push(`test.${e}(async (${r}) => {`),a.push(...pe(n,1,i,e)),a.push("});"),a}function bi(e,t,i,a){let n=" ".repeat(a),o=[],r=fo(t);if(e==="beforeAll"||e==="afterAll"){let l={...i,noAgent:!0};o.push(`${n}test.${e}(async ({ browser }, workerInfo) => {`),o.push(`${n} const page = await browser.newPage({ baseURL: workerInfo.project.use.baseURL });`),o.push(...pe(r,a+1,l,e)),o.push(`${n} await page.close();`),o.push(`${n}});`)}else{let l=xi(r),c=Rn(l);o.push(`${n}test.${e}(async (${c}) => {`),o.push(...pe(r,a+1,i,e)),o.push(`${n}});`)}return o}function fo(e){let i=oc({goal:"_hook",statements:e});return me(i).statements??[]}function wo(e,t){let i=Mn(e);for(let[a,n]of Object.entries(t))i=i.split(`<<${a}>>`).join(String(n));return me(i)}function bo(){return["import { test, expect } from 'shiplightai/fixture';"]}function yo(e,t){if(t.size>0){let i=0;for(let n=0;n<e.length;n++)e[n].startsWith("import ")&&(i=n+1);let a=Array.from(t);e.splice(i,0,...a)}}var go=5;function vo(e,t,i){let a={expandingPaths:new Set([Nn(t)]),depth:0,referencedPaths:new Set,basePath:i},n={...e};Array.isArray(n.statements)&&(n.statements=Ne(n.statements,t,a)),Array.isArray(n.teardown)&&(n.teardown=Ne(n.teardown,t,a));for(let o of["beforeAll","afterAll","beforeEach","afterEach"])Array.isArray(n[o])&&(n[o]=Ne(n[o],t,a));return{doc:n,referencedTemplatePaths:Array.from(a.referencedPaths)}}function Ne(e,t,i){let a=[];for(let n of e)if(Mc(n)){let o=Ic(n,t,i);a.push(o)}else a.push(Pc(n,t,i));return a}function Mc(e){return typeof e=="object"&&e!==null&&typeof e.template=="string"}function Ic(e,t,i){if(i.depth>=go)throw new Error(`Template expansion exceeded maximum depth of ${go}. Check for deeply nested or circular template references.`);let a=Nn(Ac(t),e.template),n=!Tc(a)&&i.basePath?Nn(i.basePath,e.template):a;if(i.expandingPaths.has(n))throw new Error(`Circular template reference detected: ${n} is already being expanded. Stack: ${Array.from(i.expandingPaths).join(" \u2192 ")} \u2192 ${n}`);i.referencedPaths.add(n);let o;try{o=Ec(n,"utf-8")}catch(f){throw new Error(`Failed to read template file: ${n} (referenced from ${t}): ${f.message}`)}let r=po(o);if(!r||typeof r!="object")throw new Error(`Invalid template file: ${n} \u2014 expected a YAML object`);let s=r.params||[],l=e.params||{};for(let f of s)if(!(f in l))throw new Error(`Template ${e.template} requires param "${f}" but it was not provided. Required params: [${s.join(", ")}]`);let c=r.statements;if(!Array.isArray(c))throw new Error(`Template ${e.template} must have a "statements" array`);if(Object.keys(l).length>0){let y=$c(c);for(let[b,m]of Object.entries(l))y=y.split(`<<${b}>>`).join(String(m));c=po(y)}let d={expandingPaths:new Set([...i.expandingPaths,n]),depth:i.depth+1,referencedPaths:i.referencedPaths},h=Ne(c,n,d),g={STEP:r.name||e.template.replace(/\.yaml$/,"").split("/").pop()||e.template,template_path:e.template,statements:h};return Object.keys(l).length>0&&(g.template_params=l),g}function Pc(e,t,i){if(typeof e!="object"||e===null)return e;let a={...e};return Array.isArray(a.statements)&&(a.statements=Ne(a.statements,t,i)),Array.isArray(a.THEN)&&(a.THEN=Ne(a.THEN,t,i)),Array.isArray(a.ELSE)&&(a.ELSE=Ne(a.ELSE,t,i)),Array.isArray(a.DO)&&(a.DO=Ne(a.DO,t,i)),a}var Fn=class extends Error{constructor(e){super(e),this.name="YamlValidationError"}};function Hn(e,t,i){let a=Sc(e),n=a?.name,o=a?.tags,r=a?.use;if(a&&(a.name!==void 0||a.tags!==void 0||a.use!==void 0)&&(delete a.name,delete a.tags,delete a.use),a?.suite){if(a.goal||a.statements)throw new Fn('YAML file cannot have both "suite" and top-level "goal"/"statements". Use either suite format or single-test format.');return Oc(a,n,o,r,t,i)}return Cc(a,n,o,r,t,i)}function Cc(e,t,i,a,n,o){let r=e?.beforeEach,s=e?.afterEach,l=_o(e?.parameters),c=e?.timeout,d=e?.skip,h=e?.fail,p=e?.only,g=e?.slow,f=e?.settings,y=a;if(f){let _={};f.auto_dismiss_modal!==void 0&&(_.autoDismissModal=!!f.auto_dismiss_modal),f.browser_timezone!==void 0&&f.browser_timezone!==null&&(_.timezoneId=String(f.browser_timezone)),f.browser_language!==void 0&&f.browser_language!==null&&(_.locale=String(f.browser_language)),f.extra_http_headers!==void 0&&f.extra_http_headers!==null&&typeof f.extra_http_headers=="object"&&(_.extraHTTPHeaders=f.extra_http_headers),Object.keys(_).length>0&&(y={...y,..._})}if(e&&(delete e.beforeEach,delete e.afterEach,delete e.parameters,delete e.timeout,delete e.skip,delete e.fail,delete e.only,delete e.slow,delete e.settings),e?.url)throw new Fn(`The "url" field is not supported in local YAML tests. Use "base_url: ${e.url}" and add "- URL: /" as the first statement instead.`);e&&!e.goal&&t&&(e.goal=t);let b=[];if(n&&e&&typeof e=="object"){let _=vo(e,n,o);e=_.doc,b=_.referencedTemplatePaths}let m=xo(e),x=me(m);return n&&(Xe(x.statements??[],n,"main"),x.teardown&&Xe(x.teardown,n,"teardown")),{testFlow:x,name:t,tags:i,use:y,beforeEach:r,afterEach:s,parameters:l,timeout:c,skip:d,fail:h,only:p,slow:g,referencedTemplatePaths:b}}function Oc(e,t,i,a,n,o){let r=e.suite;if(!Array.isArray(r.tests)||r.tests.length===0)throw new Error('Suite must have a non-empty "tests" array.');let s=r.beforeAll,l=r.afterAll,c=r.beforeEach,d=r.afterEach,h=[],p=r.tests.map(y=>{if(!y.name)throw new Error('Each test in a suite must have a "name" field.');if(!Array.isArray(y.statements)||y.statements.length===0)throw new Error(`Suite test "${y.name}" must have a non-empty "statements" array.`);let b={goal:y.name,statements:y.statements};y.teardown&&(b.teardown=y.teardown);let m=[],x=b;if(n&&typeof b=="object"){let $=vo(b,n,o);x=$.doc,m=$.referencedTemplatePaths,h.push(...m)}let _=xo(x),E=me(_),A=_o(y.parameters);return{testFlow:E,name:y.name,tags:Array.isArray(y.tags)?y.tags:void 0,parameters:A,timeout:y.timeout,skip:y.skip,fail:y.fail,only:y.only,slow:y.slow}}),g=r.base_url,f=g?{...a,baseURL:g}:a;return{suite:{beforeAll:s,afterAll:l,beforeEach:c,afterEach:d,tests:p},name:t,tags:i,use:f,referencedTemplatePaths:h}}function _o(e){if(!(!Array.isArray(e)||e.length===0))return e.map((t,i)=>{if(!t.name)throw new Error(`Parameter set at index ${i} must have a "name" field.`);if(!t.values||typeof t.values!="object")throw new Error(`Parameter set "${t.name}" must have a "values" object.`);return{name:t.name,values:t.values}})}function Xe(e,t,i){for(let a=0;a<e.length;a++){let n=e[a],o=`${i}.${a}`,r=n.description||"";if(n.uid=Lc(t,o,r),n.type===ue.STEP)Xe(n.statements,t,o);else if(n.type===ue.IF_ELSE){let s=n;Xe(s.then,t,`${o}.then`),s.else&&Xe(s.else,t,`${o}.else`)}else n.type===ue.WHILE_LOOP&&Xe(n.body,t,`${o}.body`)}}function Lc(e,t,i){let a=kc("sha256").update(`${e}:${t}:${i}`).digest("hex");return`${a.slice(0,8)}-${a.slice(8,12)}-${a.slice(12,16)}-${a.slice(16,20)}-${a.slice(20,32)}`}function ko(e,t,i){let a=/\btemplate:\s/.test(e),n=/^suite:/m.test(e),o=a||n?null:Pn(e);if(o&&!o.valid)return{valid:!1,errors:o.errors,warnings:[],stats:o.stats};let r,s,l=[];try{let c=i?.parsed??Hn(e,t);l=c.referencedTemplatePaths;let d={version:i?.version,actionEntityStore:i?.actionEntityStore},h=c.testFlow?.baseURL?{...c.use,baseURL:c.testFlow.baseURL}:c.use;c.suite?r=xc(c.suite,{...d,testName:c.name,tags:c.tags,use:c.use}):r=yc(c.testFlow,{...d,testName:c.name,tags:c.tags,use:h,beforeEach:c.beforeEach,afterEach:c.afterEach,parameters:c.parameters,timeout:c.timeout,skip:c.skip,fail:c.fail,only:c.only,slow:c.slow});let p=r.split(`
|
|
4366
4366
|
`).filter(g=>!g.startsWith("import ")).join(`
|
|
4367
|
-
`);new Function(p),s=t.replace(/\.test\.yaml$/,".yaml.spec.ts"),Dc(Rc(s),{recursive:!0}),Nc(s,r)}catch(c){let d=c instanceof Fn?"":c.message.includes("Unexpected token")?" This usually means a YAML escaping issue \u2014 in double-quoted strings, use \\\\/ instead of \\/ for regex patterns, or use single quotes / block scalars.":" This may indicate a transpiler bug \u2014 please report it.";return{valid:!1,errors:[`Transpilation failed: ${c.message}.${d}`],warnings:[],stats:o?.stats??{total:0,action:0,draft:0,coverage:0},referencedTemplatePaths:l}}return{valid:!0,errors:[],warnings:o?.warnings??[],stats:o?.stats??{total:0,action:0,draft:0,coverage:0},specFile:s,referencedTemplatePaths:l}}var Wn="0.1.
|
|
4367
|
+
`);new Function(p),s=t.replace(/\.test\.yaml$/,".yaml.spec.ts"),Dc(Rc(s),{recursive:!0}),Nc(s,r)}catch(c){let d=c instanceof Fn?"":c.message.includes("Unexpected token")?" This usually means a YAML escaping issue \u2014 in double-quoted strings, use \\\\/ instead of \\/ for regex patterns, or use single quotes / block scalars.":" This may indicate a transpiler bug \u2014 please report it.";return{valid:!1,errors:[`Transpilation failed: ${c.message}.${d}`],warnings:[],stats:o?.stats??{total:0,action:0,draft:0,coverage:0},referencedTemplatePaths:l}}return{valid:!0,errors:[],warnings:o?.warnings??[],stats:o?.stats??{total:0,action:0,draft:0,coverage:0},specFile:s,referencedTemplatePaths:l}}var Wn="0.1.64";function So(e){try{return Fc(e).mtimeMs}catch{return 0}}var Uc=`// @generated by shiplightai v${Wn}`;function Bc(e,t){if(!To(e)||Eo(e,"utf-8").split(`
|
|
4368
4368
|
`,1)[0]!==Uc)return!1;let a=So(e);for(let n of t)if(So(n)>a)return!1;return!0}function Gc(e){let t=process.argv.slice(2),i=[],a=Un(e);for(let n of t){if(n.startsWith("-"))continue;let o=n.endsWith(".yaml.spec.ts")?n.replace(/\.yaml\.spec\.ts$/,".test.yaml"):n;if(!o.endsWith(".test.yaml"))continue;let r=Un(e,o);To(r)&&i.push(r.startsWith(a)?r.slice(a.length+1):o)}return i.length>0?i:null}function Ao(e){let t=Gc(e.cwd),i=t??Wc("**/*.test.yaml",{cwd:e.cwd,ignore:["**/node_modules/**"]}),a=[];for(let n of i){let o=Un(e.cwd,n),r=o.replace(/\.test\.yaml$/,".yaml.spec.ts"),s=Eo(o,"utf-8");try{let l=Hn(s,o,e.projectRoot??e.cwd),c=Hc(e.cwd,o),d=e.actionEntityStores?.get(c)??e.actionEntityStores?.get("*");if(!(d&&Object.keys(d.entries).length>0)&&Bc(r,[o,...l.referencedTemplatePaths]))continue;let p=ko(s,o,{version:Wn,actionEntityStore:d,parsed:l});if(!p.valid)throw new Error(p.errors.join("; "))}catch(l){console.error(`[shiplight] Failed to transpile ${n}:`,l),a.push({file:n,error:l})}}if(a.length>0){let n=`[shiplight] Transpilation failed for ${a.length} file(s):
|
|
4369
4369
|
`+a.map(o=>` - ${o.file}`).join(`
|
|
4370
4370
|
`);if(t)throw new Error(n);console.warn(n+" (skipped)")}}import*as ae from"fs";import*as ft from"path";import{globSync as ed}from"glob";Si();function Lo(e){let t=ki().SHIPLIGHT_API_TOKEN;return process.env.CI&&t?new zn:new Kn(e)}var td=".shiplight/action-cache";function Oo(e){return e.replace(/\//g,"__")+".json"}function id(e){return e.replace(/\.json$/,"").replace(/__/g,"/")}var Kn=class{constructor(t){this.cwd=t;this.cacheDir=ft.join(t,td)}isCloud=!1;cacheDir;async lookup(t){let i=new Map;if(t.length===0||!ae.existsSync(this.cacheDir))return i;for(let a of t){let n=ft.join(this.cacheDir,Oo(a));try{if(ae.existsSync(n)){let o=ae.readFileSync(n,"utf-8");i.set(a,JSON.parse(o))}}catch{}}return i}async update(t){if(t.size===0)return 0;ae.mkdirSync(this.cacheDir,{recursive:!0});let i=0;for(let[a,n]of t)try{let o=ft.join(this.cacheDir,Oo(a)),r=Cn();if(ae.existsSync(o))try{r=JSON.parse(ae.readFileSync(o,"utf-8"))}catch{}let s={...r,entries:{...r.entries,...n.entries}};ae.writeFileSync(o,JSON.stringify(s,null,2)),i++}catch{}return i}loadAll(){if(!ae.existsSync(this.cacheDir))return;let t=ed("*.json",{cwd:this.cacheDir});if(t.length===0)return;let i=new Map,a=0;for(let n of t)try{let o=ae.readFileSync(ft.join(this.cacheDir,n),"utf-8"),r=JSON.parse(o),s=id(n);i.set(s,r),a+=Object.keys(r.entries??{}).length}catch{}if(i.size!==0)return console.log(`[shiplight] Cache: loaded ${a} cached action entit${a===1?"y":"ies"} for ${i.size} test file${i.size!==1?"s":""}`),i}},zn=class{isCloud=!0;async lookup(t){let{lookupActionStores:i}=await Promise.resolve().then(()=>(jn(),Gn));return i(t)}async update(t){let{updateActionStores:i}=await Promise.resolve().then(()=>(jn(),Gn));return i(t)}loadAll(){}};Si();function od(e={}){e.dotenv!==!1&&sd(e.scanDir||process.cwd());let t=e.scanDir||process.cwd(),a=Lo(t).loadAll();return Ao({cwd:t,projectRoot:process.cwd(),actionEntityStores:a}),{reporter:[["list"],["shiplightai/reporter",{outputFolder:"shiplight-report",open:"never"}]]}}function rd(e,...t){return ad(e,...t)}function sd(e){Mo(e);for(let t of Bn(e))nd.config({path:t})}R();R();Lt();Qe();import{z as tl}from"zod";var Th=tl.object({instruction:tl.string().describe('The instruction of the operation to perform. Can only include one operation. Do not inlcude element indexes just describe the element, e.g. "select the text "Hello, world!" in "Hello, world!""')});function il(e){e.register({name:"perform_accurate_operation",description:"Perform an operation that requires accurate interaction like dragging or interacting with a specific area of an element. Only use this action when neccecary.",schema:Th,usesElementIndex:!1,async execute(t,i){let{instruction:a}=t,n={page:i.page,agentServices:i.agentServices,domService:i.domService},o=await Ze(a,n,{});if(o.status==="error"||!o.actionEntity)return{success:!1,actionEntity:{action_description:a,action_data:{action_name:"perform_accurate_operation",kwargs:{instruction:a}}},error:o.error||"Failed to generate action"};let{actionEntity:r}=o,s=await Ct(r,n);return{success:s.success,actionEntity:r,message:s.success?`Successfully executed action: ${r.action_data?.action_name}`:void 0,error:s.error}}})}Te();R();import{generateText as Ah}from"ai";import{convert as $h}from"html-to-text";async function Mh(e,t,i){let{apiKey:a,domain:n}=e;if(!a||!n)throw new Error("Mailgun configuration missing. Please provide apiKey and domain");let o=[];try{let r=`https://api.mailgun.net/v3/${n}/events`,s={event:"accepted",limit:"10",ascending:"yes",recipient:t};if(i.from_email&&(s.from=i.from_email),i.since)s.begin=Math.floor(i.since/1e3).toString();else{let d=new Date(Date.now()-6e5);s.begin=Math.floor(d.getTime()/1e3).toString()}u.info(`Mailgun params: ${JSON.stringify(s)}`);let l=await fetch(r+"?"+new URLSearchParams(s),{method:"GET",headers:{Authorization:`Basic ${Buffer.from(`api:${a}`).toString("base64")}`}});if(!l.ok){let d=await l.text();throw new Error(`Mailgun events API error: ${d}`)}let c=(await l.json()).items||[];for(let d of c.slice(0,10)){if(d.event!=="accepted")continue;let h=(d.storage||{}).url;if(u.info(`message_url: ${h}`),!h){let f=(d.message||{}).headers||{},y=f.subject||"",b=f.from||"",m=f.to||"";if(i.from_email&&!b.toLowerCase().includes(i.from_email.toLowerCase())||i.to_email&&!m.toLowerCase().includes(i.to_email.toLowerCase())||i.subject&&!y.toLowerCase().includes(i.subject.toLowerCase()))continue;o.push({subject:y,from:b,to:m,date:new Date(d.timestamp*1e3).toUTCString(),body:"Message body not available (Mailgun storage disabled)",message_id:f["message-id"]||""});continue}let p=h.split("/"),g=p[p.length-1];if(u.info(`Storage key: ${g}`),g){let f=`https://api.mailgun.net/v3/domains/${n}/messages/${g}`;try{let y=await fetch(f,{method:"GET",headers:{Authorization:`Basic ${Buffer.from(`api:${a}`).toString("base64")}`,Accept:"application/json"}});if(y.ok){let b=await y.json(),m=b.Subject||"",x=b.From||"",_=b.To||"",E=b.Date||"",A=b["Message-Id"]||"";u.info(`subject: ${m}`),u.info(`from_addr: ${x}`),u.info(`to_addr: ${_}`),u.info(`date: ${E}`),u.info(`message_id: ${A}`);let $=b["body-html"]||b["body-plain"]||"";if($&&$.includes("<")&&($=$h($)),u.info(`Body: ${$.substring(0,200)}...`),i.subject&&!m.toLowerCase().includes(i.subject.toLowerCase())||i.body_contains&&!$.toLowerCase().includes(i.body_contains.toLowerCase()))continue;o.push({subject:m,from:x,to:_,date:E,body:$,message_id:A});continue}else u.warn(`Messages API returned ${y.status}`)}catch(y){u.warn(`Failed to parse JSON response: ${y}`)}}try{let f=await fetch(h,{method:"GET",headers:{Authorization:`Basic ${Buffer.from(`api:${a}`).toString("base64")}`}});if(!f.ok){u.warn(`Could not fetch stored message: ${f.status}`);continue}let y=await f.text();u.info(`Fallback: Raw email length: ${y.length}`);let b=y.split(`
|
package/dist/reporter.js
CHANGED
|
@@ -873,7 +873,7 @@ import*as k from"fs";import*as v from"path";import{z as s}from"zod";var Q=s.enum
|
|
|
873
873
|
});
|
|
874
874
|
</script>
|
|
875
875
|
</body>
|
|
876
|
-
</html>`}var Te="0.1.
|
|
876
|
+
</html>`}var Te="0.1.64",Ae=Te!=="dev"?Te:void 0;var yn=3600*1e3,bn=10080*60*1e3;var pt={before:0,main:1,teardown:2,after:3};function $e(e){let t=e.split(".")[0];return pt[t]??1}function Ee(e){return e.split(".").map(t=>{let r=Number(t);return Number.isNaN(r)?0:r})}function ht(e){return[...e].sort(([t],[r])=>{let i=$e(t),n=$e(r);if(i!==n)return i-n;let a=Ee(t),o=Ee(r);for(let c=0;c<Math.max(a.length,o.length);c++){let l=a[c]??-1,p=o[c]??-1;if(l!==p)return l-p}return 0})}function ft(e){let t=new Set;for(let r of e)if(t.add(r.category),r.category==="hook")for(let i of r.steps)t.add(i.category);return t}function mt(e,t){let r=e.toLowerCase();return r.includes("before")?"before":r.includes("after")?"after":t}function q(e,t="main",r,i,n){r===void 0&&(r=!ft(e).has("test.step")),n||(n=new Map);let a=[];for(let o of e){if(o.category==="fixture"||o.category==="test.attach")continue;if(o.category==="hook"){let l=mt(o.title,t);a.push(...q(o.steps,l,r,i,n));continue}if(o.category==="test.step"||r&&(o.category==="expect"||o.category==="pw:api")){let l=n.get(t)??0;n.set(t,l+1);let p=`${t}.${l}`,y={stepId:p,description:o.title,status:o.error?"failure":o.duration===-1?"skipped":"success",duration:o.duration>=0?o.duration:void 0};o.error&&(y.error=o.error.message??o.error.stack),i&&o.location&&i.set(p,o.location),a.push(y),o.steps.length>0&&a.push(...q(o.steps,p,r,i,n))}}return a}var Z=class{outputFolder;openMode;collected=[];config;runStartTime;constructor(t={}){this.outputFolder=t.outputFolder||"shiplight-report",this.openMode=t.open||"on-failure"}onBegin(t,r){this.config=t,this.runStartTime=new Date().toISOString()}onTestEnd(t,r){this.collected.push({test:t,result:r})}async onEnd(t){if(this.collected.length===0)return;let r=new Map;for(let p of this.collected){let y=p.test.titlePath().join(" > "),b=r.get(y);b||(b=[],r.set(y,b)),b.push(p)}let i=[];for(let[,p]of r.entries()){let y=p[0].test.location.file,b=[],d,h,f;for(let g=0;g<p.length;g++){let{test:T,result:M}=p[g],A=await this.buildReportTest(T,M,y);d=A,h||(h=A.startTime),f=A.endTime,b.push({attemptNumber:g+1,status:M.status,duration:M.duration,steps:A.steps,error:A.error,videoPath:A.videoPath,tracePath:A.tracePath})}let u=b[b.length-1],{test:S}=p[p.length-1],_={title:S.title,baseTitle:d?.baseTitle,file:v.relative(process.cwd(),y),status:u.status,duration:u.duration,steps:u.steps,error:u.error,videoPath:u.videoPath,tracePath:u.tracePath,actionStepsMap:d?.actionStepsMap,tags:d?.tags,baseUrl:d?.baseUrl,skip:d?.skip,slow:d?.slow,timeout:d?.timeout,parameterSetName:d?.parameterSetName,startTime:h,endTime:f,suiteName:d?.suiteName},w=b.some(g=>g.status==="failed"||g.status==="timedOut");b.length>1&&w&&u.status==="passed"&&(_.flaky=!0,_.retries=b.length-1,_.attempts=b),i.push(_)}let n={tests:i,totalDuration:t.duration,timestamp:new Date().toISOString(),shiplightVersion:Ae},a=v.isAbsolute(this.outputFolder)?this.outputFolder:v.join(process.cwd(),this.outputFolder);k.mkdirSync(a,{recursive:!0});let o=v.join(a,"screenshots");for(let p=0;p<n.tests.length;p++){let y=n.tests[p],b=y.attempts&&y.attempts.length>0,d=[{obj:y,prefix:b?`test-${p}-attempt-0`:`test-${p}`,screenshotSubDir:`test-${p}`}];if(y.attempts)for(let h=0;h<y.attempts.length;h++)d.push({obj:y.attempts[h],prefix:`test-${p}-attempt-${h+1}`,screenshotSubDir:`test-${p}/attempt-${h}`});for(let{obj:h,prefix:f,screenshotSubDir:u}of d){let S=v.join(o,u),_=!1;for(let w of h.steps)if(w.screenshot&&v.isAbsolute(w.screenshot))try{_||(k.mkdirSync(S,{recursive:!0}),_=!0);let g=`${w.stepId.replace(/\./g,"-")}.png`;k.copyFileSync(w.screenshot,v.join(S,g)),w.screenshot=`screenshots/${u}/${g}`}catch(g){console.warn(`[reporter] Failed to copy screenshot for ${w.stepId}:`,g)}if(h.videoPath&&v.isAbsolute(h.videoPath)){let w=v.extname(h.videoPath)||".webm",g=`${f}-video${w}`;try{k.copyFileSync(h.videoPath,v.join(a,g)),h.videoPath=g}catch{h.videoPath=void 0}}if(h.tracePath&&v.isAbsolute(h.tracePath)){let w=v.extname(h.tracePath)||".zip",g=`${f}-trace${w}`;try{k.copyFileSync(h.tracePath,v.join(a,g)),h.tracePath=g}catch{h.tracePath=void 0}}}}let c=v.join(a,"report-data.json");k.writeFileSync(c,JSON.stringify(n,null,2),"utf-8");let l=v.join(a,"index.html");if(k.writeFileSync(l,ke(n),"utf-8"),console.log(`
|
|
877
877
|
Shiplight report written to: ${l}`),this.openMode==="always"||this.openMode==="on-failure"&&t.status!=="passed")try{let p=(await import("open")).default;await p(l)}catch{}}printsToStdio(){return!1}async buildReportTest(t,r,i){let n={title:t.title,file:v.relative(process.cwd(),i),status:r.status,duration:r.duration,steps:[],startTime:new Date(r.startTime).toISOString(),endTime:new Date(r.startTime.getTime()+r.duration).toISOString()};r.errors.length>0&&(n.error=r.errors.map(d=>d.message||d.stack||String(d)).join(`
|
|
878
878
|
|
|
879
879
|
`)),r.stdout.length>0&&(n.stdout=r.stdout.map(d=>typeof d=="string"?d:d.toString()).join("")),r.stderr.length>0&&(n.stderr=r.stderr.map(d=>typeof d=="string"?d:d.toString()).join(""));for(let d of r.attachments)d.name==="video"&&d.path&&(n.videoPath=d.path),d.name==="trace"&&d.path&&(n.tracePath=d.path);let a=r.attachments.find(d=>d.name==="shiplight-results"),o=null;if(a)try{if(a.body)o=JSON.parse(a.body.toString("utf-8"));else if(a.path){let d=k.readFileSync(a.path,"utf-8");o=JSON.parse(d)}}catch{}let c=i.replace(/\.yaml\.spec\.ts$/,".test.yaml"),l={},p=t.title.match(/^(.*)\s+\[([^\]]+)\]$/),y=p?p[1]:t.title,b=p?p[2]:void 0;if(b&&(n.parameterSetName=b),k.existsSync(c))try{let d=k.readFileSync(c,"utf-8"),h=_e(d,c);if(h.suite){let f=h.suite.tests.find(u=>u.name===y);f&&(l=K(f.testFlow),f.tags?.length&&(n.tags=f.tags),f.skip!==void 0&&(n.skip=f.skip),f.slow&&(n.slow=f.slow),f.timeout!==void 0&&(n.timeout=f.timeout),n.baseTitle=f.name||f.testFlow?.goal),n.suiteName=h.name,h.tags?.length&&(n.suiteTags=h.tags),h.use?.baseURL&&(n.baseUrl=h.use.baseURL)}else h.testFlow&&(l=K(h.testFlow),n.baseTitle=h.name||h.testFlow?.goal,h.tags?.length&&(n.tags=h.tags),h.use?.baseURL&&(n.baseUrl=h.use.baseURL))}catch{}if(o||Object.keys(l).length>0){let d=new Set([...Object.keys(l),...Object.keys(o||{})]),h=Array.from(d).map(u=>[u,null]),f=ht(h);for(let[u]of f){let S=l[u],_=o?.[u],w=S?.description;if(!w||w==="Action"||w==="Draft"){let T=S?.action_entity;w=T?.action_description||T?.action_data?.kwargs?.description||_?.description||u}let g={stepId:u,description:w,status:_?.status||"pending",duration:_?.duration};if(_?.message){let T=typeof _.message=="string"?_.message:JSON.stringify(_.message,null,2);_.status==="failure"?g.error=T:g.message=T}if(_?.screenshot){let T=_.screenshot,M=a?.path?v.dirname(a.path):"",A=v.isAbsolute(T)?T:v.join(M,T);k.existsSync(A)&&(g.screenshot=A)}_?.code&&(g.code=_.code),n.steps.push(g)}}if(o===null&&Object.keys(l).length===0&&!i.endsWith(".yaml.spec.ts")){let d=new Map;if(n.steps=q(r.steps,"main",void 0,d),n.steps.length>0){let h=new Map;n.actionStepsMap=Object.fromEntries(n.steps.map(f=>{let u=d.get(f.stepId),S;if(u?.file){if(!h.has(u.file))try{h.set(u.file,k.readFileSync(u.file,"utf-8").split(`
|
|
@@ -7060,6 +7060,8 @@ function useSessions() {
|
|
|
7060
7060
|
const [activeSessionId, setActiveSessionId] = reactExports.useState(null);
|
|
7061
7061
|
const mountedRef = reactExports.useRef(true);
|
|
7062
7062
|
const failureCountRef = reactExports.useRef(0);
|
|
7063
|
+
const sessionsRef = reactExports.useRef(sessions);
|
|
7064
|
+
sessionsRef.current = sessions;
|
|
7063
7065
|
const fetchSessions = reactExports.useCallback(async () => {
|
|
7064
7066
|
try {
|
|
7065
7067
|
const res = await fetch("/api/debugger/sessions", { cache: "no-store" });
|
|
@@ -7068,7 +7070,10 @@ function useSessions() {
|
|
|
7068
7070
|
if (!mountedRef.current) return true;
|
|
7069
7071
|
setSessions(data.sessions);
|
|
7070
7072
|
setActiveSessionId((prev) => {
|
|
7071
|
-
if (prev
|
|
7073
|
+
if (prev) {
|
|
7074
|
+
const stillExists = data.sessions.some((s) => s.sessionId === prev);
|
|
7075
|
+
if (stillExists) return prev;
|
|
7076
|
+
}
|
|
7072
7077
|
const first = data.sessions.find((s) => s.status !== "ended");
|
|
7073
7078
|
return first ? first.sessionId : null;
|
|
7074
7079
|
});
|
|
@@ -7099,28 +7104,28 @@ function useSessions() {
|
|
|
7099
7104
|
};
|
|
7100
7105
|
}, [fetchSessions]);
|
|
7101
7106
|
const openSession = reactExports.useCallback(
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
if (!res.ok) {
|
|
7110
|
-
console.error("[shell] openSession failed:", res.status, await res.text());
|
|
7111
|
-
return null;
|
|
7112
|
-
}
|
|
7113
|
-
const session = await res.json();
|
|
7114
|
-
setActiveSessionId(session.sessionId);
|
|
7115
|
-
setSessions((prev) => {
|
|
7116
|
-
const without = prev.filter((s) => s.sessionId !== session.sessionId);
|
|
7117
|
-
return [...without, session];
|
|
7118
|
-
});
|
|
7119
|
-
return session;
|
|
7120
|
-
} catch (err) {
|
|
7121
|
-
console.error("[shell] openSession threw:", err);
|
|
7122
|
-
return null;
|
|
7107
|
+
(yamlPath) => {
|
|
7108
|
+
const existing = sessionsRef.current.find(
|
|
7109
|
+
(s) => s.yamlPath === yamlPath && s.status !== "ended"
|
|
7110
|
+
);
|
|
7111
|
+
if (existing) {
|
|
7112
|
+
setActiveSessionId(existing.sessionId);
|
|
7113
|
+
return;
|
|
7123
7114
|
}
|
|
7115
|
+
(async () => {
|
|
7116
|
+
try {
|
|
7117
|
+
const res = await fetch("/api/debugger/sessions", {
|
|
7118
|
+
method: "POST",
|
|
7119
|
+
headers: { "Content-Type": "application/json" },
|
|
7120
|
+
body: JSON.stringify({ yamlPath })
|
|
7121
|
+
});
|
|
7122
|
+
if (!res.ok) return;
|
|
7123
|
+
const session = await res.json();
|
|
7124
|
+
setSessions((prev) => [...prev, session]);
|
|
7125
|
+
setActiveSessionId(session.sessionId);
|
|
7126
|
+
} catch {
|
|
7127
|
+
}
|
|
7128
|
+
})();
|
|
7124
7129
|
},
|
|
7125
7130
|
[]
|
|
7126
7131
|
);
|
|
@@ -7157,6 +7162,7 @@ const LONG_PRESS_MS = 500;
|
|
|
7157
7162
|
const MOVE_CANCEL_PX = 10;
|
|
7158
7163
|
function usePointerGestures(args) {
|
|
7159
7164
|
const { onFocus, onOpen, onContextMenu, isFocused } = args;
|
|
7165
|
+
const longPressedRef = reactExports.useRef(false);
|
|
7160
7166
|
const downRef = reactExports.useRef(null);
|
|
7161
7167
|
const clearLongPress = reactExports.useCallback(() => {
|
|
7162
7168
|
const d = downRef.current;
|
|
@@ -7167,22 +7173,18 @@ function usePointerGestures(args) {
|
|
|
7167
7173
|
}, []);
|
|
7168
7174
|
const onPointerDown = reactExports.useCallback(
|
|
7169
7175
|
(e) => {
|
|
7170
|
-
|
|
7171
|
-
const y = e.clientY;
|
|
7172
|
-
const pointerType = e.pointerType;
|
|
7176
|
+
longPressedRef.current = false;
|
|
7173
7177
|
downRef.current = {
|
|
7174
|
-
x,
|
|
7175
|
-
y,
|
|
7176
|
-
|
|
7177
|
-
longPressTimer: null,
|
|
7178
|
-
longPressed: false
|
|
7178
|
+
x: e.clientX,
|
|
7179
|
+
y: e.clientY,
|
|
7180
|
+
longPressTimer: null
|
|
7179
7181
|
};
|
|
7180
|
-
if (pointerType === "touch") {
|
|
7182
|
+
if (e.pointerType === "touch") {
|
|
7183
|
+
const x = e.clientX;
|
|
7184
|
+
const y = e.clientY;
|
|
7181
7185
|
downRef.current.longPressTimer = window.setTimeout(() => {
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
d.longPressed = true;
|
|
7185
|
-
onContextMenu(d.x, d.y);
|
|
7186
|
+
longPressedRef.current = true;
|
|
7187
|
+
onContextMenu(x, y);
|
|
7186
7188
|
}, LONG_PRESS_MS);
|
|
7187
7189
|
}
|
|
7188
7190
|
},
|
|
@@ -7200,47 +7202,25 @@ function usePointerGestures(args) {
|
|
|
7200
7202
|
},
|
|
7201
7203
|
[clearLongPress]
|
|
7202
7204
|
);
|
|
7203
|
-
const
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
clearLongPress();
|
|
7208
|
-
downRef.current = null;
|
|
7209
|
-
if (d == null ? void 0 : d.longPressed) {
|
|
7210
|
-
e.preventDefault();
|
|
7211
|
-
e.stopPropagation();
|
|
7212
|
-
return;
|
|
7213
|
-
}
|
|
7214
|
-
if (isFocused) {
|
|
7215
|
-
if (justOpenedRef.current !== null) {
|
|
7216
|
-
window.clearTimeout(justOpenedRef.current);
|
|
7217
|
-
}
|
|
7218
|
-
justOpenedRef.current = window.setTimeout(() => {
|
|
7219
|
-
justOpenedRef.current = null;
|
|
7220
|
-
}, 300);
|
|
7221
|
-
onOpen();
|
|
7222
|
-
} else {
|
|
7223
|
-
onFocus();
|
|
7224
|
-
}
|
|
7225
|
-
},
|
|
7226
|
-
[clearLongPress, isFocused, onFocus, onOpen]
|
|
7227
|
-
);
|
|
7205
|
+
const onPointerUp = reactExports.useCallback(() => {
|
|
7206
|
+
clearLongPress();
|
|
7207
|
+
downRef.current = null;
|
|
7208
|
+
}, [clearLongPress]);
|
|
7228
7209
|
const onPointerCancel = reactExports.useCallback(() => {
|
|
7229
7210
|
clearLongPress();
|
|
7230
7211
|
downRef.current = null;
|
|
7231
7212
|
}, [clearLongPress]);
|
|
7232
|
-
const
|
|
7213
|
+
const handleClick = reactExports.useCallback(
|
|
7233
7214
|
(e) => {
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
if (justOpenedRef.current !== null) {
|
|
7237
|
-
window.clearTimeout(justOpenedRef.current);
|
|
7238
|
-
justOpenedRef.current = null;
|
|
7215
|
+
if (longPressedRef.current) {
|
|
7216
|
+
longPressedRef.current = false;
|
|
7239
7217
|
return;
|
|
7240
7218
|
}
|
|
7219
|
+
e.preventDefault();
|
|
7220
|
+
if (!isFocused) onFocus();
|
|
7241
7221
|
onOpen();
|
|
7242
7222
|
},
|
|
7243
|
-
[onOpen]
|
|
7223
|
+
[isFocused, onFocus, onOpen]
|
|
7244
7224
|
);
|
|
7245
7225
|
const onContextMenuHandler = reactExports.useCallback(
|
|
7246
7226
|
(e) => {
|
|
@@ -7251,11 +7231,11 @@ function usePointerGestures(args) {
|
|
|
7251
7231
|
[onContextMenu]
|
|
7252
7232
|
);
|
|
7253
7233
|
return {
|
|
7234
|
+
onClick: handleClick,
|
|
7254
7235
|
onPointerDown,
|
|
7255
7236
|
onPointerMove,
|
|
7256
7237
|
onPointerUp,
|
|
7257
7238
|
onPointerCancel,
|
|
7258
|
-
onDoubleClick,
|
|
7259
7239
|
onContextMenu: onContextMenuHandler
|
|
7260
7240
|
};
|
|
7261
7241
|
}
|
|
@@ -7284,8 +7264,6 @@ const colors = {
|
|
|
7284
7264
|
// error h2
|
|
7285
7265
|
// Accent
|
|
7286
7266
|
accent: "#7c5cfc",
|
|
7287
|
-
// active-tab top border, session-indicator dot, spinner top
|
|
7288
|
-
scrollbarHover: "#4d5057",
|
|
7289
7267
|
// scrollbar thumb on hover
|
|
7290
7268
|
spinnerTrack: "#333333"
|
|
7291
7269
|
// spinner background ring (both large + thin variants)
|
|
@@ -7820,57 +7798,43 @@ function SessionFrame({ session, isActive }) {
|
|
|
7820
7798
|
);
|
|
7821
7799
|
}
|
|
7822
7800
|
function DebuggerSessionsHost({ sessions, activeSessionId }) {
|
|
7823
|
-
const
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
inset: 0,
|
|
7831
|
-
pointerEvents: anyActive ? "auto" : "none"
|
|
7801
|
+
const live = sessions.filter((s) => s.status !== "ended");
|
|
7802
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "absolute", inset: 0 }, children: [
|
|
7803
|
+
live.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
7804
|
+
SessionFrame,
|
|
7805
|
+
{
|
|
7806
|
+
session: s,
|
|
7807
|
+
isActive: s.sessionId === activeSessionId
|
|
7832
7808
|
},
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
"
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { style: inlineCode, children: ".test.yaml" }),
|
|
7863
|
-
" file in the tree to open one."
|
|
7864
|
-
] })
|
|
7865
|
-
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
7866
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: spinner }),
|
|
7867
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginTop: 12 }, children: "Starting session…" })
|
|
7868
|
-
] })
|
|
7869
|
-
}
|
|
7870
|
-
)
|
|
7871
|
-
]
|
|
7872
|
-
}
|
|
7873
|
-
);
|
|
7809
|
+
s.sessionId
|
|
7810
|
+
)),
|
|
7811
|
+
sessions.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
7812
|
+
"div",
|
|
7813
|
+
{
|
|
7814
|
+
style: {
|
|
7815
|
+
position: "absolute",
|
|
7816
|
+
inset: 0,
|
|
7817
|
+
display: "flex",
|
|
7818
|
+
flexDirection: "column",
|
|
7819
|
+
alignItems: "center",
|
|
7820
|
+
justifyContent: "center",
|
|
7821
|
+
color: colors.textDim,
|
|
7822
|
+
fontSize: 13,
|
|
7823
|
+
textAlign: "center",
|
|
7824
|
+
padding: 24,
|
|
7825
|
+
pointerEvents: "none"
|
|
7826
|
+
},
|
|
7827
|
+
children: [
|
|
7828
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: 15, color: colors.textMuted, marginBottom: 8 }, children: "No debugger sessions open" }),
|
|
7829
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
7830
|
+
"Double-click a ",
|
|
7831
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { style: inlineCode, children: ".test.yaml" }),
|
|
7832
|
+
" file in the tree to open one."
|
|
7833
|
+
] })
|
|
7834
|
+
]
|
|
7835
|
+
}
|
|
7836
|
+
)
|
|
7837
|
+
] });
|
|
7874
7838
|
}
|
|
7875
7839
|
const inlineCode = {
|
|
7876
7840
|
background: colors.bgHover,
|
|
@@ -7879,14 +7843,6 @@ const inlineCode = {
|
|
|
7879
7843
|
fontSize: 12,
|
|
7880
7844
|
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace"
|
|
7881
7845
|
};
|
|
7882
|
-
const spinner = {
|
|
7883
|
-
width: 24,
|
|
7884
|
-
height: 24,
|
|
7885
|
-
border: `3px solid ${colors.spinnerTrack}`,
|
|
7886
|
-
borderTopColor: colors.accent,
|
|
7887
|
-
borderRadius: "50%",
|
|
7888
|
-
animation: "shp-spin 0.8s linear infinite"
|
|
7889
|
-
};
|
|
7890
7846
|
function readBootstrap() {
|
|
7891
7847
|
if (typeof window === "undefined") return { initialDir: "/", initialFile: null };
|
|
7892
7848
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -7923,7 +7879,7 @@ function DebuggerShellApp() {
|
|
|
7923
7879
|
if (autoOpenedRef.current) return;
|
|
7924
7880
|
if (!bootstrap.initialFile) return;
|
|
7925
7881
|
autoOpenedRef.current = true;
|
|
7926
|
-
|
|
7882
|
+
openSession(bootstrap.initialFile);
|
|
7927
7883
|
}, [bootstrap.initialFile, openSession]);
|
|
7928
7884
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
7929
7885
|
/* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: globalCss }),
|
|
@@ -7959,7 +7915,7 @@ function DebuggerShellApp() {
|
|
|
7959
7915
|
onFocus: setFocusedPath,
|
|
7960
7916
|
onOpen: (path) => {
|
|
7961
7917
|
setFocusedPath(path);
|
|
7962
|
-
|
|
7918
|
+
sessionsState.openSession(path);
|
|
7963
7919
|
}
|
|
7964
7920
|
}
|
|
7965
7921
|
),
|
|
@@ -8014,14 +7970,11 @@ function DebuggerShellApp() {
|
|
|
8014
7970
|
const globalCss = `
|
|
8015
7971
|
html, body, #root { height: 100%; margin: 0; padding: 0; }
|
|
8016
7972
|
* { box-sizing: border-box; }
|
|
8017
|
-
|
|
8018
|
-
::-webkit-scrollbar-track { background: transparent; }
|
|
8019
|
-
::-webkit-scrollbar-thumb { background: ${colors.bgInputHint}; border-radius: 5px; }
|
|
8020
|
-
::-webkit-scrollbar-thumb:hover { background: ${colors.scrollbarHover}; }
|
|
7973
|
+
* { scrollbar-width: thin; scrollbar-color: ${colors.bgInputHint} transparent; }
|
|
8021
7974
|
@keyframes shp-spin { to { transform: rotate(360deg); } }
|
|
8022
7975
|
`;
|
|
8023
7976
|
const container = document.getElementById("root");
|
|
8024
7977
|
if (container) {
|
|
8025
7978
|
clientExports.createRoot(container).render(/* @__PURE__ */ jsxRuntimeExports.jsx(DebuggerShellApp, {}));
|
|
8026
7979
|
}
|
|
8027
|
-
//# sourceMappingURL=index-
|
|
7980
|
+
//# sourceMappingURL=index-BQYw0-8-.js.map
|
package/dist/static/index.html
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
html, body { margin: 0; height: 100%; background: #1a1b1e; color: #c1c2c5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; }
|
|
14
14
|
#root { height: 100%; }
|
|
15
15
|
</style>
|
|
16
|
-
<script type="module" crossorigin src="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-BQYw0-8-.js"></script>
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<div id="root"></div>
|