@framedash/cli 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +23 -0
  2. package/dist/commands/builds.d.ts +2 -0
  3. package/dist/commands/builds.d.ts.map +1 -0
  4. package/dist/commands/builds.js +31 -0
  5. package/dist/commands/builds.js.map +1 -0
  6. package/dist/commands/content.d.ts.map +1 -1
  7. package/dist/commands/content.js +2 -2
  8. package/dist/commands/content.js.map +1 -1
  9. package/dist/commands/dashboard.d.ts.map +1 -1
  10. package/dist/commands/dashboard.js +3 -3
  11. package/dist/commands/dashboard.js.map +1 -1
  12. package/dist/commands/funnel.d.ts.map +1 -1
  13. package/dist/commands/funnel.js +7 -8
  14. package/dist/commands/funnel.js.map +1 -1
  15. package/dist/commands/map-capture.d.ts.map +1 -1
  16. package/dist/commands/map-capture.js +5 -4
  17. package/dist/commands/map-capture.js.map +1 -1
  18. package/dist/commands/perf-diff.d.ts +2 -0
  19. package/dist/commands/perf-diff.d.ts.map +1 -0
  20. package/dist/commands/perf-diff.js +125 -0
  21. package/dist/commands/perf-diff.js.map +1 -0
  22. package/dist/commands/retention.d.ts.map +1 -1
  23. package/dist/commands/retention.js +3 -3
  24. package/dist/commands/retention.js.map +1 -1
  25. package/dist/commands/run-profile-test.d.ts +7 -0
  26. package/dist/commands/run-profile-test.d.ts.map +1 -0
  27. package/dist/commands/run-profile-test.js +386 -0
  28. package/dist/commands/run-profile-test.js.map +1 -0
  29. package/dist/index.js +6 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/lib/create-client.d.ts +9 -1
  32. package/dist/lib/create-client.d.ts.map +1 -1
  33. package/dist/lib/create-client.js +17 -1
  34. package/dist/lib/create-client.js.map +1 -1
  35. package/dist/lib/logger.d.ts +2 -0
  36. package/dist/lib/logger.d.ts.map +1 -1
  37. package/dist/lib/logger.js +4 -0
  38. package/dist/lib/logger.js.map +1 -1
  39. package/dist/lib/perf-diff-eval.d.ts +58 -0
  40. package/dist/lib/perf-diff-eval.d.ts.map +1 -0
  41. package/dist/lib/perf-diff-eval.js +49 -0
  42. package/dist/lib/perf-diff-eval.js.map +1 -0
  43. package/dist/lib/run-profile-test-lib.d.ts +117 -0
  44. package/dist/lib/run-profile-test-lib.d.ts.map +1 -0
  45. package/dist/lib/run-profile-test-lib.js +124 -0
  46. package/dist/lib/run-profile-test-lib.js.map +1 -0
  47. package/package.json +2 -2
