@hyperframes/producer 0.8.175 → 0.8.176
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.
- package/dist/distributed.js +1109 -932
- package/dist/index.js +1052 -950
- package/dist/public-server.js +30 -11
- package/dist/services/distributed/chunkFrames.d.ts +48 -0
- package/dist/services/distributed/plan.d.ts +8 -2
- package/dist/services/distributed/planFrameCost.d.ts +24 -0
- package/dist/services/distributed/renderChunk.d.ts +4 -69
- package/package.json +7 -7
package/dist/public-server.js
CHANGED
|
@@ -13763,13 +13763,42 @@ import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
|
|
|
13763
13763
|
// src/services/distributed/plan.ts
|
|
13764
13764
|
import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframes/engine";
|
|
13765
13765
|
import { extname as extname4, isAbsolute as isAbsolute4, join as join29, relative as relative4, sep as sep3 } from "node:path";
|
|
13766
|
-
import {
|
|
13766
|
+
import {
|
|
13767
|
+
fpsToNumber as fpsToNumber10,
|
|
13768
|
+
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
|
|
13769
|
+
} from "@hyperframes/core";
|
|
13767
13770
|
import {
|
|
13768
13771
|
getEncoderPreset as getEncoderPreset4,
|
|
13769
13772
|
normalizeVp9CpuUsed,
|
|
13770
13773
|
resolveConfig as resolveConfig3
|
|
13771
13774
|
} from "@hyperframes/engine";
|
|
13772
13775
|
|
|
13776
|
+
// src/services/distributed/planFrameCost.ts
|
|
13777
|
+
import {
|
|
13778
|
+
closeCaptureSession as closeCaptureSession9,
|
|
13779
|
+
createCaptureSession as createCaptureSession8,
|
|
13780
|
+
createFrameLookupTable as createFrameLookupTable3,
|
|
13781
|
+
initializeSession as initializeSession8
|
|
13782
|
+
} from "@hyperframes/engine";
|
|
13783
|
+
|
|
13784
|
+
// src/services/distributed/chunkFrames.ts
|
|
13785
|
+
import {
|
|
13786
|
+
compositionRequiresWebGpu as compositionRequiresWebGpu3,
|
|
13787
|
+
createFrameLookupTable as createFrameLookupTable2,
|
|
13788
|
+
createVideoFrameInjector as createVideoFrameInjector2,
|
|
13789
|
+
extractVideoFramesRange
|
|
13790
|
+
} from "@hyperframes/engine";
|
|
13791
|
+
|
|
13792
|
+
// src/services/distributed/shared.ts
|
|
13793
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
13794
|
+
import { promisify as promisify2 } from "node:util";
|
|
13795
|
+
import {
|
|
13796
|
+
getFfmpegBinary,
|
|
13797
|
+
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME3
|
|
13798
|
+
} from "@hyperframes/engine";
|
|
13799
|
+
var PLAN_AUDIO_RELATIVE_PATH = MIXED_AUDIO_FILENAME3;
|
|
13800
|
+
var execFile = promisify2(execFileCallback);
|
|
13801
|
+
|
|
13773
13802
|
// src/services/distributed/planProtocol.ts
|
|
13774
13803
|
var PLAN_SCHEMA_VERSION = 1;
|
|
13775
13804
|
var PLAN_ARTIFACT_LAYOUT = "plan-dir-v1";
|
|
@@ -13806,16 +13835,6 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
13806
13835
|
// src/services/render/stages/planHash.ts
|
|
13807
13836
|
var FIELD_DELIMITER = Buffer.from([0]);
|
|
13808
13837
|
|
|
13809
|
-
// src/services/distributed/shared.ts
|
|
13810
|
-
import { execFile as execFileCallback } from "node:child_process";
|
|
13811
|
-
import { promisify as promisify2 } from "node:util";
|
|
13812
|
-
import {
|
|
13813
|
-
getFfmpegBinary,
|
|
13814
|
-
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME3
|
|
13815
|
-
} from "@hyperframes/engine";
|
|
13816
|
-
var PLAN_AUDIO_RELATIVE_PATH = MIXED_AUDIO_FILENAME3;
|
|
13817
|
-
var execFile = promisify2(execFileCallback);
|
|
13818
|
-
|
|
13819
13838
|
// src/services/distributed/planSize.ts
|
|
13820
13839
|
var PLAN_ROOT_CATEGORY = {
|
|
13821
13840
|
compiled: "compiled",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type BeforeCaptureHook, BROWSER_GPU_NOT_SOFTWARE, type CaptureOptions, type EngineConfig, type ExtractedFrames, type FrameLookupTable } from "@hyperframes/engine";
|
|
2
|
+
import type { LockedRenderConfig } from "../render/stages/freezePlan.js";
|
|
3
|
+
import { type DistributedFormat, INVALID_VIDEO_METADATA, type PlanVideosJson } from "./shared.js";
|
|
4
|
+
/** Distinct codes so adapters route retries per cause: a missing artifact may heal, a hash mismatch never. */
|
|
5
|
+
export declare const FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
|
|
6
|
+
export declare const PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
|
|
7
|
+
export declare const MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
|
|
8
|
+
export declare const CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
|
|
9
|
+
export declare const MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
|
|
10
|
+
export { INVALID_VIDEO_METADATA };
|
|
11
|
+
export type RenderChunkValidationCode = typeof FFMPEG_VERSION_MISMATCH | typeof PLAN_HASH_MISMATCH | typeof MISSING_PLAN_ARTIFACT | typeof CHUNK_INDEX_OUT_OF_RANGE | typeof MISSING_RUNTIME_ENV_SNAPSHOT | typeof INVALID_VIDEO_METADATA | typeof BROWSER_GPU_NOT_SOFTWARE;
|
|
12
|
+
/** A malformed planDir or a worker that does not match the plan's fingerprint; adapters key retries off `code`. */
|
|
13
|
+
export declare class RenderChunkValidationError extends Error {
|
|
14
|
+
readonly code: RenderChunkValidationCode;
|
|
15
|
+
constructor(code: RenderChunkValidationCode, message: string);
|
|
16
|
+
}
|
|
17
|
+
/** One injector per browser session: a hook reused on a fresh page can skip its first injection. */
|
|
18
|
+
export declare function createChunkVideoFrameInjectorFactory(frameLookup: FrameLookupTable | null): () => BeforeCaptureHook | null;
|
|
19
|
+
/**
|
|
20
|
+
* Rebuilds `ExtractedFrames[]` from `<planDir>/video-frames/<videoId>/`. `framePaths` keys are 0-based
|
|
21
|
+
* frame indexes (what `getFrameAtTime` reads); any other key drops every video's first-paint frame.
|
|
22
|
+
*/
|
|
23
|
+
export declare function rebuildExtractedFramesFromPlanDir(planDir: string, videos: PlanVideosJson["extracted"], indexMode?: "dense-v1" | "sparse-v2"): ExtractedFrames[];
|
|
24
|
+
/** Capture options a chunk session uses; the planner measures frame cost with the same ones. */
|
|
25
|
+
export declare function buildChunkCaptureOptions(input: {
|
|
26
|
+
dimensions: {
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
fpsNum: number;
|
|
30
|
+
fpsDen: number;
|
|
31
|
+
format: DistributedFormat;
|
|
32
|
+
};
|
|
33
|
+
encoder: Pick<LockedRenderConfig, "quality" | "deviceScaleFactor" | "variables">;
|
|
34
|
+
videoCount: number;
|
|
35
|
+
html: string;
|
|
36
|
+
}): CaptureOptions;
|
|
37
|
+
/** Extracts the given frames of deferred videos under the names a full extraction gives them. */
|
|
38
|
+
export declare function extractDeferredVideoFramesForChunk(input: {
|
|
39
|
+
planDir: string;
|
|
40
|
+
planVideos: PlanVideosJson;
|
|
41
|
+
frames: Iterable<number>;
|
|
42
|
+
fps: {
|
|
43
|
+
num: number;
|
|
44
|
+
den: number;
|
|
45
|
+
};
|
|
46
|
+
outputRoot: string;
|
|
47
|
+
cfg: Pick<EngineConfig, "ffmpegProcessTimeout">;
|
|
48
|
+
}): Promise<ExtractedFrames[]>;
|
|
@@ -261,7 +261,11 @@ export declare const MIN_CHUNK_SIZE = 10;
|
|
|
261
261
|
/** Most chunks one render may plan; `validateDistributedRenderConfig` enforces it on `maxParallelChunks`. */
|
|
262
262
|
export declare const MAX_PARALLEL_CHUNKS_CEILING = 256;
|
|
263
263
|
/** Predicted seconds for one chunk of `frames` frames at `megapixels` output pixels per frame. */
|
|
264
|
-
export declare function estimateChunkSeconds(frames: number, megapixels: number): number;
|
|
264
|
+
export declare function estimateChunkSeconds(frames: number, megapixels: number, measuredSecondsPerFrame?: number): number;
|
|
265
|
+
/** Graded media runs a full-frame shader per frame, which the per-pixel estimate cannot see. */
|
|
266
|
+
export declare function planNeedsFrameCostMeasurement(html: string): boolean;
|
|
267
|
+
/** Whether {@link resolveMaxParallelChunks} sizes chunks from what a frame costs. */
|
|
268
|
+
export declare function chunkSizingUsesFrameCost(config: Pick<DistributedRenderConfig, "maxParallelChunks" | "chunkSize" | "targetChunkFrames" | "workerTimeoutSeconds" | "workerConcurrency">): boolean;
|
|
265
269
|
/** Non-retryable: even {@link MAX_PARALLEL_CHUNKS_CEILING} chunks cannot fit the worker time limit. */
|
|
266
270
|
export declare const CHUNK_EXCEEDS_WORKER_TIMEOUT = "CHUNK_EXCEEDS_WORKER_TIMEOUT";
|
|
267
271
|
export declare class ChunkExceedsWorkerTimeoutError extends Error {
|
|
@@ -274,7 +278,9 @@ export declare class ChunkExceedsWorkerTimeoutError extends Error {
|
|
|
274
278
|
*/
|
|
275
279
|
export declare function resolveMaxParallelChunks(config: Pick<DistributedRenderConfig, "maxParallelChunks" | "chunkSize" | "targetChunkFrames" | "workerTimeoutSeconds" | "workerConcurrency">, totalFrames: number,
|
|
276
280
|
/** Output pixels per frame, after supersampling. */
|
|
277
|
-
megapixels: number, log?: ProducerLogger
|
|
281
|
+
megapixels: number, log?: ProducerLogger,
|
|
282
|
+
/** Measured capture seconds per frame; frames are priced at it when it beats the estimate. */
|
|
283
|
+
measuredSecondsPerFrame?: number): number;
|
|
278
284
|
/**
|
|
279
285
|
* Default hard ceiling on `<planDir>/` size in bytes. 2 GB fits inside
|
|
280
286
|
* AWS Lambda's 10 GB `/tmp` alongside the chunk worker's captured frames
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type EngineConfig } from "@hyperframes/engine";
|
|
2
|
+
import type { ProducerLogger } from "../../logger.js";
|
|
3
|
+
import type { DistributedFormat, PlanVideosJson } from "./shared.js";
|
|
4
|
+
/**
|
|
5
|
+
* Captures the renderer's calibration frames in a session set up like a chunk
|
|
6
|
+
* worker's (same capture options, engine config and video frames) and returns
|
|
7
|
+
* the p95 capture seconds per frame.
|
|
8
|
+
*/
|
|
9
|
+
export declare function measurePlanSecondsPerFrame(input: {
|
|
10
|
+
planDir: string;
|
|
11
|
+
workDir: string;
|
|
12
|
+
html: string;
|
|
13
|
+
planVideos: PlanVideosJson;
|
|
14
|
+
totalFrames: number;
|
|
15
|
+
fps: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
format: DistributedFormat;
|
|
19
|
+
quality: "draft" | "standard" | "high";
|
|
20
|
+
deviceScaleFactor: number;
|
|
21
|
+
variables?: Record<string, unknown>;
|
|
22
|
+
cfg: EngineConfig;
|
|
23
|
+
log: ProducerLogger;
|
|
24
|
+
}): Promise<number>;
|
|
@@ -33,35 +33,10 @@
|
|
|
33
33
|
* Every determinism toggle above is opt-in — only this primitive enables them.
|
|
34
34
|
* In-process renders (`executeRenderJob`) leave them off.
|
|
35
35
|
*/
|
|
36
|
-
import { assertSwiftShader, type
|
|
37
|
-
import { type
|
|
38
|
-
import {
|
|
39
|
-
|
|
40
|
-
* Non-retryable error codes raised when the planDir is structurally
|
|
41
|
-
* malformed, semantically out of range, or fingerprints differently from
|
|
42
|
-
* what the controller wrote. Each is distinct so adapter retry policies
|
|
43
|
-
* can route them independently — e.g. `MISSING_PLAN_ARTIFACT` may point
|
|
44
|
-
* to a partial S3 download that a retry could heal, while
|
|
45
|
-
* `PLAN_HASH_MISMATCH` strictly indicates cross-version drift that
|
|
46
|
-
* retries won't fix.
|
|
47
|
-
*/
|
|
48
|
-
export declare const FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
|
|
49
|
-
export declare const PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
|
|
50
|
-
export declare const MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
|
|
51
|
-
export declare const CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
|
|
52
|
-
export declare const MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
|
|
53
|
-
export { INVALID_VIDEO_METADATA };
|
|
54
|
-
export type RenderChunkValidationCode = typeof FFMPEG_VERSION_MISMATCH | typeof PLAN_HASH_MISMATCH | typeof MISSING_PLAN_ARTIFACT | typeof CHUNK_INDEX_OUT_OF_RANGE | typeof MISSING_RUNTIME_ENV_SNAPSHOT | typeof INVALID_VIDEO_METADATA | typeof BROWSER_GPU_NOT_SOFTWARE;
|
|
55
|
-
/**
|
|
56
|
-
* Typed non-retryable error raised by `renderChunk` when the planDir is
|
|
57
|
-
* malformed or the worker's runtime doesn't match the planDir's
|
|
58
|
-
* controller-side fingerprint. Workflow adapters key retry policies off
|
|
59
|
-
* `code` — most of these failures will not heal on retry.
|
|
60
|
-
*/
|
|
61
|
-
export declare class RenderChunkValidationError extends Error {
|
|
62
|
-
readonly code: RenderChunkValidationCode;
|
|
63
|
-
constructor(code: RenderChunkValidationCode, message: string);
|
|
64
|
-
}
|
|
36
|
+
import { assertSwiftShader, type CaptureOptions, type CaptureMode, type CaptureSession, closeCaptureSession, createCaptureSession, type EngineConfig, initializeSession, probeBeginFrameLiveness, readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
|
|
37
|
+
import { type LockedRenderConfig } from "../render/stages/freezePlan.js";
|
|
38
|
+
import { type PlanVideosJson } from "./shared.js";
|
|
39
|
+
export { CHUNK_INDEX_OUT_OF_RANGE, createChunkVideoFrameInjectorFactory, extractDeferredVideoFramesForChunk, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, MISSING_PLAN_ARTIFACT, MISSING_RUNTIME_ENV_SNAPSHOT, PLAN_HASH_MISMATCH, rebuildExtractedFramesFromPlanDir, RenderChunkValidationError, } from "./chunkFrames.js";
|
|
65
40
|
/** Validate the shared video contract before any v1 chunk can inject frames. */
|
|
66
41
|
export declare function validatePlanVideosForChunk(value: unknown): PlanVideosJson;
|
|
67
42
|
/**
|
|
@@ -134,12 +109,6 @@ export declare const distributedCaptureSessionDependencies: DistributedCaptureSe
|
|
|
134
109
|
* assertion, including fresh screenshot browsers created after a fallback.
|
|
135
110
|
*/
|
|
136
111
|
export declare function createVerifiedDistributedCaptureSession(serverUrl: string, framesDir: string, captureOptions: CaptureOptions, cfg: EngineConfig, dependencies?: DistributedCaptureSessionDependencies): Promise<CaptureSession>;
|
|
137
|
-
/**
|
|
138
|
-
* Build the immutable lookup table once, but keep injector state scoped to a
|
|
139
|
-
* browser session. Reusing one hook across workers or a whole-chunk retry can
|
|
140
|
-
* incorrectly suppress injection on a fresh page.
|
|
141
|
-
*/
|
|
142
|
-
export declare function createChunkVideoFrameInjectorFactory(frameLookup: FrameLookupTable | null): () => BeforeCaptureHook | null;
|
|
143
112
|
/**
|
|
144
113
|
* Only BeginFrame-specific failures are safe to retry in screenshot mode.
|
|
145
114
|
* Cancellation, memory exhaustion, and unrelated authoring/IO failures must
|
|
@@ -161,40 +130,6 @@ export declare function runCaptureWithScreenshotFallback<T>(input: {
|
|
|
161
130
|
* and frame zero. `true` means the entire chunk should use screenshot mode.
|
|
162
131
|
*/
|
|
163
132
|
export declare function beginFrameSessionNeedsScreenshotFallback(session: Pick<CaptureSession, "page" | "launchCaptureMode" | "beginFrameTimeTicks" | "beginFrameIntervalMs">, probe?: typeof probeBeginFrameLiveness): Promise<boolean>;
|
|
164
|
-
/**
|
|
165
|
-
* Rebuild the engine's in-memory `ExtractedFrames[]` from the on-disk
|
|
166
|
-
* planDir layout. `<planDir>/video-frames/<videoId>/` holds the numbered
|
|
167
|
-
* frame files plan() extracted; this lists each dir and rebuilds the
|
|
168
|
-
* 0-based `framePaths` Map that `FrameLookupTable` / `videoFrameInjector`
|
|
169
|
-
* both index against — the consumer is
|
|
170
|
-
* `videoFrameExtractor.ts:getFrameAtTime`, which floors `localTime * fps`
|
|
171
|
-
* to a 0-based index and reads `framePaths.get(frameIndex)`. Any drift
|
|
172
|
-
* from that key convention silently drops every `<video>`'s first-paint
|
|
173
|
-
* frame; see HF#1731 / HF#1730.
|
|
174
|
-
*
|
|
175
|
-
* Exported so a unit test can pin the 0-based contract without spinning
|
|
176
|
-
* up the heavyweight Docker fixture — the bug surfaces only under
|
|
177
|
-
* distributed mode and only at video first-paint, so this primitive is
|
|
178
|
-
* the right granularity to guard.
|
|
179
|
-
*/
|
|
180
|
-
export declare function rebuildExtractedFramesFromPlanDir(planDir: string, videos: PlanVideosJson["extracted"], indexMode?: "dense-v1" | "sparse-v2"): ExtractedFrames[];
|
|
181
|
-
/**
|
|
182
|
-
* Extract the frames this chunk shows from videos the planner left
|
|
183
|
-
* unextracted. Frames land in `outputRoot/<videoId>/` under the names a full
|
|
184
|
-
* extraction gives them, so the lookup indexes them exactly as it would
|
|
185
|
-
* frames the planner wrote.
|
|
186
|
-
*/
|
|
187
|
-
export declare function extractDeferredVideoFramesForChunk(input: {
|
|
188
|
-
planDir: string;
|
|
189
|
-
planVideos: PlanVideosJson;
|
|
190
|
-
slice: ChunkSliceJson;
|
|
191
|
-
fps: {
|
|
192
|
-
num: number;
|
|
193
|
-
den: number;
|
|
194
|
-
};
|
|
195
|
-
outputRoot: string;
|
|
196
|
-
cfg: Pick<EngineConfig, "ffmpegProcessTimeout">;
|
|
197
|
-
}): Promise<ExtractedFrames[]>;
|
|
198
133
|
/**
|
|
199
134
|
* Re-export the runtime-env apply helper so adapters that import only
|
|
200
135
|
* this subpath can prime `process.env` before instantiating their own
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.176",
|
|
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.2.1",
|
|
37
37
|
"puppeteer-core": "^25.2.1",
|
|
38
|
-
"@hyperframes/core": "0.8.
|
|
39
|
-
"@hyperframes/engine": "0.8.
|
|
40
|
-
"@hyperframes/parsers": "0.8.
|
|
41
|
-
"@hyperframes/
|
|
42
|
-
"@hyperframes/
|
|
38
|
+
"@hyperframes/core": "0.8.176",
|
|
39
|
+
"@hyperframes/engine": "0.8.176",
|
|
40
|
+
"@hyperframes/parsers": "0.8.176",
|
|
41
|
+
"@hyperframes/lint": "0.8.176",
|
|
42
|
+
"@hyperframes/studio-server": "0.8.176"
|
|
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.8.
|
|
51
|
+
"@hyperframes/aws-lambda": "0.8.176"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|