@hyperframes/producer 0.9.14 → 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.
package/dist/index.js CHANGED
@@ -9,12 +9,12 @@ import {
9
9
  // src/services/renderOrchestrator.ts
10
10
  import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
11
11
  import {
12
- existsSync as existsSync18,
12
+ existsSync as existsSync20,
13
13
  mkdirSync as mkdirSync10,
14
14
  readdirSync as readdirSync5,
15
15
  rmSync as rmSync10,
16
16
  statSync as statSync8,
17
- writeFileSync as writeFileSync6,
17
+ writeFileSync as writeFileSync7,
18
18
  copyFileSync as copyFileSync5,
19
19
  appendFileSync
20
20
  } from "fs";
@@ -29,9 +29,7 @@ import {
29
29
  toFps as toFps2
30
30
  } from "@hyperframes/core";
31
31
  import { HF_AUDIO_GROUP_TAG } from "@hyperframes/core/audio-groups";
32
- import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
33
32
  import { HTML_BODY_CSS_HEIGHT_FIRST_RE, HTML_BODY_CSS_WIDTH_FIRST_RE } from "@hyperframes/parsers";
34
- import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
35
33
  import {
36
34
  extractMediaMetadata as extractMediaMetadata4,
37
35
  resolveConfig as resolveConfig2,
@@ -62,7 +60,8 @@ import {
62
60
  isDrawElementCaptureError,
63
61
  getDrawElementVerificationDetails,
64
62
  augmentProtocolTimeoutError,
65
- augmentPageNavigationTimeoutError
63
+ augmentPageNavigationTimeoutError,
64
+ displaySize
66
65
  } from "@hyperframes/engine";
67
66
  import {
68
67
  referenceBackgroundResult,
@@ -5895,8 +5894,11 @@ function countAuthoredTimedClips(html) {
5895
5894
  }
5896
5895
 
5897
5896
  // src/services/render/stages/compileStage.ts
5897
+ import { existsSync as existsSync11, writeFileSync as writeFileSync5 } from "node:fs";
5898
5898
  import { join as join16 } from "node:path";
5899
5899
  import { suggestMatchingPreset } from "@hyperframes/core";
5900
+ import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
5901
+ import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
5900
5902
 
5901
5903
  // src/services/htmlCompiler.ts
5902
5904
  import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
@@ -8604,11 +8606,36 @@ function adaptAspectAgnosticResolution(requested, aspectAgnostic, width, height,
8604
8606
  });
8605
8607
  return flipped;
8606
8608
  }
8609
+ function resolveEntryHtmlPath(input) {
8610
+ const { projectDir, workDir, entryFile, log } = input;
8611
+ const htmlPath = join16(projectDir, entryFile);
8612
+ const rawEntry = readTextFile2(htmlPath);
8613
+ if (entryFile === "index.html" || !isTemplateEntry(rawEntry)) return htmlPath;
8614
+ const projectIndexPath = join16(projectDir, "index.html");
8615
+ if (!existsSync11(projectIndexPath)) {
8616
+ throw new Error(
8617
+ `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
8618
+ );
8619
+ }
8620
+ const standaloneHtml = extractStandaloneEntryFromIndex(
8621
+ readTextFile2(projectIndexPath),
8622
+ entryFile,
8623
+ rawEntry
8624
+ );
8625
+ if (!standaloneHtml) {
8626
+ throw new Error(
8627
+ `Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
8628
+ );
8629
+ }
8630
+ const wrapperPath = join16(workDir, "standalone-entry.html");
8631
+ writeFileSync5(wrapperPath, standaloneHtml, "utf-8");
8632
+ log.info("Extracted standalone entry from index.html host context", { entryFile });
8633
+ return wrapperPath;
8634
+ }
8607
8635
  async function runCompileStage(input) {
8608
8636
  const {
8609
8637
  projectDir,
8610
8638
  workDir,
8611
- htmlPath,
8612
8639
  entryFile,
8613
8640
  job,
8614
8641
  cfg,
@@ -8619,6 +8646,7 @@ async function runCompileStage(input) {
8619
8646
  allowSystemFontCapture,
8620
8647
  abortSignal
8621
8648
  } = input;
8649
+ const htmlPath = resolveEntryHtmlPath({ projectDir, workDir, entryFile, log });
8622
8650
  const compileStart = Date.now();
8623
8651
  const compiled = await compileForRender(projectDir, htmlPath, join16(workDir, "downloads"), {
8624
8652
  log,
@@ -9450,7 +9478,7 @@ function validateDistributedDuration(input) {
9450
9478
  }
9451
9479
 
9452
9480
  // src/services/render/stages/extractVideosStage.ts
9453
- import { existsSync as existsSync11 } from "node:fs";
9481
+ import { existsSync as existsSync12 } from "node:fs";
9454
9482
  import { join as join18 } from "node:path";
9455
9483
  import {
9456
9484
  classifyVideoExtractionError,
@@ -9725,7 +9753,7 @@ async function runExtractVideosStage(input) {
9725
9753
  const probeFailures = await Promise.all(
9726
9754
  composition.videos.map(async (v) => {
9727
9755
  const videoPath = resolveProjectRelativeSrc3(v.src, projectDir, compiledDir);
9728
- if (!existsSync11(videoPath)) return null;
9756
+ if (!existsSync12(videoPath)) return null;
9729
9757
  try {
9730
9758
  const attempted = extractionPolicy.maxTransientRetries === 0 ? { result: await extractMediaMetadata3(videoPath), retries: 0 } : await runVideoExtractionWithRetry(() => extractMediaMetadata3(videoPath), {
9731
9759
  signal: abortSignal,
@@ -9763,7 +9791,7 @@ async function runExtractVideosStage(input) {
9763
9791
  const probed = await Promise.all(
9764
9792
  composition.images.map(async (img) => {
9765
9793
  const imgPath = resolveProjectRelativeSrc3(img.src, projectDir, compiledDir);
9766
- if (!existsSync11(imgPath)) return null;
9794
+ if (!existsSync12(imgPath)) return null;
9767
9795
  const colorSpace = await probeColorSpaceSafely(imgPath, log);
9768
9796
  if (isHdrColorSpace2(colorSpace)) {
9769
9797
  nativeHdrImageIds.add(img.id);
@@ -9883,7 +9911,7 @@ async function appendVideoAudioFromProbe(input) {
9883
9911
  videos.map(async (video) => {
9884
9912
  const path = isHttpUrl2(video.src) ? video.src : resolveProjectRelativeSrc3(video.src, projectDir, compiledDir);
9885
9913
  const readFrom = urlByAbsentLocalPath?.get(path) ?? path;
9886
- if (!isHttpUrl2(readFrom) && !existsSync11(readFrom)) return null;
9914
+ if (!isHttpUrl2(readFrom) && !existsSync12(readFrom)) return null;
9887
9915
  const metadata = await extractMediaMetadata3(readFrom);
9888
9916
  const window2 = await resolveFinalFrameExtractionWindow2(
9889
9917
  readFrom,
@@ -9903,7 +9931,7 @@ async function appendVideoAudioFromProbe(input) {
9903
9931
  }
9904
9932
 
9905
9933
  // src/services/render/videoLayerPassthroughPlan.ts
9906
- import { existsSync as existsSync12 } from "node:fs";
9934
+ import { existsSync as existsSync13 } from "node:fs";
9907
9935
  import {
9908
9936
  buildVideoLayerInput,
9909
9937
  isHttpUrl as isHttpUrl3,
@@ -9971,7 +9999,7 @@ async function planVideoLayerPassthrough(input) {
9971
9999
  const { video } = q;
9972
10000
  if (isHttpUrl3(video.src)) return { reason: "remote_source" };
9973
10001
  const path = resolveProjectRelativeSrc4(video.src, input.projectDir, input.compiledDir);
9974
- if (!existsSync12(path)) return { reason: "source_missing" };
10002
+ if (!existsSync13(path)) return { reason: "source_missing" };
9975
10003
  const layout = await probeVideoLayout(input.page, video.id, input.canvas, input.duration);
9976
10004
  if ("reason" in layout) return layout;
9977
10005
  const layer = await buildVideoLayerInput(
@@ -9986,6 +10014,7 @@ async function planVideoLayerPassthrough(input) {
9986
10014
  }
9987
10015
 
9988
10016
  // src/services/render/stages/audioStage.ts
10017
+ import { existsSync as existsSync15 } from "node:fs";
9989
10018
  import { join as join21 } from "node:path";
9990
10019
  import {
9991
10020
  MIXED_AUDIO_FILENAME,
@@ -9996,7 +10025,7 @@ import {
9996
10025
  } from "@hyperframes/engine";
9997
10026
 
9998
10027
  // src/services/render/audioDeliveryLimit.ts
9999
- import { existsSync as existsSync13 } from "node:fs";
10028
+ import { existsSync as existsSync14 } from "node:fs";
10000
10029
  import { dirname as dirname9, join as join20 } from "node:path";
10001
10030
  import {
10002
10031
  applyAudioFxChain,
@@ -10119,8 +10148,9 @@ var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
10119
10148
  var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
10120
10149
  var AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB = 0.5;
10121
10150
  var MAX_TRUE_PEAK_CORRECTION_PASSES = 3;
10151
+ var gainFilter = (gainDb) => `volume=${gainDb.toFixed(3)}dB`;
10122
10152
  function aacTruePeakCorrectionFilter(targetDurationSeconds, attenuationDb) {
10123
- return `volume=${attenuationDb.toFixed(3)}dB,apad,atrim=0:${formatSeconds(targetDurationSeconds)}`;
10153
+ return `${gainFilter(attenuationDb)},apad,atrim=0:${formatSeconds(targetDurationSeconds)}`;
10124
10154
  }
10125
10155
  function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, targetDurationSeconds) {
10126
10156
  const delta = targetDurationSeconds - sourceDurationSeconds;
@@ -10158,7 +10188,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10158
10188
  const probeAudio = input.probeAudioInfo ?? defaultProbeAudioInfo;
10159
10189
  const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
10160
10190
  const runner = input.runFfmpeg ?? ((args) => defaultRunFfmpeg(args, timeoutMs, input.signal));
10161
- const encode = input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal);
10191
+ const encode = input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath);
10162
10192
  const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg || input.encodeAac ? void 0 : (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }));
10163
10193
  const [videoResult, audioResult] = await Promise.allSettled([
10164
10194
  probeVideo(input.videoPath),
@@ -10206,7 +10236,12 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10206
10236
  );
10207
10237
  try {
10208
10238
  for (const step of plan2.steps) {
10209
- const ffmpegResult = step.kind === "copy" ? await runner(step.args) : await encode(input.audioPath, input.outputPath, targetDurationSeconds, step.filter);
10239
+ const ffmpegResult = step.kind === "copy" ? await runner(step.args) : await encode(
10240
+ input.sourcePath ?? input.audioPath,
10241
+ input.outputPath,
10242
+ targetDurationSeconds,
10243
+ step.filter
10244
+ );
10210
10245
  if (!ffmpegResult.success) {
10211
10246
  return {
10212
10247
  success: false,
@@ -10235,6 +10270,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10235
10270
  if (probeTruePeak) {
10236
10271
  const correction = await enforceAacTruePeak({
10237
10272
  audioPath: input.outputPath,
10273
+ sourcePath: input.sourcePath,
10238
10274
  targetDurationSeconds,
10239
10275
  signal: input.signal,
10240
10276
  probeTruePeak,
@@ -10270,9 +10306,27 @@ async function constrainAacTruePeak(input) {
10270
10306
  targetDurationSeconds: input.targetDurationSeconds,
10271
10307
  signal: input.signal,
10272
10308
  probeTruePeak: input.probeTruePeakDbfs ?? ((audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal })),
10273
- encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
10309
+ encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath),
10310
+ rewriteSource: input.rewriteSource ?? ((sourcePath, outputPath, filter) => defaultRunFfmpeg(
10311
+ ["-i", sourcePath, "-af", filter, "-c:a", "pcm_f32le", "-y", outputPath],
10312
+ timeoutMs,
10313
+ input.signal
10314
+ ))
10274
10315
  });
10275
10316
  }
10317
+ async function cutSource(input, scratchDir, attenuationDb) {
10318
+ if (!input.sourcePath || !input.rewriteSource) return true;
10319
+ const cutPath = join19(scratchDir, "source.wav");
10320
+ try {
10321
+ const result = await input.rewriteSource(input.sourcePath, cutPath, gainFilter(attenuationDb));
10322
+ if (!result.success) throw new Error(result.error);
10323
+ renameWithRetry5(cutPath, input.sourcePath, { copyFallback: true });
10324
+ return true;
10325
+ } catch {
10326
+ removeScratch(input.sourcePath);
10327
+ return false;
10328
+ }
10329
+ }
10276
10330
  async function enforceAacTruePeak(input) {
10277
10331
  let scratchDir;
10278
10332
  try {
@@ -10292,8 +10346,10 @@ async function enforceAacTruePeak(input) {
10292
10346
  }
10293
10347
  if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
10294
10348
  if (measuredPath !== correctedPath) return { success: true };
10349
+ const sourceCut = await cutSource(input, scratchDir, attenuationDb);
10295
10350
  renameWithRetry5(correctedPath, input.audioPath, { copyFallback: true });
10296
- return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
10351
+ const loweredDb = Number((-attenuationDb).toFixed(2));
10352
+ return sourceCut ? { success: true, loweredDb } : { success: true, loweredDb, sourceStale: true };
10297
10353
  }
10298
10354
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
10299
10355
  const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
@@ -10303,7 +10359,7 @@ async function enforceAacTruePeak(input) {
10303
10359
  const referenceDbfs = Math.max(truePeakDbfs, (sourcePeakDbfs ?? -Infinity) + attenuationDb);
10304
10360
  attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - referenceDbfs;
10305
10361
  const result = await input.encode(
10306
- input.audioPath,
10362
+ input.sourcePath ?? input.audioPath,
10307
10363
  correctedPath,
10308
10364
  input.targetDurationSeconds,
10309
10365
  aacTruePeakCorrectionFilter(input.targetDurationSeconds, attenuationDb)
@@ -10476,7 +10532,7 @@ async function limitDeliveryPeaks(input, deps = {
10476
10532
  applyChain: applyAudioFxChain,
10477
10533
  encode: encodeAac2,
10478
10534
  rename: renameWithRetry6,
10479
- sourceExists: existsSync13
10535
+ sourceExists: existsSync14
10480
10536
  }) {
10481
10537
  const { audioPath, sourcePath, signal, timeoutMs } = input;
10482
10538
  if (!deps.sourceExists(sourcePath)) return { success: true };
@@ -10532,36 +10588,54 @@ async function runAudioStageScoped(input) {
10532
10588
  const stageStart = Date.now();
10533
10589
  const pcmSourcePath = join21(input.workDir, "audio-source.wav");
10534
10590
  const duration = input.totalFrames * input.fps.den / input.fps.num;
10591
+ let deliverySourcePath;
10535
10592
  try {
10536
10593
  const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
10537
10594
  let audioLoweredDb;
10538
10595
  if (result.hasAudio) {
10539
10596
  const guardStart = Date.now();
10540
- const limited = await limitDeliveryPeaks({
10541
- audioPath: result.audioOutputPath,
10542
- sourcePath: pcmSourcePath,
10543
- targetDurationSeconds: duration,
10544
- signal: input.abortSignal,
10545
- timeoutMs: input.ffmpegProcessTimeout,
10546
- warn: (message, details) => input.log?.warn?.(message, details)
10547
- });
10548
- if (!limited.success) throw encoderFailureError("Audio true-peak limit failed", limited);
10549
- const peak = await constrainAacTruePeak({
10550
- audioPath: result.audioOutputPath,
10551
- sourcePath: pcmSourcePath,
10552
- targetDurationSeconds: duration,
10553
- signal: input.abortSignal,
10554
- timeoutMs: input.ffmpegProcessTimeout
10555
- });
10556
- if (!peak.success) throw encoderFailureError("Audio true-peak check failed", peak);
10557
- audioLoweredDb = peak.loweredDb;
10597
+ const guard = await guardDeliveryPeaks(
10598
+ input,
10599
+ result.audioOutputPath,
10600
+ pcmSourcePath,
10601
+ duration
10602
+ );
10603
+ audioLoweredDb = guard.loweredDb;
10558
10604
  input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
10605
+ if (input.keepDeliverySource && !guard.sourceStale && existsSync15(pcmSourcePath)) {
10606
+ deliverySourcePath = pcmSourcePath;
10607
+ }
10559
10608
  }
10560
- return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
10609
+ return {
10610
+ ...result,
10611
+ audioLoweredDb,
10612
+ deliverySourcePath,
10613
+ audioProcessMs: Date.now() - stageStart
10614
+ };
10561
10615
  } finally {
10562
- removeScratch2(pcmSourcePath);
10616
+ if (!deliverySourcePath) removeScratch2(pcmSourcePath);
10563
10617
  }
10564
10618
  }
10619
+ async function guardDeliveryPeaks(input, audioPath, sourcePath, duration) {
10620
+ const limited = await limitDeliveryPeaks({
10621
+ audioPath,
10622
+ sourcePath,
10623
+ targetDurationSeconds: duration,
10624
+ signal: input.abortSignal,
10625
+ timeoutMs: input.ffmpegProcessTimeout,
10626
+ warn: (message, details) => input.log?.warn?.(message, details)
10627
+ });
10628
+ if (!limited.success) throw encoderFailureError("Audio true-peak limit failed", limited);
10629
+ const peak = await constrainAacTruePeak({
10630
+ audioPath,
10631
+ sourcePath,
10632
+ targetDurationSeconds: duration,
10633
+ signal: input.abortSignal,
10634
+ timeoutMs: input.ffmpegProcessTimeout
10635
+ });
10636
+ if (!peak.success) throw encoderFailureError("Audio true-peak check failed", peak);
10637
+ return { loweredDb: peak.loweredDb, sourceStale: peak.sourceStale };
10638
+ }
10565
10639
  async function mixAudioForDelivery(input, duration, pcmSourcePath) {
10566
10640
  const {
10567
10641
  projectDir,
@@ -10656,7 +10730,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
10656
10730
  }
10657
10731
 
10658
10732
  // src/services/render/stages/captureStage.ts
10659
- import { existsSync as existsSync14, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
10733
+ import { existsSync as existsSync16, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
10660
10734
  import { join as join22 } from "node:path";
10661
10735
  import {
10662
10736
  classifyCaptureFailure as classifyCaptureFailure2,
@@ -10727,7 +10801,7 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
10727
10801
  }
10728
10802
  function measureCaptureFrameBytes(workDir, framesDir) {
10729
10803
  let total = sampleDirectoryBytes(framesDir);
10730
- if (!existsSync14(workDir)) return total;
10804
+ if (!existsSync16(workDir)) return total;
10731
10805
  for (const attempt of readdirSync3(workDir)) {
10732
10806
  if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
10733
10807
  total += sampleDirectoryBytes(join22(workDir, attempt));
@@ -11030,7 +11104,7 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
11030
11104
  }
11031
11105
 
11032
11106
  // src/services/render/stages/captureHdrStage.ts
11033
- import { existsSync as existsSync16, mkdirSync as mkdirSync8 } from "node:fs";
11107
+ import { existsSync as existsSync18, mkdirSync as mkdirSync8 } from "node:fs";
11034
11108
  import { join as join26 } from "node:path";
11035
11109
  import {
11036
11110
  closeCaptureSession as closeCaptureSession7,
@@ -11276,7 +11350,7 @@ import { Worker } from "node:worker_threads";
11276
11350
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
11277
11351
  import { dirname as dirname10, join as join24 } from "node:path";
11278
11352
  import { createRequire } from "node:module";
11279
- import { existsSync as existsSync15 } from "node:fs";
11353
+ import { existsSync as existsSync17 } from "node:fs";
11280
11354
  import { cpus as cpus2 } from "node:os";
11281
11355
  function resolveWorkerEntry(explicit) {
11282
11356
  if (explicit && explicit.length > 0) {
@@ -11289,7 +11363,7 @@ function resolveWorkerEntry(explicit) {
11289
11363
  }
11290
11364
  const moduleDir = dirname10(fileURLToPath3(import.meta.url));
11291
11365
  const jsPath = join24(moduleDir, "shaderTransitionWorker.js");
11292
- if (existsSync15(jsPath)) return { path: jsPath, isTs: false };
11366
+ if (existsSync17(jsPath)) return { path: jsPath, isTs: false };
11293
11367
  const tsPath = join24(moduleDir, "shaderTransitionWorker.ts");
11294
11368
  return { path: tsPath, isTs: true };
11295
11369
  }
@@ -11850,7 +11924,7 @@ async function runCaptureHdrStage(input) {
11850
11924
  }
11851
11925
  const debugDumpEnabled = process.env.KEEP_TEMP === "1";
11852
11926
  const debugDumpDir = debugDumpEnabled ? join26(framesDir, "debug-composite") : null;
11853
- if (debugDumpDir && !existsSync16(debugDumpDir)) {
11927
+ if (debugDumpDir && !existsSync18(debugDumpDir)) {
11854
11928
  mkdirSync8(debugDumpDir, { recursive: true });
11855
11929
  }
11856
11930
  const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
@@ -12007,13 +12081,13 @@ async function runCaptureHdrStage(input) {
12007
12081
  // src/services/render/stages/encodeStage.ts
12008
12082
  import {
12009
12083
  copyFileSync as copyFileSync4,
12010
- existsSync as existsSync17,
12084
+ existsSync as existsSync19,
12011
12085
  mkdirSync as mkdirSync9,
12012
12086
  readdirSync as readdirSync4,
12013
12087
  readFileSync as readFileSync8,
12014
12088
  rmSync as rmSync9,
12015
12089
  statSync as statSync7,
12016
- writeFileSync as writeFileSync5
12090
+ writeFileSync as writeFileSync6
12017
12091
  } from "node:fs";
12018
12092
  import { readFile as readFile2 } from "node:fs/promises";
12019
12093
  import { dirname as dirname11, join as join27 } from "node:path";
@@ -12109,7 +12183,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
12109
12183
  const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
12110
12184
  return gifEncodeFailure(startTime, outputPath, error);
12111
12185
  }
12112
- writeFileSync5(outputPath, gif);
12186
+ writeFileSync6(outputPath, gif);
12113
12187
  return null;
12114
12188
  }
12115
12189
  async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
@@ -12141,7 +12215,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
12141
12215
  const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
12142
12216
  if (failure) return failure;
12143
12217
  }
12144
- const fileSize = existsSync17(outputPath) ? statSync7(outputPath).size : 0;
12218
+ const fileSize = existsSync19(outputPath) ? statSync7(outputPath).size : 0;
12145
12219
  return {
12146
12220
  success: true,
12147
12221
  outputPath,
@@ -12302,7 +12376,7 @@ async function runEncodeStage(input) {
12302
12376
  const stage5Start = Date.now();
12303
12377
  if (isPngSequence) {
12304
12378
  const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
12305
- if (!existsSync17(outputPath)) mkdirSync9(outputPath, { recursive: true });
12379
+ if (!existsSync19(outputPath)) mkdirSync9(outputPath, { recursive: true });
12306
12380
  const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
12307
12381
  if (captured.length === 0) {
12308
12382
  throw new Error(
@@ -12313,7 +12387,7 @@ async function runEncodeStage(input) {
12313
12387
  const dst = join27(outputPath, formatExportFrameName(i, "png"));
12314
12388
  copyFileSync4(join27(framesDir, name), dst);
12315
12389
  });
12316
- if (hasAudio && audioOutputPath && existsSync17(audioOutputPath)) {
12390
+ if (hasAudio && audioOutputPath && existsSync19(audioOutputPath)) {
12317
12391
  copyFileSync4(audioOutputPath, join27(outputPath, MIXED_AUDIO_FILENAME2));
12318
12392
  log.info(
12319
12393
  `[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
@@ -12424,6 +12498,7 @@ async function assembleOutput(input, onSecondsWritten) {
12424
12498
  const normalizeResult = await padOrTrimAudioToVideoFrameCount({
12425
12499
  videoPath: videoOnlyPath,
12426
12500
  audioPath: audioOutputPath,
12501
+ sourcePath: input.audioPcmPath,
12427
12502
  outputPath: normalizedAudioPath,
12428
12503
  signal: abortSignal,
12429
12504
  timeoutMs: ffmpegProcessTimeout
@@ -12694,18 +12769,14 @@ function hasUsableVideoDimensions(metadata) {
12694
12769
  return Number.isFinite(metadata.width) && Number.isFinite(metadata.height) && metadata.width > 0 && metadata.height > 0;
12695
12770
  }
12696
12771
  function collectVideoMetadataHints(extractedVideos) {
12697
- return extractedVideos.filter((video) => hasUsableVideoDimensions(video.metadata)).map((video) => ({
12698
- id: video.videoId,
12699
- width: video.metadata.width,
12700
- height: video.metadata.height
12701
- })).sort((a, b) => a.id.localeCompare(b.id));
12772
+ return extractedVideos.filter((video) => hasUsableVideoDimensions(video.metadata)).map((video) => ({ id: video.videoId, ...displaySize(video.metadata) })).sort((a, b) => a.id.localeCompare(b.id));
12702
12773
  }
12703
12774
  function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
12704
12775
  const ranges = [];
12705
12776
  let rangeStart = null;
12706
12777
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
12707
12778
  const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12708
- const missing = !existsSync18(framePath) || statSync8(framePath).size <= 8;
12779
+ const missing = !existsSync20(framePath) || statSync8(framePath).size <= 8;
12709
12780
  if (missing && rangeStart === null) {
12710
12781
  rangeStart = frameIndex;
12711
12782
  } else if (!missing && rangeStart !== null) {
@@ -12830,7 +12901,7 @@ function countCapturedFrames(totalFrames, framesDir, frameExt) {
12830
12901
  let captured = 0;
12831
12902
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
12832
12903
  const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12833
- if (existsSync18(framePath)) captured++;
12904
+ if (existsSync20(framePath)) captured++;
12834
12905
  }
12835
12906
  return captured;
12836
12907
  }
@@ -13495,7 +13566,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
13495
13566
  validateDeliveryEncoderConfig(job.config);
13496
13567
  const debugDir = resolveRenderDebugDir();
13497
13568
  const outputDir = dirname12(outputPath);
13498
- if (!existsSync18(outputDir)) mkdirSync10(outputDir, { recursive: true });
13569
+ if (!existsSync20(outputDir)) mkdirSync10(outputDir, { recursive: true });
13499
13570
  const workDir = job.config.debug ? join28(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
13500
13571
  const pipelineStart = Date.now();
13501
13572
  const baseLog = job.config.logger ?? defaultLogger;
@@ -13637,7 +13708,7 @@ async function executeRenderPipeline(input) {
13637
13708
  job.startedAt = /* @__PURE__ */ new Date();
13638
13709
  assertNotAborted();
13639
13710
  assertConfiguredFfmpegBinariesExist();
13640
- if (!existsSync18(workDir)) mkdirSync10(workDir, { recursive: true });
13711
+ if (!existsSync20(workDir)) mkdirSync10(workDir, { recursive: true });
13641
13712
  if (job.config.debug) {
13642
13713
  log.info("[Render] Debug artifacts enabled", { workDir, logPath });
13643
13714
  }
@@ -13666,36 +13737,11 @@ async function executeRenderPipeline(input) {
13666
13737
  requestedWorkers: job.config.workers ?? "auto"
13667
13738
  });
13668
13739
  const entryFile = job.config.entryFile || "index.html";
13669
- let htmlPath = join28(projectDir, entryFile);
13670
- if (!existsSync18(htmlPath)) {
13740
+ const htmlPath = join28(projectDir, entryFile);
13741
+ if (!existsSync20(htmlPath)) {
13671
13742
  throw new Error(`Entry file not found: ${htmlPath}`);
13672
13743
  }
13673
13744
  assertNotAborted();
13674
- const rawEntry = readTextFile2(htmlPath);
13675
- if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
13676
- const wrapperPath = join28(workDir, "standalone-entry.html");
13677
- const projectIndexPath = join28(projectDir, "index.html");
13678
- if (!existsSync18(projectIndexPath)) {
13679
- throw new Error(
13680
- `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
13681
- );
13682
- }
13683
- const standaloneHtml = extractStandaloneEntryFromIndex(
13684
- readTextFile2(projectIndexPath),
13685
- entryFile,
13686
- rawEntry
13687
- );
13688
- if (!standaloneHtml) {
13689
- throw new Error(
13690
- `Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
13691
- );
13692
- }
13693
- writeFileSync6(wrapperPath, standaloneHtml, "utf-8");
13694
- htmlPath = wrapperPath;
13695
- log.info("Extracted standalone entry from index.html host context", {
13696
- entryFile
13697
- });
13698
- }
13699
13745
  const stage1Start = Date.now();
13700
13746
  updateJobStatus(job, "preprocessing", "Compiling composition", 5, onProgress, {
13701
13747
  code: "compile"
@@ -13707,7 +13753,6 @@ async function executeRenderPipeline(input) {
13707
13753
  () => runCompileStage({
13708
13754
  projectDir,
13709
13755
  workDir,
13710
- htmlPath,
13711
13756
  entryFile,
13712
13757
  job,
13713
13758
  cfg,
@@ -13986,7 +14031,8 @@ async function executeRenderPipeline(input) {
13986
14031
  masterFxChain: composition.masterFxChain,
13987
14032
  abortSignal: executionSignal,
13988
14033
  assertNotAborted,
13989
- log
14034
+ log,
14035
+ keepDeliverySource: true
13990
14036
  })
13991
14037
  );
13992
14038
  pendingAudio = audioStage.catch(() => void 0);
@@ -14018,7 +14064,7 @@ async function executeRenderPipeline(input) {
14018
14064
  throw new Error("File server failed to initialize before frame capture");
14019
14065
  }
14020
14066
  const framesDir = join28(workDir, "captured-frames");
14021
- if (!existsSync18(framesDir)) mkdirSync10(framesDir, { recursive: true });
14067
+ if (!existsSync20(framesDir)) mkdirSync10(framesDir, { recursive: true });
14022
14068
  updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
14023
14069
  code: "start_browsers"
14024
14070
  });
@@ -14694,7 +14740,7 @@ async function executeRenderPipeline(input) {
14694
14740
  const effectiveQuality = job.config.crf ?? preset.quality;
14695
14741
  const effectiveBitrate = job.config.crf != null ? void 0 : job.config.videoBitrate;
14696
14742
  const audioResult = await audioStage;
14697
- const { audioOutputPath, hasAudio } = audioResult;
14743
+ const { audioOutputPath, deliverySourcePath, hasAudio } = audioResult;
14698
14744
  perfStages.audioProcessMs = audioResult.audioProcessMs;
14699
14745
  recordLimiterAttenuation(job, audioResult.audioLoweredDb);
14700
14746
  if (audioResult.audioError) {
@@ -15320,6 +15366,7 @@ async function executeRenderPipeline(input) {
15320
15366
  job,
15321
15367
  videoOnlyPath,
15322
15368
  audioOutputPath,
15369
+ audioPcmPath: deliverySourcePath,
15323
15370
  outputPath: stagedOutputPath,
15324
15371
  hasAudio,
15325
15372
  format: outputFormat,
@@ -15345,7 +15392,7 @@ async function executeRenderPipeline(input) {
15345
15392
  })
15346
15393
  );
15347
15394
  const totalElapsed = Date.now() - pipelineStart;
15348
- const tmpPeakBytes = existsSync18(workDir) ? sampleDirectoryBytes(workDir) : 0;
15395
+ const tmpPeakBytes = existsSync20(workDir) ? sampleDirectoryBytes(workDir) : 0;
15349
15396
  recordTransientRetryObservability();
15350
15397
  observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
15351
15398
  const observabilitySummary = observability.summary({
@@ -15417,7 +15464,7 @@ async function executeRenderPipeline(input) {
15417
15464
  job.perfSummary = perfSummary;
15418
15465
  if (job.config.debug) {
15419
15466
  try {
15420
- writeFileSync6(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
15467
+ writeFileSync7(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
15421
15468
  } catch (err) {
15422
15469
  log.debug("Failed to write perf summary", {
15423
15470
  perfOutputPath,
@@ -15426,7 +15473,7 @@ async function executeRenderPipeline(input) {
15426
15473
  }
15427
15474
  }
15428
15475
  if (job.config.debug) {
15429
- if (!isPngSequence && !isHls && existsSync18(stagedOutputPath)) {
15476
+ if (!isPngSequence && !isHls && existsSync20(stagedOutputPath)) {
15430
15477
  const debugOutput = join28(workDir, `output${videoExt}`);
15431
15478
  copyFileSync5(stagedOutputPath, debugOutput);
15432
15479
  }
@@ -15938,11 +15985,11 @@ import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframe
15938
15985
  import {
15939
15986
  copyFileSync as copyFileSync6,
15940
15987
  cpSync as cpSync2,
15941
- existsSync as existsSync23,
15988
+ existsSync as existsSync25,
15942
15989
  linkSync,
15943
15990
  mkdirSync as mkdirSync14,
15944
15991
  rmSync as rmSync12,
15945
- writeFileSync as writeFileSync9
15992
+ writeFileSync as writeFileSync10
15946
15993
  } from "node:fs";
15947
15994
  import { extname as extname6, isAbsolute as isAbsolute3, join as join37, relative as relative5, sep as sep2 } from "node:path";
15948
15995
  import {
@@ -15969,7 +16016,7 @@ import {
15969
16016
  } from "@hyperframes/engine";
15970
16017
 
15971
16018
  // src/services/distributed/chunkFrames.ts
15972
- import { existsSync as existsSync20, readdirSync as readdirSync6 } from "node:fs";
16019
+ import { existsSync as existsSync22, readdirSync as readdirSync6 } from "node:fs";
15973
16020
  import { extname as extname4, join as join31 } from "node:path";
15974
16021
  import {
15975
16022
  compositionRequiresWebGpu as compositionRequiresWebGpu3,
@@ -15981,7 +16028,7 @@ import {
15981
16028
  // src/services/distributed/shared.ts
15982
16029
  import { execFile as execFileCallback } from "node:child_process";
15983
16030
  import { dirname as dirname14, join as join30 } from "node:path";
15984
- import { existsSync as existsSync19, readFileSync as readFileSync9 } from "node:fs";
16031
+ import { existsSync as existsSync21, readFileSync as readFileSync9 } from "node:fs";
15985
16032
  import { fileURLToPath as fileURLToPath5 } from "node:url";
15986
16033
  import { promisify as promisify2 } from "node:util";
15987
16034
  import {
@@ -15997,7 +16044,7 @@ function isPlanAudioArtifactPath(path) {
15997
16044
  function resolvePlanAudioPath(planDir) {
15998
16045
  for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
15999
16046
  const candidate = join30(planDir, name);
16000
- if (existsSync19(candidate)) return candidate;
16047
+ if (existsSync21(candidate)) return candidate;
16001
16048
  }
16002
16049
  return null;
16003
16050
  }
@@ -16258,7 +16305,7 @@ function readProducerVersion() {
16258
16305
  let current = startDir;
16259
16306
  for (let i = 0; i < 10; i++) {
16260
16307
  const candidate = join30(current, "package.json");
16261
- if (existsSync19(candidate)) {
16308
+ if (existsSync21(candidate)) {
16262
16309
  try {
16263
16310
  const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
16264
16311
  if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
@@ -16312,7 +16359,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
16312
16359
  const result = [];
16313
16360
  for (const v of videos) {
16314
16361
  const outputDir = join31(planDir, "video-frames", v.videoId);
16315
- if (!existsSync20(outputDir)) {
16362
+ if (!existsSync22(outputDir)) {
16316
16363
  throw new Error(
16317
16364
  `[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
16318
16365
  );
@@ -16428,7 +16475,7 @@ async function extractDeferredVideoFramesForChunk(input) {
16428
16475
  const range = video.deferredRange;
16429
16476
  const remoteUrl = remoteSources[range.sourcePath];
16430
16477
  const sourcePath = remoteUrl ?? join31(input.planDir, ...range.sourcePath.split("/"));
16431
- if (remoteUrl === void 0 && !existsSync20(sourcePath)) {
16478
+ if (remoteUrl === void 0 && !existsSync22(sourcePath)) {
16432
16479
  throw new RenderChunkValidationError(
16433
16480
  MISSING_PLAN_ARTIFACT,
16434
16481
  `[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
@@ -16646,7 +16693,7 @@ async function readPlanReferenceBackground(input) {
16646
16693
  }
16647
16694
 
16648
16695
  // src/services/render/stages/freezePlan.ts
16649
- import { existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync10, readdirSync as readdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
16696
+ import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync10, readdirSync as readdirSync7, writeFileSync as writeFileSync8 } from "node:fs";
16650
16697
  import { join as join34, relative as relative3, resolve as resolve9 } from "node:path";
16651
16698
  import {
16652
16699
  REFERENCE_BACKGROUND_FIELD,
@@ -16840,7 +16887,7 @@ function planFileSha256(absolutePath, digests) {
16840
16887
  return sha256;
16841
16888
  }
16842
16889
  async function prehashPlanFiles(dir, digests) {
16843
- if (!existsSync21(dir)) return;
16890
+ if (!existsSync23(dir)) return;
16844
16891
  for (const { absolutePath } of listPlanFiles(dir)) {
16845
16892
  if (!digests.has(absolutePath)) digests.set(absolutePath, await sha256FileAsync(absolutePath));
16846
16893
  }
@@ -16906,19 +16953,19 @@ var PLAN_REMOTE_FILES_RELATIVE_PATH = "meta/remote-files.json";
16906
16953
  function writePlanRemoteFiles(planDir, files) {
16907
16954
  if (files.length === 0) return;
16908
16955
  const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : 1);
16909
- writeFileSync7(join34(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
16956
+ writeFileSync8(join34(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
16910
16957
  }
16911
16958
  function readPlanRemoteFiles(planDir) {
16912
16959
  const path = join34(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
16913
- return existsSync21(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
16960
+ return existsSync23(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
16914
16961
  }
16915
16962
  function recomputePlanHashFromPlanDir(planDir, digests) {
16916
16963
  const planJsonPath = join34(planDir, "plan.json");
16917
16964
  const encoderJsonPath = join34(planDir, "meta", "encoder.json");
16918
- if (!existsSync21(planJsonPath)) {
16965
+ if (!existsSync23(planJsonPath)) {
16919
16966
  throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
16920
16967
  }
16921
- if (!existsSync21(encoderJsonPath)) {
16968
+ if (!existsSync23(encoderJsonPath)) {
16922
16969
  throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
16923
16970
  }
16924
16971
  const planJson = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
@@ -16948,12 +16995,12 @@ async function freezePlan(input) {
16948
16995
  hasAudio,
16949
16996
  digests
16950
16997
  } = input;
16951
- if (!existsSync21(planDir)) {
16998
+ if (!existsSync23(planDir)) {
16952
16999
  throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
16953
17000
  }
16954
17001
  const metaDir = join34(planDir, "meta");
16955
- if (!existsSync21(metaDir)) mkdirSync12(metaDir, { recursive: true });
16956
- writeFileSync7(
17002
+ if (!existsSync23(metaDir)) mkdirSync12(metaDir, { recursive: true });
17003
+ writeFileSync8(
16957
17004
  join34(metaDir, "composition.json"),
16958
17005
  `${JSON.stringify(composition, null, 2)}
16959
17006
  `,
@@ -16961,8 +17008,8 @@ async function freezePlan(input) {
16961
17008
  );
16962
17009
  const encoderForCanonical = stripUndefined(encoder);
16963
17010
  const encoderConfigCanonicalJson = canonicalJsonStringify(encoderForCanonical);
16964
- writeFileSync7(join34(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
16965
- writeFileSync7(join34(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
17011
+ writeFileSync8(join34(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
17012
+ writeFileSync8(join34(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
16966
17013
  `, "utf-8");
16967
17014
  const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
16968
17015
  const planHash = computePlanHash({
@@ -16988,7 +17035,7 @@ async function freezePlan(input) {
16988
17035
  hasAudio
16989
17036
  };
16990
17037
  const planJsonPath = join34(planDir, "plan.json");
16991
- writeFileSync7(planJsonPath, `${JSON.stringify(planJson, null, 2)}
17038
+ writeFileSync8(planJsonPath, `${JSON.stringify(planJson, null, 2)}
16992
17039
  `, "utf-8");
16993
17040
  return { planJsonPath, planHash };
16994
17041
  }
@@ -17029,8 +17076,8 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
17029
17076
  }
17030
17077
 
17031
17078
  // src/services/distributed/chunkAudio.ts
17032
- import { closeSync as closeSync6, existsSync as existsSync22, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync11 } from "node:fs";
17033
- import { writeFileSync as writeFileSync8, writeSync } from "node:fs";
17079
+ import { closeSync as closeSync6, existsSync as existsSync24, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync11 } from "node:fs";
17080
+ import { writeFileSync as writeFileSync9, writeSync } from "node:fs";
17034
17081
  import { join as join35 } from "node:path";
17035
17082
  import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
17036
17083
  function audioCutForChunk(grid, chunks, index, fps) {
@@ -17127,15 +17174,15 @@ async function writeChunkAudioSlices(input) {
17127
17174
  const start = offsets[cut.firstPacket];
17128
17175
  layout.partStarts[index] = start;
17129
17176
  const end = offsets[last] + track.sizes[last];
17130
- writeFileSync8(join35(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
17177
+ writeFileSync9(join35(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
17131
17178
  });
17132
17179
  const frame = [bytes.subarray(0, payloadStart), bytes.subarray(payloadEnd)];
17133
- writeFileSync8(join35(input.planDir, SLICE_FRAME), Buffer.concat(frame));
17134
- writeFileSync8(join35(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
17180
+ writeFileSync9(join35(input.planDir, SLICE_FRAME), Buffer.concat(frame));
17181
+ writeFileSync9(join35(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
17135
17182
  }
17136
17183
  function chunkAudioSource(planDir, chunkIndex, scratchPath) {
17137
17184
  const layoutPath = join35(planDir, SLICE_LAYOUT);
17138
- if (!existsSync22(layoutPath)) return resolvePlanAudioPath(planDir);
17185
+ if (!existsSync24(layoutPath)) return resolvePlanAudioPath(planDir);
17139
17186
  const layout = JSON.parse(readFileSync11(layoutPath, "utf-8"));
17140
17187
  const frame = readFileSync11(join35(planDir, SLICE_FRAME));
17141
17188
  const fd = openSync6(scratchPath, "w");
@@ -17695,7 +17742,7 @@ function createVideoSourceShipper(input) {
17695
17742
  let result = null;
17696
17743
  for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
17697
17744
  const rel = pathInside(root, srcPath);
17698
- if (rel !== null && (existsSync23(join37(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
17745
+ if (rel !== null && (existsSync25(join37(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
17699
17746
  result = ["compiled", ...rel.split(sep2)].join("/");
17700
17747
  break;
17701
17748
  }
@@ -17723,7 +17770,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
17723
17770
  useGpu: false,
17724
17771
  browserGpuMode: "software"
17725
17772
  });
17726
- if (!existsSync23(planDir)) mkdirSync14(planDir, { recursive: true });
17773
+ if (!existsSync25(planDir)) mkdirSync14(planDir, { recursive: true });
17727
17774
  const log = config.logger ?? defaultLogger;
17728
17775
  const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
17729
17776
  const abortSignal = config.abortSignal;
@@ -17753,7 +17800,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
17753
17800
  });
17754
17801
  const entryFile = config.entryFile ?? "index.html";
17755
17802
  const htmlPath = join37(projectDir, entryFile);
17756
- if (!existsSync23(htmlPath)) {
17803
+ if (!existsSync25(htmlPath)) {
17757
17804
  throw new Error(`[plan] entry file not found: ${htmlPath}`);
17758
17805
  }
17759
17806
  const workDir = join37(planDir, ".plan-work");
@@ -17797,7 +17844,6 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
17797
17844
  urlByAbsentLocalPath,
17798
17845
  projectDir,
17799
17846
  workDir,
17800
- htmlPath,
17801
17847
  entryFile,
17802
17848
  job,
17803
17849
  cfg,
@@ -17982,15 +18028,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
17982
18028
  logPlanStage(log, "extract", stageStarted);
17983
18029
  const stagedVideoFrames = join37(compiledDir, "__hyperframes_video_frames");
17984
18030
  const videoFramesDst = join37(planDir, "video-frames");
17985
- if (existsSync23(videoFramesDst)) rmSync12(videoFramesDst, { recursive: true, force: true });
18031
+ if (existsSync25(videoFramesDst)) rmSync12(videoFramesDst, { recursive: true, force: true });
17986
18032
  const videoSourcesDir = join37(planDir, "video-sources");
17987
18033
  rmSync12(videoSourcesDir, { recursive: true, force: true });
17988
- if (existsSync23(stagedVideoFrames)) {
18034
+ if (existsSync25(stagedVideoFrames)) {
17989
18035
  renameWithRetry8(stagedVideoFrames, videoFramesDst);
17990
18036
  } else {
17991
18037
  mkdirSync14(videoFramesDst, { recursive: true });
17992
18038
  }
17993
- if (existsSync23(finalCompiledDir)) rmSync12(finalCompiledDir, { recursive: true, force: true });
18039
+ if (existsSync25(finalCompiledDir)) rmSync12(finalCompiledDir, { recursive: true, force: true });
17994
18040
  renameWithRetry8(compiledDir, finalCompiledDir);
17995
18041
  const shipVideoSource = createVideoSourceShipper({
17996
18042
  planDir,
@@ -18029,7 +18075,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
18029
18075
  sizeBytes
18030
18076
  }))
18031
18077
  );
18032
- writeFileSync9(
18078
+ writeFileSync10(
18033
18079
  join37(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
18034
18080
  JSON.stringify(planVideosJson, null, 2),
18035
18081
  "utf-8"
@@ -18110,10 +18156,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
18110
18156
  applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
18111
18157
  }
18112
18158
  const planAudioPath = join37(planDir, PLAN_AUDIO_RELATIVE_PATH);
18113
- if (audioResult?.hasAudio && existsSync23(audioResult.audioOutputPath)) {
18159
+ if (audioResult?.hasAudio && existsSync25(audioResult.audioOutputPath)) {
18114
18160
  renameWithRetry8(audioResult.audioOutputPath, planAudioPath);
18115
18161
  }
18116
- if (chunksCarryAudio && existsSync23(planAudioPath)) {
18162
+ if (chunksCarryAudio && existsSync25(planAudioPath)) {
18117
18163
  if (options.sliceChunkAudio) {
18118
18164
  stageStarted = Date.now();
18119
18165
  await writeChunkAudioSlices({
@@ -18231,7 +18277,7 @@ async function plan(projectDir, config, planDir, options = {}) {
18231
18277
 
18232
18278
  // src/services/distributed/renderChunk.ts
18233
18279
  import { randomBytes } from "node:crypto";
18234
- import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync13, readdirSync as readdirSync10, rmSync as rmSync15, writeFileSync as writeFileSync12 } from "node:fs";
18280
+ import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync13, readdirSync as readdirSync10, rmSync as rmSync15, writeFileSync as writeFileSync13 } from "node:fs";
18235
18281
  import { cpus as cpus3 } from "node:os";
18236
18282
  import { join as join41 } from "node:path";
18237
18283
  import {
@@ -18258,7 +18304,7 @@ import {
18258
18304
  } from "@hyperframes/engine";
18259
18305
  import {
18260
18306
  copyFileSync as copyFileSync8,
18261
- existsSync as existsSync25,
18307
+ existsSync as existsSync27,
18262
18308
  lstatSync as lstatSync3,
18263
18309
  mkdirSync as mkdirSync16,
18264
18310
  mkdtempSync as mkdtempSync6,
@@ -18266,7 +18312,7 @@ import {
18266
18312
  readdirSync as readdirSync9,
18267
18313
  rmSync as rmSync14,
18268
18314
  statSync as statSync10,
18269
- writeFileSync as writeFileSync11
18315
+ writeFileSync as writeFileSync12
18270
18316
  } from "node:fs";
18271
18317
  import { tmpdir as tmpdir3 } from "node:os";
18272
18318
  import { dirname as dirname16, isAbsolute as isAbsolute4, join as join40, relative as relative6, resolve as resolve10, sep as sep3 } from "node:path";
@@ -18276,13 +18322,13 @@ import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as r
18276
18322
  // src/services/distributed/planV2Publisher.ts
18277
18323
  import {
18278
18324
  copyFileSync as copyFileSync7,
18279
- existsSync as existsSync24,
18325
+ existsSync as existsSync26,
18280
18326
  linkSync as linkSync2,
18281
18327
  mkdirSync as mkdirSync15,
18282
18328
  mkdtempSync as mkdtempSync5,
18283
18329
  rmSync as rmSync13,
18284
18330
  statSync as statSync9,
18285
- writeFileSync as writeFileSync10
18331
+ writeFileSync as writeFileSync11
18286
18332
  } from "node:fs";
18287
18333
  import { renameWithRetry as renameWithRetry9 } from "@hyperframes/engine";
18288
18334
  import { dirname as dirname15, join as join39 } from "node:path";
@@ -18345,7 +18391,7 @@ var LocalPlanV2ArtifactPublisher = class {
18345
18391
  #linkFile;
18346
18392
  #committed = false;
18347
18393
  constructor(destinationDir, options = {}) {
18348
- if (existsSync24(destinationDir)) {
18394
+ if (existsSync26(destinationDir)) {
18349
18395
  throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
18350
18396
  }
18351
18397
  this.destinationDir = destinationDir;
@@ -18362,7 +18408,7 @@ var LocalPlanV2ArtifactPublisher = class {
18362
18408
  );
18363
18409
  }
18364
18410
  const destinationPath = planV2BlobPath(this.temporaryDir, digest);
18365
- if (existsSync24(destinationPath)) return;
18411
+ if (existsSync26(destinationPath)) return;
18366
18412
  mkdirSync15(dirname15(destinationPath), { recursive: true });
18367
18413
  const stagingDir = mkdtempSync5(join39(dirname15(destinationPath), ".plan-v2-blob-"));
18368
18414
  const temporaryPath = join39(stagingDir, "blob");
@@ -18380,13 +18426,13 @@ var LocalPlanV2ArtifactPublisher = class {
18380
18426
  }
18381
18427
  async commitManifest(manifestBytes) {
18382
18428
  for (const digest of new Set(manifestDigests(manifestBytes))) {
18383
- if (!existsSync24(planV2BlobPath(this.temporaryDir, digest))) {
18429
+ if (!existsSync26(planV2BlobPath(this.temporaryDir, digest))) {
18384
18430
  throw new PlanV2IntegrityError(
18385
18431
  `cannot commit manifest before referenced blob is durable: ${digest}`
18386
18432
  );
18387
18433
  }
18388
18434
  }
18389
- writeFileSync10(join39(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
18435
+ writeFileSync11(join39(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
18390
18436
  renameWithRetry9(this.temporaryDir, this.destinationDir);
18391
18437
  this.#committed = true;
18392
18438
  }
@@ -18459,7 +18505,7 @@ function assertValidExtractionCacheCompleteSentinel(path) {
18459
18505
  }
18460
18506
  function validateExtractionCacheCompleteSentinels(executionPlanDir) {
18461
18507
  const videoRoot = join40(executionPlanDir, "video-frames");
18462
- if (!existsSync25(videoRoot)) return;
18508
+ if (!existsSync27(videoRoot)) return;
18463
18509
  for (const videoEntry of readdirSync9(videoRoot, { withFileTypes: true })) {
18464
18510
  if (!videoEntry.isDirectory()) continue;
18465
18511
  const videoDir = join40(videoRoot, videoEntry.name);
@@ -18560,7 +18606,7 @@ function parsePlanVideosJson2(value) {
18560
18606
  }
18561
18607
  function materializeExtractedVideoDirectories(planDir) {
18562
18608
  const videosPath = join40(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18563
- if (!existsSync25(videosPath)) return;
18609
+ if (!existsSync27(videosPath)) return;
18564
18610
  const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
18565
18611
  for (const video of videos.extracted) {
18566
18612
  mkdirSync16(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
@@ -18594,9 +18640,9 @@ function parseChunkSlices(value) {
18594
18640
  }
18595
18641
  function buildVideoChunkDependencies(executionPlanDir, dimensions) {
18596
18642
  const videoRoot = join40(executionPlanDir, "video-frames");
18597
- const hasExtractedFrames = existsSync25(videoRoot) && listFiles(videoRoot).length > 0;
18643
+ const hasExtractedFrames = existsSync27(videoRoot) && listFiles(videoRoot).length > 0;
18598
18644
  const videosPath = join40(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18599
- if (!existsSync25(videosPath)) {
18645
+ if (!existsSync27(videosPath)) {
18600
18646
  return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
18601
18647
  }
18602
18648
  const chunksPath = join40(executionPlanDir, "meta", "chunks.json");
@@ -18653,7 +18699,7 @@ function computeManifestHash(manifest) {
18653
18699
  return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
18654
18700
  }
18655
18701
  function writeBlob(sourcePath, destinationPath) {
18656
- if (existsSync25(destinationPath)) return;
18702
+ if (existsSync27(destinationPath)) return;
18657
18703
  mkdirSync16(dirname16(destinationPath), { recursive: true });
18658
18704
  const temporaryDir = mkdtempSync6(join40(dirname16(destinationPath), ".plan-v2-blob-"));
18659
18705
  const temporaryPath = join40(temporaryDir, "blob");
@@ -18693,7 +18739,7 @@ async function putTasks(tasks) {
18693
18739
  }
18694
18740
  function buildPlanV2Publication(executionPlanDir, digests) {
18695
18741
  const executionPlanPath = join40(executionPlanDir, "plan.json");
18696
- if (!existsSync25(executionPlanPath)) {
18742
+ if (!existsSync27(executionPlanPath)) {
18697
18743
  throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
18698
18744
  }
18699
18745
  const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
@@ -18773,7 +18819,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
18773
18819
  };
18774
18820
  }
18775
18821
  function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
18776
- if (existsSync25(planV2Dir)) {
18822
+ if (existsSync27(planV2Dir)) {
18777
18823
  throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
18778
18824
  }
18779
18825
  const publication = buildPlanV2Publication(executionPlanDir);
@@ -18786,7 +18832,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
18786
18832
  for (const blob of publication.blobs) {
18787
18833
  writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
18788
18834
  }
18789
- writeFileSync11(
18835
+ writeFileSync12(
18790
18836
  join40(tempDir, "plan.json"),
18791
18837
  canonicalJsonStringify(publication.manifest),
18792
18838
  "utf-8"
@@ -18859,7 +18905,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
18859
18905
  }
18860
18906
  }
18861
18907
  async function planV2(projectDir, config, planV2Dir) {
18862
- if (existsSync25(planV2Dir)) {
18908
+ if (existsSync27(planV2Dir)) {
18863
18909
  throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
18864
18910
  }
18865
18911
  const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
@@ -19013,7 +19059,7 @@ function parsePlanV2Manifest(value) {
19013
19059
  }
19014
19060
  function readPlanV2Manifest(planV2Dir) {
19015
19061
  const manifestPath = join40(planV2Dir, "plan.json");
19016
- if (!existsSync25(manifestPath)) {
19062
+ if (!existsSync27(manifestPath)) {
19017
19063
  throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
19018
19064
  }
19019
19065
  const value = readJsonFile(manifestPath, "v2 manifest");
@@ -19032,7 +19078,7 @@ function listPlanV2ArtifactsForTarget(manifest, target) {
19032
19078
  }
19033
19079
  function verifyBlob(planV2Dir, artifact) {
19034
19080
  const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
19035
- if (!existsSync25(sourcePath)) {
19081
+ if (!existsSync27(sourcePath)) {
19036
19082
  throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
19037
19083
  }
19038
19084
  const stats = statSync10(sourcePath);
@@ -19045,7 +19091,7 @@ function verifyBlob(planV2Dir, artifact) {
19045
19091
  return sourcePath;
19046
19092
  }
19047
19093
  function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
19048
- if (existsSync25(destinationDir)) {
19094
+ if (existsSync27(destinationDir)) {
19049
19095
  throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
19050
19096
  }
19051
19097
  const manifest = readPlanV2Manifest(planV2Dir);
@@ -19066,7 +19112,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
19066
19112
  copyFileSync8(sourcePath, destinationPath);
19067
19113
  }
19068
19114
  if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
19069
- writeFileSync11(
19115
+ writeFileSync12(
19070
19116
  join40(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
19071
19117
  canonicalJsonStringify({
19072
19118
  manifest,
@@ -19110,7 +19156,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
19110
19156
  }
19111
19157
  function readValidatedPlanV2Materialization(planDir, expectedTarget) {
19112
19158
  const markerPath = join40(planDir, PLAN_V2_MATERIALIZATION_MARKER);
19113
- if (!existsSync25(markerPath)) return null;
19159
+ if (!existsSync27(markerPath)) return null;
19114
19160
  const marker = readJsonFile(markerPath, "materialization marker");
19115
19161
  if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
19116
19162
  throw new PlanV2IntegrityError("malformed materialization marker");
@@ -19128,7 +19174,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
19128
19174
  for (const artifact of required) {
19129
19175
  if (Object.hasOwn(remoteSources, artifact.path)) continue;
19130
19176
  const path = join40(planDir, ...artifact.path.split("/"));
19131
- if (!existsSync25(path) || statSync10(path).size !== artifact.sizeBytes) {
19177
+ if (!existsSync27(path) || statSync10(path).size !== artifact.sizeBytes) {
19132
19178
  throw new PlanV2IntegrityError(
19133
19179
  `materialized artifact missing or truncated: ${artifact.path}`
19134
19180
  );
@@ -19268,7 +19314,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19268
19314
  const planJsonPath = join41(planDir, "plan.json");
19269
19315
  const encoderJsonPath = join41(planDir, "meta", "encoder.json");
19270
19316
  const chunksJsonPath = join41(planDir, "meta", "chunks.json");
19271
- if (!existsSync26(planJsonPath)) {
19317
+ if (!existsSync28(planJsonPath)) {
19272
19318
  throw new RenderChunkValidationError(
19273
19319
  MISSING_PLAN_ARTIFACT,
19274
19320
  `[renderChunk] planDir is missing required artifact: ${planJsonPath}`
@@ -19284,7 +19330,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19284
19330
  const v2Manifest = v2Materialization?.manifest ?? null;
19285
19331
  const remoteSources = v2Materialization?.remoteSources ?? {};
19286
19332
  for (const required of [encoderJsonPath, chunksJsonPath]) {
19287
- if (!existsSync26(required)) {
19333
+ if (!existsSync28(required)) {
19288
19334
  throw new RenderChunkValidationError(
19289
19335
  MISSING_PLAN_ARTIFACT,
19290
19336
  `[renderChunk] planDir is missing required artifact: ${required}`
@@ -19295,7 +19341,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19295
19341
  const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
19296
19342
  const videosJsonPath = join41(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
19297
19343
  let planVideos = null;
19298
- if (existsSync26(videosJsonPath)) {
19344
+ if (existsSync28(videosJsonPath)) {
19299
19345
  try {
19300
19346
  planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync13(videosJsonPath, "utf-8")));
19301
19347
  } catch (err) {
@@ -19326,7 +19372,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19326
19372
  const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
19327
19373
  const framesInChunk = range.endFrame - range.startFrame;
19328
19374
  const compiledDir = join41(planDir, "compiled");
19329
- if (!existsSync26(compiledDir)) {
19375
+ if (!existsSync28(compiledDir)) {
19330
19376
  throw new RenderChunkValidationError(
19331
19377
  MISSING_PLAN_ARTIFACT,
19332
19378
  `[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
@@ -19576,10 +19622,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19576
19622
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
19577
19623
  const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
19578
19624
  if (isPngSequence) {
19579
- if (!existsSync26(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
19625
+ if (!existsSync28(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
19580
19626
  } else {
19581
19627
  const outDir = join41(outputChunkPath, "..");
19582
- if (!existsSync26(outDir)) mkdirSync17(outDir, { recursive: true });
19628
+ if (!existsSync28(outDir)) mkdirSync17(outDir, { recursive: true });
19583
19629
  }
19584
19630
  const encodeInput = {
19585
19631
  job,
@@ -19789,7 +19835,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19789
19835
  producerVersion: plan2.producerVersion,
19790
19836
  ffmpegVersion
19791
19837
  };
19792
- writeFileSync12(perfPath, `${JSON.stringify(perfPayload, null, 2)}
19838
+ writeFileSync13(perfPath, `${JSON.stringify(perfPayload, null, 2)}
19793
19839
  `, "utf-8");
19794
19840
  try {
19795
19841
  rmSync15(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
@@ -20479,11 +20525,11 @@ import {
20479
20525
 
20480
20526
  // src/server.ts
20481
20527
  import {
20482
- existsSync as existsSync29,
20528
+ existsSync as existsSync31,
20483
20529
  mkdirSync as mkdirSync19,
20484
20530
  statSync as statSync12,
20485
20531
  mkdtempSync as mkdtempSync8,
20486
- writeFileSync as writeFileSync13,
20532
+ writeFileSync as writeFileSync14,
20487
20533
  rmSync as rmSync17,
20488
20534
  createReadStream as createReadStream5
20489
20535
  } from "node:fs";
@@ -20502,7 +20548,7 @@ import {
20502
20548
 
20503
20549
  // src/services/hyperframeLint.ts
20504
20550
  import {
20505
- existsSync as existsSync27,
20551
+ existsSync as existsSync29,
20506
20552
  readFileSync as readFileSync15,
20507
20553
  statSync as statSync11,
20508
20554
  openSync as openSync7,
@@ -20556,7 +20602,7 @@ function readEntryHtml(filePath) {
20556
20602
  }
20557
20603
  function readProjectEntryFile(projectDir, preferredEntryFile) {
20558
20604
  const absProjectDir = resolve11(projectDir);
20559
- if (!existsSync27(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
20605
+ if (!existsSync29(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
20560
20606
  return { error: `Project directory not found: ${absProjectDir}` };
20561
20607
  }
20562
20608
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
@@ -20625,7 +20671,7 @@ async function runHyperframeLint(prepared) {
20625
20671
  import { Worker as Worker2 } from "node:worker_threads";
20626
20672
  import { fileURLToPath as fileURLToPath6 } from "node:url";
20627
20673
  import { dirname as dirname17, join as join44 } from "node:path";
20628
- import { existsSync as existsSync28 } from "node:fs";
20674
+ import { existsSync as existsSync30 } from "node:fs";
20629
20675
  var DEFAULT_HEALTH_PORT = 9848;
20630
20676
  async function startHealthWorker(options = {}) {
20631
20677
  const log = options.logger ?? defaultLogger2();
@@ -20702,7 +20748,7 @@ function resolveWorkerEntry2() {
20702
20748
  const here = dirname17(fileURLToPath6(import.meta.url));
20703
20749
  const candidates = [join44(here, "healthWorkerThread.js"), join44(here, "healthWorkerThread.ts")];
20704
20750
  for (const candidate of candidates) {
20705
- if (existsSync28(candidate)) return candidate;
20751
+ if (existsSync30(candidate)) return candidate;
20706
20752
  }
20707
20753
  return void 0;
20708
20754
  }
@@ -20848,7 +20894,7 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
20848
20894
  const { input, cleanupProjectDir } = prepared;
20849
20895
  const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
20850
20896
  const outputDir = dirname18(absoluteOutputPath);
20851
- if (!existsSync29(outputDir)) mkdirSync19(outputDir, { recursive: true });
20897
+ if (!existsSync31(outputDir)) mkdirSync19(outputDir, { recursive: true });
20852
20898
  return { input, cleanupProjectDir, absoluteOutputPath };
20853
20899
  }
20854
20900
  function validateRenderOverrides(body) {
@@ -20889,11 +20935,11 @@ function prepareProjectDirectory(projectDir, options) {
20889
20935
  const candidate = nonEmptyString(projectDir);
20890
20936
  if (!candidate) return null;
20891
20937
  const absProjectDir = resolve12(candidate);
20892
- if (!existsSync29(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
20938
+ if (!existsSync31(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
20893
20939
  return { error: `Project directory not found: ${absProjectDir}` };
20894
20940
  }
20895
20941
  const entry = options.entryFile || "index.html";
20896
- if (!existsSync29(resolve12(absProjectDir, entry))) {
20942
+ if (!existsSync31(resolve12(absProjectDir, entry))) {
20897
20943
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
20898
20944
  }
20899
20945
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -20915,7 +20961,7 @@ async function resolveInlineRenderHtml(body) {
20915
20961
  function materializeInlineProject(html, options) {
20916
20962
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir5();
20917
20963
  const tempProjectDir = mkdtempSync8(join45(tempRoot, "producer-project-"));
20918
- writeFileSync13(join45(tempProjectDir, "index.html"), html, "utf-8");
20964
+ writeFileSync14(join45(tempProjectDir, "index.html"), html, "utf-8");
20919
20965
  return {
20920
20966
  prepared: {
20921
20967
  input: { projectDir: tempProjectDir, ...options },
@@ -20985,7 +21031,7 @@ function cleanupTempDir(dir, log) {
20985
21031
  }
20986
21032
  }
20987
21033
  function outputFileSize(path) {
20988
- return existsSync29(path) ? statSync12(path).size : 0;
21034
+ return existsSync31(path) ? statSync12(path).size : 0;
20989
21035
  }
20990
21036
  function createBlockingProgressReporter(log, requestId) {
20991
21037
  let lastLoggedPct = -10;
@@ -21289,7 +21335,7 @@ function createRenderHandlers(options = {}) {
21289
21335
  if (!artifact) {
21290
21336
  return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
21291
21337
  }
21292
- if (!existsSync29(artifact.path)) {
21338
+ if (!existsSync31(artifact.path)) {
21293
21339
  store.delete(token);
21294
21340
  return c.json({ success: false, error: "Output artifact file missing" }, 404);
21295
21341
  }
@@ -21395,14 +21441,14 @@ import {
21395
21441
  } from "@hyperframes/engine";
21396
21442
  import {
21397
21443
  cpSync as cpSync3,
21398
- existsSync as existsSync30,
21444
+ existsSync as existsSync32,
21399
21445
  mkdirSync as mkdirSync20,
21400
21446
  mkdtempSync as mkdtempSync9,
21401
21447
  readFileSync as readFileSync16,
21402
21448
  readdirSync as readdirSync11,
21403
21449
  rmSync as rmSync18,
21404
21450
  statSync as statSync13,
21405
- writeFileSync as writeFileSync14
21451
+ writeFileSync as writeFileSync15
21406
21452
  } from "node:fs";
21407
21453
  import { dirname as dirname19, join as join46 } from "node:path";
21408
21454
  import {
@@ -21450,7 +21496,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
21450
21496
  }
21451
21497
  const format = plan2.dimensions.format;
21452
21498
  const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
21453
- if (!existsSync30(dirname19(outputPath))) {
21499
+ if (!existsSync32(dirname19(outputPath))) {
21454
21500
  mkdirSync20(dirname19(outputPath), { recursive: true });
21455
21501
  }
21456
21502
  const workDir = mkdtempSync9(`${outputPath}.assemble-work-`);
@@ -21474,7 +21520,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
21474
21520
  try {
21475
21521
  const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
21476
21522
  const finalPath = join46(workDir, `output.${format}`);
21477
- const audio = audioPath !== null && existsSync30(audioPath) ? audioPath : null;
21523
+ const audio = audioPath !== null && existsSync32(audioPath) ? audioPath : null;
21478
21524
  await writeJoined(ctx, videoSources, audio, finalPath);
21479
21525
  renameWithRetry11(finalPath, outputPath);
21480
21526
  log.info("[assemble] stage timings", {
@@ -21492,7 +21538,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
21492
21538
  });
21493
21539
  }
21494
21540
  }
21495
- const fileSize = existsSync30(outputPath) ? statSync13(outputPath).size : 0;
21541
+ const fileSize = existsSync32(outputPath) ? statSync13(outputPath).size : 0;
21496
21542
  return {
21497
21543
  ...referenceBackgroundResult8(plan2[REFERENCE_BACKGROUND_FIELD6]),
21498
21544
  outputPath,
@@ -21504,13 +21550,13 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
21504
21550
  function readAssemblePlan(planDir, chunkPaths) {
21505
21551
  const planJsonPath = join46(planDir, "plan.json");
21506
21552
  const chunksJsonPath = join46(planDir, "meta", "chunks.json");
21507
- if (!existsSync30(planJsonPath)) {
21553
+ if (!existsSync32(planJsonPath)) {
21508
21554
  throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
21509
21555
  }
21510
21556
  const plan2 = JSON.parse(readFileSync16(planJsonPath, "utf-8"));
21511
21557
  readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
21512
21558
  validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
21513
- if (!existsSync30(chunksJsonPath)) {
21559
+ if (!existsSync32(chunksJsonPath)) {
21514
21560
  throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
21515
21561
  }
21516
21562
  const chunks = JSON.parse(readFileSync16(chunksJsonPath, "utf-8"));
@@ -21525,7 +21571,7 @@ function readAssemblePlan(planDir, chunkPaths) {
21525
21571
  }
21526
21572
  }
21527
21573
  for (const path of chunkPaths.flat()) {
21528
- if (!existsSync30(path)) {
21574
+ if (!existsSync32(path)) {
21529
21575
  throw new Error(`[assemble] chunk path does not exist: ${path}`);
21530
21576
  }
21531
21577
  }
@@ -21623,7 +21669,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
21623
21669
  function concatDemuxerInput(chunkPaths, workDir, fpsArg) {
21624
21670
  const listPath = join46(workDir, "concat-list.txt");
21625
21671
  const body = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
21626
- writeFileSync14(listPath, `${body}
21672
+ writeFileSync15(listPath, `${body}
21627
21673
  `, "utf-8");
21628
21674
  return ["-r", fpsArg, "-f", "concat", "-safe", "0", "-i", listPath];
21629
21675
  }
@@ -21664,7 +21710,7 @@ function planFormat(planDir) {
21664
21710
  }
21665
21711
  function lockedAudio(planDir) {
21666
21712
  const encoderJsonPath = join46(planDir, "meta", "encoder.json");
21667
- if (!existsSync30(encoderJsonPath)) return {};
21713
+ if (!existsSync32(encoderJsonPath)) return {};
21668
21714
  return JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
21669
21715
  }
21670
21716
  function readCfrEncoder(planDir, format) {
@@ -21674,7 +21720,7 @@ function readCfrEncoder(planDir, format) {
21674
21720
  );
21675
21721
  }
21676
21722
  const encoderJsonPath = join46(planDir, "meta", "encoder.json");
21677
- if (!existsSync30(encoderJsonPath)) {
21723
+ if (!existsSync32(encoderJsonPath)) {
21678
21724
  throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
21679
21725
  }
21680
21726
  const encoderJson = JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
@@ -21686,7 +21732,7 @@ function readCfrEncoder(planDir, format) {
21686
21732
  return encoderJson;
21687
21733
  }
21688
21734
  function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
21689
- if (existsSync30(outputPath)) rmSync18(outputPath, { recursive: true, force: true });
21735
+ if (existsSync32(outputPath)) rmSync18(outputPath, { recursive: true, force: true });
21690
21736
  mkdirSync20(outputPath, { recursive: true });
21691
21737
  let globalIdx = 0;
21692
21738
  for (const chunkDir of chunkPaths) {
@@ -21710,7 +21756,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
21710
21756
  `[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
21711
21757
  );
21712
21758
  }
21713
- if (audioPath !== null && existsSync30(audioPath)) {
21759
+ if (audioPath !== null && existsSync32(audioPath)) {
21714
21760
  const sidecar = join46(outputPath, MIXED_AUDIO_FILENAME4);
21715
21761
  cpSync3(audioPath, sidecar);
21716
21762
  }