Files
hyperframes/packages/producer/src/distributed.ts
James RussoandClaude Opus 5.5 fcd93ce184 feat(producer): render a distributed chunk as frame sub-ranges (#5916)
* feat(producer): render a distributed chunk as frame sub-ranges

renderChunk and renderChunkV2 take an optional frameRange inside the
chunk's slice, so a slow chunk can be retried as smaller parts rendered in
parallel. A part renders as a chunk of its own: capture, deferred video
frames, GOP size, frame names and the audio packets it carries all follow
the range. Omitting it renders the whole slice as before.

assemble and assembleV2 accept the ordered parts of a chunk in place of
its single output. DISTRIBUTED_RENDER_CAPABILITIES.chunkFrameRange
advertises both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(producer): keep the chunk sub-range comments within the comment checks

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 23:07:45 +00:00

203 lines
7.7 KiB
TypeScript

/**
* `@hyperframes/producer/distributed` — the distributed render primitives.
*
* The distributed activities are pure functions over local file paths;
* networking + orchestration live in adapters. New integrations should use
* Plan v2; the v1 functions remain available for compatibility.
*
* Adopters (AWS Lambda, Cloud Run Jobs, Temporal, K8s Jobs, plain SSH):
*
* ```ts
* import {
* planV2,
* renderChunkV2,
* assembleV2,
* } from "@hyperframes/producer/distributed";
*
* // Controller-side: publish a content-addressed Plan v2 manifest + CAS.
* const planResult = await planV2(projectDir, config, planV2Dir);
*
* // Worker-side: render one chunk. Byte-identical retries on the same
* // (planV2Dir, chunkIndex) — Temporal / Step Functions retry policies are
* // safe to point at this.
* const chunk = await renderChunkV2(planV2Dir, chunkIndex, outputChunkPath);
*
* // Controller-side: stitch chunks into the final deliverable.
* await assembleV2(planV2Dir, chunkPaths, outputPath);
* ```
*
* No networking, no AWS SDK, no Temporal SDK — those live in adapter
* packages. This module is library code only.
*/
// ── Plan (Activity A) ───────────────────────────────────────────────────────
export {
// Functions
buildChunkSlices,
measurePlanDirBytes,
plan,
rejectUnsupportedDistributedFormat,
resolveChunkPlan,
// Types
type DistributedRenderConfig,
type PlanResult,
type RemoteProjectFile,
// Constants
DEFAULT_CHUNK_SIZE,
DEFAULT_MAX_PARALLEL_CHUNKS,
MIN_CHUNK_SIZE,
PLAN_DIR_SIZE_LIMIT_BYTES,
PLAN_PROJECT_DIR_SKIP_SEGMENTS,
// Error codes + classes
CHUNK_EXCEEDS_WORKER_TIMEOUT,
ChunkExceedsWorkerTimeoutError,
FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED,
FormatNotSupportedInDistributedError,
PLAN_TOO_LARGE,
PlanTooLargeError,
} from "./services/distributed/plan.js";
// ── Plan v2 content-addressed transport ────────────────────────────────────
export {
createPlanV2FromExecutionPlan,
createPlanV2FromV1,
getPlanV2ExecutionPlanHash,
isPlanV2DeferredSourceArtifact,
listPlanV2ArtifactsForTarget,
materializePlanV2Target,
planV2,
planV2WithPublisher,
publishPlanV2FromExecutionPlan,
publishPlanV2FromV1,
readPlanV2Manifest,
validatePlanV2MaterializedTarget,
PLAN_V2_INTEGRITY_UNRECOVERABLE,
PLAN_V2_MATERIALIZATION_MARKER,
PlanV2IntegrityError,
type PlanV2Artifact,
type PlanV2Limitations,
type PlanV2Manifest,
type PlanV2MaterializeOptions,
type PlanV2MaterializationResult,
type PlanV2MaterializationTarget,
type PlanV2RemoteSources,
type PlanV2Result,
type PlanV2WithPublisherOptions,
} from "./services/distributed/planV2.js";
export {
LocalPlanV2ArtifactPublisher,
type LocalPlanV2ArtifactPublisherOptions,
type PlanV2ArtifactPublisher,
type PlanV2PublishBlob,
} from "./services/distributed/planV2Publisher.js";
export { assembleV2, renderChunkV2 } from "./services/distributed/planV2Execution.js";
// ── RenderChunk (Activity B) ────────────────────────────────────────────────
export {
applyRuntimeEnvSnapshot,
readWebGlVendorInfoFromCanvas,
renderChunk,
// Types
type ChunkFrameRange,
type ChunkRenderer,
type ChunkResult,
type EffectiveChunkResult,
type RenderChunkOptions,
// Error codes + classes
CHUNK_FRAME_RANGE_INVALID,
FFMPEG_VERSION_MISMATCH,
INVALID_VIDEO_METADATA,
PLAN_HASH_MISMATCH,
RenderChunkValidationError,
} from "./services/distributed/renderChunk.js";
// ── Assemble (Activity C) ───────────────────────────────────────────────────
export {
assemble,
chunksAreStitchedMp4,
type AssembleChunkPaths,
type AssembleResult,
} from "./services/distributed/assemble.js";
export { muxChunkWithAudio } from "./services/distributed/chunkAudio.js";
export {
buildStitchedHeader,
readMp4Layout,
type Mp4Layout,
type ReadRange,
} from "./services/distributed/mp4Stitch.js";
// ── Cloud-agnostic adapter helpers ──────────────────────────────────────────
// Shared by the distributed-render adapters (aws-lambda, gcp-cloud-run, …) so
// the config-shape validator lives in one place; each adapter layers only its
// own wire-format size cap on top.
export {
InvalidConfigError,
type SerializableDistributedRenderConfig,
validateDistributedRenderConfig,
validateVariablesPayload,
} from "./services/distributed/renderConfigValidation.js";
export { hashProjectDir, listSiteFiles } from "./services/distributed/projectHash.js";
// ── Plan protocol compatibility ────────────────────────────────────────────
// Workers validate this descriptor before consuming layout-specific
// artifacts. Missing descriptors remain compatible with legacy v1 plans.
export {
CURRENT_PLAN_PROTOCOL,
DISTRIBUTED_RENDER_CAPABILITIES,
getDistributedRenderCapabilities,
PLAN_ARTIFACT_LAYOUT,
PLAN_HASH_SCHEMA,
PLAN_PROTOCOL_V1,
PLAN_PROTOCOL_V2,
PLAN_PROTOCOL_UNSUPPORTED,
PLAN_SCHEMA_VERSION,
PLAN_V2_ARTIFACT_LAYOUT,
PLAN_V2_HASH_SCHEMA,
PLAN_V2_SCHEMA_VERSION,
PlanProtocolUnsupportedError,
readPlanProtocol,
readPlanProtocolV1,
type DistributedRenderCapabilities,
type PlanProtocolConsumerCapabilities,
type PlanProtocolDescriptor,
type PlanProtocolV1Descriptor,
type PlanProtocolV2Descriptor,
type SupportedPlanProtocolDescriptor,
} from "./services/distributed/planProtocol.js";
// ── Format union ────────────────────────────────────────────────────────────
// Canonical output-format type. The aws-lambda package re-exports it so
// CLI / adopter SDKs can derive runtime allowlists from one source.
export { PlanVideosMetadataError, type DistributedFormat } from "./services/distributed/shared.js";
// ── Plan artifact names ─────────────────────────────────────────────────────
// The cloud adapters locate and publish the plan's audio artifact by name. Its
// extension selects the container, so they must read it from here rather than
// restate it: a literal that drifts from the writer's is a silently missing
// audio track, not a loud failure.
export {
isPlanAudioArtifactPath,
PLAN_AUDIO_LEGACY_RELATIVE_PATH,
PLAN_AUDIO_RELATIVE_PATH,
resolvePlanAudioPath,
} from "./services/distributed/shared.js";
// ── Plan-time shared types from `freezePlan` ───────────────────────────────
// Re-exported so adopters that deserialize a planDir's `meta/encoder.json`
// or `meta/chunks.json` see the same shapes the producer wrote them as.
export type {
ChunkSliceJson,
CompositionMetadataJson,
LockedRenderConfig,
} from "./services/render/stages/freezePlan.js";
// ── Plan-time validation errors ────────────────────────────────────────────
// Export typed deterministic validation codes so orchestration adapters can
// mark authoring/configuration failures as terminal while still retrying real
// infrastructure faults.
export {
DISTRIBUTED_DURATION_OUT_OF_RANGE,
MAX_DISTRIBUTED_DURATION_SECONDS,
PlanValidationError,
} from "./services/render/planValidation.js";