@hyperframes/producer 0.9.20 → 0.9.22

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.
@@ -351,7 +351,7 @@ function buildVirtualTimeShim(options) {
351
351
  } catch (e) {}
352
352
  }
353
353
  ` : "";
354
- const seekToTimeReseedCall = seedRandomFromFrame ? "reseedRngFromTime(safeTimeMs);\n " : "";
354
+ const seekToTimeReseedCall = seedRandomFromFrame ? "reseedRngFromTime(safeTimeMs);\n " : "";
355
355
  return String.raw`(function() {
356
356
  if (window.__HF_VIRTUAL_TIME__) return;
357
357
 
@@ -430,6 +430,82 @@ ${seededRandomBlock}
430
430
  }
431
431
  };
432
432
 
433
+ // Timers set once rendering has started run on film time: each seek fires every timer due by then, in
434
+ // due order. Until a seek after the page reports ready, timers keep the wall clock: setup code waits on
435
+ // them while the renderer may already be seeking.
436
+ var rendering = false;
437
+ var timerQueue = [];
438
+ var nextTimerId = 1000000000;
439
+ var firingDepth = 0;
440
+ function addTimer(callback, delay, args, repeats) {
441
+ // A timer set from a firing timer waits at least 1 ms, so a zero-delay chain cannot stall a seek.
442
+ var ms = Math.max(firingDepth > 0 ? 1 : 0, Number(delay) || 0);
443
+ var entry = { id: nextTimerId++, callback: callback, args: args, due: virtualNowMs + ms, every: repeats ? Math.max(1, ms) : 0, cancelled: false };
444
+ timerQueue.push(entry);
445
+ return entry.id;
446
+ }
447
+ function cancelTimer(id) {
448
+ for (var i = 0; i < timerQueue.length; i++) {
449
+ if (timerQueue[i].id === id) {
450
+ timerQueue[i].cancelled = true;
451
+ return true;
452
+ }
453
+ }
454
+ return false;
455
+ }
456
+ function fireDueTimers(untilMs) {
457
+ // Frame times are fractional, so a timer due on a frame lands a rounding error past it; it still fires on it.
458
+ untilMs += 0.000001;
459
+ while (true) {
460
+ var next = null;
461
+ for (var i = 0; i < timerQueue.length; i++) {
462
+ var candidate = timerQueue[i];
463
+ if (!candidate.cancelled && candidate.due <= untilMs && (next === null || candidate.due < next.due)) next = candidate;
464
+ }
465
+ if (next === null) break;
466
+ var dueMs = next.due;
467
+ if (next.every) next.due += next.every;
468
+ else next.cancelled = true;
469
+ virtualNowMs = dueMs;
470
+ firingDepth++;
471
+ try {
472
+ next.callback.apply(window, next.args);
473
+ } catch (err) {
474
+ originalSetTimeout(function() { throw err; }, 0);
475
+ }
476
+ firingDepth--;
477
+ }
478
+ timerQueue = timerQueue.filter(function(entry) { return !entry.cancelled; });
479
+ }
480
+ window.setTimeout = function(callback, delay) {
481
+ if (!rendering || typeof callback !== "function") return originalSetTimeout.apply(null, arguments);
482
+ return addTimer(callback, delay, Array.prototype.slice.call(arguments, 2), false);
483
+ };
484
+ window.setInterval = function(callback, delay) {
485
+ if (!rendering || typeof callback !== "function") return originalSetInterval.apply(null, arguments);
486
+ return addTimer(callback, delay, Array.prototype.slice.call(arguments, 2), true);
487
+ };
488
+ window.clearTimeout = function(id) {
489
+ if (!cancelTimer(id)) originalClearTimeout(id);
490
+ };
491
+ window.clearInterval = function(id) {
492
+ if (!cancelTimer(id)) originalClearInterval(id);
493
+ };
494
+
495
+ function seekToTime(nextTimeMs) {
496
+ var safeTimeMs = Math.max(0, Number(nextTimeMs) || 0);
497
+ if (!rendering && window.__renderReady === true) {
498
+ rendering = true;
499
+ // A worker whose first frame is past the start plays the film's first frame first, so timers the
500
+ // page starts there keep the schedule a one-worker render gives them.
501
+ if (safeTimeMs > 0) seekToTime(0);
502
+ }
503
+ ${seekToTimeReseedCall}fireDueTimers(safeTimeMs);
504
+ virtualNowMs = safeTimeMs;
505
+ flushAnimationFrame();
506
+ return virtualNowMs;
507
+ }
508
+
433
509
  window.__HF_VIRTUAL_TIME__ = {
434
510
  originalSetTimeout: originalSetTimeout,
435
511
  originalClearTimeout: originalClearTimeout,
@@ -437,12 +513,7 @@ ${seededRandomBlock}
437
513
  originalClearInterval: originalClearInterval,
438
514
  originalRequestAnimationFrame: originalRequestAnimationFrame,
439
515
  originalCancelAnimationFrame: originalCancelAnimationFrame,
440
- seekToTime: function(nextTimeMs) {
441
- var safeTimeMs = Math.max(0, Number(nextTimeMs) || 0);
442
- virtualNowMs = safeTimeMs;
443
- ${seekToTimeReseedCall}flushAnimationFrame();
444
- return virtualNowMs;
445
- },
516
+ seekToTime: seekToTime,
446
517
  getTime: function() {
447
518
  return virtualNowMs;
448
519
  },
@@ -8372,13 +8443,14 @@ var RenderChunkValidationError = class extends Error {
8372
8443
  this.code = code;
8373
8444
  }
8374
8445
  };
