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/README.md +213 -1160
- package/chunk-85cap0sk.js +21 -0
- package/chunk-8m8jb8gw.js +16 -0
- package/cli.js +107 -259
- package/index.d.ts +39 -75
- package/index.js +1 -1
- package/package.json +5 -1
- package/runner.ts +4 -5
- package/chunk-1wydyvyj.js +0 -12
- package/chunk-cf9hya3b.js +0 -22
package/cli.js
CHANGED
|
@@ -1,245 +1,100 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{e,
|
|
4
|
-
`)}var
|
|
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
|
|
7
|
-
`)?
|
|
8
|
-
`)
|
|
9
|
-
`)[0],...
|
|
10
|
-
`)}async function
|
|
11
|
-
Run 'ostia ${
|
|
12
|
-
`;function
|
|
13
|
-
`)}else if(
|
|
14
|
-
${
|
|
15
|
-
`)}var
|
|
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
|
|
19
|
-
|
|
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
|
|
27
|
-
|
|
28
|
-
--
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
--
|
|
32
|
-
--
|
|
33
|
-
--
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
--
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
89
|
-
ostia time --samples 25 --
|
|
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 --
|
|
94
|
-
|
|
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
|
|
101
|
-
|
|
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
|
|
105
|
-
--samples N
|
|
106
|
-
|
|
107
|
-
--
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
--
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
--
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
207
|
-
ostia bench --
|
|
208
|
-
ostia bench
|
|
209
|
-
|
|
210
|
-
|
|
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
|
|
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
|
-
--
|
|
221
|
-
--
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
|
237
|
-
|
|
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
|
-
`,
|
|
125
|
+
`,ze=`ostia ci [flags]
|
|
271
126
|
|
|
272
|
-
|
|
273
|
-
|
|
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
|
|
277
|
-
--baseline NAME
|
|
278
|
-
--save-baseline
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
--
|
|
283
|
-
--
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
|
314
|
-
`),
|
|
315
|
-
`);if(
|
|
316
|
-
`)}let U=
|
|
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
|
|
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
|
|
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: \`${
|
|
170
|
+
Baseline: \`${M}\` (\`${o(h,n.baseline)}\`) \xB7 ${y.summary.cached} cached, ${y.summary.executed} executed
|
|
323
171
|
|
|
324
|
-
`),await
|
|
325
|
-
Run 'ostia baseline --help'.`),2;let
|
|
326
|
-
Run 'ostia baseline --help'.`),2}let
|
|
327
|
-
`),0}async function
|
|
328
|
-
`),0;for(let
|
|
329
|
-
`)}return 0}async function
|
|
330
|
-
Run 'ostia baseline --help'.`),2;let
|
|
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
|
|
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};
|