@tempo-ai/visual-review 0.0.113-staging.2 → 0.0.113-staging.4

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
@@ -6,7 +6,7 @@ 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-32JWCJ2S.js")).main(commandArgs);
9
+ return await (await import("./cli-OT7EUYFD.js")).main(commandArgs);
10
10
  case "claim":
11
11
  return await (await import("./claim-J654JAS6.js")).main(commandArgs);
12
12
  case "report":
@@ -134,6 +134,7 @@ var SETTLE_STEPS = [
134
134
  "content-quiet",
135
135
  "paint"
136
136
  ];
137
+ var VIDEO_FRAME_WAIT_CAP_MS = 2e3;
137
138
  var DEFAULT_SETTLE_QUIET_MS = 300;
138
139
  var DEFAULT_SETTLE_STEP_TIMEOUT_MS = 15e3;
139
140
  var DEFAULT_SETTLE_POLL_MS = 50;
@@ -209,22 +210,48 @@ function buildSettleWaitScript(options) {
209
210
 
210
211
  var quietKey = null;
211
212
  var quietSince = 0;
213
+ // When a rendered video was first seen without a decoded frame, and how
214
+ // long that may hold the quiet window before we give up on it.
215
+ var videoWaitStartedAt = null;
216
+ var videoWaitCapMs = ${VIDEO_FRAME_WAIT_CAP_MS};
212
217
  // Images that take part in the capture: laid out and not hidden. A
213
218
  // hidden responsive variant never starts native lazy loading and must
214
219
  // not hold the window; a rendered lazy image never starts either,
215
220
  // because a capture does not scroll, so it is asked for eagerly.
216
- function renderedImages(target) {
217
- var all = Array.prototype.slice.call(target.querySelectorAll('img'));
221
+ function renderedMatches(target, tag) {
222
+ var all = Array.prototype.slice.call(target.querySelectorAll(tag));
218
223
  var rendered = [];
219
224
  for (var i = 0; i < all.length; i++) {
220
- var img = all[i];
221
- if (img.getClientRects().length === 0) continue;
222
- var visibility = getComputedStyle(img).visibility;
225
+ var el = all[i];
226
+ if (el.getClientRects().length === 0) continue;
227
+ var visibility = getComputedStyle(el).visibility;
223
228
  if (visibility === 'hidden' || visibility === 'collapse') continue;
224
- rendered.push(img);
229
+ rendered.push(el);
225
230
  }
226
231
  return rendered;
227
232
  }
233
+ function renderedImages(target) {
234
+ return renderedMatches(target, 'img');
235
+ }
236
+ // A video paints its current frame only once that frame is DECODED.
237
+ // HAVE_CURRENT_DATA (2) is exactly that bar.
238
+ //
239
+ // Everything else here is about FAILING OPEN. The baseline lane runs
240
+ // this step strict, so a video that can never reach that bar would turn
241
+ // a capture that used to succeed into a settle.content-quiet timeout \u2014
242
+ // for every storyboard in the repo, not just ones we had in mind. So a
243
+ // video only holds the window while it is plausibly ON ITS WAY to a
244
+ // frame: an errored element, one with no source at all, and one the page
245
+ // explicitly opted out of loading (preload="none", whose poster-or-
246
+ // nothing paint is already deterministic) are all treated as finished.
247
+ function videoFrameReady(video) {
248
+ if (video.error) return true;
249
+ if (video.preload === 'none') return true;
250
+ var networkState = video.networkState;
251
+ if (networkState === 0 /* NETWORK_EMPTY */) return true;
252
+ if (networkState === 3 /* NETWORK_NO_SOURCE */) return true;
253
+ return video.readyState >= 2 /* HAVE_CURRENT_DATA */;
254
+ }
228
255
  function contentQuiet() {
229
256
  var target = document.querySelector(selector);
230
257
  if (!target) return false;
@@ -240,6 +267,23 @@ function buildSettleWaitScript(options) {
240
267
  return false;
241
268
  }
242
269
  }
270
+ // Second line of defence behind videoFrameReady's fail-open rules: a
271
+ // video that is legitimately loading can still stall forever (a dev
272
+ // server that never answers, a huge asset). Waiting is an OPTIMISATION
273
+ // for baseline stability, never a reason to fail a capture, so the
274
+ // whole video wait is bounded \u2014 past the cap we capture whatever is
275
+ // on screen, exactly as we did before videos were considered at all.
276
+ var videos = renderedMatches(target, 'video');
277
+ for (var w = 0; w < videos.length; w++) {
278
+ if (!videoFrameReady(videos[w])) {
279
+ if (videoWaitStartedAt === null) videoWaitStartedAt = now();
280
+ if (now() - videoWaitStartedAt < videoWaitCapMs) {
281
+ quietKey = null;
282
+ return false;
283
+ }
284
+ break;
285
+ }
286
+ }
243
287
  var rect = target.getBoundingClientRect();
244
288
  var text = target.innerText || '';
245
289
  var key = [
@@ -4202,6 +4246,16 @@ import os from "os";
4202
4246
  import path6 from "path";
4203
4247
  var PRE_BOOT_CLOCK_SLACK_MS = 5 * 60 * 1e3;
4204
4248
 
4249
+ // ../tempo-devserver/install-environment-fallback.ts
4250
+ function missingInstallEnvironment(error) {
4251
+ const message = error instanceof Error ? error.message : String(error);
4252
+ return /(?:^|[\s/"'])(?:docker|pnpm|yarn|bun)(?:["'])?: (?:command )?not found\b/im.test(
4253
+ message
4254
+ ) || /['"](?:docker|pnpm|yarn|bun)['"] is not recognized as an internal or external command/i.test(
4255
+ message
4256
+ ) || /cannot connect to the docker daemon/i.test(message) || /is the docker daemon running\?/i.test(message);
4257
+ }
4258
+
4205
4259
  // ../tempo-devserver/tempo-devserver.ts
4206
4260
  import { spawn as spawn5 } from "child_process";
4207
4261
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
@@ -4825,7 +4879,7 @@ async function parcelEventToFileEvent(event, rootInfo) {
4825
4879
  // ../tempo-sdk/package.json
4826
4880
  var package_default = {
4827
4881
  name: "tempo-sdk",
4828
- version: "0.0.45",
4882
+ version: "0.0.48",
4829
4883
  type: "module",
4830
4884
  description: "Tempo SDK \u2014 Vite, Next.js, and Expo plugins for JSX annotation and shared page/storyboard types",
4831
4885
  repository: {
@@ -4906,7 +4960,7 @@ var package_default = {
4906
4960
  demo: "TEMPO=true pnpm --filter @tempo-modules/vite-annotate-plugin-demo dev",
4907
4961
  "demo:nextjs": "TEMPO=true pnpm --filter @tempo-modules/tempo-sdk-nextjs-demo dev",
4908
4962
  test: "vitest run",
4909
- "test:browser": "vitest run canvas-capture-determinism-browser.test.ts canvas-host-vite-e2e.test.ts canvas-host-packed-sdk-e2e.test.ts error-tracking-e2e.test.ts",
4963
+ "test:browser": "vitest run 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",
4910
4964
  "test:watch": "vitest",
4911
4965
  "test:nextjs:unit": "vitest run nextjs-loader.test.ts nextjs-wrapper.test.ts nextjs-fixtures.test.ts nextjs-published.test.ts",
4912
4966
  "test:nextjs:integration": "vitest run nextjs-e2e-correctness.test.ts",
@@ -7301,6 +7355,7 @@ async function walkPagesDir(directory, collector) {
7301
7355
  var CanvasRegistry = class {
7302
7356
  constructor(projectRoot, tempoRoot, _pagesDir, options) {
7303
7357
  this.pendingRequestedCanvasPaths = null;
7358
+ this.requestedThisSession = /* @__PURE__ */ new Set();
7304
7359
  this.requestedCanvasesQueue = Promise.resolve();
7305
7360
  this.projectRoot = projectRoot;
7306
7361
  this.tempoRoot = tempoRoot;
@@ -7338,9 +7393,31 @@ var CanvasRegistry = class {
7338
7393
  async resetRequestedCanvases() {
7339
7394
  await this.runRequestedCanvasesExclusive(async () => {
7340
7395
  this.pendingRequestedCanvasPaths = null;
7396
+ this.requestedThisSession.clear();
7341
7397
  await rm3(this.requestedCanvasesPath, { force: true });
7342
7398
  });
7343
7399
  }
7400
+ /**
7401
+ * Drop demand registrations inherited from an older host session without
7402
+ * erasing canvases already requested through this registry instance.
7403
+ *
7404
+ * Canvas admission may run while first-time initialization is queued. The
7405
+ * registry queue makes both orderings safe: a reset that wins clears the old
7406
+ * file before admission writes it, while a reset that follows admission
7407
+ * observes the current-session request and preserves the file.
7408
+ */
7409
+ async resetStaleRequestedCanvases() {
7410
+ await this.runRequestedCanvasesExclusive(async () => {
7411
+ const requestedPaths = [...this.requestedThisSession];
7412
+ this.pendingRequestedCanvasPaths = requestedPaths;
7413
+ if (requestedPaths.length === 0) {
7414
+ await rm3(this.requestedCanvasesPath, { force: true });
7415
+ } else {
7416
+ await this.writeRequestedCanvasPaths(requestedPaths);
7417
+ }
7418
+ this.pendingRequestedCanvasPaths = null;
7419
+ });
7420
+ }
7344
7421
  requestedPathToAbsolutePath(relativePath) {
7345
7422
  if (typeof relativePath !== "string" || relativePath.length === 0) {
7346
7423
  return null;
@@ -7465,6 +7542,7 @@ var CanvasRegistry = class {
7465
7542
  }
7466
7543
  return this.runRequestedCanvasesExclusive(async () => {
7467
7544
  const requestedPath = this.toRequestedPath(absolutePath);
7545
+ this.requestedThisSession.add(requestedPath);
7468
7546
  const pruned = await this.pruneDeletedRequestedPaths(
7469
7547
  await this.readRequestedCanvasPaths()
7470
7548
  );
@@ -9523,9 +9601,24 @@ var TempoDevServer = class _TempoDevServer {
9523
9601
  }
9524
9602
  this.state.dependenciesInstalled = false;
9525
9603
  await this.persistStatus();
9526
- for (const step of steps) {
9604
+ try {
9605
+ for (const step of steps) {
9606
+ throwIfLaunchAborted(signal);
9607
+ await runInstallStep(step, signal);
9608
+ }
9609
+ } catch (error) {
9527
9610
  throwIfLaunchAborted(signal);
9528
- await runInstallStep(step, signal);
9611
+ if (!steps.some((step) => step.label === "custom tempo install") || !missingInstallEnvironment(error))
9612
+ throw error;
9613
+ const localSteps = await this.buildInstallSteps(false, false);
9614
+ if (!localSteps.length) throw error;
9615
+ console.warn(
9616
+ "[tempo-devserver] The configured installer needs unavailable local tooling; trying a local dependency install for this launch."
9617
+ );
9618
+ for (const step of localSteps) {
9619
+ throwIfLaunchAborted(signal);
9620
+ await runInstallStep(step, signal);
9621
+ }
9529
9622
  }
9530
9623
  };
9531
9624
  while (_TempoDevServer.installBarriers.has(this.projectRoot)) {
@@ -9555,9 +9648,9 @@ var TempoDevServer = class _TempoDevServer {
9555
9648
  throw error;
9556
9649
  }
9557
9650
  }
9558
- async buildInstallSteps(forceInstall) {
9651
+ async buildInstallSteps(forceInstall, useConfiguredScript = true) {
9559
9652
  const configuredInstall = this.config.scripts.install.trim();
9560
- if (!looksLikeGeneratedInstallScript(configuredInstall)) {
9653
+ if (useConfiguredScript && !looksLikeGeneratedInstallScript(configuredInstall)) {
9561
9654
  if (!forceInstall && await this.projectRootInstallReady() && await installCompletedForCurrentDeps(this.tempoRoot) && await this.tempoInstallReady()) {
9562
9655
  devLog("supervisor:custom_install_skipped_premat", {
9563
9656
  projectRoot: this.projectRoot,
@@ -9617,7 +9710,7 @@ var TempoDevServer = class _TempoDevServer {
9617
9710
  markerRoots: [this.tempoRoot]
9618
9711
  });
9619
9712
  }
9620
- if (!rootManager && !tempoManager) {
9713
+ if (useConfiguredScript && !rootManager && !tempoManager) {
9621
9714
  steps.push({
9622
9715
  cwd: this.commandCwd,
9623
9716
  command: configuredInstall,
@@ -10211,7 +10304,7 @@ var TempoDevServer = class _TempoDevServer {
10211
10304
  const firstInitialize = !this.state.initialized;
10212
10305
  const hostAlive = this.activeHostHasLivenessAuthority();
10213
10306
  if (firstInitialize && !hostAlive) {
10214
- await this.registry.resetRequestedCanvases();
10307
+ await this.registry.resetStaleRequestedCanvases();
10215
10308
  }
10216
10309
  const reconcileResult = await this.enqueueReconcile(
10217
10310
  "initialize",
@@ -13974,4 +14067,4 @@ export {
13974
14067
  buildBaselineCaptureArgs,
13975
14068
  main
13976
14069
  };
13977
- //# sourceMappingURL=chunk-AX52T3IT.js.map
14070
+ //# sourceMappingURL=chunk-W3EZWPOB.js.map