@hyperframes/producer 0.8.145 → 0.8.146

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.
@@ -288,7 +288,7 @@ function buildVirtualTimeShim(options) {
288
288
  const seedRandomFromFrame = options.seedRandomFromFrame === true;
289
289
  const seededRandomBlock = seedRandomFromFrame ? String.raw`
290
290
  // Seeded Math.random / crypto.getRandomValues, keyed by virtual time.
291
- // Mulberry32 — single uint32 state, deterministic, fast.
291
+ // Mulberry32: single uint32 state, deterministic, fast.
292
292
  var rngState = 0;
293
293
  function mulberry32() {
294
294
  rngState |= 0; rngState = (rngState + 0x6D2B79F5) | 0;
@@ -299,7 +299,7 @@ function buildVirtualTimeShim(options) {
299
299
  }
300
300
  function reseedRngFromTime(ms) {
301
301
  var ms32 = Math.max(0, Math.floor(Number(ms) || 0)) | 0;
302
- // Knuth's multiplicative hash + golden-ratio offset — gives a well-
302
+ // Knuth's multiplicative hash + golden-ratio offset gives a well-
303
303
  // distributed seed even for frame 0 (otherwise rngState=0 degenerates
304
304
  // the PRNG's first few outputs).
305
305
  rngState = (Math.imul(ms32, -1640531527) + 0x9E3779B9) | 0;
@@ -541,7 +541,7 @@ function buildRenderModeScript(fps) {
541
541
  };
542
542
  window.__playerReady = true;
543
543
  // Media-fallback player has no timeline to bind, so render-ready is immediate.
544
- // init.ts defers __renderReady until the timeline is bound \u2014 different runtime.
544
+ // init.ts defers __renderReady until the timeline is bound (a different runtime).
545
545
  window.__renderReady = true;
546
546
  return true;
547
547
  }
@@ -2243,7 +2243,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
2243
2243
  if (tagName !== "video") {
2244
2244
  if (error instanceof NotMediaPayloadError2) {
2245
2245
  log?.warn(
2246
- `[compile] Audio "${elementIdentity}" (${src}) is a text document, not a media file \u2014 the element is dropped from the render. Point it at a rendered media file.`
2246
+ `[compile] Audio "${elementIdentity}" (${src}) is a text document, not a media file, so the element is dropped from the render. Point it at a rendered media file.`
2247
2247
  );
2248
2248
  }
2249
2249
  return { duration: null, resolvedPath: filePath };
@@ -2332,7 +2332,7 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
2332
2332
  if (r.maxDuration != null && shouldClampResolvedMediaDuration(r.tagName, r.duration, r.maxDuration)) {
2333
2333
  clampList.push({ id: r.id, duration: r.maxDuration });
2334
2334
  log?.warn(
2335
- `[compile] Audio "${r.id}" (${r.src}) is ${r.maxDuration.toFixed(2)}s but its data-duration is ${r.duration.toFixed(2)}s \u2014 the slot is shortened to the media length. Set data-duration to ~${r.maxDuration.toFixed(2)}s, trim data-media-start, or use a longer/looping source if that isn't intended.`
2335
+ `[compile] Audio "${r.id}" (${r.src}) is ${r.maxDuration.toFixed(2)}s but its data-duration is ${r.duration.toFixed(2)}s, so the slot is shortened to the media length. Set data-duration to ~${r.maxDuration.toFixed(2)}s, trim data-media-start, or use a longer/looping source if that isn't intended.`
2336
2336
  );
2337
2337
  }
2338
2338
  }
@@ -2721,7 +2721,7 @@ ${safeText}
2721
2721
  } else {
2722
2722
  if (download.reason instanceof ScriptIntegrityError) throw download.reason;
2723
2723
  defaultLogger.warn(
2724
- `[Compiler] WARNING: Failed to download CDN script: ${src} \u2014 ${download.reason}. The render may fail if this script is required (e.g. GSAP). Consider bundling it locally in your project.`
2724
+ `[Compiler] WARNING: Failed to download CDN script: ${src} (${download.reason}). The render may fail if this script is required (e.g. GSAP). Consider bundling it locally in your project.`
2725
2725
  );
2726
2726
  }
2727
2727
  }
@@ -2773,7 +2773,7 @@ function collectExternalAssets(html, projectDir) {
2773
2773
  }
2774
2774
  if (externalAssets.size > 0) {
2775
2775
  defaultLogger.info(
2776
- `[Compiler] Found ${externalAssets.size} asset(s) outside project directory \u2014 will copy to render output`
2776
+ `[Compiler] Found ${externalAssets.size} asset(s) outside project directory, will copy to render output`
2777
2777
  );
2778
2778
  }
