@tempo-ai/visual-review 0.0.1 → 0.0.107-staging.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.
- package/dist/bin.js +2 -2
- package/dist/{chunk-HMPQK5ZG.js → chunk-4KKBAMNP.js} +14 -7
- package/dist/chunk-4KKBAMNP.js.map +1 -0
- package/dist/{chunk-4QAZFBC3.js → chunk-MW6S36CX.js} +170 -75
- package/dist/chunk-MW6S36CX.js.map +1 -0
- package/dist/{cli-3HH2BY7Y.js → cli-JCGZRFSY.js} +4 -2
- package/dist/index.d.ts +6 -12
- package/dist/index.js +2 -2
- package/dist/{report-RCKB2CVS.js → report-RD2OQ5TP.js} +6 -4
- package/package.json +2 -2
- package/templates/visual-review.yml +3 -2
- package/dist/chunk-4QAZFBC3.js.map +0 -1
- package/dist/chunk-HMPQK5ZG.js.map +0 -1
- /package/dist/{cli-3HH2BY7Y.js.map → cli-JCGZRFSY.js.map} +0 -0
- /package/dist/{report-RCKB2CVS.js.map → report-RD2OQ5TP.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -6,11 +6,11 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
6
6
|
const [command, ...commandArgs] = argv;
|
|
7
7
|
switch (command) {
|
|
8
8
|
case "capture":
|
|
9
|
-
return await (await import("./cli-
|
|
9
|
+
return await (await import("./cli-JCGZRFSY.js")).main(commandArgs);
|
|
10
10
|
case "claim":
|
|
11
11
|
return await (await import("./claim-WI2ZVIDS.js")).main(commandArgs);
|
|
12
12
|
case "report":
|
|
13
|
-
return await (await import("./report-
|
|
13
|
+
return await (await import("./report-RD2OQ5TP.js")).main(commandArgs);
|
|
14
14
|
case "install-browsers":
|
|
15
15
|
return await (await import("./install-browsers-5RCJN3AK.js")).main(commandArgs);
|
|
16
16
|
default:
|
|
@@ -7,6 +7,13 @@ import {
|
|
|
7
7
|
import { execFileSync } from "child_process";
|
|
8
8
|
var STICKY_MARKER = "<!-- tempo-visual-review -->";
|
|
9
9
|
var STATUS_CONTEXT = "visual-review";
|
|
10
|
+
function publisherDeliveryFailure(status) {
|
|
11
|
+
const publishError = status.lastPublishError;
|
|
12
|
+
if (publishError == null) return null;
|
|
13
|
+
if (publishError.revision < (status.lastPublish?.revision ?? 0)) return null;
|
|
14
|
+
if (status.statusPublishing !== false) return null;
|
|
15
|
+
return publishError;
|
|
16
|
+
}
|
|
10
17
|
function parseArgs(argv) {
|
|
11
18
|
const args = {
|
|
12
19
|
pollTimeoutS: 900,
|
|
@@ -34,7 +41,7 @@ function parseArgs(argv) {
|
|
|
34
41
|
return args;
|
|
35
42
|
}
|
|
36
43
|
function gh(...ghArgs) {
|
|
37
|
-
return execFileSync("gh", ghArgs, { encoding: "utf-8" });
|
|
44
|
+
return execFileSync("gh", ghArgs, { encoding: "utf-8", windowsHide: true });
|
|
38
45
|
}
|
|
39
46
|
function postCommitStatus(repo, sha, state, description, targetUrl) {
|
|
40
47
|
gh(
|
|
@@ -254,16 +261,15 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
254
261
|
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
255
262
|
}
|
|
256
263
|
const verdict = describeGate(status.gate ?? {}, status.state);
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
if (status.publisherConfigured && !publishFailing) {
|
|
264
|
+
const publishFailure = publisherDeliveryFailure(status);
|
|
265
|
+
if (status.publisherConfigured && publishFailure === null) {
|
|
260
266
|
console.log(
|
|
261
267
|
`verdict (${verdict.state}) owned by the Convex publisher \u2014 skipping CI status write`
|
|
262
268
|
);
|
|
263
269
|
} else {
|
|
264
|
-
if (
|
|
270
|
+
if (publishFailure !== null) {
|
|
265
271
|
console.log(
|
|
266
|
-
`Convex publisher reported a delivery failure (${
|
|
272
|
+
`Convex publisher reported a delivery failure (${publishFailure.error}) \u2014 writing the status from CI as a fallback`
|
|
267
273
|
);
|
|
268
274
|
}
|
|
269
275
|
postCommitStatus(
|
|
@@ -283,6 +289,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
283
289
|
}
|
|
284
290
|
|
|
285
291
|
export {
|
|
292
|
+
publisherDeliveryFailure,
|
|
286
293
|
main
|
|
287
294
|
};
|
|
288
|
-
//# sourceMappingURL=chunk-
|
|
295
|
+
//# sourceMappingURL=chunk-4KKBAMNP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/report.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nimport { visualReviewPrDeclaration } from \"./github-actions\";\n\nconst STICKY_MARKER = \"<!-- tempo-visual-review -->\";\nconst STATUS_CONTEXT = \"visual-review\";\n\ntype ReportArgs = {\n pollTimeoutS: number;\n bypass: boolean;\n noVisualChange: boolean;\n fallbackFailure: boolean;\n statusUrl?: string;\n pr?: number;\n sha?: string;\n};\n\ntype Gate = {\n satisfied?: boolean;\n required?: number;\n approvalsNeeded?: number;\n rejected?: number;\n blockingErrors?: number;\n};\n\n/**\n * Shape of the `/visual-review/status` poll. Convex serialises absent\n * optional fields as `null`, not `undefined` — every optional here must be\n * read null-safely.\n */\nexport type ReportStatus = {\n state?: \"complete\" | \"error\" | string;\n reportUrl?: string;\n trackedTotal?: number;\n counts?: Record<string, number | undefined>;\n gate?: Gate;\n topChanged?: Array<{\n name?: string;\n storyboardId: string;\n diffRatio?: number | null;\n verdict: string;\n error?: string;\n baselineHash?: string;\n candidateHash?: string;\n }>;\n blobUrlTemplate?: string;\n approximateBase?: boolean;\n freshEpoch?: boolean;\n publisherConfigured?: boolean;\n lastPublishError?: { revision: number; error: string } | null;\n lastPublish?: { revision: number } | null;\n statusPublishing?: boolean | null;\n};\n\n/**\n * `publisherConfigured` only means the GitHub App env is set GLOBALLY — it\n * does NOT mean the app can post on THIS repo (it may not be installed\n * there, or lack the permission). When the publisher has a TERMINAL\n * outstanding failure for the current revision, CI writes the status itself\n * so the check can't hang pending forever. Terminal means the error is\n * current (revision-compare, mirroring the portal's \"delivery failing\"\n * banner) AND no publish worker holds the singleflight claim\n * (`statusPublishing === false`). A transient failure mid-retry-ladder keeps\n * the claim, so falling back then would race the publisher's own retry and\n * could land a stale snapshot after a newer portal flip.\n *\n * Returns the terminal failure, or `null` when Convex still owns the verdict.\n */\nexport function publisherDeliveryFailure(\n status: ReportStatus,\n): { revision: number; error: string } | null {\n const publishError = status.lastPublishError;\n // A healthy run reports `lastPublishError: null` (0.0.1 compared against\n // `undefined` only and crashed on `.revision`).\n if (publishError == null) return null;\n if (publishError.revision < (status.lastPublish?.revision ?? 0)) return null;\n if (status.statusPublishing !== false) return null;\n return publishError;\n}\n\nfunction parseArgs(argv: string[]): ReportArgs {\n const args: ReportArgs = {\n pollTimeoutS: 900,\n bypass: false,\n noVisualChange: false,\n fallbackFailure: false,\n };\n for (let index = 0; index < argv.length; index += 1) {\n const flag = argv[index];\n const next = () => {\n const value = argv[++index];\n if (value === undefined) throw new Error(`${flag} needs a value`);\n return value;\n };\n if (flag === \"--status-url\") args.statusUrl = next();\n else if (flag === \"--pr\") args.pr = Number(next());\n else if (flag === \"--sha\") args.sha = next();\n else if (flag === \"--poll-timeout-s\") args.pollTimeoutS = Number(next());\n else if (flag === \"--bypass\") args.bypass = true;\n else if (flag === \"--no-visual-change\") args.noVisualChange = true;\n else if (flag === \"--fallback-failure\") args.fallbackFailure = true;\n else throw new Error(`unknown flag ${flag}`);\n }\n if (!args.sha) throw new Error(\"--sha is required\");\n return args;\n}\n\nfunction gh(...ghArgs: string[]): string {\n return execFileSync(\"gh\", ghArgs, { encoding: \"utf-8\", windowsHide: true });\n}\n\nfunction postCommitStatus(\n repo: string,\n sha: string,\n state: \"success\" | \"failure\" | \"pending\",\n description: string,\n targetUrl?: string,\n): void {\n gh(\n \"api\",\n `repos/${repo}/statuses/${sha}`,\n \"-f\",\n `state=${state}`,\n \"-f\",\n `context=${STATUS_CONTEXT}`,\n \"-f\",\n `description=${description.slice(0, 140)}`,\n ...(targetUrl ? [\"-f\", `target_url=${targetUrl}`] : []),\n );\n}\n\nfunction upsertStickyComment(repo: string, pr: number, body: string): void {\n const comments = (\n JSON.parse(\n gh(\"api\", `repos/${repo}/issues/${pr}/comments`, \"--paginate\", \"--slurp\"),\n ) as Array<Array<{ id: number; body?: string }>>\n ).flat();\n const existing = comments.find(\n (comment) =>\n typeof comment.body === \"string\" && comment.body.includes(STICKY_MARKER),\n );\n if (existing) {\n gh(\n \"api\",\n \"--method\",\n \"PATCH\",\n `repos/${repo}/issues/comments/${existing.id}`,\n \"-f\",\n `body=${body}`,\n );\n } else {\n gh(\"api\", `repos/${repo}/issues/${pr}/comments`, \"-f\", `body=${body}`);\n }\n}\n\nfunction describeGate(\n gate: Gate,\n state: ReportStatus[\"state\"],\n): { state: \"success\" | \"failure\" | \"pending\"; description: string } {\n if (state === \"error\") {\n return {\n state: \"failure\",\n description: \"visual review run failed — see report\",\n };\n }\n if (state !== \"complete\") {\n return {\n state: \"pending\",\n description: \"capturing & comparing storyboards…\",\n };\n }\n if (gate.satisfied) {\n return {\n state: \"success\",\n description:\n gate.required === 0\n ? \"no visual changes\"\n : `${gate.required} visual change${gate.required === 1 ? \"\" : \"s\"} approved`,\n };\n }\n const parts: string[] = [];\n if ((gate.approvalsNeeded ?? 0) > 0) {\n parts.push(`${gate.approvalsNeeded} of ${gate.required} need approval`);\n }\n if ((gate.rejected ?? 0) > 0) parts.push(`${gate.rejected} rejected`);\n if ((gate.blockingErrors ?? 0) > 0) {\n parts.push(\n `${gate.blockingErrors} render error${gate.blockingErrors === 1 ? \"\" : \"s\"}`,\n );\n }\n return {\n state: \"failure\",\n description: `visual review: ${parts.join(\", \")} — see report`,\n };\n}\n\nfunction buildCommentBody(status: ReportStatus): string {\n const counts = status.counts ?? {};\n const gateLine = status.gate?.satisfied\n ? \"✅ **Check passing** — every visual change is approved.\"\n : `🔴 **Check failing** — ${status.gate?.approvalsNeeded ?? \"?\"} of ${status.gate?.required ?? \"?\"} storyboards need approval${status.gate?.blockingErrors ? `, ${status.gate.blockingErrors} render error(s)` : \"\"}${status.gate?.rejected ? `, ${status.gate.rejected} rejected` : \"\"}. Approving in the report flips the check instantly — no CI re-run.`;\n const summary = `| tracked | changed | new | removed | render errors | unchanged |\n| --- | --- | --- | --- | --- | --- |\n| ${status.trackedTotal ?? 0} | ${counts.changed ?? 0} | ${counts.added ?? 0} | ${counts.removed ?? 0} | ${counts.errors ?? 0} | ${counts.unchanged ?? 0} |`;\n\n const thumbs = (status.topChanged ?? [])\n .slice(0, 6)\n .map((board) => {\n const title = board.name ?? board.storyboardId;\n const meta =\n board.diffRatio !== undefined && board.diffRatio !== null\n ? ` · ${(board.diffRatio * 100).toFixed(2)}%`\n : \"\";\n if (board.verdict === \"render-error\") {\n return `**${title}** — render error${board.error ? `: \\`${board.error.slice(0, 120)}\\`` : \"\"}`;\n }\n const cell = (hash: string | undefined, label: string) =>\n hash && status.blobUrlTemplate\n ? `<img src=\"${status.blobUrlTemplate}${hash}\" alt=\"${label}\" width=\"360\" />`\n : \"—\";\n return `<details><summary><b>${title}</b> — ${board.verdict}${meta}</summary>\n\n| baseline | candidate |\n| --- | --- |\n| ${cell(board.baselineHash, \"baseline\")} | ${cell(board.candidateHash, \"candidate\")} |\n\n</details>`;\n })\n .join(\"\\n\");\n\n const notes: string[] = [];\n if (status.approximateBase) {\n notes.push(\n \"⚠ exact merge-base had no visual run — compared against the nearest captured ancestor.\",\n );\n }\n if (status.freshEpoch) {\n notes.push(\n \"ℹ fresh baseline epoch (capture environment changed) — everything reports as new.\",\n );\n }\n\n return `${STICKY_MARKER}\n## 👁 Visual review\n\n${gateLine}\n\n${summary}\n\n${notes.length > 0 ? `${notes.join(\"\\n\")}\\n` : \"\"}${thumbs}\n\n📷 **[Open the visual review report](${status.reportUrl})** — side-by-side, onion-skin and diff views; approve, reject, and comment there.`;\n}\n\nfunction buildNoVisualChangeComment(): string {\n return `${STICKY_MARKER}\n## 👁 Visual review\n\n✅ **Not applicable — no storyboard changes expected.** Storyboard capture was skipped from the PR declaration.`;\n}\n\nfunction latestContextState(repo: string, sha: string): string | null {\n const statuses = (\n JSON.parse(\n gh(\n \"api\",\n `repos/${repo}/commits/${sha}/statuses`,\n \"--paginate\",\n \"--slurp\",\n ),\n ) as Array<Array<{ context?: string; state?: string }>>\n ).flat();\n return (\n statuses.find((status) => status.context === STATUS_CONTEXT)?.state ?? null\n );\n}\n\nfunction isPermanentError(error: unknown): boolean {\n return (\n error instanceof Error && \"permanent\" in error && error.permanent === true\n );\n}\n\nexport async function main(argv = process.argv.slice(2)): Promise<number> {\n const args = parseArgs(argv);\n const repo = process.env.GITHUB_REPOSITORY;\n if (!repo) throw new Error(\"GITHUB_REPOSITORY is not set\");\n const sha = args.sha;\n if (!sha) throw new Error(\"--sha is required\");\n const declaration = visualReviewPrDeclaration();\n\n if (args.bypass || declaration.bypass) {\n postCommitStatus(\n repo,\n sha,\n \"success\",\n \"bypassed via visual-review:bypass label\",\n );\n console.log(\"bypass label present — posted success and skipped the report\");\n return 0;\n }\n\n if (args.noVisualChange || declaration.noVisualChange) {\n postCommitStatus(\n repo,\n sha,\n \"success\",\n \"not applicable — no storyboard changes expected\",\n );\n if (args.pr)\n upsertStickyComment(repo, args.pr, buildNoVisualChangeComment());\n console.log(\n \"no-visual-change declaration present — posted success and skipped capture\",\n );\n return 0;\n }\n\n if (args.fallbackFailure) {\n const current = latestContextState(repo, sha);\n if (current === null || current === \"pending\") {\n postCommitStatus(\n repo,\n sha,\n \"failure\",\n \"visual review reporting failed — see workflow logs\",\n );\n console.log(\n `reporting fallback: latest state was ${current ?? \"absent\"} — posted failure`,\n );\n } else {\n console.log(\n `reporting fallback: latest '${STATUS_CONTEXT}' state is '${current}' — leaving it`,\n );\n }\n return 0;\n }\n\n const statusUrl = args.statusUrl;\n if (!statusUrl) throw new Error(\"--status-url is required\");\n const deadline = Date.now() + args.pollTimeoutS * 1_000;\n let status: ReportStatus | undefined;\n for (;;) {\n try {\n const response = await fetch(statusUrl);\n if (response.status === 404) {\n const error = new Error(\"status poll 404 — wrong report key\");\n Object.assign(error, { permanent: true });\n throw error;\n }\n if (!response.ok)\n throw new Error(`status poll failed: ${response.status}`);\n status = (await response.json()) as ReportStatus;\n } catch (error) {\n if (isPermanentError(error)) throw error;\n console.warn(\n `transient status poll failure, retrying: ${error instanceof Error ? error.message : error}`,\n );\n }\n if (status && (status.state === \"complete\" || status.state === \"error\")) {\n break;\n }\n if (Date.now() > deadline) {\n postCommitStatus(\n repo,\n sha,\n \"failure\",\n \"visual review timed out waiting for compare — see report\",\n status?.reportUrl,\n );\n throw new Error(\"timed out waiting for the run to complete\");\n }\n await new Promise((resolve) => setTimeout(resolve, 5_000));\n }\n\n const verdict = describeGate(status.gate ?? {}, status.state);\n const publishFailure = publisherDeliveryFailure(status);\n if (status.publisherConfigured && publishFailure === null) {\n console.log(\n `verdict (${verdict.state}) owned by the Convex publisher — skipping CI status write`,\n );\n } else {\n if (publishFailure !== null) {\n console.log(\n `Convex publisher reported a delivery failure (${publishFailure.error}) — writing the status from CI as a fallback`,\n );\n }\n postCommitStatus(\n repo,\n sha,\n verdict.state,\n verdict.description,\n status.reportUrl,\n );\n console.log(`commit status: ${verdict.state} — ${verdict.description}`);\n }\n\n if (args.pr) {\n upsertStickyComment(repo, args.pr, buildCommentBody(status));\n console.log(`sticky comment upserted on #${args.pr}`);\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -534,6 +534,9 @@ function installRenderProfileInPage() {
|
|
|
534
534
|
const renderers = /* @__PURE__ */ new Map();
|
|
535
535
|
profileWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
|
|
536
536
|
supportsFiber: true,
|
|
537
|
+
// Consumers that also load `modules/iframe-projection` see this window
|
|
538
|
+
// property through its global `DevToolsHook` augmentation, where
|
|
539
|
+
// `renderers` is typed by ReactRenderer; this stub only counts them.
|
|
537
540
|
renderers,
|
|
538
541
|
inject(renderer) {
|
|
539
542
|
profile.reactInjectedMs ??= performance.now() - profile.startedAtMs;
|
|
@@ -4603,7 +4606,7 @@ async function parcelEventToFileEvent(event, rootInfo) {
|
|
|
4603
4606
|
// ../tempo-sdk/package.json
|
|
4604
4607
|
var package_default = {
|
|
4605
4608
|
name: "tempo-sdk",
|
|
4606
|
-
version: "0.0.
|
|
4609
|
+
version: "0.0.40",
|
|
4607
4610
|
type: "module",
|
|
4608
4611
|
description: "Tempo SDK \u2014 Vite, Next.js, and Expo plugins for JSX annotation and shared page/storyboard types",
|
|
4609
4612
|
repository: {
|
|
@@ -8009,7 +8012,7 @@ ${output}` : "";
|
|
|
8009
8012
|
this.notifyExitListeners(code, signal, "launcher-finished", false);
|
|
8010
8013
|
return;
|
|
8011
8014
|
}
|
|
8012
|
-
this.lastExitCrashed = !this.intentionalStop && (code !== 0 || signal != null
|
|
8015
|
+
this.lastExitCrashed = !this.intentionalStop && !isWindowsTeardownExitCode(code) && (code !== 0 || signal != null || this.readinessConfirmed);
|
|
8013
8016
|
if (cleanLauncherExit) {
|
|
8014
8017
|
this.launcherStopRequired = true;
|
|
8015
8018
|
}
|
|
@@ -8051,11 +8054,17 @@ ${output}` : "";
|
|
|
8051
8054
|
onExit(listener) {
|
|
8052
8055
|
if (this.launcherFinished && !this.terminalExitNotified) {
|
|
8053
8056
|
this.exitListeners.push(listener);
|
|
8054
|
-
listener(0, null, false, "launcher-finished");
|
|
8057
|
+
listener(0, null, false, "launcher-finished", false);
|
|
8055
8058
|
return;
|
|
8056
8059
|
}
|
|
8057
8060
|
if (this.stopped) {
|
|
8058
|
-
listener(
|
|
8061
|
+
listener(
|
|
8062
|
+
null,
|
|
8063
|
+
null,
|
|
8064
|
+
this.lastExitCrashed,
|
|
8065
|
+
"process-exit",
|
|
8066
|
+
this.intentionalStop
|
|
8067
|
+
);
|
|
8059
8068
|
return;
|
|
8060
8069
|
}
|
|
8061
8070
|
this.exitListeners.push(listener);
|
|
@@ -8178,6 +8187,19 @@ Hint: ${current.hint}` : "";
|
|
|
8178
8187
|
this.lastExitCrashed = true;
|
|
8179
8188
|
this.notifyExitListeners(null, null, "probe-death");
|
|
8180
8189
|
}
|
|
8190
|
+
/**
|
|
8191
|
+
* The supervisor's liveness check found this child gone without an `exit`
|
|
8192
|
+
* event having reached us (a lost SIGCHLD). Deliver the one terminal exit
|
|
8193
|
+
* its consumers are owed, so the adapter and pool release the host instead
|
|
8194
|
+
* of keeping a dead incarnation `running`; an `exit` event that arrives
|
|
8195
|
+
* late is then a no-op (`terminalExitNotified`).
|
|
8196
|
+
*/
|
|
8197
|
+
reportObservedChildDeath() {
|
|
8198
|
+
if (this.stopped || this.terminalExitNotified) return;
|
|
8199
|
+
this.stopped = true;
|
|
8200
|
+
this.lastExitCrashed = !this.intentionalStop;
|
|
8201
|
+
this.notifyExitListeners(null, null, "process-exit");
|
|
8202
|
+
}
|
|
8181
8203
|
/** Whether the most recent exit looked like an unexpected crash. */
|
|
8182
8204
|
didLastExitCrash() {
|
|
8183
8205
|
return this.lastExitCrashed;
|
|
@@ -8434,7 +8456,13 @@ Hint: ${current.hint}` : "";
|
|
|
8434
8456
|
}
|
|
8435
8457
|
for (const listener of listeners) {
|
|
8436
8458
|
try {
|
|
8437
|
-
listener(
|
|
8459
|
+
listener(
|
|
8460
|
+
code,
|
|
8461
|
+
signal,
|
|
8462
|
+
this.lastExitCrashed,
|
|
8463
|
+
reason,
|
|
8464
|
+
this.intentionalStop
|
|
8465
|
+
);
|
|
8438
8466
|
} catch {
|
|
8439
8467
|
}
|
|
8440
8468
|
}
|
|
@@ -8811,13 +8839,13 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8811
8839
|
const hintListeners = [];
|
|
8812
8840
|
const exitListeners = [];
|
|
8813
8841
|
const attachProcessListeners = (activeProcess) => {
|
|
8814
|
-
activeProcess.onExit((code, signal, crashed, reason) => {
|
|
8842
|
+
activeProcess.onExit((code, signal, crashed, reason, intentional) => {
|
|
8815
8843
|
if (reason === "launcher-finished") {
|
|
8816
8844
|
devLog("supervisor:launcher_finished", {
|
|
8817
8845
|
pid: activeProcess.getPid()
|
|
8818
8846
|
});
|
|
8819
8847
|
for (const listener of exitListeners) {
|
|
8820
|
-
listener(code, signal, crashed, reason);
|
|
8848
|
+
listener(code, signal, crashed, reason, intentional);
|
|
8821
8849
|
}
|
|
8822
8850
|
return;
|
|
8823
8851
|
}
|
|
@@ -8850,7 +8878,7 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8850
8878
|
this.persistStatus().catch(() => {
|
|
8851
8879
|
});
|
|
8852
8880
|
for (const listener of exitListeners) {
|
|
8853
|
-
listener(code, signal, crashed, reason);
|
|
8881
|
+
listener(code, signal, crashed, reason, intentional);
|
|
8854
8882
|
}
|
|
8855
8883
|
});
|
|
8856
8884
|
for (const listener of outputListeners) {
|
|
@@ -8911,6 +8939,14 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8911
8939
|
this.devserverLaunchPid = activeProcess.getPid();
|
|
8912
8940
|
this.devserverLaunchProcessStartedAt = this.devserverLaunchPid ? await readProcessStartedAt(this.devserverLaunchPid) : null;
|
|
8913
8941
|
attachProcessListeners(activeProcess);
|
|
8942
|
+
try {
|
|
8943
|
+
options?.onProcessSpawned?.();
|
|
8944
|
+
} catch (error) {
|
|
8945
|
+
devLog("supervisor:on_process_spawned_threw", {
|
|
8946
|
+
pid: activeProcess.getPid(),
|
|
8947
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8948
|
+
});
|
|
8949
|
+
}
|
|
8914
8950
|
await this.writeProcessMarker(activeProcess.getPid()).catch(() => {
|
|
8915
8951
|
});
|
|
8916
8952
|
if (options?.signal?.aborted) {
|
|
@@ -8987,7 +9023,7 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8987
9023
|
onExit(listener) {
|
|
8988
9024
|
exitListeners.push(listener);
|
|
8989
9025
|
if (process2.getLivenessSource() === "probe") {
|
|
8990
|
-
listener(0, null, false, "launcher-finished");
|
|
9026
|
+
listener(0, null, false, "launcher-finished", false);
|
|
8991
9027
|
}
|
|
8992
9028
|
},
|
|
8993
9029
|
onOutput(listener) {
|
|
@@ -9833,6 +9869,7 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
9833
9869
|
this.stopUrlHealthProbe();
|
|
9834
9870
|
return;
|
|
9835
9871
|
}
|
|
9872
|
+
this.activeHostProcess?.reportObservedChildDeath();
|
|
9836
9873
|
this.state.devserverUrl = null;
|
|
9837
9874
|
this.state.urlUnreachableSince = null;
|
|
9838
9875
|
this.idleProcessStatus = "exited";
|
|
@@ -12199,6 +12236,44 @@ async function readTempoDevserverConfig(tempoConfigPath) {
|
|
|
12199
12236
|
tempoHostUrl: config.urls?.tempoHost ?? null
|
|
12200
12237
|
};
|
|
12201
12238
|
}
|
|
12239
|
+
function projectRootForTempoDir(tempoDir) {
|
|
12240
|
+
const resolved = path14.resolve(tempoDir);
|
|
12241
|
+
return path14.basename(resolved) === "tempo" ? path14.dirname(resolved) : resolved;
|
|
12242
|
+
}
|
|
12243
|
+
async function resolveCanvasHostLayout(options) {
|
|
12244
|
+
const repoRoot = path14.resolve(options.repoRoot);
|
|
12245
|
+
const candidates = options.tempoDir ? [path14.join(path14.resolve(options.tempoDir), "tempo.config.json")] : [
|
|
12246
|
+
path14.join(repoRoot, "tempo", "tempo.config.json"),
|
|
12247
|
+
path14.join(repoRoot, "tempo.config.json")
|
|
12248
|
+
];
|
|
12249
|
+
for (const configPath of candidates) {
|
|
12250
|
+
let parsed;
|
|
12251
|
+
try {
|
|
12252
|
+
parsed = JSON.parse(await readFile10(configPath, "utf-8"));
|
|
12253
|
+
} catch {
|
|
12254
|
+
continue;
|
|
12255
|
+
}
|
|
12256
|
+
if (typeof parsed !== "object" || parsed === null || !("scripts" in parsed)) {
|
|
12257
|
+
continue;
|
|
12258
|
+
}
|
|
12259
|
+
const tempoDir2 = path14.dirname(configPath);
|
|
12260
|
+
const paths = parsed.paths;
|
|
12261
|
+
const canvases = typeof paths?.canvases === "string" && paths.canvases.trim().length > 0 ? paths.canvases.trim().split("\\").join("/") : "designs";
|
|
12262
|
+
return {
|
|
12263
|
+
tempoDir: tempoDir2,
|
|
12264
|
+
projectRoot: projectRootForTempoDir(tempoDir2),
|
|
12265
|
+
pagesDir: path14.resolve(tempoDir2, canvases),
|
|
12266
|
+
configPath
|
|
12267
|
+
};
|
|
12268
|
+
}
|
|
12269
|
+
const tempoDir = options.tempoDir ? path14.resolve(options.tempoDir) : path14.join(repoRoot, "tempo");
|
|
12270
|
+
return {
|
|
12271
|
+
tempoDir,
|
|
12272
|
+
projectRoot: projectRootForTempoDir(tempoDir),
|
|
12273
|
+
pagesDir: path14.join(tempoDir, "designs"),
|
|
12274
|
+
configPath: null
|
|
12275
|
+
};
|
|
12276
|
+
}
|
|
12202
12277
|
function resolveTempoDir(options) {
|
|
12203
12278
|
if (options.tempoConfigPath) return path14.dirname(options.tempoConfigPath);
|
|
12204
12279
|
if (options.tempoDir) return options.tempoDir;
|
|
@@ -12283,7 +12358,7 @@ async function startDevserverSession(options) {
|
|
|
12283
12358
|
scripts: config.scripts
|
|
12284
12359
|
});
|
|
12285
12360
|
await devServer.initialize();
|
|
12286
|
-
const projectRoot =
|
|
12361
|
+
const projectRoot = projectRootForTempoDir(tempoDir);
|
|
12287
12362
|
for (const hostCanvasPath of hostCanvasPaths) {
|
|
12288
12363
|
await devServer.ensureCanvasRegistered(
|
|
12289
12364
|
path14.join(projectRoot, hostCanvasPath)
|
|
@@ -12359,14 +12434,14 @@ async function startDevserverSession(options) {
|
|
|
12359
12434
|
}
|
|
12360
12435
|
|
|
12361
12436
|
// ../canvas-capture-headless/src/enumerate.ts
|
|
12362
|
-
import { readFile as readFile11 } from "fs/promises";
|
|
12437
|
+
import { access as access4, readFile as readFile11 } from "fs/promises";
|
|
12363
12438
|
import path15 from "path";
|
|
12364
12439
|
function toPosixPath2(value) {
|
|
12365
12440
|
return value.split(path15.sep).join("/");
|
|
12366
12441
|
}
|
|
12367
12442
|
async function enumerateCanvasStoryboards(options) {
|
|
12368
12443
|
const pagesDir = options.pagesDir ?? path15.join(options.tempoDir, "designs");
|
|
12369
|
-
const projectRoot =
|
|
12444
|
+
const projectRoot = projectRootForTempoDir(options.tempoDir);
|
|
12370
12445
|
const canvasFiles = await scanCanvasFiles(pagesDir);
|
|
12371
12446
|
const storyboards = [];
|
|
12372
12447
|
const structuralErrors = [];
|
|
@@ -12431,6 +12506,61 @@ async function enumerateCanvasStoryboards(options) {
|
|
|
12431
12506
|
vrtConfigErrors
|
|
12432
12507
|
};
|
|
12433
12508
|
}
|
|
12509
|
+
async function enumerateTrackedStoryboards(options) {
|
|
12510
|
+
try {
|
|
12511
|
+
await access4(options.tempoDir);
|
|
12512
|
+
} catch {
|
|
12513
|
+
return {
|
|
12514
|
+
tracked: [],
|
|
12515
|
+
paused: [],
|
|
12516
|
+
totalStoryboards: 0,
|
|
12517
|
+
totalCanvases: 0,
|
|
12518
|
+
errors: []
|
|
12519
|
+
};
|
|
12520
|
+
}
|
|
12521
|
+
const enumeration = await enumerateCanvasStoryboards(options);
|
|
12522
|
+
const tracked = [];
|
|
12523
|
+
const paused = [];
|
|
12524
|
+
const errors = [
|
|
12525
|
+
...enumeration.structuralErrors,
|
|
12526
|
+
...enumeration.vrtConfigErrors
|
|
12527
|
+
];
|
|
12528
|
+
for (const storyboard of enumeration.storyboards) {
|
|
12529
|
+
if (!storyboard.vrt) continue;
|
|
12530
|
+
const board = {
|
|
12531
|
+
canvasPath: storyboard.canvasPath,
|
|
12532
|
+
hostCanvasPath: storyboard.hostCanvasPath,
|
|
12533
|
+
storyboardId: storyboard.storyboardId,
|
|
12534
|
+
name: storyboard.name,
|
|
12535
|
+
layout: storyboard.layout,
|
|
12536
|
+
threshold: storyboard.vrt.threshold,
|
|
12537
|
+
ignore: storyboard.vrt.ignore
|
|
12538
|
+
};
|
|
12539
|
+
const maskedArea = (storyboard.vrt.ignore ?? []).reduce(
|
|
12540
|
+
(total, region) => total + Math.max(0, Math.min(region.w, board.layout.width)) * Math.max(0, Math.min(region.h, board.layout.height)),
|
|
12541
|
+
0
|
|
12542
|
+
);
|
|
12543
|
+
if (maskedArea >= 0.9 * board.layout.width * board.layout.height) {
|
|
12544
|
+
errors.push({
|
|
12545
|
+
filePath: storyboard.filePath,
|
|
12546
|
+
message: `<Storyboard id="${storyboard.storyboardId}"> vrt.ignore masks \u226590% of the board \u2014 untrack it instead`
|
|
12547
|
+
});
|
|
12548
|
+
continue;
|
|
12549
|
+
}
|
|
12550
|
+
if (storyboard.vrt.skip) {
|
|
12551
|
+
paused.push(board);
|
|
12552
|
+
continue;
|
|
12553
|
+
}
|
|
12554
|
+
tracked.push(board);
|
|
12555
|
+
}
|
|
12556
|
+
return {
|
|
12557
|
+
tracked,
|
|
12558
|
+
paused,
|
|
12559
|
+
totalStoryboards: enumeration.totalStoryboards,
|
|
12560
|
+
totalCanvases: enumeration.totalCanvases,
|
|
12561
|
+
errors
|
|
12562
|
+
};
|
|
12563
|
+
}
|
|
12434
12564
|
|
|
12435
12565
|
// ../canvas-capture-headless/src/timing.ts
|
|
12436
12566
|
function percentile(sortedValues, p) {
|
|
@@ -12510,64 +12640,8 @@ var TimingReport = class {
|
|
|
12510
12640
|
};
|
|
12511
12641
|
|
|
12512
12642
|
// src/enumerate.ts
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
try {
|
|
12516
|
-
await access4(options.tempoDir);
|
|
12517
|
-
} catch {
|
|
12518
|
-
return {
|
|
12519
|
-
tracked: [],
|
|
12520
|
-
paused: [],
|
|
12521
|
-
totalStoryboards: 0,
|
|
12522
|
-
totalCanvases: 0,
|
|
12523
|
-
errors: []
|
|
12524
|
-
};
|
|
12525
|
-
}
|
|
12526
|
-
const enumeration = await enumerateCanvasStoryboards({
|
|
12527
|
-
tempoDir: options.tempoDir,
|
|
12528
|
-
pagesDir: options.pagesDir
|
|
12529
|
-
});
|
|
12530
|
-
const tracked = [];
|
|
12531
|
-
const paused = [];
|
|
12532
|
-
const errors = [
|
|
12533
|
-
...enumeration.structuralErrors,
|
|
12534
|
-
...enumeration.vrtConfigErrors
|
|
12535
|
-
];
|
|
12536
|
-
for (const storyboard of enumeration.storyboards) {
|
|
12537
|
-
if (!storyboard.vrt) continue;
|
|
12538
|
-
const board = {
|
|
12539
|
-
canvasPath: storyboard.canvasPath,
|
|
12540
|
-
hostCanvasPath: storyboard.hostCanvasPath,
|
|
12541
|
-
storyboardId: storyboard.storyboardId,
|
|
12542
|
-
name: storyboard.name,
|
|
12543
|
-
layout: storyboard.layout,
|
|
12544
|
-
threshold: storyboard.vrt.threshold,
|
|
12545
|
-
ignore: storyboard.vrt.ignore
|
|
12546
|
-
};
|
|
12547
|
-
const maskedArea = (storyboard.vrt.ignore ?? []).reduce(
|
|
12548
|
-
(total, region) => total + Math.max(0, Math.min(region.w, board.layout.width)) * Math.max(0, Math.min(region.h, board.layout.height)),
|
|
12549
|
-
0
|
|
12550
|
-
);
|
|
12551
|
-
if (maskedArea >= 0.9 * board.layout.width * board.layout.height) {
|
|
12552
|
-
errors.push({
|
|
12553
|
-
filePath: storyboard.filePath,
|
|
12554
|
-
message: `<Storyboard id="${storyboard.storyboardId}"> vrt.ignore masks \u226590% of the board \u2014 untrack it instead`
|
|
12555
|
-
});
|
|
12556
|
-
continue;
|
|
12557
|
-
}
|
|
12558
|
-
if (storyboard.vrt.skip) {
|
|
12559
|
-
paused.push(board);
|
|
12560
|
-
continue;
|
|
12561
|
-
}
|
|
12562
|
-
tracked.push(board);
|
|
12563
|
-
}
|
|
12564
|
-
return {
|
|
12565
|
-
tracked,
|
|
12566
|
-
paused,
|
|
12567
|
-
totalStoryboards: enumeration.totalStoryboards,
|
|
12568
|
-
totalCanvases: enumeration.totalCanvases,
|
|
12569
|
-
errors
|
|
12570
|
-
};
|
|
12643
|
+
async function enumerateTrackedStoryboards2(options) {
|
|
12644
|
+
return await enumerateTrackedStoryboards(options);
|
|
12571
12645
|
}
|
|
12572
12646
|
|
|
12573
12647
|
// src/fingerprint.ts
|
|
@@ -12790,6 +12864,7 @@ function parseArgs(argv) {
|
|
|
12790
12864
|
const args = {
|
|
12791
12865
|
repoRoot: inferRepoRoot(),
|
|
12792
12866
|
tempoDir: "",
|
|
12867
|
+
tempoDirExplicit: false,
|
|
12793
12868
|
// The published CLI is intentionally near-argumentless in Actions. Local
|
|
12794
12869
|
// capture remains Convex-free unless --upload is explicit.
|
|
12795
12870
|
upload: process.env.GITHUB_ACTIONS === "true" && inferredTrigger !== "manual",
|
|
@@ -12820,6 +12895,7 @@ function parseArgs(argv) {
|
|
|
12820
12895
|
break;
|
|
12821
12896
|
case "--tempo-dir":
|
|
12822
12897
|
args.tempoDir = path16.resolve(next());
|
|
12898
|
+
args.tempoDirExplicit = true;
|
|
12823
12899
|
break;
|
|
12824
12900
|
case "--out":
|
|
12825
12901
|
args.out = path16.resolve(next());
|
|
@@ -13012,6 +13088,12 @@ function startClaimHeartbeat(options) {
|
|
|
13012
13088
|
}
|
|
13013
13089
|
};
|
|
13014
13090
|
}
|
|
13091
|
+
function explicitTempoDirRelative(args) {
|
|
13092
|
+
if (!args.tempoDirExplicit) return void 0;
|
|
13093
|
+
const relative2 = path16.relative(args.repoRoot, args.tempoDir);
|
|
13094
|
+
if (relative2.startsWith("..") || path16.isAbsolute(relative2)) return void 0;
|
|
13095
|
+
return relative2;
|
|
13096
|
+
}
|
|
13015
13097
|
function buildBaselineCaptureArgs(input) {
|
|
13016
13098
|
const args = [
|
|
13017
13099
|
"capture",
|
|
@@ -13033,6 +13115,12 @@ function buildBaselineCaptureArgs(input) {
|
|
|
13033
13115
|
];
|
|
13034
13116
|
if (input.url) args.push("--url", input.url);
|
|
13035
13117
|
if (input.githubRepo) args.push("--github-repo", input.githubRepo);
|
|
13118
|
+
if (input.tempoDirRelative !== void 0) {
|
|
13119
|
+
args.push(
|
|
13120
|
+
"--tempo-dir",
|
|
13121
|
+
path16.join(input.worktreeRoot, input.tempoDirRelative || ".")
|
|
13122
|
+
);
|
|
13123
|
+
}
|
|
13036
13124
|
if (input.repoKey) args.push("--repo-key", input.repoKey);
|
|
13037
13125
|
if (input.settleTimeoutMs !== void 0) {
|
|
13038
13126
|
args.push("--settle-timeout", String(input.settleTimeoutMs));
|
|
@@ -13067,7 +13155,8 @@ async function runBaselineCaptureInWorktree(options) {
|
|
|
13067
13155
|
url: options.args.url,
|
|
13068
13156
|
githubRepo: options.args.githubRepo,
|
|
13069
13157
|
repoKey: options.args.repoKey,
|
|
13070
|
-
settleTimeoutMs: options.args.settleTimeoutMs
|
|
13158
|
+
settleTimeoutMs: options.args.settleTimeoutMs,
|
|
13159
|
+
tempoDirRelative: explicitTempoDirRelative(options.args)
|
|
13071
13160
|
})
|
|
13072
13161
|
];
|
|
13073
13162
|
await new Promise((resolve2, reject) => {
|
|
@@ -13118,10 +13207,15 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
13118
13207
|
if (args.trigger === "pr" && baseCandidates.length === 0) {
|
|
13119
13208
|
throw new Error("--trigger pr requires --base <merge-base sha>");
|
|
13120
13209
|
}
|
|
13121
|
-
const
|
|
13210
|
+
const layout = await resolveCanvasHostLayout({
|
|
13211
|
+
repoRoot: args.repoRoot,
|
|
13212
|
+
tempoDir: args.tempoDirExplicit ? args.tempoDir : void 0
|
|
13213
|
+
});
|
|
13214
|
+
args.tempoDir = layout.tempoDir;
|
|
13215
|
+
const pagesDir = layout.pagesDir;
|
|
13122
13216
|
const enumeration = await timing.phase(
|
|
13123
13217
|
"enumerate",
|
|
13124
|
-
() =>
|
|
13218
|
+
() => enumerateTrackedStoryboards2({ tempoDir: args.tempoDir, pagesDir })
|
|
13125
13219
|
);
|
|
13126
13220
|
if (enumeration.errors.length > 0) {
|
|
13127
13221
|
for (const error of enumeration.errors) {
|
|
@@ -13574,13 +13668,14 @@ export {
|
|
|
13574
13668
|
percentile,
|
|
13575
13669
|
summarize,
|
|
13576
13670
|
TimingReport,
|
|
13577
|
-
enumerateTrackedStoryboards,
|
|
13671
|
+
enumerateTrackedStoryboards2 as enumerateTrackedStoryboards,
|
|
13578
13672
|
scaleIgnoreRegionsToDevicePx,
|
|
13579
13673
|
VisualReviewHttpError,
|
|
13580
13674
|
VisualReviewIngestClient,
|
|
13581
13675
|
resolveOrClaimBaseline,
|
|
13676
|
+
explicitTempoDirRelative,
|
|
13582
13677
|
buildBaselineCaptureArgs,
|
|
13583
13678
|
main,
|
|
13584
13679
|
VISUAL_REVIEW_OIDC_AUDIENCE
|
|
13585
13680
|
};
|
|
13586
|
-
//# sourceMappingURL=chunk-
|
|
13681
|
+
//# sourceMappingURL=chunk-MW6S36CX.js.map
|