hyperframes 0.8.27 → 0.8.29

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.
@@ -3433,10 +3433,10 @@ function compareDocumentPosition(nodeA, nodeB) {
3433
3433
  function uniqueSort(nodes) {
3434
3434
  nodes = nodes.filter((node, i, arr) => !arr.includes(node, i + 1));
3435
3435
  nodes.sort((a, b) => {
3436
- const relative7 = compareDocumentPosition(a, b);
3437
- if (relative7 & DocumentPosition.PRECEDING) {
3436
+ const relative8 = compareDocumentPosition(a, b);
3437
+ if (relative8 & DocumentPosition.PRECEDING) {
3438
3438
  return -1;
3439
- } else if (relative7 & DocumentPosition.FOLLOWING) {
3439
+ } else if (relative8 & DocumentPosition.FOLLOWING) {
3440
3440
  return 1;
3441
3441
  }
3442
3442
  return 0;
@@ -5771,10 +5771,10 @@ function parse2(formula) {
5771
5771
  if (idx < formula.length && formula.charAt(idx) === "n") {
5772
5772
  idx++;
5773
5773
  a = sign * (number !== null && number !== void 0 ? number : 1);
5774
- skipWhitespace();
5774
+ skipWhitespace2();
5775
5775
  if (idx < formula.length) {
5776
5776
  sign = readSign();
5777
- skipWhitespace();
5777
+ skipWhitespace2();
5778
5778
  number = readNumber();
5779
5779
  } else {
5780
5780
  sign = number = 0;
@@ -5803,7 +5803,7 @@ function parse2(formula) {
5803
5803
  }
5804
5804
  return idx === start ? null : value;
5805
5805
  }
5806
- function skipWhitespace() {
5806
+ function skipWhitespace2() {
5807
5807
  while (idx < formula.length && whitespace.has(formula.charCodeAt(idx))) {
5808
5808
  idx++;
5809
5809
  }
@@ -17022,7 +17022,7 @@ var require_util = __commonJS({
17022
17022
  return path;
17023
17023
  }
17024
17024
  exports.normalize = normalize;
17025
- function join56(aRoot, aPath) {
17025
+ function join57(aRoot, aPath) {
17026
17026
  if (aRoot === "") {
17027
17027
  aRoot = ".";
17028
17028
  }
@@ -17054,11 +17054,11 @@ var require_util = __commonJS({
17054
17054
  }
17055
17055
  return joined;
17056
17056
  }
17057
- exports.join = join56;
17057
+ exports.join = join57;
17058
17058
  exports.isAbsolute = function(aPath) {
17059
17059
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
17060
17060
  };
17061
- function relative7(aRoot, aPath) {
17061
+ function relative8(aRoot, aPath) {
17062
17062
  if (aRoot === "") {
17063
17063
  aRoot = ".";
17064
17064
  }
@@ -17077,7 +17077,7 @@ var require_util = __commonJS({
17077
17077
  }
17078
17078
  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
17079
17079
  }
17080
- exports.relative = relative7;
17080
+ exports.relative = relative8;
17081
17081
  var supportsNullProto = (function() {
17082
17082
  var obj = /* @__PURE__ */ Object.create(null);
17083
17083
  return !("__proto__" in obj);
@@ -17227,7 +17227,7 @@ var require_util = __commonJS({
17227
17227
  parsed.path = parsed.path.substring(0, index + 1);
17228
17228
  }
17229
17229
  }
17230
- sourceURL = join56(urlGenerate(parsed), sourceURL);
17230
+ sourceURL = join57(urlGenerate(parsed), sourceURL);
17231
17231
  }
17232
17232
  return normalize(sourceURL);
17233
17233
  }
@@ -50174,13 +50174,6 @@ function checkOutputResolutionCompatibility(input) {
50174
50174
  message: `outputResolution cannot be combined with hdrMode='force-hdr'. HDR rendering composites at composition dimensions and does not yet support supersampling. Pick one or render in two passes.`
50175
50175
  };
50176
50176
  }
50177
- if (input.alphaRequested) {
50178
- return {
50179
- ok: false,
50180
- kind: "alpha-incompatible",
50181
- message: `outputResolution cannot be combined with alpha output (--format webm|mov|png-sequence). The alpha screenshot path does not yet apply deviceScaleFactor and would silently produce composition-resolution frames. Render alpha at composition resolution and upscale separately, or use --format mp4.`
50182
- };
50183
- }
50184
50177
  const target = CANVAS_DIMENSIONS[outputResolution];
50185
50178
  if (target.width * compositionHeight !== target.height * compositionWidth) {
50186
50179
  return buildAspectMismatch(compositionWidth, compositionHeight, outputResolution, target);
@@ -52567,7 +52560,7 @@ var init_telemetryRedaction = __esm({
52567
52560
 
52568
52561
  // ../core/dist/safePath.js
52569
52562
  import { resolve as resolve3, sep, join as join2, dirname as dirname3, basename } from "path";
52570
- import { realpathSync } from "fs";
52563
+ import { lstatSync, realpathSync } from "fs";
52571
52564
  var init_safePath = __esm({
52572
52565
  "../core/dist/safePath.js"() {
52573
52566
  "use strict";
@@ -57686,6 +57679,7 @@ var init_captureFailure = __esm({
57686
57679
  PROTOCOL_TIMEOUT_PATTERNS = [
57687
57680
  /Runtime\.callFunctionOn timed out/i,
57688
57681
  /Runtime\.evaluate timed out/i,
57682
+ /Page\.captureScreenshot timed out/i,
57689
57683
  /HeadlessExperimental\.beginFrame timed out/i,
57690
57684
  /drawElement worker encode timed out \(frame \d+\)/i,
57691
57685
  /Protocol error[\s\S]*tim(?:ed|e) out/i,
@@ -58652,6 +58646,18 @@ function recordScriptLoadFailure(session, url) {
58652
58646
  session.scriptLoadFailures.push(url);
58653
58647
  }
58654
58648
  }
58649
+ function classifyConsoleScriptFailure(type, text) {
58650
+ if (type !== "error") return null;
58651
+ if (text.startsWith("[HyperFrames] composition script error:")) {
58652
+ const detail = text.slice("[HyperFrames] composition script error:".length).trim();
58653
+ const compId = detail.split(" ")[0] || "unknown";
58654
+ return `runtime-error:${compId}`;
58655
+ }
58656
+ if (/failed to find a valid digest in the ['"]integrity['"] attribute/i.test(text) && /resource has been blocked/i.test(text)) {
58657
+ return "runtime-error:subresource-integrity";
58658
+ }
58659
+ return null;
58660
+ }
58655
58661
  async function initializeSession(session) {
58656
58662
  const { page, serverUrl } = session;
58657
58663
  page.on("console", (msg) => {
@@ -58661,11 +58667,8 @@ async function initializeSession(session) {
58661
58667
  const diagnostic = formatConsoleDiagnostic(type, text, locationUrl);
58662
58668
  if (!diagnostic.suppressHostLog) console.log(diagnostic.text);
58663
58669
  appendBrowserDiagnostic(session, diagnostic.text);
58664
- if (type === "error" && text.startsWith("[HyperFrames] composition script error:")) {
58665
- const detail = text.slice("[HyperFrames] composition script error:".length).trim();
58666
- const compId = detail.split(" ")[0] || "unknown";
58667
- recordScriptLoadFailure(session, `runtime-error:${compId}`);
58668
- }
58670
+ const scriptFailure = classifyConsoleScriptFailure(type, text);
58671
+ if (scriptFailure) recordScriptLoadFailure(session, scriptFailure);
58669
58672
  });
58670
58673
  page.on("pageerror", (err) => {
58671
58674
  const message = err instanceof Error ? err.message : String(err);
@@ -62143,7 +62146,7 @@ import {
62143
62146
  linkSync,
62144
62147
  mkdtempSync,
62145
62148
  mkdirSync as mkdirSync4,
62146
- lstatSync,
62149
+ lstatSync as lstatSync2,
62147
62150
  openSync,
62148
62151
  readdirSync as readdirSync3,
62149
62152
  rmdirSync,
@@ -62257,9 +62260,9 @@ function sameCacheLockStatGeneration(left, right) {
62257
62260
  }
62258
62261
  function observeCachePathLock(lockPath) {
62259
62262
  for (let pass = 0; pass < 3; pass += 1) {
62260
- const before2 = lstatSync(lockPath);
62263
+ const before2 = lstatSync2(lockPath);
62261
62264
  const owner = readdirSync3(lockPath).find((name) => name.startsWith(CACHE_LOCK_OWNER_PREFIX));
62262
- const after2 = lstatSync(lockPath);
62265
+ const after2 = lstatSync2(lockPath);
62263
62266
  if (sameCacheLockStatGeneration(before2, after2)) return { stats: after2, owner };
62264
62267
  }
62265
62268
  throw new UrlDownloadError("filesystem", true, "Cache lock changed repeatedly during inspection");
@@ -62770,7 +62773,7 @@ function syncAndPublishPartial(partialPath, localPath) {
62770
62773
  if (code !== "EEXIST") throw error;
62771
62774
  let winner;
62772
62775
  try {
62773
- winner = lstatSync(localPath);
62776
+ winner = lstatSync2(localPath);
62774
62777
  } catch (inspectionError) {
62775
62778
  if (inspectionError.code !== "ENOENT") throw inspectionError;
62776
62779
  throw new UrlDownloadError(
@@ -62966,7 +62969,7 @@ async function reuseOrInvalidateCachedFile(url, localPath, timeoutMs, signal, op
62966
62969
  for (let pass = 0; pass < 3; pass += 1) {
62967
62970
  let before2;
62968
62971
  try {
62969
- before2 = lstatSync(localPath);
62972
+ before2 = lstatSync2(localPath);
62970
62973
  } catch (error) {
62971
62974
  if (error.code === "ENOENT") continue;
62972
62975
  throw error;
@@ -62984,7 +62987,7 @@ async function reuseOrInvalidateCachedFile(url, localPath, timeoutMs, signal, op
62984
62987
  }
62985
62988
  let after2;
62986
62989
  try {
62987
- after2 = lstatSync(localPath);
62990
+ after2 = lstatSync2(localPath);
62988
62991
  } catch (error) {
62989
62992
  if (error.code === "ENOENT") continue;
62990
62993
  throw error;
@@ -63440,7 +63443,7 @@ function scopeSelector(selector, scope, compositionId, authoredRootId, compoundA
63440
63443
  if (/^(html|body|:root)$/i.test(trimmed)) {
63441
63444
  return scopeRootSelectors ? compositionBoxSelector(scope) : selector;
63442
63445
  }
63443
- const compositionIdPattern = new RegExp(`\\[\\s*data-composition-id\\s*=\\s*(["'])${escapeRegExp(compositionId)}\\1\\s*\\]`, "g");
63446
+ const compositionIdPattern = new RegExp(`\\[\\s*data-composition-id\\s*[\\^\\*\\$]?=\\s*(["'])${escapeRegExp(compositionId)}\\1\\s*\\]`, "g");
63444
63447
  if (compositionIdPattern.test(trimmed)) {
63445
63448
  const isRootBoxSelector = trimmed.replace(compositionIdPattern, "").trim() === "";
63446
63449
  if (isRootBoxSelector) {
@@ -69892,6 +69895,59 @@ function hasAttrName(tagSource, attr) {
69892
69895
  const attrs = tagSource.replace(/^<\s*[a-z][\w:-]*/i, "");
69893
69896
  return new RegExp(`(?:^|\\s)${escaped}(?:\\s*=|\\s|/?>)`, "i").test(attrs);
69894
69897
  }
69898
+ function srcKind(src) {
69899
+ const stripped = src.trim();
69900
+ if (!stripped) return null;
69901
+ const lower = stripped.toLowerCase();
69902
+ if (lower.startsWith("data:")) {
69903
+ const mime = /^data:([^;,]+)/i.exec(stripped)?.[1]?.toLowerCase();
69904
+ if (!mime) return null;
69905
+ if (mime.startsWith("image/")) return "image";
69906
+ if (mime.startsWith("video/")) return "video";
69907
+ return null;
69908
+ }
69909
+ if (lower.startsWith("blob:")) return null;
69910
+ let pathname = stripped;
69911
+ try {
69912
+ if (/^https?:/i.test(stripped)) {
69913
+ pathname = decodeURIComponent(new URL(stripped).pathname);
69914
+ } else {
69915
+ pathname = stripped.split("?")[0]?.split("#")[0] ?? stripped;
69916
+ }
69917
+ } catch {
69918
+ pathname = stripped.split("?")[0]?.split("#")[0] ?? stripped;
69919
+ }
69920
+ const base2 = pathname.split("/").pop() ?? "";
69921
+ const dot = base2.lastIndexOf(".");
69922
+ if (dot < 0) return null;
69923
+ const ext = base2.slice(dot + 1).toLowerCase();
69924
+ if (IMAGE_SRC_EXT.has(ext)) return "image";
69925
+ if (VIDEO_SRC_EXT.has(ext)) return "video";
69926
+ return null;
69927
+ }
69928
+ function findMediaSrcKindMismatchFindings(ctx) {
69929
+ const findings = [];
69930
+ for (const tag of ctx.tags) {
69931
+ if (tag.name !== "video" && tag.name !== "img") continue;
69932
+ const src = readAttr(tag.raw, "src");
69933
+ if (!src) continue;
69934
+ const kind = srcKind(src);
69935
+ if (kind === null) continue;
69936
+ if (tag.name === "video" && kind !== "image") continue;
69937
+ if (tag.name === "img" && kind !== "video") continue;
69938
+ const elementId = readAttr(tag.raw, "id") || void 0;
69939
+ const expected = tag.name === "video" ? "video" : "image";
69940
+ findings.push({
69941
+ code: "media_src_kind_mismatch",
69942
+ severity: "error",
69943
+ message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> src is a ${kind}, not a ${expected}. The producer fail-closes when the tag and file kind disagree.`,
69944
+ elementId,
69945
+ fixHint: tag.name === "video" ? "Use <img> for a still, or point <video> at a video URL (mp4/webm/mov/\u2026)." : "Use <video> for a video URL, or point <img> at a still (png/jpg/webp/\u2026).",
69946
+ snippet: truncateSnippet(tag.raw)
69947
+ });
69948
+ }
69949
+ return findings;
69950
+ }
69895
69951
  function mediaHasResolvableSrc(tag, tags) {
69896
69952
  if (readAttr(tag.raw, "src")) return true;
69897
69953
  const end = tag.closeIndex ?? tag.endIndex;
@@ -71276,7 +71332,7 @@ async function lintHyperframeHtml(html, options = {}) {
71276
71332
  timings: { totalMs: performance.now() - startedAt, ...timings }
71277
71333
  };
71278
71334
  }
71279
- var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, mediaRules, AUDIO_GROUP_TIMING_ATTRS, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, compositionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
71335
+ var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, IMAGE_SRC_EXT, VIDEO_SRC_EXT, mediaRules, AUDIO_GROUP_TIMING_ATTRS, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, compositionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
71280
71336
  var init_dist3 = __esm({
71281
71337
  "../lint/dist/index.js"() {
71282
71338
  "use strict";
@@ -71549,7 +71605,7 @@ var init_dist3 = __esm({
71549
71605
  {
71550
71606
  pattern: /crypto\.getRandomValues\s*\(/,
71551
71607
  label: "crypto.getRandomValues()",
71552
- hint: "Remove time-dependent code. Use a seeded PRNG for deterministic renders."
71608
+ hint: "Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames."
71553
71609
  },
71554
71610
  {
71555
71611
  pattern: /gsap\.utils\.random\s*\(/,
@@ -71585,6 +71641,31 @@ var init_dist3 = __esm({
71585
71641
  return findings;
71586
71642
  }
71587
71643
  ];
71644
+ IMAGE_SRC_EXT = /* @__PURE__ */ new Set([
71645
+ "jpg",
71646
+ "jpeg",
71647
+ "png",
71648
+ "gif",
71649
+ "bmp",
71650
+ "webp",
71651
+ "svg",
71652
+ "heic",
71653
+ "heif",
71654
+ "tiff",
71655
+ "ico"
71656
+ ]);
71657
+ VIDEO_SRC_EXT = /* @__PURE__ */ new Set([
71658
+ "mp4",
71659
+ "mov",
71660
+ "avi",
71661
+ "webm",
71662
+ "mkv",
71663
+ "flv",
71664
+ "wmv",
71665
+ "m4v",
71666
+ "mpg",
71667
+ "mpeg"
71668
+ ]);
71588
71669
  mediaRules = [
71589
71670
  // duplicate_media_id + duplicate_media_discovery_risk
71590
71671
  ({ tags }) => {
@@ -71788,6 +71869,8 @@ var init_dist3 = __esm({
71788
71869
  }
71789
71870
  return findings;
71790
71871
  },
71872
+ // media_src_kind_mismatch
71873
+ findMediaSrcKindMismatchFindings,
71791
71874
  // placeholder_media_url
71792
71875
  ({ tags }) => {
71793
71876
  const findings = [];
@@ -74735,7 +74818,7 @@ var init_extractedFrameIndex = __esm({
74735
74818
  import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
74736
74819
  import {
74737
74820
  existsSync as existsSync9,
74738
- lstatSync as lstatSync2,
74821
+ lstatSync as lstatSync3,
74739
74822
  mkdirSync as mkdirSync5,
74740
74823
  readdirSync as readdirSync5,
74741
74824
  renameSync,
@@ -74839,7 +74922,7 @@ function isPartialChild(name) {
74839
74922
  }
74840
74923
  function directorySizeBytes(path) {
74841
74924
  try {
74842
- const stat = lstatSync2(path);
74925
+ const stat = lstatSync3(path);
74843
74926
  if (!stat.isDirectory()) return stat.size;
74844
74927
  } catch {
74845
74928
  return 0;
@@ -74854,7 +74937,7 @@ function directorySizeBytes(path) {
74854
74937
  for (const child of children) {
74855
74938
  const childPath = join10(path, child);
74856
74939
  try {
74857
- const stat = lstatSync2(childPath);
74940
+ const stat = lstatSync3(childPath);
74858
74941
  if (stat.isDirectory()) {
74859
74942
  total += directorySizeBytes(childPath);
74860
74943
  } else {
@@ -79127,12 +79210,17 @@ var init_fileServer = __esm({
79127
79210
  ".gif": "image/gif",
79128
79211
  ".svg": "image/svg+xml",
79129
79212
  ".webp": "image/webp",
79213
+ ".avif": "image/avif",
79214
+ ".ico": "image/vnd.microsoft.icon",
79130
79215
  ".mp4": "video/mp4",
79216
+ ".mov": "video/quicktime",
79131
79217
  ".webm": "video/webm",
79132
79218
  ".mp3": "audio/mpeg",
79133
79219
  ".wav": "audio/wav",
79134
79220
  ".ogg": "audio/ogg",
79135
79221
  ".aac": "audio/aac",
79222
+ ".flac": "audio/flac",
79223
+ ".m4a": "audio/mp4",
79136
79224
  ".woff": "font/woff",
79137
79225
  ".woff2": "font/woff2",
79138
79226
  ".ttf": "font/ttf",
@@ -84151,7 +84239,15 @@ __export(fontCompression_exports, {
84151
84239
  fontToDataUri: () => fontToDataUri
84152
84240
  });
84153
84241
  import { createHash as createHash6 } from "crypto";
84154
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync8, writeFileSync as writeFileSync8 } from "fs";
84242
+ import {
84243
+ existsSync as existsSync21,
84244
+ mkdirSync as mkdirSync10,
84245
+ mkdtempSync as mkdtempSync5,
84246
+ readFileSync as readFileSync7,
84247
+ renameSync as renameSync4,
84248
+ rmSync as rmSync8,
84249
+ writeFileSync as writeFileSync8
84250
+ } from "fs";
84155
84251
  import { homedir as homedir4, tmpdir as tmpdir4 } from "os";
84156
84252
  import { dirname as dirname11, join as join24 } from "path";
84157
84253
  async function compressToWoff2(input) {
@@ -84178,15 +84274,17 @@ function readCachedCompression(path) {
84178
84274
  }
84179
84275
  }
84180
84276
  function cacheCompression(path, compressed) {
84181
- const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
84277
+ let tempDir;
84182
84278
  try {
84183
84279
  mkdirSync10(dirname11(path), { recursive: true });
84280
+ tempDir = mkdtempSync5(join24(dirname11(path), ".compression-"));
84281
+ const tmpPath = join24(tempDir, "font.woff2");
84184
84282
  writeFileSync8(tmpPath, compressed, { flag: "wx", mode: 420 });
84185
84283
  renameSync4(tmpPath, path);
84186
84284
  } catch {
84187
84285
  } finally {
84188
84286
  try {
84189
- rmSync8(tmpPath, { force: true });
84287
+ if (tempDir) rmSync8(tempDir, { recursive: true, force: true });
84190
84288
  } catch {
84191
84289
  }
84192
84290
  }
@@ -84236,7 +84334,7 @@ init_src();
84236
84334
  import {
84237
84335
  existsSync as existsSync30,
84238
84336
  mkdirSync as mkdirSync17,
84239
- mkdtempSync as mkdtempSync7,
84337
+ mkdtempSync as mkdtempSync9,
84240
84338
  readFileSync as readFileSync12,
84241
84339
  readdirSync as readdirSync11,
84242
84340
  rmSync as rmSync13,
@@ -84246,7 +84344,7 @@ import {
84246
84344
  appendFileSync
84247
84345
  } from "fs";
84248
84346
  import { tmpdir as tmpdir7 } from "os";
84249
- import { join as join41, dirname as dirname16, resolve as resolve13 } from "path";
84347
+ import { join as join42, dirname as dirname17, resolve as resolve13 } from "path";
84250
84348
  import { totalmem as totalmem3 } from "os";
84251
84349
  import { randomUUID as randomUUID3 } from "crypto";
84252
84350
  import { fileURLToPath as fileURLToPath5 } from "url";
@@ -84400,12 +84498,17 @@ var MIME_TYPES2 = {
84400
84498
  ".gif": "image/gif",
84401
84499
  ".svg": "image/svg+xml",
84402
84500
  ".webp": "image/webp",
84501
+ ".avif": "image/avif",
84502
+ ".ico": "image/vnd.microsoft.icon",
84403
84503
  ".mp4": "video/mp4",
84504
+ ".mov": "video/quicktime",
84404
84505
  ".webm": "video/webm",
84405
84506
  ".mp3": "audio/mpeg",
84406
84507
  ".wav": "audio/wav",
84407
84508
  ".ogg": "audio/ogg",
84408
84509
  ".aac": "audio/aac",
84510
+ ".flac": "audio/flac",
84511
+ ".m4a": "audio/mp4",
84409
84512
  ".woff": "font/woff",
84410
84513
  ".woff2": "font/woff2",
84411
84514
  ".ttf": "font/ttf",
@@ -86683,7 +86786,9 @@ function isToleratedShortClipBoundaryMiss(report, threshold) {
86683
86786
  }
86684
86787
  function expectedFramesForVideo(video, entry, fps) {
86685
86788
  const rounding = entry && !entry.metadata.isVFR ? "nearest" : "ceil";
86686
- const slotFrames = expectedFramesForClip(video.start, video.end, fps, rounding);
86789
+ const playbackRate = normalizePlaybackRate(video.playbackRate ?? 1);
86790
+ const slotSourceDuration = Math.max(0, video.end - video.start) * playbackRate;
86791
+ const slotFrames = expectedFramesForClip(0, slotSourceDuration, fps, rounding);
86687
86792
  if (!entry) return slotFrames;
86688
86793
  const sourceDuration = resolvePlayableVideoDuration(entry.metadata) - video.mediaStart;
86689
86794
  if (!Number.isFinite(sourceDuration) || sourceDuration <= 0) return slotFrames;
@@ -86743,7 +86848,7 @@ init_compiler();
86743
86848
  init_subCompositionValidity();
86744
86849
  init_assetResolution();
86745
86850
  import { createReadStream as createReadStream4, existsSync as existsSync25, mkdirSync as mkdirSync13, readFileSync as readFileSync10 } from "fs";
86746
- import { join as join27, dirname as dirname13, resolve as resolve12, basename as basename6 } from "path";
86851
+ import { join as join27, dirname as dirname13, resolve as resolve12, basename as basename6, relative as relative2 } from "path";
86747
86852
 
86748
86853
  // ../producer/src/services/renderMediaCollector.ts
86749
86854
  init_esm10();
@@ -86829,7 +86934,7 @@ init_src();
86829
86934
 
86830
86935
  // ../producer/src/services/deterministicFonts.ts
86831
86936
  import { createHash as createHash7 } from "crypto";
86832
- import { existsSync as existsSync22, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
86937
+ import { existsSync as existsSync22, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
86833
86938
  import { homedir as homedir5, tmpdir as tmpdir5 } from "os";
86834
86939
  import { join as join25 } from "path";
86835
86940
 
@@ -86838,7 +86943,7 @@ init_dist();
86838
86943
 
86839
86944
  // ../core/dist/fonts/systemFontLocator.js
86840
86945
  import { execFileSync as execFileSync2 } from "child_process";
86841
- import { existsSync as existsSync20, lstatSync as lstatSync3, readdirSync as readdirSync9, realpathSync as realpathSync3 } from "fs";
86946
+ import { existsSync as existsSync20, lstatSync as lstatSync4, readdirSync as readdirSync9, realpathSync as realpathSync3 } from "fs";
86842
86947
  import { homedir as homedir3, platform as platform2 } from "os";
86843
86948
  import { join as join23, resolve as resolve10 } from "path";
86844
86949
  var SYSTEM_FONT_SIZE_LIMIT = 5 * 1024 * 1024;
@@ -86892,9 +86997,9 @@ function isPathBounded(filePath) {
86892
86997
  }
86893
86998
  function isRegularFile(filePath) {
86894
86999
  try {
86895
- const lst = lstatSync3(filePath);
87000
+ const lst = lstatSync4(filePath);
86896
87001
  if (lst.isSymbolicLink())
86897
- return isPathBounded(filePath) && lstatSync3(realpathSync3(filePath)).isFile();
87002
+ return isPathBounded(filePath) && lstatSync4(realpathSync3(filePath)).isFile();
86898
87003
  return lst.isFile();
86899
87004
  } catch {
86900
87005
  return false;
@@ -87441,6 +87546,11 @@ function extractRequestedFontFamilies(html) {
87441
87546
  function fontFormatHint(src) {
87442
87547
  return src.startsWith("data:font/collection;") ? "collection" : "woff2";
87443
87548
  }
87549
+ var BUNDLED_SUBSET_UNICODE_RANGE = "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD";
87550
+ function isBundledSubsetRange(unicodeRange) {
87551
+ const normalize = (range) => range.toLowerCase().replace(/\s+/g, "");
87552
+ return unicodeRange !== void 0 && normalize(unicodeRange) === normalize(BUNDLED_SUBSET_UNICODE_RANGE);
87553
+ }
87444
87554
  function buildFontFaceRule(familyName, src, weight, style, unicodeRange) {
87445
87555
  return [
87446
87556
  "@font-face {",
@@ -87512,16 +87622,25 @@ async function buildFontFaceCss(requestedFamilies, options, fontText) {
87512
87622
  const canonical = CANONICAL_FONTS[canonicalKey];
87513
87623
  if (!canonical) continue;
87514
87624
  const coveredWeights = /* @__PURE__ */ new Set();
87625
+ const bundledRules = [];
87515
87626
  for (const face of canonical.faces) {
87516
87627
  const style = face.style || "normal";
87517
87628
  const src = fontDataUri(canonical.packageName, face.weight, style);
87518
- rules.push(buildFontFaceRule(originalCaseFamily, src, face.weight, style));
87629
+ bundledRules.push(
87630
+ buildFontFaceRule(
87631
+ originalCaseFamily,
87632
+ src,
87633
+ face.weight,
87634
+ style,
87635
+ BUNDLED_SUBSET_UNICODE_RANGE
87636
+ )
87637
+ );
87519
87638
  coveredWeights.add(coverageKey(face.weight, style));
87520
87639
  }
87521
87640
  const canonicalFamily = resolveAliasDisplayName(normalizedFamily);
87522
87641
  const googleFaces2 = canonicalFamily ? await fetchGoogleFont(canonicalFamily, options, fontText) : [];
87523
87642
  const supplementary = googleFaces2.filter(
87524
- (face) => !coveredWeights.has(coverageKey(face.weight, face.style))
87643
+ (face) => !coveredWeights.has(coverageKey(face.weight, face.style)) || !isBundledSubsetRange(face.unicodeRange)
87525
87644
  );
87526
87645
  const runs = groupFacesBySource(supplementary).flatMap(
87527
87646
  (group) => partitionWeightRuns(group, coveredWeights)
@@ -87542,6 +87661,7 @@ async function buildFontFaceCss(requestedFamilies, options, fontText) {
87542
87661
  )
87543
87662
  );
87544
87663
  }
87664
+ rules.push(...bundledRules);
87545
87665
  continue;
87546
87666
  }
87547
87667
  const googleFaces = await fetchGoogleFont(originalCaseFamily, options, fontText);
@@ -87610,7 +87730,7 @@ function resolveFontCacheRoot() {
87610
87730
  return process.env.HYPERFRAMES_FONT_CACHE_DIR;
87611
87731
  }
87612
87732
  if (process.env.AWS_LAMBDA_FUNCTION_NAME) {
87613
- lambdaFontCacheRoot ??= mkdtempSync5(join25(tmpdir5(), "hyperframes-fonts-"));
87733
+ lambdaFontCacheRoot ??= mkdtempSync6(join25(tmpdir5(), "hyperframes-fonts-"));
87614
87734
  return lambdaFontCacheRoot;
87615
87735
  }
87616
87736
  return join25(homedir5(), ".cache", "hyperframes", "fonts");
@@ -87627,7 +87747,7 @@ function fontCacheDir(slug) {
87627
87747
  mkdirSync11(dir, { recursive: true });
87628
87748
  } catch {
87629
87749
  const firstFallback = ephemeralFontCacheRoot === void 0;
87630
- ephemeralFontCacheRoot ??= mkdtempSync5(join25(tmpdir5(), "hyperframes-fonts-"));
87750
+ ephemeralFontCacheRoot ??= mkdtempSync6(join25(tmpdir5(), "hyperframes-fonts-"));
87631
87751
  const fallback = join25(ephemeralFontCacheRoot, slug);
87632
87752
  mkdirSync11(fallback, { recursive: true });
87633
87753
  if (firstFallback) {
@@ -87853,17 +87973,108 @@ async function fetchGoogleFont(familyName, options, fontText) {
87853
87973
  return faces;
87854
87974
  }
87855
87975
  var GOOGLE_FONTS_TEXT_MAX_ENCODED_LENGTH = 1700;
87976
+ var SMALL_TO_FULL_KANA = /* @__PURE__ */ new Map([
87977
+ ["\u3041", "\u3042"],
87978
+ ["\u3043", "\u3044"],
87979
+ ["\u3045", "\u3046"],
87980
+ ["\u3047", "\u3048"],
87981
+ ["\u3049", "\u304A"],
87982
+ ["\u3063", "\u3064"],
87983
+ ["\u3083", "\u3084"],
87984
+ ["\u3085", "\u3086"],
87985
+ ["\u3087", "\u3088"],
87986
+ ["\u308E", "\u308F"],
87987
+ ["\u30A1", "\u30A2"],
87988
+ ["\u30A3", "\u30A4"],
87989
+ ["\u30A5", "\u30A6"],
87990
+ ["\u30A7", "\u30A8"],
87991
+ ["\u30A9", "\u30AA"],
87992
+ ["\u30C3", "\u30C4"],
87993
+ ["\u30E3", "\u30E4"],
87994
+ ["\u30E5", "\u30E6"],
87995
+ ["\u30E7", "\u30E8"],
87996
+ ["\u30EE", "\u30EF"],
87997
+ ["\u30F5", "\u30AB"],
87998
+ ["\u30F6", "\u30B1"]
87999
+ ]);
88000
+ function collectLangAttributes(document2) {
88001
+ const locales = /* @__PURE__ */ new Set();
88002
+ for (const element of document2.querySelectorAll("[lang]")) {
88003
+ const lang = element.getAttribute("lang");
88004
+ if (!lang) continue;
88005
+ const primary = lang.split("-")[0].toLowerCase();
88006
+ try {
88007
+ Intl.getCanonicalLocales(primary);
88008
+ locales.add(primary);
88009
+ } catch {
88010
+ }
88011
+ }
88012
+ return locales;
88013
+ }
88014
+ function addCaseClosure(out, character, locales) {
88015
+ out.add(character);
88016
+ for (const variant of `${character.toUpperCase()}${character.toLowerCase()}`) {
88017
+ out.add(variant);
88018
+ }
88019
+ for (const locale of locales) {
88020
+ for (const variant of `${character.toLocaleUpperCase(locale)}${character.toLocaleLowerCase(locale)}`) {
88021
+ out.add(variant);
88022
+ }
88023
+ }
88024
+ }
88025
+ function addFullwidthVariants(chars) {
88026
+ for (const character of [...chars]) {
88027
+ const code = character.codePointAt(0) ?? 0;
88028
+ if (code >= 33 && code <= 126) {
88029
+ chars.add(String.fromCodePoint(code + 65248));
88030
+ }
88031
+ }
88032
+ }
88033
+ function addFullSizeKanaVariants(chars) {
88034
+ for (const character of [...chars]) {
88035
+ const full = SMALL_TO_FULL_KANA.get(character);
88036
+ if (full) chars.add(full);
88037
+ }
88038
+ }
88039
+ var FULL_WIDTH_KEYWORD_RE = /\bfull-width\b/;
88040
+ var FULL_SIZE_KANA_KEYWORD_RE = /\bfull-size-kana\b/;
88041
+ var DECLARATION_BOUNDARY_RE = /[;{}]/;
88042
+ function skipWhitespace(s, pos) {
88043
+ while (pos < s.length && " \n\r\f\v".includes(s[pos])) pos += 1;
88044
+ return pos;
88045
+ }
88046
+ function findDeclarationEnd(s, pos) {
88047
+ const match = DECLARATION_BOUNDARY_RE.exec(s.slice(pos));
88048
+ return match ? pos + match.index : s.length;
88049
+ }
88050
+ function hasTextTransformKeyword(html, keyword) {
88051
+ const haystack = html.toLowerCase();
88052
+ const property = "text-transform";
88053
+ let from = 0;
88054
+ for (; ; ) {
88055
+ const at2 = haystack.indexOf(property, from);
88056
+ if (at2 === -1) return false;
88057
+ const afterProp = skipWhitespace(haystack, at2 + property.length);
88058
+ if (haystack[afterProp] !== ":") {
88059
+ from = at2 + property.length;
88060
+ continue;
88061
+ }
88062
+ const end = findDeclarationEnd(haystack, afterProp + 1);
88063
+ if (keyword.test(haystack.slice(afterProp + 1, end))) return true;
88064
+ from = end;
88065
+ }
88066
+ }
87856
88067
  function extractGoogleFontsText(html) {
87857
88068
  const { document: document2 } = parseHTML(html);
87858
88069
  const decodedBodyText = document2.body?.textContent ?? "";
87859
- const characters = [...Array.from(html), ...Array.from(decodedBodyText)];
88070
+ const locales = collectLangAttributes(document2);
87860
88071
  const uniqueCharacters = /* @__PURE__ */ new Set();
87861
- for (const character of characters) {
87862
- uniqueCharacters.add(character);
87863
- for (const variant of `${character.toUpperCase()}${character.toLowerCase()}`) {
87864
- uniqueCharacters.add(variant);
87865
- }
88072
+ for (const character of /* @__PURE__ */ new Set([...Array.from(html), ...Array.from(decodedBodyText)])) {
88073
+ addCaseClosure(uniqueCharacters, character, locales);
87866
88074
  }
88075
+ if (hasTextTransformKeyword(html, FULL_WIDTH_KEYWORD_RE)) addFullwidthVariants(uniqueCharacters);
88076
+ if (hasTextTransformKeyword(html, FULL_SIZE_KANA_KEYWORD_RE))
88077
+ addFullSizeKanaVariants(uniqueCharacters);
87867
88078
  const fontText = [...uniqueCharacters].join("");
87868
88079
  return encodeURIComponent(fontText).length <= GOOGLE_FONTS_TEXT_MAX_ENCODED_LENGTH ? fontText : void 0;
87869
88080
  }
@@ -88712,6 +88923,7 @@ async function preflightCompositionAssetMediaTypes(input) {
88712
88923
  function logRemoteDownloadTelemetry(event) {
88713
88924
  defaultLogger.info("[Compiler] Remote asset download integrity", { ...event });
88714
88925
  }
88926
+ var INFERRED_MEDIA_DURATION_ATTR = "data-hf-inferred-duration";
88715
88927
  function parseSubCompHtmlForValidity(html) {
88716
88928
  return parseHTML(html).document;
88717
88929
  }
@@ -88946,6 +89158,18 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
88946
89158
  return { duration, resolvedPath: filePath };
88947
89159
  });
88948
89160
  }
89161
+ function markInferredVariableMediaDurations(html, unresolvedMedia) {
89162
+ const unresolvedIds = new Set(unresolvedMedia.map((element) => element.id));
89163
+ if (unresolvedIds.size === 0) return html;
89164
+ const { document: document2 } = parseHTML(html);
89165
+ let changed = false;
89166
+ for (const element of document2.querySelectorAll("video[data-var-src], audio[data-var-src]")) {
89167
+ if (!unresolvedIds.has(element.id)) continue;
89168
+ element.setAttribute(INFERRED_MEDIA_DURATION_ATTR, "");
89169
+ changed = true;
89170
+ }
89171
+ return changed ? document2.toString() : html;
89172
+ }
88949
89173
  async function compileHtmlFile(html, baseDir, downloadDir, log) {
88950
89174
  const { html: staticCompiled, unresolved } = compileTimingAttrs(html);
88951
89175
  const mediaUnresolved = unresolved.filter(
@@ -88969,7 +89193,8 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
88969
89193
  const resolutions = resolvedResults.filter(
88970
89194
  (r) => r.duration != null && Number.isFinite(r.duration)
88971
89195
  );
88972
- let compiledHtml = resolutions.length > 0 ? injectDurations(staticCompiled, resolutions) : staticCompiled;
89196
+ const markedStaticHtml = markInferredVariableMediaDurations(staticCompiled, mediaUnresolved);
89197
+ let compiledHtml = resolutions.length > 0 ? injectDurations(markedStaticHtml, resolutions) : markedStaticHtml;
88973
89198
  const preResolved = extractResolvedMedia(compiledHtml);
88974
89199
  const clampResults = await Promise.all(
88975
89200
  preResolved.filter((el) => !!el.src && !el.loop).map(async (el) => {
@@ -89739,8 +89964,38 @@ function rewriteUnresolvableGsapToCdn(html, projectDir) {
89739
89964
  }
89740
89965
  );
89741
89966
  }
89967
+ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
89968
+ if (!isPathInside3(htmlPath, projectDir)) return html;
89969
+ const entryPath = relative2(projectDir, htmlPath).replace(/\\/g, "/");
89970
+ if (!entryPath.includes("/")) return html;
89971
+ const { document: document2 } = parseHTML(html);
89972
+ const assetExists = (path) => existsSync25(resolve12(projectDir, path));
89973
+ rewriteAssetPaths(
89974
+ document2.querySelectorAll("[src], [href]"),
89975
+ entryPath,
89976
+ (el, attr) => el.getAttribute(attr),
89977
+ (el, attr, value) => el.setAttribute(attr, value),
89978
+ assetExists
89979
+ );
89980
+ rewriteInlineStyleAssetUrls(
89981
+ document2.querySelectorAll("[style]"),
89982
+ entryPath,
89983
+ (el) => el.getAttribute("style"),
89984
+ (el, value) => el.setAttribute("style", value),
89985
+ assetExists
89986
+ );
89987
+ for (const style of document2.querySelectorAll("style")) {
89988
+ style.textContent = rewriteCssAssetUrls(style.textContent || "", entryPath, assetExists);
89989
+ }
89990
+ return document2.toString();
89991
+ }
89742
89992
  async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
89743
- const rawHtml = rewriteUnresolvableGsapToCdn(readFileSync10(htmlPath, "utf-8"), projectDir);
89993
+ const entryHtml = rebaseDirectEntryAssetPaths(
89994
+ readFileSync10(htmlPath, "utf-8"),
89995
+ projectDir,
89996
+ htmlPath
89997
+ );
89998
+ const rawHtml = rewriteUnresolvableGsapToCdn(entryHtml, projectDir);
89744
89999
  assertSubCompositionsUsable(rawHtml, projectDir);
89745
90000
  const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
89746
90001
  rawHtml,
@@ -89897,6 +90152,8 @@ async function discoverMediaFromBrowser(page) {
89897
90152
  const start = parseFloat(htmlEl.getAttribute("data-start") || "0");
89898
90153
  const endRaw = htmlEl.getAttribute("data-end");
89899
90154
  const durationRaw = htmlEl.getAttribute("data-duration");
90155
+ const durationInferred = htmlEl.hasAttribute("data-hf-inferred-duration");
90156
+ const intrinsicDuration = isImage ? 0 : htmlEl.duration;
89900
90157
  const playbackStartRaw = htmlEl.getAttribute("data-playback-start");
89901
90158
  const mediaStartRaw = htmlEl.getAttribute("data-media-start");
89902
90159
  const loop = htmlEl.hasAttribute("loop");
@@ -89910,6 +90167,8 @@ async function discoverMediaFromBrowser(page) {
89910
90167
  start,
89911
90168
  endRaw,
89912
90169
  durationRaw,
90170
+ intrinsicDuration,
90171
+ durationInferred,
89913
90172
  playbackStartRaw,
89914
90173
  mediaStartRaw,
89915
90174
  loop,
@@ -89920,18 +90179,20 @@ async function discoverMediaFromBrowser(page) {
89920
90179
  });
89921
90180
  return results;
89922
90181
  });
89923
- return elements.map(({ endRaw, durationRaw, playbackStartRaw, mediaStartRaw, ...element }) => ({
89924
- ...element,
89925
- end: parseStrictFiniteTimingNumber(endRaw) ?? 0,
89926
- duration: parseStrictFiniteTimingNumber(durationRaw) ?? 0,
89927
- mediaStart: readMediaStart({
89928
- getAttribute(name) {
89929
- if (name === "data-playback-start") return playbackStartRaw;
89930
- if (name === "data-media-start") return mediaStartRaw;
89931
- return null;
89932
- }
90182
+ return elements.map(
90183
+ ({ endRaw, durationRaw, intrinsicDuration, playbackStartRaw, mediaStartRaw, ...element }) => ({
90184
+ ...element,
90185
+ end: parseStrictFiniteTimingNumber(endRaw) ?? 0,
90186
+ duration: element.durationInferred && Number.isFinite(intrinsicDuration) && intrinsicDuration > 0 ? intrinsicDuration : parseStrictFiniteTimingNumber(durationRaw) ?? 0,
90187
+ mediaStart: readMediaStart({
90188
+ getAttribute(name) {
90189
+ if (name === "data-playback-start") return playbackStartRaw;
90190
+ if (name === "data-media-start") return mediaStartRaw;
90191
+ return null;
90192
+ }
90193
+ })
89933
90194
  })
89934
- }));
90195
+ );
89935
90196
  }
89936
90197
  async function discoverAudioVolumeAutomationFromTimeline(page, audioIds, compositionDuration, sampleFps) {
89937
90198
  if (audioIds.length === 0 || compositionDuration <= 0) return [];
@@ -90377,6 +90638,11 @@ function hasVariableBoundMedia(html, variables) {
90377
90638
  return Boolean(variableId && Object.hasOwn(variables, variableId));
90378
90639
  });
90379
90640
  }
90641
+ function reconcileBrowserMediaEnd(existingEnd, projectedEnd, sourceChanged, durationInferred) {
90642
+ if (projectedEnd <= 0) return existingEnd;
90643
+ if (sourceChanged && durationInferred) return projectedEnd;
90644
+ return existingEnd <= 0 ? projectedEnd : Math.min(existingEnd, projectedEnd);
90645
+ }
90380
90646
  function hasRuntimeInsertedMedia(html) {
90381
90647
  const { document: document2 } = parseHTML(html);
90382
90648
  const scriptBodies = [...document2.querySelectorAll("script")].map((script) => script.textContent ?? "").join("\n");
@@ -90608,7 +90874,8 @@ async function runProbeStage(input) {
90608
90874
  if (existingVideoIds.has(el.id)) {
90609
90875
  const existing = composition.videos.find((v) => v.id === el.id);
90610
90876
  if (existing) {
90611
- if (existing.src !== src) {
90877
+ const sourceChanged = existing.src !== src;
90878
+ if (sourceChanged) {
90612
90879
  existing.src = src;
90613
90880
  }
90614
90881
  const projectedEnd = projectBrowserEndToCompositionTimeline(
@@ -90616,9 +90883,12 @@ async function runProbeStage(input) {
90616
90883
  el.start,
90617
90884
  resolveBrowserMediaEnd(el.start, el.end, el.duration)
90618
90885
  );
90619
- if (projectedEnd > 0) {
90620
- existing.end = existing.end <= 0 ? projectedEnd : Math.min(existing.end, projectedEnd);
90621
- }
90886
+ existing.end = reconcileBrowserMediaEnd(
90887
+ existing.end,
90888
+ projectedEnd,
90889
+ sourceChanged,
90890
+ el.durationInferred
90891
+ );
90622
90892
  if (el.mediaStart > 0 && (existing.mediaStart <= 0 || Math.abs(existing.mediaStart - el.mediaStart) > BROWSER_MEDIA_EPSILON)) {
90623
90893
  existing.mediaStart = el.mediaStart;
90624
90894
  }
@@ -90645,7 +90915,8 @@ async function runProbeStage(input) {
90645
90915
  if (existingAudioIds.has(el.id)) {
90646
90916
  const existing = composition.audios.find((a) => a.id === el.id);
90647
90917
  if (existing) {
90648
- if (existing.src !== src) {
90918
+ const sourceChanged = existing.src !== src;
90919
+ if (sourceChanged) {
90649
90920
  existing.src = src;
90650
90921
  }
90651
90922
  const projectedEnd = projectBrowserEndToCompositionTimeline(
@@ -90653,9 +90924,12 @@ async function runProbeStage(input) {
90653
90924
  el.start,
90654
90925
  resolveBrowserMediaEnd(el.start, el.end, el.duration)
90655
90926
  );
90656
- if (projectedEnd > 0) {
90657
- existing.end = existing.end <= 0 ? projectedEnd : Math.min(existing.end, projectedEnd);
90658
- }
90927
+ existing.end = reconcileBrowserMediaEnd(
90928
+ existing.end,
90929
+ projectedEnd,
90930
+ sourceChanged,
90931
+ el.durationInferred
90932
+ );
90659
90933
  if (el.mediaStart > 0 && (existing.mediaStart <= 0 || Math.abs(existing.mediaStart - el.mediaStart) > BROWSER_MEDIA_EPSILON)) {
90660
90934
  existing.mediaStart = el.mediaStart;
90661
90935
  }
@@ -91336,9 +91610,32 @@ async function runAudioStage(input) {
91336
91610
 
91337
91611
  // ../producer/src/services/render/stages/captureStage.ts
91338
91612
  init_src();
91613
+ import { statfsSync } from "fs";
91339
91614
  function shouldAllowAdaptiveCaptureRetry(workerCount, _explicitlyConfigured) {
91340
91615
  return workerCount > 1;
91341
91616
  }
91617
+ function estimateDiskCaptureBytes(totalFrames, captureOptions) {
91618
+ const scale = captureOptions.deviceScaleFactor ?? 1;
91619
+ const outputWidth = Math.ceil(captureOptions.width * scale);
91620
+ const outputHeight = Math.ceil(captureOptions.height * scale);
91621
+ return Math.ceil(totalFrames) * outputWidth * outputHeight * 4;
91622
+ }
91623
+ function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, freeDiskBytes = (path) => {
91624
+ try {
91625
+ const stat = statfsSync(path);
91626
+ return stat.bavail * stat.bsize;
91627
+ } catch {
91628
+ return null;
91629
+ }
91630
+ }) {
91631
+ const freeBytes = freeDiskBytes(framesDir);
91632
+ if (freeBytes === null) return;
91633
+ const estimatedBytes = estimateDiskCaptureBytes(totalFrames, captureOptions);
91634
+ if (estimatedBytes <= freeBytes * 0.9) return;
91635
+ throw new Error(
91636
+ `Disk capture may need ~${(estimatedBytes / 1e6).toFixed(1)} MB of temporary frame storage, but only ${(freeBytes / 1e6).toFixed(1)} MB is free at ${framesDir}. Re-run with --low-memory-mode to stream frames, raise PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS if streaming is supported, or free up disk space.`
91637
+ );
91638
+ }
91342
91639
  async function runCaptureStage(input) {
91343
91640
  const {
91344
91641
  fileServer,
@@ -91377,6 +91674,8 @@ async function runCaptureStage(input) {
91377
91674
  );
91378
91675
  }
91379
91676
  }
91677
+ const captureOptions = buildCaptureOptions();
91678
+ assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions);
91380
91679
  if (workerCount > 1) {
91381
91680
  const attempts = await executeDiskCaptureWithAdaptiveRetry({
91382
91681
  serverUrl: fileServer.url,
@@ -91386,7 +91685,7 @@ async function runCaptureStage(input) {
91386
91685
  initialWorkerCount: workerCount,
91387
91686
  allowRetry: shouldAllowAdaptiveCaptureRetry(workerCount, job.config.workers !== void 0),
91388
91687
  frameExt: needsAlpha ? "png" : "jpg",
91389
- captureOptions: buildCaptureOptions(),
91688
+ captureOptions,
91390
91689
  createBeforeCaptureHook: createRenderVideoFrameInjector,
91391
91690
  abortSignal,
91392
91691
  frameRangeStart: frameRange?.startFrame,
@@ -91424,7 +91723,7 @@ async function runCaptureStage(input) {
91424
91723
  const session = probeSession ?? await createCaptureSession(
91425
91724
  fileServer.url,
91426
91725
  framesDir,
91427
- buildCaptureOptions(),
91726
+ captureOptions,
91428
91727
  videoInjector,
91429
91728
  captureCfg
91430
91729
  );
@@ -91964,11 +92263,11 @@ import {
91964
92263
  constants as constants2,
91965
92264
  fstatSync as fstatSync2,
91966
92265
  mkdirSync as mkdirSync14,
91967
- mkdtempSync as mkdtempSync6,
92266
+ mkdtempSync as mkdtempSync7,
91968
92267
  openSync as openSync4,
91969
92268
  readFileSync as readFileSync11,
91970
92269
  rmSync as rmSync10,
91971
- statfsSync
92270
+ statfsSync as statfsSync2
91972
92271
  } from "fs";
91973
92272
  import { join as join33 } from "path";
91974
92273
  var NO_FOLLOW_FLAG = constants2.O_NOFOLLOW ?? 0;
@@ -92147,7 +92446,7 @@ function assertHdrExtractionDiskHeadroom(framesDir, plannedVideos, fps, log) {
92147
92446
  }
92148
92447
  let freeBytes;
92149
92448
  try {
92150
- const stat = statfsSync(framesDir);
92449
+ const stat = statfsSync2(framesDir);
92151
92450
  freeBytes = stat.bavail * stat.bsize;
92152
92451
  } catch {
92153
92452
  return { estimatedBytes, budgetBytes: configuredBudget };
@@ -92214,7 +92513,7 @@ async function extractHdrVideoFrames(args) {
92214
92513
  const window3 = extractionWindows.get(videoId);
92215
92514
  if (!video || !window3) continue;
92216
92515
  mkdirSync14(framesDir, { recursive: true });
92217
- const frameDir = mkdtempSync6(join33(framesDir, `hdr_${tempDirSafePrefix(videoId)}-`));
92516
+ const frameDir = mkdtempSync7(join33(framesDir, `hdr_${tempDirSafePrefix(videoId)}-`));
92218
92517
  createdFrameDirs.add(frameDir);
92219
92518
  const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
92220
92519
  const rawPath = join33(frameDir, "frames.rgb48le");
@@ -94213,7 +94512,8 @@ async function runEncodeStage(input) {
94213
94512
  return { encodeMs: Date.now() - stage5Start };
94214
94513
  }
94215
94514
  updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
94216
- const scaledEncodeTimeout = Math.ceil((job.duration ?? 0) * 4e3);
94515
+ const baseScaledEncodeTimeout = Math.ceil((job.duration ?? 0) * 4e3);
94516
+ const scaledEncodeTimeout = baseScaledEncodeTimeout * (job.config.quality === "high" ? 6 : 1);
94217
94517
  const videoEngineCfg = scaledEncodeTimeout > engineCfg.ffmpegEncodeTimeout ? { ...engineCfg, ffmpegEncodeTimeout: scaledEncodeTimeout } : engineCfg;
94218
94518
  const frameExt = needsAlpha ? "png" : "jpg";
94219
94519
  const framePattern = `frame_%06d.${frameExt}`;
@@ -94266,8 +94566,30 @@ import { extname as extname6 } from "path";
94266
94566
  init_src();
94267
94567
  init_dist2();
94268
94568
  import { spawn as spawn5 } from "child_process";
94269
- import { rmSync as rmSync12 } from "fs";
94569
+ import { mkdtempSync as mkdtempSync8, renameSync as renameSync6, rmSync as rmSync12 } from "fs";
94570
+ import { dirname as dirname16, join as join41 } from "path";
94270
94571
  var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
94572
+ var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
94573
+ var MAX_TRUE_PEAK_CORRECTION_PASSES = 3;
94574
+ function buildAacTruePeakCorrectionArgs(inputPath, outputPath, targetDurationSeconds, attenuationDb) {
94575
+ return [
94576
+ "-i",
94577
+ inputPath,
94578
+ "-map",
94579
+ "0:a:0",
94580
+ "-vn",
94581
+ "-af",
94582
+ `volume=${attenuationDb.toFixed(3)}dB`,
94583
+ "-t",
94584
+ formatSeconds(targetDurationSeconds),
94585
+ "-c:a",
94586
+ "aac",
94587
+ "-b:a",
94588
+ "192k",
94589
+ "-y",
94590
+ outputPath
94591
+ ];
94592
+ }
94271
94593
  function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, targetDurationSeconds) {
94272
94594
  const delta = targetDurationSeconds - sourceDurationSeconds;
94273
94595
  const targetSec = formatSeconds(targetDurationSeconds);
@@ -94342,6 +94664,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
94342
94664
  const probeVideo = input.probeVideoFrameInfo ?? ((videoPath) => defaultProbeVideoFrameInfo(videoPath, input.signal));
94343
94665
  const probeAudio = input.probeAudioInfo ?? defaultProbeAudioInfo;
94344
94666
  const runner = input.runFfmpeg ?? ((args) => defaultRunFfmpeg(args, input.signal));
94667
+ const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg ? void 0 : defaultProbeAudioTruePeakDbfs);
94345
94668
  const [videoResult, audioResult] = await Promise.allSettled([
94346
94669
  probeVideo(input.videoPath),
94347
94670
  probeAudio(input.audioPath, input.signal)
@@ -94413,6 +94736,26 @@ async function padOrTrimAudioToVideoFrameCount(input) {
94413
94736
  } finally {
94414
94737
  for (const path of plan2.cleanupPaths) rmSync12(path, { force: true });
94415
94738
  }
94739
+ if (probeTruePeak) {
94740
+ const correction = await enforceAacTruePeak({
94741
+ audioPath: input.outputPath,
94742
+ targetDurationSeconds,
94743
+ signal: input.signal,
94744
+ probeTruePeak,
94745
+ runner
94746
+ });
94747
+ if (!correction.success) {
94748
+ return {
94749
+ success: false,
94750
+ outputPath: input.outputPath,
94751
+ targetDurationSeconds,
94752
+ sourceDurationSeconds: audioInfo.durationSeconds,
94753
+ operation: plan2.operation,
94754
+ error: correction.error,
94755
+ failureReason: correction.failureReason
94756
+ };
94757
+ }
94758
+ }
94416
94759
  return {
94417
94760
  success: true,
94418
94761
  outputPath: input.outputPath,
@@ -94421,6 +94764,60 @@ async function padOrTrimAudioToVideoFrameCount(input) {
94421
94764
  operation: plan2.operation
94422
94765
  };
94423
94766
  }
94767
+ async function enforceAacTruePeak(input) {
94768
+ let scratchDir;
94769
+ try {
94770
+ scratchDir = mkdtempSync8(join41(dirname16(input.audioPath), ".true-peak-"));
94771
+ const correctedPath = join41(scratchDir, "audio.m4a");
94772
+ let attenuationDb = 0;
94773
+ let measuredPath = input.audioPath;
94774
+ for (let pass = 0; pass <= MAX_TRUE_PEAK_CORRECTION_PASSES; pass += 1) {
94775
+ const truePeakDbfs = await input.probeTruePeak(measuredPath, input.signal);
94776
+ if (Number.isNaN(truePeakDbfs) || truePeakDbfs === Number.POSITIVE_INFINITY) {
94777
+ return { success: false, error: "audioPadTrim: FFmpeg reported an invalid true peak" };
94778
+ }
94779
+ if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
94780
+ if (measuredPath === correctedPath) renameSync6(correctedPath, input.audioPath);
94781
+ return { success: true };
94782
+ }
94783
+ if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
94784
+ attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - truePeakDbfs;
94785
+ const result = await input.runner(
94786
+ buildAacTruePeakCorrectionArgs(
94787
+ input.audioPath,
94788
+ correctedPath,
94789
+ input.targetDurationSeconds,
94790
+ attenuationDb
94791
+ )
94792
+ );
94793
+ if (!result.success) {
94794
+ return {
94795
+ success: false,
94796
+ error: sanitizeProbeFailure(
94797
+ result.error ?? "audioPadTrim: failed to constrain AAC true peak",
94798
+ [input.audioPath, correctedPath]
94799
+ ),
94800
+ failureReason: result.failureReason
94801
+ };
94802
+ }
94803
+ measuredPath = correctedPath;
94804
+ }
94805
+ return {
94806
+ success: false,
94807
+ error: `audioPadTrim: AAC true peak remained above ${AAC_DELIVERY_TRUE_PEAK_DBFS} dBFS after ${MAX_TRUE_PEAK_CORRECTION_PASSES} correction passes`
94808
+ };
94809
+ } catch (err) {
94810
+ return {
94811
+ success: false,
94812
+ error: `audioPadTrim: failed to validate AAC true peak: ${sanitizeProbeFailure(
94813
+ err,
94814
+ scratchDir ? [input.audioPath, scratchDir] : [input.audioPath]
94815
+ )}`
94816
+ };
94817
+ } finally {
94818
+ if (scratchDir) rmSync12(scratchDir, { recursive: true, force: true });
94819
+ }
94820
+ }
94424
94821
  function failResult(outputPath, target, source, error) {
94425
94822
  return {
94426
94823
  success: false,
@@ -94494,6 +94891,22 @@ async function defaultProbeAudioInfo(audioPath, signal) {
94494
94891
  audioCodec: metadata.audioCodec
94495
94892
  };
94496
94893
  }
94894
+ async function defaultProbeAudioTruePeakDbfs(audioPath, signal) {
94895
+ const result = await runFfmpeg(
94896
+ ["-i", audioPath, "-map", "0:a:0", "-vn", "-af", "ebur128=peak=true", "-f", "null", "-"],
94897
+ { signal }
94898
+ );
94899
+ if (!result.success) {
94900
+ throw new Error(`[audioPadTrim] ${formatFfmpegError(result.exitCode, result.stderr)}`);
94901
+ }
94902
+ const matches2 = [...result.stderr.matchAll(/^\s*Peak:\s*([+-]?(?:[\d.]+|inf)) dBFS$/gim)];
94903
+ const token = matches2.at(-1)?.[1]?.toLowerCase();
94904
+ const value = token === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
94905
+ if (Number.isNaN(value) || value === Number.POSITIVE_INFINITY) {
94906
+ throw new Error("[audioPadTrim] FFmpeg did not report AAC true peak");
94907
+ }
94908
+ return value;
94909
+ }
94497
94910
  async function defaultRunFfmpeg(args, signal) {
94498
94911
  const result = await runFfmpeg(args, { signal });
94499
94912
  if (result.success) return { success: true };
@@ -94608,7 +95021,7 @@ function sampleDirectoryBytes(dir) {
94608
95021
  continue;
94609
95022
  }
94610
95023
  for (const name of entries2) {
94611
- const full = join41(current2, name);
95024
+ const full = join42(current2, name);
94612
95025
  try {
94613
95026
  const st = statSync10(full);
94614
95027
  if (st.isDirectory()) {
@@ -94652,7 +95065,8 @@ function summarizeExtractionObservability(extractionResult, videoCount, coverage
94652
95065
  var RenderQualityError = class extends Error {
94653
95066
  constructor(warnings) {
94654
95067
  super(
94655
- `Render blocked by ${warnings.length} correctness warning${warnings.length === 1 ? "" : "s"}: ` + warnings.map((warning) => warning.code).join(", ")
95068
+ `Render blocked by ${warnings.length} correctness warning${warnings.length === 1 ? "" : "s"}:
95069
+ ` + warnings.map((warning) => `- ${warning.code}: ${redactTelemetryString(warning.message)}`).join("\n")
94656
95070
  );
94657
95071
  this.warnings = warnings;
94658
95072
  this.name = "RenderQualityError";
@@ -94756,7 +95170,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
94756
95170
  const ranges = [];
94757
95171
  let rangeStart = null;
94758
95172
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
94759
- const framePath = join41(framesDir, formatCaptureFrameName(frameIndex, frameExt));
95173
+ const framePath = join42(framesDir, formatCaptureFrameName(frameIndex, frameExt));
94760
95174
  const missing = !existsSync30(framePath) || statSync10(framePath).size <= 8;
94761
95175
  if (missing && rangeStart === null) {
94762
95176
  rangeStart = frameIndex;
@@ -94779,7 +95193,7 @@ function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, ran
94779
95193
  workerId,
94780
95194
  startFrame: rangeStart + range.startFrame,
94781
95195
  endFrame: rangeStart + range.endFrame,
94782
- outputDir: join41(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`),
95196
+ outputDir: join42(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`),
94783
95197
  outputFrameOffset: rangeStart
94784
95198
  }));
94785
95199
  batches.push(batch);
@@ -94802,8 +95216,8 @@ function getNextRetryWorkerCount(currentWorkers) {
94802
95216
  return Math.max(1, Math.floor(currentWorkers / 2));
94803
95217
  }
94804
95218
  function resolveRenderWorkDirPrefix(outputPath, jobId, platform3 = process.platform, systemTempDir = tmpdir7()) {
94805
- if (platform3 === "win32") return join41(systemTempDir, "hf-render-");
94806
- return join41(dirname16(outputPath), `work-${jobId}-`);
95219
+ if (platform3 === "win32") return join42(systemTempDir, "hf-render-");
95220
+ return join42(dirname17(outputPath), `work-${jobId}-`);
94807
95221
  }
94808
95222
  var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
94809
95223
  function captureAttemptMadeProgress(attemptTargetFrameCount, remainingFrameCount) {
@@ -94832,7 +95246,7 @@ The composition is too large for the available memory. To reduce memory pressure
94832
95246
  function countCapturedFrames(totalFrames, framesDir, frameExt) {
94833
95247
  let captured = 0;
94834
95248
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
94835
- const framePath = join41(framesDir, formatCaptureFrameName(frameIndex, frameExt));
95249
+ const framePath = join42(framesDir, formatCaptureFrameName(frameIndex, frameExt));
94836
95250
  if (existsSync30(framePath)) captured++;
94837
95251
  }
94838
95252
  return captured;
@@ -94857,7 +95271,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
94857
95271
  reason: attempt === 0 ? "initial" : pendingTransientRetry ? "transient-retry" : "retry"
94858
95272
  });
94859
95273
  pendingTransientRetry = false;
94860
- const attemptWorkDir = join41(options.workDir, `capture-attempt-${attempt}`);
95274
+ const attemptWorkDir = join42(options.workDir, `capture-attempt-${attempt}`);
94861
95275
  const batches = missingRanges ? buildMissingFrameRetryBatches(
94862
95276
  missingRanges,
94863
95277
  currentWorkers,
@@ -95194,15 +95608,15 @@ function deVerifyFallbackTelemetry(err) {
95194
95608
  };
95195
95609
  }
95196
95610
  async function executeRenderJob(job, projectDir, outputPath, progressSink, abortSignal) {
95197
- const moduleDir = dirname16(fileURLToPath5(import.meta.url));
95611
+ const moduleDir = dirname17(fileURLToPath5(import.meta.url));
95198
95612
  const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve13(process.env.PRODUCER_RENDERS_DIR, "..") : resolve13(moduleDir, "../..");
95199
- const debugDir = join41(producerRoot, ".debug");
95200
- const outputDir = dirname16(outputPath);
95613
+ const debugDir = join42(producerRoot, ".debug");
95614
+ const outputDir = dirname17(outputPath);
95201
95615
  if (!existsSync30(outputDir)) mkdirSync17(outputDir, { recursive: true });
95202
- const workDir = job.config.debug ? join41(debugDir, job.id) : mkdtempSync7(resolveRenderWorkDirPrefix(outputPath, job.id));
95616
+ const workDir = job.config.debug ? join42(debugDir, job.id) : mkdtempSync9(resolveRenderWorkDirPrefix(outputPath, job.id));
95203
95617
  const pipelineStart = Date.now();
95204
95618
  const baseLog = job.config.logger ?? defaultLogger;
95205
- const logPath = job.config.debug ? join41(workDir, "render.log") : null;
95619
+ const logPath = job.config.debug ? join42(workDir, "render.log") : null;
95206
95620
  const execution = new RenderExecutionContext({
95207
95621
  request: { renderJobId: job.id, projectDir, outputPath },
95208
95622
  logger: logPath ? createRenderFileLogger(logPath, baseLog) : baseLog,
@@ -95250,7 +95664,7 @@ async function executeRenderPipeline(input) {
95250
95664
  imageDecodeFailures: 0
95251
95665
  };
95252
95666
  let hdrPerf;
95253
- const perfOutputPath = join41(workDir, "perf-summary.json");
95667
+ const perfOutputPath = join42(workDir, "perf-summary.json");
95254
95668
  const cfg = { ...job.config.producerConfig ?? resolveConfig() };
95255
95669
  const observability = new RenderObservabilityRecorder({
95256
95670
  pipelineStartMs: pipelineStart,
@@ -95342,15 +95756,15 @@ async function executeRenderPipeline(input) {
95342
95756
  requestedWorkers: job.config.workers ?? "auto"
95343
95757
  });
95344
95758
  const entryFile = job.config.entryFile || "index.html";
95345
- let htmlPath = join41(projectDir, entryFile);
95759
+ let htmlPath = join42(projectDir, entryFile);
95346
95760
  if (!existsSync30(htmlPath)) {
95347
95761
  throw new Error(`Entry file not found: ${htmlPath}`);
95348
95762
  }
95349
95763
  assertNotAborted();
95350
95764
  const rawEntry = readFileSync12(htmlPath, "utf-8");
95351
95765
  if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
95352
- const wrapperPath = join41(workDir, "standalone-entry.html");
95353
- const projectIndexPath = join41(projectDir, "index.html");
95766
+ const wrapperPath = join42(workDir, "standalone-entry.html");
95767
+ const projectIndexPath = join42(projectDir, "index.html");
95354
95768
  if (!existsSync30(projectIndexPath)) {
95355
95769
  throw new Error(
95356
95770
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
@@ -95505,7 +95919,7 @@ async function executeRenderPipeline(input) {
95505
95919
  beginFrameStalled: probeResult.beginFrameStalled
95506
95920
  });
95507
95921
  updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
95508
- const compiledDir = join41(workDir, "compiled");
95922
+ const compiledDir = join42(workDir, "compiled");
95509
95923
  const extractResult = await observeRenderStage(
95510
95924
  observability,
95511
95925
  "video_extract",
@@ -95625,7 +96039,7 @@ async function executeRenderPipeline(input) {
95625
96039
  try {
95626
96040
  fileServer = await createFileServer2({
95627
96041
  projectDir,
95628
- compiledDir: join41(workDir, "compiled"),
96042
+ compiledDir: join42(workDir, "compiled"),
95629
96043
  port: 0,
95630
96044
  preHeadScripts: [VIRTUAL_TIME_SHIM],
95631
96045
  fps: job.config.fps
@@ -95643,7 +96057,7 @@ async function executeRenderPipeline(input) {
95643
96057
  if (!activeFileServer) {
95644
96058
  throw new Error("File server failed to initialize before frame capture");
95645
96059
  }
95646
- const framesDir = join41(workDir, "captured-frames");
96060
+ const framesDir = join42(workDir, "captured-frames");
95647
96061
  if (!existsSync30(framesDir)) mkdirSync17(framesDir, { recursive: true });
95648
96062
  const resolvedBrowserGpuMode = await resolveBrowserGpuMode(cfg.browserGpuMode, {
95649
96063
  chromePath: resolveHeadlessShellPath(cfg),
@@ -95968,7 +96382,7 @@ async function executeRenderPipeline(input) {
95968
96382
  gif: ".gif"
95969
96383
  };
95970
96384
  const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
95971
- const videoOnlyPath = join41(workDir, `video-only${videoExt}`);
96385
+ const videoOnlyPath = join42(workDir, `video-only${videoExt}`);
95972
96386
  const usePageSideCompositingForTransitions = (cfg.enablePageSideCompositing || isGif) && compiled.hasShaderTransitions && !hasHdrContent && outputSupportsPageSideShaderCompositing(outputFormat);
95973
96387
  if (usePageSideCompositingForTransitions) {
95974
96388
  activeFileServer.addPreHeadScript(HF_PAGE_SIDE_COMPOSITING_STUB);
@@ -96561,7 +96975,7 @@ async function executeRenderPipeline(input) {
96561
96975
  }
96562
96976
  if (job.config.debug) {
96563
96977
  if (!isPngSequence && existsSync30(stagedOutputPath)) {
96564
- const debugOutput = join41(workDir, `output${videoExt}`);
96978
+ const debugOutput = join42(workDir, `output${videoExt}`);
96565
96979
  copyFileSync6(stagedOutputPath, debugOutput);
96566
96980
  }
96567
96981
  }
@@ -96899,12 +97313,12 @@ import {
96899
97313
  existsSync as existsSync33,
96900
97314
  mkdirSync as mkdirSync18,
96901
97315
  statSync as statSync12,
96902
- mkdtempSync as mkdtempSync8,
97316
+ mkdtempSync as mkdtempSync10,
96903
97317
  writeFileSync as writeFileSync11,
96904
97318
  rmSync as rmSync14,
96905
97319
  createReadStream as createReadStream5
96906
97320
  } from "fs";
96907
- import { resolve as resolve15, dirname as dirname18, join as join44 } from "path";
97321
+ import { resolve as resolve15, dirname as dirname19, join as join45 } from "path";
96908
97322
  import { tmpdir as tmpdir8 } from "os";
96909
97323
  import { parseArgs } from "util";
96910
97324
  import crypto from "crypto";
@@ -96915,7 +97329,7 @@ import { serve as serve3 } from "@hono/node-server";
96915
97329
  // ../producer/src/services/hyperframeLint.ts
96916
97330
  init_dist3();
96917
97331
  import { existsSync as existsSync31, readFileSync as readFileSync13, statSync as statSync11 } from "fs";
96918
- import { resolve as resolve14, join as join42 } from "path";
97332
+ import { resolve as resolve14, join as join43 } from "path";
96919
97333
  function isStringRecord(value) {
96920
97334
  if (!value || typeof value !== "object" || Array.isArray(value)) {
96921
97335
  return false;
@@ -96963,7 +97377,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
96963
97377
  }
96964
97378
  }
96965
97379
  return {
96966
- error: `No HTML entry file found in project directory: ${join42(absProjectDir, preferredEntryFile || "index.html")}`
97380
+ error: `No HTML entry file found in project directory: ${join43(absProjectDir, preferredEntryFile || "index.html")}`
96967
97381
  };
96968
97382
  }
96969
97383
  function prepareHyperframeLintBody(body) {
@@ -97006,7 +97420,7 @@ async function runHyperframeLint(prepared) {
97006
97420
  // ../producer/src/services/healthWorker.ts
97007
97421
  import { Worker as Worker3 } from "worker_threads";
97008
97422
  import { fileURLToPath as fileURLToPath6 } from "url";
97009
- import { dirname as dirname17, join as join43 } from "path";
97423
+ import { dirname as dirname18, join as join44 } from "path";
97010
97424
  import { existsSync as existsSync32 } from "fs";
97011
97425
  var DEFAULT_HEALTH_PORT = 9848;
97012
97426
  async function startHealthWorker(options = {}) {
@@ -97081,8 +97495,8 @@ function defaultLogger2() {
97081
97495
  };
97082
97496
  }
97083
97497
  function resolveWorkerEntry2() {
97084
- const here = dirname17(fileURLToPath6(import.meta.url));
97085
- const candidates = [join43(here, "healthWorkerThread.js"), join43(here, "healthWorkerThread.ts")];
97498
+ const here = dirname18(fileURLToPath6(import.meta.url));
97499
+ const candidates = [join44(here, "healthWorkerThread.js"), join44(here, "healthWorkerThread.ts")];
97086
97500
  for (const candidate of candidates) {
97087
97501
  if (existsSync32(candidate)) return candidate;
97088
97502
  }
@@ -97219,7 +97633,7 @@ function buildRenderJobConfig(input, outputPath, log) {
97219
97633
  function resolvePreparedRenderOutput(prepared, rendersDir, log) {
97220
97634
  const { input, cleanupProjectDir } = prepared;
97221
97635
  const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
97222
- const outputDir = dirname18(absoluteOutputPath);
97636
+ const outputDir = dirname19(absoluteOutputPath);
97223
97637
  if (!existsSync33(outputDir)) mkdirSync18(outputDir, { recursive: true });
97224
97638
  return { input, cleanupProjectDir, absoluteOutputPath };
97225
97639
  }
@@ -97287,8 +97701,8 @@ async function resolveInlineRenderHtml(body) {
97287
97701
  }
97288
97702
  function materializeInlineProject(html, options) {
97289
97703
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir8();
97290
- const tempProjectDir = mkdtempSync8(join44(tempRoot, "producer-project-"));
97291
- writeFileSync11(join44(tempProjectDir, "index.html"), html, "utf-8");
97704
+ const tempProjectDir = mkdtempSync10(join45(tempRoot, "producer-project-"));
97705
+ writeFileSync11(join45(tempProjectDir, "index.html"), html, "utf-8");
97292
97706
  return {
97293
97707
  prepared: {
97294
97708
  input: { projectDir: tempProjectDir, ...options },
@@ -97738,12 +98152,12 @@ init_src();
97738
98152
  // ../producer/src/services/distributed/plan.ts
97739
98153
  init_dist2();
97740
98154
  init_src();
97741
- import { cpSync as cpSync2, existsSync as existsSync36, mkdirSync as mkdirSync20, renameSync as renameSync6, rmSync as rmSync15, writeFileSync as writeFileSync13 } from "fs";
97742
- import { join as join48, relative as relative4, sep as sep4 } from "path";
98155
+ import { cpSync as cpSync2, existsSync as existsSync36, mkdirSync as mkdirSync20, renameSync as renameSync7, rmSync as rmSync15, writeFileSync as writeFileSync13 } from "fs";
98156
+ import { join as join49, relative as relative5, sep as sep4 } from "path";
97743
98157
 
97744
98158
  // ../producer/src/services/render/stages/freezePlan.ts
97745
98159
  import { existsSync as existsSync34, mkdirSync as mkdirSync19, readFileSync as readFileSync14, readdirSync as readdirSync12, writeFileSync as writeFileSync12 } from "fs";
97746
- import { join as join45, relative as relative2, resolve as resolve16 } from "path";
98160
+ import { join as join46, relative as relative3, resolve as resolve16 } from "path";
97747
98161
 
97748
98162
  // ../producer/src/services/distributed/planProtocol.ts
97749
98163
  var PLAN_SCHEMA_VERSION = 1;
@@ -97785,7 +98199,7 @@ var FIELD_DELIMITER = Buffer.from([0]);
97785
98199
  // ../producer/src/services/distributed/shared.ts
97786
98200
  init_src();
97787
98201
  import { execFile as execFileCallback } from "child_process";
97788
- import { dirname as dirname19, join as join46 } from "path";
98202
+ import { dirname as dirname20, join as join47 } from "path";
97789
98203
  import { existsSync as existsSync35, readFileSync as readFileSync15 } from "fs";
97790
98204
  import { fileURLToPath as fileURLToPath7 } from "url";
97791
98205
  import { promisify as promisify3 } from "util";
@@ -97794,8 +98208,8 @@ var execFile3 = promisify3(execFileCallback);
97794
98208
 
97795
98209
  // ../producer/src/services/distributed/planSize.ts
97796
98210
  import { createHash as createHash10 } from "crypto";
97797
- import { lstatSync as lstatSync4, readdirSync as readdirSync13 } from "fs";
97798
- import { extname as extname7, join as join47, relative as relative3 } from "path";
98211
+ import { lstatSync as lstatSync5, readdirSync as readdirSync13 } from "fs";
98212
+ import { extname as extname7, join as join48, relative as relative4 } from "path";
97799
98213
  var PLAN_ROOT_CATEGORY = {
97800
98214
  compiled: "compiled",
97801
98215
  [PLAN_AUDIO_RELATIVE_PATH]: "audio",
@@ -97807,9 +98221,9 @@ var PLAN_ROOT_CATEGORY = {
97807
98221
  var PLAN_DIR_SIZE_LIMIT_BYTES = 2 * 1024 * 1024 * 1024;
97808
98222
  var FREEZE_OWNED_PLAN_FILES = [
97809
98223
  "plan.json",
97810
- join48("meta", "composition.json"),
97811
- join48("meta", "encoder.json"),
97812
- join48("meta", "chunks.json")
98224
+ join49("meta", "composition.json"),
98225
+ join49("meta", "encoder.json"),
98226
+ join49("meta", "chunks.json")
97813
98227
  ];
97814
98228
 
97815
98229
  // ../producer/src/services/distributed/planV2.ts
@@ -97818,21 +98232,21 @@ import {
97818
98232
  closeSync as closeSync6,
97819
98233
  copyFileSync as copyFileSync8,
97820
98234
  existsSync as existsSync38,
97821
- lstatSync as lstatSync5,
98235
+ lstatSync as lstatSync6,
97822
98236
  mkdirSync as mkdirSync22,
97823
- mkdtempSync as mkdtempSync10,
98237
+ mkdtempSync as mkdtempSync12,
97824
98238
  openSync as openSync5,
97825
98239
  readFileSync as readFileSync16,
97826
98240
  readSync as readSync3,
97827
98241
  readdirSync as readdirSync14,
97828
- renameSync as renameSync8,
98242
+ renameSync as renameSync9,
97829
98243
  rmSync as rmSync17,
97830
98244
  statSync as statSync14,
97831
98245
  writeFileSync as writeFileSync15
97832
98246
  } from "fs";
97833
98247
  import { createHash as createHash11 } from "crypto";
97834
98248
  import { tmpdir as tmpdir9 } from "os";
97835
- import { dirname as dirname21, isAbsolute as isAbsolute4, join as join51, relative as relative5, resolve as resolve17, sep as sep5 } from "path";
98249
+ import { dirname as dirname22, isAbsolute as isAbsolute4, join as join52, relative as relative6, resolve as resolve17, sep as sep5 } from "path";
97836
98250
 
97837
98251
  // ../producer/src/services/distributed/planV2Publisher.ts
97838
98252
  import {
@@ -97840,21 +98254,21 @@ import {
97840
98254
  existsSync as existsSync37,
97841
98255
  linkSync as linkSync3,
97842
98256
  mkdirSync as mkdirSync21,
97843
- mkdtempSync as mkdtempSync9,
97844
- renameSync as renameSync7,
98257
+ mkdtempSync as mkdtempSync11,
98258
+ renameSync as renameSync8,
97845
98259
  rmSync as rmSync16,
97846
98260
  statSync as statSync13,
97847
98261
  writeFileSync as writeFileSync14
97848
98262
  } from "fs";
97849
- import { dirname as dirname20, join as join50 } from "path";
98263
+ import { dirname as dirname21, join as join51 } from "path";
97850
98264
 
97851
98265
  // ../producer/src/services/distributed/planV2Layout.ts
97852
- import { join as join49 } from "path";
98266
+ import { join as join50 } from "path";
97853
98267
 
97854
98268
  // ../producer/src/services/distributed/planV2Execution.ts
97855
- import { mkdtempSync as mkdtempSync11, rmSync as rmSync20 } from "fs";
98269
+ import { mkdtempSync as mkdtempSync13, rmSync as rmSync20 } from "fs";
97856
98270
  import { tmpdir as tmpdir10 } from "os";
97857
- import { join as join54 } from "path";
98271
+ import { join as join55 } from "path";
97858
98272
 
97859
98273
  // ../producer/src/services/distributed/assemble.ts
97860
98274
  init_src();
@@ -97869,19 +98283,19 @@ import {
97869
98283
  statSync as statSync15,
97870
98284
  writeFileSync as writeFileSync16
97871
98285
  } from "fs";
97872
- import { dirname as dirname22, join as join52 } from "path";
98286
+ import { dirname as dirname23, join as join53 } from "path";
97873
98287
 
97874
98288
  // ../producer/src/services/distributed/renderChunk.ts
97875
98289
  init_src();
97876
98290
  import { randomBytes as randomBytes2 } from "crypto";
97877
98291
  import { existsSync as existsSync40, mkdirSync as mkdirSync24, readFileSync as readFileSync18, readdirSync as readdirSync16, rmSync as rmSync19, writeFileSync as writeFileSync17 } from "fs";
97878
- import { extname as extname8, join as join53 } from "path";
98292
+ import { extname as extname8, join as join54 } from "path";
97879
98293
  init_src();
97880
98294
 
97881
98295
  // ../producer/src/services/distributed/projectHash.ts
97882
98296
  import { readdirSync as readdirSync17, readFileSync as readFileSync19 } from "fs";
97883
98297
  import { createHash as createHash12 } from "crypto";
97884
- import { join as join55, relative as relative6 } from "path";
98298
+ import { join as join56, relative as relative7 } from "path";
97885
98299
 
97886
98300
  // src/fontLocalize.ts
97887
98301
  function safeVersion(version2) {
@@ -97922,8 +98336,8 @@ async function runFontLocalize(io, localize) {
97922
98336
  }
97923
98337
 
97924
98338
  // src/version.ts
97925
- var VERSION = true ? "0.8.27" : "0.0.0-dev";
97926
- var PRODUCER_VERSION = true ? "0.8.27" : "0.0.0-dev";
98339
+ var VERSION = true ? "0.8.29" : "0.0.0-dev";
98340
+ var PRODUCER_VERSION = true ? "0.8.29" : "0.0.0-dev";
97927
98341
 
97928
98342
  // src/fontLocalizeCli.ts
97929
98343
  async function readStdin() {