8446
+ var chunkFrameInjectorDependencies = { createVideoFrameInjector: createVideoFrameInjector3 };
8375
8447
  function createChunkVideoFrameInjectorFactory(frameLookup, options) {
8376
8448
  if (!(frameLookup instanceof Promise))
8377
- return () => createVideoFrameInjector3(frameLookup, options);
8449
+ return () => chunkFrameInjectorDependencies.createVideoFrameInjector(frameLookup, options);
8378
8450
  return () => {
8379
8451
  let hook;
8380
8452
  return async (page, time, heldVideoTime) => {
8381
- hook ??= createVideoFrameInjector3(await frameLookup, options);
8453
+ hook ??= chunkFrameInjectorDependencies.createVideoFrameInjector(await frameLookup, options);
8382
8454
  await hook?.(page, time, heldVideoTime);
8383
8455
  };
8384
8456
  };
@@ -9939,7 +10011,7 @@ function assertPlanSizeWithinLimit(input) {
9939
10011
  });
9940
10012
  throw new PlanTooLargeError(breakdown.totalBytes, input.limitBytes, breakdown, input.observedAt);
9941
10013
  }
9942
- function resolveChunkPlan(totalFrames, configChunkSize, maxParallelChunks, targetChunkFrames) {
10014
+ function resolveChunkPlan(totalFrames, configChunkSize, maxParallelChunks, targetChunkFrames, minParallelChunks = 1) {
9943
10015
  assertPositiveInteger("totalFrames", totalFrames);
9944
10016
  assertPositiveInteger("maxParallelChunks", maxParallelChunks);
9945
10017
  if (configChunkSize !== void 0) {
@@ -9948,7 +10020,11 @@ function resolveChunkPlan(totalFrames, configChunkSize, maxParallelChunks, targe
9948
10020
  if (targetChunkFrames !== void 0) {
9949
10021
  assertPositiveInteger("targetChunkFrames", targetChunkFrames);
9950
10022
  }
9951
- const autoSizeParallel = targetChunkFrames === void 0 ? maxParallelChunks : Math.min(maxParallelChunks, Math.max(1, Math.ceil(totalFrames / targetChunkFrames)));
10023
+ assertPositiveInteger("minParallelChunks", minParallelChunks);
10024
+ const autoSizeParallel = targetChunkFrames === void 0 ? maxParallelChunks : Math.min(
10025
+ maxParallelChunks,
10026
+ Math.max(minParallelChunks, Math.ceil(totalFrames / targetChunkFrames))
10027
+ );
9952
10028
  const resolvedChunkSize = configChunkSize ?? Math.max(MIN_CHUNK_SIZE, Math.ceil(totalFrames / autoSizeParallel));
9953
10029
  const naiveCount = Math.ceil(totalFrames / resolvedChunkSize);
9954
10030
  const chunkCount = Math.min(maxParallelChunks, Math.max(1, naiveCount));
@@ -10527,7 +10603,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
10527
10603
  totalFrames,
10528
10604
  config.chunkSize,
10529
10605
  maxParallel,
10530
- config.targetChunkFrames
10606
+ config.targetChunkFrames,
10607
+ config.minParallelChunks
10531
10608
  );
10532
10609
  const chunks = buildPlanChunkSlices(
10533
10610
  frameWeights,
@@ -12803,6 +12880,20 @@ function validateDistributedRenderConfig(config) {
12803
12880
  );
12804
12881
  }
12805
12882
  }
12883
+ if (config.minParallelChunks !== void 0) {
12884
+ if (!Number.isInteger(config.minParallelChunks) || config.minParallelChunks < 1) {
12885
+ throw new InvalidConfigError(
12886
+ "config.minParallelChunks",
12887
+ `must be a positive integer; got ${config.minParallelChunks}`
12888
+ );
12889
+ }
12890
+ if (config.minParallelChunks > MAX_PARALLEL_CHUNKS_CEILING) {
12891
+ throw new InvalidConfigError(
12892
+ "config.minParallelChunks",
12893
+ `must be <= ${MAX_PARALLEL_CHUNKS_CEILING}; got ${config.minParallelChunks}`
12894
+ );
12895
+ }
12896
+ }
12806
12897
  for (const field of ["workerTimeoutSeconds", "workerConcurrency"]) {
12807
12898
  const value = config[field];
12808
12899
  if (value !== void 0 && (!Number.isInteger(value) || value < 1)) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "0.1.0",
3
3
  "buildId": "dev",
4
- "sha256": "347118bef63abc711d082cef8209cc65a539e5e87be2b0ce55474b0240a601c7",
4
+ "sha256": "a42261c262fc9a936c9626d8dabb6459afdde52a8d51986331f622f0e815b4dd",
5
5
  "artifacts": {
6
6
  "iife": "hyperframe.runtime.iife.js",
7
7
  "esm": "hyperframe.runtime.mjs"