hyperframes 0.8.16 → 0.8.18

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.
@@ -50943,16 +50943,6 @@ var init_timingCompiler = __esm({
50943
50943
  });
50944
50944
 
50945
50945
  // ../core/dist/compiler/mediaRenderIds.js
50946
- function hasPlayableSource(el) {
50947
- if (el.getAttribute("src"))
50948
- return true;
50949
- const sources = el.querySelectorAll?.("source[src]");
50950
- if (!sources)
50951
- return false;
50952
- for (const _ of sources)
50953
- return true;
50954
- return false;
50955
- }
50956
50946
  function uniqueRenderId(baseId, taken) {
50957
50947
  if (!taken.has(baseId))
50958
50948
  return baseId;
@@ -50965,8 +50955,6 @@ function assignMediaRenderIds(document2) {
50965
50955
  const taken = /* @__PURE__ */ new Set();
50966
50956
  const pending = [];
50967
50957
  for (const el of document2.querySelectorAll(MEDIA_SELECTOR)) {
50968
- if (!hasPlayableSource(el))
50969
- continue;
50970
50958
  const existing = el.getAttribute(MEDIA_RENDER_ID_ATTR);
50971
50959
  if (existing) {
50972
50960
  taken.add(existing);
@@ -58584,11 +58572,12 @@ function recordCaptureWarnings(session, warnings) {
58584
58572
  function recordSubTimelineWarning(session, timeoutMs) {
58585
58573
  if (session.subTimelineWaitOutcome === "ready" || !session.subTimelineWaitOutcome) return;
58586
58574
  const scriptFailure = session.subTimelineWaitOutcome === "script_failure";
58575
+ const hasRuntimeErrors = session.scriptLoadFailures.some((f) => f.startsWith("runtime-error:"));
58587
58576
  recordCaptureWarnings(session, [
58588
58577
  {
58589
58578
  code: scriptFailure ? "sub_timeline_script_failure" : "sub_timeline_readiness_timeout",
58590
- message: scriptFailure ? "A sub-composition timeline script failed to load" : `Sub-composition timelines did not become ready within ${timeoutMs}ms`,
58591
- details: { timeoutMs }
58579
+ message: scriptFailure ? hasRuntimeErrors ? `A sub-composition script threw during execution \u2014 timeline registration never arrived (${session.scriptLoadFailures.join(", ")})` : `A sub-composition timeline script failed to load (${session.scriptLoadFailures.join(", ")})` : `Sub-composition timelines did not become ready within ${timeoutMs}ms`,
58580
+ details: { timeoutMs, sources: [...session.scriptLoadFailures] }
58592
58581
  }
58593
58582
  ]);
58594
58583
  }
@@ -58672,6 +58661,11 @@ async function initializeSession(session) {
58672
58661
  const diagnostic = formatConsoleDiagnostic(type, text, locationUrl);
58673
58662
  if (!diagnostic.suppressHostLog) console.log(diagnostic.text);
58674
58663
  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
+ }
58675
58669
  });
58676
58670
  page.on("pageerror", (err) => {
58677
58671
  const message = err instanceof Error ? err.message : String(err);
@@ -60863,7 +60857,8 @@ async function extractMediaMetadata(filePath) {
60863
60857
  hasAudio: false,
60864
60858
  isVFR: false,
60865
60859
  hasAlpha: false,
60866
- colorSpace: stillImageMeta.colorSpace
60860
+ colorSpace: stillImageMeta.colorSpace,
60861
+ frames: 1
60867
60862
  };
60868
60863
  }
60869
60864
  throw new Error("[FFmpeg] No video stream found");
@@ -60889,6 +60884,8 @@ async function extractMediaMetadata(filePath) {
60889
60884
  const streamDuration = videoStream.duration ? parseFloat(videoStream.duration) : 0;
60890
60885
  const parsedStreamStart = videoStream.start_time ? parseFloat(videoStream.start_time) : 0;
60891
60886
  const streamStart = Number.isFinite(parsedStreamStart) ? parsedStreamStart : 0;
60887
+ const parsedNbFrames = videoStream.nb_frames ? parseInt(videoStream.nb_frames, 10) : NaN;
60888
+ const frames = Number.isFinite(parsedNbFrames) && parsedNbFrames > 0 ? parsedNbFrames : void 0;
60892
60889
  return {
60893
60890
  durationSeconds: containerDuration,
60894
60891
  videoStreamDurationSeconds: streamDuration > 0 ? streamDuration : containerDuration,
@@ -60900,7 +60897,8 @@ async function extractMediaMetadata(filePath) {
60900
60897
  hasAudio: output?.streams.some((s) => s.codec_type === "audio") ?? false,
60901
60898
  isVFR,
60902
60899
  hasAlpha,
60903
- colorSpace
60900
+ colorSpace,
60901
+ frames
60904
60902
  };
60905
60903
  })();
60906
60904
  videoMetadataCache.set(filePath, probePromise);
@@ -85230,11 +85228,23 @@ import {
85230
85228
  rmSync as rmSync7
85231
85229
  } from "fs";
85232
85230
  import { basename as basename5, dirname as dirname10, extname as extname5, join as join21, resolve as resolve9 } from "path";
85231
+
85232
+ // ../producer/src/utils/ffprobe.ts
85233
+ init_src();
85234
+
85235
+ // ../producer/src/services/render/artifactTransaction.ts
85233
85236
  var defaultFileSystem = {
85234
85237
  existsSync: existsSync19,
85235
85238
  renameSync: renameSync3,
85236
85239
  rmSync: rmSync7
85237
85240
  };
85241
+ async function defaultArtifactDurationProbe(path) {
85242
+ const meta = await extractMediaMetadata(path);
85243
+ return {
85244
+ durationSeconds: meta.durationSeconds,
85245
+ frames: meta.frames
85246
+ };
85247
+ }
85238
85248
  function createSiblingTransactionDirectory(destination) {
85239
85249
  const parent = dirname10(destination);
85240
85250
  const extension = extname5(destination);
@@ -85265,14 +85275,44 @@ function collectDirectoryFiles(root) {
85265
85275
  visit(root);
85266
85276
  return files;
85267
85277
  }
85278
+ function durationToleranceSeconds(expected) {
85279
+ if (expected.toleranceSeconds !== void 0) return expected.toleranceSeconds;
85280
+ const fps = expected.fps;
85281
+ return fps && fps > 0 ? 1 / fps : 0.02;
85282
+ }
85283
+ function assertUsableProbedDuration(probedSeconds, expectedSeconds, stagingPath) {
85284
+ if (!Number.isFinite(probedSeconds) || probedSeconds <= 0) {
85285
+ throw new Error(
85286
+ `Render artifact duration probe returned no usable duration for ${stagingPath} (got ${String(probedSeconds)}); expected ${expectedSeconds.toFixed(3)}s. Refusing to publish an artifact whose duration cannot be verified.`
85287
+ );
85288
+ }
85289
+ }
85290
+ function assertDurationWithinTolerance(expectedSeconds, probedSeconds, toleranceSeconds, stagingPath) {
85291
+ const deficit = expectedSeconds - probedSeconds;
85292
+ if (deficit <= toleranceSeconds) return;
85293
+ throw new Error(
85294
+ `Render artifact is truncated: expected ${expectedSeconds.toFixed(3)}s, probed ${probedSeconds.toFixed(3)}s (deficit ${deficit.toFixed(3)}s exceeds tolerance ${toleranceSeconds.toFixed(3)}s). Artifact: ${stagingPath}`
85295
+ );
85296
+ }
85297
+ function assertFrameCountWithinTolerance(expectedFrames, probedFrames, stagingPath) {
85298
+ if (probedFrames === void 0 || !Number.isFinite(probedFrames) || probedFrames <= 0) {
85299
+ return;
85300
+ }
85301
+ const shortfall = expectedFrames - probedFrames;
85302
+ if (shortfall <= 1) return;
85303
+ throw new Error(
85304
+ `Render artifact is truncated: expected ${expectedFrames} frames, probed ${probedFrames} frames (shortfall ${shortfall} frames). Artifact: ${stagingPath}`
85305
+ );
85306
+ }
85268
85307
  var ArtifactTransaction = class {
85269
- constructor(destinationPath, kind, fileSystem = defaultFileSystem) {
85308
+ constructor(destinationPath, kind, fileSystem = defaultFileSystem, durationProbe = defaultArtifactDurationProbe) {
85270
85309
  this.kind = kind;
85271
85310
  this.fileSystem = fileSystem;
85272
85311
  this.destinationPath = resolve9(destinationPath);
85273
85312
  this.transactionDirectory = createSiblingTransactionDirectory(this.destinationPath);
85274
85313
  this.stagingPath = join21(this.transactionDirectory, basename5(this.destinationPath));
85275
85314
  this.backupPath = join21(this.transactionDirectory, "backup");
85315
+ this.durationProbe = durationProbe;
85276
85316
  }
85277
85317
  kind;
85278
85318
  fileSystem;
@@ -85281,9 +85321,11 @@ var ArtifactTransaction = class {
85281
85321
  transactionDirectory;
85282
85322
  backupPath;
85283
85323
  state = "active";
85284
- validate() {
85324
+ durationProbe;
85325
+ async validate(expected) {
85285
85326
  if (this.kind === "file") {
85286
85327
  assertReadableNonEmptyFile(this.stagingPath);
85328
+ if (expected) await this.assertArtifactDuration(expected);
85287
85329
  return;
85288
85330
  }
85289
85331
  let files;
@@ -85299,11 +85341,37 @@ var ArtifactTransaction = class {
85299
85341
  }
85300
85342
  for (const file of files) assertReadableNonEmptyFile(file);
85301
85343
  }
85302
- commit() {
85344
+ async assertArtifactDuration(expected) {
85345
+ const expectedSeconds = expected.expectedDurationSeconds;
85346
+ if (!Number.isFinite(expectedSeconds) || expectedSeconds <= 0) return;
85347
+ const probed = await this.runDurationProbe();
85348
+ assertUsableProbedDuration(probed.durationSeconds, expectedSeconds, this.stagingPath);
85349
+ const toleranceSeconds = durationToleranceSeconds(expected);
85350
+ assertDurationWithinTolerance(
85351
+ expectedSeconds,
85352
+ probed.durationSeconds,
85353
+ toleranceSeconds,
85354
+ this.stagingPath
85355
+ );
85356
+ if (expected.expectedFrames !== void 0) {
85357
+ assertFrameCountWithinTolerance(expected.expectedFrames, probed.frames, this.stagingPath);
85358
+ }
85359
+ }
85360
+ async runDurationProbe() {
85361
+ try {
85362
+ return await this.durationProbe(this.stagingPath);
85363
+ } catch (error) {
85364
+ throw new Error(
85365
+ `Render artifact duration probe failed for ${this.stagingPath}: ${error instanceof Error ? error.message : String(error)}`,
85366
+ { cause: error }
85367
+ );
85368
+ }
85369
+ }
85370
+ async commit() {
85303
85371
  if (this.state !== "active") {
85304
85372
  throw new Error(`Cannot commit an artifact transaction in state ${this.state}`);
85305
85373
  }
85306
- this.validate();
85374
+ await this.validate();
85307
85375
  const hadDestination = this.fileSystem.existsSync(this.destinationPath);
85308
85376
  if (this.kind === "file" || !hadDestination) {
85309
85377
  this.fileSystem.renameSync(this.stagingPath, this.destinationPath);
@@ -86510,9 +86578,6 @@ init_assetResolution();
86510
86578
  import { createReadStream as createReadStream4, existsSync as existsSync25, mkdirSync as mkdirSync13, readFileSync as readFileSync10 } from "fs";
86511
86579
  import { join as join27, dirname as dirname13, resolve as resolve12, basename as basename6 } from "path";
86512
86580
 
86513
- // ../producer/src/utils/ffprobe.ts
86514
- init_src();
86515
-
86516
86581
  // ../producer/src/services/renderMediaCollector.ts
86517
86582
  init_esm10();
86518
86583
  init_dist2();
@@ -89639,7 +89704,7 @@ async function discoverMediaFromBrowser(page) {
89639
89704
  const htmlEl = el;
89640
89705
  const isImage = htmlEl.tagName.toLowerCase() === "img";
89641
89706
  const tagName19 = isImage ? "image" : htmlEl.tagName.toLowerCase() === "video" ? "video" : "audio";
89642
- const id = htmlEl.id || (isImage ? autoImageIds.get(htmlEl) : void 0);
89707
+ const id = htmlEl.getAttribute("data-hf-render-id") || htmlEl.id || (isImage ? autoImageIds.get(htmlEl) : void 0);
89643
89708
  if (!id) return;
89644
89709
  const src = htmlEl.currentSrc || htmlEl.src || htmlEl.getAttribute("src") || "";
89645
89710
  const start = parseFloat(htmlEl.getAttribute("data-start") || "0");
@@ -89686,7 +89751,7 @@ async function discoverAudioVolumeAutomationFromTimeline(page, audioIds, composi
89686
89751
  const sampleStep = 1 / Math.min(60, Math.max(1, sampleFps));
89687
89752
  const rawWindows = await page.evaluate((ids) => {
89688
89753
  return ids.flatMap((id) => {
89689
- const el = document.getElementById(id) ?? document.getElementById(id.replace(/-audio$/, ""));
89754
+ const el = window.__hfMediaEl?.(id) ?? document.getElementById(id) ?? document.getElementById(id.replace(/-audio$/, ""));
89690
89755
  if (!(el instanceof HTMLAudioElement) && !(el instanceof HTMLVideoElement)) return [];
89691
89756
  return [
89692
89757
  {
@@ -89745,7 +89810,7 @@ async function discoverAudioVolumeAutomationFromTimeline(page, audioIds, composi
89745
89810
  }
89746
89811
  };
89747
89812
  for (const { id, start, end } of clips2) {
89748
- const el = document.getElementById(id) ?? document.getElementById(id.replace(/-audio$/, ""));
89813
+ const el = window.__hfMediaEl?.(id) ?? document.getElementById(id) ?? document.getElementById(id.replace(/-audio$/, ""));
89749
89814
  if (!(el instanceof HTMLAudioElement) && !(el instanceof HTMLVideoElement)) continue;
89750
89815
  const sampleStart = Math.max(0, start);
89751
89816
  const sampleEnd = Math.min(duration, end);
@@ -89821,7 +89886,7 @@ async function discoverVideoVisibilityFromTimeline(page, compositionDuration) {
89821
89886
  const BINARY_PRECISION = 1 / 60;
89822
89887
  const entries2 = [];
89823
89888
  for (const videoEl of videos) {
89824
- const id = videoEl.id;
89889
+ const id = videoEl.getAttribute?.("data-hf-render-id") || videoEl.id;
89825
89890
  if (!id) continue;
89826
89891
  entries2.push({
89827
89892
  id,
@@ -90633,7 +90698,7 @@ function shouldCopyExtractedFrames(platform3) {
90633
90698
  }
90634
90699
  function resolveVideoExtractionPolicy(env = process.env) {
90635
90700
  const rawMode = env.HF_VIDEO_EXTRACTION_FAILURE_MODE?.trim().toLowerCase();
90636
- const failureMode = rawMode === "observe" || rawMode === "enforce" ? rawMode : "off";
90701
+ const failureMode = rawMode === "observe" || rawMode === "off" ? rawMode : "enforce";
90637
90702
  const maxTransientRetries = failureMode !== "off" && env.HF_VIDEO_EXTRACTION_MAX_RETRIES?.trim() === "1" ? 1 : 0;
90638
90703
  return { failureMode, maxTransientRetries };
90639
90704
  }
@@ -94305,7 +94370,10 @@ function applyRenderWarningPolicy(job, captureWarnings, log = defaultLogger) {
94305
94370
  const hasAudioProcessingFailure = job.warnings.some(
94306
94371
  (warning) => warning.code === "audio_processing_failed"
94307
94372
  );
94308
- if (strictness === "strict" || hasAudioProcessingFailure) {
94373
+ const hasSubTimelineScriptFailure = job.warnings.some(
94374
+ (warning) => warning.code === "sub_timeline_script_failure"
94375
+ );
94376
+ if (strictness === "strict" || hasAudioProcessingFailure || hasSubTimelineScriptFailure) {
94309
94377
  throw new RenderQualityError(job.warnings);
94310
94378
  }
94311
94379
  }
@@ -96097,7 +96165,13 @@ async function executeRenderPipeline(input) {
96097
96165
  } else {
96098
96166
  observability.checkpoint("assemble", `skipped for ${outputFormat}`);
96099
96167
  }
96100
- artifactTransaction.validate();
96168
+ await artifactTransaction.validate(
96169
+ !isPngSequence && !isGif && Number.isFinite(job.duration) && job.duration > 0 ? {
96170
+ expectedDurationSeconds: job.duration,
96171
+ fps: fpsToNumber(job.config.fps),
96172
+ expectedFrames: captureTotalFrames
96173
+ } : void 0
96174
+ );
96101
96175
  const totalElapsed = Date.now() - pipelineStart;
96102
96176
  const tmpPeakBytes = existsSync30(workDir) ? sampleDirectoryBytes(workDir) : 0;
96103
96177
  recordTransientRetryObservability();
@@ -96169,7 +96243,7 @@ async function executeRenderPipeline(input) {
96169
96243
  copyFileSync6(stagedOutputPath, debugOutput);
96170
96244
  }
96171
96245
  }
96172
- artifactTransaction.commit();
96246
+ await artifactTransaction.commit();
96173
96247
  job.outputPath = outputPath;
96174
96248
  updateJobStatus(job, "complete", "Render complete", 100, onProgress);
96175
96249
  await eventPublisher.flush();
@@ -97515,8 +97589,8 @@ async function runFontLocalize(io, localize) {
97515
97589
  }
97516
97590
 
97517
97591
  // src/version.ts
97518
- var VERSION = true ? "0.8.16" : "0.0.0-dev";
97519
- var PRODUCER_VERSION = true ? "0.8.16" : "0.0.0-dev";
97592
+ var VERSION = true ? "0.8.18" : "0.0.0-dev";
97593
+ var PRODUCER_VERSION = true ? "0.8.18" : "0.0.0-dev";
97520
97594
 
97521
97595
  // src/fontLocalizeCli.ts
97522
97596
  async function readStdin() {