@hyperframes/producer 0.9.15 → 0.9.16

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.
@@ -8,11 +8,11 @@ import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframe
8
8
  import {
9
9
  copyFileSync as copyFileSync6,
10
10
  cpSync as cpSync2,
11
- existsSync as existsSync17,
11
+ existsSync as existsSync18,
12
12
  linkSync,
13
13
  mkdirSync as mkdirSync10,
14
14
  rmSync as rmSync7,
15
- writeFileSync as writeFileSync7
15
+ writeFileSync as writeFileSync8
16
16
  } from "node:fs";
17
17
  import { extname as extname4, isAbsolute as isAbsolute3, join as join24, relative as relative5, sep as sep2 } from "node:path";
18
18
  import {
@@ -71,12 +71,12 @@ var defaultLogger = createConsoleLogger("info");
71
71
  // src/services/renderOrchestrator.ts
72
72
  import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
73
73
  import {
74
- existsSync as existsSync12,
74
+ existsSync as existsSync13,
75
75
  mkdirSync as mkdirSync6,
76
76
  readdirSync as readdirSync3,
77
77
  rmSync as rmSync5,
78
78
  statSync as statSync5,
79
- writeFileSync as writeFileSync4,
79
+ writeFileSync as writeFileSync5,
80
80
  copyFileSync as copyFileSync5,
81
81
  appendFileSync
82
82
  } from "fs";
@@ -90,9 +90,7 @@ import {
90
90
  toFps as toFps2
91
91
  } from "@hyperframes/core";
92
92
  import { HF_AUDIO_GROUP_TAG } from "@hyperframes/core/audio-groups";
93
- import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
94
93
  import { HTML_BODY_CSS_HEIGHT_FIRST_RE, HTML_BODY_CSS_WIDTH_FIRST_RE } from "@hyperframes/parsers";
95
- import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
96
94
  import {
97
95
  extractMediaMetadata as extractMediaMetadata4,
98
96
  resolveConfig as resolveConfig2,
@@ -123,7 +121,8 @@ import {
123
121
  isDrawElementCaptureError,
124
122
  getDrawElementVerificationDetails,
125
123
  augmentProtocolTimeoutError,
126
- augmentPageNavigationTimeoutError
124
+ augmentPageNavigationTimeoutError,
125
+ displaySize
127
126
  } from "@hyperframes/engine";
128
127
  import {
129
128
  referenceBackgroundResult,
@@ -1914,8 +1913,11 @@ import {
1914
1913
  } from "@hyperframes/engine";
1915
1914
 
1916
1915
  // src/services/render/stages/compileStage.ts
1916
+ import { existsSync as existsSync7, writeFileSync as writeFileSync3 } from "node:fs";
1917
1917
  import { join as join7 } from "node:path";
1918
1918
  import { suggestMatchingPreset } from "@hyperframes/core";
1919
+ import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
1920
+ import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
1919
1921
 
1920
1922
  // src/services/htmlCompiler.ts
1921
1923
  import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
@@ -4620,11 +4622,36 @@ function adaptAspectAgnosticResolution(requested, aspectAgnostic, width, height,
4620
4622
  });
4621
4623
  return flipped;
4622
4624
  }
4625
+ function resolveEntryHtmlPath(input) {
4626
+ const { projectDir, workDir, entryFile, log } = input;
4627
+ const htmlPath = join7(projectDir, entryFile);
4628
+ const rawEntry = readTextFile2(htmlPath);
4629
+ if (entryFile === "index.html" || !isTemplateEntry(rawEntry)) return htmlPath;
4630
+ const projectIndexPath = join7(projectDir, "index.html");
4631
+ if (!existsSync7(projectIndexPath)) {
4632
+ throw new Error(
4633
+ `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
4634
+ );
4635
+ }
4636
+ const standaloneHtml = extractStandaloneEntryFromIndex(
4637
+ readTextFile2(projectIndexPath),
4638
+ entryFile,
4639
+ rawEntry
4640
+ );
4641
+ if (!standaloneHtml) {
4642
+ throw new Error(
4643
+ `Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
4644
+ );
4645
+ }
4646
+ const wrapperPath = join7(workDir, "standalone-entry.html");
4647
+ writeFileSync3(wrapperPath, standaloneHtml, "utf-8");
4648
+ log.info("Extracted standalone entry from index.html host context", { entryFile });
4649
+ return wrapperPath;
4650
+ }
4623
4651
  async function runCompileStage(input) {
4624
4652
  const {
4625
4653
  projectDir,
4626
4654
  workDir,
4627
- htmlPath,
4628
4655
  entryFile,
4629
4656
  job,
4630
4657
  cfg,
@@ -4635,6 +4662,7 @@ async function runCompileStage(input) {
4635
4662
  allowSystemFontCapture,
4636
4663
  abortSignal
4637
4664
  } = input;
4665
+ const htmlPath = resolveEntryHtmlPath({ projectDir, workDir, entryFile, log });
4638
4666
  const compileStart = Date.now();
4639
4667
  const compiled = await compileForRender(projectDir, htmlPath, join7(workDir, "downloads"), {
4640
4668
  log,
@@ -5466,7 +5494,7 @@ function validateDistributedDuration(input) {
5466
5494
  }
5467
5495
 
5468
5496
  // src/services/render/stages/extractVideosStage.ts
5469
- import { existsSync as existsSync7 } from "node:fs";
5497
+ import { existsSync as existsSync8 } from "node:fs";
5470
5498
  import { join as join9 } from "node:path";
5471
5499
  import {
5472
5500
  classifyVideoExtractionError,
@@ -5741,7 +5769,7 @@ async function runExtractVideosStage(input) {
5741
5769
  const probeFailures = await Promise.all(
5742
5770
  composition.videos.map(async (v) => {
5743
5771
  const videoPath = resolveProjectRelativeSrc3(v.src, projectDir, compiledDir);
5744
- if (!existsSync7(videoPath)) return null;
5772
+ if (!existsSync8(videoPath)) return null;
5745
5773
  try {
5746
5774
  const attempted = extractionPolicy.maxTransientRetries === 0 ? { result: await extractMediaMetadata3(videoPath), retries: 0 } : await runVideoExtractionWithRetry(() => extractMediaMetadata3(videoPath), {
5747
5775
  signal: abortSignal,
@@ -5779,7 +5807,7 @@ async function runExtractVideosStage(input) {
5779
5807
  const probed = await Promise.all(
5780
5808
  composition.images.map(async (img) => {
5781
5809
  const imgPath = resolveProjectRelativeSrc3(img.src, projectDir, compiledDir);
5782
- if (!existsSync7(imgPath)) return null;
5810
+ if (!existsSync8(imgPath)) return null;
5783
5811
  const colorSpace = await probeColorSpaceSafely(imgPath, log);
5784
5812
  if (isHdrColorSpace2(colorSpace)) {
5785
5813
  nativeHdrImageIds.add(img.id);
@@ -5899,7 +5927,7 @@ async function appendVideoAudioFromProbe(input) {
5899
5927
  videos.map(async (video) => {
5900
5928
  const path = isHttpUrl2(video.src) ? video.src : resolveProjectRelativeSrc3(video.src, projectDir, compiledDir);
5901
5929
  const readFrom = urlByAbsentLocalPath?.get(path) ?? path;
5902
- if (!isHttpUrl2(readFrom) && !existsSync7(readFrom)) return null;
5930
+ if (!isHttpUrl2(readFrom) && !existsSync8(readFrom)) return null;
5903
5931
  const metadata = await extractMediaMetadata3(readFrom);
5904
5932
  const window2 = await resolveFinalFrameExtractionWindow2(
5905
5933
  readFrom,
@@ -5927,7 +5955,7 @@ import {
5927
5955
  } from "@hyperframes/engine";
5928
5956
 
5929
5957
  // src/services/render/stages/audioStage.ts
5930
- import { existsSync as existsSync9 } from "node:fs";
5958
+ import { existsSync as existsSync10 } from "node:fs";
5931
5959
  import { join as join12 } from "node:path";
5932
5960
  import {
5933
5961
  MIXED_AUDIO_FILENAME,
@@ -5938,7 +5966,7 @@ import {
5938
5966
  } from "@hyperframes/engine";
5939
5967
 
5940
5968
  // src/services/render/audioDeliveryLimit.ts
5941
- import { existsSync as existsSync8 } from "node:fs";
5969
+ import { existsSync as existsSync9 } from "node:fs";
5942
5970
  import { dirname as dirname7, join as join11 } from "node:path";
5943
5971
  import {
5944
5972
  applyAudioFxChain,
@@ -6445,7 +6473,7 @@ async function limitDeliveryPeaks(input, deps = {
6445
6473
  applyChain: applyAudioFxChain,
6446
6474
  encode: encodeAac2,
6447
6475
  rename: renameWithRetry6,
6448
- sourceExists: existsSync8
6476
+ sourceExists: existsSync9
6449
6477
  }) {
6450
6478
  const { audioPath, sourcePath, signal, timeoutMs } = input;
6451
6479
  if (!deps.sourceExists(sourcePath)) return { success: true };
@@ -6515,7 +6543,7 @@ async function runAudioStageScoped(input) {
6515
6543
  );
6516
6544
  audioLoweredDb = guard.loweredDb;
6517
6545
  input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
6518
- if (input.keepDeliverySource && !guard.sourceStale && existsSync9(pcmSourcePath)) {
6546
+ if (input.keepDeliverySource && !guard.sourceStale && existsSync10(pcmSourcePath)) {
6519
6547
  deliverySourcePath = pcmSourcePath;
6520
6548
  }
6521
6549
  }
@@ -6643,7 +6671,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
6643
6671
  }
6644
6672
 
6645
6673
  // src/services/render/stages/captureStage.ts
6646
- import { existsSync as existsSync10, readdirSync, statfsSync } from "node:fs";
6674
+ import { existsSync as existsSync11, readdirSync, statfsSync } from "node:fs";
6647
6675
  import { join as join13 } from "node:path";
6648
6676
  import {
6649
6677
  classifyCaptureFailure as classifyCaptureFailure2,
@@ -6695,7 +6723,7 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
6695
6723
  }
6696
6724
  function measureCaptureFrameBytes(workDir, framesDir) {
6697
6725
  let total = sampleDirectoryBytes(framesDir);
6698
- if (!existsSync10(workDir)) return total;
6726
+ if (!existsSync11(workDir)) return total;
6699
6727
  for (const attempt of readdirSync(workDir)) {
6700
6728
  if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
6701
6729
  total += sampleDirectoryBytes(join13(workDir, attempt));
@@ -7032,13 +7060,13 @@ import {
7032
7060
  // src/services/render/stages/encodeStage.ts
7033
7061
  import {
7034
7062
  copyFileSync as copyFileSync4,
7035
- existsSync as existsSync11,
7063
+ existsSync as existsSync12,
7036
7064
  mkdirSync as mkdirSync5,
7037
7065
  readdirSync as readdirSync2,
7038
7066
  readFileSync as readFileSync5,
7039
7067
  rmSync as rmSync4,
7040
7068
  statSync as statSync4,
7041
- writeFileSync as writeFileSync3
7069
+ writeFileSync as writeFileSync4
7042
7070
  } from "node:fs";
7043
7071
  import { readFile as readFile2 } from "node:fs/promises";
7044
7072
  import { dirname as dirname8, join as join14 } from "node:path";
@@ -7134,7 +7162,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
7134
7162
  const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
7135
7163
  return gifEncodeFailure(startTime, outputPath, error);
7136
7164
  }
7137
- writeFileSync3(outputPath, gif);
7165
+ writeFileSync4(outputPath, gif);
7138
7166
  return null;
7139
7167
  }
7140
7168
  async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
@@ -7166,7 +7194,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
7166
7194
  const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
7167
7195
  if (failure) return failure;
7168
7196
  }
7169
- const fileSize = existsSync11(outputPath) ? statSync4(outputPath).size : 0;
7197
+ const fileSize = existsSync12(outputPath) ? statSync4(outputPath).size : 0;
7170
7198
  return {
7171
7199
  success: true,
7172
7200
  outputPath,
@@ -7327,7 +7355,7 @@ async function runEncodeStage(input) {
7327
7355
  const stage5Start = Date.now();
7328
7356
  if (isPngSequence) {
7329
7357
  const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
7330
- if (!existsSync11(outputPath)) mkdirSync5(outputPath, { recursive: true });
7358
+ if (!existsSync12(outputPath)) mkdirSync5(outputPath, { recursive: true });
7331
7359
  const captured = readdirSync2(framesDir).filter((name) => name.endsWith(".png")).sort();
7332
7360
  if (captured.length === 0) {
7333
7361
  throw new Error(
@@ -7338,7 +7366,7 @@ async function runEncodeStage(input) {
7338
7366
  const dst = join14(outputPath, formatExportFrameName(i, "png"));
7339
7367
  copyFileSync4(join14(framesDir, name), dst);
7340
7368
  });
7341
- if (hasAudio && audioOutputPath && existsSync11(audioOutputPath)) {
7369
+ if (hasAudio && audioOutputPath && existsSync12(audioOutputPath)) {
7342
7370
  copyFileSync4(audioOutputPath, join14(outputPath, MIXED_AUDIO_FILENAME2));
7343
7371
  log.info(
7344
7372
  `[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
@@ -7494,18 +7522,14 @@ function hasUsableVideoDimensions(metadata) {
7494
7522
  return Number.isFinite(metadata.width) && Number.isFinite(metadata.height) && metadata.width > 0 && metadata.height > 0;
7495
7523
  }
7496
7524
  function collectVideoMetadataHints(extractedVideos) {
7497
- return extractedVideos.filter((video) => hasUsableVideoDimensions(video.metadata)).map((video) => ({
7498
- id: video.videoId,
7499
- width: video.metadata.width,
7500
- height: video.metadata.height
7501
- })).sort((a, b) => a.id.localeCompare(b.id));
7525
+ return extractedVideos.filter((video) => hasUsableVideoDimensions(video.metadata)).map((video) => ({ id: video.videoId, ...displaySize(video.metadata) })).sort((a, b) => a.id.localeCompare(b.id));
7502
7526
  }
7503
7527
  function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
7504
7528
  const ranges = [];
7505
7529
  let rangeStart = null;
7506
7530
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
7507
7531
  const framePath = join15(framesDir, formatCaptureFrameName(frameIndex, frameExt));
7508
- const missing = !existsSync12(framePath) || statSync5(framePath).size <= 8;
7532
+ const missing = !existsSync13(framePath) || statSync5(framePath).size <= 8;
7509
7533
  if (missing && rangeStart === null) {
7510
7534
  rangeStart = frameIndex;
7511
7535
  } else if (!missing && rangeStart !== null) {
@@ -7595,7 +7619,7 @@ function countCapturedFrames(totalFrames, framesDir, frameExt) {
7595
7619
  let captured = 0;
7596
7620
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
7597
7621
  const framePath = join15(framesDir, formatCaptureFrameName(frameIndex, frameExt));
7598
- if (existsSync12(framePath)) captured++;
7622
+ if (existsSync13(framePath)) captured++;
7599
7623
  }
7600
7624
  return captured;
7601
7625
  }
@@ -7822,7 +7846,7 @@ import {
7822
7846
  } from "@hyperframes/engine";
7823
7847
 
7824
7848
  // src/services/distributed/chunkFrames.ts
7825
- import { existsSync as existsSync14, readdirSync as readdirSync4 } from "node:fs";
7849
+ import { existsSync as existsSync15, readdirSync as readdirSync4 } from "node:fs";
7826
7850
  import { extname as extname2, join as join17 } from "node:path";
7827
7851
  import {
7828
7852
  compositionRequiresWebGpu as compositionRequiresWebGpu3,
@@ -7834,7 +7858,7 @@ import {
7834
7858
  // src/services/distributed/shared.ts
7835
7859
  import { execFile as execFileCallback } from "node:child_process";
7836
7860
  import { dirname as dirname10, join as join16 } from "node:path";
7837
- import { existsSync as existsSync13, readFileSync as readFileSync6 } from "node:fs";
7861
+ import { existsSync as existsSync14, readFileSync as readFileSync6 } from "node:fs";
7838
7862
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7839
7863
  import { promisify as promisify2 } from "node:util";
7840
7864
  import {
@@ -7850,7 +7874,7 @@ function isPlanAudioArtifactPath(path) {
7850
7874
  function resolvePlanAudioPath(planDir) {
7851
7875
  for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
7852
7876
  const candidate = join16(planDir, name);
7853
- if (existsSync13(candidate)) return candidate;
7877
+ if (existsSync14(candidate)) return candidate;
7854
7878
  }
7855
7879
  return null;
7856
7880
  }
@@ -8111,7 +8135,7 @@ function readProducerVersion() {
8111
8135
  let current = startDir;
8112
8136
  for (let i = 0; i < 10; i++) {
8113
8137
  const candidate = join16(current, "package.json");
8114
- if (existsSync13(candidate)) {
8138
+ if (existsSync14(candidate)) {
8115
8139
  try {
8116
8140
  const pkg = JSON.parse(readFileSync6(candidate, "utf-8"));
8117
8141
  if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
@@ -8165,7 +8189,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
8165
8189
  const result = [];
8166
8190
  for (const v of videos) {
8167
8191
  const outputDir = join17(planDir, "video-frames", v.videoId);
8168
- if (!existsSync14(outputDir)) {
8192
+ if (!existsSync15(outputDir)) {
8169
8193
  throw new Error(
8170
8194
  `[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
8171
8195
  );
@@ -8281,7 +8305,7 @@ async function extractDeferredVideoFramesForChunk(input) {
8281
8305
  const range = video.deferredRange;
8282
8306
  const remoteUrl = remoteSources[range.sourcePath];
8283
8307
  const sourcePath = remoteUrl ?? join17(input.planDir, ...range.sourcePath.split("/"));
8284
- if (remoteUrl === void 0 && !existsSync14(sourcePath)) {
8308
+ if (remoteUrl === void 0 && !existsSync15(sourcePath)) {
8285
8309
  throw new RenderChunkValidationError(
8286
8310
  MISSING_PLAN_ARTIFACT,
8287
8311
  `[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
@@ -8499,7 +8523,7 @@ async function readPlanReferenceBackground(input) {
8499
8523
  }
8500
8524
 
8501
8525
  // src/services/render/stages/freezePlan.ts
8502
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
8526
+ import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync6 } from "node:fs";
8503
8527
  import { join as join20, relative as relative3, resolve as resolve8 } from "node:path";
8504
8528
  import {
8505
8529
  REFERENCE_BACKGROUND_FIELD,
@@ -8693,7 +8717,7 @@ function planFileSha256(absolutePath, digests) {
8693
8717
  return sha256;
8694
8718
  }
8695
8719
  async function prehashPlanFiles(dir, digests) {
8696
- if (!existsSync15(dir)) return;
8720
+ if (!existsSync16(dir)) return;
8697
8721
  for (const { absolutePath } of listPlanFiles(dir)) {
8698
8722
  if (!digests.has(absolutePath)) digests.set(absolutePath, await sha256FileAsync(absolutePath));
8699
8723
  }
@@ -8759,19 +8783,19 @@ var PLAN_REMOTE_FILES_RELATIVE_PATH = "meta/remote-files.json";
8759
8783
  function writePlanRemoteFiles(planDir, files) {
8760
8784
  if (files.length === 0) return;
8761
8785
  const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : 1);
8762
- writeFileSync5(join20(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
8786
+ writeFileSync6(join20(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
8763
8787
  }
8764
8788
  function readPlanRemoteFiles(planDir) {
8765
8789
  const path = join20(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
8766
- return existsSync15(path) ? JSON.parse(readFileSync7(path, "utf-8")) : [];
8790
+ return existsSync16(path) ? JSON.parse(readFileSync7(path, "utf-8")) : [];
8767
8791
  }
8768
8792
  function recomputePlanHashFromPlanDir(planDir, digests) {
8769
8793
  const planJsonPath = join20(planDir, "plan.json");
8770
8794
  const encoderJsonPath = join20(planDir, "meta", "encoder.json");
8771
- if (!existsSync15(planJsonPath)) {
8795
+ if (!existsSync16(planJsonPath)) {
8772
8796
  throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
8773
8797
  }
8774
- if (!existsSync15(encoderJsonPath)) {
8798
+ if (!existsSync16(encoderJsonPath)) {
8775
8799
  throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
8776
8800
  }
8777
8801
  const planJson = JSON.parse(readFileSync7(planJsonPath, "utf-8"));
@@ -8801,12 +8825,12 @@ async function freezePlan(input) {
8801
8825
  hasAudio,
8802
8826
  digests
8803
8827
  } = input;
8804
- if (!existsSync15(planDir)) {
8828
+ if (!existsSync16(planDir)) {
8805
8829
  throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
8806
8830
  }
8807
8831
  const metaDir = join20(planDir, "meta");
8808
- if (!existsSync15(metaDir)) mkdirSync7(metaDir, { recursive: true });
8809
- writeFileSync5(
8832
+ if (!existsSync16(metaDir)) mkdirSync7(metaDir, { recursive: true });
8833
+ writeFileSync6(
8810
8834
  join20(metaDir, "composition.json"),
8811
8835
  `${JSON.stringify(composition, null, 2)}
8812
8836
  `,
@@ -8814,8 +8838,8 @@ async function freezePlan(input) {
8814
8838
  );
8815
8839
  const encoderForCanonical = stripUndefined(encoder);
8816
8840
  const encoderConfigCanonicalJson = canonicalJsonStringify(encoderForCanonical);
8817
- writeFileSync5(join20(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
8818
- writeFileSync5(join20(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
8841
+ writeFileSync6(join20(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
8842
+ writeFileSync6(join20(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
8819
8843
  `, "utf-8");
8820
8844
  const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
8821
8845
  const planHash = computePlanHash({
@@ -8841,7 +8865,7 @@ async function freezePlan(input) {
8841
8865
  hasAudio
8842
8866
  };
8843
8867
  const planJsonPath = join20(planDir, "plan.json");
8844
- writeFileSync5(planJsonPath, `${JSON.stringify(planJson, null, 2)}
8868
+ writeFileSync6(planJsonPath, `${JSON.stringify(planJson, null, 2)}
8845
8869
  `, "utf-8");
8846
8870
  return { planJsonPath, planHash };
8847
8871
  }
@@ -8882,8 +8906,8 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
8882
8906
  }
8883
8907
 
8884
8908
  // src/services/distributed/chunkAudio.ts
8885
- import { closeSync as closeSync4, existsSync as existsSync16, ftruncateSync, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as readFileSync8 } from "node:fs";
8886
- import { writeFileSync as writeFileSync6, writeSync } from "node:fs";
8909
+ import { closeSync as closeSync4, existsSync as existsSync17, ftruncateSync, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as readFileSync8 } from "node:fs";
8910
+ import { writeFileSync as writeFileSync7, writeSync } from "node:fs";
8887
8911
  import { join as join22 } from "node:path";
8888
8912
  import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
8889
8913
 
@@ -9365,15 +9389,15 @@ async function writeChunkAudioSlices(input) {
9365
9389
  const start = offsets[cut.firstPacket];
9366
9390
  layout.partStarts[index] = start;
9367
9391
  const end = offsets[last] + track.sizes[last];
9368
- writeFileSync6(join22(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
9392
+ writeFileSync7(join22(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
9369
9393
  });
9370
9394
  const frame = [bytes.subarray(0, payloadStart), bytes.subarray(payloadEnd)];
9371
- writeFileSync6(join22(input.planDir, SLICE_FRAME), Buffer.concat(frame));
9372
- writeFileSync6(join22(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
9395
+ writeFileSync7(join22(input.planDir, SLICE_FRAME), Buffer.concat(frame));
9396
+ writeFileSync7(join22(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
9373
9397
  }
9374
9398
  function chunkAudioSource(planDir, chunkIndex, scratchPath) {
9375
9399
  const layoutPath = join22(planDir, SLICE_LAYOUT);
9376
- if (!existsSync16(layoutPath)) return resolvePlanAudioPath(planDir);
9400
+ if (!existsSync17(layoutPath)) return resolvePlanAudioPath(planDir);
9377
9401
  const layout = JSON.parse(readFileSync8(layoutPath, "utf-8"));
9378
9402
  const frame = readFileSync8(join22(planDir, SLICE_FRAME));
9379
9403
  const fd = openSync4(scratchPath, "w");
@@ -9937,7 +9961,7 @@ function createVideoSourceShipper(input) {
9937
9961
  let result = null;
9938
9962
  for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
9939
9963
  const rel = pathInside(root, srcPath);
9940
- if (rel !== null && (existsSync17(join24(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
9964
+ if (rel !== null && (existsSync18(join24(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
9941
9965
  result = ["compiled", ...rel.split(sep2)].join("/");
9942
9966
  break;
9943
9967
  }
@@ -9965,7 +9989,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
9965
9989
  useGpu: false,
9966
9990
  browserGpuMode: "software"
9967
9991
  });
9968
- if (!existsSync17(planDir)) mkdirSync10(planDir, { recursive: true });
9992
+ if (!existsSync18(planDir)) mkdirSync10(planDir, { recursive: true });
9969
9993
  const log = config.logger ?? defaultLogger;
9970
9994
  const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
9971
9995
  const abortSignal = config.abortSignal;
@@ -9995,7 +10019,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
9995
10019
  });
9996
10020
  const entryFile = config.entryFile ?? "index.html";
9997
10021
  const htmlPath = join24(projectDir, entryFile);
9998
- if (!existsSync17(htmlPath)) {
10022
+ if (!existsSync18(htmlPath)) {
9999
10023
  throw new Error(`[plan] entry file not found: ${htmlPath}`);
10000
10024
  }
10001
10025
  const workDir = join24(planDir, ".plan-work");
@@ -10039,7 +10063,6 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
10039
10063
  urlByAbsentLocalPath,
10040
10064
  projectDir,
10041
10065
  workDir,
10042
- htmlPath,
10043
10066
  entryFile,
10044
10067
  job,
10045
10068
  cfg,
@@ -10224,15 +10247,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
10224
10247
  logPlanStage(log, "extract", stageStarted);
10225
10248
  const stagedVideoFrames = join24(compiledDir, "__hyperframes_video_frames");
10226
10249
  const videoFramesDst = join24(planDir, "video-frames");
10227
- if (existsSync17(videoFramesDst)) rmSync7(videoFramesDst, { recursive: true, force: true });
10250
+ if (existsSync18(videoFramesDst)) rmSync7(videoFramesDst, { recursive: true, force: true });
10228
10251
  const videoSourcesDir = join24(planDir, "video-sources");
10229
10252
  rmSync7(videoSourcesDir, { recursive: true, force: true });
10230
- if (existsSync17(stagedVideoFrames)) {
10253
+ if (existsSync18(stagedVideoFrames)) {
10231
10254
  renameWithRetry8(stagedVideoFrames, videoFramesDst);
10232
10255
  } else {
10233
10256
  mkdirSync10(videoFramesDst, { recursive: true });
10234
10257
  }
10235
- if (existsSync17(finalCompiledDir)) rmSync7(finalCompiledDir, { recursive: true, force: true });
10258
+ if (existsSync18(finalCompiledDir)) rmSync7(finalCompiledDir, { recursive: true, force: true });
10236
10259
  renameWithRetry8(compiledDir, finalCompiledDir);
10237
10260
  const shipVideoSource = createVideoSourceShipper({
10238
10261
  planDir,
@@ -10271,7 +10294,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
10271
10294
  sizeBytes
10272
10295
  }))
10273
10296
  );
10274
- writeFileSync7(
10297
+ writeFileSync8(
10275
10298
  join24(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
10276
10299
  JSON.stringify(planVideosJson, null, 2),
10277
10300
  "utf-8"
@@ -10352,10 +10375,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
10352
10375
  applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
10353
10376
  }
10354
10377
  const planAudioPath = join24(planDir, PLAN_AUDIO_RELATIVE_PATH);
10355
- if (audioResult?.hasAudio && existsSync17(audioResult.audioOutputPath)) {
10378
+ if (audioResult?.hasAudio && existsSync18(audioResult.audioOutputPath)) {
10356
10379
  renameWithRetry8(audioResult.audioOutputPath, planAudioPath);
10357
10380
  }
10358
- if (chunksCarryAudio && existsSync17(planAudioPath)) {
10381
+ if (chunksCarryAudio && existsSync18(planAudioPath)) {
10359
10382
  if (options.sliceChunkAudio) {
10360
10383
  stageStarted = Date.now();
10361
10384
  await writeChunkAudioSlices({
@@ -10468,7 +10491,7 @@ async function buildPlanAudio(projectDir, config, workRoot, outputPath, options
10468
10491
  if (mixed.audioError) {
10469
10492
  applyDistributedAudioWarningPolicy(c.job, mixed.audioError, mixed.audioFailures, c.log);
10470
10493
  }
10471
- if (!mixed.hasAudio || !existsSync17(mixed.audioOutputPath)) return { hasAudio: false };
10494
+ if (!mixed.hasAudio || !existsSync18(mixed.audioOutputPath)) return { hasAudio: false };
10472
10495
  renameWithRetry8(mixed.audioOutputPath, outputPath);
10473
10496
  return { hasAudio: true };
10474
10497
  } finally {
@@ -10483,7 +10506,7 @@ import {
10483
10506
  } from "@hyperframes/engine";
10484
10507
  import {
10485
10508
  copyFileSync as copyFileSync8,
10486
- existsSync as existsSync19,
10509
+ existsSync as existsSync20,
10487
10510
  lstatSync as lstatSync2,
10488
10511
  mkdirSync as mkdirSync12,
10489
10512
  mkdtempSync as mkdtempSync4,
@@ -10491,7 +10514,7 @@ import {
10491
10514
  readdirSync as readdirSync7,
10492
10515
  rmSync as rmSync9,
10493
10516
  statSync as statSync7,
10494
- writeFileSync as writeFileSync9
10517
+ writeFileSync as writeFileSync10
10495
10518
  } from "node:fs";
10496
10519
  import { tmpdir } from "node:os";
10497
10520
  import { dirname as dirname13, isAbsolute as isAbsolute4, join as join27, relative as relative6, resolve as resolve9, sep as sep3 } from "node:path";
@@ -10501,13 +10524,13 @@ import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as r
10501
10524
  // src/services/distributed/planV2Publisher.ts
10502
10525
  import {
10503
10526
  copyFileSync as copyFileSync7,
10504
- existsSync as existsSync18,
10527
+ existsSync as existsSync19,
10505
10528
  linkSync as linkSync2,
10506
10529
  mkdirSync as mkdirSync11,
10507
10530
  mkdtempSync as mkdtempSync3,
10508
10531
  rmSync as rmSync8,
10509
10532
  statSync as statSync6,
10510
- writeFileSync as writeFileSync8
10533
+ writeFileSync as writeFileSync9
10511
10534
  } from "node:fs";
10512
10535
  import { renameWithRetry as renameWithRetry9 } from "@hyperframes/engine";
10513
10536
  import { dirname as dirname12, join as join26 } from "node:path";
@@ -10570,7 +10593,7 @@ var LocalPlanV2ArtifactPublisher = class {
10570
10593
  #linkFile;
10571
10594
  #committed = false;
10572
10595
  constructor(destinationDir, options = {}) {
10573
- if (existsSync18(destinationDir)) {
10596
+ if (existsSync19(destinationDir)) {
10574
10597
  throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
10575
10598
  }
10576
10599
  this.destinationDir = destinationDir;
@@ -10587,7 +10610,7 @@ var LocalPlanV2ArtifactPublisher = class {
10587
10610
  );
10588
10611
  }
10589
10612
  const destinationPath = planV2BlobPath(this.temporaryDir, digest);
10590
- if (existsSync18(destinationPath)) return;
10613
+ if (existsSync19(destinationPath)) return;
10591
10614
  mkdirSync11(dirname12(destinationPath), { recursive: true });
10592
10615
  const stagingDir = mkdtempSync3(join26(dirname12(destinationPath), ".plan-v2-blob-"));
10593
10616
  const temporaryPath = join26(stagingDir, "blob");
@@ -10605,13 +10628,13 @@ var LocalPlanV2ArtifactPublisher = class {
10605
10628
  }
10606
10629
  async commitManifest(manifestBytes) {
10607
10630
  for (const digest of new Set(manifestDigests(manifestBytes))) {
10608
- if (!existsSync18(planV2BlobPath(this.temporaryDir, digest))) {
10631
+ if (!existsSync19(planV2BlobPath(this.temporaryDir, digest))) {
10609
10632
  throw new PlanV2IntegrityError(
10610
10633
  `cannot commit manifest before referenced blob is durable: ${digest}`
10611
10634
  );
10612
10635
  }
10613
10636
  }
10614
- writeFileSync8(join26(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
10637
+ writeFileSync9(join26(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
10615
10638
  renameWithRetry9(this.temporaryDir, this.destinationDir);
10616
10639
  this.#committed = true;
10617
10640
  }
@@ -10684,7 +10707,7 @@ function assertValidExtractionCacheCompleteSentinel(path) {
10684
10707
  }
10685
10708
  function validateExtractionCacheCompleteSentinels(executionPlanDir) {
10686
10709
  const videoRoot = join27(executionPlanDir, "video-frames");
10687
- if (!existsSync19(videoRoot)) return;
10710
+ if (!existsSync20(videoRoot)) return;
10688
10711
  for (const videoEntry of readdirSync7(videoRoot, { withFileTypes: true })) {
10689
10712
  if (!videoEntry.isDirectory()) continue;
10690
10713
  const videoDir = join27(videoRoot, videoEntry.name);
@@ -10785,7 +10808,7 @@ function parsePlanVideosJson2(value) {
10785
10808
  }
10786
10809
  function materializeExtractedVideoDirectories(planDir) {
10787
10810
  const videosPath = join27(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
10788
- if (!existsSync19(videosPath)) return;
10811
+ if (!existsSync20(videosPath)) return;
10789
10812
  const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
10790
10813
  for (const video of videos.extracted) {
10791
10814
  mkdirSync12(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
@@ -10819,9 +10842,9 @@ function parseChunkSlices(value) {
10819
10842
  }
10820
10843
  function buildVideoChunkDependencies(executionPlanDir, dimensions) {
10821
10844
  const videoRoot = join27(executionPlanDir, "video-frames");
10822
- const hasExtractedFrames = existsSync19(videoRoot) && listFiles(videoRoot).length > 0;
10845
+ const hasExtractedFrames = existsSync20(videoRoot) && listFiles(videoRoot).length > 0;
10823
10846
  const videosPath = join27(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
10824
- if (!existsSync19(videosPath)) {
10847
+ if (!existsSync20(videosPath)) {
10825
10848
  return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
10826
10849
  }
10827
10850
  const chunksPath = join27(executionPlanDir, "meta", "chunks.json");
@@ -10878,7 +10901,7 @@ function computeManifestHash(manifest) {
10878
10901
  return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
10879
10902
  }
10880
10903
  function writeBlob(sourcePath, destinationPath) {
10881
- if (existsSync19(destinationPath)) return;
10904
+ if (existsSync20(destinationPath)) return;
10882
10905
  mkdirSync12(dirname13(destinationPath), { recursive: true });
10883
10906
  const temporaryDir = mkdtempSync4(join27(dirname13(destinationPath), ".plan-v2-blob-"));
10884
10907
  const temporaryPath = join27(temporaryDir, "blob");
@@ -10918,7 +10941,7 @@ async function putTasks(tasks) {
10918
10941
  }
10919
10942
  function buildPlanV2Publication(executionPlanDir, digests) {
10920
10943
  const executionPlanPath = join27(executionPlanDir, "plan.json");
10921
- if (!existsSync19(executionPlanPath)) {
10944
+ if (!existsSync20(executionPlanPath)) {
10922
10945
  throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
10923
10946
  }
10924
10947
  const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
@@ -10998,7 +11021,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
10998
11021
  };
10999
11022
  }
11000
11023
  function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
11001
- if (existsSync19(planV2Dir)) {
11024
+ if (existsSync20(planV2Dir)) {
11002
11025
  throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
11003
11026
  }
11004
11027
  const publication = buildPlanV2Publication(executionPlanDir);
@@ -11011,7 +11034,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
11011
11034
  for (const blob of publication.blobs) {
11012
11035
  writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
11013
11036
  }
11014
- writeFileSync9(
11037
+ writeFileSync10(
11015
11038
  join27(tempDir, "plan.json"),
11016
11039
  canonicalJsonStringify(publication.manifest),
11017
11040
  "utf-8"
@@ -11084,7 +11107,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
11084
11107
  }
11085
11108
  }
11086
11109
  async function planV2(projectDir, config, planV2Dir) {
11087
- if (existsSync19(planV2Dir)) {
11110
+ if (existsSync20(planV2Dir)) {
11088
11111
  throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
11089
11112
  }
11090
11113
  const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
@@ -11238,7 +11261,7 @@ function parsePlanV2Manifest(value) {
11238
11261
  }
11239
11262
  function readPlanV2Manifest(planV2Dir) {
11240
11263
  const manifestPath = join27(planV2Dir, "plan.json");
11241
- if (!existsSync19(manifestPath)) {
11264
+ if (!existsSync20(manifestPath)) {
11242
11265
  throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
11243
11266
  }
11244
11267
  const value = readJsonFile(manifestPath, "v2 manifest");
@@ -11257,7 +11280,7 @@ function listPlanV2ArtifactsForTarget(manifest, target) {
11257
11280
  }
11258
11281
  function verifyBlob(planV2Dir, artifact) {
11259
11282
  const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
11260
- if (!existsSync19(sourcePath)) {
11283
+ if (!existsSync20(sourcePath)) {
11261
11284
  throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
11262
11285
  }
11263
11286
  const stats = statSync7(sourcePath);
@@ -11270,7 +11293,7 @@ function verifyBlob(planV2Dir, artifact) {
11270
11293
  return sourcePath;
11271
11294
  }
11272
11295
  function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
11273
- if (existsSync19(destinationDir)) {
11296
+ if (existsSync20(destinationDir)) {
11274
11297
  throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
11275
11298
  }
11276
11299
  const manifest = readPlanV2Manifest(planV2Dir);
@@ -11291,7 +11314,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
11291
11314
  copyFileSync8(sourcePath, destinationPath);
11292
11315
  }
11293
11316
  if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
11294
- writeFileSync9(
11317
+ writeFileSync10(
11295
11318
  join27(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
11296
11319
  canonicalJsonStringify({
11297
11320
  manifest,
@@ -11335,7 +11358,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
11335
11358
  }
11336
11359
  function readValidatedPlanV2Materialization(planDir, expectedTarget) {
11337
11360
  const markerPath = join27(planDir, PLAN_V2_MATERIALIZATION_MARKER);
11338
- if (!existsSync19(markerPath)) return null;
11361
+ if (!existsSync20(markerPath)) return null;
11339
11362
  const marker = readJsonFile(markerPath, "materialization marker");
11340
11363
  if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
11341
11364
  throw new PlanV2IntegrityError("malformed materialization marker");
@@ -11353,7 +11376,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
11353
11376
  for (const artifact of required) {
11354
11377
  if (Object.hasOwn(remoteSources, artifact.path)) continue;
11355
11378
  const path = join27(planDir, ...artifact.path.split("/"));
11356
- if (!existsSync19(path) || statSync7(path).size !== artifact.sizeBytes) {
11379
+ if (!existsSync20(path) || statSync7(path).size !== artifact.sizeBytes) {
11357
11380
  throw new PlanV2IntegrityError(
11358
11381
  `materialized artifact missing or truncated: ${artifact.path}`
11359
11382
  );
@@ -11377,14 +11400,14 @@ import {
11377
11400
  } from "@hyperframes/engine";
11378
11401
  import {
11379
11402
  cpSync as cpSync3,
11380
- existsSync as existsSync20,
11403
+ existsSync as existsSync21,
11381
11404
  mkdirSync as mkdirSync13,
11382
11405
  mkdtempSync as mkdtempSync5,
11383
11406
  readFileSync as readFileSync10,
11384
11407
  readdirSync as readdirSync8,
11385
11408
  rmSync as rmSync10,
11386
11409
  statSync as statSync8,
11387
- writeFileSync as writeFileSync10
11410
+ writeFileSync as writeFileSync11
11388
11411
  } from "node:fs";
11389
11412
  import { dirname as dirname14, join as join28 } from "node:path";
11390
11413
  import {
@@ -11432,7 +11455,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
11432
11455
  }
11433
11456
  const format = plan.dimensions.format;
11434
11457
  const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
11435
- if (!existsSync20(dirname14(outputPath))) {
11458
+ if (!existsSync21(dirname14(outputPath))) {
11436
11459
  mkdirSync13(dirname14(outputPath), { recursive: true });
11437
11460
  }
11438
11461
  const workDir = mkdtempSync5(`${outputPath}.assemble-work-`);
@@ -11456,7 +11479,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
11456
11479
  try {
11457
11480
  const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
11458
11481
  const finalPath = join28(workDir, `output.${format}`);
11459
- const audio = audioPath !== null && existsSync20(audioPath) ? audioPath : null;
11482
+ const audio = audioPath !== null && existsSync21(audioPath) ? audioPath : null;
11460
11483
  await writeJoined(ctx, videoSources, audio, finalPath);
11461
11484
  renameWithRetry11(finalPath, outputPath);
11462
11485
  log.info("[assemble] stage timings", {
@@ -11474,7 +11497,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
11474
11497
  });
11475
11498
  }
11476
11499
  }
11477
- const fileSize = existsSync20(outputPath) ? statSync8(outputPath).size : 0;
11500
+ const fileSize = existsSync21(outputPath) ? statSync8(outputPath).size : 0;
11478
11501
  return {
11479
11502
  ...referenceBackgroundResult6(plan[REFERENCE_BACKGROUND_FIELD4]),
11480
11503
  outputPath,
@@ -11486,13 +11509,13 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
11486
11509
  function readAssemblePlan(planDir, chunkPaths) {
11487
11510
  const planJsonPath = join28(planDir, "plan.json");
11488
11511
  const chunksJsonPath = join28(planDir, "meta", "chunks.json");
11489
- if (!existsSync20(planJsonPath)) {
11512
+ if (!existsSync21(planJsonPath)) {
11490
11513
  throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
11491
11514
  }
11492
11515
  const plan = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
11493
11516
  readPlanProtocolV1(plan, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
11494
11517
  validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
11495
- if (!existsSync20(chunksJsonPath)) {
11518
+ if (!existsSync21(chunksJsonPath)) {
11496
11519
  throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
11497
11520
  }
11498
11521
  const chunks = JSON.parse(readFileSync10(chunksJsonPath, "utf-8"));
@@ -11507,7 +11530,7 @@ function readAssemblePlan(planDir, chunkPaths) {
11507
11530
  }
11508
11531
  }
11509
11532
  for (const path of chunkPaths.flat()) {
11510
- if (!existsSync20(path)) {
11533
+ if (!existsSync21(path)) {
11511
11534
  throw new Error(`[assemble] chunk path does not exist: ${path}`);
11512
11535
  }
11513
11536
  }
@@ -11605,7 +11628,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
11605
11628
  function concatDemuxerInput(chunkPaths, workDir, fpsArg) {
11606
11629
  const listPath = join28(workDir, "concat-list.txt");
11607
11630
  const body = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
11608
- writeFileSync10(listPath, `${body}
11631
+ writeFileSync11(listPath, `${body}
11609
11632
  `, "utf-8");
11610
11633
  return ["-r", fpsArg, "-f", "concat", "-safe", "0", "-i", listPath];
11611
11634
  }
@@ -11646,7 +11669,7 @@ function planFormat(planDir) {
11646
11669
  }
11647
11670
  function lockedAudio(planDir) {
11648
11671
  const encoderJsonPath = join28(planDir, "meta", "encoder.json");
11649
- if (!existsSync20(encoderJsonPath)) return {};
11672
+ if (!existsSync21(encoderJsonPath)) return {};
11650
11673
  return JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
11651
11674
  }
11652
11675
  function readCfrEncoder(planDir, format) {
@@ -11656,7 +11679,7 @@ function readCfrEncoder(planDir, format) {
11656
11679
  );
11657
11680
  }
11658
11681
  const encoderJsonPath = join28(planDir, "meta", "encoder.json");
11659
- if (!existsSync20(encoderJsonPath)) {
11682
+ if (!existsSync21(encoderJsonPath)) {
11660
11683
  throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
11661
11684
  }
11662
11685
  const encoderJson = JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
@@ -11668,7 +11691,7 @@ function readCfrEncoder(planDir, format) {
11668
11691
  return encoderJson;
11669
11692
  }
11670
11693
  function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
11671
- if (existsSync20(outputPath)) rmSync10(outputPath, { recursive: true, force: true });
11694
+ if (existsSync21(outputPath)) rmSync10(outputPath, { recursive: true, force: true });
11672
11695
  mkdirSync13(outputPath, { recursive: true });
11673
11696
  let globalIdx = 0;
11674
11697
  for (const chunkDir of chunkPaths) {
@@ -11692,7 +11715,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
11692
11715
  `[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
11693
11716
  );
11694
11717
  }
11695
- if (audioPath !== null && existsSync20(audioPath)) {
11718
+ if (audioPath !== null && existsSync21(audioPath)) {
11696
11719
  const sidecar = join28(outputPath, MIXED_AUDIO_FILENAME4);
11697
11720
  cpSync3(audioPath, sidecar);
11698
11721
  }
@@ -11713,7 +11736,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
11713
11736
 
11714
11737
  // src/services/distributed/renderChunk.ts
11715
11738
  import { randomBytes } from "node:crypto";
11716
- import { existsSync as existsSync21, mkdirSync as mkdirSync14, readFileSync as readFileSync11, readdirSync as readdirSync9, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
11739
+ import { existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync11, readdirSync as readdirSync9, rmSync as rmSync11, writeFileSync as writeFileSync12 } from "node:fs";
11717
11740
  import { cpus } from "node:os";
11718
11741
  import { join as join29 } from "node:path";
11719
11742
  import {
@@ -11859,7 +11882,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
11859
11882
  const planJsonPath = join29(planDir, "plan.json");
11860
11883
  const encoderJsonPath = join29(planDir, "meta", "encoder.json");
11861
11884
  const chunksJsonPath = join29(planDir, "meta", "chunks.json");
11862
- if (!existsSync21(planJsonPath)) {
11885
+ if (!existsSync22(planJsonPath)) {
11863
11886
  throw new RenderChunkValidationError(
11864
11887
  MISSING_PLAN_ARTIFACT,
11865
11888
  `[renderChunk] planDir is missing required artifact: ${planJsonPath}`
@@ -11875,7 +11898,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
11875
11898
  const v2Manifest = v2Materialization?.manifest ?? null;
11876
11899
  const remoteSources = v2Materialization?.remoteSources ?? {};
11877
11900
  for (const required of [encoderJsonPath, chunksJsonPath]) {
11878
- if (!existsSync21(required)) {
11901
+ if (!existsSync22(required)) {
11879
11902
  throw new RenderChunkValidationError(
11880
11903
  MISSING_PLAN_ARTIFACT,
11881
11904
  `[renderChunk] planDir is missing required artifact: ${required}`
@@ -11886,7 +11909,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
11886
11909
  const chunks = JSON.parse(readFileSync11(chunksJsonPath, "utf-8"));
11887
11910
  const videosJsonPath = join29(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
11888
11911
  let planVideos = null;
11889
- if (existsSync21(videosJsonPath)) {
11912
+ if (existsSync22(videosJsonPath)) {
11890
11913
  try {
11891
11914
  planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync11(videosJsonPath, "utf-8")));
11892
11915
  } catch (err) {
@@ -11917,7 +11940,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
11917
11940
  const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
11918
11941
  const framesInChunk = range.endFrame - range.startFrame;
11919
11942
  const compiledDir = join29(planDir, "compiled");
11920
- if (!existsSync21(compiledDir)) {
11943
+ if (!existsSync22(compiledDir)) {
11921
11944
  throw new RenderChunkValidationError(
11922
11945
  MISSING_PLAN_ARTIFACT,
11923
11946
  `[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
@@ -12167,10 +12190,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
12167
12190
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
12168
12191
  const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
12169
12192
  if (isPngSequence) {
12170
- if (!existsSync21(outputChunkPath)) mkdirSync14(outputChunkPath, { recursive: true });
12193
+ if (!existsSync22(outputChunkPath)) mkdirSync14(outputChunkPath, { recursive: true });
12171
12194
  } else {
12172
12195
  const outDir = join29(outputChunkPath, "..");
12173
- if (!existsSync21(outDir)) mkdirSync14(outDir, { recursive: true });
12196
+ if (!existsSync22(outDir)) mkdirSync14(outDir, { recursive: true });
12174
12197
  }
12175
12198
  const encodeInput = {
12176
12199
  job,
@@ -12380,7 +12403,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
12380
12403
  producerVersion: plan.producerVersion,
12381
12404
  ffmpegVersion
12382
12405
  };
12383
- writeFileSync11(perfPath, `${JSON.stringify(perfPayload, null, 2)}
12406
+ writeFileSync12(perfPath, `${JSON.stringify(perfPayload, null, 2)}
12384
12407
  `, "utf-8");
12385
12408
  try {
12386
12409
  rmSync11(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
@@ -12797,7 +12820,7 @@ var isMissing = (result) => result.error?.code === "ENOENT";
12797
12820
  var ffmpegIsMissing = async (signal) => isMissing(await runFfmpeg9(["-version"], { signal }));
12798
12821
 
12799
12822
  // src/services/distributed/probeFacts.ts
12800
- import { writeFileSync as writeFileSync12 } from "node:fs";
12823
+ import { writeFileSync as writeFileSync13 } from "node:fs";
12801
12824
  import { extname as extname6 } from "node:path";
12802
12825
  import { buildProbeFactsText } from "@hyperframes/engine";
12803
12826
  var PROBE_FACTS_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".mov", ".m4v", ".webm", ".mkv"]);
@@ -12806,7 +12829,7 @@ function wantsProbeFacts(path) {
12806
12829
  }
12807
12830
  async function buildProbeFacts(inputPath, outputPath, signal) {
12808
12831
  try {
12809
- writeFileSync12(outputPath, await buildProbeFactsText(inputPath, signal));
12832
+ writeFileSync13(outputPath, await buildProbeFactsText(inputPath, signal));
12810
12833
  return "built";
12811
12834
  } catch (error) {
12812
12835
  if (signal?.aborted) throw error;