@hyperframes/producer 0.8.225 → 0.8.226

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/index.js CHANGED
@@ -1,9 +1,8 @@
1
1
  // src/services/renderOrchestrator.ts
2
2
  import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
3
3
  import {
4
- existsSync as existsSync16,
4
+ existsSync as existsSync17,
5
5
  mkdirSync as mkdirSync10,
6
- readFileSync as readFileSync9,
7
6
  readdirSync as readdirSync5,
8
7
  rmSync as rmSync10,
9
8
  statSync as statSync8,
@@ -24,6 +23,7 @@ import {
24
23
  import { HF_AUDIO_GROUP_TAG } from "@hyperframes/core/audio-groups";
25
24
  import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
26
25
  import { HTML_BODY_CSS_HEIGHT_FIRST_RE, HTML_BODY_CSS_WIDTH_FIRST_RE } from "@hyperframes/parsers";
26
+ import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
27
27
  import {
28
28
  extractMediaMetadata as extractMediaMetadata4,
29
29
  resolveConfig as resolveConfig2,
@@ -2134,6 +2134,14 @@ function pushWorkerDedupPerfs(results, sink) {
2134
2134
  if (r.perf) sink.push(r.perf);
2135
2135
  }
2136
2136
  }
2137
+ function capturedBeyondViewport(perfs, fallback) {
2138
+ const used = perfs.flatMap((perf) => perf.captureBeyondViewport ?? []);
2139
+ return used.length > 0 ? used.includes(true) : fallback;
2140
+ }
2141
+ function withCapturedBeyondViewport(capture, perfs) {
2142
+ const used = capturedBeyondViewport(perfs, capture.captureBeyondViewport);
2143
+ return used === void 0 ? capture : { ...capture, captureBeyondViewport: used };
2144
+ }
2137
2145
  function pushWorkerPageErrors(results, sink) {
2138
2146
  for (const result of results) {
2139
2147
  for (const line of result.pageErrorLines ?? []) if (!sink.includes(line)) sink.push(line);
@@ -5766,7 +5774,7 @@ import { suggestMatchingPreset } from "@hyperframes/core";
5766
5774
  // src/services/htmlCompiler.ts
5767
5775
  import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
5768
5776
  import { createHash as createHash6 } from "node:crypto";
5769
- import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync7 } from "fs";
5777
+ import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
5770
5778
  import { join as join15, dirname as dirname7, resolve as resolve7, basename as basename4, relative as relative2 } from "path";
5771
5779
  import { parseHTML as parseHTML5 } from "linkedom";
5772
5780
  import {
@@ -5819,7 +5827,8 @@ import {
5819
5827
  } from "@hyperframes/parsers/sub-composition-validity";
5820
5828
  import {
5821
5829
  isUnresolvedAssetPlaceholder,
5822
- readProjectFile
5830
+ readProjectFile,
5831
+ readTextFile
5823
5832
  } from "@hyperframes/parsers/asset-resolution";
5824
5833
 
5825
5834
  // src/services/renderMediaCollector.ts
@@ -7908,11 +7917,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
7908
7917
  return document2.toString();
7909
7918
  }
7910
7919
  async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
7911
- const entryHtml = rebaseDirectEntryAssetPaths(
7912
- readFileSync7(htmlPath, "utf-8"),
7913
- projectDir,
7914
- htmlPath
7915
- );
7920
+ const entryHtml = rebaseDirectEntryAssetPaths(readTextFile(htmlPath), projectDir, htmlPath);
7916
7921
  const rawHtml = rewriteUnresolvableGsapToCdn(entryHtml, projectDir);
7917
7922
  assertSubCompositionsUsable(rawHtml, projectDir);
7918
7923
  const probes = /* @__PURE__ */ new Map();
@@ -8763,7 +8768,7 @@ async function runProbeStage(input) {
8763
8768
  );
8764
8769
  const hasVariableMedia = hasVariableBoundMedia(compiled.html, job.config.variables);
8765
8770
  const hasInsertedMedia = hasRuntimeMediaChanges(compiled.html);
8766
- const needsBrowser = probeRequiresBrowser({
8771
+ const needsBrowser = cfg.videoLayerPassthrough || probeRequiresBrowser({
8767
8772
  durationSeconds: composition.duration,
8768
8773
  unresolvedCompositionCount: compiled.unresolvedCompositions.length,
8769
8774
  hasAutoStart,
@@ -9395,17 +9400,23 @@ function resolveVideoExtractionPolicy(env = process.env) {
9395
9400
  const maxTransientRetries = failureMode !== "off" && env.HF_VIDEO_EXTRACTION_MAX_RETRIES?.trim() === "1" ? 1 : 0;
9396
9401
  return { failureMode, maxTransientRetries };
9397
9402
  }
9403
+ var MAX_NAMED_SOURCES = 5;
9398
9404
  var VideoExtractionStageError = class extends Error {
9399
9405
  constructor(code, retryable, failures, extractionFailure = buildExtractionFailureMetadata(
9400
9406
  failures.map((failure) => ({
9401
9407
  kind: failure.kind,
9402
9408
  affectedElementCount: failure.count
9403
9409
  }))
9404
- ), ffmpegReason) {
9410
+ ), ffmpegReason, sources = []) {
9405
9411
  const total = failures.reduce((sum2, failure) => sum2 + failure.count, 0);
9406
9412
  const breakdown = failures.map((failure) => `${failure.kind}=${failure.count}`).join(",");
9407
9413
  const reason = ffmpegReason ? `: ffmpeg said: ${ffmpegReason}` : "";
9408
- super(`Video extraction failed for ${total} source(s) [${code}; ${breakdown}]${reason}`);
9414
+ const shown = sources.slice(0, MAX_NAMED_SOURCES).join(", ");
9415
+ const more = sources.length > MAX_NAMED_SOURCES ? ` and ${sources.length - MAX_NAMED_SOURCES} more` : "";
9416
+ const files = sources.length > 0 ? ` (${shown}${more})` : "";
9417
+ super(
9418
+ `Video extraction failed for ${total} source(s) [${code}; ${breakdown}]${files}${reason}`
9419
+ );
9409
9420
  this.code = code;
9410
9421
  this.retryable = retryable;
9411
9422
  this.failures = failures;
@@ -9453,7 +9464,10 @@ function buildVideoExtractionStageError(result) {
9453
9464
  retryable,
9454
9465
  failures,
9455
9466
  extractionFailureMetadataFromResult(result),
9456
- result.errors.find((failure) => failure.ffmpegReason)?.ffmpegReason
9467
+ result.errors.find((failure) => failure.ffmpegReason)?.ffmpegReason,
9468
+ [
9469
+ ...new Set(result.errors.flatMap((failure) => failure.source ? [failure.source] : []))
9470
+ ].sort()
9457
9471
  );
9458
9472
  }
9459
9473
  function buildHdrProbeStageError(failures) {
@@ -9532,7 +9546,8 @@ async function runExtractVideosStage(input) {
9532
9546
  assertNotAborted,
9533
9547
  materializeSymlinks,
9534
9548
  deferRangeExtraction,
9535
- urlByAbsentLocalPath
9549
+ urlByAbsentLocalPath,
9550
+ skipFrameExtractionIds = []
9536
9551
  } = input;
9537
9552
  const stage2Start = Date.now();
9538
9553
  const extractionPolicy = resolveVideoExtractionPolicy();
@@ -9603,7 +9618,10 @@ async function runExtractVideosStage(input) {
9603
9618
  );
9604
9619
  imageColorSpaces.push(...probed);
9605
9620
  }
9606
- if (composition.videos.length > 0) {
9621
+ if (skipFrameExtractionIds.length > 0) {
9622
+ await appendVideoAudioFromProbe({ projectDir, compiledDir, composition, urlByAbsentLocalPath });
9623
+ videoReadinessSkipIds = [...skipFrameExtractionIds];
9624
+ } else if (composition.videos.length > 0) {
9607
9625
  const totalVideos = composition.videos.length;
9608
9626
  for (let i = 0; i < totalVideos; i++) {
9609
9627
  const v = composition.videos[i];
@@ -9699,6 +9717,116 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
9699
9717
  existingAudioSrcs.add(video.src);
9700
9718
  }
9701
9719
  }
9720
+ async function appendVideoAudioFromProbe(input) {
9721
+ const { projectDir, compiledDir, composition, urlByAbsentLocalPath } = input;
9722
+ const videos = composition.videos.filter(
9723
+ (video) => !isVideoPastTimelineEnd(video, composition.duration)
9724
+ );
9725
+ const probed = await Promise.all(
9726
+ videos.map(async (video) => {
9727
+ const path = isHttpUrl2(video.src) ? video.src : resolveProjectRelativeSrc3(video.src, projectDir, compiledDir);
9728
+ const readFrom = urlByAbsentLocalPath?.get(path) ?? path;
9729
+ if (!isHttpUrl2(readFrom) && !existsSync11(readFrom)) return null;
9730
+ const metadata = await extractMediaMetadata3(readFrom);
9731
+ const window2 = await resolveFinalFrameExtractionWindow2(
9732
+ readFrom,
9733
+ video,
9734
+ metadata,
9735
+ resolveVideoExtractionWindow2(video, metadata, composition.duration)
9736
+ );
9737
+ if (window2.durationSeconds <= 0) return null;
9738
+ rebaseVideoToWindow(video, window2);
9739
+ return { videoId: video.id, metadata };
9740
+ })
9741
+ );
9742
+ appendAutoDetectedVideoAudio(
9743
+ composition,
9744
+ probed.filter((entry) => entry !== null)
9745
+ );
9746
+ }
9747
+
9748
+ // src/services/render/videoLayerPassthroughPlan.ts
9749
+ import { existsSync as existsSync12 } from "node:fs";
9750
+ import {
9751
+ buildVideoLayerInput,
9752
+ isHttpUrl as isHttpUrl3,
9753
+ qualifyVideoLayer,
9754
+ resolveProjectRelativeSrc as resolveProjectRelativeSrc4
9755
+ } from "@hyperframes/engine";
9756
+ var SAMPLES = 9;
9757
+ function inspectVideoLayout(id, w, h, time) {
9758
+ window.__hf?.seek?.(time);
9759
+ const video = document.getElementById(id);
9760
+ if (!video) return { reason: "video_missing" };
9761
+ const r = video.getBoundingClientRect();
9762
+ if (Math.abs(r.left) > 0.5 || Math.abs(r.top) > 0.5) return { reason: "not_full_canvas" };
9763
+ if (Math.abs(r.width - w) > 0.5 || Math.abs(r.height - h) > 0.5) {
9764
+ return { reason: "not_full_canvas" };
9765
+ }
9766
+ const painted = (s) => s.backgroundColor !== "" && s.backgroundColor !== "transparent" && s.backgroundColor !== "rgba(0, 0, 0, 0)" || s.backgroundImage !== "" && s.backgroundImage !== "none";
9767
+ for (let e = video; e && e !== document.documentElement; ) {
9768
+ const s = getComputedStyle(e);
9769
+ if (s.opacity !== "1") return { reason: "opacity" };
9770
+ if (s.transform !== "none" || s.filter !== "none") return { reason: "transform_or_filter" };
9771
+ if (s.clipPath !== "none" || s.maskImage !== "none") return { reason: "clip_or_mask" };
9772
+ if (s.mixBlendMode !== "normal") return { reason: "blend_mode" };
9773
+ if (s.visibility !== "visible" || s.display === "none") return { reason: "not_shown" };
9774
+ const cleared = e === document.body || e.hasAttribute("data-composition-id");
9775
+ if (e !== video && painted(s) && !cleared) return { reason: "ancestor_background" };
9776
+ e = e.parentElement;
9777
+ }
9778
+ for (const root of Array.from(document.querySelectorAll("[data-composition-id]"))) {
9779
+ if (root.parentElement?.closest("[data-composition-id]") && painted(getComputedStyle(root))) {
9780
+ return { reason: "subcomposition_background" };
9781
+ }
9782
+ }
9783
+ const fitStyle = getComputedStyle(video).objectFit;
9784
+ if (fitStyle !== "fill" && fitStyle !== "cover") return { reason: "object_fit" };
9785
+ const own = Number.parseInt(getComputedStyle(video).zIndex, 10) || 0;
9786
+ for (const other of Array.from(document.body.querySelectorAll("*"))) {
9787
+ if (other === video || other.contains(video) || video.contains(other)) continue;
9788
+ if (other.tagName === "SCRIPT" || other.tagName === "STYLE") continue;
9789
+ const z = Number.parseInt(getComputedStyle(other).zIndex, 10) || 0;
9790
+ const before = video.compareDocumentPosition(other) & Node.DOCUMENT_POSITION_PRECEDING;
9791
+ if (z < own || z === own && before) {
9792
+ const box2 = other.getBoundingClientRect();
9793
+ if (box2.width > 0 && box2.height > 0) return { reason: "paints_under" };
9794
+ }
9795
+ }
9796
+ return { fit: fitStyle === "cover" ? "cover" : "fill" };
9797
+ }
9798
+ async function probeVideoLayout(page, videoId, canvas, duration) {
9799
+ let fit = "fill";
9800
+ for (let i = 0; i < SAMPLES; i++) {
9801
+ const t = duration * i / SAMPLES;
9802
+ const seen = await page.evaluate(inspectVideoLayout, videoId, canvas.width, canvas.height, t);
9803
+ if ("reason" in seen) return seen;
9804
+ if (seen.fit === "cover") fit = "cover";
9805
+ }
9806
+ await page.evaluate(
9807
+ () => window.__hf?.seek?.(0)
9808
+ );
9809
+ return { fit };
9810
+ }
9811
+ async function planVideoLayerPassthrough(input) {
9812
+ const q = qualifyVideoLayer(input.videos, input.duration);
9813
+ if (!q.qualifies) return { reason: q.reason };
9814
+ const { video } = q;
9815
+ if (isHttpUrl3(video.src)) return { reason: "remote_source" };
9816
+ const path = resolveProjectRelativeSrc4(video.src, input.projectDir, input.compiledDir);
9817
+ if (!existsSync12(path)) return { reason: "source_missing" };
9818
+ const layout = await probeVideoLayout(input.page, video.id, input.canvas, input.duration);
9819
+ if ("reason" in layout) return layout;
9820
+ const layer = await buildVideoLayerInput(
9821
+ path,
9822
+ video.mediaStart,
9823
+ input.duration,
9824
+ input.fps,
9825
+ input.canvas,
9826
+ layout.fit
9827
+ );
9828
+ return layer ? { videoId: video.id, layer } : { reason: "source_unsupported" };
9829
+ }
9702
9830
 
9703
9831
  // src/services/render/stages/audioStage.ts
9704
9832
  import { join as join20 } from "node:path";
@@ -10310,7 +10438,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
10310
10438
  }
10311
10439
 
10312
10440
  // src/services/render/stages/captureStage.ts
10313
- import { existsSync as existsSync12, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
10441
+ import { existsSync as existsSync13, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
10314
10442
  import { join as join21 } from "node:path";
10315
10443
  import {
10316
10444
  classifyCaptureFailure as classifyCaptureFailure2,
@@ -10381,7 +10509,7 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
10381
10509
  }
10382
10510
  function measureCaptureFrameBytes(workDir, framesDir) {
10383
10511
  let total = sampleDirectoryBytes(framesDir);
10384
- if (!existsSync12(workDir)) return total;
10512
+ if (!existsSync13(workDir)) return total;
10385
10513
  for (const attempt of readdirSync3(workDir)) {
10386
10514
  if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
10387
10515
  total += sampleDirectoryBytes(join21(workDir, attempt));
@@ -10684,7 +10812,7 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
10684
10812
  }
10685
10813
 
10686
10814
  // src/services/render/stages/captureHdrStage.ts
10687
- import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "node:fs";
10815
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8 } from "node:fs";
10688
10816
  import { join as join25 } from "node:path";
10689
10817
  import {
10690
10818
  closeCaptureSession as closeCaptureSession7,
@@ -10930,7 +11058,7 @@ import { Worker } from "node:worker_threads";
10930
11058
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
10931
11059
  import { dirname as dirname9, join as join23 } from "node:path";
10932
11060
  import { createRequire } from "node:module";
10933
- import { existsSync as existsSync13 } from "node:fs";
11061
+ import { existsSync as existsSync14 } from "node:fs";
10934
11062
  import { cpus as cpus2 } from "node:os";
10935
11063
  function resolveWorkerEntry(explicit) {
10936
11064
  if (explicit && explicit.length > 0) {
@@ -10943,7 +11071,7 @@ function resolveWorkerEntry(explicit) {
10943
11071
  }
10944
11072
  const moduleDir = dirname9(fileURLToPath3(import.meta.url));
10945
11073
  const jsPath = join23(moduleDir, "shaderTransitionWorker.js");
10946
- if (existsSync13(jsPath)) return { path: jsPath, isTs: false };
11074
+ if (existsSync14(jsPath)) return { path: jsPath, isTs: false };
10947
11075
  const tsPath = join23(moduleDir, "shaderTransitionWorker.ts");
10948
11076
  return { path: tsPath, isTs: true };
10949
11077
  }
@@ -11504,7 +11632,7 @@ async function runCaptureHdrStage(input) {
11504
11632
  }
11505
11633
  const debugDumpEnabled = process.env.KEEP_TEMP === "1";
11506
11634
  const debugDumpDir = debugDumpEnabled ? join25(framesDir, "debug-composite") : null;
11507
- if (debugDumpDir && !existsSync14(debugDumpDir)) {
11635
+ if (debugDumpDir && !existsSync15(debugDumpDir)) {
11508
11636
  mkdirSync8(debugDumpDir, { recursive: true });
11509
11637
  }
11510
11638
  const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
@@ -11661,10 +11789,10 @@ async function runCaptureHdrStage(input) {
11661
11789
  // src/services/render/stages/encodeStage.ts
11662
11790
  import {
11663
11791
  copyFileSync as copyFileSync4,
11664
- existsSync as existsSync15,
11792
+ existsSync as existsSync16,
11665
11793
  mkdirSync as mkdirSync9,
11666
11794
  readdirSync as readdirSync4,
11667
- readFileSync as readFileSync8,
11795
+ readFileSync as readFileSync7,
11668
11796
  rmSync as rmSync9,
11669
11797
  statSync as statSync7,
11670
11798
  writeFileSync as writeFileSync5
@@ -11758,7 +11886,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
11758
11886
  const error = formatFfmpegError(result.exitCode, result.stderr);
11759
11887
  return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
11760
11888
  }
11761
- const gif = readFileSync8(outputPath);
11889
+ const gif = readFileSync7(outputPath);
11762
11890
  if (!clearGifFramesBeforeNext(gif)) {
11763
11891
  const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
11764
11892
  return gifEncodeFailure(startTime, outputPath, error);
@@ -11791,11 +11919,11 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
11791
11919
  return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
11792
11920
  }
11793
11921
  }
11794
- if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync8(outputPath)) !== false) {
11922
+ if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync7(outputPath)) !== false) {
11795
11923
  const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
11796
11924
  if (failure) return failure;
11797
11925
  }
11798
- const fileSize = existsSync15(outputPath) ? statSync7(outputPath).size : 0;
11926
+ const fileSize = existsSync16(outputPath) ? statSync7(outputPath).size : 0;
11799
11927
  return {
11800
11928
  success: true,
11801
11929
  outputPath,
@@ -11956,7 +12084,7 @@ async function runEncodeStage(input) {
11956
12084
  const stage5Start = Date.now();
11957
12085
  if (isPngSequence) {
11958
12086
  const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
11959
- if (!existsSync15(outputPath)) mkdirSync9(outputPath, { recursive: true });
12087
+ if (!existsSync16(outputPath)) mkdirSync9(outputPath, { recursive: true });
11960
12088
  const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
11961
12089
  if (captured.length === 0) {
11962
12090
  throw new Error(
@@ -11967,7 +12095,7 @@ async function runEncodeStage(input) {
11967
12095
  const dst = join26(outputPath, formatExportFrameName(i, "png"));
11968
12096
  copyFileSync4(join26(framesDir, name), dst);
11969
12097
  });
11970
- if (hasAudio && audioOutputPath && existsSync15(audioOutputPath)) {
12098
+ if (hasAudio && audioOutputPath && existsSync16(audioOutputPath)) {
11971
12099
  copyFileSync4(audioOutputPath, join26(outputPath, MIXED_AUDIO_FILENAME2));
11972
12100
  log.info(
11973
12101
  `[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
@@ -12348,7 +12476,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
12348
12476
  let rangeStart = null;
12349
12477
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
12350
12478
  const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12351
- const missing = !existsSync16(framePath) || statSync8(framePath).size <= 8;
12479
+ const missing = !existsSync17(framePath) || statSync8(framePath).size <= 8;
12352
12480
  if (missing && rangeStart === null) {
12353
12481
  rangeStart = frameIndex;
12354
12482
  } else if (!missing && rangeStart !== null) {
@@ -12473,7 +12601,7 @@ function countCapturedFrames(totalFrames, framesDir, frameExt) {
12473
12601
  let captured = 0;
12474
12602
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
12475
12603
  const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12476
- if (existsSync16(framePath)) captured++;
12604
+ if (existsSync17(framePath)) captured++;
12477
12605
  }
12478
12606
  return captured;
12479
12607
  }
@@ -13136,7 +13264,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
13136
13264
  validateDeliveryEncoderConfig(job.config);
13137
13265
  const debugDir = resolveRenderDebugDir();
13138
13266
  const outputDir = dirname11(outputPath);
13139
- if (!existsSync16(outputDir)) mkdirSync10(outputDir, { recursive: true });
13267
+ if (!existsSync17(outputDir)) mkdirSync10(outputDir, { recursive: true });
13140
13268
  const workDir = job.config.debug ? join27(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
13141
13269
  const pipelineStart = Date.now();
13142
13270
  const baseLog = job.config.logger ?? defaultLogger;
@@ -13277,7 +13405,7 @@ async function executeRenderPipeline(input) {
13277
13405
  job.startedAt = /* @__PURE__ */ new Date();
13278
13406
  assertNotAborted();
13279
13407
  assertConfiguredFfmpegBinariesExist();
13280
- if (!existsSync16(workDir)) mkdirSync10(workDir, { recursive: true });
13408
+ if (!existsSync17(workDir)) mkdirSync10(workDir, { recursive: true });
13281
13409
  if (job.config.debug) {
13282
13410
  log.info("[Render] Debug artifacts enabled", { workDir, logPath });
13283
13411
  }
@@ -13307,21 +13435,21 @@ async function executeRenderPipeline(input) {
13307
13435
  });
13308
13436
  const entryFile = job.config.entryFile || "index.html";
13309
13437
  let htmlPath = join27(projectDir, entryFile);
13310
- if (!existsSync16(htmlPath)) {
13438
+ if (!existsSync17(htmlPath)) {
13311
13439
  throw new Error(`Entry file not found: ${htmlPath}`);
13312
13440
  }
13313
13441
  assertNotAborted();
13314
- const rawEntry = readFileSync9(htmlPath, "utf-8");
13442
+ const rawEntry = readTextFile2(htmlPath);
13315
13443
  if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
13316
13444
  const wrapperPath = join27(workDir, "standalone-entry.html");
13317
13445
  const projectIndexPath = join27(projectDir, "index.html");
13318
- if (!existsSync16(projectIndexPath)) {
13446
+ if (!existsSync17(projectIndexPath)) {
13319
13447
  throw new Error(
13320
13448
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
13321
13449
  );
13322
13450
  }
13323
13451
  const standaloneHtml = extractStandaloneEntryFromIndex(
13324
- readFileSync9(projectIndexPath, "utf-8"),
13452
+ readTextFile2(projectIndexPath),
13325
13453
  entryFile,
13326
13454
  rawEntry
13327
13455
  );
@@ -13484,6 +13612,42 @@ async function executeRenderPipeline(input) {
13484
13612
  compositionHash,
13485
13613
  beginFrameStalled: probeResult.beginFrameStalled
13486
13614
  });
13615
+ let videoLayerPlan;
13616
+ if (cfg.videoLayerPassthrough) {
13617
+ const gates = {
13618
+ mp4: outputFormat === "mp4",
13619
+ software_encode: !job.config.useGpu,
13620
+ opaque_output: !needsAlpha,
13621
+ sdr: rendersSdrOnly(job.config, composition) || (job.config.hdrMode === void 0 || job.config.hdrMode === "auto") && composition.images.length === 0,
13622
+ streaming: shouldUseStreamingEncode(cfg, outputFormat, 1, probeResult.duration),
13623
+ probe_page: probeSession?.isInitialized === true
13624
+ };
13625
+ const failed = Object.entries(gates).find(([, ok]) => !ok)?.[0];
13626
+ if (failed || !probeSession) {
13627
+ log.info(`[Render] Video layer passthrough off: ${failed ?? "probe_page"}`);
13628
+ } else {
13629
+ videoLayerPlan = await planVideoLayerPassthrough({
13630
+ videos: composition.videos,
13631
+ duration: probeResult.duration,
13632
+ fps: job.config.fps,
13633
+ canvas: { width, height },
13634
+ page: probeSession.page,
13635
+ projectDir,
13636
+ compiledDir: join27(workDir, "compiled")
13637
+ });
13638
+ if (videoLayerPlan.videoId === void 0) {
13639
+ log.info(`[Render] Video layer passthrough off: ${videoLayerPlan.reason}`);
13640
+ } else {
13641
+ cfg.useDrawElement = false;
13642
+ cfg.forceScreenshot = true;
13643
+ captureForceScreenshot = true;
13644
+ const opened = probeSession;
13645
+ probeSession = null;
13646
+ await closeCaptureSession8(opened);
13647
+ }
13648
+ }
13649
+ }
13650
+ const videoLayer = videoLayerPlan?.videoId === void 0 ? void 0 : videoLayerPlan;
13487
13651
  updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
13488
13652
  code: "extract_video"
13489
13653
  });
@@ -13499,6 +13663,7 @@ async function executeRenderPipeline(input) {
13499
13663
  cfg,
13500
13664
  log,
13501
13665
  composition,
13666
+ skipFrameExtractionIds: videoLayer ? [videoLayer.videoId] : [],
13502
13667
  abortSignal: executionSignal,
13503
13668
  assertNotAborted,
13504
13669
  // Local staging can use links; distributed plan() alone requires real copies.
@@ -13617,7 +13782,7 @@ async function executeRenderPipeline(input) {
13617
13782
  throw new Error("File server failed to initialize before frame capture");
13618
13783
  }
13619
13784
  const framesDir = join27(workDir, "captured-frames");
13620
- if (!existsSync16(framesDir)) mkdirSync10(framesDir, { recursive: true });
13785
+ if (!existsSync17(framesDir)) mkdirSync10(framesDir, { recursive: true });
13621
13786
  updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
13622
13787
  code: "start_browsers"
13623
13788
  });
@@ -13643,8 +13808,9 @@ async function executeRenderPipeline(input) {
13643
13808
  width,
13644
13809
  height,
13645
13810
  fps: job.config.fps,
13646
- format: captureImageFormat,
13647
- transparent: needsAlpha,
13811
+ format: videoLayer ? "png" : captureImageFormat,
13812
+ transparent: needsAlpha || videoLayer !== void 0,
13813
+ ...videoLayer ? { hiddenElementIds: [videoLayer.videoId] } : {},
13648
13814
  quality: captureImageFormat === "png" ? void 0 : resolveCaptureJpegQuality(job.config.quality),
13649
13815
  variables: job.config.variables,
13650
13816
  deviceScaleFactor,
@@ -13941,6 +14107,7 @@ async function executeRenderPipeline(input) {
13941
14107
  }
13942
14108
  }
13943
14109
  );
14110
+ if (videoLayer) workerCount = 1;
13944
14111
  const preRoutingWorkerCount = workerCount;
13945
14112
  if (deParallelRouterEligible && workerCount > 1) {
13946
14113
  deParallelRouter = "routed";
@@ -14606,6 +14773,7 @@ async function executeRenderPipeline(input) {
14606
14773
  vp9CpuUsed: cfg.vp9CpuUsed,
14607
14774
  useGpu: job.config.useGpu,
14608
14775
  imageFormat: captureOptions.format || "jpeg",
14776
+ ...videoLayer ? { videoLayer: videoLayer.layer } : {},
14609
14777
  hdr: preset.hdr,
14610
14778
  // HLS only: force an IDR every `gopSize` frames so the
14611
14779
  // assemble stage's `-c copy` segmentation cuts exactly on
@@ -14936,12 +15104,12 @@ async function executeRenderPipeline(input) {
14936
15104
  })
14937
15105
  );
14938
15106
  const totalElapsed = Date.now() - pipelineStart;
14939
- const tmpPeakBytes = existsSync16(workDir) ? sampleDirectoryBytes(workDir) : 0;
15107
+ const tmpPeakBytes = existsSync17(workDir) ? sampleDirectoryBytes(workDir) : 0;
14940
15108
  recordTransientRetryObservability();
14941
15109
  observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
14942
15110
  const observabilitySummary = observability.summary({
14943
15111
  lastBrowserConsole,
14944
- capture: captureObservability,
15112
+ capture: withCapturedBeyondViewport(captureObservability, dedupPerfs),
14945
15113
  initFallback: mergeWorkerInitObservability(dedupPerfs),
14946
15114
  extraction: extractionObservability,
14947
15115
  compositionHash
@@ -15017,7 +15185,7 @@ async function executeRenderPipeline(input) {
15017
15185
  }
15018
15186
  }
15019
15187
  if (job.config.debug) {
15020
- if (!isPngSequence && !isHls && existsSync16(stagedOutputPath)) {
15188
+ if (!isPngSequence && !isHls && existsSync17(stagedOutputPath)) {
15021
15189
  const debugOutput = join27(workDir, `output${videoExt}`);
15022
15190
  copyFileSync5(stagedOutputPath, debugOutput);
15023
15191
  }
@@ -15077,7 +15245,7 @@ async function executeRenderPipeline(input) {
15077
15245
  const failedStage = job.currentStage || "pipeline";
15078
15246
  const observabilitySummary = observability.summary({
15079
15247
  lastBrowserConsole,
15080
- capture: captureObservability,
15248
+ capture: withCapturedBeyondViewport(captureObservability, dedupPerfs),
15081
15249
  initFallback: mergeWorkerInitObservability(dedupPerfs),
15082
15250
  extraction: extractionObservability,
15083
15251
  compositionHash
@@ -15123,7 +15291,7 @@ async function executeRenderPipeline(input) {
15123
15291
  }
15124
15292
 
15125
15293
  // src/services/distributed/renderSpan.ts
15126
- import { mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync7, readFileSync as readFileSync15, rmSync as rmSync16 } from "node:fs";
15294
+ import { mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync7, readFileSync as readFileSync13, rmSync as rmSync16 } from "node:fs";
15127
15295
  import { join as join40 } from "node:path";
15128
15296
  import { tmpdir as tmpdir4 } from "node:os";
15129
15297
 
@@ -15515,7 +15683,7 @@ import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframe
15515
15683
  import {
15516
15684
  copyFileSync as copyFileSync6,
15517
15685
  cpSync as cpSync2,
15518
- existsSync as existsSync21,
15686
+ existsSync as existsSync22,
15519
15687
  linkSync,
15520
15688
  mkdirSync as mkdirSync14,
15521
15689
  rmSync as rmSync12,
@@ -15546,7 +15714,7 @@ import {
15546
15714
  } from "@hyperframes/engine";
15547
15715
 
15548
15716
  // src/services/distributed/chunkFrames.ts
15549
- import { existsSync as existsSync18, readdirSync as readdirSync6 } from "node:fs";
15717
+ import { existsSync as existsSync19, readdirSync as readdirSync6 } from "node:fs";
15550
15718
  import { extname as extname4, join as join30 } from "node:path";
15551
15719
  import {
15552
15720
  compositionRequiresWebGpu as compositionRequiresWebGpu3,
@@ -15558,7 +15726,7 @@ import {
15558
15726
  // src/services/distributed/shared.ts
15559
15727
  import { execFile as execFileCallback } from "node:child_process";
15560
15728
  import { dirname as dirname13, join as join29 } from "node:path";
15561
- import { existsSync as existsSync17, readFileSync as readFileSync10 } from "node:fs";
15729
+ import { existsSync as existsSync18, readFileSync as readFileSync8 } from "node:fs";
15562
15730
  import { fileURLToPath as fileURLToPath5 } from "node:url";
15563
15731
  import { promisify as promisify2 } from "node:util";
15564
15732
  import {
@@ -15574,7 +15742,7 @@ function isPlanAudioArtifactPath(path) {
15574
15742
  function resolvePlanAudioPath(planDir) {
15575
15743
  for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
15576
15744
  const candidate = join29(planDir, name);
15577
- if (existsSync17(candidate)) return candidate;
15745
+ if (existsSync18(candidate)) return candidate;
15578
15746
  }
15579
15747
  return null;
15580
15748
  }
@@ -15835,9 +16003,9 @@ function readProducerVersion() {
15835
16003
  let current = startDir;
15836
16004
  for (let i = 0; i < 10; i++) {
15837
16005
  const candidate = join29(current, "package.json");
15838
- if (existsSync17(candidate)) {
16006
+ if (existsSync18(candidate)) {
15839
16007
  try {
15840
- const pkg = JSON.parse(readFileSync10(candidate, "utf-8"));
16008
+ const pkg = JSON.parse(readFileSync8(candidate, "utf-8"));
15841
16009
  if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
15842
16010
  cachedProducerVersion = pkg.version;
15843
16011
  return pkg.version;
@@ -15889,7 +16057,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
15889
16057
  const result = [];
15890
16058
  for (const v of videos) {
15891
16059
  const outputDir = join30(planDir, "video-frames", v.videoId);
15892
- if (!existsSync18(outputDir)) {
16060
+ if (!existsSync19(outputDir)) {
15893
16061
  throw new Error(
15894
16062
  `[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
15895
16063
  );
@@ -16005,7 +16173,7 @@ async function extractDeferredVideoFramesForChunk(input) {
16005
16173
  const range = video.deferredRange;
16006
16174
  const remoteUrl = remoteSources[range.sourcePath];
16007
16175
  const sourcePath = remoteUrl ?? join30(input.planDir, ...range.sourcePath.split("/"));
16008
- if (remoteUrl === void 0 && !existsSync18(sourcePath)) {
16176
+ if (remoteUrl === void 0 && !existsSync19(sourcePath)) {
16009
16177
  throw new RenderChunkValidationError(
16010
16178
  MISSING_PLAN_ARTIFACT,
16011
16179
  `[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
@@ -16167,7 +16335,7 @@ function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
16167
16335
  }
16168
16336
 
16169
16337
  // src/services/render/stages/freezePlan.ts
16170
- import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync11, readdirSync as readdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
16338
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync9, readdirSync as readdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
16171
16339
  import { join as join32, relative as relative3, resolve as resolve9 } from "node:path";
16172
16340
 
16173
16341
  // src/services/distributed/planProtocol.ts
@@ -16361,7 +16529,7 @@ function planFileSha256(absolutePath, digests) {
16361
16529
  return sha256;
16362
16530
  }
16363
16531
  async function prehashPlanFiles(dir, digests) {
16364
- if (!existsSync19(dir)) return;
16532
+ if (!existsSync20(dir)) return;
16365
16533
  for (const { absolutePath } of listPlanFiles(dir)) {
16366
16534
  if (!digests.has(absolutePath)) digests.set(absolutePath, await sha256FileAsync(absolutePath));
16367
16535
  }
@@ -16407,7 +16575,7 @@ function collectPlanAssetShas(planDir, digests) {
16407
16575
  const assets = [];
16408
16576
  for (const file of files) {
16409
16577
  if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
16410
- compositionHtml = readFileSync11(file.absolutePath);
16578
+ compositionHtml = readFileSync9(file.absolutePath);
16411
16579
  continue;
16412
16580
  }
16413
16581
  if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
@@ -16431,19 +16599,19 @@ function writePlanRemoteFiles(planDir, files) {
16431
16599
  }
16432
16600
  function readPlanRemoteFiles(planDir) {
16433
16601
  const path = join32(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
16434
- return existsSync19(path) ? JSON.parse(readFileSync11(path, "utf-8")) : [];
16602
+ return existsSync20(path) ? JSON.parse(readFileSync9(path, "utf-8")) : [];
16435
16603
  }
16436
16604
  function recomputePlanHashFromPlanDir(planDir, digests) {
16437
16605
  const planJsonPath = join32(planDir, "plan.json");
16438
16606
  const encoderJsonPath = join32(planDir, "meta", "encoder.json");
16439
- if (!existsSync19(planJsonPath)) {
16607
+ if (!existsSync20(planJsonPath)) {
16440
16608
  throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
16441
16609
  }
16442
- if (!existsSync19(encoderJsonPath)) {
16610
+ if (!existsSync20(encoderJsonPath)) {
16443
16611
  throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
16444
16612
  }
16445
- const planJson = JSON.parse(readFileSync11(planJsonPath, "utf-8"));
16446
- const encoderConfigCanonicalJson = readFileSync11(encoderJsonPath, "utf-8");
16613
+ const planJson = JSON.parse(readFileSync9(planJsonPath, "utf-8"));
16614
+ const encoderConfigCanonicalJson = readFileSync9(encoderJsonPath, "utf-8");
16447
16615
  const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
16448
16616
  return computePlanHash({
16449
16617
  compositionHtml,
@@ -16469,11 +16637,11 @@ async function freezePlan(input) {
16469
16637
  hasAudio,
16470
16638
  digests
16471
16639
  } = input;
16472
- if (!existsSync19(planDir)) {
16640
+ if (!existsSync20(planDir)) {
16473
16641
  throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
16474
16642
  }
16475
16643
  const metaDir = join32(planDir, "meta");
16476
- if (!existsSync19(metaDir)) mkdirSync12(metaDir, { recursive: true });
16644
+ if (!existsSync20(metaDir)) mkdirSync12(metaDir, { recursive: true });
16477
16645
  writeFileSync7(
16478
16646
  join32(metaDir, "composition.json"),
16479
16647
  `${JSON.stringify(composition, null, 2)}
@@ -16549,7 +16717,7 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
16549
16717
  }
16550
16718
 
16551
16719
  // src/services/distributed/chunkAudio.ts
16552
- import { closeSync as closeSync6, existsSync as existsSync20, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync12 } from "node:fs";
16720
+ import { closeSync as closeSync6, existsSync as existsSync21, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync10 } from "node:fs";
16553
16721
  import { writeFileSync as writeFileSync8, writeSync } from "node:fs";
16554
16722
  import { join as join33 } from "node:path";
16555
16723
  import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
@@ -16637,7 +16805,7 @@ async function writeChunkAudioSlices(input) {
16637
16805
  offset += track.sizes[offsets.length - 1];
16638
16806
  }
16639
16807
  });
16640
- const bytes = readFileSync12(input.audioPath);
16808
+ const bytes = readFileSync10(input.audioPath);
16641
16809
  const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
16642
16810
  mkdirSync13(join33(input.planDir, SLICE_DIR), { recursive: true });
16643
16811
  input.chunks.forEach((_, index) => {
@@ -16655,9 +16823,9 @@ async function writeChunkAudioSlices(input) {
16655
16823
  }
16656
16824
  function chunkAudioSource(planDir, chunkIndex, scratchPath) {
16657
16825
  const layoutPath = join33(planDir, SLICE_LAYOUT);
16658
- if (!existsSync20(layoutPath)) return resolvePlanAudioPath(planDir);
16659
- const layout = JSON.parse(readFileSync12(layoutPath, "utf-8"));
16660
- const frame = readFileSync12(join33(planDir, SLICE_FRAME));
16826
+ if (!existsSync21(layoutPath)) return resolvePlanAudioPath(planDir);
16827
+ const layout = JSON.parse(readFileSync10(layoutPath, "utf-8"));
16828
+ const frame = readFileSync10(join33(planDir, SLICE_FRAME));
16661
16829
  const fd = openSync6(scratchPath, "w");
16662
16830
  try {
16663
16831
  writeSync(fd, frame, 0, layout.payloadStart, 0);
@@ -16670,7 +16838,7 @@ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
16670
16838
  );
16671
16839
  const start = layout.partStarts[chunkIndex];
16672
16840
  if (start !== void 0) {
16673
- const part = readFileSync12(join33(planDir, chunkAudioSlicePath(chunkIndex)));
16841
+ const part = readFileSync10(join33(planDir, chunkAudioSlicePath(chunkIndex)));
16674
16842
  writeSync(fd, part, 0, part.length, start);
16675
16843
  }
16676
16844
  ftruncateSync(fd, layout.sizeBytes);
@@ -17215,7 +17383,7 @@ function createVideoSourceShipper(input) {
17215
17383
  let result = null;
17216
17384
  for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
17217
17385
  const rel = pathInside(root, srcPath);
17218
- if (rel !== null && (existsSync21(join35(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
17386
+ if (rel !== null && (existsSync22(join35(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
17219
17387
  result = ["compiled", ...rel.split(sep2)].join("/");
17220
17388
  break;
17221
17389
  }
@@ -17243,7 +17411,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
17243
17411
  useGpu: false,
17244
17412
  browserGpuMode: "software"
17245
17413
  });
17246
- if (!existsSync21(planDir)) mkdirSync14(planDir, { recursive: true });
17414
+ if (!existsSync22(planDir)) mkdirSync14(planDir, { recursive: true });
17247
17415
  const log = config.logger ?? defaultLogger;
17248
17416
  const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
17249
17417
  const abortSignal = config.abortSignal;
@@ -17273,7 +17441,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
17273
17441
  });
17274
17442
  const entryFile = config.entryFile ?? "index.html";
17275
17443
  const htmlPath = join35(projectDir, entryFile);
17276
- if (!existsSync21(htmlPath)) {
17444
+ if (!existsSync22(htmlPath)) {
17277
17445
  throw new Error(`[plan] entry file not found: ${htmlPath}`);
17278
17446
  }
17279
17447
  const workDir = join35(planDir, ".plan-work");
@@ -17484,15 +17652,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
17484
17652
  logPlanStage(log, "extract", stageStarted);
17485
17653
  const stagedVideoFrames = join35(compiledDir, "__hyperframes_video_frames");
17486
17654
  const videoFramesDst = join35(planDir, "video-frames");
17487
- if (existsSync21(videoFramesDst)) rmSync12(videoFramesDst, { recursive: true, force: true });
17655
+ if (existsSync22(videoFramesDst)) rmSync12(videoFramesDst, { recursive: true, force: true });
17488
17656
  const videoSourcesDir = join35(planDir, "video-sources");
17489
17657
  rmSync12(videoSourcesDir, { recursive: true, force: true });
17490
- if (existsSync21(stagedVideoFrames)) {
17658
+ if (existsSync22(stagedVideoFrames)) {
17491
17659
  renameWithRetry7(stagedVideoFrames, videoFramesDst);
17492
17660
  } else {
17493
17661
  mkdirSync14(videoFramesDst, { recursive: true });
17494
17662
  }
17495
- if (existsSync21(finalCompiledDir)) rmSync12(finalCompiledDir, { recursive: true, force: true });
17663
+ if (existsSync22(finalCompiledDir)) rmSync12(finalCompiledDir, { recursive: true, force: true });
17496
17664
  renameWithRetry7(compiledDir, finalCompiledDir);
17497
17665
  const shipVideoSource = createVideoSourceShipper({
17498
17666
  planDir,
@@ -17612,10 +17780,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
17612
17780
  applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
17613
17781
  }
17614
17782
  const planAudioPath = join35(planDir, PLAN_AUDIO_RELATIVE_PATH);
17615
- if (audioResult?.hasAudio && existsSync21(audioResult.audioOutputPath)) {
17783
+ if (audioResult?.hasAudio && existsSync22(audioResult.audioOutputPath)) {
17616
17784
  renameWithRetry7(audioResult.audioOutputPath, planAudioPath);
17617
17785
  }
17618
- if (chunksCarryAudio && existsSync21(planAudioPath)) {
17786
+ if (chunksCarryAudio && existsSync22(planAudioPath)) {
17619
17787
  if (options.sliceChunkAudio) {
17620
17788
  stageStarted = Date.now();
17621
17789
  await writeChunkAudioSlices({
@@ -17730,7 +17898,7 @@ async function plan(projectDir, config, planDir, options = {}) {
17730
17898
 
17731
17899
  // src/services/distributed/renderChunk.ts
17732
17900
  import { randomBytes } from "node:crypto";
17733
- import { existsSync as existsSync24, mkdirSync as mkdirSync17, readFileSync as readFileSync14, readdirSync as readdirSync10, rmSync as rmSync15, writeFileSync as writeFileSync12 } from "node:fs";
17901
+ import { existsSync as existsSync25, mkdirSync as mkdirSync17, readFileSync as readFileSync12, readdirSync as readdirSync10, rmSync as rmSync15, writeFileSync as writeFileSync12 } from "node:fs";
17734
17902
  import { cpus as cpus3 } from "node:os";
17735
17903
  import { join as join39 } from "node:path";
17736
17904
  import {
@@ -17753,11 +17921,11 @@ import {
17753
17921
  // src/services/distributed/planV2.ts
17754
17922
  import {
17755
17923
  copyFileSync as copyFileSync8,
17756
- existsSync as existsSync23,
17924
+ existsSync as existsSync24,
17757
17925
  lstatSync as lstatSync3,
17758
17926
  mkdirSync as mkdirSync16,
17759
17927
  mkdtempSync as mkdtempSync6,
17760
- readFileSync as readFileSync13,
17928
+ readFileSync as readFileSync11,
17761
17929
  readdirSync as readdirSync9,
17762
17930
  rmSync as rmSync14,
17763
17931
  statSync as statSync10,
@@ -17771,7 +17939,7 @@ import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as r
17771
17939
  // src/services/distributed/planV2Publisher.ts
17772
17940
  import {
17773
17941
  copyFileSync as copyFileSync7,
17774
- existsSync as existsSync22,
17942
+ existsSync as existsSync23,
17775
17943
  linkSync as linkSync2,
17776
17944
  mkdirSync as mkdirSync15,
17777
17945
  mkdtempSync as mkdtempSync5,
@@ -17840,7 +18008,7 @@ var LocalPlanV2ArtifactPublisher = class {
17840
18008
  #linkFile;
17841
18009
  #committed = false;
17842
18010
  constructor(destinationDir, options = {}) {
17843
- if (existsSync22(destinationDir)) {
18011
+ if (existsSync23(destinationDir)) {
17844
18012
  throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
17845
18013
  }
17846
18014
  this.destinationDir = destinationDir;
@@ -17857,7 +18025,7 @@ var LocalPlanV2ArtifactPublisher = class {
17857
18025
  );
17858
18026
  }
17859
18027
  const destinationPath = planV2BlobPath(this.temporaryDir, digest);
17860
- if (existsSync22(destinationPath)) return;
18028
+ if (existsSync23(destinationPath)) return;
17861
18029
  mkdirSync15(dirname14(destinationPath), { recursive: true });
17862
18030
  const stagingDir = mkdtempSync5(join37(dirname14(destinationPath), ".plan-v2-blob-"));
17863
18031
  const temporaryPath = join37(stagingDir, "blob");
@@ -17875,7 +18043,7 @@ var LocalPlanV2ArtifactPublisher = class {
17875
18043
  }
17876
18044
  async commitManifest(manifestBytes) {
17877
18045
  for (const digest of new Set(manifestDigests(manifestBytes))) {
17878
- if (!existsSync22(planV2BlobPath(this.temporaryDir, digest))) {
18046
+ if (!existsSync23(planV2BlobPath(this.temporaryDir, digest))) {
17879
18047
  throw new PlanV2IntegrityError(
17880
18048
  `cannot commit manifest before referenced blob is durable: ${digest}`
17881
18049
  );
@@ -17906,7 +18074,7 @@ function isNonNegativeIntegerArray(value) {
17906
18074
  return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0);
17907
18075
  }
17908
18076
  function readJsonFile(path, label) {
17909
- const contents = readFileSync13(path, "utf-8");
18077
+ const contents = readFileSync11(path, "utf-8");
17910
18078
  try {
17911
18079
  const parsed = JSON.parse(contents);
17912
18080
  return parsed;
@@ -17954,7 +18122,7 @@ function assertValidExtractionCacheCompleteSentinel(path) {
17954
18122
  }
17955
18123
  function validateExtractionCacheCompleteSentinels(executionPlanDir) {
17956
18124
  const videoRoot = join38(executionPlanDir, "video-frames");
17957
- if (!existsSync23(videoRoot)) return;
18125
+ if (!existsSync24(videoRoot)) return;
17958
18126
  for (const videoEntry of readdirSync9(videoRoot, { withFileTypes: true })) {
17959
18127
  if (!videoEntry.isDirectory()) continue;
17960
18128
  const videoDir = join38(videoRoot, videoEntry.name);
@@ -18055,7 +18223,7 @@ function parsePlanVideosJson2(value) {
18055
18223
  }
18056
18224
  function materializeExtractedVideoDirectories(planDir) {
18057
18225
  const videosPath = join38(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18058
- if (!existsSync23(videosPath)) return;
18226
+ if (!existsSync24(videosPath)) return;
18059
18227
  const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
18060
18228
  for (const video of videos.extracted) {
18061
18229
  mkdirSync16(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
@@ -18089,9 +18257,9 @@ function parseChunkSlices(value) {
18089
18257
  }
18090
18258
  function buildVideoChunkDependencies(executionPlanDir, dimensions) {
18091
18259
  const videoRoot = join38(executionPlanDir, "video-frames");
18092
- const hasExtractedFrames = existsSync23(videoRoot) && listFiles(videoRoot).length > 0;
18260
+ const hasExtractedFrames = existsSync24(videoRoot) && listFiles(videoRoot).length > 0;
18093
18261
  const videosPath = join38(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18094
- if (!existsSync23(videosPath)) {
18262
+ if (!existsSync24(videosPath)) {
18095
18263
  return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
18096
18264
  }
18097
18265
  const chunksPath = join38(executionPlanDir, "meta", "chunks.json");
@@ -18148,7 +18316,7 @@ function computeManifestHash(manifest) {
18148
18316
  return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
18149
18317
  }
18150
18318
  function writeBlob(sourcePath, destinationPath) {
18151
- if (existsSync23(destinationPath)) return;
18319
+ if (existsSync24(destinationPath)) return;
18152
18320
  mkdirSync16(dirname15(destinationPath), { recursive: true });
18153
18321
  const temporaryDir = mkdtempSync6(join38(dirname15(destinationPath), ".plan-v2-blob-"));
18154
18322
  const temporaryPath = join38(temporaryDir, "blob");
@@ -18188,7 +18356,7 @@ async function putTasks(tasks) {
18188
18356
  }
18189
18357
  function buildPlanV2Publication(executionPlanDir, digests) {
18190
18358
  const executionPlanPath = join38(executionPlanDir, "plan.json");
18191
- if (!existsSync23(executionPlanPath)) {
18359
+ if (!existsSync24(executionPlanPath)) {
18192
18360
  throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
18193
18361
  }
18194
18362
  const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
@@ -18268,7 +18436,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
18268
18436
  };
18269
18437
  }
18270
18438
  function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
18271
- if (existsSync23(planV2Dir)) {
18439
+ if (existsSync24(planV2Dir)) {
18272
18440
  throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
18273
18441
  }
18274
18442
  const publication = buildPlanV2Publication(executionPlanDir);
@@ -18360,7 +18528,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
18360
18528
  }
18361
18529
  }
18362
18530
  async function planV2(projectDir, config, planV2Dir) {
18363
- if (existsSync23(planV2Dir)) {
18531
+ if (existsSync24(planV2Dir)) {
18364
18532
  throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
18365
18533
  }
18366
18534
  const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
@@ -18509,7 +18677,7 @@ function parsePlanV2Manifest(value) {
18509
18677
  }
18510
18678
  function readPlanV2Manifest(planV2Dir) {
18511
18679
  const manifestPath = join38(planV2Dir, "plan.json");
18512
- if (!existsSync23(manifestPath)) {
18680
+ if (!existsSync24(manifestPath)) {
18513
18681
  throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
18514
18682
  }
18515
18683
  const value = readJsonFile(manifestPath, "v2 manifest");
@@ -18528,7 +18696,7 @@ function listPlanV2ArtifactsForTarget(manifest, target) {
18528
18696
  }
18529
18697
  function verifyBlob(planV2Dir, artifact) {
18530
18698
  const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
18531
- if (!existsSync23(sourcePath)) {
18699
+ if (!existsSync24(sourcePath)) {
18532
18700
  throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
18533
18701
  }
18534
18702
  const stats = statSync10(sourcePath);
@@ -18541,7 +18709,7 @@ function verifyBlob(planV2Dir, artifact) {
18541
18709
  return sourcePath;
18542
18710
  }
18543
18711
  function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
18544
- if (existsSync23(destinationDir)) {
18712
+ if (existsSync24(destinationDir)) {
18545
18713
  throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
18546
18714
  }
18547
18715
  const manifest = readPlanV2Manifest(planV2Dir);
@@ -18606,7 +18774,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
18606
18774
  }
18607
18775
  function readValidatedPlanV2Materialization(planDir, expectedTarget) {
18608
18776
  const markerPath = join38(planDir, PLAN_V2_MATERIALIZATION_MARKER);
18609
- if (!existsSync23(markerPath)) return null;
18777
+ if (!existsSync24(markerPath)) return null;
18610
18778
  const marker = readJsonFile(markerPath, "materialization marker");
18611
18779
  if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
18612
18780
  throw new PlanV2IntegrityError("malformed materialization marker");
@@ -18624,7 +18792,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
18624
18792
  for (const artifact of required) {
18625
18793
  if (Object.hasOwn(remoteSources, artifact.path)) continue;
18626
18794
  const path = join38(planDir, ...artifact.path.split("/"));
18627
- if (!existsSync23(path) || statSync10(path).size !== artifact.sizeBytes) {
18795
+ if (!existsSync24(path) || statSync10(path).size !== artifact.sizeBytes) {
18628
18796
  throw new PlanV2IntegrityError(
18629
18797
  `materialized artifact missing or truncated: ${artifact.path}`
18630
18798
  );
@@ -18741,10 +18909,10 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
18741
18909
  return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
18742
18910
  }
18743
18911
  function hashChunkOutput(outputPath, kind) {
18744
- if (kind === "file") return sha256Hex(readFileSync14(outputPath));
18912
+ if (kind === "file") return sha256Hex(readFileSync12(outputPath));
18745
18913
  const entries = readdirSync10(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
18746
18914
  const lines = entries.map(
18747
- (name) => `${name}\0${sha256Hex(readFileSync14(join39(outputPath, name)))}`
18915
+ (name) => `${name}\0${sha256Hex(readFileSync12(join39(outputPath, name)))}`
18748
18916
  );
18749
18917
  return sha256Hex(lines.join("\0"));
18750
18918
  }
@@ -18764,13 +18932,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18764
18932
  const planJsonPath = join39(planDir, "plan.json");
18765
18933
  const encoderJsonPath = join39(planDir, "meta", "encoder.json");
18766
18934
  const chunksJsonPath = join39(planDir, "meta", "chunks.json");
18767
- if (!existsSync24(planJsonPath)) {
18935
+ if (!existsSync25(planJsonPath)) {
18768
18936
  throw new RenderChunkValidationError(
18769
18937
  MISSING_PLAN_ARTIFACT,
18770
18938
  `[renderChunk] planDir is missing required artifact: ${planJsonPath}`
18771
18939
  );
18772
18940
  }
18773
- const plan2 = JSON.parse(readFileSync14(planJsonPath, "utf-8"));
18941
+ const plan2 = JSON.parse(readFileSync12(planJsonPath, "utf-8"));
18774
18942
  readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
18775
18943
  const planHashStarted = Date.now();
18776
18944
  const v2Materialization = readValidatedPlanV2Materialization(planDir, {
@@ -18780,20 +18948,20 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18780
18948
  const v2Manifest = v2Materialization?.manifest ?? null;
18781
18949
  const remoteSources = v2Materialization?.remoteSources ?? {};
18782
18950
  for (const required of [encoderJsonPath, chunksJsonPath]) {
18783
- if (!existsSync24(required)) {
18951
+ if (!existsSync25(required)) {
18784
18952
  throw new RenderChunkValidationError(
18785
18953
  MISSING_PLAN_ARTIFACT,
18786
18954
  `[renderChunk] planDir is missing required artifact: ${required}`
18787
18955
  );
18788
18956
  }
18789
18957
  }
18790
- const encoder = JSON.parse(readFileSync14(encoderJsonPath, "utf-8"));
18791
- const chunks = JSON.parse(readFileSync14(chunksJsonPath, "utf-8"));
18958
+ const encoder = JSON.parse(readFileSync12(encoderJsonPath, "utf-8"));
18959
+ const chunks = JSON.parse(readFileSync12(chunksJsonPath, "utf-8"));
18792
18960
  const videosJsonPath = join39(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18793
18961
  let planVideos = null;
18794
- if (existsSync24(videosJsonPath)) {
18962
+ if (existsSync25(videosJsonPath)) {
18795
18963
  try {
18796
- planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync14(videosJsonPath, "utf-8")));
18964
+ planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync12(videosJsonPath, "utf-8")));
18797
18965
  } catch (err) {
18798
18966
  if (err instanceof RenderChunkValidationError) throw err;
18799
18967
  throw new RenderChunkValidationError(
@@ -18822,7 +18990,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18822
18990
  const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
18823
18991
  const framesInChunk = range.endFrame - range.startFrame;
18824
18992
  const compiledDir = join39(planDir, "compiled");
18825
- if (!existsSync24(compiledDir)) {
18993
+ if (!existsSync25(compiledDir)) {
18826
18994
  throw new RenderChunkValidationError(
18827
18995
  MISSING_PLAN_ARTIFACT,
18828
18996
  `[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
@@ -18904,7 +19072,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18904
19072
  dimensions: plan2.dimensions,
18905
19073
  encoder,
18906
19074
  videoCount: planVideos?.videos.length ?? 0,
18907
- html: readFileSync14(join39(compiledDir, "index.html"), "utf-8"),
19075
+ html: readFileSync12(join39(compiledDir, "index.html"), "utf-8"),
18908
19076
  // The plan ships a deferred source only to chunks that show it; don't wait on the rest.
18909
19077
  skipReadinessVideoIds: deferredVideos.filter(
18910
19078
  (video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
@@ -19072,10 +19240,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19072
19240
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
19073
19241
  const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
19074
19242
  if (isPngSequence) {
19075
- if (!existsSync24(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
19243
+ if (!existsSync25(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
19076
19244
  } else {
19077
19245
  const outDir = join39(outputChunkPath, "..");
19078
- if (!existsSync24(outDir)) mkdirSync17(outDir, { recursive: true });
19246
+ if (!existsSync25(outDir)) mkdirSync17(outDir, { recursive: true });
19079
19247
  }
19080
19248
  const encodeInput = {
19081
19249
  job,
@@ -19366,7 +19534,7 @@ async function renderSpan(projectDir, config, span, outputPath) {
19366
19534
  planned.totalFrames
19367
19535
  );
19368
19536
  const slices = JSON.parse(
19369
- readFileSync15(join40(planDir, "meta", "chunks.json"), "utf-8")
19537
+ readFileSync13(join40(planDir, "meta", "chunks.json"), "utf-8")
19370
19538
  );
19371
19539
  const parts = [];
19372
19540
  for (const slice of slices) {
@@ -19969,7 +20137,7 @@ import {
19969
20137
 
19970
20138
  // src/server.ts
19971
20139
  import {
19972
- existsSync as existsSync27,
20140
+ existsSync as existsSync28,
19973
20141
  mkdirSync as mkdirSync19,
19974
20142
  statSync as statSync12,
19975
20143
  mkdtempSync as mkdtempSync8,
@@ -19988,8 +20156,8 @@ import { fetchPublicHttpsText as fetchPublicHttpsText2 } from "@hyperframes/engi
19988
20156
 
19989
20157
  // src/services/hyperframeLint.ts
19990
20158
  import {
19991
- existsSync as existsSync25,
19992
- readFileSync as readFileSync16,
20159
+ existsSync as existsSync26,
20160
+ readFileSync as readFileSync14,
19993
20161
  statSync as statSync11,
19994
20162
  openSync as openSync7,
19995
20163
  fstatSync as fstatSync3,
@@ -20035,14 +20203,14 @@ function readEntryHtml(filePath) {
20035
20203
  }
20036
20204
  try {
20037
20205
  if (!fstatSync3(fd).isFile()) return null;
20038
- return readFileSync16(fd, "utf-8");
20206
+ return readFileSync14(fd, "utf-8");
20039
20207
  } finally {
20040
20208
  closeSync7(fd);
20041
20209
  }
20042
20210
  }
20043
20211
  function readProjectEntryFile(projectDir, preferredEntryFile) {
20044
20212
  const absProjectDir = resolve11(projectDir);
20045
- if (!existsSync25(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
20213
+ if (!existsSync26(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
20046
20214
  return { error: `Project directory not found: ${absProjectDir}` };
20047
20215
  }
20048
20216
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
@@ -20111,7 +20279,7 @@ async function runHyperframeLint(prepared) {
20111
20279
  import { Worker as Worker2 } from "node:worker_threads";
20112
20280
  import { fileURLToPath as fileURLToPath6 } from "node:url";
20113
20281
  import { dirname as dirname16, join as join42 } from "node:path";
20114
- import { existsSync as existsSync26 } from "node:fs";
20282
+ import { existsSync as existsSync27 } from "node:fs";
20115
20283
  var DEFAULT_HEALTH_PORT = 9848;
20116
20284
  async function startHealthWorker(options = {}) {
20117
20285
  const log = options.logger ?? defaultLogger2();
@@ -20188,7 +20356,7 @@ function resolveWorkerEntry2() {
20188
20356
  const here = dirname16(fileURLToPath6(import.meta.url));
20189
20357
  const candidates = [join42(here, "healthWorkerThread.js"), join42(here, "healthWorkerThread.ts")];
20190
20358
  for (const candidate of candidates) {
20191
- if (existsSync26(candidate)) return candidate;
20359
+ if (existsSync27(candidate)) return candidate;
20192
20360
  }
20193
20361
  return void 0;
20194
20362
  }
@@ -20331,7 +20499,7 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
20331
20499
  const { input, cleanupProjectDir } = prepared;
20332
20500
  const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
20333
20501
  const outputDir = dirname17(absoluteOutputPath);
20334
- if (!existsSync27(outputDir)) mkdirSync19(outputDir, { recursive: true });
20502
+ if (!existsSync28(outputDir)) mkdirSync19(outputDir, { recursive: true });
20335
20503
  return { input, cleanupProjectDir, absoluteOutputPath };
20336
20504
  }
20337
20505
  function validateRenderOverrides(body) {
@@ -20372,11 +20540,11 @@ function prepareProjectDirectory(projectDir, options) {
20372
20540
  const candidate = nonEmptyString(projectDir);
20373
20541
  if (!candidate) return null;
20374
20542
  const absProjectDir = resolve12(candidate);
20375
- if (!existsSync27(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
20543
+ if (!existsSync28(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
20376
20544
  return { error: `Project directory not found: ${absProjectDir}` };
20377
20545
  }
20378
20546
  const entry = options.entryFile || "index.html";
20379
- if (!existsSync27(resolve12(absProjectDir, entry))) {
20547
+ if (!existsSync28(resolve12(absProjectDir, entry))) {
20380
20548
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
20381
20549
  }
20382
20550
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -20468,7 +20636,7 @@ function cleanupTempDir(dir, log) {
20468
20636
  }
20469
20637
  }
20470
20638
  function outputFileSize(path) {
20471
- return existsSync27(path) ? statSync12(path).size : 0;
20639
+ return existsSync28(path) ? statSync12(path).size : 0;
20472
20640
  }
20473
20641
  function createBlockingProgressReporter(log, requestId) {
20474
20642
  let lastLoggedPct = -10;
@@ -20770,7 +20938,7 @@ function createRenderHandlers(options = {}) {
20770
20938
  if (!artifact) {
20771
20939
  return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
20772
20940
  }
20773
- if (!existsSync27(artifact.path)) {
20941
+ if (!existsSync28(artifact.path)) {
20774
20942
  store.delete(token);
20775
20943
  return c.json({ success: false, error: "Output artifact file missing" }, 404);
20776
20944
  }
@@ -20872,10 +21040,10 @@ import { join as join45 } from "node:path";
20872
21040
  // src/services/distributed/assemble.ts
20873
21041
  import {
20874
21042
  cpSync as cpSync3,
20875
- existsSync as existsSync28,
21043
+ existsSync as existsSync29,
20876
21044
  mkdirSync as mkdirSync20,
20877
21045
  mkdtempSync as mkdtempSync9,
20878
- readFileSync as readFileSync17,
21046
+ readFileSync as readFileSync15,
20879
21047
  readdirSync as readdirSync11,
20880
21048
  rmSync as rmSync18,
20881
21049
  statSync as statSync13,
@@ -20923,7 +21091,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
20923
21091
  }
20924
21092
  const format = plan2.dimensions.format;
20925
21093
  const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
20926
- if (!existsSync28(dirname18(outputPath))) {
21094
+ if (!existsSync29(dirname18(outputPath))) {
20927
21095
  mkdirSync20(dirname18(outputPath), { recursive: true });
20928
21096
  }
20929
21097
  const workDir = mkdtempSync9(`${outputPath}.assemble-work-`);
@@ -20947,7 +21115,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
20947
21115
  try {
20948
21116
  const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
20949
21117
  const finalPath = join44(workDir, `output.${format}`);
20950
- const audio = audioPath !== null && existsSync28(audioPath) ? audioPath : null;
21118
+ const audio = audioPath !== null && existsSync29(audioPath) ? audioPath : null;
20951
21119
  await writeJoined(ctx, videoSources, audio, finalPath);
20952
21120
  renameWithRetry10(finalPath, outputPath);
20953
21121
  log.info("[assemble] stage timings", {
@@ -20965,7 +21133,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
20965
21133
  });
20966
21134
  }
20967
21135
  }
20968
- const fileSize = existsSync28(outputPath) ? statSync13(outputPath).size : 0;
21136
+ const fileSize = existsSync29(outputPath) ? statSync13(outputPath).size : 0;
20969
21137
  return {
20970
21138
  outputPath,
20971
21139
  durationMs: Date.now() - start,
@@ -20976,16 +21144,16 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
20976
21144
  function readAssemblePlan(planDir, chunkPaths) {
20977
21145
  const planJsonPath = join44(planDir, "plan.json");
20978
21146
  const chunksJsonPath = join44(planDir, "meta", "chunks.json");
20979
- if (!existsSync28(planJsonPath)) {
21147
+ if (!existsSync29(planJsonPath)) {
20980
21148
  throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
20981
21149
  }
20982
- const plan2 = JSON.parse(readFileSync17(planJsonPath, "utf-8"));
21150
+ const plan2 = JSON.parse(readFileSync15(planJsonPath, "utf-8"));
20983
21151
  readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
20984
21152
  validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
20985
- if (!existsSync28(chunksJsonPath)) {
21153
+ if (!existsSync29(chunksJsonPath)) {
20986
21154
  throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
20987
21155
  }
20988
- const chunks = JSON.parse(readFileSync17(chunksJsonPath, "utf-8"));
21156
+ const chunks = JSON.parse(readFileSync15(chunksJsonPath, "utf-8"));
20989
21157
  if (chunkPaths.length !== chunks.length) {
20990
21158
  throw new Error(
20991
21159
  `[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
@@ -20997,7 +21165,7 @@ function readAssemblePlan(planDir, chunkPaths) {
20997
21165
  }
20998
21166
  }
20999
21167
  for (const path of chunkPaths.flat()) {
21000
- if (!existsSync28(path)) {
21168
+ if (!existsSync29(path)) {
21001
21169
  throw new Error(`[assemble] chunk path does not exist: ${path}`);
21002
21170
  }
21003
21171
  }
@@ -21131,13 +21299,13 @@ function stitchesSeparateAudio(planDir) {
21131
21299
  return lockedAudio(planDir).separateAudio === true;
21132
21300
  }
21133
21301
  function planFormat(planDir) {
21134
- const plan2 = JSON.parse(readFileSync17(join44(planDir, "plan.json"), "utf-8"));
21302
+ const plan2 = JSON.parse(readFileSync15(join44(planDir, "plan.json"), "utf-8"));
21135
21303
  return plan2.dimensions.format;
21136
21304
  }
21137
21305
  function lockedAudio(planDir) {
21138
21306
  const encoderJsonPath = join44(planDir, "meta", "encoder.json");
21139
- if (!existsSync28(encoderJsonPath)) return {};
21140
- return JSON.parse(readFileSync17(encoderJsonPath, "utf-8"));
21307
+ if (!existsSync29(encoderJsonPath)) return {};
21308
+ return JSON.parse(readFileSync15(encoderJsonPath, "utf-8"));
21141
21309
  }
21142
21310
  function readCfrEncoder(planDir, format) {
21143
21311
  if (format !== "mp4") {
@@ -21146,10 +21314,10 @@ function readCfrEncoder(planDir, format) {
21146
21314
  );
21147
21315
  }
21148
21316
  const encoderJsonPath = join44(planDir, "meta", "encoder.json");
21149
- if (!existsSync28(encoderJsonPath)) {
21317
+ if (!existsSync29(encoderJsonPath)) {
21150
21318
  throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
21151
21319
  }
21152
- const encoderJson = JSON.parse(readFileSync17(encoderJsonPath, "utf-8"));
21320
+ const encoderJson = JSON.parse(readFileSync15(encoderJsonPath, "utf-8"));
21153
21321
  if (encoderJson.encoder === "libx265-software") {
21154
21322
  throw new Error(
21155
21323
  `[assemble] cfr=true is not yet supported with codec: "h265". The cfr re-encode pass uses libx264 and would silently transcode the h265 chunks. Either disable cfr or render with codec: "h264".`
@@ -21158,7 +21326,7 @@ function readCfrEncoder(planDir, format) {
21158
21326
  return encoderJson;
21159
21327
  }
21160
21328
  function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
21161
- if (existsSync28(outputPath)) rmSync18(outputPath, { recursive: true, force: true });
21329
+ if (existsSync29(outputPath)) rmSync18(outputPath, { recursive: true, force: true });
21162
21330
  mkdirSync20(outputPath, { recursive: true });
21163
21331
  let globalIdx = 0;
21164
21332
  for (const chunkDir of chunkPaths) {
@@ -21182,7 +21350,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
21182
21350
  `[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
21183
21351
  );
21184
21352
  }
21185
- if (audioPath !== null && existsSync28(audioPath)) {
21353
+ if (audioPath !== null && existsSync29(audioPath)) {
21186
21354
  const sidecar = join44(outputPath, MIXED_AUDIO_FILENAME4);
21187
21355
  cpSync3(audioPath, sidecar);
21188
21356
  }