ostia 0.2.4 → 0.2.6

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/cli.js CHANGED
@@ -1,245 +1,100 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{e,m,x,t,P,d,o,f,E,a,r,g,h}from"./chunk-1wydyvyj.js";import{c,W,j,C,S,s,L,I,_,H,J,b,z,w,n,u,i,V}from"./chunk-cf9hya3b.js";function he(p){return e("cache",p.workloadId,p.phase,p.configFingerprint,p.bunVersion,p.toolVersion,p.instrumented,p.inputsDigest??null)}async function ge(p,l=process.cwd()){if(p.length===0)return;let k=await I(p,l),y=await Promise.all(k.map(async(v)=>{let M=await Bun.file(`${l}/${v}`).arrayBuffer();return{path:v,sha256:Bun.CryptoHasher.hash("sha256",M,"hex")}}));return e("inputs",y)}function be(p,l){return`${p}/cache/${l}.json`}async function we(p,l){let k=Bun.file(be(p,l));if(!await k.exists())return;return await k.json()}async function ke(p,l,k){await Bun.write(be(p,l),`${JSON.stringify(k,null,2)}
4
- `)}var ve=600000;class ie extends Error{path;constructor(p){super(`No baseline document at ${p}. Create one with: ostia time --export-json ${p} <command...>`);this.path=p}}class re extends Error{path;missing;total;constructor(p,l,k){super(`${l} of ${k} configured workload(s) have no matching row in baseline ${p} (by workload id). Re-seed with: ostia baseline save`);this.path=p;this.missing=l;this.total=k}}function De(p,l,k){if(p)return p;return l===k?"fail":"warn"}function Ie(p,l=[]){let k=new Set(l),y=p.filter((v)=>!v.timedOut&&!v.timeSourceNoMatch).map((v)=>v.exitCode).filter((v)=>v!==void 0);return y.length>0&&y.every((v)=>v!==0&&!k.has(v))}async function me(p,l){let k=p.noiseCheck===!1?void 0:g(),y=k?h(k):void 0,v=[];for(let M of p.workloads){if(M.suites){let D=await H(M.suites,process.cwd()),ee=await b({suites:D,outDir:p.outDir,noiseCheck:!1,budgetMs:p.bench?.budgetMs,samples:p.bench?.samples,minSamples:p.bench?.minSamples,gc:p.bench?.gc,cpu:p.bench?.cpu,alloc:p.bench?.alloc,filter:p.bench?.filter,isolate:p.bench?.isolate,preload:p.bench?.preload,jobs:typeof p.bench?.jobs==="number"?p.bench.jobs:void 0,timeoutMs:p.bench?.timeoutMs??ve});for(let Y of ee.workloads){let X=ee.measurements.find((N)=>N.workloadId===Y.id&&N.phase==="timing");if(!X)continue;if(y)X.warnings.push(y);v.push({workload:Y,status:"executed",run:X,harnessFailed:!1})}continue}let R=P(M.command,M.label,{prepare:M.prepare,timeSource:M.timeSource}),A=await ge(M.inputs??[]),T=o({runs:p.runs,warmup:p.warmup}),K=typeof M.prepare!=="function",B=he({workloadId:R.id,phase:"timing",configFingerprint:T,bunVersion:Bun.version,toolVersion:x,instrumented:!1,inputsDigest:A}),U=l||!K?void 0:await we(p.outDir,B),G,O;if(U)G=U,O="cached";else{let D=await z({argv:M.command,samples:p.runs??void 0,warmup:p.warmup,prepare:M.prepare,timeSource:M.timeSource,timeoutMs:M.timeoutMs??ve,ignoreExitCodes:M.ignoreExitCodes,failOnNonzero:M.failOnNonzero});if(G=d({workload:R,configFingerprint:T,trials:D.trials,timing:D.timing,warnings:y?[...D.warnings,y]:D.warnings}),K)await ke(p.outDir,B,G);O="executed"}v.push({workload:R,status:O,run:G,harnessFailed:Ie(G.trials,M.ignoreExitCodes)})}return{results:v,environment:k}}async function ye(p){let{config:l}=p,k=s(l,p.baselineName);if(!await Bun.file(k).exists())throw new ie(k);let v=await r(k),{results:M,environment:R}=await me(l,p.full),A=M.map((N)=>({...N})),T=A.filter((N)=>N.status==="executed").length,K=A.length-T,B=A.filter((N)=>N.harnessFailed).length,U=t(A.map((N)=>N.workload),A.map((N)=>N.run),R),G=0,O=0,D=0,ee=W(v,U,l.thresholds);for(let N of A){let de=ee.compare(N.workload.id);if(!de){D++;continue}if(N.comparison=de,de.verdict==="pass")G++;else O++}if(D>0){if(De(l.onMissingBaseline,D,A.length)==="fail")throw new re(k,D,A.length)}let Y=A.map((N)=>N.comparison).filter((N)=>N!==void 0);U.comparisons=Y;let X=ee.unmatched();return U.unmatched={baseOnly:X.baseOnly.map((N)=>N.id),candOnly:X.candOnly.map((N)=>N.id)},U.comparisonSummary=j(Y,ee.effectiveTimingPct),{document:U,summary:{total:A.length,cached:K,executed:T,passed:G,regressed:O,missingBaseline:D,failed:B,results:A,unmatched:X},baseline:v}}function Ce(p){let l=[];if(l.push(`${p.total} workloads`),l.push(`${p.cached} cached`),l.push(`${p.executed} executed`),p.missingBaseline>0)l.push(`${p.missingBaseline} skipped (no matching baseline workload)`);if(p.failed>0){let y=p.results.filter((v)=>v.harnessFailed).map((v)=>n(v.workload));l.push(`${p.failed} failed (harness error, every trial exited non-zero: ${y.join(", ")})`)}let k=p.results.filter((y)=>y.comparison?.verdict==="fail").map((y)=>{let v=y.comparison.timing,M=n(y.workload);return v?`${v.medianDeltaPct>0?"+":""}${v.medianDeltaPct.toFixed(1)}% median on ${M}`:M});return l.push(`${p.passed} passed ${p.regressed} regressed${k.length>0?` (${k.join(", ")})`:""}`),l.push(""),l.push(`Profile CI: ${p.regressed>0||p.failed>0?"\u2717":"\u2713"}`),`${l.join(`
3
+ import{e,g,t,b,R,r,I,w,c,k,J,u,x,s,l,d}from"./chunk-8m8jb8gw.js";import{m,q,K,F,P,E,o,Y,A,ne,B,Z,X,W,v,p,i,Q}from"./chunk-85cap0sk.js";import{resolve as Be}from"path";function be(a){return e("cache",a.workloadId,a.phase,a.configFingerprint,a.bunVersion,a.toolVersion,a.instrumented,a.inputsDigest??null)}async function we(a,n=process.cwd()){if(a.length===0)return;let f=await A(a,n),h=await Promise.all(f.map(async(y)=>{let C=await Bun.file(Be(n,y)).arrayBuffer();return{path:y,sha256:Bun.CryptoHasher.hash("sha256",C,"hex")}}));return e("inputs",h)}function ke(a,n){return`${a}/cache/${n}.json`}async function ve(a,n){let f=Bun.file(ke(a,n));if(!await f.exists())return;return await f.json()}async function ye(a,n,f){await Bun.write(ke(a,n),`${JSON.stringify(f,null,2)}
4
+ `)}var Ce=600000;class ce extends Error{path;constructor(a){super(`No baseline document at ${a}. Create one with: ostia baseline save ${a.split("/").pop().replace(/\.json$/,"")}`);this.path=a}}class ue extends Error{path;missing;total;constructor(a,n,f){super(`${n} of ${f} configured workload(s) have no matching row in baseline ${a} (by workload id). Re-seed with: ostia baseline save`);this.path=a;this.missing=n;this.total=f}}function _e(a,n,f){if(a)return a;return n===f?"fail":"warn"}async function pe(a,n){let f=a.noiseCheck===!1?void 0:l(),h=f?d(f):void 0,y=[];for(let C of a.workloads){if(C.suites){let _=await X(C.suites,process.cwd()),te=await W({suites:_,preload:[],bunFlags:[],noiseCheck:!1},a.bench),ee=await v({...te,outDir:te.outDir??a.outDir,timeoutMs:te.timeoutMs??Ce});for(let z of ee.workloads){let j=ee.measurements.find((oe)=>oe.workloadId===z.id&&oe.phase==="timing");if(!j)continue;if(h)j.warnings.push(h);y.push({workload:z,status:"executed",run:j,harnessFailed:!1})}continue}let D=I(C.command,C.label,{prepare:C.prepare,timeSource:C.timeSource}),M=await we(C.inputs??[]),S=c({samples:a.samples??null,budgetMs:a.budgetMs??null,minSamples:a.minSamples??null,warmup:a.warmup}),G=C.inputs!==void 0&&typeof C.prepare!=="function",L=be({workloadId:D.id,phase:"timing",configFingerprint:S,bunVersion:Bun.version,toolVersion:R,instrumented:!1,inputsDigest:M}),U=n||!G?void 0:await ve(a.outDir,L),O,T;if(U)O=U,T="cached";else{let _=await ne({argv:C.command,samples:a.samples,budgetMs:a.budgetMs,minSamples:a.minSamples,warmup:a.warmup,prepare:C.prepare,timeSource:C.timeSource,timeoutMs:C.timeoutMs??Ce,ignoreExitCodes:C.ignoreExitCodes});if(O=w({workload:D,configFingerprint:S,trials:_.trials,timing:_.timing,warnings:h?[..._.warnings,h]:_.warnings}),G)await ye(a.outDir,L,O);T="executed"}y.push({workload:D,status:T,run:O,harnessFailed:B(O,C.ignoreExitCodes)})}return{results:y,environment:f}}async function xe(a){let{config:n}=a,f=o(n,a.baselineName);if(!await Bun.file(f).exists())throw new ce(f);let y=await s(f),{results:C,environment:D}=await pe(n,a.full),M=C.map((j)=>({...j})),S=M.filter((j)=>j.status==="executed").length,G=M.length-S,L=M.filter((j)=>j.harnessFailed).length,U=r(M.map((j)=>j.workload),M.map((j)=>j.run),D),O=0,T=0,_=0,te=q(y,U,n.thresholds);for(let j of M){let oe=te.compare(j.workload.id);if(!oe){_++;continue}if(j.comparison=oe,oe.verdict==="pass")O++;else T++}if(_>0){if(_e(n.onMissingBaseline,_,M.length)==="fail")throw new ue(f,_,M.length)}let ee=M.map((j)=>j.comparison).filter((j)=>j!==void 0);U.comparisons=ee;let z=te.unmatched();return U.unmatched={baseOnly:z.baseOnly.map((j)=>j.id),candOnly:z.candOnly.map((j)=>j.id)},U.comparisonSummary=K(ee,te.effectiveTimingPct),{document:U,summary:{total:M.length,cached:G,executed:S,passed:O,regressed:T,missingBaseline:_,failed:L,results:M,unmatched:z},baseline:y}}function Pe(a){let n=[];if(n.push(`${a.total} workloads`),n.push(`${a.cached} cached`),n.push(`${a.executed} executed`),a.missingBaseline>0)n.push(`${a.missingBaseline} skipped (no matching baseline workload)`);if(a.failed>0){let h=a.results.filter((y)=>y.harnessFailed).map((y)=>t(y.workload));n.push(`${a.failed} failed (harness error, a command exited non-zero or produced no samples: ${h.join(", ")})`)}let f=a.results.filter((h)=>h.comparison?.verdict==="fail").map((h)=>{let y=h.comparison.timing,C=t(h.workload);return y?`${y.medianDeltaPct>0?"+":""}${y.medianDeltaPct.toFixed(1)}% median on ${C}`:C});return n.push(`${a.passed} passed ${a.regressed} regressed${f.length>0?` (${f.join(", ")})`:""}`),n.push(""),n.push(`Profile CI: ${a.regressed>0||a.failed>0?"\u2717":"\u2713"}`),`${n.join(`
5
5
  `)}
6
- `}async function xe(p,l){let{results:k,environment:y}=await me(p,!1),v=t(k.map((R)=>R.workload),k.map((R)=>R.run),y),M=s(p,l);return await a(v,M),M}async function Pe(p){let l=[];try{let y=new Bun.Glob("*.json");for await(let v of y.scan({cwd:p.baselineDir,absolute:!1}))l.push(v.replace(/\.json$/,""))}catch{return[]}return l.sort(),(await Promise.all(l.map(async(y)=>{let v=s(p,y);try{let M=await r(v);return{name:y,path:v,createdAt:M.createdAt,toolVersion:M.toolVersion,bunVersion:M.bunVersion,workloads:M.workloads.length,...M.git!==void 0&&{git:M.git}}}catch{return}}))).filter((y)=>y!==void 0)}function te(p){return p instanceof Error?p.message:String(p)}var q=(p)=>Bun.write(Bun.stdout,p),$e=(p)=>Bun.write(Bun.stderr,p);class Q extends Error{}async function F(p,l,k){await $e(l.endsWith(`
7
- `)?l:`${l}
8
- `),await $e(`${JSON.stringify({event:"error",protocolVersion:u,code:p,message:l.split(`
9
- `)[0],...k&&{data:k}})}
10
- `)}async function Z(p,l){return await q(p),l?0:2}async function Fe(p,l){if(!(p instanceof Q))throw p;return await F("invalid-flag",`${p.message}
11
- Run 'ostia ${l} --help'.`),2}function Be(p,l,k={}){if(k.allowAuto&&l==="auto")return _();let y=k.min??1,v=Number(l);if(!Number.isInteger(v)||v<y)throw new Q(`Invalid ${p} "${l}": expected an integer \u2265 ${y}${k.allowAuto?' (or "auto")':""}`);return v}function Le(p,l,k={}){let y=k.min??0,v=Number(l);if(!Number.isFinite(v)||v<y)throw new Q(`Invalid ${p} "${l}": expected a number \u2265 ${y}`);return v}var se=["table","json","jsonl","markdown","minimal"],pe=["collapsed","mermaid","speedscope","cpuprofile"],Ue=[...se,...pe];function oe(p,l){if(l.includes(p))return;throw new Q(`Unknown --format "${p}". Expected one of: ${l.join(", ")}`)}var ue=`No CPU evidence in this document; rerun with --cpu (ostia time) or --cpu (ostia bench)
12
- `;function Ee(p){return p.measurements.some((l)=>l.phase==="cpu")}async function fe(p,l,k={}){if(l.format==="json"&&l.exportJson){let y=f(p);if(await E(y,l.exportJson),!l.quiet)await q(y);return 0}if(l.exportJson)await a(p,l.exportJson);if(l.quiet)return 0;if(pe.includes(l.format)&&!Ee(p))return await F("no-cpu-evidence",ue),2;return await ne(await i[l.format].render(p,k)),0}async function le(p){let l=await S();if(!l){await F("config-missing",p?`No ostia.config.json found. "${p}" needs configured workloads.`:"No ostia.config.json found.");return}if(p&&l.workloads.length===0){let k=await L()??"ostia.config.json";await F("config-missing",`${k} has no "workloads" configured.`);return}return l}async function ne(p,l){if(p.text)await q(p.text);if(!p.files||p.files.length===0)return;if(l)for(let k of p.files){let y=k.path?`${l}/${k.path}`:l;await Bun.write(y,k.content),await q(`wrote ${y}
13
- `)}else if(p.files.length===1)await q(p.files[0].content);else for(let k of p.files)await q(`--- ${k.path??"(unnamed)"} ---
14
- ${k.content}
15
- `)}var _e=`ostia time [flags] <command...>
6
+ `}async function $e(a,n){let{results:f,environment:h}=await pe(a,!1),y=r(f.map((D)=>D.workload),f.map((D)=>D.run),h),C=o(a,n);return await u(y,C),C}async function Me(a){let n=[];try{let h=new Bun.Glob("*.json");for await(let y of h.scan({cwd:a.baselineDir,absolute:!1}))n.push(y.replace(/\.json$/,""))}catch{return[]}return n.sort(),(await Promise.all(n.map(async(h)=>{let y=o(a,h);try{let C=await s(y);return{name:h,path:y,createdAt:C.createdAt,toolVersion:C.toolVersion,bunVersion:C.bunVersion,workloads:C.workloads.length,...C.git!==void 0&&{git:C.git}}}catch{return}}))).filter((h)=>h!==void 0)}function ae(a){return a instanceof Error?a.message:String(a)}var H=(a)=>Bun.write(Bun.stdout,a),Se=(a)=>Bun.write(Bun.stderr,a);class se extends Error{}var Fe=[],Ae=new Set(["minimal","json","jsonl"]);async function We(a){let{isatty:n}=await import("tty");if(!n(2))return!0;return a.some((f,h)=>f==="--format"&&Ae.has(a[h+1]??"")||f.startsWith("--format=")&&Ae.has(f.slice(9)))}async function N(a,n,f){if(await Se(n.endsWith(`
7
+ `)?n:`${n}
8
+ `),!await We(Fe))return;await Se(`${JSON.stringify({event:"error",protocolVersion:p,code:a,message:n.split(`
9
+ `)[0],...f&&{data:f}})}
10
+ `)}async function V(a,n){return await H(a),n?0:2}async function Ue(a,n){return await N("invalid-flag",`${a.message}
11
+ Run 'ostia ${n} --help'.`),2}function He(a,n,f={}){if(f.allowAuto&&n==="auto")return Z();let h=f.min??1,y=Number(n);if(!Number.isInteger(y)||y<h)throw new se(`Invalid ${a} "${n}": expected an integer \u2265 ${h}${f.allowAuto?' (or "auto")':""}`);return y}var Le=["ns","us","ms","s"],re=["table","json","jsonl","markdown","minimal"],ge=["collapsed","mermaid","speedscope","cpuprofile"],qe=[...re,...ge],fe=`No CPU evidence in this document; rerun with --cpu (ostia time) or --cpu (ostia bench)
12
+ `;function De(a){return a.measurements.some((n)=>n.phase==="cpu")}async function he(a,n,f={}){if(n.format==="json"&&n.exportJson){let h=k(a);if(await J(h,n.exportJson),!n.quiet)await H(h);return 0}if(n.exportJson)await u(a,n.exportJson);if(n.quiet)return 0;if(ge.includes(n.format)&&!De(a))return await N("no-cpu-evidence",fe),2;return await ie(await i[n.format].render(a,f)),0}async function me(a){let n=await E();if(!n){await N("config-missing",a?`No ostia.config.json found. "${a}" needs configured workloads.`:"No ostia.config.json found.");return}if(a&&n.workloads.length===0){let f=await Y()??"ostia.config.json";await N("config-missing",`${f} has no "workloads" configured.`);return}return n}async function ie(a,n){if(a.text)await H(a.text);if(!a.files||a.files.length===0)return;if(n)for(let f of a.files){let h=f.path?`${n}/${f.path}`:n;await Bun.write(h,f.content),await H(`wrote ${h}
13
+ `)}else if(a.files.length===1)await H(a.files[0].content);else for(let f of a.files)await H(`--- ${f.path??"(unnamed)"} ---
14
+ ${f.content}
15
+ `)}var Je=`ostia time [flags] <command...>
16
16
  ostia time [flags] -- <argv...>
17
17
 
18
- Time one or more commands N times with warmup and report timing statistics. Each
19
- <command> is a string, whitespace-split into argv exactly like hyperfine -N (no shell,
20
- so no quoting/globbing/pipes/redirection) - it can't express an argument containing a
21
- space. \`-- <argv...>\` is the escape hatch: everything after -- is one command's argv
22
- verbatim, space-preserving, not further flag-parsed. It's one more command alongside any
23
- given as regular arguments, not a replacement for them.
18
+ Time commands as subprocesses. Each <command> is whitespace-split into argv (no shell);
19
+ everything after -- is one more command's argv, verbatim.
24
20
 
25
21
  Flags:
26
- --samples N exact number of timed trials per command (each command gets its
27
- own N trials, not a total split across them)
28
- --budget MS wall-clock time budget for the sampling loop (default: a
29
- hyperfine-style ~3s min-total-time loop when neither
30
- --samples nor --budget is given)
31
- --min-samples N hard floor on trials when --samples is not given
32
- --warmup N warmup trials, discarded (default: 3)
33
- --no-interleave run each command's whole trial loop to completion before the next
34
- command starts, instead of round-robin (one trial per command,
35
- repeated). Round-robin is the default with 2+ commands: it spreads
36
- drift over the run's wall-clock span (thermal throttling, a noisy
37
- neighbor process) evenly across every command instead of favoring
38
- whichever ran first or last. Meaningless (and ignored) with one
39
- command. Interleaved measurements carry Measurement.interleaved: true.
40
- --prepare CMD run CMD before every trial (warmup and --cpu/--heap trials
41
- included), unmeasured, in the same cwd; it must exit 0. Whitespace-
42
- split like the commands themselves (no shell) - there's no --
43
- equivalent for --prepare, so a hook needing an argument with a
44
- space needs ostia.config.ts's array form (prepare: ["a", "b c"])
45
- instead. Given once it applies to every command; given once per
46
- command it pairs up in order, so the same command can be timed
47
- warm and cold side by side. Its stderr is captured (bounded to
48
- 1 MiB) rather than streamed live, and folded into the error on a
49
- trial where it times out or exits non-zero.
50
- --time-source REGEX take each trial's time from the first REGEX match in the command's
51
- own stdout (then stderr), capture group 1, instead of its wall clock -
52
- e.g. --time-source "built in (\\d+)ms" for a build tool whose own
53
- summary excludes runtime startup. A trial whose output doesn't match
54
- contributes no sample (a time-source-no-match warning records it); if
55
- every trial misses, that command has no timing stats. Trials keep
56
- wallNs alongside the reported value.
57
- --time-unit UNIT unit of the --time-source number: ns | us | ms | s (default: ms)
58
- --cpu capture one instrumented CPU-profile trial (subprocess --cpu-prof)
59
- --heap capture one instrumented heap-snapshot trial (subprocess --heap-prof)
60
- --cpu-interval USEC CPU sampling interval in microseconds (default: 1000)
61
- --timeout MS kill a trial (or --prepare hook) with SIGKILL if it hasn't finished
62
- after this many ms. No default: unset never times out. A timed-out
63
- trial contributes no sample; if every trial of a command times out,
64
- that command has no timing stats.
65
- --ignore-failure[=CODE,...]
66
- treat the given exit codes as success (default with no value: every
67
- code). A matching trial still contributes its sample and gets no
68
- nonzero-exit warning, as if it had exited 0. Repeatable.
69
- --fail-on-nonzero stop a command's trial loop after its first non-zero, non-ignored
70
- exit (that trial's sample is still recorded) instead of always
71
- running its full sample count.
72
- --out-dir PATH directory for captured artifacts (default: node_modules/.cache/ostia)
73
- --no-noise-check skip the ~200ms machine noise floor reference measurement
74
- --export-json PATH write the full ProfileDocument to PATH
75
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
76
- --quiet suppress the rendered report (still writes --export-json)
77
- --help show this message
78
-
79
- Instrumented runs (--cpu, --heap) are labeled separately from clean timing and never
80
- mixed into the timing statistics.
81
-
82
- Exit codes: 0 pass, 2 harness error (a command had a non-zero, non-ignored exit; a
83
- timed-out or --time-source-mismatched command had no timing stats at all; or a bad flag).
84
- 130 if cancelled with Ctrl-C. 1 is never returned by "time" - it's reserved for
85
- "compare"/"ci" regressions.
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.
86
44
 
87
45
  Examples:
88
- ostia time "bun ./fixtures/work.ts"
89
- ostia time --samples 25 --warmup 3 "bun a.ts" "bun b.ts"
90
- ostia time --no-interleave "bun a.ts" "bun b.ts"
46
+ ostia time "bun a.ts" "bun b.ts"
47
+ ostia time --samples 25 --cpu --heap "bun src/server.ts"
91
48
  ostia time --prepare "rm -rf dist" "bun build.ts"
92
49
  ostia time --time-source "built in (\\d+)ms" "bun build.ts"
93
- ostia time --cpu --heap "bun src/server.ts"
94
- ostia time --format json "bun a.ts"
95
- ostia time --ignore-failure=3 "bun flaky.ts"
96
- ostia time --fail-on-nonzero "bun might-hang-if-broken.ts"
97
- ostia time -- bun -e "console.log('a b')" # argument with a space, unsplit
98
- `,We=Array.from({length:255},(p,l)=>l+1),je=`ostia bench [flags] <suite.ts...>
50
+ ostia time -- bun -e "console.log('a b')"
51
+ `,Ge=Array.from({length:255},(a,n)=>n+1),Ee=`ostia bench [flags] <suite.ts...>
99
52
 
100
- Run in-process benchmark suites (registered via group()/task()). Each suite file runs
101
- in its own spawned child process (isolated from CLI startup state).
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.
102
55
 
103
56
  Flags:
104
- --budget MS sampling budget per task; always runs at least this long (default: 500).
105
- --samples N exact trial count per task; when set, the budget is ignored -
106
- the in-process equivalent of "ostia time"'s --samples.
107
- --min-samples N hard floor on samples per task, kept even when it overruns the
108
- budget. Default: cost-aware - as many as fit in the budget (max 20),
109
- but never below the floor the task's per-trial cost earns it: 3 at
110
- <=1ms, +2 per decade of cost, 10 from ~3s up. Cheap tasks are
111
- time-bound and collect thousands either way; only the few expensive
112
- tasks in a suite pay for the extra rigor. A run that ends below its
113
- cost-class floor (only possible with an explicit --min-samples or
114
- per-task minSamples) carries a "low-sample-count" warning.
115
- --jobs N|auto suite files to run at once, each still in its own process (default: 1).
116
- Concurrent CPU-bound processes contend for cores, caches and turbo
117
- headroom, so numbers taken at --jobs > 1 are noisier and not
118
- like-for-like with a baseline measured at 1. "auto" = CPU count.
119
- --gc / --no-gc Bun.gc(true) between trials (default: off - hides allocation cost).
120
- Per-task { gc } / per-group { gc } override this default; --no-gc /
121
- --gc on the CLI overrides those and ostia.config.json individually,
122
- so a config-wide default can be turned off for one invocation.
123
- --cpu / --no-cpu capture an extra phase: "cpu" measurement per task (200ms of the
124
- task looped under the JSC sampling profiler, JIT tiers included) on
125
- top of its timing numbers. Per-task { cpu } / per-group { cpu }
126
- override this default. Once captured, "ostia compare" reports
127
- per-frame CPU deltas the same way it already does for "ostia time --cpu".
128
- --alloc / --no-alloc capture an extra phase: "memstats" measurement per task: bytes
129
- allocated per call, from a Bun.gc(true)-bracketed batch of 100 calls.
130
- Per-task { alloc } / per-group { alloc } override this default.
131
- --filter REGEX only run tasks whose "group/name" id matches this regex (substring,
132
- case-sensitive; unmatched tasks are skipped, not timed)
133
- --isolate / --no-isolate give every task its own subprocess instead of sharing its
134
- suite file's, isolating JIT tier state and heap shape between tasks
135
- the way suite files are already isolated from each other. Per-task
136
- { isolate } / per-group { isolate } override this default.
137
- --jobs then pools across those per-task processes, so pair a
138
- higher --jobs with --isolate deliberately: overhead now scales
139
- with task count, not file count.
140
- --timeout MS kill a suite file's subprocess (or, under --isolate, one task's
141
- dedicated subprocess) with SIGKILL if it hasn't finished after this
142
- many ms. No default: unset never times out.
143
- --preload PATH script imported before each suite file loads, in the same
144
- subprocess (repeatable; runs in the order given). Use it to
145
- install globals (jsdom's document/window) or register a
146
- Bun.plugin() file-loader before the suite's own code runs.
147
- --bun-flags FLAGS extra flags passed through to the \`bun\` invocation that runs each
148
- suite file (repeatable; space-separated flags in one value are all
149
- appended). Useful for packages whose exports map branches on a
150
- resolution condition Bun doesn't set by default, e.g. Svelte/Vue's
151
- "browser" vs "default" build: --bun-flags="--conditions=browser"
152
- --out-dir PATH directory for scratch IPC files (default: node_modules/.cache/ostia)
153
- --no-noise-check skip the ~200ms machine noise floor reference measurement
154
- --export-json PATH write the full ProfileDocument to PATH
155
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
156
- "minimal" is protocol v1 (see README's "Using ostia from an AI
157
- agent"): one JSON "run" event per task, {event, protocolVersion,
158
- schemaVersion, workloadId, task, group, description, params,
159
- samples, batch, mean, median, stddevPct, relative,
160
- warnings[{code,data}]} in ns, no raw sample array - built to pipe
161
- into an LLM agent's context.
162
- --quiet suppress the rendered report (still writes --export-json)
163
- --help show this message
164
-
165
- Suite files register tasks like:
166
- import { group, task } from "<pkg>"
167
- group("parse", () => {
168
- task("small input", () => parse(smallBuf))
169
- task("full pipeline", () => build(), { budgetMs: 2000, minSamples: 10 })
170
- }, { description: "parser throughput on representative inputs" })
171
- Per-task options override --budget / --min-samples / --gc / --isolate / --cpu / --alloc
172
- for that task only; per-group { gc } / { isolate } / { cpu } / { alloc } set the default for
173
- every task in that group.
174
- Optional { description } on group() and task() flows into the document (Workload.description
175
- / Workload.groupDescription) so the intent travels with the numbers.
176
-
177
- Sweep one or more dimensions with sweep(dims, fn): a cartesian product over the
178
- dimensions, calling fn once per point. task() calls inside automatically inherit the
179
- point as Workload.params (an explicit { params } on a task merges over it):
180
- import { group, task, range, sweep } from "<pkg>"
181
- group("parse", () => {
182
- sweep({ size: range(100, 10_000), impl: ["current", "fast"] }, ({ size, impl }) => {
183
- const input = buildInput(size) // setup, runs once per point, unmeasured
184
- task(\`\${impl}\`, () => impls[impl](input))
185
- })
186
- })
187
- range(start, end, multiplier?) is the geometric point generator that feeds sweep()
188
- (mitata's .range(), default multiplier 8, always ending on the end value).
189
-
190
- task.skip(...) / group.skip(...) register without measuring: the document still
191
- carries the workload (marked skipped) instead of it being absent, so a renderer
192
- prints "- skipped" and compare reports it as unchanged with a warning rather than
193
- silently passing. task.only(...) / group.only(...) restrict the suite file to only
194
- the selected tasks (--filter still applies on top) and print a one-line notice to
195
- stderr, so a forgotten .only is visible.
196
-
197
- Project defaults: with no suite files given on the command line, ostia falls back to
198
- ostia.config.json's "bench" section in the current directory - suites is a list of globs
199
- (expanded with Bun.Glob), the rest are the same defaults as their matching flag:
200
- { "bench": { "suites": ["bench/**/*.bench.ts"], "preload": ["./bench/setup.ts"], "jobs": "auto" } }
201
- Any suite files given on the command line replace (not merge with) the config's "suites"
202
- list; every other flag/config field is overridden individually, so "ostia bench --jobs 1"
203
- still works as a one-off override without editing the config.
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 (--no-cpu)
64
+ --alloc extra per-task bytes-allocated-per-call measurement (--no-alloc)
65
+ --filter REGEX only tasks whose "group/name" matches
66
+ --preload PATH import before each suite file (repeatable, in order)
67
+ --bun-flags FLAGS extra flags for the bun process running each suite (repeatable)
68
+ --timeout MS kill a suite (or isolated task) process after MS
69
+ --out-dir PATH scratch directory (default: node_modules/.cache/ostia)
70
+ --no-noise-check skip the ~200ms noise-floor measurement
71
+ --export-json PATH write the ProfileDocument to PATH
72
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
73
+ --quiet don't print the report
74
+ --help show this message
75
+
76
+ Per-task/per-group options ({ budgetMs, minSamples, gc, isolate, cpu, alloc }) override
77
+ the suite-wide defaults. Exit codes: 0 ok, 2 a suite failed (or a bad flag), 130 Ctrl-C.
204
78
 
205
79
  Examples:
206
- ostia bench benches/parse.ts
207
- ostia bench --budget 1000 --min-samples 50 benches/*.ts
208
- ostia bench benches/*.ts --filter parse
209
- ostia bench benches/*.ts --jobs auto --format minimal
210
- ostia bench benches/*.ts --cpu --alloc
211
- ostia bench --preload ./bench/jsdom-setup.ts benches/*.dom.bench.ts
212
- ostia bench --bun-flags="--conditions=browser" bench/*.dom.bench.ts
213
- ostia bench # picks up suites/preload/jobs from ostia.config.json
214
- `,Se=`ostia compare <base.json> <candidate.json>
215
- ostia compare <candidate.json> --baseline <path.json>
80
+ ostia bench bench/*.ts
81
+ ostia bench bench/*.ts --filter parse --cpu --alloc
82
+ ostia bench --preload ./bench/dom-setup.ts --bun-flags="--conditions=browser" bench/*.ts
83
+ `,Re=`ostia compare <base.json> <candidate.json>
84
+ ostia compare <candidate.json> --baseline <base.json>
216
85
 
217
- Compare two ProfileDocuments (matched by workload id) and rank timing/frame/heap deltas.
86
+ Compare two ProfileDocuments by workload id. Gates on ostia.config's "thresholds" when
87
+ present (the same ones "ostia ci" uses), else the defaults.
218
88
 
219
89
  Flags:
220
- --export-json PATH write the resulting document (with comparisons) to PATH
221
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
222
- "minimal" is protocol v1 (see README's "Using ostia from an AI
223
- agent"): a "run" event per task with delta: {medianPct, meanPct,
224
- verdict, pass, ci95?, pValue?, effectiveTimingPct, matched}, an
225
- "unmatched" event per workload absent from one side, and a
226
- trailing "summary" event with the overall verdict and exitCode.
227
- --timing-pct N override thresholds.timingPct (percent)
228
- --alpha N override thresholds.alpha (Mann-Whitney significance level)
229
- --no-config ignore ostia.config.ts/.json; use DEFAULT_THRESHOLDS
230
- --quiet suppress the rendered report (still writes --export-json)
231
- --help show this message
232
-
233
- Reads ostia.config.ts/.json's "thresholds" when present (same discovery as "ostia ci"),
234
- else DEFAULT_THRESHOLDS; --timing-pct/--alpha override individual fields on top.
90
+ --baseline PATH the base document, when only the candidate is positional
91
+ --export-json PATH write the candidate document with comparisons to PATH
92
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
93
+ --quiet don't print the report
94
+ --help show this message
235
95
 
236
- Exit codes: 0 pass, 1 at least one workload regressed, 2 nothing was compared (zero
237
- matched workloads) or a harness error (documents failed to load, or a bad flag).
238
-
239
- Examples:
240
- ostia compare before.json after.json
241
- ostia compare after.json --baseline .ostia/baselines/main.json
242
- `,qe=`ostia report <document.json> [flags]
96
+ Exit codes: 0 pass, 1 a workload regressed, 2 nothing matched or a harness error.
97
+ `,Ve=`ostia report <document.json> [flags]
243
98
 
244
99
  Render a saved ProfileDocument. Files, not a GUI for the visualization formats -
245
100
  hand the output to speedscope.app, flamegraph.pl, or a Mermaid renderer.
@@ -267,31 +122,24 @@ Examples:
267
122
  ostia report doc.json --format markdown
268
123
  ostia report doc.json --format speedscope --out-dir node_modules/.cache/ostia/viz
269
124
  ostia report doc.json --format collapsed | flamegraph.pl > flame.svg
270
- `,He=`ostia ci [--full] [--baseline NAME] [--save-baseline]
125
+ `,ze=`ostia ci [flags]
271
126
 
272
- Load ostia.config.json, run configured workloads (reusing cached results when their
273
- fingerprint is unchanged), compare against the named baseline, and gate on regressions.
127
+ Run ostia.config's workloads (reusing cached runs whose declared inputs are unchanged),
128
+ compare against a saved baseline, and gate on regressions.
274
129
 
275
130
  Flags:
276
- --full ignore the cache; rerun every configured workload
277
- --baseline NAME baseline name (default: config's "baseline" field, or "main")
278
- --save-baseline after a pass (no regressions, no harness failures), write the
279
- just-measured document as the new baseline at the same path
280
- just compared against - promotes today's numbers to
281
- tomorrow's floor in one step.
282
- --export-json PATH write the resulting document (with comparisons) to PATH
283
- --format FORMAT table | json | jsonl | markdown | minimal (default: table)
284
- --on-missing-baseline POLICY "warn" or "fail" when a configured workload has no matching
285
- row in the baseline (default: "fail" when every configured
286
- workload is missing, "warn" otherwise)
287
- --no-noise-check skip the ~200ms noise-floor reference measurement (default:
288
- on; same as ostia.config's noiseCheck: false)
289
- --quiet suppress the rendered report entirely (still writes --export-json)
290
- --help show this message
291
-
292
- Exit codes: 0 pass, 1 regression, 2 harness error (missing config/baseline, every trial of
293
- a workload exited non-zero, an "onMissingBaseline: fail" mismatch, or a spawn failure).
294
- `,ce=`ostia baseline <save|list|show> [args]
131
+ --full ignore the cache
132
+ --baseline NAME baseline to compare against (default: config "baseline", or "main")
133
+ --save-baseline after a pass, save this run as the new baseline
134
+ --no-noise-check skip the ~200ms noise-floor measurement
135
+ --export-json PATH write the candidate document with comparisons to PATH
136
+ --format FORMAT table | json | jsonl | markdown | minimal (default: table)
137
+ --quiet don't print the report
138
+ --help show this message
139
+
140
+ Exit codes: 0 pass, 1 regression, 2 harness error (missing config/baseline, a workload
141
+ failed or produced no samples, or an onMissingBaseline "fail" mismatch).
142
+ `,de=`ostia baseline <save|list|show> [args]
295
143
 
296
144
  Manage the baseline ProfileDocuments "ostia ci" gates against and "ostia compare --baseline"
297
145
  reads.
@@ -310,24 +158,24 @@ Examples:
310
158
  ostia baseline list
311
159
  ostia baseline show main
312
160
  ostia baseline show main --format markdown
313
- `;function ae(p,l,k,y,v,M){let R=y;for(let A=0;A<p.length;A++){let T=p[A],K=M?.(T,A,y);if(K!==void 0){A=K;continue}let B=k[T];if(!B){if(T.startsWith("-"))throw new Q(`Unknown flag "${T}" for "ostia ${l}".`);v(T,y);continue}switch(B.spec.kind){case"string":R[B.dest]=p[++A];break;case"int":R[B.dest]=Be(T,p[++A],B.spec);break;case"float":R[B.dest]=Le(T,p[++A],B.spec);break;case"bool":R[B.dest]=B.spec.value;break;case"list":R[B.dest].push(p[++A]??"");break;case"enum":{let U=p[++A];if(!B.spec.values.includes(U))throw new Q(`Invalid ${T} "${U}": expected one of: ${B.spec.values.join(", ")}`);R[B.dest]=U;break}}}return y}var Je={"--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:"string"}},"--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}},"--fail-on-nonzero":{dest:"failOnNonzero",spec:{kind:"bool",value:!0}},"--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:"string"}},"--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 ze(p){return ae(p,"time",Je,{commands:[],prepare:[],interleave:!0,cpu:!1,heap:!1,ignoreExitCodes:[],failOnNonzero:!1,noiseCheck:!0,format:"table",quiet:!1,help:!1},(k,y)=>y.commands.push(k),(k,y,v)=>{if(k==="--")return v.argvCommand=p.slice(y+1),p.length;if(k==="--ignore-failure"||k.startsWith("--ignore-failure=")){let M=k.startsWith("--ignore-failure=")?k.slice(17):void 0;return v.ignoreExitCodes.push(...M===void 0?We:M.split(",").map(Number)),y}return})}async function Te(p){let l=new AbortController,k=()=>l.abort();process.on("SIGINT",k);try{return{result:await p(l.signal),aborted:l.signal.aborted}}finally{process.off("SIGINT",k)}}var Me=["ns","us","ms","s"];async function Ge(p){let l=ze(p),k=(l.argvCommand?.length??0)>0;if(l.help||l.commands.length===0&&!k)return Z(_e,l.help);oe(l.format,se);let y=l.commands.length+(k?1:0);if(l.prepare.length>1&&l.prepare.length!==y)return await F("invalid-flag",`--prepare given ${l.prepare.length} times for ${y} command(s): give it once (applies to all) or once per command.`),2;if(l.timeUnit!==void 0&&!Me.includes(l.timeUnit))return await F("invalid-flag",`Unknown --time-unit "${l.timeUnit}". Expected one of: ${Me.join(", ")}`),2;if(l.timeSource!==void 0)try{new RegExp(l.timeSource)}catch(O){return await F("invalid-flag",`Invalid --time-source regex: ${te(O)}`),2}let v=l.timeSource!==void 0?{pattern:l.timeSource,unit:l.timeUnit}:void 0,M=(O)=>l.prepare.length===1?l.prepare[0]:l.prepare[O],R=l.commands.map((O,D)=>({command:O,prepare:M(D)}));if(k)R.push({command:l.argvCommand,prepare:M(l.commands.length)});let A,T;try{({result:A,aborted:T}=await Te((O)=>V({commands:R,timeSource:v,samples:l.samples,budgetMs:l.budgetMs,minSamples:l.minSamples,warmup:l.warmup,interleave:l.interleave,cpu:l.cpu,heap:l.heap,cpuIntervalUs:l.cpuIntervalUs,timeoutMs:l.timeoutMs,ignoreExitCodes:l.ignoreExitCodes,failOnNonzero:l.failOnNonzero,outDir:l.outDir,noiseCheck:l.noiseCheck,signal:O})))}catch(O){return await F("spawn-failed",`Run failed: ${te(O)}`),2}let K=await fe(A,l);if(K!==0)return K;if(T)return 130;let B=new Set(l.ignoreExitCodes),U=A.workloads.some((O)=>!A.measurements.some((D)=>D.workloadId===O.id&&D.timing)),G=A.measurements.some((O)=>O.trials.some((D)=>D.exitCode!==void 0&&D.exitCode!==0&&!B.has(D.exitCode)));if(G||U){let O=A.measurements.some((Y)=>Y.warnings.some((X)=>X.code==="timeout")),D=A.measurements.some((Y)=>Y.warnings.some((X)=>X.code==="time-source-no-match"));return await F(G?"command-failed":O?"timeout":D?"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 Ve={"--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}},"--alloc":{dest:"alloc",spec:{kind:"bool",value:!0}},"--no-alloc":{dest:"alloc",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:"string"}},"--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 Ke(p){return ae(p,"bench",Ve,{suites:[],preload:[],bunFlags:[],noiseCheck:!0,format:"table",quiet:!1,help:!1},(k,y)=>y.suites.push(k),(k,y,v)=>{if(k==="--bun-flags")return v.bunFlags.push(...m(p[y+1]??"")),y+1;if(k.startsWith("--bun-flags="))return v.bunFlags.push(...m(k.slice(12))),y;return})}async function Xe(p){let l=Ke(p);if(l.help)return Z(je,!0);oe(l.format,se);let k=await S(),y=await J(l,k?.bench);if(y.suites.length===0)return Z(je,!1);if(y.jobs!==void 0&&!(y.jobs>=1))return await F("invalid-flag",'--jobs expects a positive integer or "auto".'),2;let v,M;try{({result:v,aborted:M}=await Te((A)=>b({...y,signal:A})))}catch(A){return await F("spawn-failed",`Bench failed: ${te(A)}`),2}let R=await fe(v,l);if(R!==0)return R;return M?130:0}var Ze={"--baseline":{dest:"baseline",spec:{kind:"string"}},"--export-json":{dest:"exportJson",spec:{kind:"string"}},"--format":{dest:"format",spec:{kind:"string"}},"--timing-pct":{dest:"timingPct",spec:{kind:"float"}},"--alpha":{dest:"alpha",spec:{kind:"float"}},"--no-config":{dest:"noConfig",spec:{kind:"bool",value:!0}},"--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 Ye(p){return ae(p,"compare",Ze,{paths:[],format:"table",quiet:!1,help:!1,noConfig:!1},(k,y)=>y.paths.push(k))}async function Qe(p){let l=c,k="defaults";if(!p.noConfig){let y=await S();if(y)l=y.thresholds,k=await L()??"ostia.config.json"}if(p.timingPct!==void 0||p.alpha!==void 0)l={...l,...p.timingPct!==void 0&&{timingPct:p.timingPct},...p.alpha!==void 0&&{alpha:p.alpha}};return{thresholds:l,source:k}}async function et(p){let l=Ye(p);if(l.help)return Z(Se,!0);oe(l.format,se);let k,y;if(l.baseline)k=l.baseline,y=l.paths[0];else k=l.paths[0],y=l.paths[1];if(!k||!y)return Z(Se,!1);let v,M;try{[v,M]=await Promise.all([r(k),r(y)])}catch(O){return await F("document-load-failed",`Failed to load documents: ${te(O)}`),2}let{thresholds:R,source:A}=await Qe(l),T=C(v,M,R),K={...M,comparisons:T.comparisons,comparisonSummary:T.summary,unmatched:{baseOnly:T.unmatched.baseOnly.map((O)=>O.id),candOnly:T.unmatched.candOnly.map((O)=>O.id)}},B=l.format==="table"||l.format==="markdown";if(!l.quiet&&B){if(await q(`thresholds: ${A}
314
- `),v.git&&M.git)await q(`base ${w(v.git)} \u2192 cand ${w(M.git)}
315
- `);if(l.format==="table"&&T.summary.effectiveTimingPct>R.timingPct)await q(`threshold ${R.timingPct}% (widened to ${T.summary.effectiveTimingPct.toFixed(1)}% by noise floor)
316
- `)}let U=T.summary.matched===0?2:T.summary.verdict==="fail"?1:0,G=await fe(K,l,{protocol:{command:"compare",exitCode:U,unmatched:T.unmatched,baseGit:v.git,candGit:M.git,...l.exportJson&&{exportedTo:l.exportJson}}});if(G!==0)return G;if(!l.quiet&&(l.format==="table"||l.format==="markdown")&&(T.unmatched.baseOnly.length>0||T.unmatched.candOnly.length>0))await q(tt(T.unmatched,l.format));if(T.summary.matched===0)await F("no-matches","No workload matched between base and candidate; nothing was compared.");return U}function tt(p,l){let k=(v)=>v.map((M)=>n(M)).join(", ");if(l==="markdown"){let v=["### Unmatched",""];if(p.baseOnly.length>0)v.push(`- baseline only: ${k(p.baseOnly)}`);if(p.candOnly.length>0)v.push(`- candidate only: ${k(p.candOnly)}`);return v.push(""),`${v.join(`
161
+ `;function le(a,n,f,h,y,C){let D=h;for(let M=0;M<a.length;M++){let S=a[M],G=C?.(S,M,h);if(G!==void 0){M=G;continue}let L=S.startsWith("--")?S.indexOf("="):-1,U=L>0?S.slice(0,L):S,O=f[U];if(!O||L>0&&O.spec.kind==="bool"){if(S.startsWith("-"))throw new se(`Unknown flag "${S}" for "ostia ${n}".`);y(S,h);continue}let T=()=>L>0?S.slice(L+1):a[++M];switch(O.spec.kind){case"string":D[O.dest]=T();break;case"int":D[O.dest]=He(U,T(),O.spec);break;case"bool":D[O.dest]=O.spec.value;break;case"list":D[O.dest].push(T()??"");break;case"enum":{let _=T();if(!O.spec.values.includes(_))throw new se(`Invalid ${U} "${_}": expected one of: ${O.spec.values.join(", ")}`);D[O.dest]=_;break}}}return h}var Ke={"--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:Le}},"--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:re}},"--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 Xe(a){return le(a,"time",Ke,{commands:[],prepare:[],interleave:!0,cpu:!1,heap:!1,ignoreExitCodes:[],noiseCheck:!0,format:"table",quiet:!1,help:!1},(f,h)=>h.commands.push(f),(f,h,y)=>{if(f==="--")return y.argvCommand=a.slice(h+1),a.length;if(f==="--ignore-failure"||f.startsWith("--ignore-failure=")){let C=f.startsWith("--ignore-failure=")?f.slice(17):void 0;return y.ignoreExitCodes.push(...C===void 0?Ge:C.split(",").map(Number)),h}return})}async function Oe(a){let n=new AbortController,f=()=>n.abort();process.on("SIGINT",f);try{return{result:await a(n.signal),aborted:n.signal.aborted}}finally{process.off("SIGINT",f)}}async function Ze(a){let n=Xe(a),f=(n.argvCommand?.length??0)>0;if(n.help||n.commands.length===0&&!f)return V(Je,n.help);let h=n.commands.length+(f?1:0);if(n.prepare.length>1&&n.prepare.length!==h)return await N("invalid-flag",`--prepare given ${n.prepare.length} times for ${h} command(s): give it once (applies to all) or once per command.`),2;if(n.timeSource!==void 0)try{new RegExp(n.timeSource)}catch(T){return await N("invalid-flag",`Invalid --time-source regex: ${ae(T)}`),2}let y=n.timeSource!==void 0?{pattern:n.timeSource,unit:n.timeUnit}:void 0,C=(T)=>n.prepare.length===1?n.prepare[0]:n.prepare[T],D=n.commands.map((T,_)=>({command:T,prepare:C(_)}));if(f)D.push({command:n.argvCommand,prepare:C(n.commands.length)});let M,S;try{({result:M,aborted:S}=await Oe((T)=>Q({commands:D,timeSource:y,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:T})))}catch(T){return await N("spawn-failed",`Run failed: ${ae(T)}`),2}let G=await he(M,n);if(G!==0)return G;if(S)return 130;let L=M.workloads.map((T)=>M.measurements.find((_)=>_.workloadId===T.id&&_.phase==="timing")??{trials:[],timing:void 0}),U=L.some((T)=>!T.timing),O=L.some((T)=>T.timing&&B(T,n.ignoreExitCodes));if(O||U){let T=M.measurements.some((ee)=>ee.warnings.some((z)=>z.code==="timeout")),_=M.measurements.some((ee)=>ee.warnings.some((z)=>z.code==="time-source-no-match"));return await N(O?"command-failed":T?"timeout":_?"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 Qe={"--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}},"--alloc":{dest:"alloc",spec:{kind:"bool",value:!0}},"--no-alloc":{dest:"alloc",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:re}},"--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 Ye(a){return le(a,"bench",Qe,{suites:[],preload:[],bunFlags:[],noiseCheck:!0,format:"table",quiet:!1,help:!1},(f,h)=>h.suites.push(f),(f,h,y)=>{if(f==="--bun-flags")return y.bunFlags.push(...b(a[h+1]??"")),h+1;if(f.startsWith("--bun-flags="))return y.bunFlags.push(...b(f.slice(12))),h;return})}async function en(a){let n=Ye(a);if(n.help)return V(Ee,!0);let f=await E(),h=await W(n,f?.bench);if(h.suites.length===0)return V(Ee,!1);if(h.jobs!==void 0&&!(h.jobs>=1))return await N("invalid-flag",'--jobs expects a positive integer or "auto".'),2;let y,C;try{({result:y,aborted:C}=await Oe((M)=>v({...h,signal:M})))}catch(M){return await N("spawn-failed",`Bench failed: ${ae(M)}`),2}let D=await he(y,n);if(D!==0)return D;return C?130:0}var nn={"--baseline":{dest:"baseline",spec:{kind:"string"}},"--export-json":{dest:"exportJson",spec:{kind:"string"}},"--format":{dest:"format",spec:{kind:"enum",values:re}},"--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 tn(a){return le(a,"compare",nn,{paths:[],format:"table",quiet:!1,help:!1},(f,h)=>h.paths.push(f))}async function sn(){let a=await E();if(!a)return{thresholds:m,source:"defaults"};return{thresholds:a.thresholds,source:await Y()??"ostia.config.json"}}async function on(a){let n=tn(a);if(n.help)return V(Re,!0);let f,h;if(n.baseline)f=n.baseline,h=n.paths[0];else f=n.paths[0],h=n.paths[1];if(!f||!h)return V(Re,!1);let y,C;try{[y,C]=await Promise.all([s(f),s(h)])}catch(T){return await N("document-load-failed",`Failed to load documents: ${ae(T)}`),2}let{thresholds:D,source:M}=await sn(),S=F(y,C,D),G={...C,comparisons:S.comparisons,comparisonSummary:S.summary,unmatched:{baseOnly:S.unmatched.baseOnly.map((T)=>T.id),candOnly:S.unmatched.candOnly.map((T)=>T.id)}},L=n.format==="table"||n.format==="markdown";if(!n.quiet&&L){if(await H(`thresholds: ${M}
162
+ `),y.git&&C.git)await H(`base ${g(y.git)} \u2192 cand ${g(C.git)}
163
+ `);if(n.format==="table"&&S.summary.effectiveTimingPct>D.timingPct)await H(`threshold ${D.timingPct}% (widened to ${S.summary.effectiveTimingPct.toFixed(1)}% by noise floor)
164
+ `)}let U=S.summary.matched===0?2:S.summary.verdict==="fail"?1:0,O=await he(G,n,{protocol:{command:"compare",exitCode:U,unmatched:S.unmatched,baseGit:y.git,candGit:C.git,...n.exportJson&&{exportedTo:n.exportJson}}});if(O!==0)return O;if(!n.quiet&&(n.format==="table"||n.format==="markdown")&&(S.unmatched.baseOnly.length>0||S.unmatched.candOnly.length>0))await H(an(S.unmatched,n.format));if(S.summary.matched===0)await N("no-matches","No workload matched between base and candidate; nothing was compared.");return U}function an(a,n){let f=(y)=>y.map((C)=>t(C)).join(", ");if(n==="markdown"){let y=["### Unmatched",""];if(a.baseOnly.length>0)y.push(`- baseline only: ${f(a.baseOnly)}`);if(a.candOnly.length>0)y.push(`- candidate only: ${f(a.candOnly)}`);return y.push(""),`${y.join(`
317
165
  `)}
318
- `}let y=["","Unmatched:"];if(p.baseOnly.length>0)y.push(` baseline only: ${k(p.baseOnly)}`);if(p.candOnly.length>0)y.push(` candidate only: ${k(p.candOnly)}`);return`${y.join(`
166
+ `}let h=["","Unmatched:"];if(a.baseOnly.length>0)h.push(` baseline only: ${f(a.baseOnly)}`);if(a.candOnly.length>0)h.push(` candidate only: ${f(a.candOnly)}`);return`${h.join(`
319
167
  `)}
320
- `}var nt={"--format":{dest:"format",spec:{kind:"string"}},"--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 st(p){return ae(p,"report",nt,{format:"table",help:!1},(k,y)=>{if(y.path!==void 0)throw new Q(`"ostia report" takes exactly one document path, got "${y.path}" and "${k}".`);y.path=k})}async function Re(p){let l=st(p);if(l.help||!l.path)return Z(qe,l.help);oe(l.format,Ue);let k;try{k=await r(l.path)}catch(M){return await F("document-load-failed",`Failed to load ${l.path}: ${te(M)}`),2}if(pe.includes(l.format)&&!l.measurementId&&!Ee(k))return await F("no-cpu-evidence",ue),2;let v=await i[l.format].render(k,{measurementId:l.measurementId});if(!v.text&&(!v.files||v.files.length===0))return await F("no-cpu-evidence",l.measurementId?`No CPU evidence found for measurement "${l.measurementId}".`:ue),2;return await ne(v,l.outDir),0}var ot={"--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:"string"}},"--on-missing-baseline":{dest:"onMissingBaseline",spec:{kind:"enum",values:["warn","fail"]}},"--no-noise-check":{dest:"noNoiseCheck",spec:{kind:"bool",value:!0}},"--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 at(p){return ae(p,"ci",ot,{full:!1,saveBaseline:!1,format:"table",quiet:!1,help:!1,noNoiseCheck:!1},(k)=>{throw new Q(`Unknown flag "${k}" for "ostia ci".`)})}async function it(p){let l=at(p);if(l.help)return Z(He,!0);oe(l.format,se);let k=await le("ostia ci");if(!k)return 2;let y={...k,...l.onMissingBaseline!==void 0&&{onMissingBaseline:l.onMissingBaseline},...l.noNoiseCheck&&{noiseCheck:!1}},v;try{v=await ye({config:y,full:l.full,baselineName:l.baseline})}catch(A){if(A instanceof ie||A instanceof re)return await F("baseline-missing",A.message),2;return await F("spawn-failed",`CI run failed: ${te(A)}`),2}let M=l.format==="json"&&!l.quiet?f(v.document):void 0;if(l.exportJson)if(M)await E(M,l.exportJson);else await a(v.document,l.exportJson);if(l.saveBaseline&&v.summary.regressed===0&&v.summary.failed===0)await a(v.document,s(y,l.baseline));let R=v.summary.failed>0?2:v.summary.regressed>0?1:0;if(v.summary.failed>0){let A=v.summary.results.filter((T)=>T.harnessFailed).map((T)=>n(T.workload));await F("command-failed",`${v.summary.failed} workload(s) failed (harness error, every trial exited non-zero): ${A.join(", ")}`)}if(!l.quiet){let A=l.baseline??y.baseline;if(l.format==="table"){if(await q(Ce(v.summary)),v.document.comparisons&&v.document.comparisons.length>0)await ne(await i.table.render(v.document,{}))}else if(M!==void 0)await q(M);else if(l.format==="markdown")await q(`## ostia ci
168
+ `}var rn={"--format":{dest:"format",spec:{kind:"enum",values:qe}},"--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 ln(a){return le(a,"report",rn,{format:"table",help:!1},(f,h)=>{if(h.path!==void 0)throw new se(`"ostia report" takes exactly one document path, got "${h.path}" and "${f}".`);h.path=f})}async function je(a){let n=ln(a);if(n.help||!n.path)return V(Ve,n.help);let f;try{f=await s(n.path)}catch(C){return await N("document-load-failed",`Failed to load ${n.path}: ${ae(C)}`),2}if(ge.includes(n.format)&&!n.measurementId&&!De(f))return await N("no-cpu-evidence",fe),2;let y=await i[n.format].render(f,{measurementId:n.measurementId});if(!y.text&&(!y.files||y.files.length===0))return await N("no-cpu-evidence",n.measurementId?`No CPU evidence found for measurement "${n.measurementId}".`:fe),2;return await ie(y,n.outDir),0}var cn={"--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:re}},"--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 un(a){return le(a,"ci",cn,{full:!1,saveBaseline:!1,format:"table",quiet:!1,help:!1,noiseCheck:!0},(f)=>{throw new se(`Unknown flag "${f}" for "ostia ci".`)})}async function mn(a){let n=un(a);if(n.help)return V(ze,!0);let f=await me("ostia ci");if(!f)return 2;let h={...f,...!n.noiseCheck&&{noiseCheck:!1}},y;try{y=await xe({config:h,full:n.full,baselineName:n.baseline})}catch(M){if(M instanceof ce||M instanceof ue)return await N("baseline-missing",M.message),2;if(M instanceof x)return await N("document-load-failed",M.message),2;return await N("spawn-failed",`CI run failed: ${ae(M)}`),2}let C=n.format==="json"&&!n.quiet?k(y.document):void 0;if(n.exportJson)if(C)await J(C,n.exportJson);else await u(y.document,n.exportJson);if(n.saveBaseline&&y.summary.regressed===0&&y.summary.failed===0)await u(y.document,o(h,n.baseline));let D=y.summary.failed>0?2:y.summary.regressed>0?1:0;if(y.summary.failed>0){let M=y.summary.results.filter((S)=>S.harnessFailed).map((S)=>t(S.workload));await N("command-failed",`${y.summary.failed} workload(s) failed (harness error, a command exited non-zero or produced no samples): ${M.join(", ")}`)}if(!n.quiet){let M=n.baseline??h.baseline;if(n.format==="table"){if(await H(Pe(y.summary)),y.document.comparisons&&y.document.comparisons.length>0)await ie(await i.table.render(y.document,{}))}else if(C!==void 0)await H(C);else if(n.format==="markdown")await H(`## ostia ci
321
169
 
322
- Baseline: \`${A}\` (\`${s(y,l.baseline)}\`) \xB7 ${v.summary.cached} cached, ${v.summary.executed} executed
170
+ Baseline: \`${M}\` (\`${o(h,n.baseline)}\`) \xB7 ${y.summary.cached} cached, ${y.summary.executed} executed
323
171
 
324
- `),await ne(await i.markdown.render(v.document,{}));else await ne(await i[l.format].render(v.document,{protocol:{command:"ci",exitCode:R,unmatched:v.summary.unmatched,baseGit:v.baseline.git,candGit:v.document.git,baseline:{name:A,path:s(y,l.baseline)},cached:v.summary.cached,executed:v.summary.executed,failed:v.summary.failed,missingBaseline:v.summary.missingBaseline,...l.exportJson&&{exportedTo:l.exportJson}}}))}return R}var Ae=/^[A-Za-z0-9._-]+$/;function Oe(p){if(p.startsWith("-"))return`Invalid baseline name "${p}": names can't start with "-".`;if(!Ae.test(p))return`Invalid baseline name "${p}": expected to match ${Ae}.`;return}async function rt(p){if(p.includes("--help")||p.includes("-h"))return Z(ce,!0);if(p.length>1)return await F("invalid-flag",`"ostia baseline save" takes at most one name argument, got ${p.length}.
325
- Run 'ostia baseline --help'.`),2;let l=p[0];if(l!==void 0){let v=Oe(l);if(v)return await F("invalid-flag",`${v}
326
- Run 'ostia baseline --help'.`),2}let k=await le("ostia baseline save");if(!k)return 2;let y=await xe(k,l);return await q(`Wrote ${y}
327
- `),0}async function lt(p){if(p.includes("--help")||p.includes("-h"))return Z(ce,!0);let l=await le();if(!l)return 2;let k=await Pe(l);if(k.length===0)return await q(`No baselines found in ${l.baselineDir}.
328
- `),0;for(let y of k){let v=y.git?` ${w(y.git)}`:"";await q(`${y.name} ${y.workloads} workloads created ${y.createdAt} toolVersion ${y.toolVersion}${v}
329
- `)}return 0}async function ct(p){let[l,...k]=p;if(!l||l==="--help"||l==="-h")return Z(ce,!!l);let y=Oe(l);if(y)return await F("invalid-flag",`${y}
330
- Run 'ostia baseline --help'.`),2;let v=await le();if(!v)return 2;return Re([s(v,l),...k])}async function Ne(p,l,k,y,v,M=""){if(p===void 0||p==="--help"||p==="-h")return Z(y,p!==void 0);let R=k[p];if(!R)return await F("invalid-flag",v(p)),2;try{return await R(l)}catch(A){return Fe(A,`${M}${p}`)}}async function dt(p){let[l,...k]=p;return Ne(l,k,{save:rt,list:lt,show:ct},ce,(y)=>`Unknown "ostia baseline ${y}". Run "ostia baseline --help".`,"baseline ")}var mt=`ostia - Bun-native profile IR engine
172
+ `),await ie(await i.markdown.render(y.document,{}));else await ie(await i[n.format].render(y.document,{protocol:{command:"ci",exitCode:D,unmatched:y.summary.unmatched,baseGit:y.baseline.git,candGit:y.document.git,baseline:{name:M,path:o(h,n.baseline)},cached:y.summary.cached,executed:y.summary.executed,failed:y.summary.failed,missingBaseline:y.summary.missingBaseline,...n.exportJson&&{exportedTo:n.exportJson}}}))}return D}var Te=/^[A-Za-z0-9._-]+$/;function Ie(a){if(a.startsWith("-"))return`Invalid baseline name "${a}": names can't start with "-".`;if(!Te.test(a))return`Invalid baseline name "${a}": expected to match ${Te}.`;return}async function dn(a){if(a.includes("--help")||a.includes("-h"))return V(de,!0);if(a.length>1)return await N("invalid-flag",`"ostia baseline save" takes at most one name argument, got ${a.length}.
173
+ Run 'ostia baseline --help'.`),2;let n=a[0];if(n!==void 0){let y=Ie(n);if(y)return await N("invalid-flag",`${y}
174
+ Run 'ostia baseline --help'.`),2}let f=await me("ostia baseline save");if(!f)return 2;let h=await $e(f,n);return await H(`Wrote ${h}
175
+ `),0}async function pn(a){if(a.includes("--help")||a.includes("-h"))return V(de,!0);let n=await me();if(!n)return 2;let f=await Me(n);if(f.length===0)return await H(`No baselines found in ${n.baselineDir}.
176
+ `),0;for(let h of f){let y=h.git?` ${g(h.git)}`:"";await H(`${h.name} ${h.workloads} workloads created ${h.createdAt} toolVersion ${h.toolVersion}${y}
177
+ `)}return 0}async function fn(a){let[n,...f]=a;if(!n||n==="--help"||n==="-h")return V(de,!!n);let h=Ie(n);if(h)return await N("invalid-flag",`${h}
178
+ Run 'ostia baseline --help'.`),2;let y=await me();if(!y)return 2;return je([o(y,n),...f])}async function Ne(a,n,f,h,y,C=""){if(a===void 0||a==="--help"||a==="-h")return V(h,a!==void 0);let D=f[a];if(!D)return await N("invalid-flag",y(a)),2;try{return await D(n)}catch(M){if(M instanceof P)return await N("config-invalid",M.message),2;if(M instanceof se)return Ue(M,`${C}${a}`);return await N("internal",`Internal error: ${M instanceof Error?M.stack??M.message:String(M)}`),2}}async function gn(a){let[n,...f]=a;return Ne(n,f,{save:dn,list:pn,show:fn},de,(h)=>`Unknown "ostia baseline ${h}". Run "ostia baseline --help".`,"baseline ")}var hn=`ostia - profiling and benchmarking for Bun
331
179
 
332
180
  Commands:
333
181
  time Time commands N times and report timing/CPU/heap
@@ -338,4 +186,4 @@ Commands:
338
186
  baseline Manage baseline ProfileDocuments (save/list/show)
339
187
 
340
188
  Run "ostia <command> --help" for details.
341
- `;async function ut(){let[p,...l]=process.argv.slice(2);return Ne(p,l,{time:Ge,bench:Xe,compare:et,report:Re,ci:it,baseline:dt},mt,(k)=>`Unknown subcommand "${k}". Run "ostia --help".`)}if(import.meta.main)ut().then((p)=>process.exit(p));export{Q as CliUsageError,Be as parseIntFlag};
189
+ `;async function bn(){let[a,...n]=process.argv.slice(2);return Fe=n,Ne(a,n,{time:Ze,bench:en,compare:on,report:je,ci:mn,baseline:gn},hn,(f)=>`Unknown subcommand "${f}". Run "ostia --help".`)}if(import.meta.main)bn().then((a)=>process.exit(a));export{se as CliUsageError,He as parseIntFlag};