@@ -0,0 +1,386 @@
1
+ import { execFileSync, spawnSync } from "node:child_process";
2
+ import { constants as osConstants } from "node:os";
3
+ import { buildBuildComparePath, buildBuildsPath } from "@framedash/api-client";
4
+ import { createClient } from "../lib/create-client.js";
5
+ import { formatOutput } from "../lib/formatters.js";
6
+ import { error, log, success, warn } from "../lib/logger.js";
7
+ import { evaluateRegression, formatMetricDiff, isRegressionMetric, } from "../lib/perf-diff-eval.js";
8
+ import { runCommand } from "../lib/run-command.js";
9
+ import { buildEventCount, buildSessionEnv, resolveProfileIdentity, SESSION_ENV_KEYS, waitForIngest, } from "../lib/run-profile-test-lib.js";
10
+ const DEFAULT_INGEST_TIMEOUT_S = 180;
11
+ const DEFAULT_POLL_INTERVAL_S = 5;
12
+ /** Best-effort `git` lookup; returns undefined when git is absent or fails. */
13
+ function gitOutput(args) {
14
+ try {
15
+ const out = execFileSync("git", args, {
16
+ encoding: "utf8",
17
+ stdio: ["ignore", "pipe", "ignore"],
18
+ }).trim();
19
+ return out.length > 0 ? out : undefined;
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ }
25
+ /** Parse a positive-seconds flag with a descriptive error, or fall back. */
26
+ function parseSeconds(value, flag, fallback) {
27
+ // Only a string carries a real value; undefined falls back, and a boolean
28
+ // (unreachable for these string flags, but typed) must not coerce to 1.
29
+ if (typeof value !== "string")
30
+ return fallback;
31
+ const n = Number(value);
32
+ if (!Number.isFinite(n) || n <= 0) {
33
+ error(`--${flag} must be a positive number of seconds`);
34
+ process.exit(1);
35
+ }
36
+ return n;
37
+ }
38
+ /**
39
+ * Forward the project id and base URL so a CI game build that reads them from the
40
+ * environment targets the same project/endpoint the gate queries (they must match
41
+ * or the gate would read a different project's builds).
42
+ *
43
+ * Deliberately does NOT forward the API key: the gate's key needs analytics:read,
44
+ * while the game's SDK needs a separate events:write ingest key (the presets keep
45
+ * ingest a separate plane). Overwriting the child's FRAMEDASH_API_KEY would make
46
+ * the game send events with a read-only key and 403 at ingest, so the game keeps
47
+ * whatever ingest key the CI environment already set for it.
48
+ */
49
+ function passthroughProjectEnv(config) {
50
+ return {
51
+ FRAMEDASH_PROJECT_ID: config.projectId,
52
+ FRAMEDASH_BASE_URL: config.baseUrl,
53
+ };
54
+ }
55
+ /** Validate and resolve the regression-gate flags, exiting on a misconfig. */
56
+ function resolveGateOptions(values, candidateBuildId) {
57
+ let metric;
58
+ if (values.metric !== undefined) {
59
+ const raw = values.metric.trim();
60
+ if (!isRegressionMetric(raw)) {
61
+ error(`Invalid --metric '${raw}'. Allowed: frame_time, memory, gpu_time`);
62
+ process.exit(1);
63
+ }
64
+ metric = raw;
65
+ }
66
+ let thresholdPct = 0;
67
+ if (values.threshold !== undefined) {
68
+ thresholdPct = Number(values.threshold);
69
+ if (!Number.isFinite(thresholdPct) || thresholdPct < 0) {
70
+ error("--threshold must be a non-negative number (percent)");
71
+ process.exit(1);
72
+ }
73
+ }
74
+ const baseline = values.baseline?.trim();
75
+ // A build compared against itself is a zero diff that would silently pass.
76
+ if (baseline && baseline === candidateBuildId) {
77
+ error("--baseline must differ from the candidate build id (the build under test).");
78
+ process.exit(1);
79
+ }
80
+ if (values["fail-on-regression"] && !baseline) {
81
+ error("--fail-on-regression requires --baseline (the known-good build to compare against).");
82
+ process.exit(1);
83
+ }
84
+ return { metric, thresholdPct, baseline };
85
+ }
86
+ /** Spawn the profiling command (inheriting stdio) and exit on any failure. */
87
+ function launchProfilingRun(command, env) {
88
+ const child = spawnSync(command, { shell: true, stdio: "inherit", env });
89
+ if (child.error) {
90
+ error(`Failed to launch --command: ${child.error.message}`);
91
+ process.exit(1);
92
+ }
93
+ if (child.signal) {
94
+ error(`Profiling command was terminated by signal ${child.signal}.`);
95
+ // Conventional 128 + signal number so CI sees a meaningful non-zero code.
96
+ process.exit(128 + (osConstants.signals[child.signal] ?? 0));
97
+ }
98
+ if (typeof child.status === "number" && child.status !== 0) {
99
+ error(`Profiling command exited with code ${child.status}; not gating on incomplete data.`);
100
+ process.exit(child.status);
101
+ }
102
+ }
103
+ /**
104
+ * Fetch the candidate build's list row (the suffix the pre-run snapshot and poll
105
+ * share). Scopes to the candidate buildId so the server's newest-50 cap cannot
106
+ * hide a re-run of an older build, and uses fresh=1 to bypass the aggregation
107
+ * cache so the wait reads live state (a cached, stale-low count could let an old
108
+ * run's data pass the wait).
109
+ */
110
+ function fetchBuilds(client, values, buildId) {
111
+ return client.get(client.projectPath(buildBuildsPath({ days: values.days, buildId, fresh: true })));
112
+ }
113
+ /**
114
+ * The candidate's perf-event count right now. Read BEFORE the run so the ingest
115
+ * wait can require the count to grow -- a CI re-run for the same build_id must
116
+ * not be satisfied by the previous run's data. Fails closed if the snapshot
117
+ * cannot be read: without a reliable baseline the wait could pass on old data
118
+ * (a false-green gate), so abort before launch rather than guess 0.
119
+ */
120
+ async function readPriorEventCount(client, values, buildId) {
121
+ let builds;
122
+ try {
123
+ builds = await fetchBuilds(client, values, buildId);
124
+ }
125
+ catch (err) {
126
+ error(`Could not read the build's pre-run event count (${err instanceof Error ? err.message : String(err)}). Aborting before launch; pass --skip-wait if your SDK flushes synchronously.`);
127
+ process.exit(1);
128
+ }
129
+ // A non-error response that is not an array means we cannot trust the baseline
130
+ // (it could read as 0 and let an old run's data satisfy the wait), so fail closed.
131
+ if (!Array.isArray(builds)) {
132
+ error("The builds API returned an unexpected response (no build list); cannot establish a " +
133
+ "pre-run baseline. Aborting before launch; pass --skip-wait if your SDK flushes synchronously.");
134
+ process.exit(1);
135
+ }
136
+ return buildEventCount(builds, buildId);
137
+ }
138
+ /** Poll the builds list until the candidate gains fresh events; exit on timeout. */
139
+ async function awaitIngest(client, values, buildId, priorEventCount, timeoutMs, intervalMs) {
140
+ success(`Waiting up to ${timeoutMs / 1000}s for build '${buildId}' to ingest...`);
141
+ const landed = await waitForIngest({
142
+ buildId,
143
+ minEventCount: priorEventCount,
144
+ timeoutMs,
145
+ intervalMs,
146
+ now: () => Date.now(),
147
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
148
+ fetchBuilds: () => fetchBuilds(client, values, buildId),
149
+ });
150
+ if (!landed) {
151
+ error(`Build '${buildId}' did not ingest fresh events within ${timeoutMs / 1000}s. ` +
152
+ "The SDK may not have flushed, or ingest/aggregation is lagging. " +
153
+ "Increase --ingest-timeout, or check with 'framedash builds'.");
154
+ process.exit(1);
155
+ }
156
+ success(`Build '${buildId}' ingested.`);
157
+ }
158
+ /** Fetch the build comparison, print it, and (with --fail-on-regression) gate. */
159
+ async function runRegressionGate(client, config, values, gate) {
160
+ const comparison = await client.get(client.projectPath(buildBuildComparePath({
161
+ baseline: gate.baseline,
162
+ candidate: gate.candidate,
163
+ days: values.days,
164
+ mapId: values.map,
165
+ platform: values.platform,
166
+ // Bypass the compare cache so a re-run gates on the just-ingested data.
167
+ fresh: true,
168
+ })));
169
+ // A non-error 2xx with a malformed body must fail closed, not crash the gate.
170
+ if (!comparison || !Array.isArray(comparison.diffs)) {
171
+ error("Unexpected response from the builds/compare API (no comparison data).");
172
+ process.exit(1);
173
+ }
174
+ log(config.format === "json"
175
+ ? formatOutput(comparison, "json")
176
+ : formatOutput(comparison.diffs, config.format));
177
+ const verdict = evaluateRegression(comparison, {
178
+ metric: gate.metric,
179
+ thresholdPct: gate.thresholdPct,
180
+ });
181
+ // Report-only (no --fail-on-regression): never exit non-zero, but still warn --
182
+ // on a regression, AND when nothing was comparable -- so a misconfigured CI job
183
+ // (wrong build IDs, or a metric with no data) does not silently go green.
184
+ if (!values["fail-on-regression"]) {
185
+ if (verdict.evaluated.length === 0) {
186
+ warn(gate.metric
187
+ ? `No comparable '${gate.metric}' data between these builds; the gate evaluated nothing (check the build IDs / metric).`
188
+ : "No comparable performance data between these builds; the gate evaluated nothing (check the build IDs).");
189
+ }
190
+ else if (verdict.failed) {
191
+ warn(`Regression detected (threshold ${gate.thresholdPct}%): ${verdict.offenders
192
+ .map(formatMetricDiff)
193
+ .join("; ")}. Pass --fail-on-regression to fail the build.`);
194
+ }
195
+ return;
196
+ }
197
+ // Fail closed: nothing comparable means the gate is unmeasured, not passed.
198
+ if (verdict.evaluated.length === 0) {
199
+ error(gate.metric
200
+ ? `No comparable '${gate.metric}' data between these builds; cannot evaluate a regression.`
201
+ : "No comparable performance data between these builds; cannot evaluate a regression.");
202
+ process.exit(1);
203
+ }
204
+ if (verdict.failed) {
205
+ error(`Performance regression detected (threshold ${gate.thresholdPct}%): ${verdict.offenders
206
+ .map(formatMetricDiff)
207
+ .join("; ")}`);
208
+ process.exit(1);
209
+ }
210
+ success(`No performance regression beyond ${gate.thresholdPct}% (${verdict.evaluated.length} metric(s) checked).`);
211
+ }
212
+ /**
213
+ * Turnkey CI profiling gate: export the FRAMEDASH_* session contract, launch the
214
+ * configured game/profiling command, wait for its perf data to ingest, then run
215
+ * the perf-diff regression gate against a baseline build.
216
+ */
217
+ export async function runProfileTest(args) {
218
+ await runCommand({
219
+ args,
220
+ help: HELP,
221
+ options: {
222
+ command: { type: "string" },
223
+ "build-id": { type: "string" },
224
+ branch: { type: "string" },
225
+ commit: { type: "string" },
226
+ scenario: { type: "string" },
227
+ "ingest-timeout": { type: "string" },
228
+ "poll-interval": { type: "string" },
229
+ "skip-wait": { type: "boolean" },
230
+ baseline: { type: "string" },
231
+ metric: { type: "string" },
232
+ threshold: { type: "string" },
233
+ days: { type: "string" },
234
+ map: { type: "string" },
235
+ platform: { type: "string" },
236
+ "fail-on-regression": { type: "boolean" },
237
+ },
238
+ }, async ({ client, config, values }) => {
239
+ const command = values.command?.trim();
240
+ if (!command) {
241
+ error("--command is required (the game/profiling command to launch)");
242
+ process.exit(1);
243
+ }
244
+ // Resolve the build identity: explicit flags win, else fall back to git.
245
+ // Only spawn `git` for a field the caller did not provide.
246
+ const branchFlag = values.branch;
247
+ const commitFlag = values.commit;
248
+ const identity = resolveProfileIdentity({
249
+ buildId: values["build-id"],
250
+ branch: branchFlag,
251
+ commit: commitFlag,
252
+ scenario: values.scenario,
253
+ }, {
254
+ branch: branchFlag ? undefined : gitOutput(["rev-parse", "--abbrev-ref", "HEAD"]),
255
+ commit: commitFlag ? undefined : gitOutput(["rev-parse", "HEAD"]),
256
+ });
257
+ if (!identity) {
258
+ error("Could not determine a build id: pass --build-id or --commit, or run inside a git repo.");
259
+ process.exit(1);
260
+ }
261
+ // Validate everything up front so a misconfig fails before we spend minutes
262
+ // running the game only to reject the result.
263
+ const gate = resolveGateOptions(values, identity.buildId);
264
+ const ingestTimeoutMs = parseSeconds(values["ingest-timeout"], "ingest-timeout", DEFAULT_INGEST_TIMEOUT_S) * 1000;
265
+ const pollIntervalMs = parseSeconds(values["poll-interval"], "poll-interval", DEFAULT_POLL_INTERVAL_S) * 1000;
266
+ // The ingest snapshot/poll run in a retry loop, so they need a client that
267
+ // THROWS on a transient 429/5xx (the default client exits the process,
268
+ // which would kill the wait on a temporary blip). The gate compare keeps
269
+ // the default exit-on-error client (a compare failure should fail the gate).
270
+ const skipWait = Boolean(values["skip-wait"]);
271
+ const pollClient = skipWait
272
+ ? client
273
+ : createClient(config.baseUrl, config.apiKey, config.projectId, { throwOnError: true });
274
+ // Snapshot the candidate's event count BEFORE the run (only when we will
275
+ // wait), so the ingest wait can require fresh events rather than accepting
276
+ // a prior run's data for the same build_id.
277
+ const priorEventCount = skipWait
278
+ ? 0
279
+ : await readPriorEventCount(pollClient, values, identity.buildId);
280
+ // 1) Launch the profiling command with the FRAMEDASH_* contract exported, so
281
+ // the SDK's BeginAutomatedSessionFromEnvironment() stamps build_id + ci.* tags
282
+ // onto every event (including the perf_heartbeat the gate reads).
283
+ success(`Running profiling build '${identity.buildId}'${identity.scenario ? ` (scenario: ${identity.scenario})` : ""}`);
284
+ // Build the child environment. Two safeguards:
285
+ // 1) The runner owns the whole FRAMEDASH_* session contract: clear any
286
+ // contract key it did not set this run, so the game cannot inherit stale
287
+ // branch/commit/scenario left in the environment by a prior CI step.
288
+ // 2) The gate key (analytics:read) must not be handed to the game as its key
289
+ // -- the game needs a separate events:write ingest key. When the gate key
290
+ // came from FRAMEDASH_API_KEY in the environment (no --api-key flag), strip
291
+ // it from the child so the game cannot inherit a read key.
292
+ const sessionEnv = buildSessionEnv(identity);
293
+ const childEnv = {
294
+ ...process.env,
295
+ ...passthroughProjectEnv(config),
296
+ ...sessionEnv,
297
+ };
298
+ for (const key of SESSION_ENV_KEYS) {
299
+ if (!(key in sessionEnv))
300
+ delete childEnv[key];
301
+ }
302
+ if (!values["api-key"] && !values["api-key-file"] && process.env.FRAMEDASH_API_KEY) {
303
+ delete childEnv.FRAMEDASH_API_KEY;
304
+ warn("Removed the gate's FRAMEDASH_API_KEY (an analytics:read key) from the launched " +
305
+ "game's environment so it is not used as the game's ingest key. Set the game's " +
306
+ "events:write key separately, or pass the gate key via --api-key/--api-key-file.");
307
+ }
308
+ launchProfilingRun(command, childEnv);
309
+ // 2) Wait for the candidate build's perf events to land (unless skipped).
310
+ if (!skipWait) {
311
+ await awaitIngest(pollClient, values, identity.buildId, priorEventCount, ingestTimeoutMs, pollIntervalMs);
312
+ }
313
+ // 3) Run the perf-diff gate when a baseline is given; otherwise the run is
314
+ // complete (the SDK reported the build; the caller can compare it later).
315
+ if (!gate.baseline) {
316
+ success("Profiling run complete. Pass --baseline to gate on a build-over-build regression.");
317
+ return;
318
+ }
319
+ await runRegressionGate(client, config, values, {
320
+ baseline: gate.baseline,
321
+ candidate: identity.buildId,
322
+ metric: gate.metric,
323
+ thresholdPct: gate.thresholdPct,
324
+ });
325
+ });
326
+ }
327
+ const HELP = `Usage: framedash run-profile-test --command "<cmd>" [options]
328
+
329
+ Run an automated profiling test end-to-end for CI: export the FRAMEDASH_*
330
+ session contract, launch your game/profiling command, wait for its performance
331
+ data to ingest, then (with --baseline) gate on a build-over-build regression.
332
+
333
+ The launched command inherits these environment variables; an SDK that calls
334
+ BeginAutomatedSessionFromEnvironment() picks them up automatically:
335
+ FRAMEDASH_BUILD_ID the candidate build_id (first-class)
336
+ FRAMEDASH_GIT_BRANCH ci.branch attribute
337
+ FRAMEDASH_GIT_COMMIT ci.commit attribute
338
+ FRAMEDASH_TEST_SCENARIO ci.scenario attribute
339
+ FRAMEDASH_PROJECT_ID / FRAMEDASH_BASE_URL are forwarded so the build targets the
340
+ same project the gate queries. The gate uses an analytics:read key while your
341
+ game needs a separate events:write ingest key: pass the gate key with
342
+ --api-key/--api-key-file and keep the game's ingest key in the launched command's
343
+ FRAMEDASH_API_KEY, so the gate key does not get inherited as the game's key.
344
+
345
+ Required:
346
+ --command <cmd> Game/profiling command to launch (run via the shell)
347
+
348
+ Build identity (each defaults as noted; exported to the child):
349
+ --build-id <id> Candidate build_id (default: --commit, else git HEAD)
350
+ --branch <name> default: git rev-parse --abbrev-ref HEAD
351
+ --commit <sha> default: git rev-parse HEAD
352
+ --scenario <name> Test scenario label (no default)
353
+
354
+ Note: the gate compares ALL of a build_id's samples in the window (the build_id
355
+ is the comparison unit, as in 'perf-diff'). If a CI job may re-run the SAME
356
+ commit, give each run a unique build_id (e.g. --build-id "$GIT_SHA-$RUN_ID") so
357
+ the comparison is not diluted by a previous run's samples.
358
+
359
+ Ingest wait (polls a cache-bypassing live read and waits for the build's
360
+ perf-event count to GROW past its pre-run value, so a re-run for the same build
361
+ id is not satisfied by old data):
362
+ --ingest-timeout <s> Max seconds to wait for fresh events (default: 180)
363
+ --poll-interval <s> Seconds between build-list polls (default: 5)
364
+ --skip-wait Skip the ingest wait (e.g. the SDK flushed synchronously)
365
+
366
+ Regression gate (runs only when --baseline is set):
367
+ --baseline <id> Known-good build_id to compare the candidate against
368
+ --metric <name> Gate on one metric: frame_time, memory, gpu_time (default: all)
369
+ --threshold <pct> Tolerate a regression up to this percent (default: 0)
370
+ --fail-on-regression Exit 1 on a regression beyond the threshold (needs --baseline)
371
+ --days <n> Comparison window in days: 7, 14, 30, 90 (default: 30)
372
+ --map <id> Restrict the comparison to one map
373
+ --platform <name> Restrict the comparison to one platform
374
+
375
+ Global:
376
+ --api-key <key> API key (or FRAMEDASH_API_KEY env)
377
+ --project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
378
+ --base-url <url> API base URL (default: https://app.framedash.dev)
379
+ --format <fmt> Comparison output format: json, table, csv (default: json)
380
+ -h, --help Show help
381
+
382
+ Example (GitHub Actions gate):
383
+ framedash run-profile-test \\
384
+ --command "./Build/Game.exe -nullrhi -ExecCmds='Automation RunTest Perf'" \\
385
+ --scenario nightly --baseline "$BASE_SHA" --threshold 5 --fail-on-regression`;
386
+ //# sourceMappingURL=run-profile-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-profile-test.js","sourceRoot":"","sources":["../../src/commands/run-profile-test.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -11,6 +11,9 @@ Commands:
11
11
  dashboard Show dashboard metrics
12
12
  retention Show player retention cohorts
13
13
  funnel Analyze event funnels
14
+ builds List builds seen for the project (for perf-diff)
15
+ perf-diff Compare two builds and gate CI on perf regressions
16
+ run-profile-test Run a profiling build, wait for ingest, gate on regression
14
17
  query Execute a read-only ClickHouse query
15
18
  alerts Manage alert rules (list, create, update, delete)
16
19
  maps Manage maps (list, delete)
@@ -36,6 +39,9 @@ const COMMANDS = {
36
39
  dashboard: () => import("./commands/dashboard.js").then((m) => m.dashboard),
37
40
  retention: () => import("./commands/retention.js").then((m) => m.retention),
38
41
  funnel: () => import("./commands/funnel.js").then((m) => m.funnel),
42
+ builds: () => import("./commands/builds.js").then((m) => m.builds),
43
+ "perf-diff": () => import("./commands/perf-diff.js").then((m) => m.perfDiff),
44
+ "run-profile-test": () => import("./commands/run-profile-test.js").then((m) => m.runProfileTest),
39
45
  query: () => import("./commands/query.js").then((m) => m.query),
40
46
  alerts: () => import("./commands/alerts.js").then((m) => m.alerts),
41
47
  maps: () => import("./commands/maps.js").then((m) => m.maps),
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,11 @@
1
1
  import { ApiClient } from "@framedash/api-client";
2
- export declare function createClient(baseUrl: string, apiKey: string, projectId: string): ApiClient;
2
+ /**
3
+ * Create the CLI's API client. By default a request error prints a message and
4
+ * exits the process (the right behavior for one-shot commands). Pass
5
+ * `throwOnError` for a client used inside a retry loop (e.g. the run-profile-test
6
+ * ingest poll), where a transient 429/5xx must be thrown and retried, not exit.
7
+ */
8
+ export declare function createClient(baseUrl: string, apiKey: string, projectId: string, options?: {
9
+ throwOnError?: boolean;
10
+ }): ApiClient;
3
11
  //# sourceMappingURL=create-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-client.d.ts","sourceRoot":"","sources":["../../src/lib/create-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AAGjE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,CAyB1F"}
1
+ {"version":3,"file":"create-client.d.ts","sourceRoot":"","sources":["../../src/lib/create-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AAGjE;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,SAAS,CAmCX"}
@@ -1,6 +1,22 @@
1
1
  import { ApiClient } from "@framedash/api-client";
2
2
  import { error } from "./logger.js";
3
- export function createClient(baseUrl, apiKey, projectId) {
3
+ /**
4
+ * Create the CLI's API client. By default a request error prints a message and
5
+ * exits the process (the right behavior for one-shot commands). Pass
6
+ * `throwOnError` for a client used inside a retry loop (e.g. the run-profile-test
7
+ * ingest poll), where a transient 429/5xx must be thrown and retried, not exit.
8
+ */
9
+ export function createClient(baseUrl, apiKey, projectId, options) {
10
+ if (options?.throwOnError) {
11
+ return new ApiClient({
12
+ baseUrl,
13
+ apiKey,
14
+ projectId,
15
+ onError(err) {
16
+ throw err;
17
+ },
18
+ });
19
+ }
4
20
  return new ApiClient({
5
21
  baseUrl,
6
22
  apiKey,
@@ -1 +1 @@
1
- {"version":3,"file":"create-client.js","sourceRoot":"","sources":["../../src/lib/create-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,MAAc,EAAE,SAAiB;IAC9E,OAAO,IAAI,SAAS,CAAC;QACpB,OAAO;QACP,MAAM;QACN,SAAS;QACT,OAAO,CAAC,GAAa;YACpB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxB,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;gBAClC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;oBAC9B,KAAK,CAAC,0CAA0C,UAAU,IAAI,CAAC,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACP,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;oBACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACpD,MAAM,QAAQ,GACb,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC;wBACtC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,kBAAkB,EAAE;wBAChD,CAAC,CAAC,SAAS,CAAC;oBACd,KAAK,CAAC,wCAAwC,QAAQ,GAAG,CAAC,CAAC;gBAC5D,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACpB,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"create-client.js","sourceRoot":"","sources":["../../src/lib/create-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC3B,OAAe,EACf,MAAc,EACd,SAAiB,EACjB,OAAoC;IAEpC,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;QAC3B,OAAO,IAAI,SAAS,CAAC;YACpB,OAAO;YACP,MAAM;YACN,SAAS;YACT,OAAO,CAAC,GAAa;gBACpB,MAAM,GAAG,CAAC;YACX,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,SAAS,CAAC;QACpB,OAAO;QACP,MAAM;QACN,SAAS;QACT,OAAO,CAAC,GAAa;YACpB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxB,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;gBAClC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;oBAC9B,KAAK,CAAC,0CAA0C,UAAU,IAAI,CAAC,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACP,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;oBACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACpD,MAAM,QAAQ,GACb,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC;wBACtC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,kBAAkB,EAAE;wBAChD,CAAC,CAAC,SAAS,CAAC;oBACd,KAAK,CAAC,wCAAwC,QAAQ,GAAG,CAAC,CAAC;gBAC5D,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACpB,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
@@ -5,5 +5,7 @@
5
5
  */
6
6
  export declare function log(message: string): void;
7
7
  export declare function success(message: string): void;
8
+ /** Non-fatal warning (the run still exits 0). */
9
+ export declare function warn(message: string): void;
8
10
  export declare function error(message: string): void;
9
11
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,iDAAiD;AACjD,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C"}
@@ -9,6 +9,10 @@ export function log(message) {
9
9
  export function success(message) {
10
10
  process.stderr.write(` \u2713 ${message}\n`);
11
11
  }
12
+ /** Non-fatal warning (the run still exits 0). */
13
+ export function warn(message) {
14
+ process.stderr.write(` ! ${message}\n`);
15
+ }
12
16
  export function error(message) {
13
17
  process.stderr.write(` \u2717 ${message}\n`);
14
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,GAAG,CAAC,OAAe;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,OAAO,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe;IACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,OAAO,IAAI,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,GAAG,CAAC,OAAe;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,OAAO,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,IAAI,CAAC,OAAe;IACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,OAAO,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe;IACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,OAAO,IAAI,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Pure regression-gate logic for `framedash perf-diff`, kept free of I/O so the
3
+ * CI pass/fail decision is unit-testable. The shapes mirror the builds/compare
4
+ * API response (apps/web .../builds/compare); the CLI cannot import the web
5
+ * app's types, so they are restated here.
6
+ */
7
+ /** Lower-is-better performance metrics perf-diff can gate on. */
8
+ export type RegressionMetric = "frame_time" | "memory" | "gpu_time";
9
+ export declare const REGRESSION_METRICS: readonly RegressionMetric[];
10
+ export declare function isRegressionMetric(value: string): value is RegressionMetric;
11
+ /** One metric's diff, as returned by the builds/compare API. */
12
+ export interface ApiMetricDiff {
13
+ metric: RegressionMetric;
14
+ baselineP50: number | null;
15
+ candidateP50: number | null;
16
+ /** Signed percent change of candidate P50 vs baseline P50; null = not comparable. */
17
+ diffPct: number | null;
18
+ isRegression: boolean;
19
+ baselineTail: number | null;
20
+ candidateTail: number | null;
21
+ }
22
+ export interface ApiBuildComparison {
23
+ baseline: {
24
+ build_id: string;
25
+ };
26
+ candidate: {
27
+ build_id: string;
28
+ };
29
+ diffs: ApiMetricDiff[];
30
+ }
31
+ export interface RegressionVerdict {
32
+ /** True when at least one evaluated metric regressed beyond the threshold. */
33
+ failed: boolean;
34
+ /** Metrics that regressed beyond the threshold. */
35
+ offenders: ApiMetricDiff[];
36
+ /** Metrics actually evaluated (matching the metric filter, with comparable data). */
37
+ evaluated: ApiMetricDiff[];
38
+ }
39
+ export interface EvaluateOptions {
40
+ /** Restrict the gate to a single metric; default checks all. */
41
+ metric?: RegressionMetric;
42
+ /**
43
+ * A metric fails only when its P50 worsened by MORE than this percentage,
44
+ * letting CI tolerate run-to-run noise. Default 0 = any worsening fails.
45
+ */
46
+ thresholdPct?: number;
47
+ }
48
+ /**
49
+ * Decide whether a build comparison should fail a CI gate. All regression
50
+ * metrics are lower-is-better, so a positive diffPct (candidate P50 above
51
+ * baseline) is a regression. A null diffPct (no baseline data, or a metric
52
+ * unavailable on the platform, e.g. GPU timing) is not comparable and is
53
+ * skipped -- neither an offender nor counted as evaluated.
54
+ */
55
+ export declare function evaluateRegression(comparison: ApiBuildComparison, options?: EvaluateOptions): RegressionVerdict;
56
+ /** A one-line human summary of a single metric diff (for CI logs). */
57
+ export declare function formatMetricDiff(d: ApiMetricDiff): string;
58
+ //# sourceMappingURL=perf-diff-eval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-diff-eval.d.ts","sourceRoot":"","sources":["../../src/lib/perf-diff-eval.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,iEAAiE;AACjE,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEpE,eAAO,MAAM,kBAAkB,EAAE,SAAS,gBAAgB,EAAyC,CAAC;AAEpG,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,gBAAgB,CAE3E;AAED,gEAAgE;AAChE,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qFAAqF;IACrF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,SAAS,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,KAAK,EAAE,aAAa,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC;IAChB,mDAAmD;IACnD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,qFAAqF;IACrF,SAAS,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,gEAAgE;IAChE,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,UAAU,EAAE,kBAAkB,EAC9B,OAAO,GAAE,eAAoB,GAC3B,iBAAiB,CAmBnB;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAQzD"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Pure regression-gate logic for `framedash perf-diff`, kept free of I/O so the
3
+ * CI pass/fail decision is unit-testable. The shapes mirror the builds/compare
4
+ * API response (apps/web .../builds/compare); the CLI cannot import the web
5
+ * app's types, so they are restated here.
6
+ */
7
+ export const REGRESSION_METRICS = ["frame_time", "memory", "gpu_time"];
8
+ export function isRegressionMetric(value) {
9
+ return REGRESSION_METRICS.includes(value);
10
+ }
11
+ /**
12
+ * Decide whether a build comparison should fail a CI gate. All regression
13
+ * metrics are lower-is-better, so a positive diffPct (candidate P50 above
14
+ * baseline) is a regression. A null diffPct (no baseline data, or a metric
15
+ * unavailable on the platform, e.g. GPU timing) is not comparable and is
16
+ * skipped -- neither an offender nor counted as evaluated.
17
+ */
18
+ export function evaluateRegression(comparison, options = {}) {
19
+ // Clamp a non-finite or negative threshold to 0 (the strictest gate) so a bad
20
+ // value fails safe rather than silently disabling the gate -- `x > Infinity`
21
+ // and `x > NaN` are both always false, which would pass every regression. The
22
+ // command validates --threshold too; this keeps the exported helper robust on
23
+ // its own.
24
+ const threshold = Number.isFinite(options.thresholdPct) && options.thresholdPct >= 0
25
+ ? options.thresholdPct
26
+ : 0;
27
+ // Defensive: a malformed response (missing diffs) yields no evaluable metrics,
28
+ // which the caller treats as "nothing comparable" -> fail-closed. `typeof ===
29
+ // "number"` (not `!== null`) also skips an undefined/missing diffPct safely.
30
+ const diffs = Array.isArray(comparison?.diffs) ? comparison.diffs : [];
31
+ const evaluated = diffs.filter((d) => (!options.metric || d.metric === options.metric) && typeof d.diffPct === "number");
32
+ const offenders = evaluated.filter((d) => d.diffPct > threshold);
33
+ return { failed: offenders.length > 0, offenders, evaluated };
34
+ }
35
+ /** A one-line human summary of a single metric diff (for CI logs). */
36
+ export function formatMetricDiff(d) {
37
+ // `typeof !== "number"` covers a null OR an undefined/missing diffPct, so the
38
+ // toFixed() below can never throw on a malformed row.
39
+ if (typeof d.diffPct !== "number")
40
+ return `${d.metric}: no comparable data`;
41
+ const sign = d.diffPct >= 0 ? "+" : "";
42
+ return `${d.metric}: ${sign}${d.diffPct.toFixed(1)}% (P50 ${fmtValue(d.baselineP50)} -> ${fmtValue(d.candidateP50)})`;
43
+ }
44
+ function fmtValue(value) {
45
+ // `typeof !== "number"` covers null AND a missing/undefined value on a
46
+ // malformed row, so toFixed() can never throw here.
47
+ return typeof value !== "number" ? "n/a" : value.toFixed(2);
48
+ }
49
+ //# sourceMappingURL=perf-diff-eval.js.map