@tempo-ai/visual-review 0.0.125-staging.3 → 0.0.125

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 CHANGED
@@ -7,7 +7,7 @@ async function main(argv = process.argv.slice(2)) {
7
7
  const [command, ...commandArgs] = argv;
8
8
  switch (command) {
9
9
  case "capture":
10
- return await (await import("./cli-TS2B77QU.js")).main(commandArgs);
10
+ return await (await import("./cli-XCXFIBBF.js")).main(commandArgs);
11
11
  case "claim":
12
12
  return await (await import("./claim-47UEXY7A.js")).main(commandArgs);
13
13
  case "report":
@@ -7321,7 +7321,10 @@ var DIRECT_RENDER_PARAMS = {
7321
7321
  height: "height",
7322
7322
  idle: "tempoCaptureIdle"
7323
7323
  };
7324
- var RESPONSIVE_CAPTURE_VIEWPORT = { width: 1600, height: 1200 };
7324
+ var RESPONSIVE_CAPTURE_VIEWPORT = {
7325
+ width: 1600,
7326
+ height: 1200
7327
+ };
7325
7328
  var STORYBOARD_RENDER_ID_ATTRIBUTE = "data-tempo-storyboard-render-id";
7326
7329
  var STORYBOARD_PENDING_SELECTOR = `[${PROJECTION_PENDING_ATTRIBUTE}]`;
7327
7330
  function buildCanvasStoryboardRenderId(canvasPath, storyboardId) {
@@ -7332,12 +7335,13 @@ function buildCanvasStoryboardRenderId(canvasPath, storyboardId) {
7332
7335
  function buildStoryboardRenderSelector(renderId) {
7333
7336
  return `[${STORYBOARD_RENDER_ID_ATTRIBUTE}=${JSON.stringify(renderId)}]`;
7334
7337
  }
7335
- function buildCanvasHostUrl(hostUrl) {
7338
+ function buildCanvasHostUrl(hostUrl, incremental = false) {
7336
7339
  const url = new URL(hostUrl);
7337
7340
  url.searchParams.set(
7338
7341
  CANVAS_CLIENT_PROTOCOL_PARAM,
7339
7342
  String(CANVAS_CLIENT_PROTOCOL)
7340
7343
  );
7344
+ if (incremental) url.searchParams.set("canvasRegistration", "incremental");
7341
7345
  return url;
7342
7346
  }
7343
7347
  function buildDirectRenderUrl(hostUrl, target) {
@@ -7532,6 +7536,21 @@ function buildSettleWaitScript(options) {
7532
7536
  function renderedImages(target) {
7533
7537
  return renderedMatches(target, 'img');
7534
7538
  }
7539
+ // The source each image was last asked to decode, and whether that
7540
+ // decode has settled. A new source asks again.
7541
+ var decodes = typeof WeakMap === 'function' ? new WeakMap() : null;
7542
+ function imageDecoded(img) {
7543
+ if (!decodes || typeof img.decode !== 'function') return true;
7544
+ var src = img.currentSrc || img.src;
7545
+ var entry = decodes.get(img);
7546
+ if (!entry || entry.src !== src) {
7547
+ entry = { src: src, done: false };
7548
+ decodes.set(img, entry);
7549
+ var settled = function() { entry.done = true; };
7550
+ img.decode().then(settled, settled);
7551
+ }
7552
+ return entry.done;
7553
+ }
7535
7554
  // A video paints its current frame only once that frame is DECODED.
7536
7555
  // HAVE_CURRENT_DATA (2) is exactly that bar.
7537
7556
  //
@@ -7566,6 +7585,17 @@ function buildSettleWaitScript(options) {
7566
7585
  return false;
7567
7586
  }
7568
7587
  }
7588
+ // A loaded image can still paint as empty space until it decodes
7589
+ // (decoding="async" lets the browser paint first). Ask each source
7590
+ // to decode once; a failed decode counts as finished.
7591
+ var decoding = false;
7592
+ for (var d = 0; d < images.length; d++) {
7593
+ if (!imageDecoded(images[d])) decoding = true;
7594
+ }
7595
+ if (decoding) {
7596
+ quietKey = null;
7597
+ return false;
7598
+ }
7569
7599
  // Second line of defence behind videoFrameReady's fail-open rules: a
7570
7600
  // video that is legitimately loading can still stall forever (a dev
7571
7601
  // server that never answers, a huge asset). Waiting is an OPTIMISATION
@@ -7650,6 +7680,34 @@ function buildSettleWaitScript(options) {
7650
7680
  `;
7651
7681
  }
7652
7682
 
7683
+ // ../canvas-capture-headless/src/fingerprint.ts
7684
+ var KIT_VERSION = "6";
7685
+ var FONTS_BUNDLE_VERSION = "system-1";
7686
+ var DEVICE_SCALE_FACTOR = 1;
7687
+ function buildEnvFingerprint(input) {
7688
+ const parts = {
7689
+ os: input.os,
7690
+ // FULL Chromium version, not just the major: a patch-level rendering
7691
+ // change must open a fresh epoch, not surface as mass false diffs
7692
+ // inside the old one. The version moves only with the lockfile's
7693
+ // Playwright pin, so epochs track dependency bumps.
7694
+ browser: `chromium-${input.browserVersion}`,
7695
+ dpr: DEVICE_SCALE_FACTOR,
7696
+ fontsBundleVersion: FONTS_BUNDLE_VERSION,
7697
+ kitVersion: KIT_VERSION
7698
+ };
7699
+ return {
7700
+ fingerprint: [
7701
+ parts.os,
7702
+ parts.browser,
7703
+ `dpr${parts.dpr}`,
7704
+ `fonts-${parts.fontsBundleVersion}`,
7705
+ `kit-${parts.kitVersion}`
7706
+ ].join("/"),
7707
+ parts
7708
+ };
7709
+ }
7710
+
7653
7711
  // ../canvas-capture-headless/src/navigation-errors.ts
7654
7712
  function isNavigationInterruptedError(message) {
7655
7713
  return /Execution context was destroyed|most likely because of a navigation|Cannot find context with specified id|[Ff]rame was detached|Navigation interrupted|is interrupted by another navigation|Inspected target navigated or closed|Cannot find object with id|context was destroyed/.test(
@@ -7660,7 +7718,7 @@ function isNavigationInterruptedError(message) {
7660
7718
  // ../canvas-capture-headless/src/determinism.ts
7661
7719
  var CHROMIUM_LAUNCH_ARGS = [
7662
7720
  "--force-color-profile=srgb",
7663
- "--force-device-scale-factor=2",
7721
+ `--force-device-scale-factor=${DEVICE_SCALE_FACTOR}`,
7664
7722
  "--hide-scrollbars",
7665
7723
  "--disable-lcd-text"
7666
7724
  ];
@@ -7761,34 +7819,6 @@ async function installChromium(log, spawnImpl = spawn) {
7761
7819
  });
7762
7820
  }
7763
7821
 
7764
- // ../canvas-capture-headless/src/fingerprint.ts
7765
- var KIT_VERSION = "5";
7766
- var FONTS_BUNDLE_VERSION = "system-1";
7767
- var DEVICE_SCALE_FACTOR = 2;
7768
- function buildEnvFingerprint(input) {
7769
- const parts = {
7770
- os: input.os,
7771
- // FULL Chromium version, not just the major: a patch-level rendering
7772
- // change must open a fresh epoch, not surface as mass false diffs
7773
- // inside the old one. The version moves only with the lockfile's
7774
- // Playwright pin, so epochs track dependency bumps.
7775
- browser: `chromium-${input.browserVersion}`,
7776
- dpr: DEVICE_SCALE_FACTOR,
7777
- fontsBundleVersion: FONTS_BUNDLE_VERSION,
7778
- kitVersion: KIT_VERSION
7779
- };
7780
- return {
7781
- fingerprint: [
7782
- parts.os,
7783
- parts.browser,
7784
- `dpr${parts.dpr}`,
7785
- `fonts-${parts.fontsBundleVersion}`,
7786
- `kit-${parts.kitVersion}`
7787
- ].join("/"),
7788
- parts
7789
- };
7790
- }
7791
-
7792
7822
  // ../canvas-capture-headless/src/html-document.ts
7793
7823
  var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
7794
7824
  var ROOT_SIZE_STYLE = "<style>html{width:100%;height:100%;}</style>";
@@ -17703,7 +17733,7 @@ var MissingRootDepsError = class extends TempoDevServerError {
17703
17733
  // ../tempo-sdk/package.json
17704
17734
  var package_default = {
17705
17735
  name: "tempo-sdk",
17706
- version: "0.0.57",
17736
+ version: "0.0.60",
17707
17737
  publishConfig: {
17708
17738
  tag: "sdk-v2-latest"
17709
17739
  },
@@ -17787,7 +17817,7 @@ var package_default = {
17787
17817
  demo: "TEMPO=true pnpm --filter @tempo-modules/vite-annotate-plugin-demo dev",
17788
17818
  "demo:nextjs": "TEMPO=true pnpm --filter @tempo-modules/tempo-sdk-nextjs-demo dev",
17789
17819
  test: "vitest run",
17790
- "test:browser": "vitest run pruned-module-deletion-browser.test.ts asset-live-preview-browser.test.ts asset-popover-lifecycle-browser.test.ts component-controls-browser.test.ts canvas-controls-browser.test.ts canvas-capture-determinism-browser.test.ts canvas-host-vite-e2e.test.ts canvas-host-packed-sdk-e2e.test.ts error-tracking-e2e.test.ts",
17820
+ "test:browser": "vitest run canvas-dependency-scan-browser.test.ts pruned-module-deletion-browser.test.ts asset-live-preview-browser.test.ts asset-popover-lifecycle-browser.test.ts component-controls-browser.test.ts canvas-controls-browser.test.ts canvas-capture-determinism-browser.test.ts canvas-host-vite-e2e.test.ts canvas-host-packed-sdk-e2e.test.ts error-tracking-e2e.test.ts",
17791
17821
  "test:watch": "vitest",
17792
17822
  "test:nextjs:unit": "vitest run nextjs-loader.test.ts nextjs-wrapper.test.ts nextjs-fixtures.test.ts nextjs-published.test.ts",
17793
17823
  "test:nextjs:integration": "vitest run nextjs-e2e-correctness.test.ts",
@@ -18128,7 +18158,12 @@ async function prepareManagedDependencyMigration(options) {
18128
18158
  "Yarn Classic cannot prepare a lockfile without installing packages. Configure scripts.updateSdk to migrate this environment before its frozen install."
18129
18159
  );
18130
18160
  }
18131
- await options.runCommand(plan.command, root, options.signal);
18161
+ await options.runCommand(
18162
+ plan.command,
18163
+ root,
18164
+ options.signal,
18165
+ plan.packageManager
18166
+ );
18132
18167
  synced.add(plan.lockfile);
18133
18168
  }
18134
18169
  }
@@ -24246,6 +24281,10 @@ var TempoDevServer = class _TempoDevServer {
24246
24281
  // It keeps Tempo's host cache isolated from the user's own `next dev`.
24247
24282
  extraEnv: {
24248
24283
  ...startPlan.env,
24284
+ // Scoped to this canvas host. The SDK uses this to retire the old
24285
+ // all-canvas optimizer glob in memory, preserving the user's file.
24286
+ TEMPO_CANVAS_HOST_ROOT: this.tempoRoot,
24287
+ TEMPO_CANVAS_SOURCE_ROOT: this.pagesDir,
24249
24288
  ...expectedIdentity ? { TEMPO_WORKSPACE_NONCE: expectedIdentity.workspaceNonce } : {},
24250
24289
  ...this.nextDistDir ? { TEMPO_NEXT_DIST_DIR: this.nextDistDir } : {}
24251
24290
  },
@@ -24561,10 +24600,10 @@ var TempoDevServer = class _TempoDevServer {
24561
24600
  this.config.scripts.install
24562
24601
  ),
24563
24602
  signal: options.signal,
24564
- runCommand: (command, cwd, signal) => {
24603
+ runCommand: async (command, cwd, signal, pm) => {
24565
24604
  options.onOutput?.("Updating Tempo dependency lockfile...\n");
24566
24605
  return runShellCommand(
24567
- command,
24606
+ await buildSafeInstallCommand({ pm, installCmd: command }, cwd),
24568
24607
  cwd,
24569
24608
  signal,
24570
24609
  options.onOutput,
@@ -24727,13 +24766,15 @@ var TempoDevServer = class _TempoDevServer {
24727
24766
  cwd: this.tempoRoot,
24728
24767
  command: await buildSafeInstallCommand(tempoManager, this.tempoRoot),
24729
24768
  label: "tempo sidecar install",
24730
- // On a forced retry (e.g. a crash-retry after the devserver died) the
24731
- // existing tree is suspect, so wipe it for a clean, complete install
24732
- // rather than an incremental top-up over a possibly-partial tree. A
24733
- // routine "not ready" (marker absent/stale, no force) does an ordinary
24734
- // incremental install — cheap when already satisfied, and npm restores
24735
- // any missing optional-native binary. See TEM-820.
24736
- cleanBeforeInstall: forceInstall,
24769
+ // A forced retry (e.g. a crash-retry after the devserver died) or a
24770
+ // tree whose previous install never finished is suspect, so wipe it
24771
+ // for a clean, complete install. npm never re-verifies files it already
24772
+ // extracted: a disk-full or killed install leaves truncated native
24773
+ // binaries that an incremental top-up keeps forever (TEM-2467). A
24774
+ // routine "not ready" after a COMPLETED install (deps changed) does an
24775
+ // ordinary incremental install — cheap when already satisfied, and npm
24776
+ // restores any missing optional-native binary. See TEM-820.
24777
+ cleanBeforeInstall: forceInstall || await installWasInterrupted(this.tempoRoot),
24737
24778
  // Record completion only after `npm install` exits 0 so an interrupted
24738
24779
  // install is never mistaken for a finished one on the next start.
24739
24780
  markerRoots: [this.tempoRoot]
@@ -25996,7 +26037,7 @@ function buildTempoHostWrapper(reactCanvasEntries = [], options = {}) {
25996
26037
  ${canvasModuleEntries}
25997
26038
  ];
25998
26039
  ` : "";
25999
- const directCanvasSelection = hasReactCanvasEntries ? ` const directUrlParams = new URLSearchParams(directUrlSearch);
26040
+ const directCanvasSelection = hasReactCanvasEntries && options.startFramework === "vite" ? " const activeCanvasModules = canvasModules;\n" : hasReactCanvasEntries ? ` const directUrlParams = new URLSearchParams(directUrlSearch);
26000
26041
  const requestedCanvasPath =
26001
26042
  directUrlParams.get("canvasPath")?.trim() ??
26002
26043
  directUrlParams.get("pageFile")?.trim();
@@ -26016,7 +26057,7 @@ tempoHostGlobals.${CANVAS_HOST_WRAPPER_GENERATION_KEY} =
26016
26057
  (tempoHostGlobals.${CANVAS_HOST_WRAPPER_GENERATION_KEY} ?? 0) + 1;
26017
26058
  `;
26018
26059
  return `/* THIS FILE IS AUTO-GENERATED. DO NOT EDIT. */
26019
- import { useSyncExternalStore } from "react";
26060
+ ${options.startFramework === "vite" ? "// tempo-incremental-canvas-registration\n" : ""}import { useSyncExternalStore } from "react";
26020
26061
  import { CanvasHost } from "tempo-sdk/canvas";
26021
26062
  ${recoveryImport}
26022
26063
  ${generationStamp}${canvasModulesDeclaration}
@@ -26041,7 +26082,7 @@ export default function TempoHost() {
26041
26082
  void directUrlSearch;
26042
26083
  ${directCanvasSelection}
26043
26084
  return (
26044
- <CanvasHost${canvasModulesProp}>
26085
+ <CanvasHost${canvasModulesProp}${options.startFramework === "vite" ? " incremental" : ""}>
26045
26086
  </CanvasHost>
26046
26087
  );
26047
26088
  }
@@ -26050,6 +26091,12 @@ ${directCanvasSelection}
26050
26091
  function installCompletionMarkerPath(packageRoot) {
26051
26092
  return path16.join(packageRoot, "node_modules", ".tempo-install-complete");
26052
26093
  }
26094
+ function installInProgressMarkerPath(packageRoot) {
26095
+ return path16.join(packageRoot, "node_modules", ".tempo-install-in-progress");
26096
+ }
26097
+ async function installWasInterrupted(packageRoot) {
26098
+ return await pathExists2(installInProgressMarkerPath(packageRoot));
26099
+ }
26053
26100
  var INSTALL_COMPLETION_MARKER_VERSION = "tempo-install-complete-v3";
26054
26101
  var EXTERNAL_LAYOUT_INSTALL_MARKER = ".tempo-external-install-complete";
26055
26102
  async function computeInstallCompletionKey(packageRoot) {
@@ -26248,8 +26295,12 @@ function filterProtectedInstallSteps(steps) {
26248
26295
  }
26249
26296
  async function buildSafeInstallCommand(manager, cwd) {
26250
26297
  const resolved = await resolvePackageManagerCommand(manager.pm, cwd);
26251
- const installSuffix = manager.installCmd.replace(/^[^\s]+/, "");
26252
- return `${resolved}${installSuffix}`;
26298
+ const lifted = extractLeadingEnvAssignments(manager.installCmd);
26299
+ const installSuffix = lifted.command.replace(/^[^\s]+/, "");
26300
+ return manager.installCmd.replace(
26301
+ lifted.command,
26302
+ `${resolved}${installSuffix}`
26303
+ );
26253
26304
  }
26254
26305
  function parseFirstSemverMajor(value) {
26255
26306
  const match = value.match(/(\d+)(?:\.\d+)?(?:\.\d+)?/);
@@ -26577,6 +26628,9 @@ async function runInstallStep(step, signal, onOutput, projectRoot = step.cwd) {
26577
26628
  });
26578
26629
  throwIfLaunchAborted(signal);
26579
26630
  }
26631
+ await Promise.all(
26632
+ (step.markerRoots ?? []).map(writeInstallInProgressMarker)
26633
+ );
26580
26634
  try {
26581
26635
  await runInstallCommandWithLockRetry(
26582
26636
  step.command,
@@ -26593,19 +26647,20 @@ async function runInstallStep(step, signal, onOutput, projectRoot = step.cwd) {
26593
26647
  console.warn(
26594
26648
  `[tempo-devserver] ${step.label}: pnpm skipped dependency build scripts (ERR_PNPM_IGNORED_BUILDS) but node_modules is populated \u2014 treating as a successful install.`
26595
26649
  );
26596
- await Promise.all(
26597
- (step.markerRoots ?? []).map(writeInstallCompletionMarker)
26598
- );
26599
- await Promise.all(
26600
- (step.externalLayoutMarkerRoots ?? []).map(
26601
- writeExternalLayoutInstallMarker
26602
- )
26603
- );
26650
+ await recordInstallStepCompleted(step);
26604
26651
  return;
26605
26652
  }
26606
26653
  throw classifyInstallFailure(step.command, output) ?? error;
26607
26654
  }
26608
26655
  throwIfLaunchAborted(signal);
26656
+ await recordInstallStepCompleted(step);
26657
+ }
26658
+ async function recordInstallStepCompleted(step) {
26659
+ await Promise.all(
26660
+ (step.markerRoots ?? []).map(
26661
+ (root) => rm5(installInProgressMarkerPath(root), { force: true })
26662
+ )
26663
+ );
26609
26664
  await Promise.all((step.markerRoots ?? []).map(writeInstallCompletionMarker));
26610
26665
  await Promise.all(
26611
26666
  (step.externalLayoutMarkerRoots ?? []).map(
@@ -26613,6 +26668,17 @@ async function runInstallStep(step, signal, onOutput, projectRoot = step.cwd) {
26613
26668
  )
26614
26669
  );
26615
26670
  }
26671
+ async function writeInstallInProgressMarker(packageRoot) {
26672
+ try {
26673
+ await mkdir6(path16.join(packageRoot, "node_modules"), { recursive: true });
26674
+ await writeFile6(installInProgressMarkerPath(packageRoot), "", "utf-8");
26675
+ } catch (error) {
26676
+ console.warn(
26677
+ `[tempo-devserver] failed to write install-in-progress marker at ${packageRoot}:`,
26678
+ error
26679
+ );
26680
+ }
26681
+ }
26616
26682
  async function sleepForStatusRetry(delayMs) {
26617
26683
  await new Promise((resolve2) => setTimeout(resolve2, delayMs));
26618
26684
  }
@@ -28421,13 +28487,13 @@ function readStoryboard(element, component, imports, legacyObjects, source, glob
28421
28487
  if (component === "HtmlStoryboard") {
28422
28488
  const filepath = readStringAttribute(openingElement, "filepath");
28423
28489
  const src = readStringAttribute(openingElement, "src");
28424
- storyboard.filepath = filepath?.value;
28490
+ storyboard.filepath = filepath?.value.trim() ? filepath.value : void 0;
28425
28491
  storyboard.filepathSpan = filepath?.valueSpan;
28426
28492
  storyboard.filepathAttributeSpan = filepath?.attributeSpan;
28427
28493
  storyboard.src = src?.value;
28428
28494
  storyboard.srcSpan = src?.valueSpan;
28429
28495
  storyboard.srcAttributeSpan = src?.attributeSpan;
28430
- if (!filepath && !src) {
28496
+ if (!filepath?.value.trim() && !src?.value.trim()) {
28431
28497
  errors.push({
28432
28498
  message: '<HtmlStoryboard> must include a literal "filepath" or "src" prop',
28433
28499
  span: toSpan(openingElement),
@@ -30176,12 +30242,12 @@ export {
30176
30242
  FROZEN_EPOCH_MS,
30177
30243
  buildFreezeInitScript,
30178
30244
  FREEZE_MOTION_CSS,
30179
- CHROMIUM_LAUNCH_ARGS,
30180
- settlePage,
30181
30245
  KIT_VERSION,
30182
30246
  FONTS_BUNDLE_VERSION,
30183
30247
  DEVICE_SCALE_FACTOR,
30184
30248
  buildEnvFingerprint,
30249
+ CHROMIUM_LAUNCH_ARGS,
30250
+ settlePage,
30185
30251
  storyboardRenderId,
30186
30252
  sha256Hex,
30187
30253
  looksBlank,
@@ -30199,4 +30265,4 @@ export {
30199
30265
  buildBaselineCaptureArgs,
30200
30266
  main
30201
30267
  };
30202
- //# sourceMappingURL=chunk-CYU7WCUJ.js.map
30268
+ //# sourceMappingURL=chunk-ZBXLSXRH.js.map