2779
2779
  return {
@@ -2798,7 +2798,7 @@ async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLab
2798
2798
  urlToLocal.set(url, localPath);
2799
2799
  } catch (err) {
2800
2800
  const identity = safeDownloadUrlIdentity(url);
2801
- defaultLogger.warn(`[Compiler] ${warnLabel} \u2014 using original URL as fallback.`, {
2801
+ defaultLogger.warn(`[Compiler] ${warnLabel}, using original URL as fallback.`, {
2802
2802
  urlFingerprint: identity.urlFingerprint,
2803
2803
  host: identity.host,
2804
2804
  error: err instanceof Error ? err.message : String(err)
@@ -2891,7 +2891,7 @@ async function fetchExternalStylesheetCss(href) {
2891
2891
  timeoutMs: 15e3
2892
2892
  });
2893
2893
  } catch (err) {
2894
- defaultLogger.warn("[Compiler] External stylesheet fetch failed \u2014 preserving link tag.", {
2894
+ defaultLogger.warn("[Compiler] External stylesheet fetch failed, preserving link tag.", {
2895
2895
  urlFingerprint: identity.urlFingerprint,
2896
2896
  host: identity.host,
2897
2897
  error: err instanceof Error ? err.message : String(err)
@@ -3535,12 +3535,15 @@ async function resolveCompositionDurations(page, unresolved) {
3535
3535
  const resolved = [];
3536
3536
  for (const id of compIds) {
3537
3537
  const tl = timelines[id];
3538
- if (tl && typeof tl.duration === "function") {
3539
- const dur = tl.duration();
3540
- if (dur > 0) {
3541
- resolved.push({ id, duration: dur, source: "__timelines" });
3542
- continue;
3543
- }
3538
+ let dur = 0;
3539
+ try {
3540
+ dur = typeof tl?.duration === "function" ? Number(tl.duration()) : 0;
3541
+ } catch {
3542
+ dur = 0;
3543
+ }
3544
+ if (dur > 0) {
3545
+ resolved.push({ id, duration: dur, source: "__timelines" });
3546
+ continue;
3544
3547
  }
3545
3548
  const el = document.getElementById(id);
3546
3549
  if (el) {
@@ -3636,28 +3639,28 @@ async function runCompileStage(input) {
3636
3639
  cfg.useDrawElement = false;
3637
3640
  deCompileGate = "3d";
3638
3641
  log.info(
3639
- "[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility) \u2014 disabling drawElementImage for this render. Capture uses the platform's baseline route."
3642
+ "[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility), so disabling drawElementImage for this render. Capture uses the platform's baseline route."
3640
3643
  );
3641
3644
  }
3642
3645
  if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_BLEND !== "true" && compiled.usesMixBlendMode) {
3643
3646
  cfg.useDrawElement = false;
3644
3647
  deCompileGate = "mix_blend_mode";
3645
3648
  log.info(
3646
- "[Render] Fast capture: composition uses mix-blend-mode \u2014 disabling drawElementImage for this render. Capture uses the platform's baseline route."
3649
+ "[Render] Fast capture: composition uses mix-blend-mode, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
3647
3650
  );
3648
3651
  }
3649
3652
  if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_ANCESTOR_BG !== "true" && compiled.hasAncestorBackgroundImage) {
3650
3653
  cfg.useDrawElement = false;
3651
3654
  deCompileGate = "ancestor_background_image";
3652
3655
  log.info(
3653
- "[Render] Fast capture: composition root's ancestors (body/html/wrapper) carry a background-image \u2014 disabling drawElementImage for this render. Capture uses the platform's baseline route."
3656
+ "[Render] Fast capture: composition root's ancestors (body/html/wrapper) carry a background-image, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
3654
3657
  );
3655
3658
  }
3656
3659
  if (cfg.useDrawElement && process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE !== "true" && compiled.hasShaderTransitions) {
3657
3660
  cfg.useDrawElement = false;
3658
3661
  deCompileGate = "shader_transitions";
3659
3662
  log.info(
3660
- "[Render] Fast capture: composition uses shader transitions \u2014 disabling drawElementImage so page-side compositing stays available."
3663
+ "[Render] Fast capture: composition uses shader transitions, so disabling drawElementImage so page-side compositing stays available."
3661
3664
  );
3662
3665
  }
3663
3666
  if (!cfg.useDrawElement && cfg.pageSideCompositingAutoDisabled) {
@@ -3985,7 +3988,7 @@ async function runProbeStage(input) {
3985
3988
  } else {
3986
3989
  beginFrameStalled = true;
3987
3990
  log.warn(
3988
- "[Render] BeginFrame liveness probe timed out \u2014 this composition stalls BeginFrame on this host (SwiftShader heavy-layer pattern). Relaunching the probe browser in screenshot capture mode; the render will use screenshot capture throughout.",
3991
+ "[Render] BeginFrame liveness probe timed out: this composition stalls BeginFrame on this host (SwiftShader heavy-layer pattern). Relaunching the probe browser in screenshot capture mode; the render will use screenshot capture throughout.",
3989
3992
  { probeTimeoutMs }
3990
3993
  );
3991
3994
  lastBrowserConsole = probeSession.browserConsoleBuffer;
@@ -4199,7 +4202,7 @@ async function runProbeStage(input) {
4199
4202
  video.start = win.visibleStart;
4200
4203
  video.end = win.visibleEnd;
4201
4204
  log.info(
4202
- `[Probe] Runtime video discovery: ${video.id} visible ${win.visibleStart.toFixed(2)}s\u2013${win.visibleEnd.toFixed(2)}s`
4205
+ `[Probe] Runtime video discovery: ${video.id} visible ${win.visibleStart.toFixed(2)}s to ${win.visibleEnd.toFixed(2)}s`
4203
4206
  );
4204
4207
  }
4205
4208
  }
@@ -4247,7 +4250,7 @@ async function runProbeStage(input) {
4247
4250
  });
4248
4251
  if (!timelinesInfo.gsapLoaded) {
4249
4252
  diagnostics.push(
4250
- "GSAP is not loaded \u2014 CDN script may have failed to download. Bundle GSAP locally in your project instead of using a CDN <script src>."
4253
+ "GSAP is not loaded. The CDN script may have failed to download. Bundle GSAP locally in your project instead of using a CDN <script src>."
4251
4254
  );
4252
4255
  } else if (timelinesInfo.timelineKeys.length === 0) {
4253
4256
  diagnostics.push(
@@ -4264,10 +4267,10 @@ async function runProbeStage(input) {
4264
4267
  log.warn("Failed to gather browser diagnostics for zero-duration composition", {
4265
4268
  error: err instanceof Error ? err.message : String(err)
4266
4269
  });
4267
- diagnostics.push("(Could not gather browser diagnostics \u2014 page may have crashed)");
4270
+ diagnostics.push("(Could not gather browser diagnostics. The page may have crashed.)");
4268
4271
  }
4269
4272
  const hint = diagnostics.length > 0 ? "\n\nDiagnostics:\n - " + diagnostics.join("\n - ") : "\n\nCheck that GSAP timelines are registered on window.__timelines.";
4270
- throw new Error("Composition duration is 0 \u2014 this would produce a black video." + hint);
4273
+ throw new Error("Composition duration is 0, so this would produce a black video." + hint);
4271
4274
  }
4272
4275
  if (probeSession) {
4273
4276
  const failedRequests = probeSession.browserConsoleBuffer.filter(isActionableProbeFailure);
@@ -4332,7 +4335,7 @@ function validateNoGpuEncode(config) {
4332
4335
  if (config.useGpu === true) {
4333
4336
  throw new PlanValidationError(
4334
4337
  BROWSER_GPU_NOT_SOFTWARE,
4335
- "[planValidation] GPU encode is banned in distributed mode: config.useGpu === true. Distributed retries must be byte-identical, but NVENC/QSV/VAAPI produce different output across machines. Set useGpu=false (the default) \u2014 software libx264/libx265 is the only supported encoder in distributed mode."
4338
+ "[planValidation] GPU encode is banned in distributed mode: config.useGpu === true. Distributed retries must be byte-identical, but NVENC/QSV/VAAPI produce different output across machines. Set useGpu=false (the default): software libx264/libx265 is the only supported encoder in distributed mode."
4336
4339
  );
4337
4340
  }
4338
4341
  if (config.browserGpuMode !== void 0 && config.browserGpuMode !== "software") {
@@ -4379,6 +4382,7 @@ import {
4379
4382
  detectTransfer,
4380
4383
  extractAllVideoFrames,
4381
4384
  extractMediaMetadata as extractMediaMetadata3,
4385
+ isVideoPastTimelineEnd,
4382
4386
  isHdrColorSpace as isHdrColorSpace2,
4383
4387
  resolveProjectRelativeSrc as resolveProjectRelativeSrc3,
4384
4388
  runVideoExtractionWithRetry,
@@ -4598,6 +4602,9 @@ async function runExtractVideosStage(input) {
4598
4602
  } = input;
4599
4603
  const stage2Start = Date.now();
4600
4604
  const extractionPolicy = resolveVideoExtractionPolicy();
4605
+ composition.videos = composition.videos.filter(
4606
+ (video) => !isVideoPastTimelineEnd(video, composition.duration)
4607
+ );
4601
4608
  let frameLookup = null;
4602
4609
  let extractionResult = null;
4603
4610
  let failureToEnforce = null;
@@ -4684,7 +4691,8 @@ async function runExtractVideosStage(input) {
4684
4691
  timelineEnd: composition.duration,
4685
4692
  maxTransientRetries: extractionPolicy.maxTransientRetries,
4686
4693
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
4687
- deferRangeExtraction
4694
+ deferRangeExtraction,
4695
+ contentKeyedDirs: [join8(compiledDir, REMOTE_MEDIA_SUBDIR)]
4688
4696
  },
4689
4697
  abortSignal,
4690
4698
  cfg,
@@ -6143,7 +6151,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
6143
6151
  const madeProgress = captureAttemptMadeProgress(frameCount, remainingCount);
6144
6152
  if (!madeProgress) {
6145
6153
  options.log.warn(
6146
- "[Render] Capture attempt made no forward progress; composition is likely structurally broken \u2014 not retrying.",
6154
+ "[Render] Capture attempt made no forward progress; composition is likely structurally broken, not retrying.",
6147
6155
  { attempt, frameCount, remainingCount, workers: currentWorkers }
6148
6156
  );
6149
6157
  }
@@ -6212,7 +6220,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
6212
6220
  }
6213
6221
  if (!madeProgress) {
6214
6222
  options.log.warn(
6215
- "[Render] Capture attempt made no forward progress; composition is likely structurally broken \u2014 not retrying.",
6223
+ "[Render] Capture attempt made no forward progress; composition is likely structurally broken, not retrying.",
6216
6224
  { attempt, frameCount, remainingCount, workers: currentWorkers }
6217
6225
  );
6218
6226
  }
@@ -6461,7 +6469,7 @@ function collectPlanAssetShas(planDir) {
6461
6469
  }
6462
6470
  if (compositionHtml === null) {
6463
6471
  throw new Error(
6464
- `[freezePlan] compiled HTML missing at ${COMPILED_INDEX_RELATIVE_PATH} \u2014 upstream compile stage did not materialize the expected file.`
6472
+ `[freezePlan] compiled HTML missing at ${COMPILED_INDEX_RELATIVE_PATH} (upstream compile stage did not materialize the expected file).`
6465
6473
  );
6466
6474
  }
6467
6475
  return { compositionHtml, assets };
@@ -7052,7 +7060,7 @@ var PlanTooLargeError = class extends Error {
7052
7060
  const breakdownSuffix = breakdown ? ` Breakdown: video-frames=${formatBytes(breakdown.videoFramesBytes)}, compiled=${formatBytes(breakdown.compiledBytes)} (source-media=${formatBytes(breakdown.sourceMediaBytes)}), audio=${formatBytes(breakdown.audioBytes)}, metadata=${formatBytes(breakdown.metadataBytes)}, other=${formatBytes(breakdown.otherBytes)}, files=${breakdown.fileCount}.` : "";
7053
7061
  const observationSuffix = observedAt ? ` Observed at ${observedAt}.` : "";
7054
7062
  super(
7055
- `[plan] planDir size ${formatBytes(sizeBytes)} exceeds the configured ceiling ${formatBytes(limitBytes)} (PLAN_TOO_LARGE). The default 2 GB cap fits inside AWS Lambda's 10 GB /tmp budget alongside the chunk worker's frame buffer and ffmpeg's working set. To unblock: use the content-addressed \`planV2()\` transport, shorten the composition, lower the framerate, or use the in-process renderer (\`executeRenderJob\`) \u2014 it has no planDir size cap.` + observationSuffix + breakdownSuffix
7063
+ `[plan] planDir size ${formatBytes(sizeBytes)} exceeds the configured ceiling ${formatBytes(limitBytes)} (PLAN_TOO_LARGE). The default 2 GB cap fits inside AWS Lambda's 10 GB /tmp budget alongside the chunk worker's frame buffer and ffmpeg's working set. To unblock: use the content-addressed \`planV2()\` transport, shorten the composition, lower the framerate, or use the in-process renderer (\`executeRenderJob\`), which has no planDir size cap.` + observationSuffix + breakdownSuffix
7056
7064
  );
7057
7065
  this.name = "PlanTooLargeError";
7058
7066
  this.sizeBytes = sizeBytes;
@@ -7069,7 +7077,7 @@ var FormatNotSupportedInDistributedError = class extends Error {
7069
7077
  reason;
7070
7078
  constructor(format, reason) {
7071
7079
  super(
7072
- `[plan] format ${JSON.stringify(format)} is not supported in distributed mode: ${reason}. Render with the in-process renderer (\`executeRenderJob\`) \u2014 it has full format support \u2014 or pick a distributed-supported format: mp4 SDR, mov ProRes 4444, png-sequence, or webm VP9.`
7080
+ `[plan] format ${JSON.stringify(format)} is not supported in distributed mode: ${reason}. Render with the in-process renderer (\`executeRenderJob\`), which has full format support, or pick a distributed-supported format: mp4 SDR, mov ProRes 4444, png-sequence, or webm VP9.`
7073
7081
  );
7074
7082
  this.name = "FormatNotSupportedInDistributedError";
7075
7083
  this.format = format;
@@ -7161,7 +7169,7 @@ async function readFontSnapshotSha() {
7161
7169
  const data = module.EMBEDDED_FONT_DATA;
7162
7170
  if (!data || typeof data !== "object") {
7163
7171
  throw new Error(
7164
- "[plan] EMBEDDED_FONT_DATA missing from @hyperframes/core/fonts/embedded-data \u2014 was `bun run build:fonts` run?"
7172
+ "[plan] EMBEDDED_FONT_DATA missing from @hyperframes/core/fonts/embedded-data. Was `bun run build:fonts` run?"
7165
7173
  );
7166
7174
  }
7167
7175
  const dataObj = data;
@@ -7211,7 +7219,7 @@ function resolveEncoderTriple(config) {
7211
7219
  }
7212
7220
  if (config.codec !== void 0) {
7213
7221
  throw new Error(
7214
- `[plan] DistributedRenderConfig.codec is only valid for format="mp4"; received codec=${JSON.stringify(config.codec)} with format=${JSON.stringify(config.format)}. Omit codec for non-mp4 formats \u2014 mov is always ProRes 4444, webm is always libvpx-vp9, and png-sequence has no encoder.`
7222
+ `[plan] DistributedRenderConfig.codec is only valid for format="mp4"; received codec=${JSON.stringify(config.codec)} with format=${JSON.stringify(config.format)}. Omit codec for non-mp4 formats: mov is always ProRes 4444, webm is always libvpx-vp9, and png-sequence has no encoder.`
7215
7223
  );
7216
7224
  }
7217
7225
  return resolveNonMp4EncoderTriple(config.format, config.quality ?? "standard");
@@ -9563,10 +9571,10 @@ function validateJsonSafeValue(value, field) {
9563
9571
  walkVariables(value, field, /* @__PURE__ */ new WeakSet());
9564
9572
  }
9565
9573
  var LEAF_REJECTIONS = {
9566
- undefined: "undefined leaves are silently dropped by JSON.stringify \u2014 use null if you mean an absent value",
9574
+ undefined: "undefined leaves are silently dropped by JSON.stringify. Use null if you mean an absent value",
9567
9575
  function: "functions are not JSON-serializable",
9568
9576
  symbol: "Symbols are not JSON-serializable",
9569
- bigint: "BigInt values throw at JSON.stringify \u2014 encode as a string if you need 64-bit integers"
9577
+ bigint: "BigInt values throw at JSON.stringify. Encode as a string if you need 64-bit integers"
9570
9578
  };
9571
9579
  function walkVariables(value, path, seen) {
9572
9580
  const t = typeof value;
@@ -9587,7 +9595,7 @@ function walkVariables(value, path, seen) {
9587
9595
  if (seen.has(value)) {
9588
9596
  throw new InvalidConfigError(
9589
9597
  path,
9590
- "circular reference detected \u2014 JSON.stringify cannot serialize cycles"
9598
+ "circular reference detected: JSON.stringify cannot serialize cycles"
9591
9599
  );
9592
9600
  }
9593
9601
  seen.add(value);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "0.1.0",
3
3
  "buildId": "dev",
4
- "sha256": "aa50e6ddf4edb969b9d47e3c977f01bd291df164527e2f1e5b5a60fd6c32bb34",
4
+ "sha256": "40ff09519c86046d23d0f53d7a6254fb83edc24267cbe13296b6bb185e3b1fa1",
5
5
  "artifacts": {
6
6
  "iife": "hyperframe.runtime.iife.js",
7
7
  "esm": "hyperframe.runtime.mjs"