@hyperframes/producer 0.9.3 → 0.9.5

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.
@@ -17,7 +17,11 @@ import crypto from "node:crypto";
17
17
  import { Hono as Hono2 } from "hono";
18
18
  import { streamSSE } from "hono/streaming";
19
19
  import { serve as serve2 } from "@hono/node-server";
20
- import { fetchPublicHttpsText as fetchPublicHttpsText2 } from "@hyperframes/engine";
20
+ import {
21
+ fetchPublicHttpsText as fetchPublicHttpsText2,
22
+ REFERENCE_BACKGROUND_FIELD as REFERENCE_BACKGROUND_FIELD3,
23
+ referenceBackgroundResult as referenceBackgroundResult5
24
+ } from "@hyperframes/engine";
21
25
 
22
26
  // src/services/renderOrchestrator.ts
23
27
  import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
@@ -77,6 +81,10 @@ import {
77
81
  augmentProtocolTimeoutError,
78
82
  augmentPageNavigationTimeoutError
79
83
  } from "@hyperframes/engine";
84
+ import {
85
+ referenceBackgroundResult,
86
+ createReferenceBackgroundRecorder
87
+ } from "@hyperframes/engine";
80
88
  import { join as join28, dirname as dirname12, resolve as resolve8 } from "path";
81
89
  import { totalmem as totalmem2 } from "node:os";
82
90
  import { randomUUID } from "crypto";
