@hyperframes/producer 0.8.205 → 0.8.206

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
@@ -2,13 +2,13 @@
2
2
  import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
3
3
  import {
4
4
  existsSync as existsSync16,
5
- mkdirSync as mkdirSync9,
6
- readFileSync as readFileSync8,
5
+ mkdirSync as mkdirSync10,
6
+ readFileSync as readFileSync9,
7
7
  readdirSync as readdirSync5,
8
- rmSync as rmSync11,
9
- statSync as statSync7,
10
- writeFileSync as writeFileSync5,
11
- copyFileSync as copyFileSync4,
8
+ rmSync as rmSync12,
9
+ statSync as statSync8,
10
+ writeFileSync as writeFileSync6,
11
+ copyFileSync as copyFileSync5,
12
12
  appendFileSync
13
13
  } from "fs";
14
14
  import { tmpdir as tmpdir2 } from "node:os";
@@ -56,7 +56,7 @@ import {
56
56
  augmentProtocolTimeoutError,
57
57
  augmentPageNavigationTimeoutError
58
58
  } from "@hyperframes/engine";
59
- import { join as join26, dirname as dirname10, resolve as resolve7 } from "path";
59
+ import { join as join27, dirname as dirname11, resolve as resolve8 } from "path";
60
60
  import { totalmem as totalmem2 } from "node:os";
61
61
  import { randomUUID } from "crypto";
62
62
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -80,6 +80,7 @@ import { Readable } from "node:stream";
80
80
  import { join, extname, resolve as resolve2, sep } from "node:path";
81
81
  import { injectScriptsAtHeadStart, injectScriptsIntoHtml } from "@hyperframes/core/compiler";
82
82
  import { fpsToNumber } from "@hyperframes/core";
83
+ import { stripSdrPngColourTags } from "@hyperframes/core/compiler/png-colour-tags";
83
84
 
84
85
  // src/services/hyperframeRuntimeLoader.ts
85
86
  import { createHash } from "node:crypto";
@@ -196,11 +197,11 @@ var defaultLogger = createConsoleLogger("info");
196
197
 
197
198
  // src/services/fileServer.ts
198
199
  var readFileAsync = promisify(readFile);
