document360-capture 0.2.9 → 0.2.11
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/cli.js +11 -10
- package/dist/scripts/postinstall.js +1 -1
- package/package.json +2 -2
- package/dist/annotate/compose.d.ts +0 -7
- package/dist/annotate/svg.d.ts +0 -32
- package/dist/cli.d.ts +0 -2
- package/dist/commands/auth.d.ts +0 -4
- package/dist/commands/capture.d.ts +0 -5
- package/dist/commands/doctor.d.ts +0 -4
- package/dist/commands/init.d.ts +0 -5
- package/dist/commands/list.d.ts +0 -1
- package/dist/commands/profile.d.ts +0 -3
- package/dist/commands/status.d.ts +0 -1
- package/dist/config/authState.d.ts +0 -10
- package/dist/config/projectConfig.d.ts +0 -117
- package/dist/config/userConfig.d.ts +0 -6
- package/dist/config/writerConfigDefaults.d.ts +0 -18
- package/dist/helpers/dumpAnnotations.d.ts +0 -3
- package/dist/helpers/index.d.ts +0 -4
- package/dist/helpers/scope.d.ts +0 -10
- package/dist/helpers/types.d.ts +0 -37
- package/dist/helpers/waitPastLogin.d.ts +0 -2
- package/dist/lib/updateNudge.d.ts +0 -3
- package/dist/runner/expiryDetect.d.ts +0 -1
- package/dist/runner/playwrightConfig.d.ts +0 -15
- package/dist/runner/runSpecs.d.ts +0 -26
- package/dist/scripts/postinstall.d.ts +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
3
|
-
`,"utf8")}function
|
|
2
|
+
import{Command as oo}from"commander";import{createRequire as ro}from"node:module";import{input as ee,confirm as oe}from"@inquirer/prompts";import{existsSync as tt,readFileSync as re}from"node:fs";import{basename as ne,join as ie}from"node:path";import{readFileSync as z,writeFileSync as Wt,existsSync as K,mkdirSync as qt}from"node:fs";import{basename as Gt,dirname as zt,join as T}from"node:path";var Kt=T(".d360-writer","capture.json"),j={captureDir:".d360-writer/user-docs/_capture",outputDir:".d360-writer/user-docs/_screenshots",viewport:{width:1440,height:900},loginTimeoutMs:0,annotation:{enabled:!0}},b=class extends Error{};function d(t=process.cwd()){return T(t,Kt)}function m(t){if(!K(t))throw new Error(`No project config at ${t}. Run \`d360-capture init\` to create one.`);let e=JSON.parse(z(t,"utf8").replace(/^/,""));return Yt(e,t)}function O(t,e){qt(zt(t),{recursive:!0}),Wt(t,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,"utf8")}function Xt(t){try{return"^"+new URL(t).origin.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}catch{return"."}}function Yt(t,e,o=process.cwd()){Qt(t,e);let r=Object.keys(t.profiles),n=t.defaultProfile??(r.length===1?r[0]:r.includes("staging")?"staging":r[0]);return{projectId:t.projectId||Vt(o)||Gt(o),captureDir:t.captureDir??j.captureDir,outputDir:t.outputDir??j.outputDir,viewport:t.viewport??{...j.viewport},loginTimeoutMs:t.loginTimeoutMs??j.loginTimeoutMs,annotation:t.annotation??{...j.annotation},profiles:t.profiles,defaultProfile:n}}function $(t,e){let o=e??t.defaultProfile,r=t.profiles[o];if(!r){let i=Object.keys(t.profiles).join(", ");throw new b(`Unknown profile "${o}". Available: ${i}`)}let n=r.connection.authBoundaryUrlPattern;return{name:o,connection:{startUrl:r.connection.startUrl,authBoundaryUrlPattern:n||Xt(r.connection.startUrl)},captureDir:r.captureDir??t.captureDir,outputDir:r.outputDir??t.outputDir,viewport:r.viewport??t.viewport,production:r.production===!0,scope:r.scope??{},authBoundaryDefaulted:!n}}function Qt(t,e){if(t.environments&&!t.profiles)throw new b(`Config at ${e} uses the old "environments" shape, which is no longer supported. It is now "profiles" (each with a "connection" { startUrl }) + "defaultProfile". Re-run \`d360-capture init\`, then \`d360-capture auth --profile <name>\`.`);if(!t.profiles||Object.keys(t.profiles).length===0)throw new b(`Invalid config at ${e}: needs at least one profile under "profiles".`);for(let[o,r]of Object.entries(t.profiles))if(!r?.connection?.startUrl)throw new b(`Invalid config at ${e}: profile "${o}" is missing connection.startUrl.`);if(t.defaultProfile&&!t.profiles[t.defaultProfile])throw new b(`Invalid config at ${e}: defaultProfile "${t.defaultProfile}" is not in profiles.`)}function Vt(t){let e=T(t,"package.json");if(!K(e))return null;try{return JSON.parse(z(e,"utf8")).name??null}catch{return null}}import{existsSync as X,readFileSync as Zt}from"node:fs";import{join as Y}from"node:path";var te=Y(".d360-writer","config.json");function Q(t=process.cwd()){return Y(t,te)}function V(t=process.cwd()){return X(Q(t))}function Z(t=process.cwd()){let e=Q(t);if(!X(e))return{};try{let o=JSON.parse(Zt(e,"utf8").replace(/^/,"")),r=o.docsDir?.replace(/\/+$/,"");return{projectId:o.projectId,captureDir:o.captureDir??(r?`${r}/_capture`:void 0),outputDir:o.outputDir??(r?`${r}/_screenshots`:void 0)}}catch{return{}}}async function et(t={}){let e=process.cwd(),o=d(e),r=t.yes===!0;if(tt(o)&&!r&&!await oe({message:`${o} already exists. Overwrite?`,default:!1})){console.log("Init cancelled.");return}let n=Z(e),i=n.projectId||se(e)||ne(e),s=[n.projectId&&"project name",n.captureDir&&"directories"].filter(Boolean);s.length>0?(console.log(""),console.log(`Found .d360-writer/config.json \u2014 inheriting ${s.join(" + ")}.`)):V(e)&&(console.log(""),console.log("Found .d360-writer/config.json (could not read defaults from it \u2014 using built-in defaults)."));let a=r?"https://example.com/":await ee({message:"Live app URL (where captures start):",default:"https://example.com/",validate:p=>p.startsWith("http")?!0:"Must be a full URL"}),l={projectId:i,...n.captureDir?{captureDir:n.captureDir}:{},...n.outputDir?{outputDir:n.outputDir}:{},profiles:{staging:{connection:{startUrl:a}}},defaultProfile:"staging"};O(o,l),console.log(""),console.log(`\u2713 Wrote ${o}`),r&&a==="https://example.com/"&&console.log(" \u26A0 Set the staging profile's connection.startUrl to your real app URL."),console.log(""),console.log("Next steps:"),console.log(" 1. d360-capture auth # log in once, no timeout"),console.log(" 2. d360-capture capture # run all specs in the capture dir"),console.log(""),console.log("Add a prod profile or tune the auth boundary later by editing .d360-writer/capture.json."),console.log("")}function se(t){let e=ie(t,"package.json");if(!tt(e))return null;try{return JSON.parse(re(e,"utf8")).name??null}catch{return null}}import{chromium as me}from"playwright";import{existsSync as ot,mkdirSync as ae,statSync as ce}from"node:fs";import{homedir as le}from"node:os";import{join as E,dirname as xo}from"node:path";function rt(){return E(le(),".document360-capture","auth-states")}function R(t,e){return E(rt(),t,`${e}.json`)}function nt(t){let e=E(rt(),t);ot(e)||ae(e,{recursive:!0})}function x(t,e){let o=R(t,e);if(!ot(o))return{path:o,exists:!1};let r=ce(o);return{path:o,exists:!0,ageMs:Date.now()-r.mtimeMs}}import{existsSync as pe}from"node:fs";import{spawn as ue}from"node:child_process";import{createRequire as fe}from"node:module";import{chromium as ge}from"playwright";var de=fe(import.meta.url);function L(){try{return ge.executablePath()}catch{return null}}function U(){let t=L();return t!=null&&pe(t)}async function N(){if(U())return;console.log(""),console.log("\u25B6 Chromium is not downloaded on this machine yet \u2014 fetching it now (one-time)\u2026");let t=de.resolve("playwright/cli.js"),e=await new Promise((o,r)=>{let n=ue(process.execPath,[t,"install","chromium"],{stdio:"inherit"});n.on("error",r),n.on("exit",i=>o(i??1))});if(e!==0||!U())throw new Error(`Chromium download failed (exit code ${e}). Check your network/proxy and retry, or run:
|
|
4
|
+
node "${t}" install chromium`);console.log("\u2713 Chromium ready.")}async function it(t){let e=m(d()),o=$(e,t.profile);nt(e.projectId);let r=R(e.projectId,o.name);await N(),console.log(""),o.production&&console.log(`\u26A0 Profile "${o.name}" is PRODUCTION.`),console.log(`Opening Chromium against ${o.connection.startUrl}`),console.log(`Auth-state will save to: ${r}`),console.log("");let n=await me.launch({headless:!1,args:["--start-maximized"]}),i=await n.newContext({viewport:null}),s=await i.newPage();await s.goto(o.connection.startUrl,{waitUntil:"domcontentloaded"}),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"),console.log(" Log in fully (MFA included) at your own pace."),console.log(" When you reach the post-login app, come back to this"),console.log(" terminal and press ENTER to save the session."),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"),console.log("");let a=new RegExp(o.connection.authBoundaryUrlPattern),l=setInterval(()=>{a.test(s.url())&&i.storageState({path:r}).catch(()=>{})},3e4);try{for(;;){await he();let p=s.url();if(a.test(p))break;console.log(""),console.log("\u2717 Login doesn't look complete yet \u2014 the browser is still at:"),console.log(` ${p}`),console.log(" Finish logging in until you can see the app, then press ENTER again."),console.log(" (Ctrl+C aborts without saving.)")}await i.storageState({path:r}),console.log(""),console.log(`\u2713 Session saved to ${r}`),console.log(" This file contains session cookies \u2014 never commit it to git.")}finally{clearInterval(l),await n.close().catch(()=>{})}}function he(){return new Promise(t=>{let e=()=>{process.stdin.off("data",e),process.stdin.pause(),t()};process.stdin.resume(),process.stdin.once("data",e)})}import{existsSync as Te,readdirSync as Ee}from"node:fs";import{isAbsolute as St,resolve as Dt}from"node:path";import{writeFileSync as st,mkdirSync as we,existsSync as at}from"node:fs";import{createRequire as ye}from"node:module";import{join as k,isAbsolute as ct,resolve as lt}from"node:path";var M=ye(import.meta.url);function pt(t){let{cwd:e,profileName:o,captureDir:r,viewport:n,storageStatePath:i}=t,s=k(e,".d360-writer","cache");at(s)||we(s,{recursive:!0});let a=k(s,`playwright.${o}.config.cjs`),l=ct(r)?r:lt(e,r),p=ct(i)?i:lt(e,i),f=M.resolve("@playwright/test"),c=`// Generated by document360-capture \u2014 do not edit by hand.
|
|
4
5
|
const { defineConfig } = require(${JSON.stringify(f)});
|
|
5
6
|
module.exports = defineConfig({
|
|
6
7
|
testDir: ${JSON.stringify(l)},
|
|
@@ -18,11 +19,11 @@ module.exports = defineConfig({
|
|
|
18
19
|
contextOptions: { reducedMotion: 'reduce' },
|
|
19
20
|
},
|
|
20
21
|
});
|
|
21
|
-
`;
|
|
22
|
-
[spawn error] ${
|
|
23
|
-
\u23F8 Stopping after the current spec\u2026 (press again to stop now)`))}),
|
|
24
|
-
\u25B6 ${g}`);let I=await
|
|
25
|
-
${I.stderr}`)&&(
|
|
26
|
-
`)}catch{}}function
|
|
27
|
-
\u2B06 d360-capture ${t} \u2192 ${e.latest} available \u2014 run: npm i -g ${
|
|
28
|
-
`),e?.checkedAt&&Date.now()-new Date(e.checkedAt).getTime()<
|
|
22
|
+
`;st(a,c,"utf8");let u=k(s,`tsconfig.${o}.json`),P=A=>A.replace(/\\/g,"/"),w=M.resolve("document360-capture/helpers"),y=k(M.resolve("@playwright/test/package.json"),".."),h=k(y,"index.mjs"),C=at(h)?h:f,S={compilerOptions:{baseUrl:".",paths:{"document360-capture/helpers":[P(w)],"@playwright/test":[P(C)]}}};return st(u,JSON.stringify(S,null,2),"utf8"),{configPath:a,tsconfigPath:u}}import{spawn as Pe}from"node:child_process";import{existsSync as ve}from"node:fs";import{createRequire as $e}from"node:module";import{fileURLToPath as xe}from"node:url";import{delimiter as be,dirname as B,join as ut}from"node:path";function Ce(t,e){return t!==0?"failed":/\b\d+\s+skipped\b/.test(e)&&!/\b\d+\s+passed\b/.test(e)?"skipped":"passed"}var Se=$e(import.meta.url);async function ft(t){let{cwd:e,configPath:o,tsconfigPath:r,startUrl:n,authBoundaryUrlPattern:i,specId:s,captureDirAbs:a}=t,l=Re();if(!l)return{exitCode:127,stdout:"",stderr:"Cannot locate @playwright/test CLI. Try: npm install -g document360-capture (reinstall)",outcome:"failed"};let p=[l,"test","--config",o,"--tsconfig",r];return s&&p.push(De(`${s}.spec.ts`)),new Promise(f=>{let c="",u="",P=Pe(process.execPath,p,{cwd:e,env:{...process.env,CAPTURE_START_URL:n,CAPTURE_AUTH_BOUNDARY:i,CAPTURE_SCOPE:JSON.stringify(t.scope??{}),NODE_PATH:je()},shell:!1}),w=()=>{try{P.kill()}catch{}};t.signal&&(t.signal.aborted?w():t.signal.addEventListener("abort",w,{once:!0})),P.stdout.on("data",y=>{let h=y.toString();c+=h,process.stdout.write(h)}),P.stderr.on("data",y=>{let h=y.toString();u+=h,process.stderr.write(h)}),P.on("error",y=>{t.signal?.removeEventListener("abort",w),u+=`
|
|
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=>({"<":"<",">":">","&":"&",'"':""","'":"'"})[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
|
+
\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
|
+
\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&>(`${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})}
|
|
27
|
+
`)}catch{}}function Ft(t){try{if(!to())return;let e=Ze();e?.latest&&Ve(t,e.latest)<0&&console.error(`
|
|
28
|
+
\u2B06 d360-capture ${t} \u2192 ${e.latest} available \u2014 run: npm i -g ${_t}@latest
|
|
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)});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{spawn as
|
|
1
|
+
import{spawn as i}from"node:child_process";import{createRequire as r}from"node:module";var t=process.env.npm_config_global==="true"||process.env.npm_config_location==="global";!t&&process.env.D360_CAPTURE_FORCE_POSTINSTALL!=="true"&&process.exit(0);var c=r(import.meta.url),o;try{o=c.resolve("playwright/cli.js")}catch{console.warn("[document360-capture] playwright not found; skipping chromium download"),process.exit(0)}console.log("[document360-capture] downloading Chromium (one-time)...");var n=i(process.execPath,[o,"install","chromium"],{stdio:"inherit"});n.on("exit",e=>process.exit(e??0));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document360-capture",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
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": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"./helpers": "./dist/helpers/index.js"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
-
"dist",
|
|
15
|
+
"dist/**/*.js",
|
|
16
16
|
"README.md",
|
|
17
17
|
"LICENSE"
|
|
18
18
|
],
|
package/dist/annotate/svg.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { Bbox } from '../helpers/types.js';
|
|
2
|
-
export declare function escapeXml(s: string): string;
|
|
3
|
-
export declare function highlightSvg(bbox: Bbox): string;
|
|
4
|
-
export declare function redactSvg(bbox: Bbox): string;
|
|
5
|
-
/**
|
|
6
|
-
* Just the numbered pin, placed on the target's top-left corner (clamped into the image). Pins
|
|
7
|
-
* only MARK targets — the explanation lives in the legend band — so they stay small and never
|
|
8
|
-
* cover the surrounding UI, which is what doomed inline captions on busy screens.
|
|
9
|
-
*/
|
|
10
|
-
export declare function pinSvg(label: string, bbox: Bbox, canvasW: number, canvasH: number): string;
|
|
11
|
-
export type LegendItem = {
|
|
12
|
-
label: string;
|
|
13
|
-
text: string;
|
|
14
|
-
};
|
|
15
|
-
export type LegendLayout = {
|
|
16
|
-
/** Y where the band starts (== original image height). */
|
|
17
|
-
top: number;
|
|
18
|
-
width: number;
|
|
19
|
-
/** Vertical padding above the first / below the last entry. */
|
|
20
|
-
pad: number;
|
|
21
|
-
/** Per-entry row height. */
|
|
22
|
-
lineHeight: number;
|
|
23
|
-
};
|
|
24
|
-
/** Height a legend band needs for `n` entries — the canvas is extended by exactly this much. */
|
|
25
|
-
export declare function legendBandHeight(n: number, layout: Pick<LegendLayout, 'pad' | 'lineHeight'>): number;
|
|
26
|
-
/**
|
|
27
|
-
* The legend band: a numbered list in the appended strip below the screenshot. Deterministic,
|
|
28
|
-
* always-safe placement (the band is added space, so nothing on the page is ever covered) — no
|
|
29
|
-
* collision detection or whitespace-hunting required. Each row reuses the pin's numbered chip so
|
|
30
|
-
* the legend entry and its on-image pin read as the same marker.
|
|
31
|
-
*/
|
|
32
|
-
export declare function legendSvg(items: LegendItem[], layout: LegendLayout): string;
|
package/dist/cli.d.ts
DELETED
package/dist/commands/auth.d.ts
DELETED
package/dist/commands/init.d.ts
DELETED
package/dist/commands/list.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function listCommand(): Promise<void>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function statusCommand(): Promise<void>;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare function authStateDir(): string;
|
|
2
|
-
export declare function authStatePath(projectId: string, profileName: string): string;
|
|
3
|
-
export declare function ensureAuthStateDir(projectId: string): void;
|
|
4
|
-
export type AuthStateInfo = {
|
|
5
|
-
path: string;
|
|
6
|
-
exists: boolean;
|
|
7
|
-
ageMs?: number;
|
|
8
|
-
};
|
|
9
|
-
export declare function getAuthStateInfo(projectId: string, profileName: string): AuthStateInfo;
|
|
10
|
-
export declare function ensureParentDir(path: string): void;
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/** How to reach a target deployment for capture. On disk, only `startUrl` is required —
|
|
2
|
-
`authBoundaryUrlPattern` is filled by resolveProfile (host heuristic) when omitted. */
|
|
3
|
-
export type ProfileConnection = {
|
|
4
|
-
startUrl: string;
|
|
5
|
-
authBoundaryUrlPattern?: string;
|
|
6
|
-
};
|
|
7
|
-
/** A named capture profile. `connection` is one aspect; a profile may also override
|
|
8
|
-
the directories/viewport so one repo can target multiple doc-sets/environments. */
|
|
9
|
-
export type Profile = {
|
|
10
|
-
connection: ProfileConnection;
|
|
11
|
-
captureDir?: string;
|
|
12
|
-
outputDir?: string;
|
|
13
|
-
viewport?: {
|
|
14
|
-
width: number;
|
|
15
|
-
height: number;
|
|
16
|
-
};
|
|
17
|
-
/** Visible warning only — capture is read-only (screenshots), so no hard guard. */
|
|
18
|
-
production?: boolean;
|
|
19
|
-
/** The prepared "demo" context a capture run targets, as generic key→value pairs the writer
|
|
20
|
-
recommends per product (NOT hardcoded to one product's notions). FlowForge → {project:"…"};
|
|
21
|
-
Document360 → {project,workspace,language}; others → {org}/{site}/… Injected to specs as
|
|
22
|
-
CAPTURE_SCOPE; specs read it via the captureScope() helper to navigate deterministically. */
|
|
23
|
-
scope?: Record<string, string>;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* The on-disk shape: a minimal `.d360-capture.json` need only carry a `profiles` map
|
|
27
|
-
* whose profiles each have a `connection.startUrl`. Everything else is filled at load
|
|
28
|
-
* time (see `DEFAULTS` + `normalizeProjectConfig`), so a 3-line config is valid.
|
|
29
|
-
*/
|
|
30
|
-
export type RawProjectConfig = {
|
|
31
|
-
projectId?: string;
|
|
32
|
-
captureDir?: string;
|
|
33
|
-
outputDir?: string;
|
|
34
|
-
viewport?: {
|
|
35
|
-
width: number;
|
|
36
|
-
height: number;
|
|
37
|
-
};
|
|
38
|
-
loginTimeoutMs?: number;
|
|
39
|
-
annotation?: {
|
|
40
|
-
enabled: boolean;
|
|
41
|
-
};
|
|
42
|
-
profiles: Record<string, Profile>;
|
|
43
|
-
defaultProfile?: string;
|
|
44
|
-
};
|
|
45
|
-
/** A fully-resolved config: every default materialized. What every command sees. */
|
|
46
|
-
export type ProjectConfig = {
|
|
47
|
-
projectId: string;
|
|
48
|
-
/** Defaults inherited by any profile that doesn't override them. */
|
|
49
|
-
captureDir: string;
|
|
50
|
-
outputDir: string;
|
|
51
|
-
viewport: {
|
|
52
|
-
width: number;
|
|
53
|
-
height: number;
|
|
54
|
-
};
|
|
55
|
-
loginTimeoutMs: number;
|
|
56
|
-
annotation: {
|
|
57
|
-
enabled: boolean;
|
|
58
|
-
};
|
|
59
|
-
profiles: Record<string, Profile>;
|
|
60
|
-
defaultProfile: string;
|
|
61
|
-
};
|
|
62
|
-
/** A profile connection with the auth boundary resolved (always a string). */
|
|
63
|
-
export type ResolvedConnection = {
|
|
64
|
-
startUrl: string;
|
|
65
|
-
authBoundaryUrlPattern: string;
|
|
66
|
-
};
|
|
67
|
-
/** A profile with directory/viewport defaults and the auth boundary already merged in. */
|
|
68
|
-
export type ResolvedProfile = {
|
|
69
|
-
name: string;
|
|
70
|
-
connection: ResolvedConnection;
|
|
71
|
-
captureDir: string;
|
|
72
|
-
outputDir: string;
|
|
73
|
-
viewport: {
|
|
74
|
-
width: number;
|
|
75
|
-
height: number;
|
|
76
|
-
};
|
|
77
|
-
production: boolean;
|
|
78
|
-
/** The capture scope (key→value) for this profile; {} when unset. */
|
|
79
|
-
scope: Record<string, string>;
|
|
80
|
-
/** True when the auth boundary was derived (host heuristic) rather than configured —
|
|
81
|
-
doctor surfaces this so a too-loose/tight pattern is refinable before it bites. */
|
|
82
|
-
authBoundaryDefaulted: boolean;
|
|
83
|
-
};
|
|
84
|
-
export declare const PROJECT_CONFIG_FILENAME: string;
|
|
85
|
-
/** Filled in for any field a minimal config omits. Default location lives under .d360-writer/ to
|
|
86
|
-
match the writer's single-footprint layout (the capture dirs derive from the writer's docsDir
|
|
87
|
-
when present; these are the bare fallback). */
|
|
88
|
-
export declare const DEFAULTS: {
|
|
89
|
-
readonly captureDir: ".d360-writer/user-docs/_capture";
|
|
90
|
-
readonly outputDir: ".d360-writer/user-docs/_screenshots";
|
|
91
|
-
readonly viewport: {
|
|
92
|
-
readonly width: 1440;
|
|
93
|
-
readonly height: 900;
|
|
94
|
-
};
|
|
95
|
-
readonly loginTimeoutMs: 0;
|
|
96
|
-
readonly annotation: {
|
|
97
|
-
readonly enabled: true;
|
|
98
|
-
};
|
|
99
|
-
};
|
|
100
|
-
export declare class ProfileConfigError extends Error {
|
|
101
|
-
}
|
|
102
|
-
export declare function findProjectConfigPath(cwd?: string): string;
|
|
103
|
-
export declare function readProjectConfig(path: string): ProjectConfig;
|
|
104
|
-
export declare function writeProjectConfig(path: string, cfg: RawProjectConfig): void;
|
|
105
|
-
/**
|
|
106
|
-
* Derive a permissive post-login auth boundary from the profile's start URL: any URL on the
|
|
107
|
-
* same origin. The login/IdP flow legitimately leaves that origin (SSO redirects) and returns
|
|
108
|
-
* to it once authenticated, so "back on the app origin" is a reasonable logged-in signal —
|
|
109
|
-
* good enough as a default, refinable when it isn't. Falls back to match-anything if the URL
|
|
110
|
-
* can't be parsed.
|
|
111
|
-
*/
|
|
112
|
-
export declare function deriveAuthBoundary(startUrl: string): string;
|
|
113
|
-
/** Fill every optional field from DEFAULTS / the cwd, then validate what can't be defaulted. */
|
|
114
|
-
export declare function normalizeProjectConfig(raw: RawProjectConfig & {
|
|
115
|
-
environments?: unknown;
|
|
116
|
-
}, path: string, cwd?: string): ProjectConfig;
|
|
117
|
-
export declare function resolveProfile(cfg: ProjectConfig, name?: string): ResolvedProfile;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* d360-capture rides on d360-writer's setup: when a repo already has `.d360-writer.json`,
|
|
3
|
-
* `init` inherits its project label + doc directories so the only thing left to ask is the
|
|
4
|
-
* live app URL. This reads that file as PLAIN JSON — never an engine import (the packages are
|
|
5
|
-
* deliberately decoupled; this is file I/O, not a code dependency). Tolerant by design:
|
|
6
|
-
* any parse/shape problem yields {} and init falls back to its own defaults.
|
|
7
|
-
*
|
|
8
|
-
* Field overlap is intentional — `.d360-writer.json` already carries `projectId`
|
|
9
|
-
* (its doc comment: "scopes sessions/screenshots"), `captureDir`, `outputDir`, and `docsDir`.
|
|
10
|
-
*/
|
|
11
|
-
export type WriterConfigDefaults = {
|
|
12
|
-
projectId?: string;
|
|
13
|
-
captureDir?: string;
|
|
14
|
-
outputDir?: string;
|
|
15
|
-
};
|
|
16
|
-
export declare function writerConfigPath(cwd?: string): string;
|
|
17
|
-
export declare function hasWriterConfig(cwd?: string): boolean;
|
|
18
|
-
export declare function readWriterConfigDefaults(cwd?: string): WriterConfigDefaults;
|
package/dist/helpers/index.d.ts
DELETED
package/dist/helpers/scope.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The prepared capture scope — generic key→value pairs (e.g. {project: "Docs Demo"}, or
|
|
3
|
-
* {project, workspace, language} for products that scope that way) that the writer recommends
|
|
4
|
-
* per product and the user fills in. document360-capture injects it to specs as CAPTURE_SCOPE
|
|
5
|
-
* (JSON). Specs read it to navigate into a KNOWN context deterministically instead of grabbing
|
|
6
|
-
* "the first one". Returns {} when unset, so single-context setups keep working.
|
|
7
|
-
*/
|
|
8
|
-
export declare function captureScope(): Record<string, string>;
|
|
9
|
-
/** One scope value, e.g. `scopeValue('project')`. Undefined when not configured. */
|
|
10
|
-
export declare function scopeValue(key: string): string | undefined;
|
package/dist/helpers/types.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export type Bbox = {
|
|
2
|
-
x: number;
|
|
3
|
-
y: number;
|
|
4
|
-
width: number;
|
|
5
|
-
height: number;
|
|
6
|
-
};
|
|
7
|
-
export type Annotation = {
|
|
8
|
-
target: string;
|
|
9
|
-
label: string;
|
|
10
|
-
text: string;
|
|
11
|
-
};
|
|
12
|
-
export type Placeholder = {
|
|
13
|
-
id: string;
|
|
14
|
-
saveTo: string;
|
|
15
|
-
highlight?: string[];
|
|
16
|
-
annotations?: Annotation[];
|
|
17
|
-
redact?: string[];
|
|
18
|
-
};
|
|
19
|
-
export type ResolvedAnnotations = {
|
|
20
|
-
id: string;
|
|
21
|
-
rawPath: string;
|
|
22
|
-
finalPath: string;
|
|
23
|
-
highlight: {
|
|
24
|
-
selector: string;
|
|
25
|
-
bbox: Bbox;
|
|
26
|
-
}[];
|
|
27
|
-
annotations: {
|
|
28
|
-
label: string;
|
|
29
|
-
text: string;
|
|
30
|
-
selector: string;
|
|
31
|
-
bbox: Bbox;
|
|
32
|
-
}[];
|
|
33
|
-
redact: {
|
|
34
|
-
selector: string;
|
|
35
|
-
bbox: Bbox;
|
|
36
|
-
}[];
|
|
37
|
-
};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/** Print a one-line update hint (from the cached answer) when a newer d360-capture is published, then
|
|
2
|
-
kick off a background cache refresh for next time. Best-effort; never blocks or throws. */
|
|
3
|
-
export declare function maybeNudgeCaptureUpdate(currentVersion: string): void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function looksLikeSessionExpiry(output: string): boolean;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export type RuntimeConfigArgs = {
|
|
2
|
-
cwd: string;
|
|
3
|
-
profileName: string;
|
|
4
|
-
captureDir: string;
|
|
5
|
-
viewport: {
|
|
6
|
-
width: number;
|
|
7
|
-
height: number;
|
|
8
|
-
};
|
|
9
|
-
storageStatePath: string;
|
|
10
|
-
};
|
|
11
|
-
export type RuntimeConfigPaths = {
|
|
12
|
-
configPath: string;
|
|
13
|
-
tsconfigPath: string;
|
|
14
|
-
};
|
|
15
|
-
export declare function writeRuntimePlaywrightConfig(args: RuntimeConfigArgs): RuntimeConfigPaths;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export type RunSpecsArgs = {
|
|
2
|
-
cwd: string;
|
|
3
|
-
configPath: string;
|
|
4
|
-
tsconfigPath: string;
|
|
5
|
-
startUrl: string;
|
|
6
|
-
authBoundaryUrlPattern: string;
|
|
7
|
-
specId?: string;
|
|
8
|
-
captureDirAbs: string;
|
|
9
|
-
/** Abort the in-flight Playwright run (hard interrupt) — kills the child process. */
|
|
10
|
-
signal?: AbortSignal;
|
|
11
|
-
/** Prepared capture scope (key→value), passed to specs as CAPTURE_SCOPE (JSON). */
|
|
12
|
-
scope?: Record<string, string>;
|
|
13
|
-
};
|
|
14
|
-
export type RunOutcome = 'passed' | 'failed' | 'skipped';
|
|
15
|
-
export type RunSpecsResult = {
|
|
16
|
-
exitCode: number;
|
|
17
|
-
stdout: string;
|
|
18
|
-
stderr: string;
|
|
19
|
-
/** Distinguishes a skipped spec (exit 0 + "skipped") from a real pass — so a prerequisite
|
|
20
|
-
skip isn't miscounted as a capture. */
|
|
21
|
-
outcome: RunOutcome;
|
|
22
|
-
};
|
|
23
|
-
/** Classify a single-spec run: nonzero → failed; else "skipped" only if Playwright reported a
|
|
24
|
-
skip and no pass; otherwise passed. (Per-spec mode runs exactly one test, so it's unambiguous.) */
|
|
25
|
-
export declare function classifyOutcome(exitCode: number, stdout: string): RunOutcome;
|
|
26
|
-
export declare function runSpecs(args: RunSpecsArgs): Promise<RunSpecsResult>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|