@@ -8818,6 +8826,7 @@ async function runProbeStage(input) {
8818
8826
  const motionBlur = job.config.motionBlur;
8819
8827
  const captureFormat = resolveCaptureImageFormat({ needsAlpha, motionBlur });
8820
8828
  const captureOpts = {
8829
+ recordReferenceBackground: input.recordReferenceBackground,
8821
8830
  width,
8822
8831
  height,
8823
8832
  fps: job.config.fps,
@@ -12459,6 +12468,17 @@ function applyRenderWarningPolicy(job, captureWarnings, log = defaultLogger) {
12459
12468
  throw new RenderQualityError(job.warnings);
12460
12469
  }
12461
12470
  }
12471
+ var stopPrepSessionOnScriptFailure = (options) => ({
12472
+ ...options,
12473
+ onSubTimelineScriptFailure: () => {
12474
+ throw new Error("a composition script failed, so this preparation session stops");
12475
+ }
12476
+ });
12477
+ var surfaceScriptFailure = (error, job) => error instanceof RenderQualityError || error instanceof RenderCancelledError || !job.warnings?.some((warning) => warning.code === "sub_timeline_script_failure") ? error : new RenderQualityError(job.warnings);
12478
+ var withScriptFailureStop = (options, job, log) => ({
12479
+ ...options,
12480
+ onSubTimelineScriptFailure: (warnings) => applyRenderWarningPolicy(job, warnings, log)
12481
+ });
12462
12482
  var RenderCancelledError = class extends Error {
12463
12483
  reason;
12464
12484
  constructor(message = "render_cancelled", reason = "aborted") {
@@ -12826,6 +12846,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
12826
12846
  }
12827
12847
  function createRenderJob(config) {
12828
12848
  return {
12849
+ ...referenceBackgroundResult(void 0),
12829
12850
  id: randomUUID(),
12830
12851
  config: {
12831
12852
  ...config,
@@ -13591,6 +13612,9 @@ async function executeRenderPipeline(input) {
13591
13612
  cfg.protocolTimeout = scaledProtocolTimeout;
13592
13613
  updateCaptureObservability({ protocolTimeoutMs: scaledProtocolTimeout });
13593
13614
  }
13615
+ const recordReferenceBackground = createReferenceBackgroundRecorder(
13616
+ (result) => Object.assign(job, result)
13617
+ );
13594
13618
  const subTimelineWaitMemo = {};
13595
13619
  const probeResult = await observeRenderStage(
13596
13620
  observability,
@@ -13611,7 +13635,8 @@ async function executeRenderPipeline(input) {
13611
13635
  height,
13612
13636
  needsAlpha,
13613
13637
  deviceScaleFactor,
13614
- subTimelineWaitMemo
13638
+ subTimelineWaitMemo,
13639
+ recordReferenceBackground
13615
13640
  }),
13616
13641
  // Browser probe is pre-capture; report `browser calibrating` so a
13617
13642
  // slow probe (~64s SwiftShader warm-up on Windows was the reported
@@ -13867,6 +13892,7 @@ async function executeRenderPipeline(input) {
13867
13892
  let routePageMotionBlur;
13868
13893
  const buildCaptureOptions = () => ({
13869
13894
  ...captureOptions,
13895
+ recordReferenceBackground,
13870
13896
  ...routePageMotionBlur ? { pageMotionBlur: routePageMotionBlur } : {},
13871
13897
  videoMetadataHints,
13872
13898
  skipReadinessVideoIds: videoReadinessSkipIds,
@@ -13899,6 +13925,8 @@ async function executeRenderPipeline(input) {
13899
13925
  });
13900
13926
  }
13901
13927
  });
13928
+ const buildPrepCaptureOptions = () => stopPrepSessionOnScriptFailure(buildCaptureOptions());
13929
+ const buildWorkerCaptureOptions = () => withScriptFailureStop(buildCaptureOptions(), job, log);
13902
13930
  const createRenderVideoFrameInjector = () => createVideoFrameInjector(frameLookup, frameCacheOptions(cfg, workerCount));
13903
13931
  let captureCalibration;
13904
13932
  const htmlInCanvasDetected = compiled.renderModeHints.reasons.some(
@@ -14002,7 +14030,7 @@ async function executeRenderPipeline(input) {
14002
14030
  probeSession = await createCaptureSession8(
14003
14031
  activeFileServer.url,
14004
14032
  framesDir,
14005
- buildCaptureOptions(),
14033
+ buildPrepCaptureOptions(),
14006
14034
  null,
14007
14035
  { ...cfg, forceScreenshot: captureForceScreenshot }
14008
14036
  );
@@ -14085,7 +14113,7 @@ async function executeRenderPipeline(input) {
14085
14113
  totalFrames,
14086
14114
  forceScreenshot: captureForceScreenshot,
14087
14115
  probeSession,
14088
- buildCaptureOptions,
14116
+ buildCaptureOptions: buildPrepCaptureOptions,
14089
14117
  createRenderVideoFrameInjector,
14090
14118
  assertNotAborted
14091
14119
  }),
@@ -14557,7 +14585,7 @@ async function executeRenderPipeline(input) {
14557
14585
  effectiveBitrate,
14558
14586
  deliveryEncoder,
14559
14587
  fileServer: activeFileServer,
14560
- buildCaptureOptions,
14588
+ buildCaptureOptions: buildWorkerCaptureOptions,
14561
14589
  createRenderVideoFrameInjector,
14562
14590
  hdrDiagnostics,
14563
14591
  abortSignal: attempt,
@@ -14675,7 +14703,7 @@ async function executeRenderPipeline(input) {
14675
14703
  // Each segment sets its own GOP (at most the interval); hls never reaches this route.
14676
14704
  ...deliveryEncoder
14677
14705
  },
14678
- buildCaptureOptions,
14706
+ buildCaptureOptions: buildWorkerCaptureOptions,
14679
14707
  createRenderVideoFrameInjector,
14680
14708
  abortSignal: executionSignal,
14681
14709
  assertNotAborted,
@@ -14823,7 +14851,7 @@ async function executeRenderPipeline(input) {
14823
14851
  // encoder's open-GOP output untouched.
14824
14852
  ...deliveryEncoder
14825
14853
  },
14826
- buildCaptureOptions,
14854
+ buildCaptureOptions: buildWorkerCaptureOptions,
14827
14855
  createRenderVideoFrameInjector,
14828
14856
  abortSignal: executionSignal,
14829
14857
  assertNotAborted,
@@ -15008,7 +15036,7 @@ async function executeRenderPipeline(input) {
15008
15036
  probeSession,
15009
15037
  captureAttempts,
15010
15038
  dedupPerfs,
15011
- buildCaptureOptions,
15039
+ buildCaptureOptions: buildWorkerCaptureOptions,
15012
15040
  createRenderVideoFrameInjector,
15013
15041
  abortSignal: executionSignal,
15014
15042
  assertNotAborted,
@@ -15241,7 +15269,7 @@ async function executeRenderPipeline(input) {
15241
15269
  await eventPublisher.flush();
15242
15270
  } catch (caught) {
15243
15271
  await pendingAudio;
15244
- const error = tooSlowError ?? caught;
15272
+ const error = surfaceScriptFailure(tooSlowError ?? caught, job);
15245
15273
  if (!tooSlowError && (error instanceof RenderCancelledError || executionSignal?.aborted)) {
15246
15274
  job.error = error instanceof Error ? error.message : "render_cancelled";
15247
15275
  updateJobStatus(job, "cancelled", "Render cancelled", job.progress, onProgress);
@@ -15568,6 +15596,11 @@ import {
15568
15596
  import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
15569
15597
 
15570
15598
  // src/services/distributed/plan.ts
15599
+ import {
15600
+ REFERENCE_BACKGROUND_FIELD as REFERENCE_BACKGROUND_FIELD2,
15601
+ referenceBackgroundResult as referenceBackgroundResult4,
15602
+ createReferenceBackgroundRecorder as createReferenceBackgroundRecorder2
15603
+ } from "@hyperframes/engine";
15571
15604
  import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframes/engine";
15572
15605
  import { extname as extname4, isAbsolute as isAbsolute4, join as join31, relative as relative4, sep as sep3 } from "node:path";
15573
15606
  import {
@@ -15613,6 +15646,20 @@ var execFile = promisify2(execFileCallback);
15613
15646
  // src/services/distributed/chunkWeights.ts
15614
15647
  import { resolveFrameFormat } from "@hyperframes/engine";
15615
15648
 
15649
+ // src/services/render/planReferenceBackground.ts
15650
+ import {
15651
+ closeCaptureSession as closeCaptureSession10,
15652
+ createCaptureSession as createCaptureSession10,
15653
+ readReferenceBackground,
15654
+ referenceBackgroundResult as referenceBackgroundResult2
15655
+ } from "@hyperframes/engine";
15656
+
15657
+ // src/services/render/stages/freezePlan.ts
15658
+ import {
15659
+ REFERENCE_BACKGROUND_FIELD,
15660
+ referenceBackgroundResult as referenceBackgroundResult3
15661
+ } from "@hyperframes/engine";
15662
+
15616
15663
  // src/services/distributed/planProtocol.ts
15617
15664
  var PLAN_SCHEMA_VERSION = 1;
15618
15665
  var PLAN_ARTIFACT_LAYOUT = "plan-dir-v1";
@@ -16385,6 +16432,7 @@ function createRenderHandlers(options = {}) {
16385
16432
  outputUrl,
16386
16433
  fileSize,
16387
16434
  durationMs,
16435
+ ...referenceBackgroundResult5(job[REFERENCE_BACKGROUND_FIELD3]),
16388
16436
  videoDurationSeconds: job.duration ?? null,
16389
16437
  outcome: job.outcome ?? "completed",
16390
16438
  warnings: job.warnings,
@@ -16472,6 +16520,7 @@ function createRenderHandlers(options = {}) {
16472
16520
  outputToken,
16473
16521
  outputUrl,
16474
16522
  fileSize,
16523
+ ...referenceBackgroundResult5(job[REFERENCE_BACKGROUND_FIELD3]),
16475
16524
  videoDurationSeconds: job.duration ?? null,
16476
16525
  outcome: job.outcome ?? "completed",
16477
16526
  warnings: job.warnings,
@@ -1,23 +1,4 @@
1
- /**
2
- * Activity C of the distributed render pipeline.
3
- *
4
- * `assemble(planDir, chunkPaths, audioPath, outputPath)` stitches per-chunk
5
- * outputs into the final deliverable. For mp4 / mov / webm this is
6
- * `ffmpeg -f concat -c copy` (free of re-encode loss because every
7
- * chunk's first frame is an IDR keyframe — the chunk encoder sets
8
- * `lockGopForChunkConcat` to enforce this, which for libvpx-vp9 also
9
- * disables alt-ref frames so concat seams remain independently
10
- * decodable). For png-sequence chunks (each chunk is a directory of
11
- * frames) this is a straight directory merge with global re-numbering.
12
- *
13
- * Join, audio mux and mp4 faststart run as one ffmpeg pass over the concat
14
- * demuxer, so disk holds the chunks plus one output copy. Audio is first
15
- * pad-or-trimmed to `frameCount / fps` (frames summed over the chunks) so
16
- * the mux adds no drift at the end of long renders.
17
- *
18
- * Pure function over local paths. No networking. The caller is responsible
19
- * for moving `outputPath` to its orchestration-level storage.
20
- */
1
+ import { type ReferenceBackgroundResult } from "@hyperframes/engine";
21
2
  import { type ProducerLogger } from "../../logger.js";
22
3
  import type { LockedRenderConfig } from "../render/stages/freezePlan.js";
23
4
  /**
@@ -25,7 +6,7 @@ import type { LockedRenderConfig } from "../render/stages/freezePlan.js";
25
6
  * (mp4/mov) or the cumulative byte total of the frame directory
26
7
  * (png-sequence).
27
8
  */
28
- export interface AssembleResult {
9
+ export interface AssembleResult extends ReferenceBackgroundResult {
29
10
  outputPath: string;
30
11
  durationMs: number;
31
12
  framesEncoded: number;
@@ -1,3 +1,4 @@
1
+ import { type ReferenceBackgroundResult } from "@hyperframes/engine";
1
2
  import { type CanvasResolution, type DistributedFps } from "@hyperframes/core";
2
3
  import { type EngineConfig, type VideoFrameFormat, type ProresProfile, type AudioProcessingFailure } from "@hyperframes/engine";
3
4
  import { type ProducerLogger } from "../../logger.js";
@@ -185,7 +186,7 @@ export interface DistributedRenderConfig extends Pick<RenderRequestOptions, "pag
185
186
  variables?: Record<string, unknown>;
186
187
  }
187
188
  /** Shared local representation consumed by both distributed plan transports. */
188
- export interface LocalExecutionPlan {
189
+ export interface LocalExecutionPlan extends Partial<ReferenceBackgroundResult> {
189
190
  executionPlanDir: string;
190
191
  executionPlanHash: string;
191
192
  chunkCount: number;
@@ -240,7 +241,7 @@ export declare function logPlanStage(log: ProducerLogger, stage: string, started
240
241
  * @deprecated Use `planV2()` or `planV2WithPublisher()` for new integrations.
241
242
  * The v1 result remains supported for existing transports.
242
243
  */
243
- export interface PlanResult {
244
+ export interface PlanResult extends Partial<ReferenceBackgroundResult> {
244
245
  planDir: string;
245
246
  planProtocol: Readonly<PlanProtocolV1Descriptor>;
246
247
  planHash: string;
@@ -1,15 +1,4 @@
1
- /**
2
- * Content-addressed distributed plan transport.
3
- *
4
- * V2 deliberately separates transport from execution. The transport root is
5
- * a small immutable `plan.json` manifest plus sha256-addressed blobs. Workers
6
- * select and materialize only the dependencies for their role, then invoke
7
- * the shared execution functions on the verified local layout.
8
- *
9
- * Video-frame dependencies are derived by evaluating the engine's own
10
- * FrameLookupTable at every captured global frame. If legacy video metadata
11
- * is absent, v2 explicitly falls back to the full-source pack.
12
- */
1
+ import { type ReferenceBackgroundResult } from "@hyperframes/engine";
13
2
  import { type DistributedFps } from "@hyperframes/core";
14
3
  import { type BuildLocalExecutionPlanOptions, type DistributedRenderConfig } from "./plan.js";
15
4
  import { type PlanProtocolV2Descriptor } from "./planProtocol.js";
@@ -58,7 +47,7 @@ export interface PlanV2Manifest {
58
47
  export interface PlanV2Limitations {
59
48
  readonly videoDependencyMode: "exact-rendered-frames" | "full-source-pack" | "source-extract";
60
49
  }
61
- export interface PlanV2Result {
50
+ export interface PlanV2Result extends ReferenceBackgroundResult {
62
51
  readonly planDir: string;
63
52
  readonly manifestPath: string;
64
53
  readonly planProtocol: Readonly<PlanProtocolV2Descriptor>;
@@ -1,5 +1,6 @@
1
+ import { type ReferenceBackgroundResult } from "@hyperframes/engine";
1
2
  import { type DistributedRenderConfig } from "./plan.js";
2
- export interface RenderSpanResult {
3
+ export interface RenderSpanResult extends ReferenceBackgroundResult {
3
4
  startFrame: number;
4
5
  endFrame: number;
5
6
  framesEncoded: number;
@@ -0,0 +1,12 @@
1
+ import { type CaptureOptions, type EngineConfig, type ReferenceBackgroundResult } from "@hyperframes/engine";
2
+ import type { ProducerLogger } from "../../logger.js";
3
+ interface PlanReferenceInput {
4
+ projectDir: string;
5
+ workDir: string;
6
+ cfg: EngineConfig;
7
+ options: CaptureOptions;
8
+ log: ProducerLogger;
9
+ }
10
+ /** Optional evidence must not add capture capabilities to a browser-free planner. */
11
+ export declare function readPlanReferenceBackground(input: PlanReferenceInput): Promise<ReferenceBackgroundResult>;
12
+ export {};
@@ -10,7 +10,7 @@
10
10
  * has in memory.
11
11
  */
12
12
  import type { Fps } from "@hyperframes/core";
13
- import type { ProresProfile } from "@hyperframes/engine";
13
+ import { type ReferenceBackgroundResult, type ProresProfile } from "@hyperframes/engine";
14
14
  import type { RenderRequestOptions } from "../../../renderRequest.js";
15
15
  import { type PlanDimensions } from "./planHash.js";
16
16
  /**
@@ -78,7 +78,7 @@ export interface LockedRenderConfig extends Pick<RenderRequestOptions, "pageMoti
78
78
  chunksCarryAudio?: true;
79
79
  separateAudio?: true;
80
80
  }
81
- export interface CompositionMetadataJson {
81
+ export interface CompositionMetadataJson extends Partial<ReferenceBackgroundResult> {
82
82
  durationSeconds: number;
83
83
  width: number;
84
84
  height: number;
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * probeStage — browser probe + recompile + media reconciliation.
3
3
  *
4
- * Runs only when `needsBrowser` is true (root duration unknown OR there are
5
- * unresolved nested compositions). Owns the `FileServerHandle` and the
4
+ * Runs when media discovery needs a browser. Owns the `FileServerHandle` and the
6
5
  * `CaptureSession` it creates and returns them so the sequencer can both
7
6
  * reuse them downstream (the capture stage reuses the probe session) and
8
7
  * clean them up in its `finally` block.
@@ -26,7 +25,7 @@
26
25
  * regex, the same first-10/first-5 slicing, and the same `console.warn`
27
26
  * prefix.
28
27
  */
29
- import { type CaptureSession, type CompositionLengthSource, type EngineConfig, type SubTimelineWaitMemo } from "@hyperframes/engine";
28
+ import { type CaptureOptions, type CaptureSession, type CompositionLengthSource, type EngineConfig, type SubTimelineWaitMemo } from "@hyperframes/engine";
30
29
  import type { CompiledComposition } from "../../htmlCompiler.js";
31
30
  import { type FileServerHandle } from "../../fileServer.js";
32
31
  import type { ProducerLogger } from "../../../logger.js";
@@ -35,6 +34,7 @@ import type { RenderJob } from "../../renderOrchestrator.js";
35
34
  export interface ProbeStageInput {
36
35
  projectDir: string;
37
36
  workDir: string;
37
+ recordReferenceBackground?: CaptureOptions["recordReferenceBackground"];
38
38
  job: RenderJob;
39
39
  cfg: EngineConfig;
40
40
  /**
@@ -1,5 +1,6 @@
1
1
  import { type CanvasResolution, type Fps, type FpsInput, type HfVfxCapture } from "@hyperframes/core";
2
2
  import { type EngineConfig, type ExtractionPhaseBreakdown, type VideoFrameFormat, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ProresProfile, type ParallelProgress, type WorkerTask, type CapturePerfSummary, type CaptureWarning, type SubTimelineWaitOutcome, type WorkerSizing, type StaticVerificationOutcome, explainDrawElementDisabled, type CaptureFailureKind, type MotionBlurOptions } from "@hyperframes/engine";
3
+ import { type ReferenceBackgroundResult } from "@hyperframes/engine";
3
4
  import { type ProducerLogger } from "../logger.js";
4
5
  import { type RenderOutputFormat } from "./render/renderFormat.js";
5
6
  import { type RenderCaptureObservability, type RenderObservabilitySummary, type OpenStallAttempt, type StageStallRule } from "./render/observability.js";
@@ -429,7 +430,7 @@ export interface RenderStageProgress {
429
430
  done?: number;
430
431
  total?: number;
431
432
  }
432
- export interface RenderJob {
433
+ export interface RenderJob extends Partial<ReferenceBackgroundResult> {
433
434
  id: string;
434
435
  config: RenderConfig;
435
436
  status: RenderStatus;
@@ -468,6 +469,9 @@ export declare class RenderQualityError extends Error {
468
469
  constructor(warnings: readonly RenderWarning[]);
469
470
  }
470
471
  export declare function applyRenderWarningPolicy(job: RenderJob, captureWarnings: readonly CaptureWarning[], log?: ProducerLogger): void;
472
+ export declare const stopPrepSessionOnScriptFailure: (options: CaptureOptions) => CaptureOptions;
473
+ export declare const surfaceScriptFailure: (error: unknown, job: RenderJob) => unknown;
474
+ export declare const withScriptFailureStop: (options: CaptureOptions, job: RenderJob, log: ProducerLogger) => CaptureOptions;
471
475
  export declare class RenderCancelledError extends Error {
472
476
  reason: "user_cancelled" | "timeout" | "aborted";
473
477
  constructor(message?: string, reason?: "user_cancelled" | "timeout" | "aborted");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,11 +35,11 @@
35
35
  "postcss": "^8.4.0",
36
36
  "puppeteer": "~25.11.0",
37
37
  "puppeteer-core": "^25.11.0",
38
- "@hyperframes/engine": "0.9.3",
39
- "@hyperframes/core": "0.9.3",
40
- "@hyperframes/lint": "0.9.3",
41
- "@hyperframes/parsers": "0.9.3",
42
- "@hyperframes/studio-server": "0.9.3"
38
+ "@hyperframes/core": "0.9.5",
39
+ "@hyperframes/parsers": "0.9.5",
40
+ "@hyperframes/engine": "0.9.5",
41
+ "@hyperframes/lint": "0.9.5",
42
+ "@hyperframes/studio-server": "0.9.5"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^25.0.10",
@@ -48,7 +48,7 @@
48
48
  "tsx": "^4.21.0",
49
49
  "typescript": "^5.7.2",
50
50
  "vitest": "^4.1.11",
51
- "@hyperframes/aws-lambda": "0.9.3"
51
+ "@hyperframes/aws-lambda": "0.9.5"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"