document360-capture 0.2.11 → 0.2.12

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.
Files changed (2) hide show
  1. package/dist/cli.js +1 -1
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ module.exports = defineConfig({
23
23
  [spawn error] ${y.message}`,f({exitCode:127,stdout:c,stderr:u,outcome:"failed"})}),P.on("close",y=>{t.signal?.removeEventListener("abort",w);let h=y??1;f({exitCode:h,stdout:c,stderr:u,outcome:Ce(h,c)})})})}function De(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function je(){let t=B(xe(import.meta.url));for(let o=0;o<4&&!ve(ut(t,"package.json"));o++)t=B(t);let e=[ut(t,"node_modules"),B(t)];return process.env.NODE_PATH&&e.push(process.env.NODE_PATH),e.join(be)}function Re(){let t=["@playwright/test/cli","@playwright/test/cli.js","playwright/cli","playwright/cli.js"];for(let e of t)try{return Se.resolve(e)}catch{}return null}var ke=[/login\.microsoftonline\.com/i,/login\.live\.com/i,/accounts\.google\.com/i,/\/oauth(2)?\/authorize/i,/\/sign[- ]?in/i,/\bauthn\b/i],Ae=[/waitForURL.*timed?[- ]?out/i,/page\.goto.*redirected/i,/storageState.*invalid/i];function gt(t){return ke.some(e=>e.test(t))?!0:Ae.some(e=>e.test(t))}import{readdirSync as $t,readFileSync as Oe,existsSync as xt,copyFileSync as Ue}from"node:fs";import{join as bt}from"node:path";import J from"sharp";function _(t){return t.replace(/[<>&"']/g,e=>({"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&apos;"})[e])}function mt(t){let o=Math.max(0,t.x-4),r=Math.max(0,t.y-4),n=t.width+8,i=t.height+8;return`<rect x="${o}" y="${r}" width="${n}" height="${i}" rx="6" ry="6" fill="rgba(255,213,0,0.18)" stroke="#f5a623" stroke-width="2.5"/>`}function ht(t){return`<rect x="${t.x}" y="${t.y}" width="${t.width}" height="${t.height}" fill="#1f2328"/>`}var F="-apple-system,Segoe UI,sans-serif",dt=(t,e,o)=>Math.max(e,Math.min(o,t)),Ie=13;function wt(t,e,o,r){let n=Ie,i=dt(e.x,n,Math.max(n,o-n)),s=dt(e.y,n,Math.max(n,r-n));return`<circle cx="${i}" cy="${s}" r="${n}" fill="#d1242f" stroke="#fff" stroke-width="2"/><text x="${i}" y="${s+4}" font-family="${F}" font-size="13" font-weight="700" fill="#fff" text-anchor="middle">${_(t)}</text>`}function yt(t,e){return t===0?0:e.pad*2+t*e.lineHeight}function Pt(t,e){let{top:o,width:r,pad:n,lineHeight:i}=e,s=11,a=20+s,l=[`<line x1="0" y1="${o}" x2="${r}" y2="${o}" stroke="#d0d7de" stroke-width="1"/>`];return t.forEach((p,f)=>{let c=o+n+f*i+i/2;l.push(`<circle cx="${a}" cy="${c}" r="${s}" fill="#d1242f" stroke="#fff" stroke-width="1.5"/>`,`<text x="${a}" y="${c+4}" font-family="${F}" font-size="12" font-weight="700" fill="#fff" text-anchor="middle">${_(p.label)}</text>`,`<text x="${a+s+12}" y="${c+5}" font-family="${F}" font-size="14" fill="#1f2328">${_(p.text)}</text>`)}),l.join("")}async function Ne(t){let e=JSON.parse(Oe(t,"utf8"));if(!xt(e.rawPath))return{id:e.id,ok:!1,reason:`raw missing: ${e.rawPath}`};let o=await J(e.rawPath).metadata(),r=o.width??1440,n=o.height??900,i={pad:16,lineHeight:30},s=yt(e.annotations.length,i),a=n+s,l=[];for(let c of e.highlight)l.push(mt(c.bbox));for(let c of e.redact)l.push(ht(c.bbox));for(let c of e.annotations)l.push(wt(c.label,c.bbox,r,n));if(s>0&&l.push(`<rect x="0" y="${n}" width="${r}" height="${s}" fill="#ffffff"/>`,Pt(e.annotations.map(c=>({label:c.label,text:c.text})),{top:n,width:r,...i})),l.length===0)return await J(e.rawPath).toFile(e.finalPath),{id:e.id,ok:!0,finalPath:e.finalPath};let p=`<svg xmlns="http://www.w3.org/2000/svg" width="${r}" height="${a}">${l.join("")}</svg>`,f=J(e.rawPath);return s>0&&(f=f.extend({bottom:s,background:"#ffffff"})),await f.composite([{input:Buffer.from(p),top:0,left:0}]).toFile(e.finalPath),{id:e.id,ok:!0,finalPath:e.finalPath}}async function vt(t){let e=$t(t).filter(r=>r.endsWith(".raw.png")),o=[];for(let r of e){let n=bt(t,r),i=n.replace(/\.raw\.png$/,".png");try{Ue(n,i),o.push({id:r.replace(/\.raw\.png$/,""),ok:!0,finalPath:i})}catch(s){o.push({id:r,ok:!1,reason:String(s)})}}return o}async function Ct(t,e){if(!xt(t))return[];if(!e)return vt(t);let o=$t(t).filter(n=>n.endsWith(".annotations.json"));if(o.length===0)return vt(t);let r=[];for(let n of o){let i=bt(t,n);try{r.push(await Ne(i))}catch(s){r.push({id:n,ok:!1,reason:String(s)})}}return r}async function jt(t,e){let o=process.cwd(),r=m(d(o)),n=$(r,e.profile),i=n.name,s=x(r.projectId,i);if(!s.exists)return console.error(""),console.error(`\u2717 No saved session for profile "${i}".`),console.error(` Expected at: ${s.path}`),console.error(` Run: d360-capture auth --profile ${i}`),2;let a=St(n.captureDir)?n.captureDir:Dt(o,n.captureDir);if(!Te(a))return console.error(`\u2717 Capture directory missing: ${a}`),console.error(" Generate at least one spec there before running capture."),2;await N();let{configPath:l,tsconfigPath:p}=pt({cwd:o,profileName:i,captureDir:n.captureDir,viewport:n.viewport,storageStatePath:R(r.projectId,i)}),f=t?[t]:Le(a);if(f.length===0)return console.error(`\u2717 No .spec.ts files in ${n.captureDir}.`),2;console.log(""),n.production&&console.log(`\u26A0 Profile "${i}" is PRODUCTION (read-only capture).`),console.log(`\u25B6 Running ${f.length} spec${f.length===1?"":"s"} against ${i} (${n.connection.startUrl})`),t||console.log(" Press Esc (or Ctrl+C) in this terminal to stop after the current spec; press again to stop now.");let c=!1,u=null,P=Me(()=>{c?u?.abort():(c=!0,console.log(`
24
24
  \u23F8 Stopping after the current spec\u2026 (press again to stop now)`))}),w=[],y=!1;try{for(let g of f){if(c)break;u=new AbortController,console.log(`
25
25
  \u25B6 ${g}`);let I=await ft({cwd:o,configPath:l,tsconfigPath:p,startUrl:n.connection.startUrl,authBoundaryUrlPattern:n.connection.authBoundaryUrlPattern,specId:g,captureDirAbs:a,signal:u.signal,scope:n.scope});if(w.push({id:g,outcome:I.outcome}),I.exitCode!==0&&!y&&gt(`${I.stdout}
26
- ${I.stderr}`)&&(y=!0,console.error(""),console.error(" Output mentions a login page \u2014 if specs that used to pass now fail this way, your"),console.error(` saved session may have expired. Re-auth: d360-capture auth --profile ${i}`),console.error(" (Selector/route timeouts are spec bugs, not auth bugs \u2014 inspect the failure above.)")),u.signal.aborted)break}}finally{P(),u=null}let h=w.filter(g=>g.outcome==="passed").length,C=w.filter(g=>g.outcome==="failed"),S=w.filter(g=>g.outcome==="skipped"),A=f.length-w.length,G=!e.noAnnotate&&r.annotation.enabled,Ht=St(n.outputDir)?n.outputDir:Dt(o,n.outputDir);console.log(""),console.log(G?"\u25B6 Annotating screenshots\u2026":"\u25B6 Skipping annotation (--no-annotate or config off)");let D=(await Ct(Ht,G)).filter(g=>!g.ok);console.log(""),console.log(`\u2713 ${h} captured`+(S.length?` \xB7 \u2298 ${S.length} skipped`:"")+(C.length?` \xB7 \u2717 ${C.length} failed`:"")+(A?` \xB7 ${A} not run (stopped)`:"")+` \u2192 ${n.outputDir}`);for(let g of S)console.log(` \u2298 ${g.id}`);for(let g of C)console.log(` \u2717 ${g.id}`);if(D.length>0){console.log(`\u2717 ${D.length} annotation failure${D.length===1?"":"s"}:`);for(let g of D)console.log(` - ${g.id}: ${g.reason}`)}return C.length===0&&D.length===0?0:1}function Le(t){return Ee(t).filter(e=>e.endsWith(".spec.ts")).map(e=>e.slice(0,-8)).sort()}function Me(t){let e=process.stdin;if(e.isTTY){let r=n=>{let i=n[0];(i===27||i===3||i===113||i===81)&&t()};return e.setRawMode(!0),e.resume(),e.on("data",r),()=>{e.off("data",r);try{e.setRawMode(!1)}catch{}e.pause()}}let o=()=>t();return process.on("SIGINT",o),()=>{process.off("SIGINT",o)}}import{existsSync as Rt,readdirSync as Be,statSync as _e}from"node:fs";import{isAbsolute as kt,join as Fe,resolve as At}from"node:path";async function It(){let t=process.cwd(),e=m(d(t)),o=kt(e.captureDir)?e.captureDir:At(t,e.captureDir),r=kt(e.outputDir)?e.outputDir:At(t,e.outputDir);if(!Rt(o)){console.log(`Capture dir does not exist: ${o}`);return}let n=Be(o).filter(i=>i.endsWith(".spec.ts")).sort();if(n.length===0){console.log(`No specs found in ${e.captureDir}.`);return}console.log(""),console.log(" Spec ID Last capture"),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");for(let i of n){let s=i.replace(/\.spec\.ts$/,""),a=Fe(r,`${s}.png`),l=Rt(a)?new Date(_e(a).mtimeMs).toISOString().slice(0,19).replace("T"," "):"(never)";console.log(` ${s.padEnd(47)} ${l}`)}console.log("")}import{existsSync as Ot,readFileSync as Ut}from"node:fs";import{isAbsolute as Je,resolve as He}from"node:path";import{createRequire as We}from"node:module";var Nt=We(import.meta.url);async function Tt(t){let e=process.cwd(),o=0,r=c=>console.log(` \u2713 ${c}`),n=c=>{console.log(` \u2717 ${c}`),o++};console.log(""),console.log("Checking document360-capture setup..."),console.log("");let i=d(e);if(!Ot(i))return n(`Project config missing at ${i} \u2014 run \`d360-capture init\``),2;r(`Project config: ${i}`);let s;try{s=m(i),r(`Config parses; projectId="${s.projectId}"`)}catch(c){return n(`Config invalid: ${c.message}`),2}let a;try{a=$(s,t.profile),r(`Profile "${a.name}" resolved (startUrl=${a.connection.startUrl})${a.production?" \u26A0 PRODUCTION":""}`)}catch(c){return n(`Profile resolve failed: ${c.message}`),1}a.authBoundaryDefaulted&&r(`Auth boundary: ${a.connection.authBoundaryUrlPattern} (auto-derived from start URL; set connection.authBoundaryUrlPattern to override)`);let l=Je(a.captureDir)?a.captureDir:He(e,a.captureDir);Ot(l)?r(`Capture dir exists: ${a.captureDir}`):n(`Capture dir missing: ${a.captureDir}`);let p=x(s.projectId,a.name);if(p.exists){let c=p.ageMs!=null?Math.floor(p.ageMs/864e5):null;r(`Auth-state present (${c}d old): ${p.path}`);try{let u=JSON.parse(Ut(p.path,"utf8"));u&&typeof u=="object"?r("Auth-state parses as JSON"):n("Auth-state is not a valid object")}catch(u){n(`Auth-state unreadable: ${u.message}`)}}else n(`Auth-state missing for "${a.name}" \u2014 run \`d360-capture auth --profile ${a.name}\``);try{let c=Nt.resolve("playwright/package.json"),u=JSON.parse(Ut(c,"utf8"));r(`playwright@${u.version} installed`)}catch{n("playwright not resolvable \u2014 reinstall document360-capture")}return U()?r(`Chromium browser downloaded: ${L()}`):n("Chromium browser not downloaded \u2014 it auto-downloads on the next `d360-capture auth` or `capture` run"),["@playwright/test/cli","@playwright/test/cli.js"].some(c=>{try{return Nt.resolve(c),!0}catch{return!1}})?r("@playwright/test CLI resolvable"):n("@playwright/test CLI not resolvable \u2014 reinstall document360-capture"),console.log(""),console.log(o===0?"All checks passed.":`${o} check${o===1?"":"s"} failed.`),console.log(""),o===0?0:1}async function Et(){let t=m(d());console.log(""),console.log(`projectId: ${t.projectId}`),console.log(`captureDir: ${t.captureDir} (default)`),console.log(`outputDir: ${t.outputDir} (default)`),console.log(`viewport: ${t.viewport.width}x${t.viewport.height} (default)`),console.log(`annotation enabled: ${t.annotation.enabled}`),console.log(`defaultProfile: ${t.defaultProfile}`),console.log(""),console.log("Profiles:");for(let[e,o]of Object.entries(t.profiles)){let r=x(t.projectId,e),n=r.exists?`auth saved ${r.ageMs!=null?Math.floor(r.ageMs/864e5)+"d ago":""}`:"no auth-state",i=`${e===t.defaultProfile?" \u25CF":""}${o.production?" \u26A0 PRODUCTION":""}`;console.log(` ${e.padEnd(10)} ${o.connection.startUrl}${i}`),o.captureDir&&console.log(` captureDir: ${o.captureDir}`),console.log(` ${n}`)}console.log("")}function Lt(){let t=m(d());console.log("");for(let[e,o]of Object.entries(t.profiles)){let r=e===t.defaultProfile?"\u25CF ":" ",n=o.production?" \u26A0 PRODUCTION":"",s=x(t.projectId,e).exists?"auth saved":"no auth-state";console.log(`${r}${e} \u2192 ${o.connection.startUrl}${n} [${s}]`)}console.log(""),console.log("\u25CF = default. Switch with: d360-capture profile use <name>"),console.log("")}function Mt(t){let e=d(),o=m(e);if(!o.profiles[t])return console.error(`\u2717 Unknown profile "${t}". Available: ${Object.keys(o.profiles).join(", ")}`),1;o.defaultProfile=t,O(e,o);let r=o.profiles[t].production?" \u26A0 PRODUCTION":"";return console.log(`\u2713 Default profile is now "${t}"${r}`),0}function Bt(t){let e=m(d()),o=$(e,t);console.log(""),console.log(`Profile ${o.name}${o.production?" \u26A0 PRODUCTION":""}`),console.log(` startUrl: ${o.connection.startUrl}`),console.log(` authBoundary:${o.connection.authBoundaryUrlPattern}`),console.log(` captureDir: ${o.captureDir}`),console.log(` outputDir: ${o.outputDir}`),console.log(` viewport: ${o.viewport.width}x${o.viewport.height}`);let r=Object.keys(o.scope);return r.length>0&&console.log(` scope: ${r.map(n=>`${n}=${o.scope[n]}`).join(", ")}`),console.log(""),0}import{homedir as qe}from"node:os";import{join as Ge,dirname as ze}from"node:path";import{mkdirSync as Ke,readFileSync as Xe,writeFileSync as Ye}from"node:fs";var _t="document360-capture",H=Ge(qe(),".document360-capture","update-cache.json"),Qe=10800*1e3;function Ve(t,e){let o=i=>{let[s="",a=""]=i.trim().replace(/^v/,"").split("-",2),l=s.split(".").map(p=>parseInt(p,10)||0);return{n:[l[0]??0,l[1]??0,l[2]??0],pre:a}},r=o(t),n=o(e);for(let i=0;i<3;i++)if(r.n[i]!==n.n[i])return r.n[i]<n.n[i]?-1:1;return r.pre===n.pre?0:r.pre?n.pre?r.pre<n.pre?-1:1:-1:1}function Ze(){try{return JSON.parse(Xe(H,"utf8"))}catch{return null}}function to(){let t=(process.argv[1]??"").replace(/\\/g,"/");return/\/_npx\//.test(t)||/\/packages\/capture\//.test(t)?!1:/\/node_modules\/document360-capture\//.test(t)||!/\/packages\//.test(t)}async function eo(){try{let t=new AbortController,e=setTimeout(()=>t.abort(),2500),o=await fetch(`https://registry.npmjs.org/${_t}/latest`,{signal:t.signal,headers:{accept:"application/json"}});if(clearTimeout(e),!o.ok)return;let r=await o.json(),n=typeof r.version=="string"?r.version:null;if(!n)return;Ke(ze(H),{recursive:!0}),Ye(H,`${JSON.stringify({checkedAt:new Date().toISOString(),latest:n})}
26
+ ${I.stderr}`)&&(y=!0,console.error(""),console.error(" Output mentions a login page \u2014 if specs that used to pass now fail this way, your"),console.error(` saved session may have expired. Re-auth: d360-capture auth --profile ${i}`),console.error(" (Selector/route timeouts are spec bugs, not auth bugs \u2014 inspect the failure above.)")),u.signal.aborted)break}}finally{P(),u=null}let h=w.filter(g=>g.outcome==="passed").length,C=w.filter(g=>g.outcome==="failed"),S=w.filter(g=>g.outcome==="skipped"),A=f.length-w.length,G=!e.noAnnotate&&r.annotation.enabled,Ht=St(n.outputDir)?n.outputDir:Dt(o,n.outputDir);console.log(""),console.log(G?"\u25B6 Annotating screenshots\u2026":"\u25B6 Skipping annotation (--no-annotate or config off)");let D=(await Ct(Ht,G)).filter(g=>!g.ok);console.log(""),console.log(`\u2713 ${h} captured`+(S.length?` \xB7 \u2298 ${S.length} skipped`:"")+(C.length?` \xB7 \u2717 ${C.length} failed`:"")+(A?` \xB7 ${A} not run (stopped)`:"")+` \u2192 ${n.outputDir}`);for(let g of S)console.log(` \u2298 ${g.id}`);for(let g of C)console.log(` \u2717 ${g.id}`);if(D.length>0){console.log(`\u2717 ${D.length} annotation failure${D.length===1?"":"s"}:`);for(let g of D)console.log(` - ${g.id}: ${g.reason}`)}return C.length===0&&D.length===0?0:1}function Le(t){return Ee(t).filter(e=>e.endsWith(".spec.ts")).map(e=>e.slice(0,-8)).sort()}function Me(t){let e=process.stdin;if(e.isTTY){let r=n=>{let i=n[0];(i===27||i===3||i===113||i===81)&&t()};return e.setRawMode(!0),e.resume(),e.on("data",r),()=>{e.off("data",r);try{e.setRawMode(!1)}catch{}e.pause()}}let o=()=>t();return process.on("SIGINT",o),()=>{process.off("SIGINT",o)}}import{existsSync as Rt,readdirSync as Be,statSync as _e}from"node:fs";import{isAbsolute as kt,join as Fe,resolve as At}from"node:path";async function It(){let t=process.cwd(),e=m(d(t)),o=kt(e.captureDir)?e.captureDir:At(t,e.captureDir),r=kt(e.outputDir)?e.outputDir:At(t,e.outputDir);if(!Rt(o)){console.log(`Capture dir does not exist: ${o}`);return}let n=Be(o).filter(i=>i.endsWith(".spec.ts")).sort();if(n.length===0){console.log(`No specs found in ${e.captureDir}.`);return}console.log(""),console.log(" Spec ID Last capture"),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");for(let i of n){let s=i.replace(/\.spec\.ts$/,""),a=Fe(r,`${s}.png`),l=Rt(a)?new Date(_e(a).mtimeMs).toISOString().slice(0,19).replace("T"," "):"(never)";console.log(` ${s.padEnd(47)} ${l}`)}console.log("")}import{existsSync as Ot,readFileSync as Ut}from"node:fs";import{isAbsolute as Je,resolve as He}from"node:path";import{createRequire as We}from"node:module";var Nt=We(import.meta.url);async function Tt(t){let e=process.cwd(),o=0,r=c=>console.log(` \u2713 ${c}`),n=c=>{console.log(` \u2717 ${c}`),o++};console.log(""),console.log("Checking document360-capture setup..."),console.log("");let i=d(e);if(!Ot(i))return n(`Project config missing at ${i} \u2014 run \`d360-capture init\``),2;r(`Project config: ${i}`);let s;try{s=m(i),r(`Config parses; projectId="${s.projectId}"`)}catch(c){return n(`Config invalid: ${c.message}`),2}let a;try{a=$(s,t.profile),r(`Profile "${a.name}" resolved (startUrl=${a.connection.startUrl})${a.production?" \u26A0 PRODUCTION":""}`)}catch(c){return n(`Profile resolve failed: ${c.message}`),1}a.authBoundaryDefaulted&&r(`Auth boundary: ${a.connection.authBoundaryUrlPattern} (auto-derived from start URL; set connection.authBoundaryUrlPattern to override)`);let l=Je(a.captureDir)?a.captureDir:He(e,a.captureDir);Ot(l)?r(`Capture dir exists: ${a.captureDir}`):r(`Capture dir not created yet: ${a.captureDir} (created with your first spec \u2014 author specs in d360-writer via /screenshot)`);let p=x(s.projectId,a.name);if(p.exists){let c=p.ageMs!=null?Math.floor(p.ageMs/864e5):null;r(`Auth-state present (${c}d old): ${p.path}`);try{let u=JSON.parse(Ut(p.path,"utf8"));u&&typeof u=="object"?r("Auth-state parses as JSON"):n("Auth-state is not a valid object")}catch(u){n(`Auth-state unreadable: ${u.message}`)}}else n(`Auth-state missing for "${a.name}" \u2014 run \`d360-capture auth --profile ${a.name}\``);try{let c=Nt.resolve("playwright/package.json"),u=JSON.parse(Ut(c,"utf8"));r(`playwright@${u.version} installed`)}catch{n("playwright not resolvable \u2014 reinstall document360-capture")}return U()?r(`Chromium browser downloaded: ${L()}`):n("Chromium browser not downloaded \u2014 it auto-downloads on the next `d360-capture auth` or `capture` run"),["@playwright/test/cli","@playwright/test/cli.js"].some(c=>{try{return Nt.resolve(c),!0}catch{return!1}})?r("@playwright/test CLI resolvable"):n("@playwright/test CLI not resolvable \u2014 reinstall document360-capture"),console.log(""),console.log(o===0?"All checks passed.":`${o} check${o===1?"":"s"} failed.`),console.log(""),o===0?0:1}async function Et(){let t=m(d());console.log(""),console.log(`projectId: ${t.projectId}`),console.log(`captureDir: ${t.captureDir} (default)`),console.log(`outputDir: ${t.outputDir} (default)`),console.log(`viewport: ${t.viewport.width}x${t.viewport.height} (default)`),console.log(`annotation enabled: ${t.annotation.enabled}`),console.log(`defaultProfile: ${t.defaultProfile}`),console.log(""),console.log("Profiles:");for(let[e,o]of Object.entries(t.profiles)){let r=x(t.projectId,e),n=r.exists?`auth saved ${r.ageMs!=null?Math.floor(r.ageMs/864e5)+"d ago":""}`:"no auth-state",i=`${e===t.defaultProfile?" \u25CF":""}${o.production?" \u26A0 PRODUCTION":""}`;console.log(` ${e.padEnd(10)} ${o.connection.startUrl}${i}`),o.captureDir&&console.log(` captureDir: ${o.captureDir}`),console.log(` ${n}`)}console.log("")}function Lt(){let t=m(d());console.log("");for(let[e,o]of Object.entries(t.profiles)){let r=e===t.defaultProfile?"\u25CF ":" ",n=o.production?" \u26A0 PRODUCTION":"",s=x(t.projectId,e).exists?"auth saved":"no auth-state";console.log(`${r}${e} \u2192 ${o.connection.startUrl}${n} [${s}]`)}console.log(""),console.log("\u25CF = default. Switch with: d360-capture profile use <name>"),console.log("")}function Mt(t){let e=d(),o=m(e);if(!o.profiles[t])return console.error(`\u2717 Unknown profile "${t}". Available: ${Object.keys(o.profiles).join(", ")}`),1;o.defaultProfile=t,O(e,o);let r=o.profiles[t].production?" \u26A0 PRODUCTION":"";return console.log(`\u2713 Default profile is now "${t}"${r}`),0}function Bt(t){let e=m(d()),o=$(e,t);console.log(""),console.log(`Profile ${o.name}${o.production?" \u26A0 PRODUCTION":""}`),console.log(` startUrl: ${o.connection.startUrl}`),console.log(` authBoundary:${o.connection.authBoundaryUrlPattern}`),console.log(` captureDir: ${o.captureDir}`),console.log(` outputDir: ${o.outputDir}`),console.log(` viewport: ${o.viewport.width}x${o.viewport.height}`);let r=Object.keys(o.scope);return r.length>0&&console.log(` scope: ${r.map(n=>`${n}=${o.scope[n]}`).join(", ")}`),console.log(""),0}import{homedir as qe}from"node:os";import{join as Ge,dirname as ze}from"node:path";import{mkdirSync as Ke,readFileSync as Xe,writeFileSync as Ye}from"node:fs";var _t="document360-capture",H=Ge(qe(),".document360-capture","update-cache.json"),Qe=10800*1e3;function Ve(t,e){let o=i=>{let[s="",a=""]=i.trim().replace(/^v/,"").split("-",2),l=s.split(".").map(p=>parseInt(p,10)||0);return{n:[l[0]??0,l[1]??0,l[2]??0],pre:a}},r=o(t),n=o(e);for(let i=0;i<3;i++)if(r.n[i]!==n.n[i])return r.n[i]<n.n[i]?-1:1;return r.pre===n.pre?0:r.pre?n.pre?r.pre<n.pre?-1:1:-1:1}function Ze(){try{return JSON.parse(Xe(H,"utf8"))}catch{return null}}function to(){let t=(process.argv[1]??"").replace(/\\/g,"/");return/\/_npx\//.test(t)||/\/packages\/capture\//.test(t)?!1:/\/node_modules\/document360-capture\//.test(t)||!/\/packages\//.test(t)}async function eo(){try{let t=new AbortController,e=setTimeout(()=>t.abort(),2500),o=await fetch(`https://registry.npmjs.org/${_t}/latest`,{signal:t.signal,headers:{accept:"application/json"}});if(clearTimeout(e),!o.ok)return;let r=await o.json(),n=typeof r.version=="string"?r.version:null;if(!n)return;Ke(ze(H),{recursive:!0}),Ye(H,`${JSON.stringify({checkedAt:new Date().toISOString(),latest:n})}
27
27
  `)}catch{}}function Ft(t){try{if(!to())return;let e=Ze();e?.latest&&Ve(t,e.latest)<0&&console.error(`
28
28
  \u2B06 d360-capture ${t} \u2192 ${e.latest} available \u2014 run: npm i -g ${_t}@latest
29
29
  `),e?.checkedAt&&Date.now()-new Date(e.checkedAt).getTime()<Qe||eo()}catch{}}var no=ro(import.meta.url),Jt=no("../package.json");Ft(Jt.version);var v=new oo;function W(t){t.env&&(console.error("\u2717 --env was replaced by --profile (connection profiles). Use: --profile <name>"),process.exit(2))}v.name("d360-capture").description("Capture product screenshots from Playwright specs. Login once, capture many.").version(Jt.version);v.command("init").description("Setup. Inherits from .d360-writer/config.json when present; asks only the app URL. Writes .d360-writer/capture.json.").option("-y, --yes","Non-interactive: scaffold with defaults + a placeholder URL, no prompts").action(async t=>{await et(t)});v.command("auth").description("Open Chromium for interactive login. Press ENTER when done to save the session.").option("-p, --profile <name>","Profile name (default: defaultProfile from config)").option("-e, --env <name>",!1).action(async t=>{W(t),await it(t)});v.command("capture [spec-id]").description("Run one spec by id, or all specs if omitted.").option("-p, --profile <name>","Profile name (default: defaultProfile from config)").option("-e, --env <name>",!1).option("--no-annotate","Skip annotation overlay; emit raw screenshots only").action(async(t,e)=>{W(e);let o=await jt(t,e);o!==0&&process.exit(o)});v.command("list").description("List discovered specs and their last-capture times.").action(async()=>{await It()});v.command("doctor").description("Validate config, auth-state, and browser install.").option("-p, --profile <name>","Profile to check").option("-e, --env <name>",!1).action(async t=>{W(t);let e=await Tt(t);e!==0&&process.exit(e)});v.command("status").description("Print resolved config and auth-state freshness.").action(async()=>{await Et()});var q=v.command("profile").description("Manage capture profiles for the current repo");q.command("list",{isDefault:!0}).description("List profiles (\u25CF = default)").action(()=>Lt());q.command("use <name>").description("Set the default profile for this repo").action(t=>process.exit(Mt(t)));q.command("show [name]").description("Print the resolved profile").action(t=>process.exit(Bt(t)));v.parseAsync(process.argv).catch(t=>{console.error(""),console.error(`\u2717 ${t.message}`),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document360-capture",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "CLI that captures product screenshots from Playwright specs. Login once, capture many. Companion to document360-writer.",
5
5
  "type": "module",
6
6
  "bin": {