199
- function isPathInside(child2, parent, options = {}) {
200
+ function isPathInside(child3, parent, options = {}) {
200
201
  const { resolveSymlinks = false, pathModule } = options;
201
202
  const resolveFn = pathModule?.resolve ?? resolve2;
202
203
  const separator = pathModule?.sep ?? sep;
203
- const resolvedChild = resolveFn(child2);
204
+ const resolvedChild = resolveFn(child3);
204
205
  const resolvedParent = resolveFn(parent);
205
206
  const normalizedChild = resolveSymlinks && existsSync2(resolvedChild) ? realpathSync.native(resolvedChild) : resolvedChild;
206
207
  const normalizedParent = resolveSymlinks && existsSync2(resolvedParent) ? realpathSync.native(resolvedParent) : resolvedParent;
@@ -720,6 +721,18 @@ function createFileServer(options) {
720
721
  html = isIndex ? injectScriptsIntoHtml(html, headScripts, bodyScripts, stripEmbeddedRuntime) : html;
721
722
  return c.text(html, 200, { "Content-Type": contentType });
722
723
  }
724
+ if (ext === ".png") {
725
+ const untagged = stripSdrPngColourTags(await readFileAsync(fd));
726
+ if (untagged) {
727
+ const headers = {
728
+ "Content-Type": contentType,
729
+ "Content-Length": String(untagged.length)
730
+ };
731
+ return new Response(c.req.method === "HEAD" ? null : new Uint8Array(untagged), {
732
+ headers
733
+ });
734
+ }
735
+ }
723
736
  const totalSize = stat.size;
724
737
  const rangeHeader = c.req.header("range");
725
738
  const rangeRequest = parseRangeHeader(rangeHeader, totalSize);
@@ -747,7 +760,8 @@ function createFileServer(options) {
747
760
  const stream = createReadStream(filePath, {
748
761
  fd,
749
762
  autoClose: true,
750
- ...range ? { start: range.start, end: range.end } : {}
763
+ start: range?.start ?? 0,
764
+ ...range ? { end: range.end } : {}
751
765
  });
752
766
  streamOwnsFd = true;
753
767
  try {
@@ -761,10 +775,10 @@ function createFileServer(options) {
761
775
  if (!streamOwnsFd) closeSync(fd);
762
776
  }
763
777
  });
764
- return new Promise((resolve12) => {
778
+ return new Promise((resolve13) => {
765
779
  const connections = /* @__PURE__ */ new Set();
766
780
  const server = serve({ fetch: app.fetch, port, hostname: "127.0.0.1" }, (info) => {
767
- resolve12({
781
+ resolve13({
768
782
  url: `http://localhost:${info.port}`,
769
783
  port: info.port,
770
784
  addPreHeadScript: (script) => {
@@ -1271,8 +1285,8 @@ var OrderedRenderEventPublisher = class {
1271
1285
  let timer;
1272
1286
  const drained = await Promise.race([
1273
1287
  this.tail.then(() => true),
1274
- new Promise((resolve12) => {
1275
- timer = setTimeout(() => resolve12(false), FLUSH_TIMEOUT_MS);
1288
+ new Promise((resolve13) => {
1289
+ timer = setTimeout(() => resolve13(false), FLUSH_TIMEOUT_MS);
1276
1290
  })
1277
1291
  ]);
1278
1292
  clearTimeout(timer);
@@ -3616,7 +3630,7 @@ var SequentialCaptureStallError = class extends Error {
3616
3630
  }
3617
3631
  };
3618
3632
  function raceAgainstStall(promise, deadlineMs, input, signal) {
3619
- return new Promise((resolve12, reject) => {
3633
+ return new Promise((resolve13, reject) => {
3620
3634
  const timer = setTimeout(
3621
3635
  () => {
3622
3636
  reject(
@@ -3635,7 +3649,7 @@ function raceAgainstStall(promise, deadlineMs, input, signal) {
3635
3649
  promise.then(
3636
3650
  (value) => {
3637
3651
  clearTimeout(timer);
3638
- resolve12(value);
3652
+ resolve13(value);
3639
3653
  },
3640
3654
  (error) => {
3641
3655
  clearTimeout(timer);
@@ -5563,15 +5577,15 @@ function countAuthoredTimedClips(html) {
5563
5577
  }
5564
5578
 
5565
5579
  // src/services/render/stages/compileStage.ts
5566
- import { join as join15 } from "node:path";
5580
+ import { join as join16 } from "node:path";
5567
5581
  import { suggestMatchingPreset } from "@hyperframes/core";
5568
5582
 
5569
5583
  // src/services/htmlCompiler.ts
5570
5584
  import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
5571
- import { createHash as createHash5 } from "node:crypto";
5572
- import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync6 } from "fs";
5573
- import { join as join14, dirname as dirname6, resolve as resolve6, basename as basename4, relative as relative2 } from "path";
5574
- import { parseHTML as parseHTML4 } from "linkedom";
5585
+ import { createHash as createHash6 } from "node:crypto";
5586
+ import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync7 } from "fs";
5587
+ import { join as join15, dirname as dirname7, resolve as resolve7, basename as basename4, relative as relative2 } from "path";
5588
+ import { parseHTML as parseHTML5 } from "linkedom";
5575
5589
  import {
5576
5590
  COMPOSITION_SOURCE_URL,
5577
5591
  compileTimingAttrs,
@@ -5847,7 +5861,7 @@ function readLoopOverride(el) {
5847
5861
  }
5848
5862
  return null;
5849
5863
  }
5850
- function resolveGifSourcePath(src, options) {
5864
+ function resolveLocalImageSourcePath(src, options) {
5851
5865
  const trimmed = src.trim();
5852
5866
  if (!trimmed || trimmed.startsWith("data:")) return null;
5853
5867
  const { basePath } = splitUrlSuffix(trimmed);
@@ -6070,7 +6084,7 @@ async function prepareAnimatedGifInputs(html, options) {
6070
6084
  const img = images[i];
6071
6085
  const src = (img.getAttribute("src") || "").trim();
6072
6086
  if (!hasGifExtension(src)) continue;
6073
- const sourcePath = resolveGifSourcePath(src, options);
6087
+ const sourcePath = resolveLocalImageSourcePath(src, options);
6074
6088
  if (!sourcePath) continue;
6075
6089
  const bytes = readFileSync5(sourcePath);
6076
6090
  const metadata = parseAnimatedGifMetadata(bytes);
@@ -6118,19 +6132,74 @@ async function prepareAnimatedGifInputs(html, options) {
6118
6132
  };
6119
6133
  }
6120
6134
 
6121
- // src/services/htmlCompiler.ts
6122
- import { createStudioPositionSeekReapplyScript } from "@hyperframes/studio-server/manual-edits-render-script";
6123
- import { getPositionEditsRenderScript } from "@hyperframes/core/runtime/position-edits-render";
6124
-
6125
- // src/services/assetMediaType.ts
6126
- import { existsSync as existsSync9 } from "node:fs";
6135
+ // src/services/heifImagePrep.ts
6136
+ import { createHash as createHash5 } from "node:crypto";
6127
6137
  import {
6128
- fingerprintElementId,
6129
- isNotMediaPayload,
6130
- NotMediaPayloadError,
6131
- probeMediaProfile,
6132
- resolveProjectRelativeSrc as resolveProjectRelativeSrc2
6133
- } from "@hyperframes/engine";
6138
+ closeSync as closeSync4,
6139
+ copyFileSync as copyFileSync3,
6140
+ mkdirSync as mkdirSync6,
6141
+ openSync as openSync4,
6142
+ readFileSync as readFileSync6,
6143
+ readSync as readSync3,
6144
+ rmSync as rmSync8,
6145
+ statSync as statSync6,
6146
+ writeFileSync as writeFileSync4
6147
+ } from "node:fs";
6148
+ import { dirname as dirname6, join as join14, resolve as resolve6 } from "node:path";
6149
+ import { parseHTML as parseHTML4 } from "linkedom";
6150
+ import { renameWithRetry as renameWithRetry4, runFfmpeg as runFfmpeg3, runFfprobe } from "@hyperframes/engine";
6151
+
6152
+ // src/services/heifRotation.ts
6153
+ function* boxes(buf, start, end) {
6154
+ let at = start;
6155
+ while (at + 8 <= end) {
6156
+ let size = buf.readUInt32BE(at);
6157
+ let header = 8;
6158
+ if (size === 1 && at + 16 <= end) {
6159
+ size = Number(buf.readBigUInt64BE(at + 8));
6160
+ header = 16;
6161
+ } else if (size === 0) {
6162
+ size = end - at;
6163
+ }
6164
+ if (size < header || at + size > end) return;
6165
+ yield { type: buf.toString("latin1", at + 4, at + 8), start: at + header, end: at + size };
6166
+ at += size;
6167
+ }
6168
+ }
6169
+ function child(buf, parent, type, skip = 0) {
6170
+ return [...boxes(buf, parent.start + skip, parent.end)].filter((box2) => box2.type === type);
6171
+ }
6172
+ function readHeifPrimaryRotation(buf) {
6173
+ try {
6174
+ const root = { type: "", start: 0, end: buf.length };
6175
+ const meta = child(buf, root, "meta")[0];
6176
+ const pitm = meta && child(buf, meta, "pitm", 4)[0];
6177
+ const iprp = meta && child(buf, meta, "iprp", 4)[0];
6178
+ const ipco = iprp && child(buf, iprp, "ipco")[0];
6179
+ if (!pitm || !ipco || !iprp) return 0;
6180
+ const primaryId = buf[pitm.start] === 0 ? buf.readUInt16BE(pitm.start + 4) : buf.readUInt32BE(pitm.start + 4);
6181
+ const props = [...boxes(buf, ipco.start, ipco.end)];
6182
+ for (const ipma of child(buf, iprp, "ipma")) {
6183
+ const version = buf[ipma.start];
6184
+ const wide = (buf[ipma.start + 3] & 1) === 1;
6185
+ let at = ipma.start + 8;
6186
+ for (let n = buf.readUInt32BE(ipma.start + 4); n > 0 && at < ipma.end; n--) {
6187
+ const id = version < 1 ? buf.readUInt16BE(at) : buf.readUInt32BE(at);
6188
+ at += version < 1 ? 2 : 4;
6189
+ const count = buf[at++];
6190
+ for (let i = 0; i < count; i++) {
6191
+ const index = wide ? buf.readUInt16BE(at) & 32767 : buf[at] & 127;
6192
+ at += wide ? 2 : 1;
6193
+ const prop = props[index - 1];
6194
+ if (id === primaryId && prop?.type === "irot")
6195
+ return (buf[prop.start] & 3) * 90;
6196
+ }
6197
+ }
6198
+ }
6199
+ } catch {
6200
+ }
6201
+ return 0;
6202
+ }
6134
6203
 
6135
6204
  // src/utils/semaphore.ts
6136
6205
  var Semaphore = class {
@@ -6145,10 +6214,10 @@ var Semaphore = class {
6145
6214
  this.active++;
6146
6215
  return () => this.release();
6147
6216
  }
6148
- return new Promise((resolve12) => {
6217
+ return new Promise((resolve13) => {
6149
6218
  this.queue.push(() => {
6150
6219
  this.active++;
6151
- resolve12(() => this.release());
6220
+ resolve13(() => this.release());
6152
6221
  });
6153
6222
  });
6154
6223
  }
@@ -6179,7 +6248,229 @@ async function withMediaProbeSlot(operation) {
6179
6248
  }
6180
6249
  }
6181
6250
 
6251
+ // src/services/heifImagePrep.ts
6252
+ var PREPARED_HEIF_SUBDIR = "_heif_images";
6253
+ var CACHE_SCHEMA2 = "hfheif-v1";
6254
+ var DEFAULT_TIMEOUT_MS = 12e4;
6255
+ var HEIF_IMAGE_CONVERSION_FAILED = "HEIF_IMAGE_CONVERSION_FAILED";
6256
+ var HEIF_TOOL_UNAVAILABLE = "HEIF_TOOL_UNAVAILABLE";
6257
+ var HEIF_BRANDS = /* @__PURE__ */ new Set(["heic", "heix", "hevc", "hevx", "heim", "heis", "mif1", "msf1"]);
6258
+ var AVIF_BRANDS = /* @__PURE__ */ new Set(["avif", "avis"]);
6259
+ var DATA_URL_RE = /^data:image\/(?:heic|heif)(?:-sequence)?;base64,(.*)$/is;
6260
+ var HeifImageConversionError = class extends Error {
6261
+ code = HEIF_IMAGE_CONVERSION_FAILED;
6262
+ owner = "user";
6263
+ retryable = false;
6264
+ constructor(label, reason) {
6265
+ super(
6266
+ `Could not convert HEIC/HEIF image ${label} to JPEG: ${reason}. Re-export the photo as JPEG or PNG and update its src.`
6267
+ );
6268
+ this.name = "HeifImageConversionError";
6269
+ }
6270
+ };
6271
+ var HeifToolUnavailableError = class extends Error {
6272
+ code = HEIF_TOOL_UNAVAILABLE;
6273
+ owner = "system";
6274
+ retryable = false;
6275
+ constructor(label, reason) {
6276
+ super(
6277
+ `Could not convert HEIC/HEIF image ${label}: ${reason}. Converting HEIC needs ffmpeg and ffprobe 7.0 or newer with the hevc decoder.`
6278
+ );
6279
+ this.name = "HeifToolUnavailableError";
6280
+ }
6281
+ };
6282
+ var TOOL_FAILURE_RE = /(?:ffmpeg|ffprobe) not found|ENOENT|Option not found|Unrecognized option|Unknown decoder|Decoder \S+ not found/i;
6283
+ function isHeifBytes(head) {
6284
+ const bytes = Buffer.from(head.buffer, head.byteOffset, head.byteLength);
6285
+ if (bytes.length < 16 || bytes.toString("ascii", 4, 8) !== "ftyp") return false;
6286
+ const boxEnd = Math.min(bytes.readUInt32BE(0), bytes.length);
6287
+ if (boxEnd < 16) return false;
6288
+ const brands = [bytes.toString("ascii", 8, 12)];
6289
+ for (let offset = 16; offset + 4 <= boxEnd; offset += 4) {
6290
+ brands.push(bytes.toString("ascii", offset, offset + 4));
6291
+ }
6292
+ return !brands.some((b) => AVIF_BRANDS.has(b)) && brands.some((b) => HEIF_BRANDS.has(b));
6293
+ }
6294
+ function readHead(path) {
6295
+ let fd;
6296
+ try {
6297
+ fd = openSync4(path, "r");
6298
+ const buf = Buffer.alloc(4096);
6299
+ return buf.subarray(0, readSync3(fd, buf, 0, buf.length, 0));
6300
+ } catch {
6301
+ return null;
6302
+ } finally {
6303
+ if (fd !== void 0) closeSync4(fd);
6304
+ }
6305
+ }
6306
+ async function probeJson(path, withGroups) {
6307
+ const stdout = await runFfprobe(
6308
+ path,
6309
+ withGroups ? ["-show_stream_groups", "-show_streams", "-of", "json"] : ["-show_streams", "-of", "json"]
6310
+ );
6311
+ return JSON.parse(stdout);
6312
+ }
6313
+ var ROTATION_FILTER = {
6314
+ 0: "",
6315
+ 90: "transpose=2",
6316
+ 180: "hflip,vflip",
6317
+ 270: "transpose=1"
6318
+ };
6319
+ function primaryGrid(probed) {
6320
+ const groups = probed.stream_groups ?? [];
6321
+ return (groups.find((g) => g.disposition?.default === 1) ?? groups[0])?.components?.[0];
6322
+ }
6323
+ async function convertHeifToJpeg(request) {
6324
+ const probed = await probeJson(request.inputPath, true);
6325
+ const grid = primaryGrid(probed);
6326
+ const rotation = readHeifPrimaryRotation(readFileSync6(request.inputPath));
6327
+ const filters = [];
6328
+ let inputs = [];
6329
+ let size = probed.streams?.[0];
6330
+ if (grid?.subcomponents?.length) {
6331
+ size = grid;
6332
+ const layout = grid.subcomponents.map(
6333
+ (t) => `${t.tile_horizontal_offset}_${t.tile_vertical_offset}`
6334
+ );
6335
+ inputs = grid.subcomponents.map((t) => `[0:${t.stream_index}]`);
6336
+ filters.push(
6337
+ `${inputs.join("")}xstack=inputs=${inputs.length}:layout=${layout.join("|")}`,
6338
+ `crop=${grid.width}:${grid.height}:${grid.horizontal_offset ?? 0}:${grid.vertical_offset ?? 0}`
6339
+ );
6340
+ }
6341
+ if (!size?.width || !size.height) throw new Error("no declared image size");
6342
+ if (rotation && grid) filters.push(ROTATION_FILTER[rotation]);
6343
+ const mapArgs = filters.length > 0 ? ["-filter_complex", `${filters.join(",")}[v]`, "-map", "[v]"] : [];
6344
+ const result = await runFfmpeg3(
6345
+ [
6346
+ "-hide_banner",
6347
+ "-loglevel",
6348
+ "error",
6349
+ "-i",
6350
+ request.inputPath,
6351
+ ...mapArgs,
6352
+ "-frames:v",
6353
+ "1",
6354
+ "-q:v",
6355
+ "2",
6356
+ "-f",
6357
+ "mjpeg",
6358
+ "-y",
6359
+ request.outputPath
6360
+ ],
6361
+ { timeout: request.timeoutMs ?? DEFAULT_TIMEOUT_MS }
6362
+ );
6363
+ if (!result.success) {
6364
+ throw result.error ?? new Error(`ffmpeg exited ${result.exitCode}: ${result.stderr.slice(-300)}`);
6365
+ }
6366
+ const out = (await probeJson(request.outputPath, false)).streams?.[0];
6367
+ const same = out?.width === size.width && out.height === size.height;
6368
+ const swapped = out?.width === size.height && out.height === size.width;
6369
+ if (!same && !swapped) {
6370
+ throw new Error(
6371
+ `wrong size: decoded ${out?.width}x${out?.height}, file declares ${size.width}x${size.height}`
6372
+ );
6373
+ }
6374
+ }
6375
+ function resolveImageSource(src, options) {
6376
+ const data = DATA_URL_RE.exec(src);
6377
+ if (data) {
6378
+ const bytes = Buffer.from(data[1], "base64");
6379
+ return isHeifBytes(bytes.subarray(0, 4096)) ? { path: null, bytes, label: '"inline image"' } : null;
6380
+ }
6381
+ const path = resolveLocalImageSourcePath(src, options);
6382
+ const head = path ? readHead(path) : null;
6383
+ if (!path || !head || !isHeifBytes(head)) return null;
6384
+ return { path, bytes: readFileSync6(path), label: `"${splitUrlSuffix(src).basePath}"` };
6385
+ }
6386
+ function isUsableFile2(path) {
6387
+ try {
6388
+ return statSync6(path).size > 0;
6389
+ } catch {
6390
+ return false;
6391
+ }
6392
+ }
6393
+ async function ensurePreparedJpeg(source, cachePath, outputPath, options) {
6394
+ mkdirSync6(dirname6(cachePath), { recursive: true });
6395
+ mkdirSync6(dirname6(outputPath), { recursive: true });
6396
+ if (!isUsableFile2(cachePath)) {
6397
+ const tmpPath = `${cachePath}.tmp-${process.pid}-${Date.now()}`;
6398
+ const inlinePath = `${tmpPath}.heic`;
6399
+ const inputPath = source.path ?? inlinePath;
6400
+ try {
6401
+ if (!source.path) writeFileSync4(inlinePath, source.bytes);
6402
+ await (options.transcode ?? convertHeifToJpeg)({
6403
+ inputPath,
6404
+ outputPath: tmpPath,
6405
+ timeoutMs: options.timeoutMs
6406
+ });
6407
+ if (!isUsableFile2(tmpPath)) throw new Error("conversion produced an empty file");
6408
+ renameWithRetry4(tmpPath, cachePath);
6409
+ } catch (error) {
6410
+ const reason = error instanceof Error ? error.message : String(error);
6411
+ throw TOOL_FAILURE_RE.test(reason) ? new HeifToolUnavailableError(source.label, reason) : new HeifImageConversionError(source.label, reason);
6412
+ } finally {
6413
+ rmSync8(tmpPath, { force: true });
6414
+ rmSync8(inlinePath, { force: true });
6415
+ }
6416
+ }
6417
+ if (resolve6(outputPath) !== resolve6(cachePath) && !isUsableFile2(outputPath)) {
6418
+ copyFileSync3(cachePath, outputPath);
6419
+ }
6420
+ }
6421
+ async function prepareHeifImageInputs(html, options) {
6422
+ const outputDir = options.outputDir ?? join14(options.downloadDir, PREPARED_HEIF_SUBDIR);
6423
+ const outputSrcPrefix = normalizeRelPath(options.outputSrcPrefix ?? PREPARED_HEIF_SUBDIR);
6424
+ const cacheDir = options.cacheDir ?? outputDir;
6425
+ const preparedAssets = /* @__PURE__ */ new Map();
6426
+ const preparedImages = [];
6427
+ if (!/<img\b/i.test(html)) return { html, preparedAssets, preparedImages };
6428
+ const { document: document2 } = parseHTML4(html);
6429
+ const found = [];
6430
+ for (const img of Array.from(document2.querySelectorAll("img[src]"))) {
6431
+ const src = (img.getAttribute("src") || "").trim();
6432
+ const source = src ? resolveImageSource(src, options) : null;
6433
+ if (!source) continue;
6434
+ const hash = createHash5("sha256").update(CACHE_SCHEMA2).update("\0").update(source.bytes).digest("hex");
6435
+ found.push({ img, src, source, filename: `${CACHE_SCHEMA2}-${hash.slice(0, 24)}.jpg` });
6436
+ }
6437
+ if (found.length === 0) return { html, preparedAssets, preparedImages };
6438
+ const unique = new Map(found.map((item) => [item.filename, item.source]));
6439
+ const failed = /* @__PURE__ */ new Set();
6440
+ await Promise.all(
6441
+ [...unique].map(
6442
+ ([filename, source]) => withMediaProbeSlot(
6443
+ () => ensurePreparedJpeg(source, join14(cacheDir, filename), join14(outputDir, filename), options)
6444
+ ).catch((error) => {
6445
+ if (!options.onFailure) throw error;
6446
+ failed.add(filename);
6447
+ options.onFailure(error);
6448
+ })
6449
+ )
6450
+ );
6451
+ for (const { img, src, filename } of found) {
6452
+ if (failed.has(filename)) continue;
6453
+ const outputSrc = `${outputSrcPrefix}/${filename}`;
6454
+ img.setAttribute("src", outputSrc);
6455
+ preparedAssets.set(outputSrc, join14(outputDir, filename));
6456
+ preparedImages.push({ sourceSrc: src, outputSrc, outputPath: join14(outputDir, filename) });
6457
+ }
6458
+ return { html: document2.toString(), preparedAssets, preparedImages };
6459
+ }
6460
+
6461
+ // src/services/htmlCompiler.ts
6462
+ import { createStudioPositionSeekReapplyScript } from "@hyperframes/studio-server/manual-edits-render-script";
6463
+ import { getPositionEditsRenderScript } from "@hyperframes/core/runtime/position-edits-render";
6464
+
6182
6465
  // src/services/assetMediaType.ts
6466
+ import { existsSync as existsSync9 } from "node:fs";
6467
+ import {
6468
+ fingerprintElementId,
6469
+ isNotMediaPayload,
6470
+ NotMediaPayloadError,
6471
+ probeMediaProfile,
6472
+ resolveProjectRelativeSrc as resolveProjectRelativeSrc2
6473
+ } from "@hyperframes/engine";
6183
6474
  var ASSET_MEDIA_TYPE_MISMATCH = "ASSET_MEDIA_TYPE_MISMATCH";
6184
6475
  var AssetMediaTypeMismatchError = class extends Error {
6185
6476
  code = ASSET_MEDIA_TYPE_MISMATCH;
@@ -6289,7 +6580,7 @@ function logRemoteDownloadTelemetry(event) {
6289
6580
  }
6290
6581
  var INFERRED_MEDIA_DURATION_ATTR = "data-hf-inferred-duration";
6291
6582
  function parseSubCompHtmlForValidity(html) {
6292
- return parseHTML4(html).document;
6583
+ return parseHTML5(html).document;
6293
6584
  }
6294
6585
  function endsWithSourceUrl(code) {
6295
6586
  const trimmed = code.trimEnd();
@@ -6297,7 +6588,7 @@ function endsWithSourceUrl(code) {
6297
6588
  return lastLine.startsWith("//# sourceURL=") || lastLine.startsWith("//@ sourceURL=");
6298
6589
  }
6299
6590
  function prepareCompositionScripts(html) {
6300
- const { document: document2 } = parseHTML4(html);
6591
+ const { document: document2 } = parseHTML5(html);
6301
6592
  for (const el of document2.querySelectorAll("script:not([src])")) {
6302
6593
  const isModule = (el.getAttribute("type") || "").trim().toLowerCase() === "module";
6303
6594
  const runsAsScript = isModule || isJavaScriptType(el);
@@ -6334,14 +6625,14 @@ Check that each file referenced by data-composition-src contains valid HTML with
6334
6625
  }
6335
6626
  };
6336
6627
  function assertSubCompositionsUsable(html, projectDir, visited = /* @__PURE__ */ new Set()) {
6337
- const { document: document2 } = parseHTML4(html);
6628
+ const { document: document2 } = parseHTML5(html);
6338
6629
  const hosts = [...document2.querySelectorAll("[data-composition-src]")];
6339
6630
  const problems = [];
6340
6631
  for (const el of hosts) {
6341
6632
  const srcPath = el.getAttribute("data-composition-src");
6342
6633
  if (!srcPath) continue;
6343
6634
  if (isUnresolvedAssetPlaceholder(srcPath)) continue;
6344
- const filePath = resolve6(projectDir, srcPath);
6635
+ const filePath = resolve7(projectDir, srcPath);
6345
6636
  if (visited.has(filePath)) continue;
6346
6637
  const read = readProjectFile(filePath);
6347
6638
  if (read.kind === "missing") {
@@ -6397,7 +6688,7 @@ function runtimeRoot(document2) {
6397
6688
  }
6398
6689
  function detectRenderModeHints(html, pageMotionBlur) {
6399
6690
  const reasons = [];
6400
- const { document: document2 } = parseHTML4(html);
6691
+ const { document: document2 } = parseHTML5(html);
6401
6692
  if (document2.querySelector("canvas[layoutsubtree]")) {
6402
6693
  reasons.push({
6403
6694
  code: "htmlInCanvas",
@@ -6439,7 +6730,7 @@ function detectThreeDTransformUsage(html) {
6439
6730
  }
6440
6731
  var BACKGROUND_IMAGE_DECL_PATTERN = /(?:^|;|\{)\s*background(?:-image)?\s*:[^;}]*(?:\bgradient\s*\(|url\s*\()/i;
6441
6732
  function detectAncestorBackgroundImage(html) {
6442
- const { document: document2 } = parseHTML4(html);
6733
+ const { document: document2 } = parseHTML5(html);
6443
6734
  const root = document2.querySelector("[data-composition-id]");
6444
6735
  if (!root) return false;
6445
6736
  const ancestors = [];
@@ -6486,7 +6777,7 @@ function detectShaderTransitionUsage(html) {
6486
6777
  async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
6487
6778
  let filePath = src;
6488
6779
  if (isHttpUrl(src)) {
6489
- if (!existsSync10(downloadDir)) mkdirSync6(downloadDir, { recursive: true });
6780
+ if (!existsSync10(downloadDir)) mkdirSync7(downloadDir, { recursive: true });
6490
6781
  try {
6491
6782
  filePath = await downloadToTemp(src, downloadDir, void 0, void 0, void 0, {
6492
6783
  onTelemetry: logRemoteDownloadTelemetry
@@ -6496,7 +6787,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
6496
6787
  }
6497
6788
  } else {
6498
6789
  filePath = decodedUrlPath(src);
6499
- if (!filePath.startsWith("/")) filePath = join14(baseDir, filePath);
6790
+ if (!filePath.startsWith("/")) filePath = join15(baseDir, filePath);
6500
6791
  filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
6501
6792
  }
6502
6793
  if (!isHttpUrl(filePath) && !existsSync10(filePath)) {
@@ -6553,7 +6844,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
6553
6844
  function markInferredVariableMediaDurations(html, unresolvedMedia) {
6554
6845
  const unresolvedIds = new Set(unresolvedMedia.map((element) => element.id));
6555
6846
  if (unresolvedIds.size === 0) return html;
6556
- const { document: document2 } = parseHTML4(html);
6847
+ const { document: document2 } = parseHTML5(html);
6557
6848
  let changed = false;
6558
6849
  for (const element of document2.querySelectorAll("video[data-var-src], audio[data-var-src]")) {
6559
6850
  if (!unresolvedIds.has(element.id)) continue;
@@ -6624,13 +6915,13 @@ async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocal
6624
6915
  }
6625
6916
  async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath) {
6626
6917
  const subCompositions = /* @__PURE__ */ new Map();
6627
- const { document: document2 } = parseHTML4(html);
6918
+ const { document: document2 } = parseHTML5(html);
6628
6919
  const compEls = document2.querySelectorAll("[data-composition-src]");
6629
6920
  const workItems = [];
6630
6921
  for (const el of compEls) {
6631
6922
  const srcPath = el.getAttribute("data-composition-src");
6632
6923
  if (!srcPath) continue;
6633
- const filePath = resolve6(projectDir, srcPath);
6924
+ const filePath = resolve7(projectDir, srcPath);
6634
6925
  if (visited.has(filePath)) {
6635
6926
  continue;
6636
6927
  }
@@ -6647,7 +6938,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
6647
6938
  workItems.map(async (item) => {
6648
6939
  const { html: compiledSub } = await compileHtmlFile(
6649
6940
  rewriteUnresolvableGsapToCdn(item.rawSubHtml, projectDir, item.srcPath),
6650
- dirname6(item.filePath),
6941
+ dirname7(item.filePath),
6651
6942
  downloadDir,
6652
6943
  void 0,
6653
6944
  urlByAbsentLocalPath
@@ -6675,7 +6966,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
6675
6966
  return { subCompositions };
6676
6967
  }
6677
6968
  function promoteCssImportsToLinkTags(html) {
6678
- const { document: document2 } = parseHTML4(html);
6969
+ const { document: document2 } = parseHTML5(html);
6679
6970
  const head = document2.querySelector("head");
6680
6971
  if (!head) return html;
6681
6972
  const importRe = /@import\s+url\(\s*['"]?([^'")\s]+)['"]?\s*\)\s*;?/gi;
@@ -6744,7 +7035,7 @@ var ProducerHostIdentityMap = class extends Map {
6744
7035
  }
6745
7036
  };
6746
7037
  function coalesceHeadStylesAndBodyScripts(html) {
6747
- const { document: document2 } = parseHTML4(html);
7038
+ const { document: document2 } = parseHTML5(html);
6748
7039
  const head = document2.querySelector("head");
6749
7040
  const body = document2.querySelector("body");
6750
7041
  if (!head && !body) return html;
@@ -6795,7 +7086,7 @@ function coalesceHeadStylesAndBodyScripts(html) {
6795
7086
  return document2.toString();
6796
7087
  }
6797
7088
  function inlineSubCompositions(html, subCompositions, projectDir, variableOverrides = {}) {
6798
- const { document: document2 } = parseHTML4(html);
7089
+ const { document: document2 } = parseHTML5(html);
6799
7090
  const head = document2.querySelector("head");
6800
7091
  const body = document2.querySelector("body");
6801
7092
  const hosts = Array.from(document2.querySelectorAll("[data-composition-src]"));
@@ -6824,17 +7115,17 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
6824
7115
  resolveHtml: (srcPath) => {
6825
7116
  let compHtml = subCompositions.get(srcPath) || null;
6826
7117
  if (!compHtml) {
6827
- const read = readProjectFile(resolve6(projectDir, srcPath));
7118
+ const read = readProjectFile(resolve7(projectDir, srcPath));
6828
7119
  if (read.kind === "file") {
6829
7120
  compHtml = rewriteUnresolvableGsapToCdn(read.text, projectDir, srcPath);
6830
7121
  }
6831
7122
  }
6832
7123
  return compHtml;
6833
7124
  },
6834
- parseHtml: (htmlStr) => parseHTML4(htmlStr).document,
7125
+ parseHtml: (htmlStr) => parseHTML5(htmlStr).document,
6835
7126
  // Mirrors the preview bundler: a sub-composition's SIBLING assets resolve
6836
7127
  // against its own directory, project-root refs stay as authored.
6837
- assetExists: (path) => existsSync10(resolve6(projectDir, path)),
7128
+ assetExists: (path) => existsSync10(resolve7(projectDir, path)),
6838
7129
  // Preserve the authored root wrapper as a child of the host, matching
6839
7130
  // the preview bundler's shape (htmlBundler.ts's prepareFlattenedInnerRoot,
6840
7131
  // which the runtime compositionLoader mirrors with its own copy for the
@@ -6935,7 +7226,7 @@ ${html}
6935
7226
  </html>`;
6936
7227
  }
6937
7228
  function injectTextRenderingRule(html) {
6938
- const { document: document2 } = parseHTML4(html);
7229
+ const { document: document2 } = parseHTML5(html);
6939
7230
  const head = document2.querySelector("head");
6940
7231
  if (!head) return html;
6941
7232
  if (document2.querySelector("style[data-hyperframes-text-rendering]")) {
@@ -6959,7 +7250,7 @@ function matchesScriptIntegrity(bytes, metadata) {
6959
7250
  (alg) => hashes.some((hash) => hash[1]?.toLowerCase() === alg)
6960
7251
  );
6961
7252
  if (!strongest) return true;
6962
- const actual = createHash5(strongest).update(bytes).digest();
7253
+ const actual = createHash6(strongest).update(bytes).digest();
6963
7254
  return hashes.some(
6964
7255
  (hash) => hash[1]?.toLowerCase() === strongest && actual.equals(Buffer.from(hash[2], "base64"))
6965
7256
  );
@@ -6967,7 +7258,7 @@ function matchesScriptIntegrity(bytes, metadata) {
6967
7258
  async function inlineExternalScripts(html) {
6968
7259
  const fullHtml = ensureFullDocument(html);
6969
7260
  const wrappedFragment = fullHtml !== html;
6970
- const { document: document2 } = parseHTML4(fullHtml);
7261
+ const { document: document2 } = parseHTML5(fullHtml);
6971
7262
  const scripts = document2.querySelectorAll("script[src]");
6972
7263
  const externalScripts = [];
6973
7264
  for (const el of scripts) {
@@ -7016,12 +7307,12 @@ ${safeText}
7016
7307
  return wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
7017
7308
  }
7018
7309
  function collectExternalAssets(html, projectDir) {
7019
- const absProjectDir = resolve6(projectDir);
7310
+ const absProjectDir = resolve7(projectDir);
7020
7311
  const externalAssets = /* @__PURE__ */ new Map();
7021
7312
  function processPath(rawPath) {
7022
7313
  const trimmed = rawPath.trim();
7023
7314
  if (isNonRelativeUrl(trimmed)) return null;
7024
- const absPath = resolve6(absProjectDir, trimmed);
7315
+ const absPath = resolve7(absProjectDir, trimmed);
7025
7316
  if (isPathInside2(absPath, absProjectDir)) {
7026
7317
  return null;
7027
7318
  }
@@ -7030,7 +7321,7 @@ function collectExternalAssets(html, projectDir) {
7030
7321
  externalAssets.set(safeKey, absPath);
7031
7322
  return safeKey;
7032
7323
  }
7033
- const { document: document2 } = parseHTML4(html);
7324
+ const { document: document2 } = parseHTML5(html);
7034
7325
  for (const el of document2.querySelectorAll("[src], [href]")) {
7035
7326
  for (const attr of ["src", "href"]) {
7036
7327
  const val = (el.getAttribute(attr) || "").trim();
@@ -7075,7 +7366,7 @@ var REMOTE_SOURCE_TAG_RE = /<source\b[^>]*?\bsrc\s*=\s*["'](https?:\/\/[^"']+)["
7075
7366
  var REMOTE_IMG_TAG_RE = /<img\b[^>]*?(?<![\w-])src\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi;
7076
7367
  async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLabel, extraRewrite) {
7077
7368
  if (urlSet.size === 0) return { html, remoteMediaAssets: /* @__PURE__ */ new Map() };
7078
- if (!existsSync10(remoteDir)) mkdirSync6(remoteDir, { recursive: true });
7369
+ if (!existsSync10(remoteDir)) mkdirSync7(remoteDir, { recursive: true });
7079
7370
  const urlToLocal = /* @__PURE__ */ new Map();
7080
7371
  await Promise.all(
7081
7372
  [...urlSet].map(async (url) => {
@@ -7122,7 +7413,7 @@ async function localizeRemoteMediaSources(html, downloadDir) {
7122
7413
  return downloadAndRewriteUrls(
7123
7414
  urlSet,
7124
7415
  html,
7125
- join14(downloadDir, REMOTE_MEDIA_SUBDIR),
7416
+ join15(downloadDir, REMOTE_MEDIA_SUBDIR),
7126
7417
  "Remote media download failed",
7127
7418
  "Localized remote media source(s)"
7128
7419
  );
@@ -7137,7 +7428,7 @@ async function localizeRemoteImageSources(html, downloadDir) {
7137
7428
  return downloadAndRewriteUrls(
7138
7429
  urlSet,
7139
7430
  html,
7140
- join14(downloadDir, REMOTE_MEDIA_SUBDIR),
7431
+ join15(downloadDir, REMOTE_MEDIA_SUBDIR),
7141
7432
  "Remote image download failed",
7142
7433
  "Localized remote image source(s)"
7143
7434
  );
@@ -7153,7 +7444,7 @@ async function localizeRemoteBackgroundImages(html, downloadDir) {
7153
7444
  return downloadAndRewriteUrls(
7154
7445
  urlSet,
7155
7446
  html,
7156
- join14(downloadDir, REMOTE_MEDIA_SUBDIR),
7447
+ join15(downloadDir, REMOTE_MEDIA_SUBDIR),
7157
7448
  "Remote background-image download failed",
7158
7449
  "Localized remote background-image(s)",
7159
7450
  // Quoted url('..')/url("..") are rewritten by downloadAndRewriteUrls' default
@@ -7288,7 +7579,7 @@ async function localizeRemoteFontFaces(html, downloadDir) {
7288
7579
  return downloadAndRewriteUrls(
7289
7580
  urlSet,
7290
7581
  processed,
7291
- join14(downloadDir, REMOTE_MEDIA_SUBDIR),
7582
+ join15(downloadDir, REMOTE_MEDIA_SUBDIR),
7292
7583
  "Remote font download failed",
7293
7584
  "Localized remote font face(s)",
7294
7585
  (h, url, relPath) => h.replaceAll(`url(${url})`, `url("${relPath}")`)
@@ -7333,7 +7624,7 @@ async function embedLocalFontFaces(html, projectDir) {
7333
7624
  while ((urlMatch = urlRe.exec(block)) !== null) {
7334
7625
  const localPath = urlMatch[1];
7335
7626
  if (!localPath || embeddedPaths.has(localPath)) continue;
7336
- const absPath = localPath.startsWith("/") ? localPath : resolve6(projectDir, localPath);
7627
+ const absPath = localPath.startsWith("/") ? localPath : resolve7(projectDir, localPath);
7337
7628
  if (!isPathInside2(absPath, projectDir)) continue;
7338
7629
  const ext = absPath.match(/\.(woff2?|ttf|otf|ttc)$/i)?.[1]?.toLowerCase() ?? "ttf";
7339
7630
  try {
@@ -7382,7 +7673,7 @@ function renderGsapDist() {
7382
7673
  }
7383
7674
  function rewriteUnresolvableGsapToCdn(html, projectDir, compSrcPath) {
7384
7675
  let gsapDist;
7385
- const exists = (path) => existsSync10(resolve6(projectDir, path));
7676
+ const exists = (path) => existsSync10(resolve7(projectDir, path));
7386
7677
  return html.replace(
7387
7678
  /(<script\b[^>]*\bsrc=["'])([^"']*gsap[^"']*\/dist\/([^"']+))(["'][^>]*>)/gi,
7388
7679
  (full, prefix, src, file, suffix) => {
@@ -7398,8 +7689,8 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
7398
7689
  if (!isPathInside2(htmlPath, projectDir)) return html;
7399
7690
  const entryPath = relative2(projectDir, htmlPath).replace(/\\/g, "/");
7400
7691
  if (!entryPath.includes("/")) return html;
7401
- const { document: document2 } = parseHTML4(html);
7402
- const assetExists = (path) => existsSync10(resolve6(projectDir, path));
7692
+ const { document: document2 } = parseHTML5(html);
7693
+ const assetExists = (path) => existsSync10(resolve7(projectDir, path));
7403
7694
  rewriteAssetPaths(
7404
7695
  document2.querySelectorAll("[src], [href]"),
7405
7696
  entryPath,
@@ -7421,7 +7712,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
7421
7712
  }
7422
7713
  async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
7423
7714
  const entryHtml = rebaseDirectEntryAssetPaths(
7424
- readFileSync6(htmlPath, "utf-8"),
7715
+ readFileSync7(htmlPath, "utf-8"),
7425
7716
  projectDir,
7426
7717
  htmlPath
7427
7718
  );
@@ -7491,12 +7782,26 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
7491
7782
  const { html: htmlWithLocalImages, remoteMediaAssets: remoteImageAssets } = await localizeRemoteImageSources(htmlWithLocalMedia, downloadDir);
7492
7783
  const { html: htmlWithLocalBg, remoteMediaAssets: remoteBgAssets } = await localizeRemoteBackgroundImages(htmlWithLocalImages, downloadDir);
7493
7784
  const { html: htmlWithLocalizedFonts, remoteMediaAssets: remoteFontAssets } = await localizeRemoteFontFaces(htmlWithLocalBg, downloadDir);
7785
+ const {
7786
+ html: htmlWithJpegs,
7787
+ preparedAssets: preparedHeifAssets,
7788
+ preparedImages: preparedHeifImages
7789
+ } = await prepareHeifImageInputs(htmlWithLocalizedFonts, {
7790
+ projectDir,
7791
+ downloadDir,
7792
+ cacheDir: options.animatedGifCacheDir,
7793
+ sourceAssets: remoteImageAssets,
7794
+ timeoutMs: options.ffmpegProcessTimeout
7795
+ });
7796
+ if (preparedHeifImages.length > 0) {
7797
+ defaultLogger.info(`[Compiler] Converted ${preparedHeifImages.length} HEIC image(s) to JPEG`);
7798
+ }
7494
7799
  const gifSourceAssets = new Map(remoteImageAssets);
7495
7800
  const {
7496
7801
  html: htmlWithPreparedGifs,
7497
7802
  preparedAssets: preparedGifAssets,
7498
7803
  preparedGifs
7499
- } = await prepareAnimatedGifInputs(htmlWithLocalizedFonts, {
7804
+ } = await prepareAnimatedGifInputs(htmlWithJpegs, {
7500
7805
  projectDir,
7501
7806
  downloadDir,
7502
7807
  cacheDir: options.animatedGifCacheDir,
@@ -7523,11 +7828,14 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
7523
7828
  for (const [relPath, absPath] of preparedGifAssets) {
7524
7829
  externalAssets.set(relPath, absPath);
7525
7830
  }
7831
+ for (const [relPath, absPath] of preparedHeifAssets) {
7832
+ externalAssets.set(relPath, absPath);
7833
+ }
7526
7834
  const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
7527
7835
  const masterFxChain = parseMasterFxChain(html);
7528
7836
  for (const video of videos) {
7529
7837
  if (isHttpUrl(video.src)) continue;
7530
- const videoPath = resolve6(projectDir, video.src);
7838
+ const videoPath = resolve7(projectDir, video.src);
7531
7839
  const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
7532
7840
  (async () => {
7533
7841
  const metadata = await withMediaProbeSlot(() => extractMediaMetadata(videoPath));
@@ -7547,7 +7855,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
7547
7855
  })().catch(() => {
7548
7856
  });
7549
7857
  }
7550
- const { document: document2 } = parseHTML4(html);
7858
+ const { document: document2 } = parseHTML5(html);
7551
7859
  const rootEl = document2.querySelector("[data-composition-id]");
7552
7860
  const width = rootEl ? parseInt(rootEl.getAttribute("data-width") || "1080", 10) : 1080;
7553
7861
  const height = rootEl ? parseInt(rootEl.getAttribute("data-height") || "1920", 10) : 1920;
@@ -7954,7 +8262,7 @@ async function runCompileStage(input) {
7954
8262
  abortSignal
7955
8263
  } = input;
7956
8264
  const compileStart = Date.now();
7957
- const compiled = await compileForRender(projectDir, htmlPath, join15(workDir, "downloads"), {
8265
+ const compiled = await compileForRender(projectDir, htmlPath, join16(workDir, "downloads"), {
7958
8266
  log,
7959
8267
  failClosedFontFetch: failClosedFontFetch === true,
7960
8268
  allowSystemFontCapture,
@@ -7962,7 +8270,7 @@ async function runCompileStage(input) {
7962
8270
  variables: input.variables,
7963
8271
  urlByAbsentLocalPath: input.urlByAbsentLocalPath,
7964
8272
  pageMotionBlur: input.pageMotionBlur,
7965
- animatedGifCacheDir: cfg.extractCacheDir ? join15(cfg.extractCacheDir, "animated-gif") : void 0,
8273
+ animatedGifCacheDir: cfg.extractCacheDir ? join16(cfg.extractCacheDir, "animated-gif") : void 0,
7966
8274
  ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
7967
8275
  });
7968
8276
  assertNotAborted();
@@ -8018,7 +8326,7 @@ async function runCompileStage(input) {
8018
8326
  };
8019
8327
  await preflightCompositionAssetMediaTypes({
8020
8328
  projectDir,
8021
- compiledDir: join15(workDir, "compiled"),
8329
+ compiledDir: join16(workDir, "compiled"),
8022
8330
  composition,
8023
8331
  signal: abortSignal
8024
8332
  });
@@ -8063,8 +8371,8 @@ async function runCompileStage(input) {
8063
8371
  }
8064
8372
 
8065
8373
  // src/services/render/stages/probeStage.ts
8066
- import { join as join16 } from "node:path";
8067
- import { parseHTML as parseHTML5 } from "linkedom";
8374
+ import { join as join17 } from "node:path";
8375
+ import { parseHTML as parseHTML6 } from "linkedom";
8068
8376
  import {
8069
8377
  closeCaptureSession as closeCaptureSession4,
8070
8378
  compositionRequiresWebGpu,
@@ -8111,7 +8419,7 @@ function durationToFrameCount(duration, fps) {
8111
8419
  }
8112
8420
  function hasScriptedAudioVolumeAutomation(html, audioCount) {
8113
8421
  if (audioCount <= 0) return false;
8114
- const { document: document2 } = parseHTML5(html);
8422
+ const { document: document2 } = parseHTML6(html);
8115
8423
  const scriptBodies = [...document2.querySelectorAll("script")].map((script) => script.textContent ?? "").join("\n");
8116
8424
  if (!scriptBodies) return false;
8117
8425
  return /\.\s*volume\s*=/i.test(scriptBodies) || /\b(?:gsap|tl|timeline|tween)\s*\.\s*(?:to|fromTo|set)\s*\([\s\S]{0,2000}\bvolume\s*:/i.test(
@@ -8119,12 +8427,12 @@ function hasScriptedAudioVolumeAutomation(html, audioCount) {
8119
8427
  );
8120
8428
  }
8121
8429
  function hasAutoStartVideos(html) {
8122
- const { document: document2 } = parseHTML5(html);
8430
+ const { document: document2 } = parseHTML6(html);
8123
8431
  return document2.querySelector("video[data-hf-auto-start]") !== null;
8124
8432
  }
8125
8433
  function hasVariableBoundMedia(html, variables) {
8126
8434
  if (!variables || Object.keys(variables).length === 0) return false;
8127
- const { document: document2 } = parseHTML5(html);
8435
+ const { document: document2 } = parseHTML6(html);
8128
8436
  return Array.from(
8129
8437
  document2.querySelectorAll(
8130
8438
  "img[data-var-src], audio[data-var-src], video[data-var-src], source[data-var-src]"
@@ -8135,7 +8443,7 @@ function hasVariableBoundMedia(html, variables) {
8135
8443
  });
8136
8444
  }
8137
8445
  function authoredMediaStarts(html) {
8138
- const { document: document2 } = parseHTML5(html);
8446
+ const { document: document2 } = parseHTML6(html);
8139
8447
  const starts = /* @__PURE__ */ new Map();
8140
8448
  for (const media of document2.querySelectorAll("video, audio")) {
8141
8449
  const id = media.getAttribute(MEDIA_RENDER_ID_ATTR2) || media.getAttribute("id");
@@ -8172,7 +8480,7 @@ function reconcileBrowserMediaEnd(existingEnd, projectedEnd, sourceChanged, dura
8172
8480
  return existingEnd <= 0 ? projectedEnd : Math.min(existingEnd, projectedEnd);
8173
8481
  }
8174
8482
  function hasRuntimeMediaChanges(html) {
8175
- const { document: document2 } = parseHTML5(html);
8483
+ const { document: document2 } = parseHTML6(html);
8176
8484
  const scriptBodies = [...document2.querySelectorAll("script")].map(
8177
8485
  (script) => script.textContent ?? ""
8178
8486
  );
@@ -8255,7 +8563,7 @@ async function runProbeStage(input) {
8255
8563
  });
8256
8564
  fileServer = await createFileServer({
8257
8565
  projectDir,
8258
- compiledDir: join16(workDir, "compiled"),
8566
+ compiledDir: join17(workDir, "compiled"),
8259
8567
  port: 0,
8260
8568
  preHeadScripts: [VIRTUAL_TIME_SHIM],
8261
8569
  fps: job.config.fps
@@ -8284,7 +8592,7 @@ async function runProbeStage(input) {
8284
8592
  log.info("Creating capture session...", { attempt, maxAttempts: PROBE_MAX_ATTEMPTS });
8285
8593
  probeSession = await createCaptureSession4(
8286
8594
  fileServer.url,
8287
- join16(workDir, "probe"),
8595
+ join17(workDir, "probe"),
8288
8596
  captureOpts,
8289
8597
  null,
8290
8598
  probeCfg
@@ -8369,7 +8677,7 @@ async function runProbeStage(input) {
8369
8677
  });
8370
8678
  probeSession = await createCaptureSession4(
8371
8679
  fileServer.url,
8372
- join16(workDir, "probe-screenshot"),
8680
+ join17(workDir, "probe-screenshot"),
8373
8681
  captureOpts,
8374
8682
  null,
8375
8683
  { ...probeCfg, forceScreenshot: true }
@@ -8405,7 +8713,7 @@ async function runProbeStage(input) {
8405
8713
  compiled,
8406
8714
  resolutions,
8407
8715
  projectDir,
8408
- join16(workDir, "downloads")
8716
+ join17(workDir, "downloads")
8409
8717
  );
8410
8718
  assertNotAborted();
8411
8719
  composition.videos = compiled.videos;
@@ -8596,7 +8904,7 @@ async function runProbeStage(input) {
8596
8904
  }
8597
8905
  await preflightCompositionAssetMediaTypes({
8598
8906
  projectDir,
8599
- compiledDir: join16(workDir, "compiled"),
8907
+ compiledDir: join17(workDir, "compiled"),
8600
8908
  composition,
8601
8909
  signal: abortSignal
8602
8910
  });
@@ -8761,7 +9069,7 @@ function validateDistributedDuration(input) {
8761
9069
 
8762
9070
  // src/services/render/stages/extractVideosStage.ts
8763
9071
  import { existsSync as existsSync11 } from "node:fs";
8764
- import { join as join17 } from "node:path";
9072
+ import { join as join18 } from "node:path";
8765
9073
  import {
8766
9074
  classifyVideoExtractionError,
8767
9075
  createFrameLookupTable,
@@ -9081,14 +9389,14 @@ async function runExtractVideosStage(input) {
9081
9389
  // because short boundary counts can differ by one frame.
9082
9390
  {
9083
9391
  fps: job.config.fps,
9084
- outputDir: join17(compiledDir, "__hyperframes_video_frames"),
9392
+ outputDir: join18(compiledDir, "__hyperframes_video_frames"),
9085
9393
  format: job.config.videoFrameFormat ?? "auto",
9086
9394
  toneMapHdrToSdr: job.config.hdrMode === "force-sdr",
9087
9395
  timelineEnd: composition.duration,
9088
9396
  maxTransientRetries: extractionPolicy.maxTransientRetries,
9089
9397
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
9090
9398
  deferRangeExtraction,
9091
- contentKeyedDirs: [join17(compiledDir, REMOTE_MEDIA_SUBDIR)],
9399
+ contentKeyedDirs: [join18(compiledDir, REMOTE_MEDIA_SUBDIR)],
9092
9400
  urlByAbsentLocalPath
9093
9401
  },
9094
9402
  abortSignal,
@@ -9155,8 +9463,8 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
9155
9463
  }
9156
9464
 
9157
9465
  // src/services/render/stages/audioStage.ts
9158
- import { join as join19 } from "node:path";
9159
- import { rmSync as rmSync9 } from "node:fs";
9466
+ import { join as join20 } from "node:path";
9467
+ import { rmSync as rmSync10 } from "node:fs";
9160
9468
  import {
9161
9469
  MIXED_AUDIO_FILENAME,
9162
9470
  audioFailureDiagnostics,
@@ -9165,8 +9473,8 @@ import {
9165
9473
 
9166
9474
  // src/services/render/audioPadTrim.ts
9167
9475
  import { spawn } from "node:child_process";
9168
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync8 } from "node:fs";
9169
- import { dirname as dirname7, join as join18 } from "node:path";
9476
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync9 } from "node:fs";
9477
+ import { dirname as dirname8, join as join19 } from "node:path";
9170
9478
  import {
9171
9479
  DEFAULT_CONFIG,
9172
9480
  describeFfmpegFailure as describeFfmpegFailure2,
@@ -9174,8 +9482,8 @@ import {
9174
9482
  extractAudioMetadata as extractAudioMetadata3,
9175
9483
  getFfprobeBinary,
9176
9484
  ManagedChildProcess,
9177
- renameWithRetry as renameWithRetry4,
9178
- runFfmpeg as runFfmpeg4,
9485
+ renameWithRetry as renameWithRetry5,
9486
+ runFfmpeg as runFfmpeg5,
9179
9487
  trackChildProcess
9180
9488
  } from "@hyperframes/engine";
9181
9489
  import { redactKnownPaths, redactTelemetryString as redactTelemetryString3 } from "@hyperframes/core";
@@ -9185,7 +9493,7 @@ import {
9185
9493
  aacSegmentCount,
9186
9494
  describeFfmpegFailure,
9187
9495
  extractAudioMetadata as extractAudioMetadata2,
9188
- runFfmpeg as runFfmpeg3
9496
+ runFfmpeg as runFfmpeg4
9189
9497
  } from "@hyperframes/engine";
9190
9498
  var toDbfs = (token) => token.toLowerCase() === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
9191
9499
  function checkedDbfs(value) {
@@ -9209,7 +9517,7 @@ async function scanTruePeak(audioPath, trim, timeoutMs, signal) {
9209
9517
  else laterBlocks = Math.max(laterBlocks, block);
9210
9518
  }
9211
9519
  };
9212
- const result = await runFfmpeg3(
9520
+ const result = await runFfmpeg4(
9213
9521
  ["-i", audioPath, "-map", "0:a:0", "-vn", "-af", `${trim}ebur128=peak=true`, "-f", "null", "-"],
9214
9522
  { signal, timeout: timeoutMs, onStderr: readLines }
9215
9523
  );
@@ -9369,7 +9677,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
9369
9677
  error: `audioPadTrim: failed to materialize ${plan2.operation}: ${err instanceof Error ? err.message : String(err)}`
9370
9678
  };
9371
9679
  } finally {
9372
- for (const path of plan2.cleanupPaths) rmSync8(path, { force: true });
9680
+ for (const path of plan2.cleanupPaths) rmSync9(path, { force: true });
9373
9681
  }
9374
9682
  let loweredDb = 0;
9375
9683
  if (probeTruePeak) {
@@ -9416,8 +9724,8 @@ async function constrainAacTruePeak(input) {
9416
9724
  async function enforceAacTruePeak(input) {
9417
9725
  let scratchDir;
9418
9726
  try {
9419
- scratchDir = mkdtempSync3(join18(dirname7(input.audioPath), ".true-peak-"));
9420
- const correctedPath = join18(scratchDir, "audio.m4a");
9727
+ scratchDir = mkdtempSync3(join19(dirname8(input.audioPath), ".true-peak-"));
9728
+ const correctedPath = join19(scratchDir, "audio.m4a");
9421
9729
  const measurements = [];
9422
9730
  let attenuationDb = 0;
9423
9731
  let sourcePeakDbfs;
@@ -9432,7 +9740,7 @@ async function enforceAacTruePeak(input) {
9432
9740
  }
9433
9741
  if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
9434
9742
  if (measuredPath !== correctedPath) return { success: true };
9435
- renameWithRetry4(correctedPath, input.audioPath);
9743
+ renameWithRetry5(correctedPath, input.audioPath);
9436
9744
  return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
9437
9745
  }
9438
9746
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
@@ -9473,7 +9781,7 @@ async function enforceAacTruePeak(input) {
9473
9781
  )}`
9474
9782
  };
9475
9783
  } finally {
9476
- if (scratchDir) rmSync8(scratchDir, { recursive: true, force: true });
9784
+ if (scratchDir) rmSync9(scratchDir, { recursive: true, force: true });
9477
9785
  }
9478
9786
  }
9479
9787
  function failResult(outputPath, target, source, error) {
@@ -9550,7 +9858,7 @@ async function defaultProbeAudioInfo(audioPath, signal) {
9550
9858
  };
9551
9859
  }
9552
9860
  async function defaultRunFfmpeg(args, timeoutMs, signal) {
9553
- return toStepResult(await runFfmpeg4(args, { signal, timeout: timeoutMs }), timeoutMs);
9861
+ return toStepResult(await runFfmpeg5(args, { signal, timeout: timeoutMs }), timeoutMs);
9554
9862
  }
9555
9863
  function defaultAacEncoder(timeoutMs, signal, pcmSourcePath) {
9556
9864
  return async (inputPath, outputPath, durationSeconds, filter) => {
@@ -9620,7 +9928,7 @@ function assertAudioNotInterrupted(failures) {
9620
9928
  }
9621
9929
  async function runAudioStage(input) {
9622
9930
  const stageStart = Date.now();
9623
- const pcmSourcePath = join19(input.workDir, "audio-source.wav");
9931
+ const pcmSourcePath = join20(input.workDir, "audio-source.wav");
9624
9932
  const duration = input.totalFrames * input.fps.den / input.fps.num;
9625
9933
  try {
9626
9934
  const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
@@ -9640,7 +9948,7 @@ async function runAudioStage(input) {
9640
9948
  }
9641
9949
  return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
9642
9950
  } finally {
9643
- rmSync9(pcmSourcePath, { force: true });
9951
+ rmSync10(pcmSourcePath, { force: true });
9644
9952
  }
9645
9953
  }
9646
9954
  async function mixAudioForDelivery(input, duration, pcmSourcePath) {
@@ -9658,7 +9966,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
9658
9966
  assertNotAborted,
9659
9967
  log
9660
9968
  } = input;
9661
- const audioOutputPath = join19(workDir, MIXED_AUDIO_FILENAME);
9969
+ const audioOutputPath = join20(workDir, MIXED_AUDIO_FILENAME);
9662
9970
  let hasAudio = false;
9663
9971
  let audioError;
9664
9972
  let audioFailures;
@@ -9669,7 +9977,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
9669
9977
  audioResult = await processCompositionAudio(
9670
9978
  audios,
9671
9979
  projectDir,
9672
- join19(workDir, "audio-work"),
9980
+ join20(workDir, "audio-work"),
9673
9981
  audioOutputPath,
9674
9982
  duration,
9675
9983
  abortSignal,
@@ -9738,7 +10046,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
9738
10046
 
9739
10047
  // src/services/render/stages/captureStage.ts
9740
10048
  import { existsSync as existsSync12, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
9741
- import { join as join20 } from "node:path";
10049
+ import { join as join21 } from "node:path";
9742
10050
  import {
9743
10051
  classifyCaptureFailure as classifyCaptureFailure2,
9744
10052
  frameFileExtension,
@@ -9811,13 +10119,13 @@ function measureCaptureFrameBytes(workDir, framesDir) {
9811
10119
  if (!existsSync12(workDir)) return total;
9812
10120
  for (const attempt of readdirSync3(workDir)) {
9813
10121
  if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
9814
- total += sampleDirectoryBytes(join20(workDir, attempt));
10122
+ total += sampleDirectoryBytes(join21(workDir, attempt));
9815
10123
  continue;
9816
10124
  }
9817
10125
  if (!/^capture-attempt-\d+$/.test(attempt)) continue;
9818
- const attemptDir = join20(workDir, attempt);
10126
+ const attemptDir = join21(workDir, attempt);
9819
10127
  for (const name of readdirSync3(attemptDir)) {
9820
- if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join20(attemptDir, name));
10128
+ if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join21(attemptDir, name));
9821
10129
  }
9822
10130
  }
9823
10131
  return total;
@@ -10110,8 +10418,8 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
10110
10418
  }
10111
10419
 
10112
10420
  // src/services/render/stages/captureHdrStage.ts
10113
- import { existsSync as existsSync14, mkdirSync as mkdirSync7 } from "node:fs";
10114
- import { join as join24 } from "node:path";
10421
+ import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "node:fs";
10422
+ import { join as join25 } from "node:path";
10115
10423
  import {
10116
10424
  closeCaptureSession as closeCaptureSession7,
10117
10425
  cloneCaptureWarnings as cloneCaptureWarnings2,
@@ -10183,7 +10491,7 @@ function createHdrImageTransferCache(options = {}) {
10183
10491
  }
10184
10492
 
10185
10493
  // src/services/render/stages/captureHdrSequentialLoop.ts
10186
- import { join as join21 } from "node:path";
10494
+ import { join as join22 } from "node:path";
10187
10495
  import {
10188
10496
  TRANSITIONS,
10189
10497
  crossfade
@@ -10308,7 +10616,7 @@ async function runSequentialLayeredFrameLoop(input) {
10308
10616
  );
10309
10617
  if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
10310
10618
  writeFileExclusiveSync(
10311
- join21(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
10619
+ join22(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
10312
10620
  normalCanvas
10313
10621
  );
10314
10622
  }
@@ -10340,7 +10648,7 @@ async function runSequentialLayeredFrameLoop(input) {
10340
10648
  }
10341
10649
 
10342
10650
  // src/services/render/stages/captureHdrHybridLoop.ts
10343
- import { join as join23 } from "node:path";
10651
+ import { join as join24 } from "node:path";
10344
10652
  import {
10345
10653
  TRANSITIONS as TRANSITIONS2,
10346
10654
  closeCaptureSession as closeCaptureSession6,
@@ -10354,7 +10662,7 @@ import {
10354
10662
  // src/services/shaderTransitionWorkerPool.ts
10355
10663
  import { Worker } from "node:worker_threads";
10356
10664
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
10357
- import { dirname as dirname8, join as join22 } from "node:path";
10665
+ import { dirname as dirname9, join as join23 } from "node:path";
10358
10666
  import { createRequire } from "node:module";
10359
10667
  import { existsSync as existsSync13 } from "node:fs";
10360
10668
  import { cpus as cpus2 } from "node:os";
@@ -10367,10 +10675,10 @@ function resolveWorkerEntry(explicit) {
10367
10675
  const isTs = override.endsWith(".ts");
10368
10676
  return { path: override, isTs };
10369
10677
  }
10370
- const moduleDir = dirname8(fileURLToPath3(import.meta.url));
10371
- const jsPath = join22(moduleDir, "shaderTransitionWorker.js");
10678
+ const moduleDir = dirname9(fileURLToPath3(import.meta.url));
10679
+ const jsPath = join23(moduleDir, "shaderTransitionWorker.js");
10372
10680
  if (existsSync13(jsPath)) return { path: jsPath, isTs: false };
10373
- const tsPath = join22(moduleDir, "shaderTransitionWorker.ts");
10681
+ const tsPath = join23(moduleDir, "shaderTransitionWorker.ts");
10374
10682
  return { path: tsPath, isTs: true };
10375
10683
  }
10376
10684
  function buildExecArgv(entryIsTs) {
@@ -10513,8 +10821,8 @@ async function createShaderTransitionWorkerPool(opts) {
10513
10821
  if (terminated) {
10514
10822
  throw new Error("shader-blend pool already terminated");
10515
10823
  }
10516
- return new Promise((resolve12, reject) => {
10517
- const task = traceEnabled ? { req, resolve: resolve12, reject, enqueuedAtMs: Date.now(), traceId: ++nextTaskId } : { req, resolve: resolve12, reject };
10824
+ return new Promise((resolve13, reject) => {
10825
+ const task = traceEnabled ? { req, resolve: resolve13, reject, enqueuedAtMs: Date.now(), traceId: ++nextTaskId } : { req, resolve: resolve13, reject };
10518
10826
  const idle = slots.find((s) => !s.busy && !s.dead);
10519
10827
  if (idle) {
10520
10828
  queue.unshift(task);
@@ -10738,7 +11046,7 @@ async function runHybridLayeredFrameLoop(input) {
10738
11046
  );
10739
11047
  if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
10740
11048
  writeFileExclusiveSync(
10741
- join23(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
11049
+ join24(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
10742
11050
  canvas
10743
11051
  );
10744
11052
  }
@@ -10926,9 +11234,9 @@ async function runCaptureHdrStage(input) {
10926
11234
  if (hdrVideoFrameSources.has(v.id)) hdrVideoEndTimes.set(v.id, v.end);
10927
11235
  }
10928
11236
  const debugDumpEnabled = process.env.KEEP_TEMP === "1";
10929
- const debugDumpDir = debugDumpEnabled ? join24(framesDir, "debug-composite") : null;
11237
+ const debugDumpDir = debugDumpEnabled ? join25(framesDir, "debug-composite") : null;
10930
11238
  if (debugDumpDir && !existsSync14(debugDumpDir)) {
10931
- mkdirSync7(debugDumpDir, { recursive: true });
11239
+ mkdirSync8(debugDumpDir, { recursive: true });
10932
11240
  }
10933
11241
  const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
10934
11242
  const hdrTargetTransfer = compositeTransfer === "srgb" ? void 0 : compositeTransfer;
@@ -11081,24 +11389,24 @@ async function runCaptureHdrStage(input) {
11081
11389
 
11082
11390
  // src/services/render/stages/encodeStage.ts
11083
11391
  import {
11084
- copyFileSync as copyFileSync3,
11392
+ copyFileSync as copyFileSync4,
11085
11393
  existsSync as existsSync15,
11086
- mkdirSync as mkdirSync8,
11394
+ mkdirSync as mkdirSync9,
11087
11395
  readdirSync as readdirSync4,
11088
- readFileSync as readFileSync7,
11089
- rmSync as rmSync10,
11090
- statSync as statSync6,
11091
- writeFileSync as writeFileSync4
11396
+ readFileSync as readFileSync8,
11397
+ rmSync as rmSync11,
11398
+ statSync as statSync7,
11399
+ writeFileSync as writeFileSync5
11092
11400
  } from "node:fs";
11093
11401
  import { readFile as readFile2 } from "node:fs/promises";
11094
- import { dirname as dirname9, join as join25 } from "node:path";
11402
+ import { dirname as dirname10, join as join26 } from "node:path";
11095
11403
  import {
11096
11404
  encodeFramesChunkedConcat,
11097
11405
  encodeFramesFromDir,
11098
11406
  formatFfmpegError,
11099
11407
  MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME2,
11100
11408
  resolveConfig,
11101
- runFfmpeg as runFfmpeg5,
11409
+ runFfmpeg as runFfmpeg6,
11102
11410
  spawnStreamingEncoder as spawnStreamingEncoder4
11103
11411
  } from "@hyperframes/engine";
11104
11412
  import {
@@ -11174,17 +11482,17 @@ function gifEncodeFailure(startTime, outputPath, error, failureReason) {
11174
11482
  }
11175
11483
  async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
11176
11484
  const { outputPath } = argsInput;
11177
- const result = await runFfmpeg5(buildGifPaletteuseArgs({ ...argsInput, wholeFrames: true }), run);
11485
+ const result = await runFfmpeg6(buildGifPaletteuseArgs({ ...argsInput, wholeFrames: true }), run);
11178
11486
  if (!result.success) {
11179
11487
  const error = formatFfmpegError(result.exitCode, result.stderr);
11180
11488
  return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
11181
11489
  }
11182
- const gif = readFileSync7(outputPath);
11490
+ const gif = readFileSync8(outputPath);
11183
11491
  if (!clearGifFramesBeforeNext(gif)) {
11184
11492
  const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
11185
11493
  return gifEncodeFailure(startTime, outputPath, error);
11186
11494
  }
11187
- writeFileSync4(outputPath, gif);
11495
+ writeFileSync5(outputPath, gif);
11188
11496
  return null;
11189
11497
  }
11190
11498
  async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
@@ -11206,17 +11514,17 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
11206
11514
  try {
11207
11515
  const run = { signal: input.signal, timeout: input.timeout };
11208
11516
  for (const args of [buildGifPalettegenArgs(argsInput), buildGifPaletteuseArgs(argsInput)]) {
11209
- const result = await runFfmpeg5(args, run);
11517
+ const result = await runFfmpeg6(args, run);
11210
11518
  if (!result.success) {
11211
11519
  const error = formatFfmpegError(result.exitCode, result.stderr);
11212
11520
  return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
11213
11521
  }
11214
11522
  }
11215
- if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync7(outputPath)) !== false) {
11523
+ if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync8(outputPath)) !== false) {
11216
11524
  const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
11217
11525
  if (failure) return failure;
11218
11526
  }
11219
- const fileSize = existsSync15(outputPath) ? statSync6(outputPath).size : 0;
11527
+ const fileSize = existsSync15(outputPath) ? statSync7(outputPath).size : 0;
11220
11528
  return {
11221
11529
  success: true,
11222
11530
  outputPath,
@@ -11225,7 +11533,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
11225
11533
  fileSize
11226
11534
  };
11227
11535
  } finally {
11228
- rmSync10(input.palettePath, { force: true });
11536
+ rmSync11(input.palettePath, { force: true });
11229
11537
  }
11230
11538
  }
11231
11539
  function startEncodeProgress(job, stage, onProgress) {
@@ -11297,10 +11605,10 @@ function encodeWhileCapturing(input, totalFrames) {
11297
11605
  const stream = await encoder;
11298
11606
  for (let next = 0; next < totalFrames; next++) {
11299
11607
  while (!landed.has(next) && !captureDone && !signal.aborted) {
11300
- await new Promise((resolve12) => wake = resolve12);
11608
+ await new Promise((resolve13) => wake = resolve13);
11301
11609
  }
11302
11610
  if (signal.aborted) return;
11303
- const mergedPath = join25(input.framesDir, formatCaptureFrameName(next, "jpg"));
11611
+ const mergedPath = join26(input.framesDir, formatCaptureFrameName(next, "jpg"));
11304
11612
  const frame = await readLandedFrame(landed.get(next), mergedPath);
11305
11613
  landed.delete(next);
11306
11614
  if (!await stream.writeFrame(frame)) {
@@ -11375,7 +11683,7 @@ async function runEncodeStage(input) {
11375
11683
  const stage5Start = Date.now();
11376
11684
  if (isPngSequence) {
11377
11685
  const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
11378
- if (!existsSync15(outputPath)) mkdirSync8(outputPath, { recursive: true });
11686
+ if (!existsSync15(outputPath)) mkdirSync9(outputPath, { recursive: true });
11379
11687
  const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
11380
11688
  if (captured.length === 0) {
11381
11689
  throw new Error(
@@ -11383,11 +11691,11 @@ async function runEncodeStage(input) {
11383
11691
  );
11384
11692
  }
11385
11693
  captured.forEach((name, i) => {
11386
- const dst = join25(outputPath, formatExportFrameName(i, "png"));
11387
- copyFileSync3(join25(framesDir, name), dst);
11694
+ const dst = join26(outputPath, formatExportFrameName(i, "png"));
11695
+ copyFileSync4(join26(framesDir, name), dst);
11388
11696
  });
11389
11697
  if (hasAudio && audioOutputPath && existsSync15(audioOutputPath)) {
11390
- copyFileSync3(audioOutputPath, join25(outputPath, MIXED_AUDIO_FILENAME2));
11698
+ copyFileSync4(audioOutputPath, join26(outputPath, MIXED_AUDIO_FILENAME2));
11391
11699
  log.info(
11392
11700
  `[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
11393
11701
  );
@@ -11407,7 +11715,7 @@ async function runEncodeStage(input) {
11407
11715
  const encodeResult2 = await encodeGifFromDir(framesDir, framePattern2, outputPath, {
11408
11716
  fps: job.config.fps,
11409
11717
  loop,
11410
- palettePath: join25(dirname9(videoOnlyPath), "gif-palette.png"),
11718
+ palettePath: join26(dirname10(videoOnlyPath), "gif-palette.png"),
11411
11719
  preserveAlpha: needsAlpha,
11412
11720
  signal: abortSignal,
11413
11721
  timeout: engineCfg.ffmpegEncodeTimeout
@@ -11617,9 +11925,9 @@ function sampleDirectoryBytes(dir) {
11617
11925
  continue;
11618
11926
  }
11619
11927
  for (const name of entries) {
11620
- const full = join26(current, name);
11928
+ const full = join27(current, name);
11621
11929
  try {
11622
- const st = statSync7(full);
11930
+ const st = statSync8(full);
11623
11931
  if (st.isDirectory()) {
11624
11932
  stack.push(full);
11625
11933
  } else if (st.isFile()) {
@@ -11766,8 +12074,8 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
11766
12074
  const ranges = [];
11767
12075
  let rangeStart = null;
11768
12076
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
11769
- const framePath = join26(framesDir, formatCaptureFrameName(frameIndex, frameExt));
11770
- const missing = !existsSync16(framePath) || statSync7(framePath).size <= 8;
12077
+ const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12078
+ const missing = !existsSync16(framePath) || statSync8(framePath).size <= 8;
11771
12079
  if (missing && rangeStart === null) {
11772
12080
  rangeStart = frameIndex;
11773
12081
  } else if (!missing && rangeStart !== null) {
@@ -11783,7 +12091,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
11783
12091
  function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, rangeStart = 0, stride = 1) {
11784
12092
  const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
11785
12093
  const batches = [];
11786
- const outputDirFor = (batchIndex, workerId) => join26(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
12094
+ const outputDirFor = (batchIndex, workerId) => join27(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
11787
12095
  if (stride > 1) {
11788
12096
  const spans = /* @__PURE__ */ new Map();
11789
12097
  for (const range of ranges) {
@@ -11843,8 +12151,8 @@ function getNextRetryWorkerCount(currentWorkers) {
11843
12151
  return Math.max(1, Math.floor(currentWorkers / 2));
11844
12152
  }
11845
12153
  function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
11846
- if (platform2 === "win32" || platform2 === "darwin") return join26(systemTempDir, "hf-render-");
11847
- return join26(dirname10(outputPath), `work-${jobId}-`);
12154
+ if (platform2 === "win32" || platform2 === "darwin") return join27(systemTempDir, "hf-render-");
12155
+ return join27(dirname11(outputPath), `work-${jobId}-`);
11848
12156
  }
11849
12157
  var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
11850
12158
  function isTransientCaptureRetryEligible(failureKind, missing, retriesUsed) {
@@ -11886,7 +12194,7 @@ The composition is too large for the available memory. To reduce memory pressure
11886
12194
  function countCapturedFrames(totalFrames, framesDir, frameExt) {
11887
12195
  let captured = 0;
11888
12196
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
11889
- const framePath = join26(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12197
+ const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
11890
12198
  if (existsSync16(framePath)) captured++;
11891
12199
  }
11892
12200
  return captured;
@@ -11912,7 +12220,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
11912
12220
  reason: attempt === 0 ? "initial" : pendingTransientRetry ? "transient-retry" : "retry"
11913
12221
  });
11914
12222
  pendingTransientRetry = false;
11915
- const attemptWorkDir = join26(options.workDir, `capture-attempt-${attempt}`);
12223
+ const attemptWorkDir = join27(options.workDir, `capture-attempt-${attempt}`);
11916
12224
  const batches = missingRanges ? buildMissingFrameRetryBatches(
11917
12225
  missingRanges,
11918
12226
  currentWorkers,
@@ -12518,18 +12826,18 @@ function deVerifyFallbackTelemetry(err) {
12518
12826
  };
12519
12827
  }
12520
12828
  function resolveRenderDebugDir() {
12521
- const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve7(process.env.PRODUCER_RENDERS_DIR, "..") : resolve7(dirname10(fileURLToPath4(import.meta.url)), "../..");
12522
- return join26(producerRoot, ".debug");
12829
+ const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve8(process.env.PRODUCER_RENDERS_DIR, "..") : resolve8(dirname11(fileURLToPath4(import.meta.url)), "../..");
12830
+ return join27(producerRoot, ".debug");
12523
12831
  }
12524
12832
  async function executeRenderJob(job, projectDir, outputPath, progressSink, abortSignal, assertRenderActive) {
12525
12833
  validateHlsRenderConfig(job.config);
12526
12834
  const debugDir = resolveRenderDebugDir();
12527
- const outputDir = dirname10(outputPath);
12528
- if (!existsSync16(outputDir)) mkdirSync9(outputDir, { recursive: true });
12529
- const workDir = job.config.debug ? join26(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
12835
+ const outputDir = dirname11(outputPath);
12836
+ if (!existsSync16(outputDir)) mkdirSync10(outputDir, { recursive: true });
12837
+ const workDir = job.config.debug ? join27(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
12530
12838
  const pipelineStart = Date.now();
12531
12839
  const baseLog = job.config.logger ?? defaultLogger;
12532
- const logPath = job.config.debug ? join26(workDir, "render.log") : null;
12840
+ const logPath = job.config.debug ? join27(workDir, "render.log") : null;
12533
12841
  const execution = new RenderExecutionContext({
12534
12842
  request: { renderJobId: job.id, projectDir, outputPath },
12535
12843
  logger: logPath ? createRenderFileLogger(logPath, baseLog) : baseLog,
@@ -12543,7 +12851,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
12543
12851
  log.info("KEEP_TEMP=1, leaving workDir on disk for inspection", { workDir });
12544
12852
  return;
12545
12853
  }
12546
- rmSync11(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
12854
+ rmSync12(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
12547
12855
  });
12548
12856
  try {
12549
12857
  await executeRenderPipeline({
@@ -12589,7 +12897,7 @@ async function executeRenderPipeline(input) {
12589
12897
  imageDecodeFailures: 0
12590
12898
  };
12591
12899
  let hdrPerf;
12592
- const perfOutputPath = join26(workDir, "perf-summary.json");
12900
+ const perfOutputPath = join27(workDir, "perf-summary.json");
12593
12901
  const cfg = { ...job.config.producerConfig ?? resolveConfig2() };
12594
12902
  const observability = new RenderObservabilityRecorder({
12595
12903
  pipelineStartMs: pipelineStart,
@@ -12662,7 +12970,7 @@ async function executeRenderPipeline(input) {
12662
12970
  job.startedAt = /* @__PURE__ */ new Date();
12663
12971
  assertNotAborted();
12664
12972
  assertConfiguredFfmpegBinariesExist();
12665
- if (!existsSync16(workDir)) mkdirSync9(workDir, { recursive: true });
12973
+ if (!existsSync16(workDir)) mkdirSync10(workDir, { recursive: true });
12666
12974
  if (job.config.debug) {
12667
12975
  log.info("[Render] Debug artifacts enabled", { workDir, logPath });
12668
12976
  }
@@ -12691,22 +12999,22 @@ async function executeRenderPipeline(input) {
12691
12999
  requestedWorkers: job.config.workers ?? "auto"
12692
13000
  });
12693
13001
  const entryFile = job.config.entryFile || "index.html";
12694
- let htmlPath = join26(projectDir, entryFile);
13002
+ let htmlPath = join27(projectDir, entryFile);
12695
13003
  if (!existsSync16(htmlPath)) {
12696
13004
  throw new Error(`Entry file not found: ${htmlPath}`);
12697
13005
  }
12698
13006
  assertNotAborted();
12699
- const rawEntry = readFileSync8(htmlPath, "utf-8");
13007
+ const rawEntry = readFileSync9(htmlPath, "utf-8");
12700
13008
  if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
12701
- const wrapperPath = join26(workDir, "standalone-entry.html");
12702
- const projectIndexPath = join26(projectDir, "index.html");
13009
+ const wrapperPath = join27(workDir, "standalone-entry.html");
13010
+ const projectIndexPath = join27(projectDir, "index.html");
12703
13011
  if (!existsSync16(projectIndexPath)) {
12704
13012
  throw new Error(
12705
13013
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
12706
13014
  );
12707
13015
  }
12708
13016
  const standaloneHtml = extractStandaloneEntryFromIndex(
12709
- readFileSync8(projectIndexPath, "utf-8"),
13017
+ readFileSync9(projectIndexPath, "utf-8"),
12710
13018
  entryFile,
12711
13019
  rawEntry
12712
13020
  );
@@ -12715,7 +13023,7 @@ async function executeRenderPipeline(input) {
12715
13023
  `Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
12716
13024
  );
12717
13025
  }
12718
- writeFileSync5(wrapperPath, standaloneHtml, "utf-8");
13026
+ writeFileSync6(wrapperPath, standaloneHtml, "utf-8");
12719
13027
  htmlPath = wrapperPath;
12720
13028
  log.info("Extracted standalone entry from index.html host context", {
12721
13029
  entryFile
@@ -12862,7 +13170,7 @@ async function executeRenderPipeline(input) {
12862
13170
  updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
12863
13171
  code: "extract_video"
12864
13172
  });
12865
- const compiledDir = join26(workDir, "compiled");
13173
+ const compiledDir = join27(workDir, "compiled");
12866
13174
  const extractResult = await observeRenderStage(
12867
13175
  observability,
12868
13176
  "video_extract",
@@ -12996,7 +13304,7 @@ async function executeRenderPipeline(input) {
12996
13304
  try {
12997
13305
  fileServer = await createFileServer({
12998
13306
  projectDir,
12999
- compiledDir: join26(workDir, "compiled"),
13307
+ compiledDir: join27(workDir, "compiled"),
13000
13308
  port: 0,
13001
13309
  preHeadScripts: [VIRTUAL_TIME_SHIM],
13002
13310
  fps: job.config.fps
@@ -13014,8 +13322,8 @@ async function executeRenderPipeline(input) {
13014
13322
  if (!activeFileServer) {
13015
13323
  throw new Error("File server failed to initialize before frame capture");
13016
13324
  }
13017
- const framesDir = join26(workDir, "captured-frames");
13018
- if (!existsSync16(framesDir)) mkdirSync9(framesDir, { recursive: true });
13325
+ const framesDir = join27(workDir, "captured-frames");
13326
+ if (!existsSync16(framesDir)) mkdirSync10(framesDir, { recursive: true });
13019
13327
  updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
13020
13328
  code: "start_browsers"
13021
13329
  });
@@ -13486,7 +13794,7 @@ async function executeRenderPipeline(input) {
13486
13794
  hls: ".mp4"
13487
13795
  };
13488
13796
  const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
13489
- const videoOnlyPath = join26(workDir, `video-only${videoExt}`);
13797
+ const videoOnlyPath = join27(workDir, `video-only${videoExt}`);
13490
13798
  const usePageSideCompositingForTransitions = (cfg.enablePageSideCompositing || isGif) && compiled.hasShaderTransitions && !hasHdrContent && outputSupportsPageSideShaderCompositing(outputFormat);
13491
13799
  if (usePageSideCompositingForTransitions) {
13492
13800
  activeFileServer.addPreHeadScript(HF_PAGE_SIDE_COMPOSITING_STUB);
@@ -13739,7 +14047,7 @@ async function executeRenderPipeline(input) {
13739
14047
  pageMotionBlur: job.config.pageMotionBlur,
13740
14048
  captureQuality: captureOptions.quality
13741
14049
  });
13742
- const segmentDir = segmentDirFor(join26(projectDir, "renders"), segmentPlanHash);
14050
+ const segmentDir = segmentDirFor(join27(projectDir, "renders"), segmentPlanHash);
13743
14051
  let completedSegments = /* @__PURE__ */ new Set();
13744
14052
  if (job.config.resumeSegments === true) {
13745
14053
  const existing = readSegmentManifest(segmentDir);
@@ -13754,10 +14062,10 @@ async function executeRenderPipeline(input) {
13754
14062
  });
13755
14063
  }
13756
14064
  if (completedSegments.size === 0) {
13757
- rmSync11(segmentDir, { recursive: true, force: true });
14065
+ rmSync12(segmentDir, { recursive: true, force: true });
13758
14066
  }
13759
14067
  } else {
13760
- rmSync11(segmentDir, { recursive: true, force: true });
14068
+ rmSync12(segmentDir, { recursive: true, force: true });
13761
14069
  }
13762
14070
  const segmentManifest = readSegmentManifest(segmentDir) ?? {
13763
14071
  version: 1,
@@ -13838,7 +14146,7 @@ async function executeRenderPipeline(input) {
13838
14146
  );
13839
14147
  updateCaptureObservability({ segmentRetries: segmentedRes.segmentRetries });
13840
14148
  if (job.config.keepSegments !== true) {
13841
- rmSync11(segmentDir, { recursive: true, force: true });
14149
+ rmSync12(segmentDir, { recursive: true, force: true });
13842
14150
  }
13843
14151
  } else {
13844
14152
  capturePlan = replanAfterFailure(capturePlan, { kind: "streaming_unavailable" });
@@ -14110,8 +14418,8 @@ async function executeRenderPipeline(input) {
14110
14418
  "capture_disk",
14111
14419
  "drawElement capture failed; retrying with a fresh screenshot session"
14112
14420
  );
14113
- rmSync11(framesDir, { recursive: true, force: true });
14114
- mkdirSync9(framesDir, { recursive: true });
14421
+ rmSync12(framesDir, { recursive: true, force: true });
14422
+ mkdirSync10(framesDir, { recursive: true });
14115
14423
  resetCaptureAttemptProgress(job);
14116
14424
  dedupPerfs.length = 0;
14117
14425
  cfg.useDrawElement = false;
@@ -14287,7 +14595,7 @@ async function executeRenderPipeline(input) {
14287
14595
  job.perfSummary = perfSummary;
14288
14596
  if (job.config.debug) {
14289
14597
  try {
14290
- writeFileSync5(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
14598
+ writeFileSync6(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
14291
14599
  } catch (err) {
14292
14600
  log.debug("Failed to write perf summary", {
14293
14601
  perfOutputPath,
@@ -14297,8 +14605,8 @@ async function executeRenderPipeline(input) {
14297
14605
  }
14298
14606
  if (job.config.debug) {
14299
14607
  if (!isPngSequence && !isHls && existsSync16(stagedOutputPath)) {
14300
- const debugOutput = join26(workDir, `output${videoExt}`);
14301
- copyFileSync4(stagedOutputPath, debugOutput);
14608
+ const debugOutput = join27(workDir, `output${videoExt}`);
14609
+ copyFileSync5(stagedOutputPath, debugOutput);
14302
14610
  }
14303
14611
  }
14304
14612
  await commitArtifactTransaction(artifactTransaction, () => {
@@ -14422,15 +14730,15 @@ import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
14422
14730
  // src/services/distributed/plan.ts
14423
14731
  import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframes/engine";
14424
14732
  import {
14425
- copyFileSync as copyFileSync5,
14733
+ copyFileSync as copyFileSync6,
14426
14734
  cpSync as cpSync2,
14427
14735
  existsSync as existsSync21,
14428
14736
  linkSync,
14429
- mkdirSync as mkdirSync13,
14430
- rmSync as rmSync13,
14431
- writeFileSync as writeFileSync8
14737
+ mkdirSync as mkdirSync14,
14738
+ rmSync as rmSync14,
14739
+ writeFileSync as writeFileSync9
14432
14740
  } from "node:fs";
14433
- import { extname as extname6, isAbsolute as isAbsolute3, join as join34, relative as relative5, sep as sep2 } from "node:path";
14741
+ import { extname as extname6, isAbsolute as isAbsolute3, join as join35, relative as relative5, sep as sep2 } from "node:path";
14434
14742
  import {
14435
14743
  fpsToNumber as fpsToNumber10,
14436
14744
  HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
@@ -14440,12 +14748,12 @@ import {
14440
14748
  normalizeVp9CpuUsed,
14441
14749
  resolveConfig as resolveConfig3,
14442
14750
  declareContentAddressedUrl,
14443
- renameWithRetry as renameWithRetry6,
14751
+ renameWithRetry as renameWithRetry7,
14444
14752
  declareProbeFactsUrl
14445
14753
  } from "@hyperframes/engine";
14446
14754
 
14447
14755
  // src/services/distributed/planFrameCost.ts
14448
- import { join as join29 } from "node:path";
14756
+ import { join as join30 } from "node:path";
14449
14757
  import {
14450
14758
  closeCaptureSession as closeCaptureSession9,
14451
14759
  createCaptureSession as createCaptureSession9,
@@ -14455,7 +14763,7 @@ import {
14455
14763
 
14456
14764
  // src/services/distributed/chunkFrames.ts
14457
14765
  import { existsSync as existsSync18, readdirSync as readdirSync6 } from "node:fs";
14458
- import { extname as extname4, join as join28 } from "node:path";
14766
+ import { extname as extname4, join as join29 } from "node:path";
14459
14767
  import {
14460
14768
  compositionRequiresWebGpu as compositionRequiresWebGpu3,
14461
14769
  createFrameLookupTable as createFrameLookupTable2,
@@ -14465,8 +14773,8 @@ import {
14465
14773
 
14466
14774
  // src/services/distributed/shared.ts
14467
14775
  import { execFile as execFileCallback } from "node:child_process";
14468
- import { dirname as dirname11, join as join27 } from "node:path";
14469
- import { existsSync as existsSync17, readFileSync as readFileSync9 } from "node:fs";
14776
+ import { dirname as dirname12, join as join28 } from "node:path";
14777
+ import { existsSync as existsSync17, readFileSync as readFileSync10 } from "node:fs";
14470
14778
  import { fileURLToPath as fileURLToPath5 } from "node:url";
14471
14779
  import { promisify as promisify2 } from "node:util";
14472
14780
  import {
@@ -14481,7 +14789,7 @@ function isPlanAudioArtifactPath(path) {
14481
14789
  }
14482
14790
  function resolvePlanAudioPath(planDir) {
14483
14791
  for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
14484
- const candidate = join27(planDir, name);
14792
+ const candidate = join28(planDir, name);
14485
14793
  if (existsSync17(candidate)) return candidate;
14486
14794
  }
14487
14795
  return null;
@@ -14738,13 +15046,13 @@ function buildSyntheticRenderJob(input) {
14738
15046
  var cachedProducerVersion = null;
14739
15047
  function readProducerVersion() {
14740
15048
  if (cachedProducerVersion !== null) return cachedProducerVersion;
14741
- const startDir = dirname11(fileURLToPath5(import.meta.url));
15049
+ const startDir = dirname12(fileURLToPath5(import.meta.url));
14742
15050
  let current = startDir;
14743
15051
  for (let i = 0; i < 10; i++) {
14744
- const candidate = join27(current, "package.json");
15052
+ const candidate = join28(current, "package.json");
14745
15053
  if (existsSync17(candidate)) {
14746
15054
  try {
14747
- const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
15055
+ const pkg = JSON.parse(readFileSync10(candidate, "utf-8"));
14748
15056
  if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
14749
15057
  cachedProducerVersion = pkg.version;
14750
15058
  return pkg.version;
@@ -14752,7 +15060,7 @@ function readProducerVersion() {
14752
15060
  } catch {
14753
15061
  }
14754
15062
  }
14755
- const parent = dirname11(current);
15063
+ const parent = dirname12(current);
14756
15064
  if (parent === current) break;
14757
15065
  current = parent;
14758
15066
  }
@@ -14795,7 +15103,7 @@ function frameNumberFromFileName(name) {
14795
15103
  function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v1") {
14796
15104
  const result = [];
14797
15105
  for (const v of videos) {
14798
- const outputDir = join28(planDir, "video-frames", v.videoId);
15106
+ const outputDir = join29(planDir, "video-frames", v.videoId);
14799
15107
  if (!existsSync18(outputDir)) {
14800
15108
  throw new Error(
14801
15109
  `[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
@@ -14814,7 +15122,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
14814
15122
  if (!frameName) continue;
14815
15123
  const frameNumber = indexMode === "sparse-v2" ? frameNumberFromFileName(frameName) : null;
14816
15124
  const frameIndex = frameNumber === null ? i : frameNumber - 1;
14817
- framePaths.set(frameIndex, join28(outputDir, frameName));
15125
+ framePaths.set(frameIndex, join29(outputDir, frameName));
14818
15126
  }
14819
15127
  result.push({
14820
15128
  videoId: v.videoId,
@@ -14904,14 +15212,14 @@ async function extractDeferredVideoFramesForChunk(input) {
14904
15212
  const shown = shownDeferredFrameIndexes(input.planVideos, input.frames, input.fps);
14905
15213
  return Promise.all(
14906
15214
  deferred.map(async (video) => {
14907
- const outputDir = join28(input.outputRoot, video.videoId);
15215
+ const outputDir = join29(input.outputRoot, video.videoId);
14908
15216
  const indexes = [...shown.get(video.videoId) ?? []].sort((a, b) => a - b);
14909
15217
  if (indexes.length === 0) {
14910
15218
  return { ...video, outputDir, framePaths: /* @__PURE__ */ new Map(), ownedByLookup: false };
14911
15219
  }
14912
15220
  const range = video.deferredRange;
14913
15221
  const remoteUrl = remoteSources[range.sourcePath];
14914
- const sourcePath = remoteUrl ?? join28(input.planDir, ...range.sourcePath.split("/"));
15222
+ const sourcePath = remoteUrl ?? join29(input.planDir, ...range.sourcePath.split("/"));
14915
15223
  if (remoteUrl === void 0 && !existsSync18(sourcePath)) {
14916
15224
  throw new RenderChunkValidationError(
14917
15225
  MISSING_PLAN_ARTIFACT,
@@ -14960,12 +15268,12 @@ async function measurePlanSecondsPerFrame(input) {
14960
15268
  planVideos,
14961
15269
  frames: sampled,
14962
15270
  fps: fpsRatio,
14963
- outputRoot: join29(workDir, "frame-cost-video-frames"),
15271
+ outputRoot: join30(workDir, "frame-cost-video-frames"),
14964
15272
  cfg: input.cfg
14965
15273
  })
14966
15274
  ];
14967
15275
  const lookup = extracted.length > 0 ? createFrameLookupTable3(planVideos.videos, extracted, resolveRenderFpsConfig(fpsRatio).value) : null;
14968
- const compiledDir = join29(planDir, "compiled");
15276
+ const compiledDir = join30(planDir, "compiled");
14969
15277
  const fileServer = await createFileServer({
14970
15278
  projectDir: compiledDir,
14971
15279
  compiledDir,
@@ -14977,7 +15285,7 @@ async function measurePlanSecondsPerFrame(input) {
14977
15285
  try {
14978
15286
  session = await createCaptureSession9(
14979
15287
  fileServer.url,
14980
- join29(workDir, "frame-cost"),
15288
+ join30(workDir, "frame-cost"),
14981
15289
  buildChunkCaptureOptions({
14982
15290
  dimensions: { ...input, fpsNum: fps, fpsDen: 1 },
14983
15291
  encoder: input,
@@ -15074,8 +15382,8 @@ function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
15074
15382
  }
15075
15383
 
15076
15384
  // src/services/render/stages/freezePlan.ts
15077
- import { existsSync as existsSync19, mkdirSync as mkdirSync10, readFileSync as readFileSync10, readdirSync as readdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
15078
- import { join as join30, relative as relative3, resolve as resolve8 } from "node:path";
15385
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync11, readdirSync as readdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
15386
+ import { join as join31, relative as relative3, resolve as resolve9 } from "node:path";
15079
15387
 
15080
15388
  // src/services/distributed/planProtocol.ts
15081
15389
  var PLAN_SCHEMA_VERSION = 1;
@@ -15173,12 +15481,12 @@ function readPlanProtocolV1(planJson, capabilities = DISTRIBUTED_RENDER_CAPABILI
15173
15481
  }
15174
15482
 
15175
15483
  // src/services/render/stages/planHash.ts
15176
- import { createHash as createHash6 } from "node:crypto";
15177
- import { closeSync as closeSync4, createReadStream as createReadStream3, openSync as openSync4, readSync as readSync3 } from "node:fs";
15484
+ import { createHash as createHash7 } from "node:crypto";
15485
+ import { closeSync as closeSync5, createReadStream as createReadStream3, openSync as openSync5, readSync as readSync4 } from "node:fs";
15178
15486
  var PLAN_HASH_SCHEMA_PREFIX = "hyperframes-plan-hash-v1\0";
15179
15487
  var FIELD_DELIMITER = Buffer.from([0]);
15180
15488
  function computePlanHash(input) {
15181
- const hash = createHash6("sha256");
15489
+ const hash = createHash7("sha256");
15182
15490
  hash.update(PLAN_HASH_SCHEMA_PREFIX, "utf8");
15183
15491
  hash.update(input.compositionHtml);
15184
15492
  hash.update(FIELD_DELIMITER);
@@ -15225,27 +15533,27 @@ function canonicalJsonStringify(value) {
15225
15533
  throw new TypeError(`canonicalJsonStringify: unsupported value type ${typeof value}`);
15226
15534
  }
15227
15535
  function sha256Hex(bytes) {
15228
- const h = createHash6("sha256");
15536
+ const h = createHash7("sha256");
15229
15537
  h.update(bytes);
15230
15538
  return h.digest("hex");
15231
15539
  }
15232
15540
  function sha256File(path) {
15233
- const hash = createHash6("sha256");
15541
+ const hash = createHash7("sha256");
15234
15542
  const buffer = Buffer.allocUnsafe(1024 * 1024);
15235
- const fd = openSync4(path, "r");
15543
+ const fd = openSync5(path, "r");
15236
15544
  try {
15237
15545
  let bytesRead = 0;
15238
15546
  do {
15239
- bytesRead = readSync3(fd, buffer, 0, buffer.byteLength, null);
15547
+ bytesRead = readSync4(fd, buffer, 0, buffer.byteLength, null);
15240
15548
  if (bytesRead > 0) hash.update(buffer.subarray(0, bytesRead));
15241
15549
  } while (bytesRead > 0);
15242
15550
  } finally {
15243
- closeSync4(fd);
15551
+ closeSync5(fd);
15244
15552
  }
15245
15553
  return hash.digest("hex");
15246
15554
  }
15247
15555
  async function sha256FileAsync(path) {
15248
- const hash = createHash6("sha256");
15556
+ const hash = createHash7("sha256");
15249
15557
  for await (const chunk of createReadStream3(path, { highWaterMark: 1024 * 1024 })) {
15250
15558
  hash.update(chunk);
15251
15559
  }
@@ -15289,11 +15597,11 @@ function stripUndefined(value) {
15289
15597
  }
15290
15598
  function listPlanFiles(planDir) {
15291
15599
  const results = [];
15292
- const rootResolved = resolve8(planDir);
15600
+ const rootResolved = resolve9(planDir);
15293
15601
  function walk(dir) {
15294
15602
  const entries = readdirSync7(dir, { withFileTypes: true });
15295
15603
  for (const entry of entries) {
15296
- const full = join30(dir, entry.name);
15604
+ const full = join31(dir, entry.name);
15297
15605
  if (entry.isDirectory()) {
15298
15606
  walk(full);
15299
15607
  } else if (entry.isFile()) {
@@ -15314,7 +15622,7 @@ function collectPlanAssetShas(planDir, digests) {
15314
15622
  const assets = [];
15315
15623
  for (const file of files) {
15316
15624
  if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
15317
- compositionHtml = readFileSync10(file.absolutePath);
15625
+ compositionHtml = readFileSync11(file.absolutePath);
15318
15626
  continue;
15319
15627
  }
15320
15628
  if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
@@ -15334,23 +15642,23 @@ var PLAN_REMOTE_FILES_RELATIVE_PATH = "meta/remote-files.json";
15334
15642
  function writePlanRemoteFiles(planDir, files) {
15335
15643
  if (files.length === 0) return;
15336
15644
  const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : 1);
15337
- writeFileSync6(join30(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
15645
+ writeFileSync7(join31(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
15338
15646
  }
15339
15647
  function readPlanRemoteFiles(planDir) {
15340
- const path = join30(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
15341
- return existsSync19(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
15648
+ const path = join31(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
15649
+ return existsSync19(path) ? JSON.parse(readFileSync11(path, "utf-8")) : [];
15342
15650
  }
15343
15651
  function recomputePlanHashFromPlanDir(planDir, digests) {
15344
- const planJsonPath = join30(planDir, "plan.json");
15345
- const encoderJsonPath = join30(planDir, "meta", "encoder.json");
15652
+ const planJsonPath = join31(planDir, "plan.json");
15653
+ const encoderJsonPath = join31(planDir, "meta", "encoder.json");
15346
15654
  if (!existsSync19(planJsonPath)) {
15347
15655
  throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
15348
15656
  }
15349
15657
  if (!existsSync19(encoderJsonPath)) {
15350
15658
  throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
15351
15659
  }
15352
- const planJson = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
15353
- const encoderConfigCanonicalJson = readFileSync10(encoderJsonPath, "utf-8");
15660
+ const planJson = JSON.parse(readFileSync11(planJsonPath, "utf-8"));
15661
+ const encoderConfigCanonicalJson = readFileSync11(encoderJsonPath, "utf-8");
15354
15662
  const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
15355
15663
  return computePlanHash({
15356
15664
  compositionHtml,
@@ -15379,18 +15687,18 @@ async function freezePlan(input) {
15379
15687
  if (!existsSync19(planDir)) {
15380
15688
  throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
15381
15689
  }
15382
- const metaDir = join30(planDir, "meta");
15383
- if (!existsSync19(metaDir)) mkdirSync10(metaDir, { recursive: true });
15384
- writeFileSync6(
15385
- join30(metaDir, "composition.json"),
15690
+ const metaDir = join31(planDir, "meta");
15691
+ if (!existsSync19(metaDir)) mkdirSync11(metaDir, { recursive: true });
15692
+ writeFileSync7(
15693
+ join31(metaDir, "composition.json"),
15386
15694
  `${JSON.stringify(composition, null, 2)}
15387
15695
  `,
15388
15696
  "utf-8"
15389
15697
  );
15390
15698
  const encoderForCanonical = stripUndefined(encoder);
15391
15699
  const encoderConfigCanonicalJson = canonicalJsonStringify(encoderForCanonical);
15392
- writeFileSync6(join30(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
15393
- writeFileSync6(join30(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
15700
+ writeFileSync7(join31(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
15701
+ writeFileSync7(join31(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
15394
15702
  `, "utf-8");
15395
15703
  const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
15396
15704
  const planHash = computePlanHash({
@@ -15414,8 +15722,8 @@ async function freezePlan(input) {
15414
15722
  duration: durationSeconds,
15415
15723
  hasAudio
15416
15724
  };
15417
- const planJsonPath = join30(planDir, "plan.json");
15418
- writeFileSync6(planJsonPath, `${JSON.stringify(planJson, null, 2)}
15725
+ const planJsonPath = join31(planDir, "plan.json");
15726
+ writeFileSync7(planJsonPath, `${JSON.stringify(planJson, null, 2)}
15419
15727
  `, "utf-8");
15420
15728
  return { planJsonPath, planHash };
15421
15729
  }
@@ -15456,16 +15764,16 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
15456
15764
  }
15457
15765
 
15458
15766
  // src/services/distributed/chunkAudio.ts
15459
- import { closeSync as closeSync5, existsSync as existsSync20, ftruncateSync, mkdirSync as mkdirSync12, openSync as openSync5, readFileSync as readFileSync11 } from "node:fs";
15460
- import { writeFileSync as writeFileSync7, writeSync } from "node:fs";
15461
- import { join as join32 } from "node:path";
15462
- import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg6 } from "@hyperframes/engine";
15767
+ import { closeSync as closeSync6, existsSync as existsSync20, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync12 } from "node:fs";
15768
+ import { writeFileSync as writeFileSync8, writeSync } from "node:fs";
15769
+ import { join as join33 } from "node:path";
15770
+ import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
15463
15771
 
15464
15772
  // src/services/distributed/mp4Stitch.ts
15465
- import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync4, rmSync as rmSync12 } from "node:fs";
15773
+ import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync4, rmSync as rmSync13 } from "node:fs";
15466
15774
  import { open } from "node:fs/promises";
15467
- import { renameWithRetry as renameWithRetry5 } from "@hyperframes/engine";
15468
- import { dirname as dirname12, join as join31 } from "node:path";
15775
+ import { renameWithRetry as renameWithRetry6 } from "@hyperframes/engine";
15776
+ import { dirname as dirname13, join as join32 } from "node:path";
15469
15777
  import { finished, pipeline } from "node:stream/promises";
15470
15778
  function boxAt(src, off, at, limit) {
15471
15779
  let size = src.readUInt32BE(off);
@@ -15478,16 +15786,16 @@ function boxAt(src, off, at, limit) {
15478
15786
  return { type: src.toString("latin1", off + 4, off + 8), start: at, bodyStart, end: at + size };
15479
15787
  }
15480
15788
  function childBoxes(buf, parent) {
15481
- const boxes = [];
15482
- for (let at = parent.bodyStart; at < parent.end; at = boxes.at(-1).end) {
15483
- boxes.push(boxAt(buf, at, at, parent.end));
15789
+ const boxes2 = [];
15790
+ for (let at = parent.bodyStart; at < parent.end; at = boxes2.at(-1).end) {
15791
+ boxes2.push(boxAt(buf, at, at, parent.end));
15484
15792
  }
15485
- return boxes;
15793
+ return boxes2;
15486
15794
  }
15487
15795
  function findChild(buf, parent, type) {
15488
15796
  return childBoxes(buf, parent).find((box2) => box2.type === type);
15489
15797
  }
15490
- function child(buf, parent, type) {
15798
+ function child2(buf, parent, type) {
15491
15799
  const found = findChild(buf, parent, type);
15492
15800
  if (!found) throw new Error(`[mp4Stitch] ${parent.type} has no ${type}`);
15493
15801
  return found;
@@ -15539,7 +15847,7 @@ function rows(buf, table, columns, countAt = 4) {
15539
15847
  function readMediaTime(moov, trak) {
15540
15848
  const edts = findChild(moov, trak, "edts");
15541
15849
  if (!edts) return 0;
15542
- const elst = child(moov, edts, "elst");
15850
+ const elst = child2(moov, edts, "elst");
15543
15851
  const wide = moov[elst.bodyStart] === 1;
15544
15852
  const entries = moov.readUInt32BE(elst.bodyStart + 4);
15545
15853
  for (let i = 0, at = elst.bodyStart + 8; i < entries; i++, at += wide ? 20 : 12) {
@@ -15554,7 +15862,7 @@ function readSizes(moov, stsz) {
15554
15862
  if (!fixedSize) return rows(moov, stsz, 1, 8).map(([size]) => size);
15555
15863
  return Array(moov.readUInt32BE(stsz.bodyStart + 8)).fill(fixedSize);
15556
15864
  }
15557
- function readSync4(moov, stbl, sampleCount) {
15865
+ function readSync5(moov, stbl, sampleCount) {
15558
15866
  const stss = findChild(moov, stbl, "stss");
15559
15867
  if (!stss) return null;
15560
15868
  const sync = Array(sampleCount).fill(false);
@@ -15564,10 +15872,10 @@ function readSync4(moov, stbl, sampleCount) {
15564
15872
  function readChunkOffsets(moov, stbl) {
15565
15873
  const stco = findChild(moov, stbl, "stco");
15566
15874
  if (stco) return rows(moov, stco, 1).map(([offset]) => offset);
15567
- return rows(moov, child(moov, stbl, "co64"), 2).map(([hi, lo]) => hi * 2 ** 32 + lo);
15875
+ return rows(moov, child2(moov, stbl, "co64"), 2).map(([hi, lo]) => hi * 2 ** 32 + lo);
15568
15876
  }
15569
15877
  function readSamplesPerChunk(moov, stbl, chunkCount) {
15570
- const stsc = rows(moov, child(moov, stbl, "stsc"), 3);
15878
+ const stsc = rows(moov, child2(moov, stbl, "stsc"), 3);
15571
15879
  return stsc.flatMap(([firstChunk, perChunk, description], i) => {
15572
15880
  if (description !== 1) throw new Error("[mp4Stitch] chunk uses more than one sample entry");
15573
15881
  const nextFirst = stsc[i + 1]?.[0] ?? chunkCount + 1;
@@ -15575,28 +15883,28 @@ function readSamplesPerChunk(moov, stbl, chunkCount) {
15575
15883
  });
15576
15884
  }
15577
15885
  function readTrack(moov, trak) {
15578
- const mdia = child(moov, trak, "mdia");
15579
- const hdlr = child(moov, mdia, "hdlr");
15580
- const stbl = child(moov, child(moov, mdia, "minf"), "stbl");
15886
+ const mdia = child2(moov, trak, "mdia");
15887
+ const hdlr = child2(moov, mdia, "hdlr");
15888
+ const stbl = child2(moov, child2(moov, mdia, "minf"), "stbl");
15581
15889
  if (findChild(moov, stbl, "ctts")) throw new Error("[mp4Stitch] chunk has ctts (B-frames)");
15582
- const stts = rows(moov, child(moov, stbl, "stts"), 2);
15890
+ const stts = rows(moov, child2(moov, stbl, "stts"), 2);
15583
15891
  const durations = stts.flatMap(([n, delta]) => Array(n).fill(delta));
15584
- const sizes = readSizes(moov, child(moov, stbl, "stsz"));
15892
+ const sizes = readSizes(moov, child2(moov, stbl, "stsz"));
15585
15893
  const chunkOffsets = readChunkOffsets(moov, stbl);
15586
15894
  const samplesPerChunk = readSamplesPerChunk(moov, stbl, chunkOffsets.length);
15587
15895
  if (durations.length !== sizes.length || sum(samplesPerChunk) !== sizes.length) {
15588
15896
  throw new Error("[mp4Stitch] sample tables disagree on the sample count");
15589
15897
  }
15590
- const stsd = child(moov, stbl, "stsd");
15898
+ const stsd = child2(moov, stbl, "stsd");
15591
15899
  return {
15592
15900
  trak,
15593
15901
  handler: moov.toString("latin1", hdlr.bodyStart + 8, hdlr.bodyStart + 12),
15594
- timescale: timescaleOf(moov, child(moov, mdia, "mdhd")),
15902
+ timescale: timescaleOf(moov, child2(moov, mdia, "mdhd")),
15595
15903
  mediaTime: readMediaTime(moov, trak),
15596
15904
  stsd: moov.subarray(stsd.start, stsd.end),
15597
15905
  durations,
15598
15906
  sizes,
15599
- sync: readSync4(moov, stbl, sizes.length),
15907
+ sync: readSync5(moov, stbl, sizes.length),
15600
15908
  samplesPerChunk,
15601
15909
  chunkOffsets
15602
15910
  };
@@ -15737,9 +16045,9 @@ function buildStitchedHeader(layouts, audio) {
15737
16045
  }
15738
16046
  const template = layouts[0].moov;
15739
16047
  const root = boxAt(template, 0, 0, template.length);
15740
- const mvhd = child(template, root, "mvhd");
16048
+ const mvhd = child2(template, root, "mvhd");
15741
16049
  const movieTimescale = timescaleOf(template, mvhd);
15742
- const chunkTrackIds = plan2.slice(0, chunkTrackCount).map((track) => idOf(template, child(template, track.trak, "tkhd")));
16050
+ const chunkTrackIds = plan2.slice(0, chunkTrackCount).map((track) => idOf(template, child2(template, track.trak, "tkhd")));
15743
16051
  const audioTrackId = Math.max(...chunkTrackIds) + 1;
15744
16052
  const buildMoov = (dataStart2) => {
15745
16053
  const traks = plan2.map((track, t) => {
@@ -15829,18 +16137,18 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
15829
16137
  const sourcePaths = audioPath ? [audioPath, ...chunkPaths] : chunkPaths;
15830
16138
  const layouts = await Promise.all(sourcePaths.map((path) => readMp4Layout(fileRange(path))));
15831
16139
  const header = audioPath ? buildStitchedHeader(layouts.slice(1), layouts[0]) : buildStitchedHeader(layouts);
15832
- mkdirSync11(dirname12(outputPath), { recursive: true });
16140
+ mkdirSync12(dirname13(outputPath), { recursive: true });
15833
16141
  const workDir = mkdtempSync4(`${outputPath}.stitch-`);
15834
- const out = createWriteStream(join31(workDir, "out.mp4"));
16142
+ const out = createWriteStream(join32(workDir, "out.mp4"));
15835
16143
  try {
15836
16144
  out.write(header);
15837
16145
  for (const [i, path] of sourcePaths.entries()) await copyPayload(path, layouts[i], i, out);
15838
16146
  out.end();
15839
16147
  await finished(out);
15840
- renameWithRetry5(join31(workDir, "out.mp4"), outputPath);
16148
+ renameWithRetry6(join32(workDir, "out.mp4"), outputPath);
15841
16149
  } finally {
15842
16150
  out.destroy();
15843
- rmSync12(workDir, { recursive: true, force: true });
16151
+ rmSync13(workDir, { recursive: true, force: true });
15844
16152
  }
15845
16153
  }
15846
16154
 
@@ -15929,9 +16237,9 @@ async function writeChunkAudioSlices(input) {
15929
16237
  offset += track.sizes[offsets.length - 1];
15930
16238
  }
15931
16239
  });
15932
- const bytes = readFileSync11(input.audioPath);
16240
+ const bytes = readFileSync12(input.audioPath);
15933
16241
  const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
15934
- mkdirSync12(join32(input.planDir, SLICE_DIR), { recursive: true });
16242
+ mkdirSync13(join33(input.planDir, SLICE_DIR), { recursive: true });
15935
16243
  input.chunks.forEach((_, index) => {
15936
16244
  const cut = audioCutForChunk(grid, input.chunks, index, input.fps);
15937
16245
  if (!cut) return;
@@ -15939,18 +16247,18 @@ async function writeChunkAudioSlices(input) {
15939
16247
  const start = offsets[cut.firstPacket];
15940
16248
  layout.partStarts[index] = start;
15941
16249
  const end = offsets[last] + track.sizes[last];
15942
- writeFileSync7(join32(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
16250
+ writeFileSync8(join33(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
15943
16251
  });
15944
16252
  const frame = [bytes.subarray(0, payloadStart), bytes.subarray(payloadEnd)];
15945
- writeFileSync7(join32(input.planDir, SLICE_FRAME), Buffer.concat(frame));
15946
- writeFileSync7(join32(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
16253
+ writeFileSync8(join33(input.planDir, SLICE_FRAME), Buffer.concat(frame));
16254
+ writeFileSync8(join33(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
15947
16255
  }
15948
16256
  function chunkAudioSource(planDir, chunkIndex, scratchPath) {
15949
- const layoutPath = join32(planDir, SLICE_LAYOUT);
16257
+ const layoutPath = join33(planDir, SLICE_LAYOUT);
15950
16258
  if (!existsSync20(layoutPath)) return resolvePlanAudioPath(planDir);
15951
- const layout = JSON.parse(readFileSync11(layoutPath, "utf-8"));
15952
- const frame = readFileSync11(join32(planDir, SLICE_FRAME));
15953
- const fd = openSync5(scratchPath, "w");
16259
+ const layout = JSON.parse(readFileSync12(layoutPath, "utf-8"));
16260
+ const frame = readFileSync12(join33(planDir, SLICE_FRAME));
16261
+ const fd = openSync6(scratchPath, "w");
15954
16262
  try {
15955
16263
  writeSync(fd, frame, 0, layout.payloadStart, 0);
15956
16264
  writeSync(
@@ -15962,12 +16270,12 @@ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
15962
16270
  );
15963
16271
  const start = layout.partStarts[chunkIndex];
15964
16272
  if (start !== void 0) {
15965
- const part = readFileSync11(join32(planDir, chunkAudioSlicePath(chunkIndex)));
16273
+ const part = readFileSync12(join33(planDir, chunkAudioSlicePath(chunkIndex)));
15966
16274
  writeSync(fd, part, 0, part.length, start);
15967
16275
  }
15968
16276
  ftruncateSync(fd, layout.sizeBytes);
15969
16277
  } finally {
15970
- closeSync5(fd);
16278
+ closeSync6(fd);
15971
16279
  }
15972
16280
  return scratchPath;
15973
16281
  }
@@ -15987,7 +16295,7 @@ async function muxChunkWithAudio(input) {
15987
16295
  const args = [...chunkMuxArgs(input, cut), "-c", "copy"];
15988
16296
  appendRenderProvenanceArgs(args, input.outputPath);
15989
16297
  args.push("-y", input.outputPath);
15990
- const result = await runFfmpeg6(args, { signal: input.signal, timeout: input.timeoutMs });
16298
+ const result = await runFfmpeg7(args, { signal: input.signal, timeout: input.timeoutMs });
15991
16299
  if (!result.success) {
15992
16300
  throw new Error(
15993
16301
  `[chunkAudio] chunk mux failed: ${describeFfmpegFailure3(result, input.timeoutMs)}`
@@ -16016,9 +16324,9 @@ async function muxChunkWithAudio(input) {
16016
16324
  }
16017
16325
 
16018
16326
  // src/services/distributed/planSize.ts
16019
- import { createHash as createHash7 } from "node:crypto";
16327
+ import { createHash as createHash8 } from "node:crypto";
16020
16328
  import { lstatSync as lstatSync2, readdirSync as readdirSync8 } from "node:fs";
16021
- import { extname as extname5, join as join33, relative as relative4 } from "node:path";
16329
+ import { extname as extname5, join as join34, relative as relative4 } from "node:path";
16022
16330
  var MAX_TOP_COMPONENTS = 10;
16023
16331
  var SOURCE_MEDIA_EXTENSIONS = /* @__PURE__ */ new Set([
16024
16332
  ".aac",
@@ -16046,7 +16354,7 @@ var PLAN_ROOT_CATEGORY = {
16046
16354
  "plan.json": "metadata"
16047
16355
  };
16048
16356
  function hashComponent(value) {
16049
- return createHash7("sha256").update(value).digest("hex").slice(0, 12);
16357
+ return createHash8("sha256").update(value).digest("hex").slice(0, 12);
16050
16358
  }
16051
16359
  function classifyFile(segments, rootKind) {
16052
16360
  const rootName = segments[0] ?? "";
@@ -16130,7 +16438,7 @@ function walkRegularFiles(dir, visit) {
16130
16438
  return;
16131
16439
  }
16132
16440
  for (const entry of entries) {
16133
- const full = join33(dir, entry.name);
16441
+ const full = join34(dir, entry.name);
16134
16442
  if (entry.isDirectory()) {
16135
16443
  walkRegularFiles(full, visit);
16136
16444
  } else if (entry.isFile()) {
@@ -16342,13 +16650,13 @@ function assertPositiveInteger2(name, value) {
16342
16650
  }
16343
16651
  var FREEZE_OWNED_PLAN_FILES = [
16344
16652
  "plan.json",
16345
- join34("meta", "composition.json"),
16346
- join34("meta", "encoder.json"),
16347
- join34("meta", "chunks.json")
16653
+ join35("meta", "composition.json"),
16654
+ join35("meta", "encoder.json"),
16655
+ join35("meta", "chunks.json")
16348
16656
  ];
16349
16657
  function removeFreezeOwnedPlanFiles(planDir) {
16350
16658
  for (const relativePath of FREEZE_OWNED_PLAN_FILES) {
16351
- rmSync13(join34(planDir, relativePath), { force: true });
16659
+ rmSync14(join35(planDir, relativePath), { force: true });
16352
16660
  }
16353
16661
  }
16354
16662
  function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
@@ -16493,23 +16801,23 @@ function createVideoSourceShipper(input) {
16493
16801
  const known = shipped.get(srcPath);
16494
16802
  if (known) return known;
16495
16803
  const stagedRel = pathInside(input.workVideoFramesDir, srcPath);
16496
- const current = stagedRel === null ? srcPath : join34(input.planDir, "video-frames", stagedRel);
16804
+ const current = stagedRel === null ? srcPath : join35(input.planDir, "video-frames", stagedRel);
16497
16805
  let result = null;
16498
16806
  for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
16499
16807
  const rel = pathInside(root, srcPath);
16500
- if (rel !== null && (existsSync21(join34(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
16808
+ if (rel !== null && (existsSync21(join35(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
16501
16809
  result = ["compiled", ...rel.split(sep2)].join("/");
16502
16810
  break;
16503
16811
  }
16504
16812
  }
16505
16813
  if (result === null) {
16506
16814
  result = `video-sources/${shipped.size}${extname6(current)}`;
16507
- const destination = join34(input.planDir, ...result.split("/"));
16508
- mkdirSync13(join34(input.planDir, "video-sources"), { recursive: true });
16815
+ const destination = join35(input.planDir, ...result.split("/"));
16816
+ mkdirSync14(join35(input.planDir, "video-sources"), { recursive: true });
16509
16817
  try {
16510
16818
  linkSync(current, destination);
16511
16819
  } catch {
16512
- copyFileSync5(current, destination);
16820
+ copyFileSync6(current, destination);
16513
16821
  }
16514
16822
  }
16515
16823
  shipped.set(srcPath, result);
@@ -16525,7 +16833,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
16525
16833
  useGpu: false,
16526
16834
  browserGpuMode: "software"
16527
16835
  });
16528
- if (!existsSync21(planDir)) mkdirSync13(planDir, { recursive: true });
16836
+ if (!existsSync21(planDir)) mkdirSync14(planDir, { recursive: true });
16529
16837
  const log = config.logger ?? defaultLogger;
16530
16838
  const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
16531
16839
  const abortSignal = config.abortSignal;
@@ -16554,15 +16862,15 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
16554
16862
  variables: config.variables
16555
16863
  });
16556
16864
  const entryFile = config.entryFile ?? "index.html";
16557
- const htmlPath = join34(projectDir, entryFile);
16865
+ const htmlPath = join35(projectDir, entryFile);
16558
16866
  if (!existsSync21(htmlPath)) {
16559
16867
  throw new Error(`[plan] entry file not found: ${htmlPath}`);
16560
16868
  }
16561
- const workDir = join34(planDir, ".plan-work");
16562
- rmSync13(workDir, { recursive: true, force: true });
16563
- mkdirSync13(workDir, { recursive: true });
16564
- const compiledDir = join34(workDir, "compiled");
16565
- mkdirSync13(compiledDir, { recursive: true });
16869
+ const workDir = join35(planDir, ".plan-work");
16870
+ rmSync14(workDir, { recursive: true, force: true });
16871
+ mkdirSync14(workDir, { recursive: true });
16872
+ const compiledDir = join35(workDir, "compiled");
16873
+ mkdirSync14(compiledDir, { recursive: true });
16566
16874
  let stageStarted = Date.now();
16567
16875
  cpSync2(projectDir, compiledDir, {
16568
16876
  recursive: true,
@@ -16575,10 +16883,10 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
16575
16883
  }
16576
16884
  });
16577
16885
  logPlanStage(log, "copy", stageStarted);
16578
- const finalCompiledDir = join34(planDir, "compiled");
16886
+ const finalCompiledDir = join35(planDir, "compiled");
16579
16887
  const needsAlpha = outputNeedsAlpha(config.format);
16580
16888
  const remoteFiles = Object.entries(options.remoteFiles ?? {});
16581
- const remoteLocalPath = (rel) => join34(projectDir, ...rel.split("/"));
16889
+ const remoteLocalPath = (rel) => join35(projectDir, ...rel.split("/"));
16582
16890
  for (const [, file] of remoteFiles) {
16583
16891
  declareContentAddressedUrl(file.url, file.sha256);
16584
16892
  if (file.probeFactsUrl) declareProbeFactsUrl(file.url, file.probeFactsUrl);
@@ -16764,18 +17072,18 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
16764
17072
  let stageStarted = Date.now();
16765
17073
  const extractResult = await extractPlanVideos(c, options.deferVideoExtraction);
16766
17074
  logPlanStage(log, "extract", stageStarted);
16767
- const stagedVideoFrames = join34(compiledDir, "__hyperframes_video_frames");
16768
- const videoFramesDst = join34(planDir, "video-frames");
16769
- if (existsSync21(videoFramesDst)) rmSync13(videoFramesDst, { recursive: true, force: true });
16770
- const videoSourcesDir = join34(planDir, "video-sources");
16771
- rmSync13(videoSourcesDir, { recursive: true, force: true });
17075
+ const stagedVideoFrames = join35(compiledDir, "__hyperframes_video_frames");
17076
+ const videoFramesDst = join35(planDir, "video-frames");
17077
+ if (existsSync21(videoFramesDst)) rmSync14(videoFramesDst, { recursive: true, force: true });
17078
+ const videoSourcesDir = join35(planDir, "video-sources");
17079
+ rmSync14(videoSourcesDir, { recursive: true, force: true });
16772
17080
  if (existsSync21(stagedVideoFrames)) {
16773
- renameWithRetry6(stagedVideoFrames, videoFramesDst);
17081
+ renameWithRetry7(stagedVideoFrames, videoFramesDst);
16774
17082
  } else {
16775
- mkdirSync13(videoFramesDst, { recursive: true });
17083
+ mkdirSync14(videoFramesDst, { recursive: true });
16776
17084
  }
16777
- if (existsSync21(finalCompiledDir)) rmSync13(finalCompiledDir, { recursive: true, force: true });
16778
- renameWithRetry6(compiledDir, finalCompiledDir);
17085
+ if (existsSync21(finalCompiledDir)) rmSync14(finalCompiledDir, { recursive: true, force: true });
17086
+ renameWithRetry7(compiledDir, finalCompiledDir);
16779
17087
  const shipVideoSource = createVideoSourceShipper({
16780
17088
  planDir,
16781
17089
  projectDir,
@@ -16804,7 +17112,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
16804
17112
  };
16805
17113
  })
16806
17114
  });
16807
- mkdirSync13(join34(planDir, "meta"), { recursive: true });
17115
+ mkdirSync14(join35(planDir, "meta"), { recursive: true });
16808
17116
  writePlanRemoteFiles(
16809
17117
  planDir,
16810
17118
  remoteFiles.map(([rel, { sha256, sizeBytes }]) => ({
@@ -16813,8 +17121,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
16813
17121
  sizeBytes
16814
17122
  }))
16815
17123
  );
16816
- writeFileSync8(
16817
- join34(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
17124
+ writeFileSync9(
17125
+ join35(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
16818
17126
  JSON.stringify(planVideosJson, null, 2),
16819
17127
  "utf-8"
16820
17128
  );
@@ -16893,9 +17201,9 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
16893
17201
  if (audioResult?.audioError) {
16894
17202
  applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
16895
17203
  }
16896
- const planAudioPath = join34(planDir, PLAN_AUDIO_RELATIVE_PATH);
17204
+ const planAudioPath = join35(planDir, PLAN_AUDIO_RELATIVE_PATH);
16897
17205
  if (audioResult?.hasAudio && existsSync21(audioResult.audioOutputPath)) {
16898
- renameWithRetry6(audioResult.audioOutputPath, planAudioPath);
17206
+ renameWithRetry7(audioResult.audioOutputPath, planAudioPath);
16899
17207
  }
16900
17208
  if (chunksCarryAudio && existsSync21(planAudioPath)) {
16901
17209
  if (options.sliceChunkAudio) {
@@ -16942,7 +17250,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
16942
17250
  format: config.format
16943
17251
  };
16944
17252
  try {
16945
- rmSync13(workDir, { recursive: true, force: true });
17253
+ rmSync14(workDir, { recursive: true, force: true });
16946
17254
  } catch (err) {
16947
17255
  log.warn("[plan] failed to remove temp work dir", {
16948
17256
  workDir,
@@ -17513,14 +17821,14 @@ import {
17513
17821
  // src/server.ts
17514
17822
  import {
17515
17823
  existsSync as existsSync24,
17516
- mkdirSync as mkdirSync14,
17517
- statSync as statSync9,
17824
+ mkdirSync as mkdirSync15,
17825
+ statSync as statSync10,
17518
17826
  mkdtempSync as mkdtempSync5,
17519
- writeFileSync as writeFileSync9,
17520
- rmSync as rmSync14,
17827
+ writeFileSync as writeFileSync10,
17828
+ rmSync as rmSync15,
17521
17829
  createReadStream as createReadStream5
17522
17830
  } from "node:fs";
17523
- import { resolve as resolve10, dirname as dirname14, join as join37 } from "node:path";
17831
+ import { resolve as resolve11, dirname as dirname15, join as join38 } from "node:path";
17524
17832
  import { tmpdir as tmpdir3 } from "node:os";
17525
17833
  import { parseArgs } from "node:util";
17526
17834
  import crypto from "node:crypto";
@@ -17532,14 +17840,14 @@ import { fetchPublicHttpsText as fetchPublicHttpsText2 } from "@hyperframes/engi
17532
17840
  // src/services/hyperframeLint.ts
17533
17841
  import {
17534
17842
  existsSync as existsSync22,
17535
- readFileSync as readFileSync12,
17536
- statSync as statSync8,
17537
- openSync as openSync6,
17843
+ readFileSync as readFileSync13,
17844
+ statSync as statSync9,
17845
+ openSync as openSync7,
17538
17846
  fstatSync as fstatSync3,
17539
- closeSync as closeSync6,
17847
+ closeSync as closeSync7,
17540
17848
  constants as constants3
17541
17849
  } from "node:fs";
17542
- import { resolve as resolve9, join as join35, relative as relative6, isAbsolute as isAbsolute4, sep as sep3 } from "node:path";
17850
+ import { resolve as resolve10, join as join36, relative as relative6, isAbsolute as isAbsolute4, sep as sep3 } from "node:path";
17543
17851
  import { lintHyperframeHtml } from "@hyperframes/lint";
17544
17852
  function isStringRecord(value) {
17545
17853
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -17569,30 +17877,30 @@ function pickEntryFile(files, preferredEntryFile) {
17569
17877
  function readEntryHtml(filePath) {
17570
17878
  let fd;
17571
17879
  try {
17572
- fd = openSync6(filePath, constants3.O_RDONLY | constants3.O_NONBLOCK);
17880
+ fd = openSync7(filePath, constants3.O_RDONLY | constants3.O_NONBLOCK);
17573
17881
  } catch (error) {
17574
17882
  if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
17575
17883
  return null;
17576
- if (!statSync8(filePath, { throwIfNoEntry: false })?.isFile()) return null;
17884
+ if (!statSync9(filePath, { throwIfNoEntry: false })?.isFile()) return null;
17577
17885
  throw error;
17578
17886
  }
17579
17887
  try {
17580
17888
  if (!fstatSync3(fd).isFile()) return null;
17581
- return readFileSync12(fd, "utf-8");
17889
+ return readFileSync13(fd, "utf-8");
17582
17890
  } finally {
17583
- closeSync6(fd);
17891
+ closeSync7(fd);
17584
17892
  }
17585
17893
  }
17586
17894
  function readProjectEntryFile(projectDir, preferredEntryFile) {
17587
- const absProjectDir = resolve9(projectDir);
17588
- if (!existsSync22(absProjectDir) || !statSync8(absProjectDir).isDirectory()) {
17895
+ const absProjectDir = resolve10(projectDir);
17896
+ if (!existsSync22(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
17589
17897
  return { error: `Project directory not found: ${absProjectDir}` };
17590
17898
  }
17591
17899
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
17592
17900
  (value) => typeof value === "string" && value.trim().length > 0
17593
17901
  );
17594
17902
  for (const entryFile of entryCandidates) {
17595
- const absoluteEntryPath = resolve9(absProjectDir, entryFile);
17903
+ const absoluteEntryPath = resolve10(absProjectDir, entryFile);
17596
17904
  const entryRelativePath = relative6(absProjectDir, absoluteEntryPath);
17597
17905
  if (entryRelativePath === ".." || entryRelativePath.startsWith(`..${sep3}`) || isAbsolute4(entryRelativePath)) {
17598
17906
  return { error: `Entry file must stay inside project directory: ${entryFile}` };
@@ -17607,7 +17915,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
17607
17915
  }
17608
17916
  }
17609
17917
  return {
17610
- error: `No HTML entry file found in project directory: ${join35(absProjectDir, preferredEntryFile || "index.html")}`
17918
+ error: `No HTML entry file found in project directory: ${join36(absProjectDir, preferredEntryFile || "index.html")}`
17611
17919
  };
17612
17920
  }
17613
17921
  function prepareHyperframeLintBody(body) {
@@ -17653,7 +17961,7 @@ async function runHyperframeLint(prepared) {
17653
17961
  // src/services/healthWorker.ts
17654
17962
  import { Worker as Worker2 } from "node:worker_threads";
17655
17963
  import { fileURLToPath as fileURLToPath6 } from "node:url";
17656
- import { dirname as dirname13, join as join36 } from "node:path";
17964
+ import { dirname as dirname14, join as join37 } from "node:path";
17657
17965
  import { existsSync as existsSync23 } from "node:fs";
17658
17966
  var DEFAULT_HEALTH_PORT = 9848;
17659
17967
  async function startHealthWorker(options = {}) {
@@ -17672,12 +17980,12 @@ async function startHealthWorker(options = {}) {
17672
17980
  stdout: false,
17673
17981
  stderr: false
17674
17982
  });
17675
- await new Promise((resolve12, reject) => {
17983
+ await new Promise((resolve13, reject) => {
17676
17984
  const onMessage = (msg) => {
17677
17985
  if (msg?.type === "listening") {
17678
17986
  worker.off("error", onError);
17679
17987
  worker.off("message", onMessage);
17680
- resolve12();
17988
+ resolve13();
17681
17989
  } else if (msg?.type === "listen-error") {
17682
17990
  worker.off("error", onError);
17683
17991
  worker.off("message", onMessage);
@@ -17728,8 +18036,8 @@ function defaultLogger2() {
17728
18036
  };
17729
18037
  }
17730
18038
  function resolveWorkerEntry2() {
17731
- const here = dirname13(fileURLToPath6(import.meta.url));
17732
- const candidates = [join36(here, "healthWorkerThread.js"), join36(here, "healthWorkerThread.ts")];
18039
+ const here = dirname14(fileURLToPath6(import.meta.url));
18040
+ const candidates = [join37(here, "healthWorkerThread.js"), join37(here, "healthWorkerThread.ts")];
17733
18041
  for (const candidate of candidates) {
17734
18042
  if (existsSync23(candidate)) return candidate;
17735
18043
  }
@@ -17747,6 +18055,8 @@ var DEFAULT_SERVER_FPS = { num: 30, den: 1 };
17747
18055
  var SSE_KEEPALIVE_MS = 3e4;
17748
18056
  var SAFE_RENDER_ERROR_CODES = /* @__PURE__ */ new Set([
17749
18057
  "ASSET_MEDIA_TYPE_MISMATCH",
18058
+ "HEIF_IMAGE_CONVERSION_FAILED",
18059
+ "HEIF_TOOL_UNAVAILABLE",
17750
18060
  "NOT_MEDIA_PAYLOAD",
17751
18061
  "INVALID_VIDEO_METADATA",
17752
18062
  "VIDEO_SOURCE_UNRENDERABLE",
@@ -17882,8 +18192,8 @@ function buildRenderJobConfig(input, outputPath, log) {
17882
18192
  function resolvePreparedRenderOutput(prepared, rendersDir, log) {
17883
18193
  const { input, cleanupProjectDir } = prepared;
17884
18194
  const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
17885
- const outputDir = dirname14(absoluteOutputPath);
17886
- if (!existsSync24(outputDir)) mkdirSync14(outputDir, { recursive: true });
18195
+ const outputDir = dirname15(absoluteOutputPath);
18196
+ if (!existsSync24(outputDir)) mkdirSync15(outputDir, { recursive: true });
17887
18197
  return { input, cleanupProjectDir, absoluteOutputPath };
17888
18198
  }
17889
18199
  function validateRenderOverrides(body) {
@@ -17923,12 +18233,12 @@ var MAX_PREVIEW_HTML_BYTES = 20 * 1024 * 1024;
17923
18233
  function prepareProjectDirectory(projectDir, options) {
17924
18234
  const candidate = nonEmptyString(projectDir);
17925
18235
  if (!candidate) return null;
17926
- const absProjectDir = resolve10(candidate);
17927
- if (!existsSync24(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
18236
+ const absProjectDir = resolve11(candidate);
18237
+ if (!existsSync24(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
17928
18238
  return { error: `Project directory not found: ${absProjectDir}` };
17929
18239
  }
17930
18240
  const entry = options.entryFile || "index.html";
17931
- if (!existsSync24(resolve10(absProjectDir, entry))) {
18241
+ if (!existsSync24(resolve11(absProjectDir, entry))) {
17932
18242
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
17933
18243
  }
17934
18244
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -17949,8 +18259,8 @@ async function resolveInlineRenderHtml(body) {
17949
18259
  }
17950
18260
  function materializeInlineProject(html, options) {
17951
18261
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
17952
- const tempProjectDir = mkdtempSync5(join37(tempRoot, "producer-project-"));
17953
- writeFileSync9(join37(tempProjectDir, "index.html"), html, "utf-8");
18262
+ const tempProjectDir = mkdtempSync5(join38(tempRoot, "producer-project-"));
18263
+ writeFileSync10(join38(tempProjectDir, "index.html"), html, "utf-8");
17954
18264
  return {
17955
18265
  prepared: {
17956
18266
  input: { projectDir: tempProjectDir, ...options },
@@ -17975,7 +18285,7 @@ function resolveOutputPath(projectDir, outputCandidate, rendersDir, log) {
17975
18285
  try {
17976
18286
  return resolveRenderPaths(projectDir, outputCandidate, rendersDir).absoluteOutputPath;
17977
18287
  } catch (error) {
17978
- const fallbackPath = resolve10(rendersDir, `producer-fallback-${Date.now()}.mp4`);
18288
+ const fallbackPath = resolve11(rendersDir, `producer-fallback-${Date.now()}.mp4`);
17979
18289
  log.warn("Failed to resolve output path, using fallback", {
17980
18290
  fallback: fallbackPath,
17981
18291
  error: error instanceof Error ? error.message : String(error)
@@ -18011,7 +18321,7 @@ function createArtifactStore(ttlMs) {
18011
18321
  function cleanupTempDir(dir, log) {
18012
18322
  if (!dir) return;
18013
18323
  try {
18014
- rmSync14(dir, { recursive: true, force: true });
18324
+ rmSync15(dir, { recursive: true, force: true });
18015
18325
  } catch (error) {
18016
18326
  log.warn("Failed to cleanup temp project dir", {
18017
18327
  cleanupProjectDir: dir,
@@ -18020,7 +18330,7 @@ function cleanupTempDir(dir, log) {
18020
18330
  }
18021
18331
  }
18022
18332
  function outputFileSize(path) {
18023
- return existsSync24(path) ? statSync9(path).size : 0;
18333
+ return existsSync24(path) ? statSync10(path).size : 0;
18024
18334
  }
18025
18335
  function createBlockingProgressReporter(log, requestId) {
18026
18336
  let lastLoggedPct = -10;
@@ -18326,7 +18636,7 @@ function createRenderHandlers(options = {}) {
18326
18636
  store.delete(token);
18327
18637
  return c.json({ success: false, error: "Output artifact file missing" }, 404);
18328
18638
  }
18329
- const stats = statSync9(artifact.path);
18639
+ const stats = statSync10(artifact.path);
18330
18640
  return new Response(createReadStream5(artifact.path), {
18331
18641
  headers: {
18332
18642
  "content-type": "video/mp4",
@@ -18393,7 +18703,7 @@ function startServer(options = {}) {
18393
18703
  process.on("SIGINT", () => shutdown("SIGINT"));
18394
18704
  return server;
18395
18705
  }
18396
- var entryScript = process.argv[1] ? resolve10(process.argv[1]) : "";
18706
+ var entryScript = process.argv[1] ? resolve11(process.argv[1]) : "";
18397
18707
  var isPublicServerEntry = entryScript.endsWith("/public-server.js") || entryScript.endsWith("/src/server.ts");
18398
18708
  if (isPublicServerEntry) {
18399
18709
  const { values } = parseArgs({
@@ -18418,35 +18728,35 @@ import { quantizeTimeToFrame, MEDIA_VISUAL_STYLE_PROPERTIES } from "@hyperframes
18418
18728
 
18419
18729
  // src/services/distributed/planV2.ts
18420
18730
  import {
18421
- copyFileSync as copyFileSync7,
18731
+ copyFileSync as copyFileSync8,
18422
18732
  existsSync as existsSync26,
18423
18733
  lstatSync as lstatSync3,
18424
- mkdirSync as mkdirSync16,
18734
+ mkdirSync as mkdirSync17,
18425
18735
  mkdtempSync as mkdtempSync7,
18426
- readFileSync as readFileSync13,
18736
+ readFileSync as readFileSync14,
18427
18737
  readdirSync as readdirSync9,
18428
- rmSync as rmSync16,
18429
- statSync as statSync11,
18430
- writeFileSync as writeFileSync11
18738
+ rmSync as rmSync17,
18739
+ statSync as statSync12,
18740
+ writeFileSync as writeFileSync12
18431
18741
  } from "node:fs";
18432
18742
  import { tmpdir as tmpdir4 } from "node:os";
18433
- import { dirname as dirname16, isAbsolute as isAbsolute5, join as join40, relative as relative7, resolve as resolve11, sep as sep4 } from "node:path";
18743
+ import { dirname as dirname17, isAbsolute as isAbsolute5, join as join41, relative as relative7, resolve as resolve12, sep as sep4 } from "node:path";
18434
18744
  import { DISTRIBUTED_FPS as DISTRIBUTED_FPS2, isDistributedFps as isDistributedFps3 } from "@hyperframes/core";
18435
- import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as renameWithRetry8 } from "@hyperframes/engine";
18745
+ import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as renameWithRetry9 } from "@hyperframes/engine";
18436
18746
 
18437
18747
  // src/services/distributed/planV2Publisher.ts
18438
18748
  import {
18439
- copyFileSync as copyFileSync6,
18749
+ copyFileSync as copyFileSync7,
18440
18750
  existsSync as existsSync25,
18441
18751
  linkSync as linkSync2,
18442
- mkdirSync as mkdirSync15,
18752
+ mkdirSync as mkdirSync16,
18443
18753
  mkdtempSync as mkdtempSync6,
18444
- rmSync as rmSync15,
18445
- statSync as statSync10,
18446
- writeFileSync as writeFileSync10
18754
+ rmSync as rmSync16,
18755
+ statSync as statSync11,
18756
+ writeFileSync as writeFileSync11
18447
18757
  } from "node:fs";
18448
- import { renameWithRetry as renameWithRetry7 } from "@hyperframes/engine";
18449
- import { dirname as dirname15, join as join39 } from "node:path";
18758
+ import { renameWithRetry as renameWithRetry8 } from "@hyperframes/engine";
18759
+ import { dirname as dirname16, join as join40 } from "node:path";
18450
18760
 
18451
18761
  // src/services/distributed/planV2Errors.ts
18452
18762
  var PLAN_V2_INTEGRITY_UNRECOVERABLE = "PLAN_V2_INTEGRITY_UNRECOVERABLE";
@@ -18461,7 +18771,7 @@ var PlanV2IntegrityError = class extends Error {
18461
18771
  };
18462
18772
 
18463
18773
  // src/services/distributed/planV2Layout.ts
18464
- import { join as join38 } from "node:path";
18774
+ import { join as join39 } from "node:path";
18465
18775
  var SHA256_PATTERN = /^[0-9a-f]{64}$/;
18466
18776
  function assertPlanV2Sha256(value, field) {
18467
18777
  if (typeof value !== "string" || !SHA256_PATTERN.test(value)) {
@@ -18471,7 +18781,7 @@ function assertPlanV2Sha256(value, field) {
18471
18781
  }
18472
18782
  function planV2BlobPath(root, sha256) {
18473
18783
  const digest = assertPlanV2Sha256(sha256, "artifact sha256");
18474
- return join38(root, "artifacts", "sha256", digest.slice(0, 2), digest);
18784
+ return join39(root, "artifacts", "sha256", digest.slice(0, 2), digest);
18475
18785
  }
18476
18786
 
18477
18787
  // src/services/distributed/planV2Publisher.ts
@@ -18511,12 +18821,12 @@ var LocalPlanV2ArtifactPublisher = class {
18511
18821
  }
18512
18822
  this.destinationDir = destinationDir;
18513
18823
  this.#linkFile = options.linkFile ?? linkSync2;
18514
- mkdirSync15(dirname15(destinationDir), { recursive: true });
18515
- this.temporaryDir = mkdtempSync6(join39(dirname15(destinationDir), ".plan-v2-publish-"));
18824
+ mkdirSync16(dirname16(destinationDir), { recursive: true });
18825
+ this.temporaryDir = mkdtempSync6(join40(dirname16(destinationDir), ".plan-v2-publish-"));
18516
18826
  }
18517
18827
  async putBlob(blob) {
18518
18828
  const digest = assertPlanV2Sha256(blob.sha256, "published blob sha256");
18519
- const sourceSize = statSync10(blob.sourcePath).size;
18829
+ const sourceSize = statSync11(blob.sourcePath).size;
18520
18830
  if (sourceSize !== blob.sizeBytes) {
18521
18831
  throw new PlanV2IntegrityError(
18522
18832
  `published blob size changed for ${digest}: expected ${blob.sizeBytes}, got ${sourceSize}`
@@ -18524,19 +18834,19 @@ var LocalPlanV2ArtifactPublisher = class {
18524
18834
  }
18525
18835
  const destinationPath = planV2BlobPath(this.temporaryDir, digest);
18526
18836
  if (existsSync25(destinationPath)) return;
18527
- mkdirSync15(dirname15(destinationPath), { recursive: true });
18528
- const stagingDir = mkdtempSync6(join39(dirname15(destinationPath), ".plan-v2-blob-"));
18529
- const temporaryPath = join39(stagingDir, "blob");
18837
+ mkdirSync16(dirname16(destinationPath), { recursive: true });
18838
+ const stagingDir = mkdtempSync6(join40(dirname16(destinationPath), ".plan-v2-blob-"));
18839
+ const temporaryPath = join40(stagingDir, "blob");
18530
18840
  try {
18531
18841
  try {
18532
18842
  this.#linkFile(blob.sourcePath, temporaryPath);
18533
18843
  } catch (error) {
18534
18844
  if (!canFallbackToCopy(error)) throw error;
18535
- copyFileSync6(blob.sourcePath, temporaryPath);
18845
+ copyFileSync7(blob.sourcePath, temporaryPath);
18536
18846
  }
18537
- renameWithRetry7(temporaryPath, destinationPath);
18847
+ renameWithRetry8(temporaryPath, destinationPath);
18538
18848
  } finally {
18539
- rmSync15(stagingDir, { recursive: true, force: true });
18849
+ rmSync16(stagingDir, { recursive: true, force: true });
18540
18850
  }
18541
18851
  }
18542
18852
  async commitManifest(manifestBytes) {
@@ -18547,13 +18857,13 @@ var LocalPlanV2ArtifactPublisher = class {
18547
18857
  );
18548
18858
  }
18549
18859
  }
18550
- writeFileSync10(join39(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
18551
- renameWithRetry7(this.temporaryDir, this.destinationDir);
18860
+ writeFileSync11(join40(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
18861
+ renameWithRetry8(this.temporaryDir, this.destinationDir);
18552
18862
  this.#committed = true;
18553
18863
  }
18554
18864
  async abort() {
18555
18865
  if (!this.#committed) {
18556
- rmSync15(this.temporaryDir, { recursive: true, force: true });
18866
+ rmSync16(this.temporaryDir, { recursive: true, force: true });
18557
18867
  }
18558
18868
  }
18559
18869
  };
@@ -18572,7 +18882,7 @@ function isNonNegativeIntegerArray(value) {
18572
18882
  return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0);
18573
18883
  }
18574
18884
  function readJsonFile(path, label) {
18575
- const contents = readFileSync13(path, "utf-8");
18885
+ const contents = readFileSync14(path, "utf-8");
18576
18886
  try {
18577
18887
  const parsed = JSON.parse(contents);
18578
18888
  return parsed;
@@ -18589,10 +18899,10 @@ function assertSafeRelativePath(path) {
18589
18899
  }
18590
18900
  function listFiles(root) {
18591
18901
  const files = [];
18592
- const rootResolved = resolve11(root);
18902
+ const rootResolved = resolve12(root);
18593
18903
  function walk(dir) {
18594
18904
  for (const entry of readdirSync9(dir, { withFileTypes: true })) {
18595
- const absolutePath = join40(dir, entry.name);
18905
+ const absolutePath = join41(dir, entry.name);
18596
18906
  if (entry.isDirectory()) {
18597
18907
  walk(absolutePath);
18598
18908
  } else if (entry.isFile()) {
@@ -18619,13 +18929,13 @@ function assertValidExtractionCacheCompleteSentinel(path) {
18619
18929
  }
18620
18930
  }
18621
18931
  function validateExtractionCacheCompleteSentinels(executionPlanDir) {
18622
- const videoRoot = join40(executionPlanDir, "video-frames");
18932
+ const videoRoot = join41(executionPlanDir, "video-frames");
18623
18933
  if (!existsSync26(videoRoot)) return;
18624
18934
  for (const videoEntry of readdirSync9(videoRoot, { withFileTypes: true })) {
18625
18935
  if (!videoEntry.isDirectory()) continue;
18626
- const videoDir = join40(videoRoot, videoEntry.name);
18936
+ const videoDir = join41(videoRoot, videoEntry.name);
18627
18937
  if (readdirSync9(videoDir).includes(EXTRACTION_CACHE_COMPLETE_SENTINEL)) {
18628
- const sentinelPath = join40(videoDir, EXTRACTION_CACHE_COMPLETE_SENTINEL);
18938
+ const sentinelPath = join41(videoDir, EXTRACTION_CACHE_COMPLETE_SENTINEL);
18629
18939
  assertValidExtractionCacheCompleteSentinel(sentinelPath);
18630
18940
  }
18631
18941
  }
@@ -18635,8 +18945,8 @@ function isExtractionCacheCompleteSentinelPath(path) {
18635
18945
  return segments.length === 3 && segments[0] === "video-frames" && segments[1] !== "" && segments[2] === EXTRACTION_CACHE_COMPLETE_SENTINEL;
18636
18946
  }
18637
18947
  function resolveExtractedVideoOutputDir(planDir, videoId) {
18638
- const videoRoot = resolve11(planDir, "video-frames");
18639
- const outputDir = resolve11(videoRoot, videoId);
18948
+ const videoRoot = resolve12(planDir, "video-frames");
18949
+ const outputDir = resolve12(videoRoot, videoId);
18640
18950
  if (outputDir === videoRoot || !outputDir.startsWith(`${videoRoot}${sep4}`)) {
18641
18951
  throw new PlanV2IntegrityError(`unsafe extracted video id: ${JSON.stringify(videoId)}`);
18642
18952
  }
@@ -18672,7 +18982,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
18672
18982
  if (video.deferredRange) {
18673
18983
  const framePaths2 = /* @__PURE__ */ new Map();
18674
18984
  for (let index = 0; index < video.totalFrames; index++) {
18675
- framePaths2.set(index, join40(outputDir, String(index)));
18985
+ framePaths2.set(index, join41(outputDir, String(index)));
18676
18986
  }
18677
18987
  return { ...video, outputDir, framePaths: framePaths2, ownedByLookup: false };
18678
18988
  }
@@ -18680,7 +18990,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
18680
18990
  const framePaths = /* @__PURE__ */ new Map();
18681
18991
  for (const frameName of frameNames) {
18682
18992
  if (frameName === EXTRACTION_CACHE_COMPLETE_SENTINEL) {
18683
- assertValidExtractionCacheCompleteSentinel(join40(outputDir, frameName));
18993
+ assertValidExtractionCacheCompleteSentinel(join41(outputDir, frameName));
18684
18994
  continue;
18685
18995
  }
18686
18996
  const match = /(\d+)(?=\.[^.]+$)/.exec(frameName);
@@ -18699,7 +19009,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
18699
19009
  `duplicate extracted frame index ${frameIndex} in ${outputDir}`
18700
19010
  );
18701
19011
  }
18702
- framePaths.set(frameIndex, join40(outputDir, frameName));
19012
+ framePaths.set(frameIndex, join41(outputDir, frameName));
18703
19013
  }
18704
19014
  return {
18705
19015
  ...video,
@@ -18720,11 +19030,11 @@ function parsePlanVideosJson2(value) {
18720
19030
  }
18721
19031
  }
18722
19032
  function materializeExtractedVideoDirectories(planDir) {
18723
- const videosPath = join40(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
19033
+ const videosPath = join41(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18724
19034
  if (!existsSync26(videosPath)) return;
18725
19035
  const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
18726
19036
  for (const video of videos.extracted) {
18727
- mkdirSync16(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
19037
+ mkdirSync17(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
18728
19038
  }
18729
19039
  }
18730
19040
  function parseChunkSlices(value) {
@@ -18754,13 +19064,13 @@ function parseChunkSlices(value) {
18754
19064
  });
18755
19065
  }
18756
19066
  function buildVideoChunkDependencies(executionPlanDir, dimensions) {
18757
- const videoRoot = join40(executionPlanDir, "video-frames");
19067
+ const videoRoot = join41(executionPlanDir, "video-frames");
18758
19068
  const hasExtractedFrames = existsSync26(videoRoot) && listFiles(videoRoot).length > 0;
18759
- const videosPath = join40(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
19069
+ const videosPath = join41(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18760
19070
  if (!existsSync26(videosPath)) {
18761
19071
  return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
18762
19072
  }
18763
- const chunksPath = join40(executionPlanDir, "meta", "chunks.json");
19073
+ const chunksPath = join41(executionPlanDir, "meta", "chunks.json");
18764
19074
  const videos = readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH);
18765
19075
  const chunks = readJsonFile(chunksPath, "meta/chunks.json");
18766
19076
  const parsedVideos = parsePlanVideosJson2(videos);
@@ -18797,7 +19107,7 @@ function collectChunkVideoDependencies(input) {
18797
19107
  for (let frame = chunk.startFrame; frame < chunk.endFrame; frame++) {
18798
19108
  const globalTime = frame * input.fpsDen / input.fpsNum;
18799
19109
  for (const [videoId, payload] of input.table.getActiveFramePayloads(globalTime)) {
18800
- const path = input.deferredSources.get(videoId) ?? relative7(resolve11(input.executionPlanDir), payload.framePath).split(sep4).join("/");
19110
+ const path = input.deferredSources.get(videoId) ?? relative7(resolve12(input.executionPlanDir), payload.framePath).split(sep4).join("/");
18801
19111
  assertSafeRelativePath(path);
18802
19112
  const indexes = mutable.get(path) ?? /* @__PURE__ */ new Set();
18803
19113
  indexes.add(chunk.index);
@@ -18815,21 +19125,21 @@ function computeManifestHash(manifest) {
18815
19125
  }
18816
19126
  function writeBlob(sourcePath, destinationPath) {
18817
19127
  if (existsSync26(destinationPath)) return;
18818
- mkdirSync16(dirname16(destinationPath), { recursive: true });
18819
- const temporaryDir = mkdtempSync7(join40(dirname16(destinationPath), ".plan-v2-blob-"));
18820
- const temporaryPath = join40(temporaryDir, "blob");
19128
+ mkdirSync17(dirname17(destinationPath), { recursive: true });
19129
+ const temporaryDir = mkdtempSync7(join41(dirname17(destinationPath), ".plan-v2-blob-"));
19130
+ const temporaryPath = join41(temporaryDir, "blob");
18821
19131
  try {
18822
- copyFileSync7(sourcePath, temporaryPath);
18823
- renameWithRetry8(temporaryPath, destinationPath);
19132
+ copyFileSync8(sourcePath, temporaryPath);
19133
+ renameWithRetry9(temporaryPath, destinationPath);
18824
19134
  } finally {
18825
- rmSync16(temporaryDir, { recursive: true, force: true });
19135
+ rmSync17(temporaryDir, { recursive: true, force: true });
18826
19136
  }
18827
19137
  }
18828
19138
  function publishBlobFor(absolutePath, digests) {
18829
19139
  return {
18830
19140
  sourcePath: absolutePath,
18831
19141
  sha256: planFileSha256(absolutePath, digests),
18832
- sizeBytes: statSync11(absolutePath).size
19142
+ sizeBytes: statSync12(absolutePath).size
18833
19143
  };
18834
19144
  }
18835
19145
  async function putBlobs(publisher, blobs) {
@@ -18853,7 +19163,7 @@ async function putTasks(tasks) {
18853
19163
  if (failure) throw failure.reason;
18854
19164
  }
18855
19165
  function buildPlanV2Publication(executionPlanDir, digests) {
18856
- const executionPlanPath = join40(executionPlanDir, "plan.json");
19166
+ const executionPlanPath = join41(executionPlanDir, "plan.json");
18857
19167
  if (!existsSync26(executionPlanPath)) {
18858
19168
  throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
18859
19169
  }
@@ -18881,7 +19191,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
18881
19191
  }
18882
19192
  validateExtractionCacheCompleteSentinels(executionPlanDir);
18883
19193
  const videoDependencyPlan = buildVideoChunkDependencies(executionPlanDir, dimensions);
18884
- const encoder = readJsonFile(join40(executionPlanDir, "meta", "encoder.json"), "meta/encoder.json");
19194
+ const encoder = readJsonFile(join41(executionPlanDir, "meta", "encoder.json"), "meta/encoder.json");
18885
19195
  const files = listFiles(executionPlanDir);
18886
19196
  const chunkAudio = !isRecord3(encoder) || encoder.chunksCarryAudio !== true ? "none" : files.some((file) => chunkAudioSliceTarget(file.path) !== null) ? "sliced" : "whole";
18887
19197
  for (const file of files) {
@@ -18941,21 +19251,21 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
18941
19251
  if (publication.storedBlobs.length > 0) {
18942
19252
  throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
18943
19253
  }
18944
- mkdirSync16(dirname16(planV2Dir), { recursive: true });
18945
- const tempDir = mkdtempSync7(join40(dirname16(planV2Dir), ".plan-v2-build-"));
19254
+ mkdirSync17(dirname17(planV2Dir), { recursive: true });
19255
+ const tempDir = mkdtempSync7(join41(dirname17(planV2Dir), ".plan-v2-build-"));
18946
19256
  try {
18947
19257
  for (const blob of publication.blobs) {
18948
19258
  writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
18949
19259
  }
18950
- writeFileSync11(
18951
- join40(tempDir, "plan.json"),
19260
+ writeFileSync12(
19261
+ join41(tempDir, "plan.json"),
18952
19262
  canonicalJsonStringify(publication.manifest),
18953
19263
  "utf-8"
18954
19264
  );
18955
- renameWithRetry8(tempDir, planV2Dir);
19265
+ renameWithRetry9(tempDir, planV2Dir);
18956
19266
  return resultFromManifest(planV2Dir, publication.manifest);
18957
19267
  } catch (error) {
18958
- rmSync16(tempDir, { recursive: true, force: true });
19268
+ rmSync17(tempDir, { recursive: true, force: true });
18959
19269
  throw error;
18960
19270
  }
18961
19271
  }
@@ -18995,7 +19305,7 @@ async function publishPlanV2FromV1(executionPlanDir, publisher) {
18995
19305
  return publishPlanV2FromExecutionPlan(executionPlanDir, publisher);
18996
19306
  }
18997
19307
  async function planV2WithPublisher(projectDir, config, publisher, options = {}) {
18998
- const stagingRoot = mkdtempSync7(join40(options.stagingParentDir ?? tmpdir4(), ".execution-plan-"));
19308
+ const stagingRoot = mkdtempSync7(join41(options.stagingParentDir ?? tmpdir4(), ".execution-plan-"));
18999
19309
  const digests = /* @__PURE__ */ new Map();
19000
19310
  try {
19001
19311
  await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
@@ -19006,7 +19316,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
19006
19316
  // artifactTargets ships every compiled/ file to all chunks, so each is in the manifest.
19007
19317
  whileAudioMixes: () => putBlobs(
19008
19318
  publisher,
19009
- listFiles(join40(stagingRoot, "compiled")).map(
19319
+ listFiles(join41(stagingRoot, "compiled")).map(
19010
19320
  (file) => publishBlobFor(file.absolutePath, digests)
19011
19321
  )
19012
19322
  ),
@@ -19022,7 +19332,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
19022
19332
  }
19023
19333
  throw error;
19024
19334
  } finally {
19025
- rmSync16(stagingRoot, { recursive: true, force: true });
19335
+ rmSync17(stagingRoot, { recursive: true, force: true });
19026
19336
  }
19027
19337
  }
19028
19338
  async function planV2(projectDir, config, planV2Dir) {
@@ -19031,14 +19341,14 @@ async function planV2(projectDir, config, planV2Dir) {
19031
19341
  }
19032
19342
  const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
19033
19343
  const manifest = await planV2WithPublisher(projectDir, config, publisher, {
19034
- stagingParentDir: dirname16(planV2Dir)
19344
+ stagingParentDir: dirname17(planV2Dir)
19035
19345
  });
19036
19346
  return resultFromManifest(planV2Dir, manifest);
19037
19347
  }
19038
19348
  function resultFromManifest(planV2Dir, manifest) {
19039
19349
  return {
19040
19350
  planDir: planV2Dir,
19041
- manifestPath: join40(planV2Dir, "plan.json"),
19351
+ manifestPath: join41(planV2Dir, "plan.json"),
19042
19352
  planProtocol: PLAN_PROTOCOL_V2,
19043
19353
  planHash: manifest.planHash,
19044
19354
  sourcePlanV1Hash: getPlanV2ExecutionPlanHash(manifest),
@@ -19174,7 +19484,7 @@ function parsePlanV2Manifest(value) {
19174
19484
  return parsed;
19175
19485
  }
19176
19486
  function readPlanV2Manifest(planV2Dir) {
19177
- const manifestPath = join40(planV2Dir, "plan.json");
19487
+ const manifestPath = join41(planV2Dir, "plan.json");
19178
19488
  if (!existsSync26(manifestPath)) {
19179
19489
  throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
19180
19490
  }
@@ -19197,7 +19507,7 @@ function verifyBlob(planV2Dir, artifact) {
19197
19507
  if (!existsSync26(sourcePath)) {
19198
19508
  throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
19199
19509
  }
19200
- const stats = statSync11(sourcePath);
19510
+ const stats = statSync12(sourcePath);
19201
19511
  if (!stats.isFile() || stats.size !== artifact.sizeBytes) {
19202
19512
  throw new PlanV2IntegrityError(`artifact size mismatch for ${artifact.path}`);
19203
19513
  }
@@ -19219,17 +19529,17 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
19219
19529
  artifact,
19220
19530
  sourcePath: verifyBlob(planV2Dir, artifact)
19221
19531
  }));
19222
- mkdirSync16(dirname16(destinationDir), { recursive: true });
19223
- const tempDir = mkdtempSync7(join40(dirname16(destinationDir), ".plan-v2-materialize-"));
19532
+ mkdirSync17(dirname17(destinationDir), { recursive: true });
19533
+ const tempDir = mkdtempSync7(join41(dirname17(destinationDir), ".plan-v2-materialize-"));
19224
19534
  try {
19225
19535
  for (const { artifact, sourcePath } of verified) {
19226
- const destinationPath = join40(tempDir, ...artifact.path.split("/"));
19227
- mkdirSync16(dirname16(destinationPath), { recursive: true });
19228
- copyFileSync7(sourcePath, destinationPath);
19536
+ const destinationPath = join41(tempDir, ...artifact.path.split("/"));
19537
+ mkdirSync17(dirname17(destinationPath), { recursive: true });
19538
+ copyFileSync8(sourcePath, destinationPath);
19229
19539
  }
19230
19540
  if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
19231
- writeFileSync11(
19232
- join40(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
19541
+ writeFileSync12(
19542
+ join41(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
19233
19543
  canonicalJsonStringify({
19234
19544
  manifest,
19235
19545
  target,
@@ -19237,9 +19547,9 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
19237
19547
  }),
19238
19548
  "utf-8"
19239
19549
  );
19240
- renameWithRetry8(tempDir, destinationDir);
19550
+ renameWithRetry9(tempDir, destinationDir);
19241
19551
  } catch (error) {
19242
- rmSync16(tempDir, { recursive: true, force: true });
19552
+ rmSync17(tempDir, { recursive: true, force: true });
19243
19553
  throw error;
19244
19554
  }
19245
19555
  return {
@@ -19252,7 +19562,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
19252
19562
  // Join the artifact's OWN name, not the current constant: a plan written
19253
19563
  // before the container change carries the legacy name, and materializing it
19254
19564
  // under the new one would point at a file that was never written.
19255
- audioPath: target.role === "assembler" ? audioArtifact ? join40(destinationDir, audioArtifact.path) : null : null
19565
+ audioPath: target.role === "assembler" ? audioArtifact ? join41(destinationDir, audioArtifact.path) : null : null
19256
19566
  };
19257
19567
  }
19258
19568
  function assertRemoteSources(remoteSources, artifacts) {
@@ -19271,7 +19581,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
19271
19581
  return readValidatedPlanV2Materialization(planDir, expectedTarget)?.manifest ?? null;
19272
19582
  }
19273
19583
  function readValidatedPlanV2Materialization(planDir, expectedTarget) {
19274
- const markerPath = join40(planDir, PLAN_V2_MATERIALIZATION_MARKER);
19584
+ const markerPath = join41(planDir, PLAN_V2_MATERIALIZATION_MARKER);
19275
19585
  if (!existsSync26(markerPath)) return null;
19276
19586
  const marker = readJsonFile(markerPath, "materialization marker");
19277
19587
  if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
@@ -19289,8 +19599,8 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
19289
19599
  assertRemoteSources(remoteSources, required);
19290
19600
  for (const artifact of required) {
19291
19601
  if (Object.hasOwn(remoteSources, artifact.path)) continue;
19292
- const path = join40(planDir, ...artifact.path.split("/"));
19293
- if (!existsSync26(path) || statSync11(path).size !== artifact.sizeBytes) {
19602
+ const path = join41(planDir, ...artifact.path.split("/"));
19603
+ if (!existsSync26(path) || statSync12(path).size !== artifact.sizeBytes) {
19294
19604
  throw new PlanV2IntegrityError(
19295
19605
  `materialized artifact missing or truncated: ${artifact.path}`
19296
19606
  );
@@ -19303,23 +19613,23 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
19303
19613
  }
19304
19614
 
19305
19615
  // src/services/distributed/planV2Execution.ts
19306
- import { mkdtempSync as mkdtempSync9, rmSync as rmSync19 } from "node:fs";
19616
+ import { mkdtempSync as mkdtempSync9, rmSync as rmSync20 } from "node:fs";
19307
19617
  import { tmpdir as tmpdir5 } from "node:os";
19308
- import { join as join43 } from "node:path";
19618
+ import { join as join44 } from "node:path";
19309
19619
 
19310
19620
  // src/services/distributed/assemble.ts
19311
19621
  import {
19312
19622
  cpSync as cpSync3,
19313
19623
  existsSync as existsSync27,
19314
- mkdirSync as mkdirSync17,
19624
+ mkdirSync as mkdirSync18,
19315
19625
  mkdtempSync as mkdtempSync8,
19316
- readFileSync as readFileSync14,
19626
+ readFileSync as readFileSync15,
19317
19627
  readdirSync as readdirSync10,
19318
- rmSync as rmSync17,
19319
- statSync as statSync12,
19320
- writeFileSync as writeFileSync12
19628
+ rmSync as rmSync18,
19629
+ statSync as statSync13,
19630
+ writeFileSync as writeFileSync13
19321
19631
  } from "node:fs";
19322
- import { dirname as dirname17, join as join41 } from "node:path";
19632
+ import { dirname as dirname18, join as join42 } from "node:path";
19323
19633
  import {
19324
19634
  appendRenderProvenanceArgs as appendRenderProvenanceArgs2,
19325
19635
  describeFfmpegFailure as describeFfmpegFailure4,
@@ -19327,9 +19637,9 @@ import {
19327
19637
  MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME4,
19328
19638
  muxVideoWithAudio as muxVideoWithAudio2,
19329
19639
  resolveConfig as resolveConfig6,
19330
- runFfmpeg as runFfmpeg7,
19640
+ runFfmpeg as runFfmpeg8,
19331
19641
  appendSoftwareEncoderThreadsArg,
19332
- renameWithRetry as renameWithRetry9
19642
+ renameWithRetry as renameWithRetry10
19333
19643
  } from "@hyperframes/engine";
19334
19644
  import { fpsToFfmpegArg as fpsToFfmpegArg3 } from "@hyperframes/core";
19335
19645
  function cfrRateControlArgs(locked) {
@@ -19355,13 +19665,13 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
19355
19665
  outputPath,
19356
19666
  durationMs: Date.now() - start,
19357
19667
  framesEncoded: plan2.totalFrames,
19358
- fileSize: statSync12(outputPath).size
19668
+ fileSize: statSync13(outputPath).size
19359
19669
  };
19360
19670
  }
19361
19671
  const format = plan2.dimensions.format;
19362
19672
  const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
19363
- if (!existsSync27(dirname17(outputPath))) {
19364
- mkdirSync17(dirname17(outputPath), { recursive: true });
19673
+ if (!existsSync27(dirname18(outputPath))) {
19674
+ mkdirSync18(dirname18(outputPath), { recursive: true });
19365
19675
  }
19366
19676
  const workDir = mkdtempSync8(`${outputPath}.assemble-work-`);
19367
19677
  const fps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
@@ -19383,10 +19693,10 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
19383
19693
  };
19384
19694
  try {
19385
19695
  const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
19386
- const finalPath = join41(workDir, `output.${format}`);
19696
+ const finalPath = join42(workDir, `output.${format}`);
19387
19697
  const audio = audioPath !== null && existsSync27(audioPath) ? audioPath : null;
19388
19698
  await writeJoined(ctx, videoSources, audio, finalPath);
19389
- renameWithRetry9(finalPath, outputPath);
19699
+ renameWithRetry10(finalPath, outputPath);
19390
19700
  log.info("[assemble] stage timings", {
19391
19701
  ...stageMs,
19392
19702
  chunks: chunkPaths.length,
@@ -19394,7 +19704,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
19394
19704
  });
19395
19705
  } finally {
19396
19706
  try {
19397
- rmSync17(workDir, { recursive: true, force: true });
19707
+ rmSync18(workDir, { recursive: true, force: true });
19398
19708
  } catch (err) {
19399
19709
  log.warn("[assemble] failed to remove work dir", {
19400
19710
  workDir,
@@ -19402,7 +19712,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
19402
19712
  });
19403
19713
  }
19404
19714
  }
19405
- const fileSize = existsSync27(outputPath) ? statSync12(outputPath).size : 0;
19715
+ const fileSize = existsSync27(outputPath) ? statSync13(outputPath).size : 0;
19406
19716
  return {
19407
19717
  outputPath,
19408
19718
  durationMs: Date.now() - start,
@@ -19411,18 +19721,18 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
19411
19721
  };
19412
19722
  }
19413
19723
  function readAssemblePlan(planDir, chunkPaths) {
19414
- const planJsonPath = join41(planDir, "plan.json");
19415
- const chunksJsonPath = join41(planDir, "meta", "chunks.json");
19724
+ const planJsonPath = join42(planDir, "plan.json");
19725
+ const chunksJsonPath = join42(planDir, "meta", "chunks.json");
19416
19726
  if (!existsSync27(planJsonPath)) {
19417
19727
  throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
19418
19728
  }
19419
- const plan2 = JSON.parse(readFileSync14(planJsonPath, "utf-8"));
19729
+ const plan2 = JSON.parse(readFileSync15(planJsonPath, "utf-8"));
19420
19730
  readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
19421
19731
  validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
19422
19732
  if (!existsSync27(chunksJsonPath)) {
19423
19733
  throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
19424
19734
  }
19425
- const chunks = JSON.parse(readFileSync14(chunksJsonPath, "utf-8"));
19735
+ const chunks = JSON.parse(readFileSync15(chunksJsonPath, "utf-8"));
19426
19736
  if (chunkPaths.length !== chunks.length) {
19427
19737
  throw new Error(
19428
19738
  `[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
@@ -19460,7 +19770,7 @@ function cfrEncodeArgs(inputArgs, encoder, fpsArg) {
19460
19770
  return args;
19461
19771
  }
19462
19772
  async function reencodeCfr(ctx, chunkPaths, encoder) {
19463
- const cfrOutputPath = join41(ctx.workDir, `cfr.${ctx.format}`);
19773
+ const cfrOutputPath = join42(ctx.workDir, `cfr.${ctx.format}`);
19464
19774
  const cfrArgs = cfrEncodeArgs(videoInputArgs(ctx, chunkPaths), encoder, ctx.fpsArg);
19465
19775
  appendRenderProvenanceArgs2(cfrArgs, cfrOutputPath);
19466
19776
  cfrArgs.push("-y", cfrOutputPath);
@@ -19487,7 +19797,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
19487
19797
  );
19488
19798
  return;
19489
19799
  }
19490
- const paddedAudioPath = join41(ctx.workDir, "audio-padded.m4a");
19800
+ const paddedAudioPath = join42(ctx.workDir, "audio-padded.m4a");
19491
19801
  const padTrimResult = await ctx.timed(
19492
19802
  "audioMs",
19493
19803
  () => padOrTrimAudioToVideoFrameCount({
@@ -19527,9 +19837,9 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
19527
19837
  }
19528
19838
  }
19529
19839
  function concatDemuxerInput(chunkPaths, workDir, fpsArg) {
19530
- const listPath = join41(workDir, "concat-list.txt");
19840
+ const listPath = join42(workDir, "concat-list.txt");
19531
19841
  const body = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
19532
- writeFileSync12(listPath, `${body}
19842
+ writeFileSync13(listPath, `${body}
19533
19843
  `, "utf-8");
19534
19844
  return ["-r", fpsArg, "-f", "concat", "-safe", "0", "-i", listPath];
19535
19845
  }
@@ -19544,7 +19854,7 @@ async function probeJoinedFrameInfo(videoPaths, fps, signal) {
19544
19854
  };
19545
19855
  }
19546
19856
  async function runAssembleFfmpeg(args, step, signal, timeout) {
19547
- const result = await runFfmpeg7(args, { signal, timeout });
19857
+ const result = await runFfmpeg8(args, { signal, timeout });
19548
19858
  if (!result.success) {
19549
19859
  throw encoderFailureError(`[assemble] ffmpeg ${step} failed`, {
19550
19860
  error: describeFfmpegFailure4(result, timeout),
@@ -19565,13 +19875,13 @@ function stitchesSeparateAudio(planDir) {
19565
19875
  return lockedAudio(planDir).separateAudio === true;
19566
19876
  }
19567
19877
  function planFormat(planDir) {
19568
- const plan2 = JSON.parse(readFileSync14(join41(planDir, "plan.json"), "utf-8"));
19878
+ const plan2 = JSON.parse(readFileSync15(join42(planDir, "plan.json"), "utf-8"));
19569
19879
  return plan2.dimensions.format;
19570
19880
  }
19571
19881
  function lockedAudio(planDir) {
19572
- const encoderJsonPath = join41(planDir, "meta", "encoder.json");
19882
+ const encoderJsonPath = join42(planDir, "meta", "encoder.json");
19573
19883
  if (!existsSync27(encoderJsonPath)) return {};
19574
- return JSON.parse(readFileSync14(encoderJsonPath, "utf-8"));
19884
+ return JSON.parse(readFileSync15(encoderJsonPath, "utf-8"));
19575
19885
  }
19576
19886
  function readCfrEncoder(planDir, format) {
19577
19887
  if (format !== "mp4") {
@@ -19579,11 +19889,11 @@ function readCfrEncoder(planDir, format) {
19579
19889
  `[assemble] cfr=true is only supported for format="mp4" (got "${format}"). Stream-copy paths for webm and mov already produce exact avg_frame_rate; cfr re-encode is not needed.`
19580
19890
  );
19581
19891
  }
19582
- const encoderJsonPath = join41(planDir, "meta", "encoder.json");
19892
+ const encoderJsonPath = join42(planDir, "meta", "encoder.json");
19583
19893
  if (!existsSync27(encoderJsonPath)) {
19584
19894
  throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
19585
19895
  }
19586
- const encoderJson = JSON.parse(readFileSync14(encoderJsonPath, "utf-8"));
19896
+ const encoderJson = JSON.parse(readFileSync15(encoderJsonPath, "utf-8"));
19587
19897
  if (encoderJson.encoder === "libx265-software") {
19588
19898
  throw new Error(
19589
19899
  `[assemble] cfr=true is not yet supported with codec: "h265". The cfr re-encode pass uses libx264 and would silently transcode the h265 chunks. Either disable cfr or render with codec: "h264".`
@@ -19592,11 +19902,11 @@ function readCfrEncoder(planDir, format) {
19592
19902
  return encoderJson;
19593
19903
  }
19594
19904
  function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
19595
- if (existsSync27(outputPath)) rmSync17(outputPath, { recursive: true, force: true });
19596
- mkdirSync17(outputPath, { recursive: true });
19905
+ if (existsSync27(outputPath)) rmSync18(outputPath, { recursive: true, force: true });
19906
+ mkdirSync18(outputPath, { recursive: true });
19597
19907
  let globalIdx = 0;
19598
19908
  for (const chunkDir of chunkPaths) {
19599
- if (!statSync12(chunkDir).isDirectory()) {
19909
+ if (!statSync13(chunkDir).isDirectory()) {
19600
19910
  throw new Error(
19601
19911
  `[assemble] png-sequence chunk must be a directory: ${chunkDir} (got a file)`
19602
19912
  );
@@ -19606,8 +19916,8 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
19606
19916
  throw new Error(`[assemble] png-sequence chunk has no frames: ${chunkDir}`);
19607
19917
  }
19608
19918
  for (const frame of frames) {
19609
- const dst = join41(outputPath, formatExportFrameName(globalIdx, "png"));
19610
- cpSync3(join41(chunkDir, frame), dst);
19919
+ const dst = join42(outputPath, formatExportFrameName(globalIdx, "png"));
19920
+ cpSync3(join42(chunkDir, frame), dst);
19611
19921
  globalIdx += 1;
19612
19922
  }
19613
19923
  }
@@ -19617,13 +19927,13 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
19617
19927
  );
19618
19928
  }
19619
19929
  if (audioPath !== null && existsSync27(audioPath)) {
19620
- const sidecar = join41(outputPath, MIXED_AUDIO_FILENAME4);
19930
+ const sidecar = join42(outputPath, MIXED_AUDIO_FILENAME4);
19621
19931
  cpSync3(audioPath, sidecar);
19622
19932
  }
19623
19933
  let fileSize = 0;
19624
19934
  for (const name of readdirSync10(outputPath)) {
19625
19935
  try {
19626
- fileSize += statSync12(join41(outputPath, name)).size;
19936
+ fileSize += statSync13(join42(outputPath, name)).size;
19627
19937
  } catch {
19628
19938
  }
19629
19939
  }
@@ -19637,9 +19947,9 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
19637
19947
 
19638
19948
  // src/services/distributed/renderChunk.ts
19639
19949
  import { randomBytes } from "node:crypto";
19640
- import { existsSync as existsSync28, mkdirSync as mkdirSync18, readFileSync as readFileSync15, readdirSync as readdirSync11, rmSync as rmSync18, writeFileSync as writeFileSync13 } from "node:fs";
19950
+ import { existsSync as existsSync28, mkdirSync as mkdirSync19, readFileSync as readFileSync16, readdirSync as readdirSync11, rmSync as rmSync19, writeFileSync as writeFileSync14 } from "node:fs";
19641
19951
  import { cpus as cpus3 } from "node:os";
19642
- import { join as join42 } from "node:path";
19952
+ import { join as join43 } from "node:path";
19643
19953
  import {
19644
19954
  assertSwiftShader,
19645
19955
  BROWSER_GPU_NOT_SOFTWARE as BROWSER_GPU_NOT_SOFTWARE4,
@@ -19758,10 +20068,10 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
19758
20068
  return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
19759
20069
  }
19760
20070
  function hashChunkOutput(outputPath, kind) {
19761
- if (kind === "file") return sha256Hex(readFileSync15(outputPath));
20071
+ if (kind === "file") return sha256Hex(readFileSync16(outputPath));
19762
20072
  const entries = readdirSync11(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
19763
20073
  const lines = entries.map(
19764
- (name) => `${name}\0${sha256Hex(readFileSync15(join42(outputPath, name)))}`
20074
+ (name) => `${name}\0${sha256Hex(readFileSync16(join43(outputPath, name)))}`
19765
20075
  );
19766
20076
  return sha256Hex(lines.join("\0"));
19767
20077
  }
@@ -19778,16 +20088,16 @@ function resolveLockedVp9CpuUsed(lockedEncoder) {
19778
20088
  async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19779
20089
  const start = Date.now();
19780
20090
  const log = defaultLogger;
19781
- const planJsonPath = join42(planDir, "plan.json");
19782
- const encoderJsonPath = join42(planDir, "meta", "encoder.json");
19783
- const chunksJsonPath = join42(planDir, "meta", "chunks.json");
20091
+ const planJsonPath = join43(planDir, "plan.json");
20092
+ const encoderJsonPath = join43(planDir, "meta", "encoder.json");
20093
+ const chunksJsonPath = join43(planDir, "meta", "chunks.json");
19784
20094
  if (!existsSync28(planJsonPath)) {
19785
20095
  throw new RenderChunkValidationError(
19786
20096
  MISSING_PLAN_ARTIFACT,
19787
20097
  `[renderChunk] planDir is missing required artifact: ${planJsonPath}`
19788
20098
  );
19789
20099
  }
19790
- const plan2 = JSON.parse(readFileSync15(planJsonPath, "utf-8"));
20100
+ const plan2 = JSON.parse(readFileSync16(planJsonPath, "utf-8"));
19791
20101
  readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
19792
20102
  const planHashStarted = Date.now();
19793
20103
  const v2Materialization = readValidatedPlanV2Materialization(planDir, {
@@ -19804,13 +20114,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19804
20114
  );
19805
20115
  }
19806
20116
  }
19807
- const encoder = JSON.parse(readFileSync15(encoderJsonPath, "utf-8"));
19808
- const chunks = JSON.parse(readFileSync15(chunksJsonPath, "utf-8"));
19809
- const videosJsonPath = join42(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
20117
+ const encoder = JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
20118
+ const chunks = JSON.parse(readFileSync16(chunksJsonPath, "utf-8"));
20119
+ const videosJsonPath = join43(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
19810
20120
  let planVideos = null;
19811
20121
  if (existsSync28(videosJsonPath)) {
19812
20122
  try {
19813
- planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync15(videosJsonPath, "utf-8")));
20123
+ planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync16(videosJsonPath, "utf-8")));
19814
20124
  } catch (err) {
19815
20125
  if (err instanceof RenderChunkValidationError) throw err;
19816
20126
  throw new RenderChunkValidationError(
@@ -19838,7 +20148,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19838
20148
  }
19839
20149
  const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
19840
20150
  const framesInChunk = range.endFrame - range.startFrame;
19841
- const compiledDir = join42(planDir, "compiled");
20151
+ const compiledDir = join43(planDir, "compiled");
19842
20152
  if (!existsSync28(compiledDir)) {
19843
20153
  throw new RenderChunkValidationError(
19844
20154
  MISSING_PLAN_ARTIFACT,
@@ -19895,9 +20205,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19895
20205
  forceScreenshotExplicitlyOptedOut: !encoder.forceScreenshot
19896
20206
  };
19897
20207
  const workDir = `${outputChunkPath}.work.${process.pid}.${randomBytes(4).toString("hex")}`;
19898
- mkdirSync18(workDir, { recursive: true });
19899
- const framesDir = join42(workDir, "captured-frames");
19900
- mkdirSync18(framesDir, { recursive: true });
20208
+ mkdirSync19(workDir, { recursive: true });
20209
+ const framesDir = join43(workDir, "captured-frames");
20210
+ mkdirSync19(framesDir, { recursive: true });
19901
20211
  const videoFramesDir = `${workDir}.video-frames`;
19902
20212
  const chunkFrameNumbers = Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i);
19903
20213
  const chunkFps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
@@ -19921,7 +20231,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
19921
20231
  dimensions: plan2.dimensions,
19922
20232
  encoder,
19923
20233
  videoCount: planVideos?.videos.length ?? 0,
19924
- html: readFileSync15(join42(compiledDir, "index.html"), "utf-8"),
20234
+ html: readFileSync16(join43(compiledDir, "index.html"), "utf-8"),
19925
20235
  // The plan ships a deferred source only to chunks that show it; don't wait on the rest.
19926
20236
  skipReadinessVideoIds: deferredVideos.filter(
19927
20237
  (video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
@@ -20050,10 +20360,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
20050
20360
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
20051
20361
  const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
20052
20362
  if (isPngSequence) {
20053
- if (!existsSync28(outputChunkPath)) mkdirSync18(outputChunkPath, { recursive: true });
20363
+ if (!existsSync28(outputChunkPath)) mkdirSync19(outputChunkPath, { recursive: true });
20054
20364
  } else {
20055
- const outDir = join42(outputChunkPath, "..");
20056
- if (!existsSync28(outDir)) mkdirSync18(outDir, { recursive: true });
20365
+ const outDir = join43(outputChunkPath, "..");
20366
+ if (!existsSync28(outDir)) mkdirSync19(outDir, { recursive: true });
20057
20367
  }
20058
20368
  const encodeInput = {
20059
20369
  job,
@@ -20164,8 +20474,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
20164
20474
  return observedChunkCaptureMode(capturePerfs);
20165
20475
  },
20166
20476
  resetForScreenshotRetry: () => {
20167
- rmSync18(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
20168
- mkdirSync18(framesDir, { recursive: true });
20477
+ rmSync19(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
20478
+ mkdirSync19(framesDir, { recursive: true });
20169
20479
  capturePerfs.length = 0;
20170
20480
  captureAttempts.length = 0;
20171
20481
  job.framesRendered = 0;
@@ -20207,8 +20517,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
20207
20517
  fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
20208
20518
  timeoutMs: cfg.ffmpegProcessTimeout
20209
20519
  });
20210
- rmSync18(videoOnlyPath, { force: true });
20211
- rmSync18(audioScratchPath, { force: true });
20520
+ rmSync19(videoOnlyPath, { force: true });
20521
+ rmSync19(audioScratchPath, { force: true });
20212
20522
  }
20213
20523
  encodeStageMs = Date.now() - encodeStarted;
20214
20524
  } finally {
@@ -20255,11 +20565,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
20255
20565
  producerVersion: plan2.producerVersion,
20256
20566
  ffmpegVersion
20257
20567
  };
20258
- writeFileSync13(perfPath, `${JSON.stringify(perfPayload, null, 2)}
20568
+ writeFileSync14(perfPath, `${JSON.stringify(perfPayload, null, 2)}
20259
20569
  `, "utf-8");
20260
20570
  try {
20261
- rmSync18(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
20262
- rmSync18(videoFramesDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
20571
+ rmSync19(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
20572
+ rmSync19(videoFramesDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
20263
20573
  } catch (err) {
20264
20574
  log.warn("[renderChunk] failed to remove work dir", {
20265
20575
  workDir,
@@ -20288,18 +20598,18 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
20288
20598
 
20289
20599
  // src/services/distributed/planV2Execution.ts
20290
20600
  async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
20291
- const workRoot = mkdtempSync9(join43(tmpdir5(), "hf-plan-v2-chunk-"));
20292
- const materializedPlanDir = join43(workRoot, "plan");
20601
+ const workRoot = mkdtempSync9(join44(tmpdir5(), "hf-plan-v2-chunk-"));
20602
+ const materializedPlanDir = join44(workRoot, "plan");
20293
20603
  try {
20294
20604
  materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
20295
20605
  return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
20296
20606
  } finally {
20297
- rmSync19(workRoot, { recursive: true, force: true });
20607
+ rmSync20(workRoot, { recursive: true, force: true });
20298
20608
  }
20299
20609
  }
20300
20610
  async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
20301
- const workRoot = mkdtempSync9(join43(tmpdir5(), "hf-plan-v2-assembler-"));
20302
- const materializedPlanDir = join43(workRoot, "plan");
20611
+ const workRoot = mkdtempSync9(join44(tmpdir5(), "hf-plan-v2-assembler-"));
20612
+ const materializedPlanDir = join44(workRoot, "plan");
20303
20613
  try {
20304
20614
  const materialized = materializePlanV2Target(
20305
20615
  planV2Dir,
@@ -20314,7 +20624,7 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
20314
20624
  options
20315
20625
  );
20316
20626
  } finally {
20317
- rmSync19(workRoot, { recursive: true, force: true });
20627
+ rmSync20(workRoot, { recursive: true, force: true });
20318
20628
  }
20319
20629
  }
20320
20630
 
@@ -20322,7 +20632,7 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
20322
20632
  var READ_CHUNK_BYTES = 256 * 1024;
20323
20633
 
20324
20634
  // src/services/distributed/audioSidecar.ts
20325
- import { extractAudioMetadata as extractAudioMetadata4, runFfmpeg as runFfmpeg8 } from "@hyperframes/engine";
20635
+ import { extractAudioMetadata as extractAudioMetadata4, runFfmpeg as runFfmpeg9 } from "@hyperframes/engine";
20326
20636
  var AUDIO_SIDECAR_MIN_BYTES = 32 * 1024 * 1024;
20327
20637
 
20328
20638
  // src/services/distributed/probeFacts.ts
@@ -20386,6 +20696,7 @@ export {
20386
20696
  localizeRemoteImageSources,
20387
20697
  localizeRemoteMediaSources,
20388
20698
  materializePlanV2Target,
20699
+ mediaProfileMatchesElementType,
20389
20700
  normalizeErrorMessage,
20390
20701
  parseRenderRequest,
20391
20702
  plan,
@@ -20393,6 +20704,7 @@ export {
20393
20704
  planV2WithPublisher,
20394
20705
  prepareAnimatedGifInputs,
20395
20706
  prepareCaptureSessionForReuse4 as prepareCaptureSessionForReuse,
20707
+ prepareHeifImageInputs,
20396
20708
  prepareHyperframeLintBody,
20397
20709
  publishPlanV2FromExecutionPlan,
20398
20710
  publishPlanV2FromV1,