fix(producer): preserve render request config contracts

This commit is contained in:
James
2026-07-17 17:22:34 -04:00
parent fc4e9a6c0e
commit 1a66c881b8
3 changed files with 64 additions and 4 deletions
+8 -2
View File
@@ -344,7 +344,6 @@ const BOOLEAN_ENGINE_CONFIG_FIELDS = [
] as const;
const POSITIVE_NUMBER_ENGINE_CONFIG_FIELDS = [
"coresPerWorker",
"browserTimeout",
"protocolTimeout",
"chunkSizeFrames",
@@ -394,6 +393,13 @@ function assertEngineConfigNumber(
}
}
function assertPositiveEngineConfigNumber(config: Record<string, unknown>, field: string): void {
const value = config[field];
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
throw new Error(`Engine config ${field} must be a finite number > 0`);
}
}
function assertEngineConfigEnum(
config: Record<string, unknown>,
field: string,
@@ -435,7 +441,7 @@ function validateEngineConfigParallelism(config: Record<string, unknown>): void
) {
throw new Error("Engine config concurrency must be a positive integer or auto");
}
assertEngineConfigNumber(config, "coresPerWorker", 0);
assertPositiveEngineConfigNumber(config, "coresPerWorker");
for (const field of ["minParallelFrames", "largeRenderThreshold"] as const) {
assertEngineConfigNumber(config, field, 0, true);
}
+48 -1
View File
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it } from "vitest";
import { DEFAULT_CONFIG } from "@hyperframes/engine";
import { DEFAULT_CONFIG, resolveConfig } from "@hyperframes/engine";
import {
createRenderRequest,
distributedConfigFromRequest,
@@ -8,6 +8,7 @@ import {
renderRequestFromDistributedConfig,
serializeRenderRequest,
} from "./renderRequest.js";
import { InvalidConfigError } from "./services/distributed/renderConfigValidation.js";
const originalForceScreenshot = process.env.PRODUCER_FORCE_SCREENSHOT;
@@ -203,6 +204,52 @@ describe("RenderRequest", () => {
}
});
it("accepts fractional coresPerWorker snapshots from the canonical resolver", () => {
const engineConfig = resolveConfig({ coresPerWorker: 0.5 });
const value = createRenderRequest({
projectDir: "/project",
outputPath: "/output/video.mp4",
engineConfig,
options: {
fps: { num: 30, den: 1 },
quality: "standard",
format: "mp4",
distributed: { width: 1920, height: 1080, cfr: true },
},
});
expect(value.options.engineConfig.coresPerWorker).toBe(0.5);
expect(parseRenderRequest(serializeRenderRequest(value))).toEqual(value);
const distributed = distributedConfigFromRequest(value);
(distributed as { engineConfig: unknown }).engineConfig = engineConfig;
expect(
renderRequestFromDistributedConfig({
projectDir: value.projectDir,
outputPath: value.outputPath,
config: distributed,
}).options.engineConfig.coresPerWorker,
).toBe(0.5);
});
it("preserves the distributed InvalidConfigError contract for engine snapshots", () => {
const value = request();
const distributed = distributedConfigFromRequest(value);
(distributed as { engineConfig: unknown }).engineConfig = {};
try {
renderRequestFromDistributedConfig({
projectDir: value.projectDir,
outputPath: value.outputPath,
config: distributed,
});
throw new Error("expected distributed validation to fail");
} catch (error) {
expect(error).toBeInstanceOf(InvalidConfigError);
expect((error as InvalidConfigError).field).toBe("config.engineConfig");
}
});
it("validates the reverse distributed adapter at the wire boundary", () => {
const distributed = distributedConfigFromRequest(request());
distributed.width = 1921;
@@ -208,7 +208,14 @@ export function validateDistributedRenderConfig(
validateVariablesPayload(config.variables);
}
if (config.engineConfig !== undefined) {
validateEngineConfigSnapshot(config.engineConfig);
try {
validateEngineConfigSnapshot(config.engineConfig);
} catch (error) {
throw new InvalidConfigError(
"config.engineConfig",
error instanceof Error ? error.message : "must be a valid engine config snapshot",
);
}
}
return config;