ostia 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.
@@ -0,0 +1,25 @@
1
+ // @bun
2
+ import{T,l,q,ae,n,r,me,W,i,Y,Qe,u,E,a,v,L,m,t,O}from"./chunk-5m601yfv.js";import{x,Ke,qe,fe,d,te,an,A,Ye,mn,ge,_,V,J,cn,he,z,Ze,re,K,oe,be,M,h}from"./chunk-pdv5zb3w.js";import{rn,Ge,Ve,Je,ze,on,ee,sn}from"./chunk-scvezzm9.js";import{y}from"./chunk-fc36fd0b.js";import{writeSync as Re}from"fs";import{resolve as xn}from"path";function Ne(o){return n("cache",o.workloadId,o.phase,o.configFingerprint,o.bunVersion,o.toolVersion,o.instrumented,o.inputsDigest??null)}var yn=32;async function De(o,e=process.cwd()){if(o.length===0)return;let s=await z(o,e),c=Array(s.length),p=0,g=async()=>{while(p<s.length){let C=p++,f=s[C],w=await Bun.file(xn(e,f)).arrayBuffer();c[C]={path:f,sha256:Bun.CryptoHasher.hash("sha256",w,"hex")}}};return await Promise.all(Array.from({length:Math.min(yn,s.length)},g)),n("inputs",c)}function Ie(o,e){return`${o}/cache/${e}.json`}async function Be(o,e){try{return await Bun.file(Ie(o,e)).json()}catch{return}}async function _e(o,e,s){await Bun.write(Ie(o,e),`${JSON.stringify(s,null,2)}
3
+ `)}var Le=600000;class de extends l{}class Ce extends de{path;constructor(o){super(`No baseline document at ${o}. Create one with: ostia baseline save ${o.split("/").pop().replace(/\.json$/,"")}`);this.path=o}}class Ue extends de{path;missing;total;constructor(o,e,s){super(`${e} of ${s} configured workload(s) have no matching row in baseline ${o} (by workload id). Re-seed with: ostia baseline save`);this.path=o;this.missing=e;this.total=s}}async function An(o,e,s,c){let p=await z(e.suites,process.cwd());if(p.length===0)throw new l(`Workload suites ${JSON.stringify(e.suites)} matched no files.`);let g=await re({suites:p,preload:[],bunFlags:[],noiseCheck:!1},o.bench),C=await K({...g,outDir:g.outDir??o.outDir,timeoutMs:e.timeoutMs??g.timeoutMs??Le,signal:c}),f=[];for(let w of C.workloads){let k=C.measurements.find((S)=>S.workloadId===w.id&&S.phase==="timing");if(!k)continue;if(s)k.warnings.push(s);f.push({workload:w,status:"executed",run:k,harnessFailed:!1})}return f}async function $n(o,e,s,c,p){let g=me(e.command,e.label,{prepare:e.prepare,timeSource:e.timeSource}),C=i({samples:o.samples??null,budgetMs:o.budgetMs??null,minSamples:o.minSamples??null,warmup:o.warmup}),f=e.inputs!==void 0&&typeof e.prepare!=="function",w=Ne({workloadId:g.id,phase:"timing",configFingerprint:C,bunVersion:Bun.version,toolVersion:ae,instrumented:!1,inputsDigest:await De(e.inputs??[])}),k=s||!f?void 0:await Be(o.outDir,w),S;if(k)S=k;else{let N=await mn({argv:e.command,samples:o.samples,budgetMs:o.budgetMs,minSamples:o.minSamples,warmup:o.warmup,prepare:e.prepare,timeSource:e.timeSource,timeoutMs:e.timeoutMs??Le,ignoreExitCodes:e.ignoreExitCodes,signal:p});if(S=W({workload:g,configFingerprint:C,trials:N.trials,timing:N.timing,warnings:c?[...N.warnings,c]:N.warnings}),f&&!p?.aborted)await _e(o.outDir,w,S)}return{workload:g,status:k?"cached":"executed",run:S,harnessFailed:ge(S,e.ignoreExitCodes)}}async function ve(o,e,s){let{environment:c,noiseWarning:p}=O(o.noiseCheck),g=[];for(let w of o.workloads){if(s?.aborted)break;if(w.suites)g.push(...await An(o,w,p,s));else g.push(await $n(o,w,e,p,s))}let C=s?.aborted??!1,f=g.at(-1)?.run;if(C&&f&&!f.warnings.some((w)=>w.code==="aborted"))f.warnings=[...f.warnings,oe()];if(g.some((w)=>w.status==="executed"))v(c);return{results:g,environment:c,aborted:C}}async function We(o){let{config:e}=o,s=A(e,o.baselineName);if(!await Bun.file(s).exists())throw new Ce(s);let c=await a(s),{results:p,environment:g,aborted:C}=await ve(e,o.full,o.signal),f=p.map((F)=>({...F})),w=f.filter((F)=>F.status==="executed").length,k=r(f.map((F)=>F.workload),f.map((F)=>F.run),g);if(C)return{document:k,summary:{total:f.length,cached:f.length-w,executed:w,passed:0,regressed:0,missingBaseline:0,failed:0,results:f,unmatched:{baseOnly:[],candOnly:[]}},baseline:c,aborted:C};let S=Ke(c,k,e.thresholds),N=[];for(let F of f){let ie=S.compare(F.workload.id);if(ie)F.comparison=ie,N.push([F.workload.id,ie])}let D=N.map(([,F])=>F),P=f.length-D.length,I=D.filter((F)=>F.verdict==="pass").length,B=e.onMissingBaseline??(P===f.length?"fail":"warn");if(P>0&&B==="fail")throw new Ue(s,P,f.length);let j=S.unmatched();return k.comparisons=D,k.unmatched={baseOnly:j.baseOnly.map((F)=>F.id),candOnly:j.candOnly.map((F)=>F.id)},k.compared=S.compared(N),k.comparisonSummary=qe(D,S.effectiveTimingPct,S.driftPct),{document:k,summary:{total:f.length,cached:f.length-w,executed:w,passed:I,regressed:D.length-I,missingBaseline:P,failed:f.filter((F)=>F.harnessFailed).length,results:f,unmatched:j},baseline:c,aborted:!1}}function je(o){let e=[`${o.total} workloads`,`${o.cached} cached`,`${o.executed} executed`];if(o.missingBaseline>0)e.push(`${o.missingBaseline} skipped (no matching baseline workload)`);if(o.failed>0){let c=o.results.filter((p)=>p.harnessFailed).map((p)=>m(p.workload));e.push(`${o.failed} failed (harness error, a command exited non-zero or produced no samples: ${c.join(", ")})`)}let s=o.results.filter((c)=>c.comparison?.verdict==="fail").map((c)=>{let p=c.comparison.timing,g=m(c.workload);return p?`${t(p.medianDeltaPct)} median on ${g}`:g});return e.push(`${o.passed} passed ${o.regressed} regressed${s.length>0?` (${s.join(", ")})`:""}`,"",`Profile CI: ${o.regressed>0||o.failed>0?"\u2717":"\u2713"}`),`${e.join(`
4
+ `)}
5
+ `}async function He(o,e){let{results:s,environment:c}=await ve(o,!0),p=r(s.map((C)=>C.workload),s.map((C)=>C.run),c),g=A(o,e);return await u(p,g),g}async function Xe(o){let e;try{e=(await Array.fromAsync(new Bun.Glob("*.json").scan({cwd:o.baselineDir}))).map((p)=>p.replace(/\.json$/,"")).sort()}catch{return[]}return(await Promise.all(e.map(async(c)=>{let p=A(o,c);try{let g=await a(p);return{name:c,path:p,createdAt:g.createdAt,toolVersion:g.toolVersion,bunVersion:g.bunVersion,workloads:g.workloads.length,...g.git!==void 0&&{git:g.git}}}catch{return}}))).filter((c)=>c!==void 0)}class H extends Error{}var en=["table","json","jsonl","markdown","minimal"],ke=["collapsed","mermaid","speedscope","cpuprofile"],nn=[...en,...ke];function Sn(o,e,s={}){if(s.allowAuto&&e==="auto")return Ze();let c=s.min??1,p=e?.trim()===""?Number.NaN:Number(e);if(!Number.isInteger(p)||p<c)throw new H(`Invalid ${o} "${e}": expected an integer \u2265 ${c}${s.allowAuto?' (or "auto")':""}`);return p}function Pn(o,e){return e.split(",").map((s)=>{let c=/^\d+$/.test(s)?Number(s):Number.NaN;if(!(c<=255))throw new H(`Invalid ${o} "${e}": expected exit codes 0-255, comma-separated`);return c})}var X=(o,e)=>({dest:o,spec:e}),b={str:(o)=>X(o,{kind:"string"}),int:(o,e=1,s=!1)=>X(o,{kind:"int",min:e,allowAuto:s}),num:(o,e)=>X(o,{kind:"number",min:e}),on:(o)=>X(o,{kind:"bool",value:!0}),off:(o)=>X(o,{kind:"bool",value:!1}),oneOf:(o,e)=>X(o,{kind:"enum",values:e}),list:(o)=>X(o,{kind:"list"}),words:(o)=>X(o,{kind:"words"}),exitCodes:(o)=>X(o,{kind:"exitCodes"})},Oe={format:"table",quiet:!1,help:!1},pe={...Oe,noiseCheck:!0},xe={"--help":b.on("help"),"-h":b.on("help")},ye={...xe,"--export-json":b.str("exportJson"),"--format":b.oneOf("format",en),"--quiet":b.on("quiet")},Te={"--no-noise-check":b.off("noiseCheck")},le={"--config":b.str("config")},Fe={...ye,...Te,"--timeout":b.int("timeoutMs"),"--out-dir":b.str("outDir")},Ee={...Fe,...le,"--filter":b.str("filter"),"--preload":b.list("preload"),"--bun-flags":b.words("bunFlags")};function ne(o,{command:e,flags:s,args:c,positional:p,rest:g}){let C=c;for(let f=0;f<o.length;f++){let w=o[f];if(w==="--"&&g){g(o.slice(f+1),c);break}let k=w.startsWith("--")?w.indexOf("="):-1,S=k>0?w.slice(0,k):w,N=Object.hasOwn(s,S)?s[S]:void 0;if(!N||k>0&&N.spec.kind==="bool"){if(w.startsWith("-")||!p)throw new H(`Unknown flag "${w}" for "ostia ${e}".`);p(w,c);continue}let D=()=>{if(k>0)return w.slice(k+1);if(f+1>=o.length)throw new H(`Missing value for ${S}.`);return o[++f]},{dest:P,spec:I}=N;switch(I.kind){case"string":C[P]=D();break;case"int":C[P]=Sn(S,D(),I);break;case"number":{let B=D(),j=Number(B);if(B===""||!Number.isFinite(j)||j<I.min)throw new H(`Invalid ${S} "${B}": expected a number \u2265 ${I.min}`);C[P]=j;break}case"bool":C[P]=I.value;break;case"list":C[P].push(D());break;case"words":C[P].push(...q(D()));break;case"exitCodes":C[P].push(...k>0?Pn(S,w.slice(k+1)):Array.from({length:255},(B,j)=>j+1));break;case"enum":{let B=D();if(!I.values.includes(B))throw new H(`Invalid ${S} "${B}": expected one of: ${I.values.join(", ")}`);C[P]=B;break}}}return c}var U=(o)=>Bun.write(Bun.stdout,o),tn=(o)=>Bun.write(Bun.stderr,o);class R extends Error{code;data;constructor(o,e,s){super(e);this.code=o;this.data=s}}async function Ae(o,e,s){try{return await s()}catch(c){if(c instanceof R)throw c;if(c instanceof l)throw new R("invalid-flag",`${e}: ${c.message}`);throw new R(o,`${e}: ${T(c)}`)}}var ln=new Set(["minimal","json","jsonl"]);async function vn(){let{isatty:o}=await import("tty");if(!o(2))return!0;let e=process.argv.slice(2);return e.some((s,c)=>s==="--format"&&ln.has(e[c+1]??"")||s.startsWith("--format=")&&ln.has(s.slice(9)))}async function se(o,e,s){if(await tn(e.endsWith(`
6
+ `)?e:`${e}
7
+ `),!await vn())return;await tn(`${JSON.stringify({event:"error",protocolVersion:M,code:o,message:e.split(`
8
+ `)[0],...s&&{data:s}})}
9
+ `)}async function G(o,e){return await U(o),e?0:2}async function ce(o,e){if(o.text)await U(o.text);if(!o.files||o.files.length===0)return;if(e)for(let s of o.files){let c=s.path?`${e}/${s.path}`:e;await Bun.write(c,s.content),await U(`wrote ${c}
10
+ `)}else if(o.files.length===1)await U(o.files[0].content);else for(let s of o.files)await U(`--- ${s.path??"(unnamed)"} ---
11
+ ${s.content}
12
+ `)}async function Me(o,{exportJson:e,format:s,quiet:c}){let p=s==="json"&&!c?Y(o):void 0;if(e)if(p!==void 0)await Qe(p,e);else await u(o,e);return p}async function ue(o,e,s={}){let c=await Me(o,e);if(e.quiet)return;if(c!==void 0)await U(c);else await ce(await h[e.format].render(o,s))}var fn=(o)=>o==="--help"||o==="-h";async function $e(o,e){let s=new AbortController,c=()=>s.abort();process.on("SIGINT",c);try{return{result:await Ae("spawn-failed",o,()=>e(s.signal)),aborted:s.signal.aborted}}finally{process.off("SIGINT",c)}}async function Se(o){let e=await an(o);if(!e&&o!==void 0)throw new R("config-missing",`Config file ${o} not found.`);return e}async function Pe(o,e){let s=await Se(e);if(!s){let c=te.join(" or ");throw new R("config-missing",o?`No ${c} found. "${o}" needs configured workloads.`:`No ${c} found.`)}if(o&&s.workloads.length===0){let c=e??await Ye()??te[1];throw new R("config-missing",`${c} has no "workloads" configured.`)}return s}function pn(o){return o.matched===0?2:o.verdict==="fail"?1:0}var On=["ns","us","ms","s"],Tn={...Fe,"--samples":b.int("samples"),"--budget":b.int("budgetMs"),"--min-samples":b.int("minSamples"),"--warmup":b.int("warmup",0),"--no-interleave":b.off("interleave"),"--prepare":b.list("prepare"),"--time-source":b.str("timeSource"),"--time-unit":b.oneOf("timeUnit",On),"--cpu":b.on("cpu"),"--heap":b.on("heap"),"--cpu-interval":b.int("cpuIntervalUs"),"--ignore-failure":b.exitCodes("ignoreExitCodes")};function Fn(o){let e={...pe,commands:[],argvCommand:[],prepare:[],interleave:!0,cpu:!1,heap:!1,ignoreExitCodes:[]};return ne(o,{command:"time",flags:Tn,args:e,positional:(s,c)=>c.commands.push(s),rest:(s,c)=>{c.argvCommand=s}})}async function En(o){let e=Fn(o),s=[...e.commands,...e.argvCommand.length>0?[e.argvCommand]:[]];if(e.help||s.length===0)return G(rn,e.help);if(e.prepare.length>1&&e.prepare.length!==s.length)throw new R("invalid-flag",`--prepare given ${e.prepare.length} times for ${s.length} command(s): give it once (applies to all) or once per command.`);if(e.timeUnit!==void 0&&e.timeSource===void 0)throw new R("invalid-flag","--time-unit only applies with --time-source.");if(e.cpuIntervalUs!==void 0&&!e.cpu)throw new R("invalid-flag","--cpu-interval only applies with --cpu.");if(e.timeSource!==void 0)try{new RegExp(e.timeSource)}catch(k){throw new R("invalid-flag",`Invalid --time-source regex: ${T(k)}`)}let c=e.timeSource!==void 0?{pattern:e.timeSource,unit:e.timeUnit}:void 0,p=s.map((k,S)=>({command:k,prepare:e.prepare.length===1?e.prepare[0]:e.prepare[S]})),{result:g,aborted:C}=await $e("Run failed",(k)=>be({commands:p,timeSource:c,samples:e.samples,budgetMs:e.budgetMs,minSamples:e.minSamples,warmup:e.warmup,interleave:e.interleave,cpu:e.cpu,heap:e.heap,cpuIntervalUs:e.cpuIntervalUs,timeoutMs:e.timeoutMs,ignoreExitCodes:e.ignoreExitCodes,outDir:e.outDir,noiseCheck:e.noiseCheck,signal:k}));if(await ue(g,e),C)return 130;let f=g.workloads.map((k)=>g.measurements.find((S)=>S.workloadId===k.id&&S.phase==="timing")),w=f.some((k)=>k?.timing&&ge(k,e.ignoreExitCodes));if(w||f.some((k)=>!k?.timing)){let k=(N)=>g.measurements.some((D)=>D.warnings.some((P)=>P.code===N)),S=w?"command-failed":k("timeout")?"timeout":k("time-source-no-match")?"time-source-no-match":"command-failed";throw new R(S,"One or more commands failed to produce a clean measurement; see the report above for details.")}return 0}var Mn={...Ee,"--budget":b.int("budgetMs"),"--samples":b.int("samples"),"--min-samples":b.int("minSamples"),"--jobs":b.int("jobs",1,!0),"--gc":b.on("gc"),"--no-gc":b.off("gc"),"--cpu":b.on("cpu"),"--no-cpu":b.off("cpu"),"--cpu-interval":b.int("cpuIntervalUs"),"--alloc":b.on("alloc"),"--no-alloc":b.off("alloc"),"--peak-mem":b.on("peakMem"),"--no-peak-mem":b.off("peakMem"),"--isolate":b.on("isolate"),"--no-isolate":b.off("isolate")};function Rn(o){let e={...pe,suites:[],preload:[],bunFlags:[]};return ne(o,{command:"bench",flags:Mn,args:e,positional:(s,c)=>c.suites.push(s)})}async function Nn(o){let e=Rn(o);if(e.help)return G(Ge,!0);let s=await Se(e.config),c=await re(e,s?.bench);if(c.suites.length===0)return G(Ge,!1);let{result:p,aborted:g}=await $e("Bench failed",(C)=>K({...c,signal:C}));return await ue(p,e),g?130:0}var Dn={...Ee,"--base":b.str("base"),"--base-setup":b.list("baseSetup"),"--rounds":b.int("rounds",3),"--threshold":b.num("thresholdPct",0),"--geomean-threshold":b.num("geomeanThresholdPct",0),"--confirm":b.int("confirm",0),"--alloc":b.on("alloc"),"--no-alloc":b.off("alloc"),"--peak-mem":b.on("peakMem"),"--no-peak-mem":b.off("peakMem"),"--mem-threshold":b.num("memThresholdPct",0),"--keep-trees":b.int("keepTrees"),"--clean":b.on("clean"),"--progress":b.on("progress"),"--no-progress":b.off("progress")};function In(o){switch(o.phase){case"setup":return`[ab] base setup: ${o.command}`;case"measure":return`[ab] suite ${o.suite}/${o.suites} ${o.file} \xB7 pass ${o.pass}/${o.passes} \xB7 task ${o.task}/${o.tasks} ${o.label}`;case"confirm":return`[ab] confirming flagged tasks \xB7 repeat ${o.repeat}/${o.repeats} ${o.label}`;case"memory":return`[ab] memory \xB7 process ${o.run}/${o.runs} ${y(o.side)} ${o.label}`}}function Bn(o){let e=!1;return{onProgress:(s)=>{let c=In(s);if(!o){Re(2,`${c}
13
+ `);return}let p=process.stderr.columns||80;Re(2,`\r\x1B[K${c.slice(0,p-1)}`),e=!0},done:()=>{if(e)Re(2,"\r\x1B[K");e=!1}}}function _n(o){let e={...pe,suites:[],baseSetup:[],preload:[],bunFlags:[],clean:!1};return ne(o,{command:"ab",flags:Dn,args:e,positional:(s,c)=>c.suites.push(s)})}async function Ln(o){let e=_n(o);if(e.help)return G(Ve,!0);let s=await Se(e.config);if(e.clean){let Z=await cn({outDir:e.outDir??s?.bench?.outDir});if(!e.quiet)await Bun.write(Bun.stderr,`Removed ${Z} cached base tree(s).
14
+ `);return 0}let{suites:c,filter:p,preload:g,bunFlags:C,timeoutMs:f,outDir:w,noiseCheck:k}=await re(e,s?.bench);if(c.length===0)return G(Ve,!1);let{isatty:S}=await import("tty"),N=S(2),D=e.progress??(N&&!e.quiet)?Bn(N):void 0,{result:P,aborted:I}=await $e("A/B run failed",async(Z)=>{try{return await he({suites:c,base:e.base,baseSetup:e.baseSetup.length>0?e.baseSetup:s?.ab?.setup,rounds:e.rounds,thresholdPct:e.thresholdPct,geomeanThresholdPct:e.geomeanThresholdPct,confirm:e.confirm,alloc:e.alloc??s?.ab?.alloc,peakMem:e.peakMem??s?.ab?.peakMem,memThresholdPct:e.memThresholdPct??s?.ab?.memThresholdPct,filter:p,preload:g,bunFlags:C,timeoutMs:f,outDir:w,keepTrees:e.keepTrees??s?.ab?.keepTrees,noiseCheck:k,signal:Z,onProgress:D?.onProgress})}catch(Q){if(Q instanceof _)throw new R("invalid-flag",`--base: ${Q.message}`);if(Q instanceof J)throw new R("command-failed",Q.message);if(Q instanceof V)throw new R("suite-failed",Q.message,{side:Q.side});throw Q}finally{D?.done()}}),B=P.ab,j=I?130:pn({matched:B.matched+B.threw,verdict:B.verdict}),F=new Map(P.workloads.map((Z)=>[Z.id,Z])),ie=(Z=[])=>Z.flatMap((Q)=>F.get(Q)??[]);if(await ue(P,e,{protocol:{command:"ab",exitCode:j,unmatched:{baseOnly:ie(P.unmatched?.baseOnly),candOnly:ie(P.unmatched?.candOnly)},...e.exportJson&&{exportedTo:e.exportJson}}}),j===2){let Z=B.newSuites?.length===c.length;await se("no-matches",Z?`No suite exists at ${B.base.ref}; nothing was paired. Use "ostia bench" to time new suites.`:`No task exists both at ${B.base.ref} and in the working tree; nothing was paired.`)}return j}var Un={...ye,...le,"--baseline":b.str("baseline")};function Wn(o){let e={...Oe,paths:[]};return ne(o,{command:"compare",flags:Un,args:e,positional:(s,c)=>c.paths.push(s)})}async function Gn(o){let e=Wn(o);if(e.help)return G(Je,!0);let s=e.baseline?1:2;if(e.paths.length>s)throw new H(e.baseline?`"ostia compare --baseline" takes one candidate path, got ${e.paths.length}.`:`"ostia compare" takes two document paths, got ${e.paths.length}.`);let[c,p]=e.baseline?[e.baseline,e.paths[0]]:e.paths;if(!c||!p)return G(Je,!1);let[g,C]=await Ae("document-load-failed","Failed to load documents",()=>Promise.all([a(c),a(p)])),f=await Se(e.config),w=f?.thresholds??x,k=f?e.config??await Ye()??te[1]:"defaults",S=fe(g,C,w),N={...C,comparisons:S.comparisons,comparisonSummary:S.summary,unmatched:{baseOnly:S.unmatched.baseOnly.map((I)=>I.id),candOnly:S.unmatched.candOnly.map((I)=>I.id)},compared:S.compared};if(!e.quiet&&(e.format==="table"||e.format==="markdown")){if(await U(`thresholds: ${k}
15
+ `),g.git&&C.git)await U(`base ${L(g.git)} \u2192 cand ${L(C.git)}
16
+ `)}let P=pn(S.summary);if(await ue(N,e,{protocol:{command:"compare",exitCode:P,unmatched:S.unmatched,baseGit:g.git,candGit:C.git,...e.exportJson&&{exportedTo:e.exportJson}}}),S.summary.matched===0)await se("no-matches","No workload matched between base and candidate; nothing was compared.");return P}var gn={...xe,"--format":b.oneOf("format",nn),"--measurement":b.str("measurementId"),"--out-dir":b.str("outDir")};function jn(o){return ne(o,{command:"report",flags:gn,args:{format:"table",help:!1},positional:(s,c)=>{if(c.path!==void 0)throw new H(`"ostia report" takes exactly one document path, got "${c.path}" and "${s}".`);c.path=s}})}var un="No CPU evidence in this document; rerun with --cpu (ostia time) or --cpu (ostia bench)";async function hn(o,{format:e,measurementId:s,outDir:c}){let p=ke.includes(e),g=[s!==void 0&&"--measurement",c!==void 0&&"--out-dir"].filter((w)=>w!==!1);if(!p&&g.length>0)throw new H(`${g.join(" and ")} only apply to the visualization formats (${ke.join(", ")}), not --format ${e}.`);let C=await Ae("document-load-failed",`Failed to load ${o}`,()=>a(o));if(p&&!s&&!C.measurements.some((w)=>w.phase==="cpu"))throw new R("no-cpu-evidence",un);let f=await h[e].render(C,{measurementId:s});if(!f.text&&(!f.files||f.files.length===0))throw new R("no-cpu-evidence",s?`No CPU evidence found for measurement "${s}".`:un);return await ce(f,c),0}async function Hn(o){let e=jn(o);if(e.help||!e.path)return G(ze,e.help);return hn(e.path,e)}var Vn={...ye,...Te,...le,"--full":b.on("full"),"--baseline":b.str("baseline"),"--save-baseline":b.on("saveBaseline")};function qn(o){let e={...pe,full:!1,saveBaseline:!1};return ne(o,{command:"ci",flags:Vn,args:e})}var bn=(o)=>new R("baseline-missing",o.message);function Jn(o){if(o instanceof de)throw bn(o);if(o instanceof E)throw new R("document-load-failed",o.message);throw o}async function Kn(o){let e=qn(o);if(e.help)return G(on,!0);let s=await Pe("ostia ci",e.config),c=e.noiseCheck?s:{...s,noiseCheck:!1},p=e.full||e.saveBaseline,{result:g,aborted:C}=await $e("CI run failed",(P)=>We({config:c,full:p,baselineName:e.baseline,signal:P}).catch(Jn)),{document:f,summary:w}=g;if(C)return await ue(f,e),130;let k=e.baseline??c.baseline,S=A(c,k),N=await Me(f,e);if(e.saveBaseline&&w.regressed===0&&w.failed===0)await u(f,S);let D=w.failed>0?2:w.regressed>0?1:0;if(w.failed>0){let P=w.results.filter((I)=>I.harnessFailed).map((I)=>m(I.workload));await se("command-failed",`${w.failed} workload(s) failed (harness error, a command exited non-zero or produced no samples): ${P.join(", ")}`)}if(e.quiet)return D;if(N!==void 0)await U(N);else if(e.format==="table"){if(await U(je(w)),f.comparisons&&f.comparisons.length>0)await ce(await h.table.render(f,{}))}else if(e.format==="markdown")await U(`## ostia ci
17
+
18
+ Baseline: \`${k}\` (\`${S}\`) \xB7 ${w.cached} cached, ${w.executed} executed
19
+
20
+ `),await ce(await h.markdown.render(f,{}));else await ce(await h[e.format].render(f,{protocol:{command:"ci",exitCode:D,unmatched:w.unmatched,baseGit:g.baseline.git,candGit:f.git,baseline:{name:k,path:S},cached:w.cached,executed:w.executed,failed:w.failed,missingBaseline:w.missingBaseline,...e.exportJson&&{exportedTo:e.exportJson}}}));return D}var dn=/^[A-Za-z0-9._-]+$/;function we(o){return new R("invalid-flag",`${o}
21
+ Run 'ostia baseline --help'.`)}function wn(o){if(o.startsWith("-"))throw we(`Invalid baseline name "${o}": names can't start with "-".`);if(!dn.test(o))throw we(`Invalid baseline name "${o}": expected to match ${dn}.`)}var zn={...xe,...le};function Cn(o,e,s){return ne(o,{command:e,flags:zn,args:{help:!1},positional:(c,p)=>{if(!s)throw we(`"ostia ${e}" takes no arguments, got "${c}".`);if(p.name!==void 0)throw we(`"ostia ${e}" takes at most one name argument, got "${p.name}" and "${c}".`);p.name=c}})}async function Zn(o){let{name:e,help:s,config:c}=Cn(o,"baseline save",!0);if(s)return G(ee,!0);if(e!==void 0)wn(e);let p=await Pe("ostia baseline save",c);return await U(`Wrote ${await He(p,e)}
22
+ `),0}async function Yn(o){let{help:e,config:s}=Cn(o,"baseline list",!1);if(e)return G(ee,!0);let c=await Pe(void 0,s),p=await Xe(c);if(p.length===0)return await U(`No baselines found in ${c.baselineDir}.
23
+ `),0;for(let g of p){let C=g.git?` ${L(g.git)}`:"";await U(`${g.name} ${g.workloads} workloads created ${g.createdAt} toolVersion ${g.toolVersion}${C}
24
+ `)}return 0}async function Qn(o){let[e,...s]=o;if(!e||fn(e))return G(ee,!!e);wn(e);let c=ne(s,{command:"baseline show",flags:{...gn,...le},args:{format:"table",help:!1},positional:(C)=>{throw we(`"ostia baseline show" takes one name, got "${e}" and "${C}".`)}});if(c.help)return G(ze,!0);let p=await Pe(void 0,c.config),g=A(p,e);if(!await Bun.file(g).exists())throw bn(new Ce(g));return hn(g,c)}async function kn(o,e,s,c,p,g=""){if(o===void 0||fn(o))return G(c,o!==void 0);let C=Object.hasOwn(s,o)?s[o]:void 0;try{if(!C)throw new R("invalid-flag",p(o));return await C(e)}catch(f){if(f instanceof R)await se(f.code,f.message,f.data);else if(f instanceof d)await se("config-invalid",f.message);else if(f instanceof H)await se("invalid-flag",`${f.message}
25
+ Run 'ostia ${g}${o} --help'.`);else await se("internal",`Internal error: ${f instanceof Error?f.stack??f.message:String(f)}`);return 2}}function Xn(o){let[e,...s]=o;return kn(e,s,{save:Zn,list:Yn,show:Qn},ee,(c)=>`Unknown "ostia baseline ${c}". Run "ostia baseline --help".`,"baseline ")}function zt(){let[o,...e]=process.argv.slice(2);return kn(o,e,{time:En,bench:Nn,ab:Ln,compare:Gn,report:Hn,ci:Kn,baseline:Xn},sn,(s)=>`Unknown subcommand "${s}". Run "ostia --help".`)}export{zt as main};
@@ -0,0 +1,229 @@
1
+ // @bun
2
+ var rn=`ostia time [flags] <command...>
3
+ ostia time [flags] -- <argv...>
4
+
5
+ Time commands as subprocesses. Each <command> is whitespace-split into argv (no shell);
6
+ everything after -- is one more command's argv, verbatim.
7
+
8
+ Flags:
9
+ --samples N exact trials per command (default: ~3s budget, at least 10)
10
+ --budget MS sampling time budget per command
11
+ --min-samples N floor on trials when --samples isn't given
12
+ --warmup N discarded warmup trials (default: 3)
13
+ --no-interleave run commands one after another instead of round-robin
14
+ --prepare CMD run before every trial, unmeasured; once, or once per command
15
+ --time-source REGEX take each trial's time from capture group 1 in the command's output
16
+ --time-unit UNIT unit of --time-source: ns | us | ms | s (default: ms); needs --time-source
17
+ --cpu capture one extra CPU-profile trial
18
+ --heap capture one extra heap-snapshot trial
19
+ --cpu-interval US CPU sampling interval (default: 1000); needs --cpu
20
+ --timeout MS kill a trial or --prepare hook after MS
21
+ --ignore-failure[=CODE,...] treat these exit codes (bare: all) as success
22
+ --out-dir PATH artifact directory (default: node_modules/.cache/ostia)
23
+ --no-noise-check skip the ~200ms noise-floor measurement
24
+ --export-json PATH write the ProfileDocument to PATH
25
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
26
+ --quiet don't print the report
27
+ --help show this message
28
+
29
+ A command stops at its first non-ignored non-zero exit.
30
+ Exit codes: 0 ok, 2 a command failed or produced no samples (or a bad flag), 130 Ctrl-C.
31
+
32
+ Examples:
33
+ ostia time "bun a.ts" "bun b.ts"
34
+ ostia time --samples 25 --cpu --heap "bun src/server.ts"
35
+ ostia time --prepare "rm -rf dist" "bun build.ts"
36
+ ostia time --time-source "built in (\\d+)ms" "bun build.ts"
37
+ ostia time -- bun -e "console.log('a b')"
38
+ `,Ge=`ostia bench [flags] <suite.ts...>
39
+
40
+ Run in-process group()/task() suites, each file in its own child process. With no files,
41
+ uses ostia.config's "bench" section; each flag overrides its config field.
42
+
43
+ Flags:
44
+ --budget MS sampling budget per task (default: 500)
45
+ --samples N exact trials per task (ignores --budget)
46
+ --min-samples N floor on trials (default: cost-aware, 3-20)
47
+ --jobs N|auto suite files at once (default: 1; >1 adds noise)
48
+ --isolate one process per task, not per file (--no-isolate to undo config)
49
+ --gc Bun.gc(true) between trials (--no-gc)
50
+ --cpu extra per-task CPU profile with JIT tiers, ~2,000 samples (--no-cpu)
51
+ --cpu-interval US CPU sampling interval (default: 100)
52
+ --alloc extra per-task retained-heap-per-call measurement: what calls keep
53
+ alive after a full GC, not what they allocate (--no-alloc)
54
+ --peak-mem extra per-task RSS rise during the task's first call, garbage
55
+ included; median of 3 fresh processes (--no-peak-mem)
56
+ --filter REGEX only tasks whose "group/name" matches
57
+ --preload PATH import before each suite file (repeatable, in order)
58
+ --bun-flags FLAGS extra flags for the bun process running each suite (repeatable)
59
+ --timeout MS kill a suite (or isolated task) process after MS
60
+ --out-dir PATH scratch directory (default: node_modules/.cache/ostia)
61
+ --config PATH read this config file instead of ostia.config.ts/.json
62
+ --no-noise-check skip the ~200ms noise-floor measurement
63
+ --export-json PATH write the ProfileDocument to PATH
64
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
65
+ --quiet don't print the report
66
+ --help show this message
67
+
68
+ Per-task/per-group options ({ budgetMs, minSamples, gc, isolate, cpu, alloc, peakMem }) override
69
+ the suite-wide defaults. Exit codes: 0 ok, 2 a suite failed (or a bad flag), 130 Ctrl-C.
70
+
71
+ Examples:
72
+ ostia bench bench/*.ts
73
+ ostia bench bench/*.ts --filter parse --cpu --alloc
74
+ ostia bench --preload ./bench/dom-setup.ts --bun-flags="--conditions=browser" bench/*.ts
75
+ `,Ve=`ostia ab [flags] <suite.ts...>
76
+
77
+ Pair every task on a git ref's committed tree (base) against the working tree (candidate), in
78
+ one process, alternating ~10ms batches, and gate on the per-round time ratio. Drift between
79
+ the two sides cancels within a round. Each suite runs twice, once with each side first, and
80
+ the rounds are pooled. With no files, uses ostia.config's "bench" suites.
81
+
82
+ Flags:
83
+ --base REF git ref for the base side (default: HEAD)
84
+ --base-setup CMD shell command run once in a freshly extracted base tree, e.g. to build
85
+ gitignored files the suites import (repeatable, in order)
86
+ --rounds N base/candidate rounds per task (default: 15)
87
+ --threshold PCT flag a task whose median ratio moves past PCT (default: 10)
88
+ --geomean-threshold PCT fail when the geometric mean of all ratios is slower than PCT
89
+ (default: 1.5)
90
+ --confirm N re-measure each flagged task in N fresh processes (default: 2; 0 skips)
91
+ --alloc also compare retained heap per call on each side (--no-alloc)
92
+ --peak-mem also compare each side's first-call RSS rise (--no-peak-mem); both
93
+ run in 3 fresh processes per side, and the median counts
94
+ --mem-threshold PCT flag a memory reading that moves past PCT of the base's and past
95
+ its noise floor (default: 10)
96
+ --filter REGEX only tasks whose "group/name" matches
97
+ --preload PATH import before each suite file (repeatable, in order)
98
+ --bun-flags FLAGS extra flags for the bun process running each suite (repeatable)
99
+ --timeout MS kill a suite (or repeat) process after MS
100
+ --out-dir PATH scratch directory and base-tree cache (default: node_modules/.cache/ostia)
101
+ --keep-trees N base trees to keep cached, least recently used go first (default: 5)
102
+ --clean remove every cached base tree, then exit
103
+ --config PATH read this config file instead of ostia.config.ts/.json
104
+ --no-noise-check skip the ~200ms noise-floor measurement
105
+ --export-json PATH write the ProfileDocument to PATH
106
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
107
+ --progress print progress to stderr (default: when stderr is a terminal and not
108
+ --quiet); --no-progress turns it off
109
+ --quiet don't print the report
110
+ --help show this message
111
+
112
+ The base tree is cached per commit (and --base-setup commands) under --out-dir. Setup runs
113
+ with the project's node_modules linked in, and OSTIA_AB_SHA and OSTIA_AB_CANDIDATE_DIR (the
114
+ working tree) set. A flagged task counts only when every fresh-process repeat flags it the
115
+ same way. A task that throws isn't timed; one that throws on the candidate side only fails
116
+ the run. A task whose suite file and output both changed reads "not comparable" and stays
117
+ out of the verdict. Exit codes: 0 pass, 1 a confirmed regression, a candidate-only throw,
118
+ the geomean over its threshold or a memory regression, 2 nothing paired or a harness error (not a git repo,
119
+ unknown ref, a failed setup or suite), 130 Ctrl-C.
120
+
121
+ Examples:
122
+ ostia ab bench/parse.bench.ts
123
+ ostia ab bench/*.ts --base origin/main --rounds 21
124
+ ostia ab bench/*.ts --filter parse --format minimal
125
+ ostia ab bench/*.ts --base-setup "bun scripts/generate.ts"
126
+ ostia ab bench/paint.bench.ts --alloc --peak-mem
127
+ `,Je=`ostia compare <base.json> <candidate.json>
128
+ ostia compare <candidate.json> --baseline <base.json>
129
+
130
+ Compare two ProfileDocuments by workload id (or a task's former names, from its
131
+ "aliases"). Gates on ostia.config's "thresholds" when present (the same ones "ostia ci"
132
+ uses), else the defaults, widened by machine noise and drift. Tasks on only one side are
133
+ listed under "Not compared"; with --format json, "compared" holds one row per task.
134
+
135
+ Flags:
136
+ --baseline PATH the base document, when only the candidate is positional
137
+ --config PATH read thresholds from this config file instead of ostia.config.ts/.json
138
+ --export-json PATH write the candidate document with comparisons to PATH
139
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
140
+ --quiet don't print the report
141
+ --help show this message
142
+
143
+ Exactly two document paths (one with --baseline); anything more is an error.
144
+ Exit codes: 0 pass, 1 a workload regressed, 2 nothing matched or a harness error.
145
+ `,ze=`ostia report <document.json> [flags]
146
+
147
+ Render a saved ProfileDocument. Files, not a GUI for the visualization formats -
148
+ hand the output to speedscope.app, flamegraph.pl, or a Mermaid renderer.
149
+
150
+ Formats:
151
+ table terminal timing/CPU/heap/comparison text (default)
152
+ json pretty JSON document
153
+ jsonl one metadata line, then one line per measurement
154
+ markdown agent- and human-readable report
155
+ minimal one compact JSON object per timing measurement, for LLM/CI consumption
156
+ collapsed folded stacks: "root;a;b 42" - flamegraph.pl and most flame tooling
157
+ mermaid call tree, top-15 frames by total time (never the whole profile)
158
+ speedscope sampled profile JSON for speedscope.app
159
+ cpuprofile verbatim .cpuprofile pass-through (cpu-prof/inspector origins only)
160
+
161
+ Flags:
162
+ --format FORMAT one of the formats above (default: table)
163
+ --measurement <id> visualization formats only: render only this measurement
164
+ (default: every CPU measurement in the document)
165
+ --out-dir PATH visualization formats only: write files here instead of stdout
166
+ --help show this message
167
+
168
+ Examples:
169
+ ostia report doc.json
170
+ ostia report doc.json --format markdown
171
+ ostia report doc.json --format speedscope --out-dir node_modules/.cache/ostia/viz
172
+ ostia report doc.json --format collapsed | flamegraph.pl > flame.svg
173
+ `,on=`ostia ci [flags]
174
+
175
+ Run ostia.config's workloads (reusing cached runs whose declared inputs are unchanged),
176
+ compare against a saved baseline, and gate on regressions.
177
+
178
+ Flags:
179
+ --full ignore the cache
180
+ --baseline NAME baseline to compare against (default: config "baseline", or "main")
181
+ --save-baseline after a pass, save this run as the new baseline (always measures
182
+ fresh: the cache is not used)
183
+ --config PATH read this config file instead of ostia.config.ts/.json
184
+ --no-noise-check skip the ~200ms noise-floor measurement
185
+ --export-json PATH write the candidate document with comparisons to PATH
186
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
187
+ --quiet don't print the report
188
+ --help show this message
189
+
190
+ Exit codes: 0 pass, 1 regression, 2 harness error (missing config/baseline, a workload
191
+ failed or produced no samples, or an onMissingBaseline "fail" mismatch), 130 Ctrl-C (the
192
+ partial document is exported; nothing is compared or saved).
193
+ `,ee=`ostia baseline <save|list|show> [args]
194
+
195
+ Manage the baseline ProfileDocuments "ostia ci" gates against and "ostia compare --baseline"
196
+ reads.
197
+
198
+ Flags (every subcommand): --config PATH reads that config file instead of
199
+ ostia.config.ts/.json.
200
+
201
+ Subcommands:
202
+ save [name] measure every configured workload (same code path as "ostia ci",
203
+ no comparison) and write it to <baselineDir>/<name>.json
204
+ (default name: config's "baseline" field, or "main")
205
+ list list saved baselines: name, created date, workload count
206
+ (takes no arguments)
207
+ show <name> [flags] render a saved baseline, like "ostia report" (same
208
+ --format/--measurement/--out-dir flags)
209
+
210
+ Examples:
211
+ ostia baseline save
212
+ ostia baseline save my-feature
213
+ ostia baseline list
214
+ ostia baseline show main
215
+ ostia baseline show main --format markdown
216
+ `,sn=`ostia - profiling and benchmarking for Bun
217
+
218
+ Commands:
219
+ time Time commands N times and report timing/CPU/heap
220
+ bench Run in-process benchmark suites (group()/task())
221
+ ab Pair suites against a git ref in one process, gate on regressions
222
+ compare Compare two ProfileDocuments
223
+ report Render a saved ProfileDocument (table/json/markdown/collapsed/mermaid/speedscope/...)
224
+ ci Run configured workloads against a baseline, gate on regressions
225
+ baseline Manage baseline ProfileDocuments (save/list/show)
226
+
227
+ Run "ostia <command> --help" for details.
228
+ `;
229
+ export{rn,Ge,Ve,Je,ze,on,ee,sn};
package/cli.js CHANGED
@@ -1,226 +1,3 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{e,v,t,R,S,s,_,C,i,I,ee,a,O,o,u,m}from"./chunk-cqy5fzae.js";import{f,re,se,H,N,W,l,oe,A,G,V,le,J,ie,U,z,D,w,d,ae}from"./chunk-t6q9jbb6.js";import{resolve as Ue}from"path";function ve(r){return e("cache",r.workloadId,r.phase,r.configFingerprint,r.bunVersion,r.toolVersion,r.instrumented,r.inputsDigest??null)}async function ye(r,n=process.cwd()){if(r.length===0)return;let c=await V(r,n),p=await Promise.all(c.map(async(h)=>{let g=await Bun.file(Ue(n,h)).arrayBuffer();return{path:h,sha256:Bun.CryptoHasher.hash("sha256",g,"hex")}}));return e("inputs",p)}function Ce(r,n){return`${r}/cache/${n}.json`}async function xe(r,n){let c=Bun.file(Ce(r,n));if(!await c.exists())return;return await c.json()}async function Pe(r,n,c){await Bun.write(Ce(r,n),`${JSON.stringify(c,null,2)}
4
- `)}var $e=600000;class me extends Error{path;constructor(r){super(`No baseline document at ${r}. Create one with: ostia baseline save ${r.split("/").pop().replace(/\.json$/,"")}`);this.path=r}}class de extends Error{path;missing;total;constructor(r,n,c){super(`${n} of ${c} configured workload(s) have no matching row in baseline ${r} (by workload id). Re-seed with: ostia baseline save`);this.path=r;this.missing=n;this.total=c}}function qe(r,n,c){if(r)return r;return n===c?"fail":"warn"}async function ge(r,n){let c=r.noiseCheck===!1?void 0:u(),p=c?m(c):void 0,h=[];for(let g of r.workloads){if(g.suites){let F=await U(g.suites,process.cwd()),K=await z({suites:F,preload:[],bunFlags:[],noiseCheck:!1},r.bench),Z=await D({...K,outDir:K.outDir??r.outDir,timeoutMs:K.timeoutMs??$e});for(let X of Z.workloads){let E=Z.measurements.find((te)=>te.workloadId===X.id&&te.phase==="timing");if(!E)continue;if(p)E.warnings.push(p);h.push({workload:X,status:"executed",run:E,harnessFailed:!1})}continue}let x=_(g.command,g.label,{prepare:g.prepare,timeSource:g.timeSource}),b=await ye(g.inputs??[]),k=i({samples:r.samples??null,budgetMs:r.budgetMs??null,minSamples:r.minSamples??null,warmup:r.warmup}),q=g.inputs!==void 0&&typeof g.prepare!=="function",j=ve({workloadId:x.id,phase:"timing",configFingerprint:k,bunVersion:Bun.version,toolVersion:S,instrumented:!1,inputsDigest:b}),M=n||!q?void 0:await xe(r.outDir,j),P,y;if(M)P=M,y="cached";else{let F=await le({argv:g.command,samples:r.samples,budgetMs:r.budgetMs,minSamples:r.minSamples,warmup:r.warmup,prepare:g.prepare,timeSource:g.timeSource,timeoutMs:g.timeoutMs??$e,ignoreExitCodes:g.ignoreExitCodes});if(P=C({workload:x,configFingerprint:k,trials:F.trials,timing:F.timing,warnings:p?[...F.warnings,p]:F.warnings}),q)await Pe(r.outDir,j,P);y="executed"}h.push({workload:x,status:y,run:P,harnessFailed:J(P,g.ignoreExitCodes)})}return{results:h,environment:c}}async function Ae(r){let{config:n}=r,c=l(n,r.baselineName);if(!await Bun.file(c).exists())throw new me(c);let h=await o(c),{results:g,environment:x}=await ge(n,r.full),b=g.map((E)=>({...E})),k=b.filter((E)=>E.status==="executed").length,q=b.length-k,j=b.filter((E)=>E.harnessFailed).length,M=s(b.map((E)=>E.workload),b.map((E)=>E.run),x),P=0,y=0,F=0,K=re(h,M,n.thresholds);for(let E of b){let te=K.compare(E.workload.id);if(!te){F++;continue}if(E.comparison=te,te.verdict==="pass")P++;else y++}if(F>0){if(qe(n.onMissingBaseline,F,b.length)==="fail")throw new de(c,F,b.length)}let Z=b.map((E)=>E.comparison).filter((E)=>E!==void 0);M.comparisons=Z;let X=K.unmatched();return M.unmatched={baseOnly:X.baseOnly.map((E)=>E.id),candOnly:X.candOnly.map((E)=>E.id)},M.comparisonSummary=se(Z,K.effectiveTimingPct),{document:M,summary:{total:b.length,cached:q,executed:k,passed:P,regressed:y,missingBaseline:F,failed:j,results:b,unmatched:X},baseline:h}}function Me(r){let n=[];if(n.push(`${r.total} workloads`),n.push(`${r.cached} cached`),n.push(`${r.executed} executed`),r.missingBaseline>0)n.push(`${r.missingBaseline} skipped (no matching baseline workload)`);if(r.failed>0){let p=r.results.filter((h)=>h.harnessFailed).map((h)=>t(h.workload));n.push(`${r.failed} failed (harness error, a command exited non-zero or produced no samples: ${p.join(", ")})`)}let c=r.results.filter((p)=>p.comparison?.verdict==="fail").map((p)=>{let h=p.comparison.timing,g=t(p.workload);return h?`${h.medianDeltaPct>0?"+":""}${h.medianDeltaPct.toFixed(1)}% median on ${g}`:g});return n.push(`${r.passed} passed ${r.regressed} regressed${c.length>0?` (${c.join(", ")})`:""}`),n.push(""),n.push(`Profile CI: ${r.regressed>0||r.failed>0?"\u2717":"\u2713"}`),`${n.join(`
5
- `)}
6
- `}async function Se(r,n){let{results:c,environment:p}=await ge(r,!1),h=s(c.map((x)=>x.workload),c.map((x)=>x.run),p),g=l(r,n);return await a(h,g),g}async function Fe(r){let n=[];try{let p=new Bun.Glob("*.json");for await(let h of p.scan({cwd:r.baselineDir,absolute:!1}))n.push(h.replace(/\.json$/,""))}catch{return[]}return n.sort(),(await Promise.all(n.map(async(p)=>{let h=l(r,p);try{let g=await o(h);return{name:p,path:h,createdAt:g.createdAt,toolVersion:g.toolVersion,bunVersion:g.bunVersion,workloads:g.workloads.length,...g.git!==void 0&&{git:g.git}}}catch{return}}))).filter((p)=>p!==void 0)}function Y(r){return r instanceof Error?r.message:String(r)}var B=(r)=>Bun.write(Bun.stdout,r),Te=(r)=>Bun.write(Bun.stderr,r);class Q extends Error{}var Ie=[],Ee=new Set(["minimal","json","jsonl"]);async function Le(r){let{isatty:n}=await import("tty");if(!n(2))return!0;return r.some((c,p)=>c==="--format"&&Ee.has(r[p+1]??"")||c.startsWith("--format=")&&Ee.has(c.slice(9)))}async function T(r,n,c){if(await Te(n.endsWith(`
7
- `)?n:`${n}
8
- `),!await Le(Ie))return;await Te(`${JSON.stringify({event:"error",protocolVersion:w,code:r,message:n.split(`
9
- `)[0],...c&&{data:c}})}
10
- `)}async function L(r,n){return await B(r),n?0:2}async function Je(r,n){return await T("invalid-flag",`${r.message}
11
- Run 'ostia ${n} --help'.`),2}function Ge(r,n,c={}){if(c.allowAuto&&n==="auto")return ie();let p=c.min??1,h=Number(n);if(!Number.isInteger(h)||h<p)throw new Q(`Invalid ${r} "${n}": expected an integer \u2265 ${p}${c.allowAuto?' (or "auto")':""}`);return h}var Ve=["ns","us","ms","s"],ne=["table","json","jsonl","markdown","minimal"],we=["collapsed","mermaid","speedscope","cpuprofile"],ze=[...ne,...we],be=`No CPU evidence in this document; rerun with --cpu (ostia time) or --cpu (ostia bench)
12
- `;function Ne(r){return r.measurements.some((n)=>n.phase==="cpu")}async function pe(r,n,c={}){if(n.format==="json"&&n.exportJson){let p=I(r);if(await ee(p,n.exportJson),!n.quiet)await B(p);return 0}if(n.exportJson)await a(r,n.exportJson);if(n.quiet)return 0;if(we.includes(n.format)&&!Ne(r))return await T("no-cpu-evidence",be),2;return await ue(await d[n.format].render(r,c)),0}async function fe(r){let n=await W();if(!n){await T("config-missing",r?`No ostia.config.json found. "${r}" needs configured workloads.`:"No ostia.config.json found.");return}if(r&&n.workloads.length===0){let c=await oe()??"ostia.config.json";await T("config-missing",`${c} has no "workloads" configured.`);return}return n}async function ue(r,n){if(r.text)await B(r.text);if(!r.files||r.files.length===0)return;if(n)for(let c of r.files){let p=c.path?`${n}/${c.path}`:n;await Bun.write(p,c.content),await B(`wrote ${p}
13
- `)}else if(r.files.length===1)await B(r.files[0].content);else for(let c of r.files)await B(`--- ${c.path??"(unnamed)"} ---
14
- ${c.content}
15
- `)}var Ke=`ostia time [flags] <command...>
16
- ostia time [flags] -- <argv...>
17
-
18
- Time commands as subprocesses. Each <command> is whitespace-split into argv (no shell);
19
- everything after -- is one more command's argv, verbatim.
20
-
21
- Flags:
22
- --samples N exact trials per command (default: ~3s budget, at least 10)
23
- --budget MS sampling time budget per command
24
- --min-samples N floor on trials when --samples isn't given
25
- --warmup N discarded warmup trials (default: 3)
26
- --no-interleave run commands one after another instead of round-robin
27
- --prepare CMD run before every trial, unmeasured; once, or once per command
28
- --time-source REGEX take each trial's time from capture group 1 in the command's output
29
- --time-unit UNIT unit of --time-source: ns | us | ms | s (default: ms)
30
- --cpu capture one extra CPU-profile trial
31
- --heap capture one extra heap-snapshot trial
32
- --cpu-interval US CPU sampling interval (default: 1000)
33
- --timeout MS kill a trial or --prepare hook after MS
34
- --ignore-failure[=CODE,...] treat these exit codes (bare: all) as success
35
- --out-dir PATH artifact directory (default: node_modules/.cache/ostia)
36
- --no-noise-check skip the ~200ms noise-floor measurement
37
- --export-json PATH write the ProfileDocument to PATH
38
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
39
- --quiet don't print the report
40
- --help show this message
41
-
42
- A command stops at its first non-ignored non-zero exit.
43
- Exit codes: 0 ok, 2 a command failed or produced no samples (or a bad flag), 130 Ctrl-C.
44
-
45
- Examples:
46
- ostia time "bun a.ts" "bun b.ts"
47
- ostia time --samples 25 --cpu --heap "bun src/server.ts"
48
- ostia time --prepare "rm -rf dist" "bun build.ts"
49
- ostia time --time-source "built in (\\d+)ms" "bun build.ts"
50
- ostia time -- bun -e "console.log('a b')"
51
- `,Xe=Array.from({length:255},(r,n)=>n+1),De=`ostia bench [flags] <suite.ts...>
52
-
53
- Run in-process group()/task() suites, each file in its own child process. With no files,
54
- uses ostia.config's "bench" section; each flag overrides its config field.
55
-
56
- Flags:
57
- --budget MS sampling budget per task (default: 500)
58
- --samples N exact trials per task (ignores --budget)
59
- --min-samples N floor on trials (default: cost-aware, 3-20)
60
- --jobs N|auto suite files at once (default: 1; >1 adds noise)
61
- --isolate one process per task, not per file (--no-isolate to undo config)
62
- --gc Bun.gc(true) between trials (--no-gc)
63
- --cpu extra per-task CPU profile with JIT tiers, ~2,000 samples (--no-cpu)
64
- --cpu-interval US CPU sampling interval (default: 100)
65
- --alloc extra per-task retained-heap-per-call measurement: what calls keep
66
- alive after a full GC, not what they allocate (--no-alloc)
67
- --peak-mem extra per-task RSS rise during the task's first call, garbage
68
- included; median of 3 fresh processes (--no-peak-mem)
69
- --filter REGEX only tasks whose "group/name" matches
70
- --preload PATH import before each suite file (repeatable, in order)
71
- --bun-flags FLAGS extra flags for the bun process running each suite (repeatable)
72
- --timeout MS kill a suite (or isolated task) process after MS
73
- --out-dir PATH scratch directory (default: node_modules/.cache/ostia)
74
- --no-noise-check skip the ~200ms noise-floor measurement
75
- --export-json PATH write the ProfileDocument to PATH
76
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
77
- --quiet don't print the report
78
- --help show this message
79
-
80
- Per-task/per-group options ({ budgetMs, minSamples, gc, isolate, cpu, alloc, peakMem }) override
81
- the suite-wide defaults. Exit codes: 0 ok, 2 a suite failed (or a bad flag), 130 Ctrl-C.
82
-
83
- Examples:
84
- ostia bench bench/*.ts
85
- ostia bench bench/*.ts --filter parse --cpu --alloc
86
- ostia bench --preload ./bench/dom-setup.ts --bun-flags="--conditions=browser" bench/*.ts
87
- `,Re=`ostia ab [flags] <suite.ts...>
88
-
89
- Pair every task on a git ref's committed tree (base) against the working tree (candidate), in
90
- one process, alternating ~10ms batches, and gate on the per-round time ratio. Drift between
91
- the two sides cancels within a round. With no files, uses ostia.config's "bench" suites.
92
-
93
- Flags:
94
- --base REF git ref for the base side (default: HEAD)
95
- --rounds N base/candidate rounds per task (default: 15)
96
- --threshold PCT flag a task whose median ratio moves past PCT (default: 10)
97
- --geomean-threshold PCT fail when the geometric mean of all ratios is slower than PCT
98
- (default: 1.5)
99
- --confirm N re-measure each flagged task in N fresh processes (default: 2; 0 skips)
100
- --filter REGEX only tasks whose "group/name" matches
101
- --preload PATH import before each suite file (repeatable, in order)
102
- --bun-flags FLAGS extra flags for the bun process running each suite (repeatable)
103
- --timeout MS kill a suite (or repeat) process after MS
104
- --out-dir PATH scratch directory and base-tree cache (default: node_modules/.cache/ostia)
105
- --no-noise-check skip the ~200ms noise-floor measurement
106
- --export-json PATH write the ProfileDocument to PATH
107
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
108
- --quiet don't print the report
109
- --help show this message
110
-
111
- A flagged task counts only when every fresh-process repeat flags it the same way. Exit codes:
112
- 0 pass, 1 a confirmed regression or the geomean over its threshold, 2 nothing paired or a
113
- harness error (not a git repo, unknown ref, a suite failed), 130 Ctrl-C.
114
-
115
- Examples:
116
- ostia ab bench/parse.bench.ts
117
- ostia ab bench/*.ts --base origin/main --rounds 21
118
- ostia ab bench/*.ts --filter parse --format minimal
119
- `,Oe=`ostia compare <base.json> <candidate.json>
120
- ostia compare <candidate.json> --baseline <base.json>
121
-
122
- Compare two ProfileDocuments by workload id. Gates on ostia.config's "thresholds" when
123
- present (the same ones "ostia ci" uses), else the defaults.
124
-
125
- Flags:
126
- --baseline PATH the base document, when only the candidate is positional
127
- --export-json PATH write the candidate document with comparisons to PATH
128
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
129
- --quiet don't print the report
130
- --help show this message
131
-
132
- Exit codes: 0 pass, 1 a workload regressed, 2 nothing matched or a harness error.
133
- `,Ze=`ostia report <document.json> [flags]
134
-
135
- Render a saved ProfileDocument. Files, not a GUI for the visualization formats -
136
- hand the output to speedscope.app, flamegraph.pl, or a Mermaid renderer.
137
-
138
- Formats:
139
- table terminal timing/CPU/heap/comparison text (default)
140
- json pretty JSON document
141
- jsonl one metadata line, then one line per measurement
142
- markdown agent- and human-readable report
143
- minimal one compact JSON object per timing measurement, for LLM/CI consumption
144
- collapsed folded stacks: "root;a;b 42" - flamegraph.pl and most flame tooling
145
- mermaid call tree, top-15 frames by total time (never the whole profile)
146
- speedscope sampled profile JSON for speedscope.app
147
- cpuprofile verbatim .cpuprofile pass-through (cpu-prof/inspector origins only)
148
-
149
- Flags:
150
- --format FORMAT one of the formats above (default: table)
151
- --measurement <id> for the visualization formats: render only this measurement
152
- (default: every CPU measurement in the document)
153
- --out-dir PATH write visualization files here instead of stdout
154
- --help show this message
155
-
156
- Examples:
157
- ostia report doc.json
158
- ostia report doc.json --format markdown
159
- ostia report doc.json --format speedscope --out-dir node_modules/.cache/ostia/viz
160
- ostia report doc.json --format collapsed | flamegraph.pl > flame.svg
161
- `,Qe=`ostia ci [flags]
162
-
163
- Run ostia.config's workloads (reusing cached runs whose declared inputs are unchanged),
164
- compare against a saved baseline, and gate on regressions.
165
-
166
- Flags:
167
- --full ignore the cache
168
- --baseline NAME baseline to compare against (default: config "baseline", or "main")
169
- --save-baseline after a pass, save this run as the new baseline
170
- --no-noise-check skip the ~200ms noise-floor measurement
171
- --export-json PATH write the candidate document with comparisons to PATH
172
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
173
- --quiet don't print the report
174
- --help show this message
175
-
176
- Exit codes: 0 pass, 1 regression, 2 harness error (missing config/baseline, a workload
177
- failed or produced no samples, or an onMissingBaseline "fail" mismatch).
178
- `,he=`ostia baseline <save|list|show> [args]
179
-
180
- Manage the baseline ProfileDocuments "ostia ci" gates against and "ostia compare --baseline"
181
- reads.
182
-
183
- Subcommands:
184
- save [name] measure every configured workload (same code path as "ostia ci",
185
- no comparison) and write it to <baselineDir>/<name>.json
186
- (default name: config's "baseline" field, or "main")
187
- list list saved baselines: name, created date, workload count
188
- show <name> [flags] render a saved baseline; delegates to "ostia report" (same
189
- --format/--measurement/--out-dir flags)
190
-
191
- Examples:
192
- ostia baseline save
193
- ostia baseline save my-feature
194
- ostia baseline list
195
- ostia baseline show main
196
- ostia baseline show main --format markdown
197
- `;function ce(r,n,c,p,h,g){let x=p;for(let b=0;b<r.length;b++){let k=r[b],q=g?.(k,b,p);if(q!==void 0){b=q;continue}let j=k.startsWith("--")?k.indexOf("="):-1,M=j>0?k.slice(0,j):k,P=c[M];if(!P||j>0&&P.spec.kind==="bool"){if(k.startsWith("-"))throw new Q(`Unknown flag "${k}" for "ostia ${n}".`);h(k,p);continue}let y=()=>j>0?k.slice(j+1):r[++b];switch(P.spec.kind){case"string":x[P.dest]=y();break;case"int":x[P.dest]=Ge(M,y(),P.spec);break;case"number":{let F=y(),K=Number(F);if(F===void 0||F===""||!Number.isFinite(K)||K<P.spec.min)throw new Q(`Invalid ${M} "${F}": expected a number \u2265 ${P.spec.min}`);x[P.dest]=K;break}case"bool":x[P.dest]=P.spec.value;break;case"list":x[P.dest].push(y()??"");break;case"enum":{let F=y();if(!P.spec.values.includes(F))throw new Q(`Invalid ${M} "${F}": expected one of: ${P.spec.values.join(", ")}`);x[P.dest]=F;break}}}return p}var Ye={"--samples":{dest:"samples",spec:{kind:"int",min:1}},"--budget":{dest:"budgetMs",spec:{kind:"int",min:1}},"--min-samples":{dest:"minSamples",spec:{kind:"int",min:1}},"--warmup":{dest:"warmup",spec:{kind:"int",min:0}},"--no-interleave":{dest:"interleave",spec:{kind:"bool",value:!1}},"--prepare":{dest:"prepare",spec:{kind:"list"}},"--time-source":{dest:"timeSource",spec:{kind:"string"}},"--time-unit":{dest:"timeUnit",spec:{kind:"enum",values:Ve}},"--cpu":{dest:"cpu",spec:{kind:"bool",value:!0}},"--heap":{dest:"heap",spec:{kind:"bool",value:!0}},"--cpu-interval":{dest:"cpuIntervalUs",spec:{kind:"int",min:1}},"--timeout":{dest:"timeoutMs",spec:{kind:"int",min:1}},"--out-dir":{dest:"outDir",spec:{kind:"string"}},"--no-noise-check":{dest:"noiseCheck",spec:{kind:"bool",value:!1}},"--export-json":{dest:"exportJson",spec:{kind:"string"}},"--format":{dest:"format",spec:{kind:"enum",values:ne}},"--quiet":{dest:"quiet",spec:{kind:"bool",value:!0}},"--help":{dest:"help",spec:{kind:"bool",value:!0}},"-h":{dest:"help",spec:{kind:"bool",value:!0}}};function et(r){return ce(r,"time",Ye,{commands:[],prepare:[],interleave:!0,cpu:!1,heap:!1,ignoreExitCodes:[],noiseCheck:!0,format:"table",quiet:!1,help:!1},(c,p)=>p.commands.push(c),(c,p,h)=>{if(c==="--")return h.argvCommand=r.slice(p+1),r.length;if(c==="--ignore-failure"||c.startsWith("--ignore-failure=")){let g=c.startsWith("--ignore-failure=")?c.slice(17):void 0;return h.ignoreExitCodes.push(...g===void 0?Xe:g.split(",").map(Number)),p}return})}async function ke(r){let n=new AbortController,c=()=>n.abort();process.on("SIGINT",c);try{return{result:await r(n.signal),aborted:n.signal.aborted}}finally{process.off("SIGINT",c)}}async function tt(r){let n=et(r),c=(n.argvCommand?.length??0)>0;if(n.help||n.commands.length===0&&!c)return L(Ke,n.help);let p=n.commands.length+(c?1:0);if(n.prepare.length>1&&n.prepare.length!==p)return await T("invalid-flag",`--prepare given ${n.prepare.length} times for ${p} command(s): give it once (applies to all) or once per command.`),2;if(n.timeSource!==void 0)try{new RegExp(n.timeSource)}catch(y){return await T("invalid-flag",`Invalid --time-source regex: ${Y(y)}`),2}let h=n.timeSource!==void 0?{pattern:n.timeSource,unit:n.timeUnit}:void 0,g=(y)=>n.prepare.length===1?n.prepare[0]:n.prepare[y],x=n.commands.map((y,F)=>({command:y,prepare:g(F)}));if(c)x.push({command:n.argvCommand,prepare:g(n.commands.length)});let b,k;try{({result:b,aborted:k}=await ke((y)=>ae({commands:x,timeSource:h,samples:n.samples,budgetMs:n.budgetMs,minSamples:n.minSamples,warmup:n.warmup,interleave:n.interleave,cpu:n.cpu,heap:n.heap,cpuIntervalUs:n.cpuIntervalUs,timeoutMs:n.timeoutMs,ignoreExitCodes:n.ignoreExitCodes,outDir:n.outDir,noiseCheck:n.noiseCheck,signal:y})))}catch(y){return await T("spawn-failed",`Run failed: ${Y(y)}`),2}let q=await pe(b,n);if(q!==0)return q;if(k)return 130;let j=b.workloads.map((y)=>b.measurements.find((F)=>F.workloadId===y.id&&F.phase==="timing")??{trials:[],timing:void 0}),M=j.some((y)=>!y.timing),P=j.some((y)=>y.timing&&J(y,n.ignoreExitCodes));if(P||M){let y=b.measurements.some((Z)=>Z.warnings.some((X)=>X.code==="timeout")),F=b.measurements.some((Z)=>Z.warnings.some((X)=>X.code==="time-source-no-match"));return await T(P?"command-failed":y?"timeout":F?"time-source-no-match":"command-failed","One or more commands failed to produce a clean measurement; see the report above for details."),2}return 0}var nt={"--budget":{dest:"budgetMs",spec:{kind:"int",min:1}},"--samples":{dest:"samples",spec:{kind:"int",min:1}},"--min-samples":{dest:"minSamples",spec:{kind:"int",min:1}},"--jobs":{dest:"jobs",spec:{kind:"int",min:1,allowAuto:!0}},"--gc":{dest:"gc",spec:{kind:"bool",value:!0}},"--no-gc":{dest:"gc",spec:{kind:"bool",value:!1}},"--cpu":{dest:"cpu",spec:{kind:"bool",value:!0}},"--no-cpu":{dest:"cpu",spec:{kind:"bool",value:!1}},"--cpu-interval":{dest:"cpuIntervalUs",spec:{kind:"int",min:1}},"--alloc":{dest:"alloc",spec:{kind:"bool",value:!0}},"--no-alloc":{dest:"alloc",spec:{kind:"bool",value:!1}},"--peak-mem":{dest:"peakMem",spec:{kind:"bool",value:!0}},"--no-peak-mem":{dest:"peakMem",spec:{kind:"bool",value:!1}},"--filter":{dest:"filter",spec:{kind:"string"}},"--isolate":{dest:"isolate",spec:{kind:"bool",value:!0}},"--no-isolate":{dest:"isolate",spec:{kind:"bool",value:!1}},"--preload":{dest:"preload",spec:{kind:"list"}},"--timeout":{dest:"timeoutMs",spec:{kind:"int",min:1}},"--out-dir":{dest:"outDir",spec:{kind:"string"}},"--no-noise-check":{dest:"noiseCheck",spec:{kind:"bool",value:!1}},"--export-json":{dest:"exportJson",spec:{kind:"string"}},"--format":{dest:"format",spec:{kind:"enum",values:ne}},"--quiet":{dest:"quiet",spec:{kind:"bool",value:!0}},"--help":{dest:"help",spec:{kind:"bool",value:!0}},"-h":{dest:"help",spec:{kind:"bool",value:!0}}};function st(r){return ce(r,"bench",nt,{suites:[],preload:[],bunFlags:[],noiseCheck:!0,format:"table",quiet:!1,help:!1},(c,p)=>p.suites.push(c),Be(r))}function Be(r){return(n,c,p)=>{if(n==="--bun-flags")return p.bunFlags.push(...R(r[c+1]??"")),c+1;if(n.startsWith("--bun-flags="))return p.bunFlags.push(...R(n.slice(12))),c;return}}async function ot(r){let n=st(r);if(n.help)return L(De,!0);let c=await W(),p=await z(n,c?.bench);if(p.suites.length===0)return L(De,!1);if(p.jobs!==void 0&&!(p.jobs>=1))return await T("invalid-flag",'--jobs expects a positive integer or "auto".'),2;let h,g;try{({result:h,aborted:g}=await ke((b)=>D({...p,signal:b})))}catch(b){return await T("spawn-failed",`Bench failed: ${Y(b)}`),2}let x=await pe(h,n);if(x!==0)return x;return g?130:0}var at={"--base":{dest:"base",spec:{kind:"string"}},"--rounds":{dest:"rounds",spec:{kind:"int",min:3}},"--threshold":{dest:"thresholdPct",spec:{kind:"number",min:0}},"--geomean-threshold":{dest:"geomeanThresholdPct",spec:{kind:"number",min:0}},"--confirm":{dest:"confirm",spec:{kind:"int",min:0}},"--filter":{dest:"filter",spec:{kind:"string"}},"--preload":{dest:"preload",spec:{kind:"list"}},"--timeout":{dest:"timeoutMs",spec:{kind:"int",min:1}},"--out-dir":{dest:"outDir",spec:{kind:"string"}},"--no-noise-check":{dest:"noiseCheck",spec:{kind:"bool",value:!1}},"--export-json":{dest:"exportJson",spec:{kind:"string"}},"--format":{dest:"format",spec:{kind:"enum",values:ne}},"--quiet":{dest:"quiet",spec:{kind:"bool",value:!0}},"--help":{dest:"help",spec:{kind:"bool",value:!0}},"-h":{dest:"help",spec:{kind:"bool",value:!0}}};function it(r){return ce(r,"ab",at,{suites:[],preload:[],bunFlags:[],noiseCheck:!0,format:"table",quiet:!1,help:!1},(c,p)=>p.suites.push(c),Be(r))}async function rt(r){let n=it(r);if(n.help)return L(Re,!0);let c=(await W())?.bench,p=n.suites.length>0?n.suites:c?.suites?await U(c.suites,process.cwd()):[];if(p.length===0)return L(Re,!1);let h,g;try{({result:h,aborted:g}=await ke((M)=>G({suites:p,base:n.base,rounds:n.rounds,thresholdPct:n.thresholdPct,geomeanThresholdPct:n.geomeanThresholdPct,confirm:n.confirm,filter:n.filter??c?.filter,preload:n.preload.length>0?n.preload:c?.preload??[],bunFlags:n.bunFlags.length>0?n.bunFlags:c?.bunFlags??[],timeoutMs:n.timeoutMs??c?.timeoutMs,outDir:n.outDir??c?.outDir,noiseCheck:n.noiseCheck,signal:M})))}catch(M){if(M instanceof A)return await T("invalid-flag",`--base: ${M.message}`),2;return await T("spawn-failed",`A/B run failed: ${Y(M)}`),2}let x=h.ab,b=g?130:x.matched===0?2:x.verdict==="fail"?1:0,k=new Map(h.workloads.map((M)=>[M.id,M])),q=(M=[])=>M.flatMap((P)=>k.get(P)??[]),j=await pe(h,n,{protocol:{command:"ab",exitCode:b,unmatched:{baseOnly:q(h.unmatched?.baseOnly),candOnly:q(h.unmatched?.candOnly)},...n.exportJson&&{exportedTo:n.exportJson}}});if(j!==0)return j;if(b===2)await T("no-matches",`No task exists both at ${x.base.ref} and in the working tree; nothing was paired.`);return b}var lt={"--baseline":{dest:"baseline",spec:{kind:"string"}},"--export-json":{dest:"exportJson",spec:{kind:"string"}},"--format":{dest:"format",spec:{kind:"enum",values:ne}},"--quiet":{dest:"quiet",spec:{kind:"bool",value:!0}},"--help":{dest:"help",spec:{kind:"bool",value:!0}},"-h":{dest:"help",spec:{kind:"bool",value:!0}}};function ct(r){return ce(r,"compare",lt,{paths:[],format:"table",quiet:!1,help:!1},(c,p)=>p.paths.push(c))}async function ut(){let r=await W();if(!r)return{thresholds:f,source:"defaults"};return{thresholds:r.thresholds,source:await oe()??"ostia.config.json"}}async function mt(r){let n=ct(r);if(n.help)return L(Oe,!0);let c,p;if(n.baseline)c=n.baseline,p=n.paths[0];else c=n.paths[0],p=n.paths[1];if(!c||!p)return L(Oe,!1);let h,g;try{[h,g]=await Promise.all([o(c),o(p)])}catch(y){return await T("document-load-failed",`Failed to load documents: ${Y(y)}`),2}let{thresholds:x,source:b}=await ut(),k=H(h,g,x),q={...g,comparisons:k.comparisons,comparisonSummary:k.summary,unmatched:{baseOnly:k.unmatched.baseOnly.map((y)=>y.id),candOnly:k.unmatched.candOnly.map((y)=>y.id)}},j=n.format==="table"||n.format==="markdown";if(!n.quiet&&j){if(await B(`thresholds: ${b}
198
- `),h.git&&g.git)await B(`base ${v(h.git)} \u2192 cand ${v(g.git)}
199
- `);if(n.format==="table"&&k.summary.effectiveTimingPct>x.timingPct)await B(`threshold ${x.timingPct}% (widened to ${k.summary.effectiveTimingPct.toFixed(1)}% by noise floor)
200
- `)}let M=k.summary.matched===0?2:k.summary.verdict==="fail"?1:0,P=await pe(q,n,{protocol:{command:"compare",exitCode:M,unmatched:k.unmatched,baseGit:h.git,candGit:g.git,...n.exportJson&&{exportedTo:n.exportJson}}});if(P!==0)return P;if(!n.quiet&&(n.format==="table"||n.format==="markdown")&&(k.unmatched.baseOnly.length>0||k.unmatched.candOnly.length>0))await B(dt(k.unmatched,n.format));if(k.summary.matched===0)await T("no-matches","No workload matched between base and candidate; nothing was compared.");return M}function dt(r,n){let c=(h)=>h.map((g)=>t(g)).join(", ");if(n==="markdown"){let h=["### Unmatched",""];if(r.baseOnly.length>0)h.push(`- baseline only: ${c(r.baseOnly)}`);if(r.candOnly.length>0)h.push(`- candidate only: ${c(r.candOnly)}`);return h.push(""),`${h.join(`
201
- `)}
202
- `}let p=["","Unmatched:"];if(r.baseOnly.length>0)p.push(` baseline only: ${c(r.baseOnly)}`);if(r.candOnly.length>0)p.push(` candidate only: ${c(r.candOnly)}`);return`${p.join(`
203
- `)}
204
- `}var pt={"--format":{dest:"format",spec:{kind:"enum",values:ze}},"--measurement":{dest:"measurementId",spec:{kind:"string"}},"--out-dir":{dest:"outDir",spec:{kind:"string"}},"--help":{dest:"help",spec:{kind:"bool",value:!0}},"-h":{dest:"help",spec:{kind:"bool",value:!0}}};function ft(r){return ce(r,"report",pt,{format:"table",help:!1},(c,p)=>{if(p.path!==void 0)throw new Q(`"ostia report" takes exactly one document path, got "${p.path}" and "${c}".`);p.path=c})}async function _e(r){let n=ft(r);if(n.help||!n.path)return L(Ze,n.help);let c;try{c=await o(n.path)}catch(g){return await T("document-load-failed",`Failed to load ${n.path}: ${Y(g)}`),2}if(we.includes(n.format)&&!n.measurementId&&!Ne(c))return await T("no-cpu-evidence",be),2;let h=await d[n.format].render(c,{measurementId:n.measurementId});if(!h.text&&(!h.files||h.files.length===0))return await T("no-cpu-evidence",n.measurementId?`No CPU evidence found for measurement "${n.measurementId}".`:be),2;return await ue(h,n.outDir),0}var ht={"--full":{dest:"full",spec:{kind:"bool",value:!0}},"--baseline":{dest:"baseline",spec:{kind:"string"}},"--save-baseline":{dest:"saveBaseline",spec:{kind:"bool",value:!0}},"--export-json":{dest:"exportJson",spec:{kind:"string"}},"--format":{dest:"format",spec:{kind:"enum",values:ne}},"--no-noise-check":{dest:"noiseCheck",spec:{kind:"bool",value:!1}},"--quiet":{dest:"quiet",spec:{kind:"bool",value:!0}},"--help":{dest:"help",spec:{kind:"bool",value:!0}},"-h":{dest:"help",spec:{kind:"bool",value:!0}}};function gt(r){return ce(r,"ci",ht,{full:!1,saveBaseline:!1,format:"table",quiet:!1,help:!1,noiseCheck:!0},(c)=>{throw new Q(`Unknown flag "${c}" for "ostia ci".`)})}async function bt(r){let n=gt(r);if(n.help)return L(Qe,!0);let c=await fe("ostia ci");if(!c)return 2;let p={...c,...!n.noiseCheck&&{noiseCheck:!1}},h;try{h=await Ae({config:p,full:n.full,baselineName:n.baseline})}catch(b){if(b instanceof me||b instanceof de)return await T("baseline-missing",b.message),2;if(b instanceof O)return await T("document-load-failed",b.message),2;return await T("spawn-failed",`CI run failed: ${Y(b)}`),2}let g=n.format==="json"&&!n.quiet?I(h.document):void 0;if(n.exportJson)if(g)await ee(g,n.exportJson);else await a(h.document,n.exportJson);if(n.saveBaseline&&h.summary.regressed===0&&h.summary.failed===0)await a(h.document,l(p,n.baseline));let x=h.summary.failed>0?2:h.summary.regressed>0?1:0;if(h.summary.failed>0){let b=h.summary.results.filter((k)=>k.harnessFailed).map((k)=>t(k.workload));await T("command-failed",`${h.summary.failed} workload(s) failed (harness error, a command exited non-zero or produced no samples): ${b.join(", ")}`)}if(!n.quiet){let b=n.baseline??p.baseline;if(n.format==="table"){if(await B(Me(h.summary)),h.document.comparisons&&h.document.comparisons.length>0)await ue(await d.table.render(h.document,{}))}else if(g!==void 0)await B(g);else if(n.format==="markdown")await B(`## ostia ci
205
-
206
- Baseline: \`${b}\` (\`${l(p,n.baseline)}\`) \xB7 ${h.summary.cached} cached, ${h.summary.executed} executed
207
-
208
- `),await ue(await d.markdown.render(h.document,{}));else await ue(await d[n.format].render(h.document,{protocol:{command:"ci",exitCode:x,unmatched:h.summary.unmatched,baseGit:h.baseline.git,candGit:h.document.git,baseline:{name:b,path:l(p,n.baseline)},cached:h.summary.cached,executed:h.summary.executed,failed:h.summary.failed,missingBaseline:h.summary.missingBaseline,...n.exportJson&&{exportedTo:n.exportJson}}}))}return x}var je=/^[A-Za-z0-9._-]+$/;function He(r){if(r.startsWith("-"))return`Invalid baseline name "${r}": names can't start with "-".`;if(!je.test(r))return`Invalid baseline name "${r}": expected to match ${je}.`;return}async function wt(r){if(r.includes("--help")||r.includes("-h"))return L(he,!0);if(r.length>1)return await T("invalid-flag",`"ostia baseline save" takes at most one name argument, got ${r.length}.
209
- Run 'ostia baseline --help'.`),2;let n=r[0];if(n!==void 0){let h=He(n);if(h)return await T("invalid-flag",`${h}
210
- Run 'ostia baseline --help'.`),2}let c=await fe("ostia baseline save");if(!c)return 2;let p=await Se(c,n);return await B(`Wrote ${p}
211
- `),0}async function kt(r){if(r.includes("--help")||r.includes("-h"))return L(he,!0);let n=await fe();if(!n)return 2;let c=await Fe(n);if(c.length===0)return await B(`No baselines found in ${n.baselineDir}.
212
- `),0;for(let p of c){let h=p.git?` ${v(p.git)}`:"";await B(`${p.name} ${p.workloads} workloads created ${p.createdAt} toolVersion ${p.toolVersion}${h}
213
- `)}return 0}async function vt(r){let[n,...c]=r;if(!n||n==="--help"||n==="-h")return L(he,!!n);let p=He(n);if(p)return await T("invalid-flag",`${p}
214
- Run 'ostia baseline --help'.`),2;let h=await fe();if(!h)return 2;return _e([l(h,n),...c])}async function We(r,n,c,p,h,g=""){if(r===void 0||r==="--help"||r==="-h")return L(p,r!==void 0);let x=c[r];if(!x)return await T("invalid-flag",h(r)),2;try{return await x(n)}catch(b){if(b instanceof N)return await T("config-invalid",b.message),2;if(b instanceof Q)return Je(b,`${g}${r}`);return await T("internal",`Internal error: ${b instanceof Error?b.stack??b.message:String(b)}`),2}}async function yt(r){let[n,...c]=r;return We(n,c,{save:wt,list:kt,show:vt},he,(p)=>`Unknown "ostia baseline ${p}". Run "ostia baseline --help".`,"baseline ")}var Ct=`ostia - profiling and benchmarking for Bun
215
-
216
- Commands:
217
- time Time commands N times and report timing/CPU/heap
218
- bench Run in-process benchmark suites (group()/task())
219
- ab Pair suites against a git ref in one process, gate on regressions
220
- compare Compare two ProfileDocuments
221
- report Render a saved ProfileDocument (table/json/markdown/collapsed/mermaid/speedscope/...)
222
- ci Run configured workloads against a baseline, gate on regressions
223
- baseline Manage baseline ProfileDocuments (save/list/show)
224
-
225
- Run "ostia <command> --help" for details.
226
- `;async function xt(){let[r,...n]=process.argv.slice(2);return Ie=n,We(r,n,{time:tt,bench:ot,ab:rt,compare:mt,report:_e,ci:bt,baseline:yt},Ct,(c)=>`Unknown subcommand "${c}". Run "ostia --help".`)}if(import.meta.main)xt().then((r)=>process.exit(r));export{Q as CliUsageError,Ge as parseIntFlag};
3
+ var t=process.argv[2];if(t===void 0||t==="--help"||t==="-h"){let{MAIN_HELP:i}=await import("./chunk-82vs1rw1.js");await Bun.write(Bun.stdout,i),process.exit(t===void 0?2:0)}var{main:e}=await import("./chunk-r6hwb84z.js");process.exit(await e());