@hyperframes/aws-lambda 0.6.80 → 0.6.81
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/events.d.ts +2 -8
- package/dist/events.d.ts.map +1 -1
- package/dist/index.js +10 -229
- package/dist/index.js.map +3 -3
- package/dist/sdk/deploySite.d.ts.map +1 -1
- package/dist/sdk/index.js +10 -229
- package/dist/sdk/index.js.map +3 -3
- package/dist/sdk/validateConfig.d.ts +19 -57
- package/dist/sdk/validateConfig.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/handler.ts", "../src/chromium.ts", "../src/formatExtension.ts", "../src/s3Transport.ts", "../src/sdk/deploySite.ts", "../src/sdk/renderToLambda.ts", "../src/sdk/validateConfig.ts", "../src/sdk/getRenderProgress.ts", "../src/sdk/costAccounting.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * AWS Lambda handler for HyperFrames distributed rendering.\n *\n * One Lambda function, three roles. Step Functions dispatches by setting\n * `event.Action`; the handler unwraps Map-state envelopes, primes the\n * Lambda environment (Chrome path, ffmpeg path, tmpdir), and forwards to\n * the matching OSS primitive from `@hyperframes/producer/distributed`.\n *\n * Everything heavy \u2014 capture, encode, audio mix \u2014 happens inside the OSS\n * primitives. The handler is thin glue: parse event \u2192 S3 download \u2192 call\n * primitive \u2192 S3 upload \u2192 return small JSON result.\n */\n\nimport { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { basename, join } from \"node:path\";\nimport { S3Client } from \"@aws-sdk/client-s3\";\nimport {\n assemble,\n type AssembleResult,\n type ChunkResult,\n type DistributedRenderConfig,\n plan,\n type PlanResult,\n renderChunk,\n} from \"@hyperframes/producer/distributed\";\nimport { resolveChromeExecutablePath } from \"./chromium.js\";\nimport { type DistributedFormat, formatExtension } from \"./formatExtension.js\";\nimport type {\n AssembleEvent,\n AssembleLambdaResult,\n LambdaAction,\n LambdaEvent,\n LambdaResult,\n PlanEvent,\n PlanLambdaResult,\n RenderChunkEvent,\n RenderChunkLambdaResult,\n} from \"./events.js\";\nimport {\n downloadS3ObjectToFile,\n parseS3Uri,\n tarDirectory,\n untarDirectory,\n uploadFileToS3,\n} from \"./s3Transport.js\";\n\n/**\n * Lazily-constructed S3 client. Cached at module scope so warm Lambda\n * containers reuse the underlying HTTP keep-alive pool across invocations.\n */\nlet cachedS3Client: S3Client | null = null;\nfunction getS3Client(): S3Client {\n if (cachedS3Client) return cachedS3Client;\n cachedS3Client = new S3Client({});\n return cachedS3Client;\n}\n\n/**\n * Optional injection points used by the handler's unit tests. Production\n * callers leave these unset; the real OSS primitives are used. Tests\n * inject `s3` and `primitives` directly rather than mutating module\n * state \u2014 the dependency-injection seam is sufficient and avoids a\n * second leak point for cross-test contamination.\n */\nexport interface HandlerDeps {\n s3?: S3Client;\n primitives?: {\n plan: typeof plan;\n renderChunk: typeof renderChunk;\n assemble: typeof assemble;\n };\n /** Override the per-invocation `/tmp` workdir root (defaults to Lambda's `/tmp`). */\n tmpRoot?: string;\n /** Skip Chrome resolution (used by handler dispatch tests that mock renderChunk). */\n skipChromeResolution?: boolean;\n}\n\n/**\n * Lambda entry. Step Functions sometimes wraps the event in\n * `{ Payload: ... }` or `{ Input: ... }` depending on the state machine\n * shape; unwrap until we hit a discriminated event.\n */\nexport async function handler(event: LambdaEvent, deps?: HandlerDeps): Promise<LambdaResult> {\n const unwrapped = unwrapEvent(event);\n validateEventS3Uris(unwrapped);\n primeRuntimeEnv();\n // Single structured boot log line \u2014 CloudWatch Logs Insights queries\n // key off `event=handler_start` to grep for a specific Action / S3 URI\n // when triaging without attaching a debugger.\n logEvent({ event: \"handler_start\", action: unwrapped.Action, input: summarizeEvent(unwrapped) });\n try {\n switch (unwrapped.Action) {\n case \"plan\":\n return await handlePlan(unwrapped, deps);\n case \"renderChunk\":\n return await handleRenderChunk(unwrapped, deps);\n case \"assemble\":\n return await handleAssemble(unwrapped, deps);\n default: {\n // Compile-time exhaustiveness: a new LambdaAction member trips\n // the `never` assignment before the runtime error is reachable.\n const _exhaustive: never = unwrapped;\n throw new Error(\n `[handler] unknown Action: ${JSON.stringify(\n (_exhaustive as { Action?: string }).Action,\n )}. Expected one of \"plan\", \"renderChunk\", \"assemble\".`,\n );\n }\n }\n } catch (err) {\n // Log before re-throwing so CloudWatch captures the structured\n // error context alongside Lambda's default stack trace. Otherwise\n // ops only sees the trace and has to correlate with execution\n // history to recover the action + input.\n logEvent({\n event: \"handler_error\",\n action: unwrapped.Action,\n message: err instanceof Error ? err.message : String(err),\n name: err instanceof Error ? err.name : undefined,\n });\n throw err;\n }\n}\n\n/**\n * Walk through Step Functions' Map-state and Task-state envelopes until\n * the discriminated event is found.\n */\n// Step Functions wraps at most `{Payload: {Input: ...}}` in our state\n// machine; 4 levels is 2\u00D7 headroom for unusual Map / Wait state\n// configurations and prevents infinite loops on malformed input.\nconst MAX_ENVELOPE_DEPTH = 4;\n\nexport function unwrapEvent(event: LambdaEvent): PlanEvent | RenderChunkEvent | AssembleEvent {\n let cursor: LambdaEvent = event;\n for (let i = 0; i < MAX_ENVELOPE_DEPTH; i++) {\n if (cursor && typeof cursor === \"object\") {\n const obj = cursor as Record<string, unknown>;\n if (typeof obj.Action === \"string\" && isLambdaAction(obj.Action)) {\n return cursor as PlanEvent | RenderChunkEvent | AssembleEvent;\n }\n if (\"Payload\" in obj) {\n cursor = obj.Payload as LambdaEvent;\n continue;\n }\n if (\"Input\" in obj) {\n cursor = obj.Input as LambdaEvent;\n continue;\n }\n }\n break;\n }\n throw new Error(\n `[handler] event has no recognised Action; unwrapped ${MAX_ENVELOPE_DEPTH} levels of Payload/Input without finding one.`,\n );\n}\n\nfunction isLambdaAction(value: string): value is LambdaAction {\n return value === \"plan\" || value === \"renderChunk\" || value === \"assemble\";\n}\n\n/**\n * Emit a single JSON line to stdout. CloudWatch ingests each line as a\n * structured event; Logs Insights queries can `filter event=\"...\"` and\n * project specific fields. We write to stdout (not stderr) because\n * Lambda's default destination for both is the same log group, and\n * Logs Insights' INFO/ERROR level parser keys off the JSON `level`\n * field, not the stream.\n */\nfunction logEvent(payload: Record<string, unknown>): void {\n console.log(JSON.stringify(payload));\n}\n\n/**\n * Compact, non-PII summary of a Lambda event for logging. The full\n * event payload can include the entire project config; we only emit\n * the routable fields (S3 URIs, chunk index, format) needed to triage\n * a failure from CloudWatch.\n */\nfunction summarizeEvent(\n event: PlanEvent | RenderChunkEvent | AssembleEvent,\n): Record<string, unknown> {\n switch (event.Action) {\n case \"plan\":\n return {\n projectS3Uri: event.ProjectS3Uri,\n planOutputS3Prefix: event.PlanOutputS3Prefix,\n format: event.Config.format,\n fps: event.Config.fps,\n };\n case \"renderChunk\":\n return {\n planS3Uri: event.PlanS3Uri,\n chunkIndex: event.ChunkIndex,\n format: event.Format,\n };\n case \"assemble\":\n return {\n planS3Uri: event.PlanS3Uri,\n chunkCount: event.ChunkS3Uris.length,\n hasAudio: event.AudioS3Uri !== null,\n outputS3Uri: event.OutputS3Uri,\n format: event.Format,\n };\n }\n}\n\n/**\n * Lambda sets `TMPDIR` to `/tmp` already, but the bundled binaries (Chrome\n * + ffmpeg) live alongside the handler at `/var/task/bin/`. Add that to\n * PATH the first time the handler runs so spawn(\"ffmpeg\", \u2026) inside the\n * OSS primitives resolves to the bundled binary.\n */\nlet runtimeEnvPrimed = false;\nfunction primeRuntimeEnv(): void {\n if (runtimeEnvPrimed) return;\n runtimeEnvPrimed = true;\n const taskRoot = process.env.LAMBDA_TASK_ROOT ?? \"/var/task\";\n const bin = join(taskRoot, \"bin\");\n if (existsSync(bin)) {\n process.env.PATH = `${bin}:${process.env.PATH ?? \"\"}`;\n }\n}\n\n// \u2500\u2500 Plan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function handlePlan(event: PlanEvent, deps?: HandlerDeps): Promise<PlanLambdaResult> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.plan ?? plan;\n\n // The producer's probe stage launches Chromium whenever the composition\n // needs a runtime duration probe or has unresolved sub-compositions, so\n // plan has to resolve Chrome the same way renderChunk does. Without this\n // the probe throws \"An `executablePath` or `channel` must be specified\n // for `puppeteer-core`\" the moment runProbeStage calls puppeteer.launch.\n if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n const chromePath = await resolveChromeExecutablePath();\n process.env.PRODUCER_HEADLESS_SHELL_PATH = chromePath;\n }\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-plan-\"));\n // We use `.tar.gz` (not `.zip`) as the project archive's on-the-wire\n // format because Lambda's Amazon Linux base image ships GNU `tar` but\n // not `unzip` in `/usr/bin`. The smoke script + future CLI both\n // produce tar.gz uploads.\n const projectArchive = join(work, \"project.tar.gz\");\n const projectDir = join(work, \"project\");\n const planDir = join(work, \"plan\");\n\n try {\n await downloadS3ObjectToFile(s3, event.ProjectS3Uri, projectArchive);\n await untarDirectory(projectArchive, projectDir);\n\n const config: DistributedRenderConfig = {\n ...event.Config,\n };\n const result: PlanResult = await primitive(projectDir, config, planDir);\n\n // Upload the planDir as a single tarball. Step Functions cannot pass\n // a directory-shaped artifact between states; we serialize and rely on\n // the consumer (renderChunk / assemble) to untar. Audio is co-located\n // alongside the plan so RenderChunk doesn't have to pull the whole\n // plan tarball when audio isn't relevant to the chunk.\n const planTar = join(work, \"plan.tar.gz\");\n await tarDirectory(planDir, planTar);\n const planTarUri = `${trimTrailingSlash(event.PlanOutputS3Prefix)}/plan.tar.gz`;\n const audioPath = join(planDir, \"audio.aac\");\n const hasAudio = existsSync(audioPath) && statSync(audioPath).size > 0;\n const audioUri = hasAudio ? `${trimTrailingSlash(event.PlanOutputS3Prefix)}/audio.aac` : null;\n // Plan and audio are independent S3 PUTs; run them in parallel so\n // the response returns as soon as the slower of the two completes.\n await Promise.all([\n uploadFileToS3(s3, planTar, planTarUri, \"application/gzip\"),\n hasAudio && audioUri ? uploadFileToS3(s3, audioPath, audioUri, \"audio/aac\") : null,\n ]);\n\n return {\n Action: \"plan\",\n PlanS3Uri: planTarUri,\n PlanHash: result.planHash,\n ChunkCount: result.chunkCount,\n TotalFrames: result.totalFrames,\n Fps: result.fps,\n Width: result.width,\n Height: result.height,\n Format: result.format,\n HasAudio: audioUri !== null,\n AudioS3Uri: audioUri,\n FfmpegVersion: result.ffmpegVersion,\n ProducerVersion: result.producerVersion,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\n// \u2500\u2500 RenderChunk \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function handleRenderChunk(\n event: RenderChunkEvent,\n deps?: HandlerDeps,\n): Promise<RenderChunkLambdaResult> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.renderChunk ?? renderChunk;\n\n // Sparticuz decompresses Chromium into /tmp on first call; warm starts\n // skip the work (path already cached). Guard the env-var mutation too so\n // a caller-supplied PRODUCER_HEADLESS_SHELL_PATH (e.g. the SAM-local\n // RIE smoke) wins over the auto-resolution.\n if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n const chromePath = await resolveChromeExecutablePath();\n // The OSS engine resolves Chrome via `PRODUCER_HEADLESS_SHELL_PATH`\n // first (see `browserManager.resolveHeadlessShellPath`); set it before\n // invoking the primitive so launch picks up the bundled binary.\n process.env.PRODUCER_HEADLESS_SHELL_PATH = chromePath;\n }\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-chunk-\"));\n const planTar = join(work, \"plan.tar.gz\");\n const planDir = join(work, \"plan\");\n\n try {\n await downloadS3ObjectToFile(s3, event.PlanS3Uri, planTar);\n await untarDirectory(planTar, planDir);\n\n // Verify the plan's hash matches what Step Functions told us to render.\n // The producer's renderChunk re-checks internally (defense-in-depth),\n // but doing it here at the handler boundary lets us fail before paying\n // the Chrome-launch + render cost on a misrouted chunk. Throws a\n // typed PLAN_HASH_MISMATCH that Step Functions can route as\n // non-retryable.\n verifyPlanHash(planDir, event.PlanHash);\n\n const chunkOutputBase = join(\n work,\n event.Format === \"png-sequence\"\n ? `chunk-${pad(event.ChunkIndex)}`\n : `chunk-${pad(event.ChunkIndex)}${formatExtension(event.Format)}`,\n );\n\n const result: ChunkResult = await primitive(planDir, event.ChunkIndex, chunkOutputBase);\n\n const chunkUri = await uploadChunkOutput(\n s3,\n result,\n event.ChunkOutputS3Prefix,\n event.ChunkIndex,\n );\n\n return {\n Action: \"renderChunk\",\n ChunkS3Uri: chunkUri,\n ChunkIndex: event.ChunkIndex,\n Sha256: result.sha256,\n FramesEncoded: result.framesEncoded,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\nasync function uploadChunkOutput(\n s3: S3Client,\n result: ChunkResult,\n prefix: string,\n chunkIndex: number,\n): Promise<string> {\n const trimmed = trimTrailingSlash(prefix);\n if (result.outputKind === \"file\") {\n const ext = result.outputPath.slice(result.outputPath.lastIndexOf(\".\"));\n const uri = `${trimmed}/chunks/${pad(chunkIndex)}${ext}`;\n await uploadFileToS3(s3, result.outputPath, uri);\n return uri;\n }\n // frame-dir: upload as a tarball so a single S3 object represents the chunk.\n // Assemble's png-sequence path expects a directory per chunk; it untars on\n // its end.\n const tarball = `${result.outputPath}.tar.gz`;\n await tarDirectory(result.outputPath, tarball);\n const uri = `${trimmed}/chunks/${pad(chunkIndex)}.tar.gz`;\n await uploadFileToS3(s3, tarball, uri, \"application/gzip\");\n return uri;\n}\n\n// \u2500\u2500 Assemble \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function handleAssemble(\n event: AssembleEvent,\n deps?: HandlerDeps,\n): Promise<AssembleLambdaResult> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.assemble ?? assemble;\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-assemble-\"));\n const planTar = join(work, \"plan.tar.gz\");\n const planDir = join(work, \"plan\");\n\n try {\n await downloadS3ObjectToFile(s3, event.PlanS3Uri, planTar);\n await untarDirectory(planTar, planDir);\n\n const chunkPaths = await downloadChunkObjects(s3, event.ChunkS3Uris, work, event.Format);\n\n let audioPath: string | null = null;\n if (event.AudioS3Uri) {\n audioPath = join(planDir, \"audio.aac\");\n await downloadS3ObjectToFile(s3, event.AudioS3Uri, audioPath);\n }\n\n const finalOutput =\n event.Format === \"png-sequence\"\n ? join(work, \"output-frames\")\n : join(work, `output${formatExtension(event.Format)}`);\n\n const result: AssembleResult = await primitive(planDir, chunkPaths, audioPath, finalOutput, {\n cfr: event.Cfr === true,\n });\n\n if (event.Format === \"png-sequence\") {\n const tarball = `${finalOutput}.tar.gz`;\n await tarDirectory(finalOutput, tarball);\n await uploadFileToS3(s3, tarball, event.OutputS3Uri, \"application/gzip\");\n } else {\n await uploadFileToS3(s3, finalOutput, event.OutputS3Uri);\n }\n\n return {\n Action: \"assemble\",\n OutputS3Uri: event.OutputS3Uri,\n FramesEncoded: result.framesEncoded,\n FileSize: result.fileSize,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\nasync function downloadChunkObjects(\n s3: S3Client,\n uris: string[],\n workDir: string,\n format: DistributedFormat,\n): Promise<string[]> {\n const chunksDir = join(workDir, \"chunks\");\n mkdirSync(chunksDir, { recursive: true });\n // Each chunk is an independent S3 GET (+ untar for png-sequence). Run\n // them in parallel \u2014 assemble's wall-clock is otherwise dominated by\n // `\u03A3 chunk-download-ms` instead of `max(chunk-download-ms)`. Preserve\n // the input order by writing into a pre-sized array rather than\n // pushing as each task settles.\n const local: string[] = new Array<string>(uris.length);\n await Promise.all(\n uris.map(async (uri, i) => {\n if (!uri) {\n throw new Error(`[handler] chunk URI at index ${i} is empty`);\n }\n const { key } = parseS3Uri(uri);\n const localPath = join(chunksDir, basename(key));\n await downloadS3ObjectToFile(s3, uri, localPath);\n if (format === \"png-sequence\") {\n const dirPath = join(chunksDir, `frames-${pad(i)}`);\n await untarDirectory(localPath, dirPath);\n local[i] = dirPath;\n } else {\n local[i] = localPath;\n }\n }),\n );\n return local;\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Collect every S3 URI that the handler will touch for a given event. */\nfunction getEventS3Uris(event: PlanEvent | RenderChunkEvent | AssembleEvent): string[] {\n switch (event.Action) {\n case \"plan\":\n return [event.ProjectS3Uri, event.PlanOutputS3Prefix];\n case \"renderChunk\":\n return [event.PlanS3Uri, event.ChunkOutputS3Prefix];\n case \"assemble\":\n return [event.PlanS3Uri, ...event.ChunkS3Uris, event.OutputS3Uri, event.AudioS3Uri].filter(\n (u): u is string => u != null,\n );\n }\n}\n\n/**\n * Verify every S3 URI in the event resolves to the configured render bucket.\n * Throws `S3_URI_NOT_ALLOWED` (non-retryable) when a URI targets a different\n * bucket, preventing event injection from reading or writing arbitrary S3 data.\n *\n * Skipped when `HYPERFRAMES_RENDER_BUCKET` is unset so existing deployments\n * without the env var continue to work.\n */\nfunction validateEventS3Uris(event: PlanEvent | RenderChunkEvent | AssembleEvent): void {\n const allowedBucket = process.env.HYPERFRAMES_RENDER_BUCKET?.trim();\n if (!allowedBucket) return;\n\n for (const uri of getEventS3Uris(event)) {\n const { bucket } = parseS3Uri(uri);\n if (bucket !== allowedBucket) {\n const err = new Error(\n `[handler] S3_URI_NOT_ALLOWED: URI ${JSON.stringify(uri)} targets bucket \"${bucket}\" but only \"${allowedBucket}\" is permitted`,\n );\n err.name = \"S3_URI_NOT_ALLOWED\";\n throw err;\n }\n }\n}\n\nfunction pad(n: number): string {\n return n.toString().padStart(4, \"0\");\n}\n\nfunction trimTrailingSlash(prefix: string): string {\n return prefix.endsWith(\"/\") ? prefix.slice(0, -1) : prefix;\n}\n\nfunction cleanupDir(dir: string): void {\n try {\n // Lambda warm starts can reuse `/tmp` across invocations; clean up\n // aggressively so we don't leak a chunk-sized footprint between renders.\n rmSync(dir, { recursive: true, force: true });\n } catch {\n // Best-effort \u2014 leak is preferable to crashing on success path.\n }\n}\n\n/**\n * Read the untarred planDir's `plan.json` and assert its `planHash`\n * matches what the Step Functions event claims. Throws on mismatch with\n * a typed `PLAN_HASH_MISMATCH` error name so the state machine's typed\n * non-retryable list routes it correctly.\n *\n * This is defense-in-depth \u2014 the producer's `renderChunk` does the same\n * check internally \u2014 but performing it here lets us fail before paying\n * the Chrome-launch + per-frame capture cost on a misrouted chunk.\n */\nfunction verifyPlanHash(planDir: string, expected: string): void {\n const planJsonPath = join(planDir, \"plan.json\");\n let parsed: { planHash?: unknown };\n try {\n parsed = JSON.parse(readFileSync(planJsonPath, \"utf-8\")) as { planHash?: unknown };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const error = new Error(`PLAN_HASH_MISMATCH: failed to read ${planJsonPath}: ${msg}`);\n error.name = \"PLAN_HASH_MISMATCH\";\n throw error;\n }\n const actual = parsed.planHash;\n if (typeof actual !== \"string\" || actual !== expected) {\n const error = new Error(\n `PLAN_HASH_MISMATCH: event PlanHash=${expected} did not match plan.json planHash=${String(actual)}`,\n );\n error.name = \"PLAN_HASH_MISMATCH\";\n throw error;\n }\n}\n", "/**\n * Lambda-runtime Chrome resolver.\n *\n * `renderChunk()` (the only primitive that needs a browser) launches Chrome\n * via the engine's `BrowserManager`. In Lambda we can't ship the full\n * Puppeteer-managed Chrome download \u2014 Puppeteer's Chrome binary is ~330 MB\n * unzipped, well over Lambda's 250 MB ZIP-deploy ceiling.\n *\n * Two valid runtime sources:\n *\n * 1. `@sparticuz/chromium` (primary). Decompresses a Lambda-optimised\n * `chrome-headless-shell` build into `/tmp` at runtime. ~70 MB\n * compressed; the same binary the rest of the ecosystem uses for\n * headless-Chrome-in-Lambda. CDP-level BeginFrame works because the\n * command lives in the protocol, not the binary; the\n * `scripts/probe-beginframe.ts` regression guard pins this.\n *\n * 2. A bundled `chrome-headless-shell` binary (fallback). If\n * `@sparticuz/chromium`'s build ever drops `HeadlessExperimental`\n * support, we fall back to the same `chrome-headless-shell` build\n * the K8s deploy uses. The fallback raises the ZIP from ~70 MB\n * Chrome to ~140 MB Chrome \u2014 still well under 250 MB.\n *\n * The runtime path is selected by the `HYPERFRAMES_LAMBDA_CHROME_SOURCE`\n * env var (set by `build-zip.ts`):\n *\n * \"sparticuz\" \u2192 use `@sparticuz/chromium.executablePath()`\n * \"chrome-headless-shell\" \u2192 use `process.env.HYPERFRAMES_LAMBDA_CHROME_PATH`\n *\n * Adapters that bundle this package can override\n * `HYPERFRAMES_LAMBDA_CHROME_PATH` directly when running outside Lambda\n * (e.g. the SAM-local RIE smoke).\n */\n\nimport { existsSync } from \"node:fs\";\n\n/** Discriminator for the two supported Chrome sources. */\nexport type ChromeSource = \"sparticuz\" | \"chrome-headless-shell\";\n\n/**\n * Thrown when the Chrome binary resolver can't produce a usable path.\n * The class name is the SFN `Retry: { ErrorEquals: [...] }` discriminator \u2014\n * see {@link HyperframesRenderStack}'s NON_RETRYABLE_* lists.\n */\nexport class ChromeBinaryUnavailableError extends Error {\n // Lambda's runtime serializes the error envelope's `errorType` from\n // `err.name`; this class-field override sets it across the structured\n // clone. Read indirectly; fallow can't follow.\n // fallow-ignore-next-line unused-class-member\n override readonly name = \"ChromeBinaryUnavailableError\";\n readonly source: ChromeSource;\n readonly resolvedPath: string | null;\n constructor(source: ChromeSource, resolvedPath: string | null, hint: string) {\n super(`[chromium] Chrome binary unavailable (source=${source}): ${hint}`);\n this.source = source;\n this.resolvedPath = resolvedPath;\n }\n}\n\nconst SPARTICUZ_WEDGE_HINT =\n \"@sparticuz/chromium.executablePath() returned a falsy value or a path that doesn't exist on disk. \" +\n \"This typically happens after a chunk hits `Sandbox.Timedout` mid-extraction and leaves /tmp in a \" +\n \"wedged state \u2014 subsequent invocations land on the same warm instance and never re-extract. \" +\n \"Recycle the function (e.g. `aws lambda update-function-configuration ... --environment ...` with a \" +\n \"bumped marker var, or redeploy via `hyperframes lambda deploy --skip-build`) to force fresh \" +\n \"execution environments. Tracking: investigate the upstream wedge so this auto-recovers.\";\n\n/**\n * Read which Chrome source the bundled ZIP was built against. Defaults to\n * `\"sparticuz\"` so a fresh build with no env override picks the primary\n * path.\n */\nexport function resolveChromeSource(): ChromeSource {\n const raw = process.env.HYPERFRAMES_LAMBDA_CHROME_SOURCE?.toLowerCase();\n if (raw === \"chrome-headless-shell\" || raw === \"shell\") return \"chrome-headless-shell\";\n return \"sparticuz\";\n}\n\n/**\n * Resolve the absolute path to a Chrome binary suitable for BeginFrame.\n *\n * For `\"sparticuz\"`: dynamically import `@sparticuz/chromium` and call\n * `chromium.executablePath()`. The module is dynamic so a build-zip that\n * never reaches the import (because the fallback Chrome is bundled) can\n * tree-shake it out.\n *\n * For `\"chrome-headless-shell\"`: read the path from\n * `HYPERFRAMES_LAMBDA_CHROME_PATH`. Throws if absent or non-existent so a\n * misconfigured deploy fails loudly at boot rather than at first frame.\n */\n// fallow-ignore-next-line complexity\nexport async function resolveChromeExecutablePath(): Promise<string> {\n const source = resolveChromeSource();\n if (source === \"sparticuz\") {\n const mod = await loadSparticuzChromium();\n const path = await mod.executablePath();\n // Guard against the wedge described in ChromeBinaryUnavailableError.\n // sparticuz's contract is \"return the path to a usable binary\" \u2014 when\n // it returns null/undefined/\"\" we can't hand that to puppeteer-core\n // (which will throw an unrelated-looking assertion). Same when the\n // returned path doesn't exist (extraction failed but the function\n // call returned).\n if (!path || typeof path !== \"string\") {\n throw new ChromeBinaryUnavailableError(source, null, SPARTICUZ_WEDGE_HINT);\n }\n if (!existsSync(path)) {\n throw new ChromeBinaryUnavailableError(source, path, SPARTICUZ_WEDGE_HINT);\n }\n return path;\n }\n const explicit = process.env.HYPERFRAMES_LAMBDA_CHROME_PATH;\n if (!explicit) {\n throw new ChromeBinaryUnavailableError(\n source,\n null,\n \"HYPERFRAMES_LAMBDA_CHROME_SOURCE=chrome-headless-shell requires \" +\n \"HYPERFRAMES_LAMBDA_CHROME_PATH to be set to the absolute path of the bundled binary.\",\n );\n }\n if (!existsSync(explicit)) {\n throw new ChromeBinaryUnavailableError(\n source,\n explicit,\n `HYPERFRAMES_LAMBDA_CHROME_PATH=${JSON.stringify(explicit)} does not exist on disk.`,\n );\n }\n return explicit;\n}\n\n/**\n * Resolve the Chromium launch args for the selected source. For\n * `@sparticuz/chromium` we forward `chromium.args` (Lambda-tuned defaults\n * \u2014 single-process, no-sandbox, /tmp paths). For the shell fallback the\n * engine's own arg builder owns it; we return an empty array so the\n * engine's defaults apply.\n */\nexport async function resolveChromeArgs(): Promise<string[]> {\n if (resolveChromeSource() !== \"sparticuz\") return [];\n const mod = await loadSparticuzChromium();\n return mod.args;\n}\n\n/**\n * Dynamic import wrapper isolated so unit tests can stub the module without\n * jest-style module mocking gymnastics. The narrow type here pins the\n * subset of `@sparticuz/chromium`'s surface this package depends on; if\n * the upstream module ever changes shape the type error here surfaces\n * before runtime.\n */\ninterface SparticuzChromiumModule {\n args: string[];\n executablePath(): Promise<string>;\n}\n\nlet cachedSparticuz: SparticuzChromiumModule | null = null;\n\nasync function loadSparticuzChromium(): Promise<SparticuzChromiumModule> {\n if (cachedSparticuz) return cachedSparticuz;\n const mod = (await import(\"@sparticuz/chromium\")) as\n | SparticuzChromiumModule\n | { default: SparticuzChromiumModule };\n const resolved = \"default\" in mod ? mod.default : mod;\n cachedSparticuz = resolved;\n return resolved;\n}\n\n/** Test-only seam: replace the cached `@sparticuz/chromium` module. */\nexport function _setSparticuzChromiumForTests(mod: SparticuzChromiumModule | null): void {\n cachedSparticuz = mod;\n}\n", "/**\n * Map a distributed `format` to the file extension the assembled output\n * should carry on disk + in S3. Shared by `src/handler.ts` (chunk +\n * assemble output paths) and `src/sdk/renderToLambda.ts` (final\n * output key construction) so the two sides agree on what an mp4\n * looks like vs a png-sequence.\n */\n\nimport type { DistributedFormat } from \"@hyperframes/producer/distributed\";\n\nexport type { DistributedFormat } from \"@hyperframes/producer/distributed\";\n\n// Closed-enum lookup table. TS enforces exhaustiveness via the\n// `Record<DistributedFormat, string>` annotation \u2014 adding a format to\n// `DistributedFormat` without adding the matching key here fails to\n// typecheck, which is the same exhaustiveness guarantee a switch +\n// `_exhaustive: never` arm provides but at lower complexity.\nconst FORMAT_EXTENSIONS: Record<DistributedFormat, string> = {\n mp4: \".mp4\",\n mov: \".mov\",\n webm: \".webm\",\n \"png-sequence\": \"\",\n};\n\nexport function formatExtension(format: DistributedFormat): string {\n return FORMAT_EXTENSIONS[format];\n}\n", "/**\n * Thin S3 transport for the Lambda handler.\n *\n * The OSS distributed primitives are pure functions over local file paths;\n * the Lambda handler bridges S3 \u2194 Lambda's `/tmp` filesystem on each\n * invocation. Functions here are intentionally narrow: parse a URI, download\n * an object to a local path, upload a path/directory, tar-extract a planDir,\n * tar-pack a planDir back out.\n *\n * Tar (not zip) for planDir transit:\n * - planDirs contain symlinks (extract stage materializes them but the\n * compiled/ subtree may include linked assets); tar preserves them, zip\n * does not.\n * - We use the `tar` npm package (pure JS over `node:zlib`) \u2014 AWS\n * Lambda's `nodejs:22` base image ships neither `tar` nor `unzip` in\n * `/usr/bin`, so a system-binary tar would ENOENT in the actual\n * deployment.\n */\n\nimport {\n createReadStream,\n createWriteStream,\n existsSync,\n mkdirSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { GetObjectCommand, PutObjectCommand, type S3Client } from \"@aws-sdk/client-s3\";\nimport * as tar from \"tar\";\n\n/** Parsed `s3://bucket/key` URI. */\nexport interface S3Location {\n bucket: string;\n key: string;\n}\n\n/** Parse `s3://bucket/key/path` \u2192 `{ bucket, key }`. Throws on malformed input. */\nexport function parseS3Uri(uri: string): S3Location {\n if (!uri.startsWith(\"s3://\")) {\n throw new Error(`[s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)}`);\n }\n const rest = uri.slice(\"s3://\".length);\n const slash = rest.indexOf(\"/\");\n if (slash === -1) {\n throw new Error(`[s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}`);\n }\n const bucket = rest.slice(0, slash);\n const key = rest.slice(slash + 1);\n if (!bucket || !key) {\n throw new Error(`[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)}`);\n }\n return { bucket, key };\n}\n\n/** Build `s3://bucket/key` from a location. */\nexport function formatS3Uri(loc: S3Location): string {\n return `s3://${loc.bucket}/${loc.key}`;\n}\n\n/** Stream an S3 object to a local file path. Throws if the body is missing. */\nexport async function downloadS3ObjectToFile(\n client: S3Client,\n uri: string,\n destPath: string,\n): Promise<void> {\n const { bucket, key } = parseS3Uri(uri);\n const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));\n const body = response.Body as NodeJS.ReadableStream | undefined;\n if (!body) {\n throw new Error(`[s3Transport] s3 GetObject returned empty body for ${uri}`);\n }\n mkdirSync(dirname(destPath), { recursive: true });\n await pipeline(body, createWriteStream(destPath));\n}\n\n/**\n * Upload a local file's contents to an S3 URI using a streaming\n * `PutObjectCommand`. PutObject's 5 GB cap comfortably exceeds the\n * distributed pipeline's 2 GB planDir limit and the typical\n * chunk size (\u2264 200 MB), so a single PUT works for every artifact this\n * adapter handles.\n */\nexport async function uploadFileToS3(\n client: S3Client,\n localPath: string,\n uri: string,\n contentType?: string,\n): Promise<void> {\n if (!existsSync(localPath)) {\n throw new Error(`[s3Transport] upload source missing: ${localPath}`);\n }\n const { bucket, key } = parseS3Uri(uri);\n const size = statSync(localPath).size;\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: createReadStream(localPath),\n ContentType: contentType,\n ContentLength: size,\n }),\n );\n}\n\n/**\n * Pack a directory into a `.tar.gz` at `destTarball`. Uses the `tar` npm\n * package (pure JS over `node:zlib`) rather than spawning a system tar\n * binary \u2014 the AWS Lambda Node 22 base image ships a minimal set of\n * userland tools and does NOT include `tar` in `/usr/bin`.\n */\nexport async function tarDirectory(sourceDir: string, destTarball: string): Promise<void> {\n if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) {\n throw new Error(`[s3Transport] tar source must be an existing directory: ${sourceDir}`);\n }\n mkdirSync(dirname(destTarball), { recursive: true });\n await tar.create({ gzip: true, file: destTarball, cwd: sourceDir }, [\".\"]);\n}\n\n/**\n * Extract a `.tar.gz` produced by {@link tarDirectory} into `destDir`.\n * The directory is created (or cleared) before extraction so a retried\n * invocation doesn't observe stale files from a prior run on the same\n * warm Lambda container.\n */\nexport async function untarDirectory(tarballPath: string, destDir: string): Promise<void> {\n if (!existsSync(tarballPath)) {\n throw new Error(`[s3Transport] tarball missing: ${tarballPath}`);\n }\n // Wipe target so the warm container's prior planDir doesn't bleed into\n // the new invocation. Lambda re-uses /tmp across invocations on the same\n // container.\n if (existsSync(destDir)) {\n rmSync(destDir, { recursive: true, force: true });\n }\n mkdirSync(destDir, { recursive: true });\n await tar.extract({ file: tarballPath, cwd: destDir });\n}\n", "/**\n * `deploySite` \u2014 upload a project directory to S3 once per content hash\n * and return a reusable handle.\n *\n * `renderToLambda` calls this implicitly when no `siteHandle` is passed,\n * but exposing it as a standalone verb lets adopters bundle a project\n * ahead of time and reuse the handle across many renders without\n * re-tarring the project tree on every call.\n *\n * The handle is **content-addressed**: `siteId` is derived from a SHA-256\n * over the project files. Two `deploySite` calls on an unchanged tree\n * produce the same `siteId` and `HeadObject`-short-circuit the upload.\n */\n\nimport { mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from \"node:fs\";\nimport { createHash } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport { HeadObjectCommand, S3Client } from \"@aws-sdk/client-s3\";\nimport { PLAN_PROJECT_DIR_SKIP_SEGMENTS } from \"@hyperframes/producer/distributed\";\nimport { formatS3Uri, tarDirectory, uploadFileToS3 } from \"../s3Transport.js\";\n\n/** Options for {@link deploySite}. */\nexport interface DeploySiteOptions {\n /** Local project directory containing `index.html` (and any composition assets). */\n projectDir: string;\n /** S3 bucket the SAM stack / CDK construct provisioned. */\n bucketName: string;\n /** AWS region for the S3 client. Defaults to the SDK's default chain (env / config / IMDS). */\n region?: string;\n /**\n * Override the content-addressed site id. Useful when the caller has a\n * stable external identifier they want to use (e.g. a git SHA); if\n * unset, the hash of the project tree picks it.\n */\n siteId?: string;\n /** Injection seam for tests. Production callers leave unset. */\n s3?: S3Client;\n}\n\n/** Stable handle returned by {@link deploySite}. Pass back to {@link renderToLambda}. */\nexport interface SiteHandle {\n /** Content-addressed (or caller-supplied) identifier; stable across re-uploads of the same tree. */\n siteId: string;\n /** Bucket the site landed in. Surfaced separately so callers don't have to re-parse `projectS3Uri`. */\n bucketName: string;\n /** Full `s3://bucket/sites/<siteId>/project.tar.gz` URI; pass through to `renderToLambda`. */\n projectS3Uri: string;\n /** Tarball size in bytes; useful for \"did we actually skip the upload?\" assertions. */\n bytes: number;\n /** ISO timestamp of the most recent upload OR the existing object the short-circuit found. */\n uploadedAt: string;\n /** `false` if the object already existed and we skipped the PUT. */\n uploaded: boolean;\n}\n\n/**\n * Upload `projectDir` to `s3://bucketName/sites/<siteId>/project.tar.gz`.\n *\n * Short-circuits when an object with the same key already exists in the\n * bucket \u2014 `siteId` derives from the project's content hash, so the same\n * bytes produce the same key, and re-uploading would be redundant.\n */\nexport async function deploySite(opts: DeploySiteOptions): Promise<SiteHandle> {\n if (!statSync(opts.projectDir).isDirectory()) {\n throw new Error(`[deploySite] projectDir is not a directory: ${opts.projectDir}`);\n }\n\n const siteId = opts.siteId ?? hashProjectDir(opts.projectDir);\n const key = `sites/${siteId}/project.tar.gz`;\n const projectS3Uri = formatS3Uri({ bucket: opts.bucketName, key });\n const s3 = opts.s3 ?? new S3Client({ region: opts.region });\n\n // HeadObject short-circuit. Adopters re-rendering the same project on\n // a tight inner loop (CI smoke, demo flows) save the tar+gzip+PUT pass\n // on every iteration.\n const existing = await headObject(s3, opts.bucketName, key);\n if (existing) {\n return {\n siteId,\n bucketName: opts.bucketName,\n projectS3Uri,\n bytes: existing.bytes,\n uploadedAt: existing.lastModified,\n uploaded: false,\n };\n }\n\n const workdir = mkdtempSync(join(tmpdir(), \"hf-deploy-site-\"));\n try {\n const tarball = join(workdir, \"project.tar.gz\");\n await tarDirectory(opts.projectDir, tarball);\n // Note: tarDirectory packs *everything* under `cwd`. We don't need to\n // re-implement the skip list inside the tar pack because the\n // producer's plan stage applies the same skip during its copy; the\n // archive is slightly bigger than the planDir's compiled/ subtree\n // but the cost is bounded by the project's user-authored content.\n const size = statSync(tarball).size;\n await uploadFileToS3(s3, tarball, projectS3Uri, \"application/gzip\");\n return {\n siteId,\n bucketName: opts.bucketName,\n projectS3Uri,\n bytes: size,\n uploadedAt: new Date().toISOString(),\n uploaded: true,\n };\n } finally {\n rmSync(workdir, { recursive: true, force: true });\n }\n}\n\n/**\n * SHA-256 over every regular file under `projectDir` (sorted by relative\n * path) \u2192 16-character hex prefix. The prefix is the `siteId`.\n *\n * The hash includes the relative path plus every byte of each file, so a\n * same-bytes rename still yields a fresh id. We trim to 16 chars because\n * the full 64 isn't useful in an S3 key for legibility.\n *\n * Reads are synchronous: project trees are typically tens of MB at most\n * (HTML/CSS/JS plus a few composition assets), so the simpler shape wins\n * over a streaming pipeline.\n */\nfunction hashProjectDir(projectDir: string): string {\n const hash = createHash(\"sha256\");\n const files: string[] = [];\n function walk(dir: string, isRoot: boolean): void {\n for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) =>\n a.name < b.name ? -1 : a.name > b.name ? 1 : 0,\n )) {\n if (isRoot && PLAN_PROJECT_DIR_SKIP_SEGMENTS.has(entry.name)) continue;\n const full = join(dir, entry.name);\n if (entry.isDirectory()) walk(full, false);\n else if (entry.isFile()) files.push(full);\n }\n }\n walk(projectDir, true);\n for (const file of files) {\n const rel = relative(projectDir, file).replaceAll(\"\\\\\", \"/\");\n hash.update(rel);\n hash.update(\"\\0\");\n hash.update(readFileSync(file));\n }\n return hash.digest(\"hex\").slice(0, 16);\n}\n\nasync function headObject(\n s3: S3Client,\n bucket: string,\n key: string,\n): Promise<{ bytes: number; lastModified: string } | null> {\n try {\n const res = await s3.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));\n return {\n bytes: typeof res.ContentLength === \"number\" ? res.ContentLength : 0,\n lastModified:\n res.LastModified instanceof Date\n ? res.LastModified.toISOString()\n : new Date().toISOString(),\n };\n } catch (err) {\n // The SDK throws different error shapes for 404 vs 403 vs network;\n // a 404 means \"needs upload\" and is the most common case. Anything\n // else propagates so callers see auth / network failures.\n const status = (err as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode;\n if (status === 404) return null;\n const name = (err as { name?: string }).name;\n if (name === \"NotFound\" || name === \"NoSuchKey\") return null;\n throw err;\n }\n}\n", "/**\n * `renderToLambda` \u2014 start a distributed render against an already-deployed\n * SAM/CDK stack and return a handle the caller can poll with\n * {@link getRenderProgress}.\n *\n * The function does *not* wait for the render to finish. Step Functions\n * standard workflows can run for hours; blocking the caller's process on\n * the SFN execution is the wrong default. The returned `RenderHandle`\n * carries everything the progress / cost / download paths need.\n *\n * Wire order:\n * 1. Validate config (typed throw before any AWS call).\n * 2. `deploySite` if no `siteHandle` was provided.\n * 3. `StartExecution` against the state machine with the same input\n * shape `examples/aws-lambda/scripts/smoke.sh` builds.\n * 4. Return handle. The S3 `outputKey` is deterministic from the\n * execution name so the caller can predict the final object URL.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { SFNClient, StartExecutionCommand } from \"@aws-sdk/client-sfn\";\nimport type { S3Client } from \"@aws-sdk/client-s3\";\nimport type { SerializableDistributedRenderConfig } from \"../events.js\";\nimport { formatExtension } from \"../formatExtension.js\";\nimport { formatS3Uri } from \"../s3Transport.js\";\nimport { deploySite, type SiteHandle } from \"./deploySite.js\";\nimport {\n validateDistributedRenderConfig,\n validateStepFunctionsInputSize,\n} from \"./validateConfig.js\";\n\n/** Options for {@link renderToLambda}. */\nexport interface RenderToLambdaOptions {\n /** Local project directory. Required when `siteHandle` is not supplied. */\n projectDir?: string;\n /** Re-use an existing `deploySite` upload (skips tar+S3 PUT). */\n siteHandle?: SiteHandle;\n /** Validated `SerializableDistributedRenderConfig` (no logger / abortSignal). */\n config: SerializableDistributedRenderConfig;\n /** S3 bucket from the SAM stack output (`RenderBucketName`). */\n bucketName: string;\n /** State machine ARN from the SAM stack output (`RenderStateMachineArn`). */\n stateMachineArn: string;\n /** AWS region; defaults to the SDK default chain. */\n region?: string;\n /**\n * Final output S3 key. Defaults to `renders/<executionName>/output.<ext>`\n * where `<ext>` is derived from `config.format`.\n */\n outputKey?: string;\n /**\n * Step Functions execution name. Defaults to `hf-render-<uuid>`.\n * Used as `renderId` everywhere downstream (history queries, cost\n * accounting, predictable S3 key prefix).\n */\n executionName?: string;\n /** Test injection seam \u2014 production callers leave unset. */\n sfn?: SFNClient;\n /** Test injection seam \u2014 propagated to `deploySite` when applicable. */\n s3?: S3Client;\n}\n\n/** Stable identifier + every URL/ARN the caller needs to follow the render. */\nexport interface RenderHandle {\n /** Same as the Step Functions execution name. */\n renderId: string;\n /** Full execution ARN; pass to {@link getRenderProgress}. */\n executionArn: string;\n bucketName: string;\n stateMachineArn: string;\n outputS3Uri: string;\n projectS3Uri: string;\n startedAt: string;\n}\n\n// fallow-ignore-next-line complexity\nexport async function renderToLambda(opts: RenderToLambdaOptions): Promise<RenderHandle> {\n validateDistributedRenderConfig(opts.config);\n\n if (!opts.bucketName) {\n throw new Error(\"[renderToLambda] bucketName is required\");\n }\n if (!opts.stateMachineArn) {\n throw new Error(\"[renderToLambda] stateMachineArn is required\");\n }\n if (!opts.siteHandle && !opts.projectDir) {\n throw new Error(\"[renderToLambda] either siteHandle or projectDir must be supplied\");\n }\n\n const executionName = opts.executionName ?? `hf-render-${randomUUID()}`;\n const ext = formatExtension(opts.config.format);\n const outputKey = opts.outputKey ?? `renders/${executionName}/output${ext}`;\n const planOutputS3Prefix = formatS3Uri({\n bucket: opts.bucketName,\n key: `renders/${executionName}/`,\n });\n const outputS3Uri = formatS3Uri({ bucket: opts.bucketName, key: outputKey });\n\n const site =\n opts.siteHandle ??\n (await deploySite({\n projectDir: opts.projectDir as string,\n bucketName: opts.bucketName,\n region: opts.region,\n s3: opts.s3,\n }));\n\n const input = {\n ProjectS3Uri: site.projectS3Uri,\n PlanOutputS3Prefix: planOutputS3Prefix,\n OutputS3Uri: outputS3Uri,\n Config: opts.config,\n };\n\n // Reject oversize input client-side. Step Functions Standard caps the\n // execution input at 256 KiB; without this check, the input bloat\n // (typically from `config.variables` containing inlined media) surfaces\n // as `States.DataLimitExceeded` 50 ms into the execution, far from the\n // caller's stack frame. Measured AFTER `deploySite` so the synthesised\n // `ProjectS3Uri` is counted (a few hundred bytes either way, but the\n // check should be against the actual wire payload).\n validateStepFunctionsInputSize(input);\n\n const sfn = opts.sfn ?? new SFNClient({ region: opts.region });\n const startedAt = new Date().toISOString();\n const response = await sfn.send(\n new StartExecutionCommand({\n stateMachineArn: opts.stateMachineArn,\n name: executionName,\n input: JSON.stringify(input),\n }),\n );\n\n if (!response.executionArn) {\n throw new Error(\"[renderToLambda] StartExecution returned no executionArn\");\n }\n\n return {\n renderId: executionName,\n executionArn: response.executionArn,\n bucketName: opts.bucketName,\n stateMachineArn: opts.stateMachineArn,\n outputS3Uri,\n projectS3Uri: site.projectS3Uri,\n startedAt,\n };\n}\n", "/**\n * Client-side validation of `SerializableDistributedRenderConfig` so the\n * SDK fails on shape errors with a typed `InvalidConfigError` *before* a\n * Step Functions execution starts.\n *\n * The producer's `plan` stage validates the same fields server-side, but a\n * caller staring at \"ExecutionFailed: BROWSER_GPU_NOT_SOFTWARE\" five\n * minutes after StartExecution has to dig through Step Functions history\n * to learn that the renderToLambda call passed an unsupported format.\n * Catching the obvious mistakes locally turns that wait into a synchronous\n * throw.\n *\n * The check is deliberately narrow \u2014 it covers the *shape* errors any\n * caller could have surfaced with `tsc` if they passed a literal, plus\n * the `force-hdr` rejection (HDR mp4 isn't supported in distributed\n * mode). webm was previously rejected here too; v0.7+ supports it via\n * closed-GOP concat-copy. Anything deeper (font availability, plan\n * size cap, GPU mode at runtime) needs the actual planner.\n */\n\nimport type { DistributedFormat } from \"../formatExtension.js\";\nimport type { SerializableDistributedRenderConfig } from \"../events.js\";\n\n/** Thrown for any client-side `SerializableDistributedRenderConfig` violation. */\nexport class InvalidConfigError extends Error {\n // Read via Error.prototype.toString; fallow can't see it.\n // fallow-ignore-next-line unused-class-member\n override readonly name = \"InvalidConfigError\";\n /** Dotted JSON-pointer-ish path to the offending field, e.g. `config.fps`. */\n readonly field: string;\n constructor(field: string, message: string) {\n super(`[validateConfig] ${field}: ${message}`);\n this.field = field;\n }\n}\n\nconst ALLOWED_FPS = [24, 30, 60] as const;\nconst ALLOWED_FORMATS = [\n \"mp4\",\n \"mov\",\n \"png-sequence\",\n \"webm\",\n] as const satisfies readonly DistributedFormat[];\nconst ALLOWED_CODECS = [\"h264\", \"h265\"] as const;\nconst ALLOWED_QUALITIES = [\"draft\", \"standard\", \"high\"] as const;\nconst ALLOWED_RUNTIME_CAPS = [\"lambda\", \"temporal\", \"cloud-run-job\", \"k8s-job\", \"none\"] as const;\nconst ALLOWED_HDR_MODES = [\"auto\", \"force-sdr\"] as const;\n\nconst MAX_DIMENSION = 7680;\nconst MIN_DIMENSION = 16;\nconst MAX_CHUNK_SIZE = 3600;\nconst MAX_PARALLEL_CHUNKS_CEILING = 256;\n\n/**\n * Throw an `InvalidConfigError` if `config` is not a valid\n * `SerializableDistributedRenderConfig`. Returns the same reference on\n * success so the call site reads:\n *\n * const validated = validateDistributedRenderConfig(input);\n */\nexport function validateDistributedRenderConfig(\n config: SerializableDistributedRenderConfig,\n): SerializableDistributedRenderConfig {\n if (config === null || typeof config !== \"object\") {\n throw new InvalidConfigError(\"config\", \"must be an object\");\n }\n\n if (!ALLOWED_FPS.includes(config.fps as 24 | 30 | 60)) {\n throw new InvalidConfigError(\n \"config.fps\",\n `must be one of ${ALLOWED_FPS.join(\", \")}; got ${String(config.fps)}`,\n );\n }\n\n validateIntDimension(\"config.width\", config.width);\n validateIntDimension(\"config.height\", config.height);\n\n if (!ALLOWED_FORMATS.includes(config.format)) {\n throw new InvalidConfigError(\n \"config.format\",\n `must be one of ${ALLOWED_FORMATS.join(\", \")}; got ${String(config.format)}`,\n );\n }\n\n if (config.codec !== undefined) {\n if (config.format !== \"mp4\") {\n throw new InvalidConfigError(\n \"config.codec\",\n `is only valid with format=\"mp4\"; got format=${String(config.format)}`,\n );\n }\n if (!ALLOWED_CODECS.includes(config.codec)) {\n throw new InvalidConfigError(\n \"config.codec\",\n `must be one of ${ALLOWED_CODECS.join(\", \")}; got ${String(config.codec)}`,\n );\n }\n }\n\n if (config.quality !== undefined && !ALLOWED_QUALITIES.includes(config.quality)) {\n throw new InvalidConfigError(\n \"config.quality\",\n `must be one of ${ALLOWED_QUALITIES.join(\", \")}; got ${String(config.quality)}`,\n );\n }\n\n if (config.crf !== undefined && config.bitrate !== undefined) {\n throw new InvalidConfigError(\"config.crf\", \"is mutually exclusive with config.bitrate\");\n }\n if (\n config.crf !== undefined &&\n (!Number.isInteger(config.crf) || config.crf < 0 || config.crf > 51)\n ) {\n throw new InvalidConfigError(\"config.crf\", `must be an integer in [0, 51]; got ${config.crf}`);\n }\n if (config.bitrate !== undefined && !/^\\d+(\\.\\d+)?[kKmM]?$/.test(config.bitrate)) {\n throw new InvalidConfigError(\n \"config.bitrate\",\n `must look like \"10M\" or \"5000k\"; got ${JSON.stringify(config.bitrate)}`,\n );\n }\n\n if (config.chunkSize !== undefined) {\n if (!Number.isInteger(config.chunkSize) || config.chunkSize < 1) {\n throw new InvalidConfigError(\n \"config.chunkSize\",\n `must be a positive integer; got ${config.chunkSize}`,\n );\n }\n if (config.chunkSize > MAX_CHUNK_SIZE) {\n throw new InvalidConfigError(\n \"config.chunkSize\",\n // Lambda 15-min cap leaves no useful headroom past ~3600 frames\n // at 4 fps capture-equivalent throughput; rejecting up front\n // avoids a 14-minute Plan-state retry storm.\n `must be \u2264 ${MAX_CHUNK_SIZE} (Lambda 15-min cap); got ${config.chunkSize}`,\n );\n }\n }\n\n if (config.maxParallelChunks !== undefined) {\n if (!Number.isInteger(config.maxParallelChunks) || config.maxParallelChunks < 1) {\n throw new InvalidConfigError(\n \"config.maxParallelChunks\",\n `must be a positive integer; got ${config.maxParallelChunks}`,\n );\n }\n if (config.maxParallelChunks > MAX_PARALLEL_CHUNKS_CEILING) {\n throw new InvalidConfigError(\n \"config.maxParallelChunks\",\n `must be \u2264 ${MAX_PARALLEL_CHUNKS_CEILING}; got ${config.maxParallelChunks}`,\n );\n }\n }\n\n if (config.runtimeCap !== undefined && !ALLOWED_RUNTIME_CAPS.includes(config.runtimeCap)) {\n throw new InvalidConfigError(\n \"config.runtimeCap\",\n `must be one of ${ALLOWED_RUNTIME_CAPS.join(\", \")}; got ${String(config.runtimeCap)}`,\n );\n }\n\n if (config.hdrMode !== undefined && !ALLOWED_HDR_MODES.includes(config.hdrMode)) {\n // `force-hdr` is rejected here on top of the producer's plan-stage\n // rejection \u2014 it makes the typical typo (`\"force-hdr\"` from a copy-\n // paste of in-process config) surface synchronously instead of as a\n // typed Step Functions failure two minutes in.\n throw new InvalidConfigError(\n \"config.hdrMode\",\n `distributed mode supports only ${ALLOWED_HDR_MODES.join(\", \")}; got ${String(config.hdrMode)}`,\n );\n }\n\n if (config.variables !== undefined) {\n validateVariablesPayload(config.variables);\n }\n\n return config;\n}\n\n/**\n * Hard cap on Step Functions Standard workflow execution input \u2014 256 KiB\n * per the AWS limits page. Express workflows cap at 32 KiB; the render\n * stack runs Standard for execution-history visibility, so the larger\n * limit applies. The cap is on the entire serialized input, not just the\n * variables, because users hit it at the wire boundary regardless of\n * which field caused the bloat.\n *\n * Specific to Step Functions Standard. Other workflow runtimes (Temporal,\n * Express SFN, raw Lambda invoke) have different caps; this constant\n * shouldn't be reused for those without confirming the limit.\n */\nexport const MAX_STEP_FUNCTIONS_INPUT_BYTES = 256 * 1024;\n\n/** Pointer to the docs section that explains the URL-your-assets convention. */\nconst LARGE_VARIABLES_DOCS_URL =\n \"https://hyperframes.heygen.com/deploy/templates-on-lambda#working-with-large-variables\";\n\n/**\n * Validate that the serialized Step Functions execution input fits inside\n * the 256 KiB Standard-workflow cap. Measured in UTF-8 bytes (the format\n * Step Functions uses on the wire) \u2014 JS strings count UTF-16 code units,\n * which under-reports for any multi-byte character.\n *\n * Throws {@link InvalidConfigError} with a clear message naming the actual\n * byte count, the cap, and a pointer to the \"working with large variables\"\n * docs section, so users hit the limit at the SDK boundary with actionable\n * guidance instead of as a `States.DataLimitExceeded` 50 ms into the\n * execution.\n */\n// fallow-ignore-next-line complexity\nexport function validateStepFunctionsInputSize(input: unknown): void {\n let serialized: string | undefined;\n try {\n serialized = JSON.stringify(input);\n } catch (err) {\n // JSON.stringify throws on circular refs and BigInt. The variables\n // walker catches both inside `config.variables`, but a non-variables\n // field could hit the same case in a future field addition.\n throw new InvalidConfigError(\n \"config\",\n `Step Functions execution input is not JSON-serializable: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (serialized === undefined) {\n // JSON.stringify returns undefined for non-serializable roots\n // (functions, Symbols at the top level).\n throw new InvalidConfigError(\n \"config\",\n \"Step Functions execution input is not JSON-serializable (JSON.stringify returned undefined). \" +\n \"Check that all fields, including config.variables, are plain JSON values.\",\n );\n }\n const byteLength = Buffer.byteLength(serialized, \"utf8\");\n if (byteLength > MAX_STEP_FUNCTIONS_INPUT_BYTES) {\n throw new InvalidConfigError(\n \"config\",\n `Step Functions execution input is ${byteLength} bytes, which exceeds the ` +\n `${MAX_STEP_FUNCTIONS_INPUT_BYTES}-byte (256 KiB) limit for Standard workflows. ` +\n `Variables are for typed data (strings, numbers, structured records); media assets ` +\n `(images, audio, video) should be passed as URL references the composition resolves ` +\n `at render time, not inlined as base64. See ${LARGE_VARIABLES_DOCS_URL} for the ` +\n `URL-your-assets convention.`,\n );\n }\n}\n\n/**\n * Validate that `variables` is a plain JSON-safe object \u2014 no functions,\n * Symbols, `undefined` leaves, BigInts, non-finite numbers, or non-plain\n * objects (Dates, Maps, Sets, class instances). Rejected values would\n * either round-trip incorrectly through Step Functions (`undefined` is\n * silently dropped by `JSON.stringify`) or throw at the wire boundary\n * (`bigint`), so we surface the offending path synchronously.\n *\n * The check is purely structural \u2014 semantic constraints (e.g. \"is this\n * variable declared in `data-composition-variables`?\") belong to the CLI\n * layer where the project's HTML is on disk.\n */\nexport function validateVariablesPayload(value: unknown): void {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new InvalidConfigError(\n \"config.variables\",\n `must be a plain JSON object (got ${describeValue(value)})`,\n );\n }\n walkVariables(value, \"config.variables\", new WeakSet());\n}\n\n/** Per-typeof rejection messages for JSON-unsafe leaves. */\nconst LEAF_REJECTIONS: Partial<Record<string, string>> = {\n // `JSON.stringify` silently drops `undefined` leaves \u2014 caller would never\n // notice their value isn't actually being sent.\n undefined:\n \"undefined leaves are silently dropped by JSON.stringify \u2014 use null if you mean an absent value\",\n function: \"functions are not JSON-serializable\",\n symbol: \"Symbols are not JSON-serializable\",\n bigint: \"BigInt values throw at JSON.stringify \u2014 encode as a string if you need 64-bit integers\",\n};\n\n// fallow-ignore-next-line complexity\nfunction walkVariables(value: unknown, path: string, seen: WeakSet<object>): void {\n const t = typeof value;\n if (value === null || t === \"string\" || t === \"boolean\") return;\n if (t === \"number\") {\n if (!Number.isFinite(value as number)) {\n throw new InvalidConfigError(\n path,\n `non-finite numbers (NaN / Infinity) are not JSON-serializable; got ${String(value)}`,\n );\n }\n return;\n }\n const leafReject = LEAF_REJECTIONS[t];\n if (leafReject !== undefined) {\n throw new InvalidConfigError(path, leafReject);\n }\n // t === \"object\" from here on. Reject circular refs up front \u2014 recursing\n // through a back-edge would stack-overflow with no actionable error.\n if (seen.has(value as object)) {\n throw new InvalidConfigError(\n path,\n \"circular reference detected \u2014 JSON.stringify cannot serialize cycles\",\n );\n }\n seen.add(value as object);\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n walkVariables(value[i], `${path}[${i}]`, seen);\n }\n return;\n }\n // Reject non-plain objects (Date, Map, Set, class instances) up front.\n // Date's `toJSON` does round-trip as a string, but the composition gets a\n // string, not a Date \u2014 explicit reject is clearer than silent type-loss.\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) {\n throw new InvalidConfigError(\n path,\n `non-plain objects are not supported (got ${describeValue(value)}); use a plain {\u2026} object`,\n );\n }\n for (const key of Object.keys(value as Record<string, unknown>)) {\n walkVariables((value as Record<string, unknown>)[key], `${path}.${key}`, seen);\n }\n}\n\n// fallow-ignore-next-line complexity\nfunction describeValue(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n if (typeof value !== \"object\") return typeof value;\n // Class instances expose their constructor name; plain objects fall through\n // to the generic \"object\" label. `Object.create(null)` has no constructor \u2014\n // treat its absent name the same as \"Object\" for reporting.\n const ctorName = (value as { constructor?: { name?: string } }).constructor?.name ?? \"Object\";\n return ctorName === \"Object\" ? \"object\" : ctorName;\n}\n\nfunction validateIntDimension(field: string, value: unknown): void {\n if (typeof value !== \"number\" || !Number.isInteger(value)) {\n throw new InvalidConfigError(field, `must be an integer; got ${String(value)}`);\n }\n if (value < MIN_DIMENSION || value > MAX_DIMENSION) {\n throw new InvalidConfigError(\n field,\n `must be in [${MIN_DIMENSION}, ${MAX_DIMENSION}]; got ${value}`,\n );\n }\n if (value % 2 !== 0) {\n // libx264 / libx265 yuv420p require even dimensions; rejecting now\n // beats a Plan-stage ffmpeg crash on dimension parity.\n throw new InvalidConfigError(field, `must be even (yuv420p constraint); got ${value}`);\n }\n}\n", "/**\n * `getRenderProgress` \u2014 read-only progress + cost snapshot for a single\n * render started by {@link renderToLambda}.\n *\n * Pulls one `DescribeExecution` + one `GetExecutionHistory` per call. The\n * history is paginated server-side; the helper loops until exhausted so a\n * 1,000-event Step Functions execution still produces a single\n * `RenderProgress` snapshot.\n *\n * Progress math:\n * - 0 before Plan completes (no frame count is known yet)\n * - 0.1 once Plan completes (we know `totalFrames`)\n * - 0.1 + 0.8 \u00D7 framesEncoded / totalFrames during chunk render\n * - 1.0 after Assemble completes\n *\n * Frame counts come from the parsed Lambda result payloads on each\n * `TaskSucceeded` event \u2014 Plan reports `TotalFrames`, RenderChunk reports\n * `FramesEncoded`. The shape mirrors what the handler produces in\n * `events.ts`, so the parser doesn't need to know anything beyond\n * \"JSON.parse this string and grab two fields.\"\n */\n\nimport {\n DescribeExecutionCommand,\n GetExecutionHistoryCommand,\n type HistoryEvent,\n SFNClient,\n} from \"@aws-sdk/client-sfn\";\nimport {\n type BilledLambdaInvocation,\n computeRenderCost,\n type RenderCost,\n} from \"./costAccounting.js\";\n\n/** Options for {@link getRenderProgress}. */\nexport interface GetRenderProgressOptions {\n /** Execution ARN from a {@link renderToLambda} call. */\n executionArn: string;\n /**\n * Default memory size in MB to assume for Lambda invocations when the\n * history event payload doesn't carry it explicitly. Matches the\n * `LambdaMemoryMb` parameter the stack was deployed with.\n */\n defaultMemorySizeMb?: number;\n region?: string;\n /** Test injection seam. */\n sfn?: SFNClient;\n}\n\n/** Render-status discriminant; mirrors Step Functions execution states. */\nexport type RenderStatus =\n | \"RUNNING\"\n | \"SUCCEEDED\"\n | \"FAILED\"\n | \"TIMED_OUT\"\n | \"ABORTED\"\n | \"PENDING_REDRIVE\";\n\nexport interface RenderError {\n /** State name where the failure surfaced (`Plan`, `RenderChunk`, `Assemble`, or `<unknown>`). */\n state: string;\n /** Error class / type as Step Functions reports it. */\n error: string;\n /** Cause string Step Functions surfaces (often a stringified JSON payload from the handler). */\n cause: string;\n}\n\n/** Snapshot of a single render's progress + cost + errors at one point in time. */\nexport interface RenderProgress {\n status: RenderStatus;\n /** `[0, 1]`; see module doc for the math. */\n overallProgress: number;\n framesRendered: number;\n /** `null` until Plan completes. */\n totalFrames: number | null;\n /** Total Lambda invocations scheduled so far (both optimized + raw task integrations). */\n lambdasInvoked: number;\n costs: RenderCost;\n /** Final output object if Assemble succeeded; `null` otherwise. */\n outputFile: { s3Uri: string; bytes: number | null } | null;\n errors: RenderError[];\n /** `true` once the execution has terminated in a non-`SUCCEEDED` state. */\n fatalErrorEncountered: boolean;\n startedAt: string;\n endedAt: string | null;\n}\n\nconst DEFAULT_MEMORY_MB = 10240;\n\n/** Pull a current progress snapshot for one render. */\nexport async function getRenderProgress(opts: GetRenderProgressOptions): Promise<RenderProgress> {\n if (!opts.executionArn) {\n throw new Error(\"[getRenderProgress] executionArn is required\");\n }\n const sfn = opts.sfn ?? new SFNClient({ region: opts.region });\n const memoryMb = opts.defaultMemorySizeMb ?? DEFAULT_MEMORY_MB;\n\n const describe = await sfn.send(\n new DescribeExecutionCommand({ executionArn: opts.executionArn }),\n );\n const status = (describe.status ?? \"RUNNING\") as RenderStatus;\n const startedAt = describe.startDate?.toISOString() ?? new Date(0).toISOString();\n const endedAt = describe.stopDate?.toISOString() ?? null;\n\n const history = await loadFullHistory(sfn, opts.executionArn);\n const summary = summarizeHistory(history, memoryMb);\n\n const costs = computeRenderCost(summary.lambdaInvocations, summary.stateTransitions);\n const overallProgress = computeOverallProgress({\n status,\n totalFrames: summary.totalFrames,\n framesRendered: summary.framesRendered,\n assembleComplete: summary.assembleComplete,\n });\n\n return {\n status,\n overallProgress,\n framesRendered: summary.framesRendered,\n totalFrames: summary.totalFrames,\n lambdasInvoked: summary.lambdasInvoked,\n costs,\n outputFile: summary.outputFile,\n errors: summary.errors,\n fatalErrorEncountered: isTerminalFailure(status),\n startedAt,\n endedAt,\n };\n}\n\nasync function loadFullHistory(sfn: SFNClient, executionArn: string): Promise<HistoryEvent[]> {\n const events: HistoryEvent[] = [];\n let nextToken: string | undefined;\n for (let page = 0; page < 50; page++) {\n const res = await sfn.send(\n new GetExecutionHistoryCommand({\n executionArn,\n maxResults: 1000,\n nextToken,\n reverseOrder: false,\n }),\n );\n if (res.events) events.push(...res.events);\n nextToken = res.nextToken;\n if (!nextToken) break;\n }\n return events;\n}\n\ninterface HistorySummary {\n lambdaInvocations: BilledLambdaInvocation[];\n stateTransitions: number;\n framesRendered: number;\n totalFrames: number | null;\n lambdasInvoked: number;\n assembleComplete: boolean;\n outputFile: { s3Uri: string; bytes: number | null } | null;\n errors: RenderError[];\n}\n\n/**\n * One pass over the history events that pulls every number {@link getRenderProgress}\n * needs. State transitions = the count of events that advance the state\n * machine (entering/exiting states + map iteration completions). Lambda\n * invocations = `LambdaFunctionScheduled` count. Frame totals come from\n * the success-payload of each Lambda invocation.\n */\nfunction summarizeHistory(events: HistoryEvent[], memoryMb: number): HistorySummary {\n let framesRendered = 0;\n let totalFrames: number | null = null;\n let lambdasInvoked = 0;\n let assembleComplete = false;\n let outputFile: HistorySummary[\"outputFile\"] = null;\n let stateTransitions = 0;\n const errors: RenderError[] = [];\n const lambdaInvocations: BilledLambdaInvocation[] = [];\n\n // Track the state name we most recently entered, so we can:\n // - attach the enclosing state to LambdaFunctionFailed errors, and\n // - identify when the Assemble state finished (StateExited.Assemble)\n // without relying on the inner Lambda payload's `Action` field.\n let currentLambdaState: string | null = null;\n\n for (const ev of events) {\n switch (ev.type) {\n case \"TaskStateEntered\":\n case \"MapStateEntered\":\n case \"PassStateEntered\":\n case \"ChoiceStateEntered\":\n case \"SucceedStateEntered\":\n case \"FailStateEntered\":\n case \"WaitStateEntered\":\n case \"ParallelStateEntered\":\n // Step Functions Standard Workflows bill per *state entry*, not per\n // history event. Lambda invocations produce ~5-7 history events\n // each (Scheduled / Started / Succeeded / TaskStateExited / \u2026);\n // counting every event as a transition over-reports cost by 3-5\u00D7.\n stateTransitions++;\n currentLambdaState = ev.stateEnteredEventDetails?.name ?? currentLambdaState;\n break;\n // Optimized `lambda:invoke` task emits Task* events; raw\n // `lambda:invokeFunction.sync` emits LambdaFunction*. Handle both.\n case \"TaskScheduled\":\n if (ev.taskScheduledEventDetails?.resourceType === \"lambda\") {\n lambdasInvoked++;\n }\n break;\n case \"LambdaFunctionScheduled\":\n lambdasInvoked++;\n break;\n case \"TaskSucceeded\": {\n if (ev.taskSucceededEventDetails?.resourceType !== \"lambda\") break;\n const wrapped = parseJson(ev.taskSucceededEventDetails?.output);\n const payload = unwrapLambdaPayload(wrapped);\n const billedDurationMs = inferBilledMs(payload);\n lambdaInvocations.push({\n billedDurationMs,\n memorySizeMb: memoryMb,\n estimated: billedDurationMs === 0,\n });\n applyPayloadFrameCounts(payload, currentLambdaState, (delta) => {\n framesRendered += delta;\n });\n if (payload && typeof payload === \"object\") {\n const obj = payload as Record<string, unknown>;\n if (typeof obj.TotalFrames === \"number\") totalFrames = obj.TotalFrames;\n }\n break;\n }\n case \"LambdaFunctionSucceeded\": {\n const payload = parseJson(ev.lambdaFunctionSucceededEventDetails?.output);\n const billedDurationMs = inferBilledMs(payload);\n lambdaInvocations.push({\n billedDurationMs,\n memorySizeMb: memoryMb,\n estimated: billedDurationMs === 0,\n });\n applyPayloadFrameCounts(payload, currentLambdaState, (delta) => {\n framesRendered += delta;\n });\n if (payload && typeof payload === \"object\") {\n const obj = payload as Record<string, unknown>;\n if (typeof obj.TotalFrames === \"number\") totalFrames = obj.TotalFrames;\n }\n break;\n }\n case \"TaskStateExited\":\n case \"MapStateExited\":\n // Mark the assemble step complete on its state-exit, independent\n // of the inner Lambda payload shape. The Assemble state's\n // ResultSelector pulls FileSize + OutputS3Uri from the Lambda\n // result, so we re-extract them here from the state exit's\n // own output rather than relying on the Lambda payload.\n if (ev.stateExitedEventDetails?.name === \"Assemble\") {\n assembleComplete = true;\n const exitPayload = parseJson(ev.stateExitedEventDetails?.output);\n if (exitPayload && typeof exitPayload === \"object\") {\n const obj = exitPayload as Record<string, unknown>;\n const out = obj.Output as Record<string, unknown> | undefined;\n const outputS3Uri = typeof out?.OutputS3Uri === \"string\" ? out.OutputS3Uri : null;\n const bytes = typeof out?.FileSize === \"number\" ? out.FileSize : null;\n outputFile = outputS3Uri ? { s3Uri: outputS3Uri, bytes } : outputFile;\n }\n }\n break;\n case \"TaskFailed\":\n if (ev.taskFailedEventDetails?.resourceType !== \"lambda\") break;\n errors.push({\n state: currentLambdaState ?? \"<unknown>\",\n error: ev.taskFailedEventDetails?.error ?? \"UNKNOWN\",\n cause: ev.taskFailedEventDetails?.cause ?? \"\",\n });\n break;\n case \"LambdaFunctionFailed\":\n errors.push({\n state: currentLambdaState ?? \"<unknown>\",\n error: ev.lambdaFunctionFailedEventDetails?.error ?? \"UNKNOWN\",\n cause: ev.lambdaFunctionFailedEventDetails?.cause ?? \"\",\n });\n break;\n case \"ExecutionFailed\":\n errors.push({\n state: \"<execution>\",\n error: ev.executionFailedEventDetails?.error ?? \"UNKNOWN\",\n cause: ev.executionFailedEventDetails?.cause ?? \"\",\n });\n break;\n case \"ExecutionAborted\":\n errors.push({\n state: \"<execution>\",\n error: ev.executionAbortedEventDetails?.error ?? \"ABORTED\",\n cause: ev.executionAbortedEventDetails?.cause ?? \"\",\n });\n break;\n case \"ExecutionTimedOut\":\n errors.push({\n state: \"<execution>\",\n error: \"TIMEOUT\",\n cause: ev.executionTimedOutEventDetails?.cause ?? \"\",\n });\n break;\n default:\n break;\n }\n }\n\n return {\n lambdaInvocations,\n stateTransitions,\n framesRendered,\n totalFrames,\n lambdasInvoked,\n assembleComplete,\n outputFile,\n errors,\n };\n}\n\nfunction parseJson(s: string | undefined): unknown {\n if (!s) return null;\n try {\n return JSON.parse(s);\n } catch {\n return null;\n }\n}\n\n/**\n * Optimized `lambda:invoke` wraps the Lambda response as\n * `{ ExecutedVersion, Payload: {\u2026handler payload\u2026}, StatusCode }`. Raw\n * `lambda:invokeFunction.sync` puts the handler payload at the root.\n * Return the inner `Payload` when present so callers read the same fields\n * either way.\n */\nfunction unwrapLambdaPayload(payload: unknown): unknown {\n if (payload && typeof payload === \"object\" && \"Payload\" in payload) {\n const inner = (payload as { Payload: unknown }).Payload;\n if (inner && typeof inner === \"object\") return inner;\n }\n return payload;\n}\n\n/**\n * Bump `framesRendered` only inside the `RenderChunk` state. Plan and\n * Assemble also report `FramesEncoded`, so a state-blind add would\n * double-count once Assemble runs.\n */\nfunction applyPayloadFrameCounts(\n payload: unknown,\n currentLambdaState: string | null,\n bump: (delta: number) => void,\n): void {\n if (currentLambdaState !== \"RenderChunk\") return;\n if (!payload || typeof payload !== \"object\") return;\n const obj = payload as Record<string, unknown>;\n if (typeof obj.FramesEncoded === \"number\") bump(obj.FramesEncoded);\n}\n\n/**\n * Lambda success payloads from our handler include `DurationMs` \u2014 the\n * wall-clock the handler observed. We use it as a best-effort proxy\n * for `BilledDuration` when SFN doesn't expose the latter directly\n * on `LambdaFunctionSucceeded` (the dedicated `BilledDuration` field\n * is in CloudWatch Metrics, not the SFN history payload).\n */\nfunction inferBilledMs(payload: unknown): number {\n if (!payload || typeof payload !== \"object\") return 0;\n const obj = payload as Record<string, unknown>;\n if (typeof obj.DurationMs === \"number\") return obj.DurationMs;\n return 0;\n}\n\ninterface ComputeProgressArgs {\n status: RenderStatus;\n totalFrames: number | null;\n framesRendered: number;\n assembleComplete: boolean;\n}\n\nfunction computeOverallProgress({\n status,\n totalFrames,\n framesRendered,\n assembleComplete,\n}: ComputeProgressArgs): number {\n if (status === \"SUCCEEDED\") return 1;\n if (assembleComplete) return 1;\n if (totalFrames === null) return 0;\n // 10 % Plan + 80 % chunk render + 10 % Assemble.\n const chunkProgress = Math.min(1, framesRendered / totalFrames);\n return 0.1 + 0.8 * chunkProgress;\n}\n\nfunction isTerminalFailure(status: RenderStatus): boolean {\n return status === \"FAILED\" || status === \"TIMED_OUT\" || status === \"ABORTED\";\n}\n", "/**\n * Per-render cost accounting for {@link getRenderProgress}.\n *\n * AWS bills Lambda by **GB-seconds** (billed-duration \u00D7 memory-in-GiB)\n * and Step Functions standard workflows by **state transitions**. Both\n * inputs are recoverable from the SFN execution history without an\n * extra CloudWatch query \u2014 the history events carry\n * `billedDurationInMillis` and `memorySizeInMB` on each Lambda\n * invocation, and the transition count is simply `history.length`\n * filtered to transition-worthy events.\n *\n * The math is documented inline so the constants stay close to the\n * pricing source they came from. Cost is **best-effort**: AWS pricing\n * varies by region + commitment plan; we use on-demand `us-east-1`\n * rates as of 2026-05 and label the result `displayCost` so callers\n * see the dollar value but downstream automation can also read the\n * raw number.\n */\n\n/** On-demand Lambda price, us-east-1, x86_64, on-demand: USD per GB-second. */\nconst LAMBDA_USD_PER_GB_SECOND = 0.0000166667;\n/** Step Functions Standard Workflows, us-east-1: USD per state transition. */\nconst SFN_USD_PER_TRANSITION = 0.000025;\n\n/** Raw history event subset the cost calc cares about. Caller filters from `getExecutionHistory`. */\nexport interface BilledLambdaInvocation {\n /** Millis of Lambda billed duration. Carried on `TaskSucceeded`/`TaskFailed` events. */\n billedDurationMs: number;\n /** Memory size in MB the function was configured with at invocation time. */\n memorySizeMb: number;\n /** `true` if the event payload did NOT carry a billed duration and we fell back to `Duration` or a constant. */\n estimated: boolean;\n}\n\n/** Result of {@link computeRenderCost}. */\nexport interface RenderCost {\n /** USD accrued to date. */\n accruedSoFarUsd: number;\n /** Human-readable USD string, e.g. `\"$0.0214\"`. */\n displayCost: string;\n breakdown: {\n lambdaUsd: number;\n stepFunctionsUsd: number;\n /** S3 transfer + storage cost varies by tier; we don't try to compute it here. */\n s3Estimate: \"not-included\";\n /** `true` if any Lambda invocation fell back to estimated billing. */\n estimated: boolean;\n };\n}\n\n/**\n * Sum Lambda GB-seconds + SFN transitions into an aggregate USD figure.\n *\n * `stateTransitions` is the count of billable state-machine transitions\n * \u2014 every successful state entry transitions once for standard\n * workflows. Express workflows price differently and are out of scope.\n */\nexport function computeRenderCost(\n lambdaInvocations: BilledLambdaInvocation[],\n stateTransitions: number,\n): RenderCost {\n let lambdaUsd = 0;\n let anyEstimated = false;\n for (const inv of lambdaInvocations) {\n const gbSeconds = (inv.memorySizeMb / 1024) * (inv.billedDurationMs / 1000);\n lambdaUsd += gbSeconds * LAMBDA_USD_PER_GB_SECOND;\n if (inv.estimated) anyEstimated = true;\n }\n const stepFunctionsUsd = stateTransitions * SFN_USD_PER_TRANSITION;\n const accruedSoFarUsd = roundUsd(lambdaUsd + stepFunctionsUsd);\n return {\n accruedSoFarUsd,\n displayCost: formatUsd(accruedSoFarUsd),\n breakdown: {\n lambdaUsd: roundUsd(lambdaUsd),\n stepFunctionsUsd: roundUsd(stepFunctionsUsd),\n s3Estimate: \"not-included\",\n estimated: anyEstimated,\n },\n };\n}\n\nfunction roundUsd(usd: number): number {\n // Four decimal places \u2014 enough resolution for per-chunk granularity on\n // a 10 GB Lambda. Anything finer is noise vs AWS' own rounding.\n return Math.round(usd * 10_000) / 10_000;\n}\n\nfunction formatUsd(usd: number): string {\n return `$${usd.toFixed(4)}`;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["existsSync", "mkdirSync", "rmSync", "statSync", "existsSync", "existsSync", "statSync", "uri", "mkdirSync", "rmSync", "mkdtempSync", "
|
|
4
|
+
"sourcesContent": ["/**\n * AWS Lambda handler for HyperFrames distributed rendering.\n *\n * One Lambda function, three roles. Step Functions dispatches by setting\n * `event.Action`; the handler unwraps Map-state envelopes, primes the\n * Lambda environment (Chrome path, ffmpeg path, tmpdir), and forwards to\n * the matching OSS primitive from `@hyperframes/producer/distributed`.\n *\n * Everything heavy \u2014 capture, encode, audio mix \u2014 happens inside the OSS\n * primitives. The handler is thin glue: parse event \u2192 S3 download \u2192 call\n * primitive \u2192 S3 upload \u2192 return small JSON result.\n */\n\nimport { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { basename, join } from \"node:path\";\nimport { S3Client } from \"@aws-sdk/client-s3\";\nimport {\n assemble,\n type AssembleResult,\n type ChunkResult,\n type DistributedRenderConfig,\n plan,\n type PlanResult,\n renderChunk,\n} from \"@hyperframes/producer/distributed\";\nimport { resolveChromeExecutablePath } from \"./chromium.js\";\nimport { type DistributedFormat, formatExtension } from \"./formatExtension.js\";\nimport type {\n AssembleEvent,\n AssembleLambdaResult,\n LambdaAction,\n LambdaEvent,\n LambdaResult,\n PlanEvent,\n PlanLambdaResult,\n RenderChunkEvent,\n RenderChunkLambdaResult,\n} from \"./events.js\";\nimport {\n downloadS3ObjectToFile,\n parseS3Uri,\n tarDirectory,\n untarDirectory,\n uploadFileToS3,\n} from \"./s3Transport.js\";\n\n/**\n * Lazily-constructed S3 client. Cached at module scope so warm Lambda\n * containers reuse the underlying HTTP keep-alive pool across invocations.\n */\nlet cachedS3Client: S3Client | null = null;\nfunction getS3Client(): S3Client {\n if (cachedS3Client) return cachedS3Client;\n cachedS3Client = new S3Client({});\n return cachedS3Client;\n}\n\n/**\n * Optional injection points used by the handler's unit tests. Production\n * callers leave these unset; the real OSS primitives are used. Tests\n * inject `s3` and `primitives` directly rather than mutating module\n * state \u2014 the dependency-injection seam is sufficient and avoids a\n * second leak point for cross-test contamination.\n */\nexport interface HandlerDeps {\n s3?: S3Client;\n primitives?: {\n plan: typeof plan;\n renderChunk: typeof renderChunk;\n assemble: typeof assemble;\n };\n /** Override the per-invocation `/tmp` workdir root (defaults to Lambda's `/tmp`). */\n tmpRoot?: string;\n /** Skip Chrome resolution (used by handler dispatch tests that mock renderChunk). */\n skipChromeResolution?: boolean;\n}\n\n/**\n * Lambda entry. Step Functions sometimes wraps the event in\n * `{ Payload: ... }` or `{ Input: ... }` depending on the state machine\n * shape; unwrap until we hit a discriminated event.\n */\nexport async function handler(event: LambdaEvent, deps?: HandlerDeps): Promise<LambdaResult> {\n const unwrapped = unwrapEvent(event);\n validateEventS3Uris(unwrapped);\n primeRuntimeEnv();\n // Single structured boot log line \u2014 CloudWatch Logs Insights queries\n // key off `event=handler_start` to grep for a specific Action / S3 URI\n // when triaging without attaching a debugger.\n logEvent({ event: \"handler_start\", action: unwrapped.Action, input: summarizeEvent(unwrapped) });\n try {\n switch (unwrapped.Action) {\n case \"plan\":\n return await handlePlan(unwrapped, deps);\n case \"renderChunk\":\n return await handleRenderChunk(unwrapped, deps);\n case \"assemble\":\n return await handleAssemble(unwrapped, deps);\n default: {\n // Compile-time exhaustiveness: a new LambdaAction member trips\n // the `never` assignment before the runtime error is reachable.\n const _exhaustive: never = unwrapped;\n throw new Error(\n `[handler] unknown Action: ${JSON.stringify(\n (_exhaustive as { Action?: string }).Action,\n )}. Expected one of \"plan\", \"renderChunk\", \"assemble\".`,\n );\n }\n }\n } catch (err) {\n // Log before re-throwing so CloudWatch captures the structured\n // error context alongside Lambda's default stack trace. Otherwise\n // ops only sees the trace and has to correlate with execution\n // history to recover the action + input.\n logEvent({\n event: \"handler_error\",\n action: unwrapped.Action,\n message: err instanceof Error ? err.message : String(err),\n name: err instanceof Error ? err.name : undefined,\n });\n throw err;\n }\n}\n\n/**\n * Walk through Step Functions' Map-state and Task-state envelopes until\n * the discriminated event is found.\n */\n// Step Functions wraps at most `{Payload: {Input: ...}}` in our state\n// machine; 4 levels is 2\u00D7 headroom for unusual Map / Wait state\n// configurations and prevents infinite loops on malformed input.\nconst MAX_ENVELOPE_DEPTH = 4;\n\nexport function unwrapEvent(event: LambdaEvent): PlanEvent | RenderChunkEvent | AssembleEvent {\n let cursor: LambdaEvent = event;\n for (let i = 0; i < MAX_ENVELOPE_DEPTH; i++) {\n if (cursor && typeof cursor === \"object\") {\n const obj = cursor as Record<string, unknown>;\n if (typeof obj.Action === \"string\" && isLambdaAction(obj.Action)) {\n return cursor as PlanEvent | RenderChunkEvent | AssembleEvent;\n }\n if (\"Payload\" in obj) {\n cursor = obj.Payload as LambdaEvent;\n continue;\n }\n if (\"Input\" in obj) {\n cursor = obj.Input as LambdaEvent;\n continue;\n }\n }\n break;\n }\n throw new Error(\n `[handler] event has no recognised Action; unwrapped ${MAX_ENVELOPE_DEPTH} levels of Payload/Input without finding one.`,\n );\n}\n\nfunction isLambdaAction(value: string): value is LambdaAction {\n return value === \"plan\" || value === \"renderChunk\" || value === \"assemble\";\n}\n\n/**\n * Emit a single JSON line to stdout. CloudWatch ingests each line as a\n * structured event; Logs Insights queries can `filter event=\"...\"` and\n * project specific fields. We write to stdout (not stderr) because\n * Lambda's default destination for both is the same log group, and\n * Logs Insights' INFO/ERROR level parser keys off the JSON `level`\n * field, not the stream.\n */\nfunction logEvent(payload: Record<string, unknown>): void {\n console.log(JSON.stringify(payload));\n}\n\n/**\n * Compact, non-PII summary of a Lambda event for logging. The full\n * event payload can include the entire project config; we only emit\n * the routable fields (S3 URIs, chunk index, format) needed to triage\n * a failure from CloudWatch.\n */\nfunction summarizeEvent(\n event: PlanEvent | RenderChunkEvent | AssembleEvent,\n): Record<string, unknown> {\n switch (event.Action) {\n case \"plan\":\n return {\n projectS3Uri: event.ProjectS3Uri,\n planOutputS3Prefix: event.PlanOutputS3Prefix,\n format: event.Config.format,\n fps: event.Config.fps,\n };\n case \"renderChunk\":\n return {\n planS3Uri: event.PlanS3Uri,\n chunkIndex: event.ChunkIndex,\n format: event.Format,\n };\n case \"assemble\":\n return {\n planS3Uri: event.PlanS3Uri,\n chunkCount: event.ChunkS3Uris.length,\n hasAudio: event.AudioS3Uri !== null,\n outputS3Uri: event.OutputS3Uri,\n format: event.Format,\n };\n }\n}\n\n/**\n * Lambda sets `TMPDIR` to `/tmp` already, but the bundled binaries (Chrome\n * + ffmpeg) live alongside the handler at `/var/task/bin/`. Add that to\n * PATH the first time the handler runs so spawn(\"ffmpeg\", \u2026) inside the\n * OSS primitives resolves to the bundled binary.\n */\nlet runtimeEnvPrimed = false;\nfunction primeRuntimeEnv(): void {\n if (runtimeEnvPrimed) return;\n runtimeEnvPrimed = true;\n const taskRoot = process.env.LAMBDA_TASK_ROOT ?? \"/var/task\";\n const bin = join(taskRoot, \"bin\");\n if (existsSync(bin)) {\n process.env.PATH = `${bin}:${process.env.PATH ?? \"\"}`;\n }\n}\n\n// \u2500\u2500 Plan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function handlePlan(event: PlanEvent, deps?: HandlerDeps): Promise<PlanLambdaResult> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.plan ?? plan;\n\n // The producer's probe stage launches Chromium whenever the composition\n // needs a runtime duration probe or has unresolved sub-compositions, so\n // plan has to resolve Chrome the same way renderChunk does. Without this\n // the probe throws \"An `executablePath` or `channel` must be specified\n // for `puppeteer-core`\" the moment runProbeStage calls puppeteer.launch.\n if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n const chromePath = await resolveChromeExecutablePath();\n process.env.PRODUCER_HEADLESS_SHELL_PATH = chromePath;\n }\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-plan-\"));\n // We use `.tar.gz` (not `.zip`) as the project archive's on-the-wire\n // format because Lambda's Amazon Linux base image ships GNU `tar` but\n // not `unzip` in `/usr/bin`. The smoke script + future CLI both\n // produce tar.gz uploads.\n const projectArchive = join(work, \"project.tar.gz\");\n const projectDir = join(work, \"project\");\n const planDir = join(work, \"plan\");\n\n try {\n await downloadS3ObjectToFile(s3, event.ProjectS3Uri, projectArchive);\n await untarDirectory(projectArchive, projectDir);\n\n const config: DistributedRenderConfig = {\n ...event.Config,\n };\n const result: PlanResult = await primitive(projectDir, config, planDir);\n\n // Upload the planDir as a single tarball. Step Functions cannot pass\n // a directory-shaped artifact between states; we serialize and rely on\n // the consumer (renderChunk / assemble) to untar. Audio is co-located\n // alongside the plan so RenderChunk doesn't have to pull the whole\n // plan tarball when audio isn't relevant to the chunk.\n const planTar = join(work, \"plan.tar.gz\");\n await tarDirectory(planDir, planTar);\n const planTarUri = `${trimTrailingSlash(event.PlanOutputS3Prefix)}/plan.tar.gz`;\n const audioPath = join(planDir, \"audio.aac\");\n const hasAudio = existsSync(audioPath) && statSync(audioPath).size > 0;\n const audioUri = hasAudio ? `${trimTrailingSlash(event.PlanOutputS3Prefix)}/audio.aac` : null;\n // Plan and audio are independent S3 PUTs; run them in parallel so\n // the response returns as soon as the slower of the two completes.\n await Promise.all([\n uploadFileToS3(s3, planTar, planTarUri, \"application/gzip\"),\n hasAudio && audioUri ? uploadFileToS3(s3, audioPath, audioUri, \"audio/aac\") : null,\n ]);\n\n return {\n Action: \"plan\",\n PlanS3Uri: planTarUri,\n PlanHash: result.planHash,\n ChunkCount: result.chunkCount,\n TotalFrames: result.totalFrames,\n Fps: result.fps,\n Width: result.width,\n Height: result.height,\n Format: result.format,\n HasAudio: audioUri !== null,\n AudioS3Uri: audioUri,\n FfmpegVersion: result.ffmpegVersion,\n ProducerVersion: result.producerVersion,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\n// \u2500\u2500 RenderChunk \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function handleRenderChunk(\n event: RenderChunkEvent,\n deps?: HandlerDeps,\n): Promise<RenderChunkLambdaResult> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.renderChunk ?? renderChunk;\n\n // Sparticuz decompresses Chromium into /tmp on first call; warm starts\n // skip the work (path already cached). Guard the env-var mutation too so\n // a caller-supplied PRODUCER_HEADLESS_SHELL_PATH (e.g. the SAM-local\n // RIE smoke) wins over the auto-resolution.\n if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n const chromePath = await resolveChromeExecutablePath();\n // The OSS engine resolves Chrome via `PRODUCER_HEADLESS_SHELL_PATH`\n // first (see `browserManager.resolveHeadlessShellPath`); set it before\n // invoking the primitive so launch picks up the bundled binary.\n process.env.PRODUCER_HEADLESS_SHELL_PATH = chromePath;\n }\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-chunk-\"));\n const planTar = join(work, \"plan.tar.gz\");\n const planDir = join(work, \"plan\");\n\n try {\n await downloadS3ObjectToFile(s3, event.PlanS3Uri, planTar);\n await untarDirectory(planTar, planDir);\n\n // Verify the plan's hash matches what Step Functions told us to render.\n // The producer's renderChunk re-checks internally (defense-in-depth),\n // but doing it here at the handler boundary lets us fail before paying\n // the Chrome-launch + render cost on a misrouted chunk. Throws a\n // typed PLAN_HASH_MISMATCH that Step Functions can route as\n // non-retryable.\n verifyPlanHash(planDir, event.PlanHash);\n\n const chunkOutputBase = join(\n work,\n event.Format === \"png-sequence\"\n ? `chunk-${pad(event.ChunkIndex)}`\n : `chunk-${pad(event.ChunkIndex)}${formatExtension(event.Format)}`,\n );\n\n const result: ChunkResult = await primitive(planDir, event.ChunkIndex, chunkOutputBase);\n\n const chunkUri = await uploadChunkOutput(\n s3,\n result,\n event.ChunkOutputS3Prefix,\n event.ChunkIndex,\n );\n\n return {\n Action: \"renderChunk\",\n ChunkS3Uri: chunkUri,\n ChunkIndex: event.ChunkIndex,\n Sha256: result.sha256,\n FramesEncoded: result.framesEncoded,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\nasync function uploadChunkOutput(\n s3: S3Client,\n result: ChunkResult,\n prefix: string,\n chunkIndex: number,\n): Promise<string> {\n const trimmed = trimTrailingSlash(prefix);\n if (result.outputKind === \"file\") {\n const ext = result.outputPath.slice(result.outputPath.lastIndexOf(\".\"));\n const uri = `${trimmed}/chunks/${pad(chunkIndex)}${ext}`;\n await uploadFileToS3(s3, result.outputPath, uri);\n return uri;\n }\n // frame-dir: upload as a tarball so a single S3 object represents the chunk.\n // Assemble's png-sequence path expects a directory per chunk; it untars on\n // its end.\n const tarball = `${result.outputPath}.tar.gz`;\n await tarDirectory(result.outputPath, tarball);\n const uri = `${trimmed}/chunks/${pad(chunkIndex)}.tar.gz`;\n await uploadFileToS3(s3, tarball, uri, \"application/gzip\");\n return uri;\n}\n\n// \u2500\u2500 Assemble \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function handleAssemble(\n event: AssembleEvent,\n deps?: HandlerDeps,\n): Promise<AssembleLambdaResult> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.assemble ?? assemble;\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-assemble-\"));\n const planTar = join(work, \"plan.tar.gz\");\n const planDir = join(work, \"plan\");\n\n try {\n await downloadS3ObjectToFile(s3, event.PlanS3Uri, planTar);\n await untarDirectory(planTar, planDir);\n\n const chunkPaths = await downloadChunkObjects(s3, event.ChunkS3Uris, work, event.Format);\n\n let audioPath: string | null = null;\n if (event.AudioS3Uri) {\n audioPath = join(planDir, \"audio.aac\");\n await downloadS3ObjectToFile(s3, event.AudioS3Uri, audioPath);\n }\n\n const finalOutput =\n event.Format === \"png-sequence\"\n ? join(work, \"output-frames\")\n : join(work, `output${formatExtension(event.Format)}`);\n\n const result: AssembleResult = await primitive(planDir, chunkPaths, audioPath, finalOutput, {\n cfr: event.Cfr === true,\n });\n\n if (event.Format === \"png-sequence\") {\n const tarball = `${finalOutput}.tar.gz`;\n await tarDirectory(finalOutput, tarball);\n await uploadFileToS3(s3, tarball, event.OutputS3Uri, \"application/gzip\");\n } else {\n await uploadFileToS3(s3, finalOutput, event.OutputS3Uri);\n }\n\n return {\n Action: \"assemble\",\n OutputS3Uri: event.OutputS3Uri,\n FramesEncoded: result.framesEncoded,\n FileSize: result.fileSize,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\nasync function downloadChunkObjects(\n s3: S3Client,\n uris: string[],\n workDir: string,\n format: DistributedFormat,\n): Promise<string[]> {\n const chunksDir = join(workDir, \"chunks\");\n mkdirSync(chunksDir, { recursive: true });\n // Each chunk is an independent S3 GET (+ untar for png-sequence). Run\n // them in parallel \u2014 assemble's wall-clock is otherwise dominated by\n // `\u03A3 chunk-download-ms` instead of `max(chunk-download-ms)`. Preserve\n // the input order by writing into a pre-sized array rather than\n // pushing as each task settles.\n const local: string[] = new Array<string>(uris.length);\n await Promise.all(\n uris.map(async (uri, i) => {\n if (!uri) {\n throw new Error(`[handler] chunk URI at index ${i} is empty`);\n }\n const { key } = parseS3Uri(uri);\n const localPath = join(chunksDir, basename(key));\n await downloadS3ObjectToFile(s3, uri, localPath);\n if (format === \"png-sequence\") {\n const dirPath = join(chunksDir, `frames-${pad(i)}`);\n await untarDirectory(localPath, dirPath);\n local[i] = dirPath;\n } else {\n local[i] = localPath;\n }\n }),\n );\n return local;\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Collect every S3 URI that the handler will touch for a given event. */\nfunction getEventS3Uris(event: PlanEvent | RenderChunkEvent | AssembleEvent): string[] {\n switch (event.Action) {\n case \"plan\":\n return [event.ProjectS3Uri, event.PlanOutputS3Prefix];\n case \"renderChunk\":\n return [event.PlanS3Uri, event.ChunkOutputS3Prefix];\n case \"assemble\":\n return [event.PlanS3Uri, ...event.ChunkS3Uris, event.OutputS3Uri, event.AudioS3Uri].filter(\n (u): u is string => u != null,\n );\n }\n}\n\n/**\n * Verify every S3 URI in the event resolves to the configured render bucket.\n * Throws `S3_URI_NOT_ALLOWED` (non-retryable) when a URI targets a different\n * bucket, preventing event injection from reading or writing arbitrary S3 data.\n *\n * Skipped when `HYPERFRAMES_RENDER_BUCKET` is unset so existing deployments\n * without the env var continue to work.\n */\nfunction validateEventS3Uris(event: PlanEvent | RenderChunkEvent | AssembleEvent): void {\n const allowedBucket = process.env.HYPERFRAMES_RENDER_BUCKET?.trim();\n if (!allowedBucket) return;\n\n for (const uri of getEventS3Uris(event)) {\n const { bucket } = parseS3Uri(uri);\n if (bucket !== allowedBucket) {\n const err = new Error(\n `[handler] S3_URI_NOT_ALLOWED: URI ${JSON.stringify(uri)} targets bucket \"${bucket}\" but only \"${allowedBucket}\" is permitted`,\n );\n err.name = \"S3_URI_NOT_ALLOWED\";\n throw err;\n }\n }\n}\n\nfunction pad(n: number): string {\n return n.toString().padStart(4, \"0\");\n}\n\nfunction trimTrailingSlash(prefix: string): string {\n return prefix.endsWith(\"/\") ? prefix.slice(0, -1) : prefix;\n}\n\nfunction cleanupDir(dir: string): void {\n try {\n // Lambda warm starts can reuse `/tmp` across invocations; clean up\n // aggressively so we don't leak a chunk-sized footprint between renders.\n rmSync(dir, { recursive: true, force: true });\n } catch {\n // Best-effort \u2014 leak is preferable to crashing on success path.\n }\n}\n\n/**\n * Read the untarred planDir's `plan.json` and assert its `planHash`\n * matches what the Step Functions event claims. Throws on mismatch with\n * a typed `PLAN_HASH_MISMATCH` error name so the state machine's typed\n * non-retryable list routes it correctly.\n *\n * This is defense-in-depth \u2014 the producer's `renderChunk` does the same\n * check internally \u2014 but performing it here lets us fail before paying\n * the Chrome-launch + per-frame capture cost on a misrouted chunk.\n */\nfunction verifyPlanHash(planDir: string, expected: string): void {\n const planJsonPath = join(planDir, \"plan.json\");\n let parsed: { planHash?: unknown };\n try {\n parsed = JSON.parse(readFileSync(planJsonPath, \"utf-8\")) as { planHash?: unknown };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const error = new Error(`PLAN_HASH_MISMATCH: failed to read ${planJsonPath}: ${msg}`);\n error.name = \"PLAN_HASH_MISMATCH\";\n throw error;\n }\n const actual = parsed.planHash;\n if (typeof actual !== \"string\" || actual !== expected) {\n const error = new Error(\n `PLAN_HASH_MISMATCH: event PlanHash=${expected} did not match plan.json planHash=${String(actual)}`,\n );\n error.name = \"PLAN_HASH_MISMATCH\";\n throw error;\n }\n}\n", "/**\n * Lambda-runtime Chrome resolver.\n *\n * `renderChunk()` (the only primitive that needs a browser) launches Chrome\n * via the engine's `BrowserManager`. In Lambda we can't ship the full\n * Puppeteer-managed Chrome download \u2014 Puppeteer's Chrome binary is ~330 MB\n * unzipped, well over Lambda's 250 MB ZIP-deploy ceiling.\n *\n * Two valid runtime sources:\n *\n * 1. `@sparticuz/chromium` (primary). Decompresses a Lambda-optimised\n * `chrome-headless-shell` build into `/tmp` at runtime. ~70 MB\n * compressed; the same binary the rest of the ecosystem uses for\n * headless-Chrome-in-Lambda. CDP-level BeginFrame works because the\n * command lives in the protocol, not the binary; the\n * `scripts/probe-beginframe.ts` regression guard pins this.\n *\n * 2. A bundled `chrome-headless-shell` binary (fallback). If\n * `@sparticuz/chromium`'s build ever drops `HeadlessExperimental`\n * support, we fall back to the same `chrome-headless-shell` build\n * the K8s deploy uses. The fallback raises the ZIP from ~70 MB\n * Chrome to ~140 MB Chrome \u2014 still well under 250 MB.\n *\n * The runtime path is selected by the `HYPERFRAMES_LAMBDA_CHROME_SOURCE`\n * env var (set by `build-zip.ts`):\n *\n * \"sparticuz\" \u2192 use `@sparticuz/chromium.executablePath()`\n * \"chrome-headless-shell\" \u2192 use `process.env.HYPERFRAMES_LAMBDA_CHROME_PATH`\n *\n * Adapters that bundle this package can override\n * `HYPERFRAMES_LAMBDA_CHROME_PATH` directly when running outside Lambda\n * (e.g. the SAM-local RIE smoke).\n */\n\nimport { existsSync } from \"node:fs\";\n\n/** Discriminator for the two supported Chrome sources. */\nexport type ChromeSource = \"sparticuz\" | \"chrome-headless-shell\";\n\n/**\n * Thrown when the Chrome binary resolver can't produce a usable path.\n * The class name is the SFN `Retry: { ErrorEquals: [...] }` discriminator \u2014\n * see {@link HyperframesRenderStack}'s NON_RETRYABLE_* lists.\n */\nexport class ChromeBinaryUnavailableError extends Error {\n // Lambda's runtime serializes the error envelope's `errorType` from\n // `err.name`; this class-field override sets it across the structured\n // clone. Read indirectly; fallow can't follow.\n // fallow-ignore-next-line unused-class-member\n override readonly name = \"ChromeBinaryUnavailableError\";\n readonly source: ChromeSource;\n readonly resolvedPath: string | null;\n constructor(source: ChromeSource, resolvedPath: string | null, hint: string) {\n super(`[chromium] Chrome binary unavailable (source=${source}): ${hint}`);\n this.source = source;\n this.resolvedPath = resolvedPath;\n }\n}\n\nconst SPARTICUZ_WEDGE_HINT =\n \"@sparticuz/chromium.executablePath() returned a falsy value or a path that doesn't exist on disk. \" +\n \"This typically happens after a chunk hits `Sandbox.Timedout` mid-extraction and leaves /tmp in a \" +\n \"wedged state \u2014 subsequent invocations land on the same warm instance and never re-extract. \" +\n \"Recycle the function (e.g. `aws lambda update-function-configuration ... --environment ...` with a \" +\n \"bumped marker var, or redeploy via `hyperframes lambda deploy --skip-build`) to force fresh \" +\n \"execution environments. Tracking: investigate the upstream wedge so this auto-recovers.\";\n\n/**\n * Read which Chrome source the bundled ZIP was built against. Defaults to\n * `\"sparticuz\"` so a fresh build with no env override picks the primary\n * path.\n */\nexport function resolveChromeSource(): ChromeSource {\n const raw = process.env.HYPERFRAMES_LAMBDA_CHROME_SOURCE?.toLowerCase();\n if (raw === \"chrome-headless-shell\" || raw === \"shell\") return \"chrome-headless-shell\";\n return \"sparticuz\";\n}\n\n/**\n * Resolve the absolute path to a Chrome binary suitable for BeginFrame.\n *\n * For `\"sparticuz\"`: dynamically import `@sparticuz/chromium` and call\n * `chromium.executablePath()`. The module is dynamic so a build-zip that\n * never reaches the import (because the fallback Chrome is bundled) can\n * tree-shake it out.\n *\n * For `\"chrome-headless-shell\"`: read the path from\n * `HYPERFRAMES_LAMBDA_CHROME_PATH`. Throws if absent or non-existent so a\n * misconfigured deploy fails loudly at boot rather than at first frame.\n */\n// fallow-ignore-next-line complexity\nexport async function resolveChromeExecutablePath(): Promise<string> {\n const source = resolveChromeSource();\n if (source === \"sparticuz\") {\n const mod = await loadSparticuzChromium();\n const path = await mod.executablePath();\n // Guard against the wedge described in ChromeBinaryUnavailableError.\n // sparticuz's contract is \"return the path to a usable binary\" \u2014 when\n // it returns null/undefined/\"\" we can't hand that to puppeteer-core\n // (which will throw an unrelated-looking assertion). Same when the\n // returned path doesn't exist (extraction failed but the function\n // call returned).\n if (!path || typeof path !== \"string\") {\n throw new ChromeBinaryUnavailableError(source, null, SPARTICUZ_WEDGE_HINT);\n }\n if (!existsSync(path)) {\n throw new ChromeBinaryUnavailableError(source, path, SPARTICUZ_WEDGE_HINT);\n }\n return path;\n }\n const explicit = process.env.HYPERFRAMES_LAMBDA_CHROME_PATH;\n if (!explicit) {\n throw new ChromeBinaryUnavailableError(\n source,\n null,\n \"HYPERFRAMES_LAMBDA_CHROME_SOURCE=chrome-headless-shell requires \" +\n \"HYPERFRAMES_LAMBDA_CHROME_PATH to be set to the absolute path of the bundled binary.\",\n );\n }\n if (!existsSync(explicit)) {\n throw new ChromeBinaryUnavailableError(\n source,\n explicit,\n `HYPERFRAMES_LAMBDA_CHROME_PATH=${JSON.stringify(explicit)} does not exist on disk.`,\n );\n }\n return explicit;\n}\n\n/**\n * Resolve the Chromium launch args for the selected source. For\n * `@sparticuz/chromium` we forward `chromium.args` (Lambda-tuned defaults\n * \u2014 single-process, no-sandbox, /tmp paths). For the shell fallback the\n * engine's own arg builder owns it; we return an empty array so the\n * engine's defaults apply.\n */\nexport async function resolveChromeArgs(): Promise<string[]> {\n if (resolveChromeSource() !== \"sparticuz\") return [];\n const mod = await loadSparticuzChromium();\n return mod.args;\n}\n\n/**\n * Dynamic import wrapper isolated so unit tests can stub the module without\n * jest-style module mocking gymnastics. The narrow type here pins the\n * subset of `@sparticuz/chromium`'s surface this package depends on; if\n * the upstream module ever changes shape the type error here surfaces\n * before runtime.\n */\ninterface SparticuzChromiumModule {\n args: string[];\n executablePath(): Promise<string>;\n}\n\nlet cachedSparticuz: SparticuzChromiumModule | null = null;\n\nasync function loadSparticuzChromium(): Promise<SparticuzChromiumModule> {\n if (cachedSparticuz) return cachedSparticuz;\n const mod = (await import(\"@sparticuz/chromium\")) as\n | SparticuzChromiumModule\n | { default: SparticuzChromiumModule };\n const resolved = \"default\" in mod ? mod.default : mod;\n cachedSparticuz = resolved;\n return resolved;\n}\n\n/** Test-only seam: replace the cached `@sparticuz/chromium` module. */\nexport function _setSparticuzChromiumForTests(mod: SparticuzChromiumModule | null): void {\n cachedSparticuz = mod;\n}\n", "/**\n * Map a distributed `format` to the file extension the assembled output\n * should carry on disk + in S3. Shared by `src/handler.ts` (chunk +\n * assemble output paths) and `src/sdk/renderToLambda.ts` (final\n * output key construction) so the two sides agree on what an mp4\n * looks like vs a png-sequence.\n */\n\nimport type { DistributedFormat } from \"@hyperframes/producer/distributed\";\n\nexport type { DistributedFormat } from \"@hyperframes/producer/distributed\";\n\n// Closed-enum lookup table. TS enforces exhaustiveness via the\n// `Record<DistributedFormat, string>` annotation \u2014 adding a format to\n// `DistributedFormat` without adding the matching key here fails to\n// typecheck, which is the same exhaustiveness guarantee a switch +\n// `_exhaustive: never` arm provides but at lower complexity.\nconst FORMAT_EXTENSIONS: Record<DistributedFormat, string> = {\n mp4: \".mp4\",\n mov: \".mov\",\n webm: \".webm\",\n \"png-sequence\": \"\",\n};\n\nexport function formatExtension(format: DistributedFormat): string {\n return FORMAT_EXTENSIONS[format];\n}\n", "/**\n * Thin S3 transport for the Lambda handler.\n *\n * The OSS distributed primitives are pure functions over local file paths;\n * the Lambda handler bridges S3 \u2194 Lambda's `/tmp` filesystem on each\n * invocation. Functions here are intentionally narrow: parse a URI, download\n * an object to a local path, upload a path/directory, tar-extract a planDir,\n * tar-pack a planDir back out.\n *\n * Tar (not zip) for planDir transit:\n * - planDirs contain symlinks (extract stage materializes them but the\n * compiled/ subtree may include linked assets); tar preserves them, zip\n * does not.\n * - We use the `tar` npm package (pure JS over `node:zlib`) \u2014 AWS\n * Lambda's `nodejs:22` base image ships neither `tar` nor `unzip` in\n * `/usr/bin`, so a system-binary tar would ENOENT in the actual\n * deployment.\n */\n\nimport {\n createReadStream,\n createWriteStream,\n existsSync,\n mkdirSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { GetObjectCommand, PutObjectCommand, type S3Client } from \"@aws-sdk/client-s3\";\nimport * as tar from \"tar\";\n\n/** Parsed `s3://bucket/key` URI. */\nexport interface S3Location {\n bucket: string;\n key: string;\n}\n\n/** Parse `s3://bucket/key/path` \u2192 `{ bucket, key }`. Throws on malformed input. */\nexport function parseS3Uri(uri: string): S3Location {\n if (!uri.startsWith(\"s3://\")) {\n throw new Error(`[s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)}`);\n }\n const rest = uri.slice(\"s3://\".length);\n const slash = rest.indexOf(\"/\");\n if (slash === -1) {\n throw new Error(`[s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}`);\n }\n const bucket = rest.slice(0, slash);\n const key = rest.slice(slash + 1);\n if (!bucket || !key) {\n throw new Error(`[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)}`);\n }\n return { bucket, key };\n}\n\n/** Build `s3://bucket/key` from a location. */\nexport function formatS3Uri(loc: S3Location): string {\n return `s3://${loc.bucket}/${loc.key}`;\n}\n\n/** Stream an S3 object to a local file path. Throws if the body is missing. */\nexport async function downloadS3ObjectToFile(\n client: S3Client,\n uri: string,\n destPath: string,\n): Promise<void> {\n const { bucket, key } = parseS3Uri(uri);\n const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));\n const body = response.Body as NodeJS.ReadableStream | undefined;\n if (!body) {\n throw new Error(`[s3Transport] s3 GetObject returned empty body for ${uri}`);\n }\n mkdirSync(dirname(destPath), { recursive: true });\n await pipeline(body, createWriteStream(destPath));\n}\n\n/**\n * Upload a local file's contents to an S3 URI using a streaming\n * `PutObjectCommand`. PutObject's 5 GB cap comfortably exceeds the\n * distributed pipeline's 2 GB planDir limit and the typical\n * chunk size (\u2264 200 MB), so a single PUT works for every artifact this\n * adapter handles.\n */\nexport async function uploadFileToS3(\n client: S3Client,\n localPath: string,\n uri: string,\n contentType?: string,\n): Promise<void> {\n if (!existsSync(localPath)) {\n throw new Error(`[s3Transport] upload source missing: ${localPath}`);\n }\n const { bucket, key } = parseS3Uri(uri);\n const size = statSync(localPath).size;\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: createReadStream(localPath),\n ContentType: contentType,\n ContentLength: size,\n }),\n );\n}\n\n/**\n * Pack a directory into a `.tar.gz` at `destTarball`. Uses the `tar` npm\n * package (pure JS over `node:zlib`) rather than spawning a system tar\n * binary \u2014 the AWS Lambda Node 22 base image ships a minimal set of\n * userland tools and does NOT include `tar` in `/usr/bin`.\n */\nexport async function tarDirectory(sourceDir: string, destTarball: string): Promise<void> {\n if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) {\n throw new Error(`[s3Transport] tar source must be an existing directory: ${sourceDir}`);\n }\n mkdirSync(dirname(destTarball), { recursive: true });\n await tar.create({ gzip: true, file: destTarball, cwd: sourceDir }, [\".\"]);\n}\n\n/**\n * Extract a `.tar.gz` produced by {@link tarDirectory} into `destDir`.\n * The directory is created (or cleared) before extraction so a retried\n * invocation doesn't observe stale files from a prior run on the same\n * warm Lambda container.\n */\nexport async function untarDirectory(tarballPath: string, destDir: string): Promise<void> {\n if (!existsSync(tarballPath)) {\n throw new Error(`[s3Transport] tarball missing: ${tarballPath}`);\n }\n // Wipe target so the warm container's prior planDir doesn't bleed into\n // the new invocation. Lambda re-uses /tmp across invocations on the same\n // container.\n if (existsSync(destDir)) {\n rmSync(destDir, { recursive: true, force: true });\n }\n mkdirSync(destDir, { recursive: true });\n await tar.extract({ file: tarballPath, cwd: destDir });\n}\n", "/**\n * `deploySite` \u2014 upload a project directory to S3 once per content hash\n * and return a reusable handle.\n *\n * `renderToLambda` calls this implicitly when no `siteHandle` is passed,\n * but exposing it as a standalone verb lets adopters bundle a project\n * ahead of time and reuse the handle across many renders without\n * re-tarring the project tree on every call.\n *\n * The handle is **content-addressed**: `siteId` is derived from a SHA-256\n * over the project files. Two `deploySite` calls on an unchanged tree\n * produce the same `siteId` and `HeadObject`-short-circuit the upload.\n */\n\nimport { mkdtempSync, rmSync, statSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { HeadObjectCommand, S3Client } from \"@aws-sdk/client-s3\";\nimport { hashProjectDir } from \"@hyperframes/producer/distributed\";\nimport { formatS3Uri, tarDirectory, uploadFileToS3 } from \"../s3Transport.js\";\n\n/** Options for {@link deploySite}. */\nexport interface DeploySiteOptions {\n /** Local project directory containing `index.html` (and any composition assets). */\n projectDir: string;\n /** S3 bucket the SAM stack / CDK construct provisioned. */\n bucketName: string;\n /** AWS region for the S3 client. Defaults to the SDK's default chain (env / config / IMDS). */\n region?: string;\n /**\n * Override the content-addressed site id. Useful when the caller has a\n * stable external identifier they want to use (e.g. a git SHA); if\n * unset, the hash of the project tree picks it.\n */\n siteId?: string;\n /** Injection seam for tests. Production callers leave unset. */\n s3?: S3Client;\n}\n\n/** Stable handle returned by {@link deploySite}. Pass back to {@link renderToLambda}. */\nexport interface SiteHandle {\n /** Content-addressed (or caller-supplied) identifier; stable across re-uploads of the same tree. */\n siteId: string;\n /** Bucket the site landed in. Surfaced separately so callers don't have to re-parse `projectS3Uri`. */\n bucketName: string;\n /** Full `s3://bucket/sites/<siteId>/project.tar.gz` URI; pass through to `renderToLambda`. */\n projectS3Uri: string;\n /** Tarball size in bytes; useful for \"did we actually skip the upload?\" assertions. */\n bytes: number;\n /** ISO timestamp of the most recent upload OR the existing object the short-circuit found. */\n uploadedAt: string;\n /** `false` if the object already existed and we skipped the PUT. */\n uploaded: boolean;\n}\n\n/**\n * Upload `projectDir` to `s3://bucketName/sites/<siteId>/project.tar.gz`.\n *\n * Short-circuits when an object with the same key already exists in the\n * bucket \u2014 `siteId` derives from the project's content hash, so the same\n * bytes produce the same key, and re-uploading would be redundant.\n */\nexport async function deploySite(opts: DeploySiteOptions): Promise<SiteHandle> {\n if (!statSync(opts.projectDir).isDirectory()) {\n throw new Error(`[deploySite] projectDir is not a directory: ${opts.projectDir}`);\n }\n\n const siteId = opts.siteId ?? hashProjectDir(opts.projectDir);\n const key = `sites/${siteId}/project.tar.gz`;\n const projectS3Uri = formatS3Uri({ bucket: opts.bucketName, key });\n const s3 = opts.s3 ?? new S3Client({ region: opts.region });\n\n // HeadObject short-circuit. Adopters re-rendering the same project on\n // a tight inner loop (CI smoke, demo flows) save the tar+gzip+PUT pass\n // on every iteration.\n const existing = await headObject(s3, opts.bucketName, key);\n if (existing) {\n return {\n siteId,\n bucketName: opts.bucketName,\n projectS3Uri,\n bytes: existing.bytes,\n uploadedAt: existing.lastModified,\n uploaded: false,\n };\n }\n\n const workdir = mkdtempSync(join(tmpdir(), \"hf-deploy-site-\"));\n try {\n const tarball = join(workdir, \"project.tar.gz\");\n await tarDirectory(opts.projectDir, tarball);\n // Note: tarDirectory packs *everything* under `cwd`. We don't need to\n // re-implement the skip list inside the tar pack because the\n // producer's plan stage applies the same skip during its copy; the\n // archive is slightly bigger than the planDir's compiled/ subtree\n // but the cost is bounded by the project's user-authored content.\n const size = statSync(tarball).size;\n await uploadFileToS3(s3, tarball, projectS3Uri, \"application/gzip\");\n return {\n siteId,\n bucketName: opts.bucketName,\n projectS3Uri,\n bytes: size,\n uploadedAt: new Date().toISOString(),\n uploaded: true,\n };\n } finally {\n rmSync(workdir, { recursive: true, force: true });\n }\n}\n\nasync function headObject(\n s3: S3Client,\n bucket: string,\n key: string,\n): Promise<{ bytes: number; lastModified: string } | null> {\n try {\n const res = await s3.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));\n return {\n bytes: typeof res.ContentLength === \"number\" ? res.ContentLength : 0,\n lastModified:\n res.LastModified instanceof Date\n ? res.LastModified.toISOString()\n : new Date().toISOString(),\n };\n } catch (err) {\n // The SDK throws different error shapes for 404 vs 403 vs network;\n // a 404 means \"needs upload\" and is the most common case. Anything\n // else propagates so callers see auth / network failures.\n const status = (err as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode;\n if (status === 404) return null;\n const name = (err as { name?: string }).name;\n if (name === \"NotFound\" || name === \"NoSuchKey\") return null;\n throw err;\n }\n}\n", "/**\n * `renderToLambda` \u2014 start a distributed render against an already-deployed\n * SAM/CDK stack and return a handle the caller can poll with\n * {@link getRenderProgress}.\n *\n * The function does *not* wait for the render to finish. Step Functions\n * standard workflows can run for hours; blocking the caller's process on\n * the SFN execution is the wrong default. The returned `RenderHandle`\n * carries everything the progress / cost / download paths need.\n *\n * Wire order:\n * 1. Validate config (typed throw before any AWS call).\n * 2. `deploySite` if no `siteHandle` was provided.\n * 3. `StartExecution` against the state machine with the same input\n * shape `examples/aws-lambda/scripts/smoke.sh` builds.\n * 4. Return handle. The S3 `outputKey` is deterministic from the\n * execution name so the caller can predict the final object URL.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { SFNClient, StartExecutionCommand } from \"@aws-sdk/client-sfn\";\nimport type { S3Client } from \"@aws-sdk/client-s3\";\nimport type { SerializableDistributedRenderConfig } from \"../events.js\";\nimport { formatExtension } from \"../formatExtension.js\";\nimport { formatS3Uri } from \"../s3Transport.js\";\nimport { deploySite, type SiteHandle } from \"./deploySite.js\";\nimport {\n validateDistributedRenderConfig,\n validateStepFunctionsInputSize,\n} from \"./validateConfig.js\";\n\n/** Options for {@link renderToLambda}. */\nexport interface RenderToLambdaOptions {\n /** Local project directory. Required when `siteHandle` is not supplied. */\n projectDir?: string;\n /** Re-use an existing `deploySite` upload (skips tar+S3 PUT). */\n siteHandle?: SiteHandle;\n /** Validated `SerializableDistributedRenderConfig` (no logger / abortSignal). */\n config: SerializableDistributedRenderConfig;\n /** S3 bucket from the SAM stack output (`RenderBucketName`). */\n bucketName: string;\n /** State machine ARN from the SAM stack output (`RenderStateMachineArn`). */\n stateMachineArn: string;\n /** AWS region; defaults to the SDK default chain. */\n region?: string;\n /**\n * Final output S3 key. Defaults to `renders/<executionName>/output.<ext>`\n * where `<ext>` is derived from `config.format`.\n */\n outputKey?: string;\n /**\n * Step Functions execution name. Defaults to `hf-render-<uuid>`.\n * Used as `renderId` everywhere downstream (history queries, cost\n * accounting, predictable S3 key prefix).\n */\n executionName?: string;\n /** Test injection seam \u2014 production callers leave unset. */\n sfn?: SFNClient;\n /** Test injection seam \u2014 propagated to `deploySite` when applicable. */\n s3?: S3Client;\n}\n\n/** Stable identifier + every URL/ARN the caller needs to follow the render. */\nexport interface RenderHandle {\n /** Same as the Step Functions execution name. */\n renderId: string;\n /** Full execution ARN; pass to {@link getRenderProgress}. */\n executionArn: string;\n bucketName: string;\n stateMachineArn: string;\n outputS3Uri: string;\n projectS3Uri: string;\n startedAt: string;\n}\n\n// fallow-ignore-next-line complexity\nexport async function renderToLambda(opts: RenderToLambdaOptions): Promise<RenderHandle> {\n validateDistributedRenderConfig(opts.config);\n\n if (!opts.bucketName) {\n throw new Error(\"[renderToLambda] bucketName is required\");\n }\n if (!opts.stateMachineArn) {\n throw new Error(\"[renderToLambda] stateMachineArn is required\");\n }\n if (!opts.siteHandle && !opts.projectDir) {\n throw new Error(\"[renderToLambda] either siteHandle or projectDir must be supplied\");\n }\n\n const executionName = opts.executionName ?? `hf-render-${randomUUID()}`;\n const ext = formatExtension(opts.config.format);\n const outputKey = opts.outputKey ?? `renders/${executionName}/output${ext}`;\n const planOutputS3Prefix = formatS3Uri({\n bucket: opts.bucketName,\n key: `renders/${executionName}/`,\n });\n const outputS3Uri = formatS3Uri({ bucket: opts.bucketName, key: outputKey });\n\n const site =\n opts.siteHandle ??\n (await deploySite({\n projectDir: opts.projectDir as string,\n bucketName: opts.bucketName,\n region: opts.region,\n s3: opts.s3,\n }));\n\n const input = {\n ProjectS3Uri: site.projectS3Uri,\n PlanOutputS3Prefix: planOutputS3Prefix,\n OutputS3Uri: outputS3Uri,\n Config: opts.config,\n };\n\n // Reject oversize input client-side. Step Functions Standard caps the\n // execution input at 256 KiB; without this check, the input bloat\n // (typically from `config.variables` containing inlined media) surfaces\n // as `States.DataLimitExceeded` 50 ms into the execution, far from the\n // caller's stack frame. Measured AFTER `deploySite` so the synthesised\n // `ProjectS3Uri` is counted (a few hundred bytes either way, but the\n // check should be against the actual wire payload).\n validateStepFunctionsInputSize(input);\n\n const sfn = opts.sfn ?? new SFNClient({ region: opts.region });\n const startedAt = new Date().toISOString();\n const response = await sfn.send(\n new StartExecutionCommand({\n stateMachineArn: opts.stateMachineArn,\n name: executionName,\n input: JSON.stringify(input),\n }),\n );\n\n if (!response.executionArn) {\n throw new Error(\"[renderToLambda] StartExecution returned no executionArn\");\n }\n\n return {\n renderId: executionName,\n executionArn: response.executionArn,\n bucketName: opts.bucketName,\n stateMachineArn: opts.stateMachineArn,\n outputS3Uri,\n projectS3Uri: site.projectS3Uri,\n startedAt,\n };\n}\n", "/**\n * Client-side validation for the AWS Lambda adapter.\n *\n * The cloud-agnostic config-shape validation (`validateDistributedRenderConfig`,\n * `validateVariablesPayload`, `InvalidConfigError`) lives in\n * `@hyperframes/producer/distributed` and is shared with the other adapters.\n * This module re-exports those and adds the one piece specific to Step\n * Functions: the 256 KiB Standard-workflow execution-input size cap.\n */\n\nimport { InvalidConfigError } from \"@hyperframes/producer/distributed\";\n\nexport {\n InvalidConfigError,\n validateDistributedRenderConfig,\n validateVariablesPayload,\n} from \"@hyperframes/producer/distributed\";\n\n/**\n * Hard cap on Step Functions Standard workflow execution input \u2014 256 KiB per\n * the AWS limits page. Express workflows cap at 32 KiB; the render stack runs\n * Standard for execution-history visibility, so the larger limit applies. The\n * cap is on the entire serialized input, not just the variables, because\n * users hit it at the wire boundary regardless of which field caused the\n * bloat.\n *\n * Specific to Step Functions Standard. Other workflow runtimes (Temporal,\n * Express SFN, Cloud Workflows, raw Lambda invoke) have different caps; don't\n * reuse this constant for those without confirming the limit.\n */\nexport const MAX_STEP_FUNCTIONS_INPUT_BYTES = 256 * 1024;\n\n/** Pointer to the docs section that explains the URL-your-assets convention. */\nconst LARGE_VARIABLES_DOCS_URL =\n \"https://hyperframes.heygen.com/deploy/templates-on-lambda#working-with-large-variables\";\n\n/**\n * Validate that the serialized Step Functions execution input fits inside the\n * 256 KiB Standard-workflow cap. Measured in UTF-8 bytes (the format Step\n * Functions uses on the wire) \u2014 JS strings count UTF-16 code units, which\n * under-reports for any multi-byte character.\n *\n * Throws {@link InvalidConfigError} with a clear message naming the actual\n * byte count, the cap, and a pointer to the \"working with large variables\"\n * docs section, so users hit the limit at the SDK boundary with actionable\n * guidance instead of as a `States.DataLimitExceeded` 50 ms into the\n * execution.\n */\n// fallow-ignore-next-line complexity\nexport function validateStepFunctionsInputSize(input: unknown): void {\n let serialized: string | undefined;\n try {\n serialized = JSON.stringify(input);\n } catch (err) {\n throw new InvalidConfigError(\n \"config\",\n `Step Functions execution input is not JSON-serializable: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (serialized === undefined) {\n throw new InvalidConfigError(\n \"config\",\n \"Step Functions execution input is not JSON-serializable (JSON.stringify returned undefined). \" +\n \"Check that all fields, including config.variables, are plain JSON values.\",\n );\n }\n const byteLength = Buffer.byteLength(serialized, \"utf8\");\n if (byteLength > MAX_STEP_FUNCTIONS_INPUT_BYTES) {\n throw new InvalidConfigError(\n \"config\",\n `Step Functions execution input is ${byteLength} bytes, which exceeds the ` +\n `${MAX_STEP_FUNCTIONS_INPUT_BYTES}-byte (256 KiB) limit for Standard workflows. ` +\n `Variables are for typed data (strings, numbers, structured records); media assets ` +\n `(images, audio, video) should be passed as URL references the composition resolves ` +\n `at render time, not inlined as base64. See ${LARGE_VARIABLES_DOCS_URL} for the ` +\n `URL-your-assets convention.`,\n );\n }\n}\n", "/**\n * `getRenderProgress` \u2014 read-only progress + cost snapshot for a single\n * render started by {@link renderToLambda}.\n *\n * Pulls one `DescribeExecution` + one `GetExecutionHistory` per call. The\n * history is paginated server-side; the helper loops until exhausted so a\n * 1,000-event Step Functions execution still produces a single\n * `RenderProgress` snapshot.\n *\n * Progress math:\n * - 0 before Plan completes (no frame count is known yet)\n * - 0.1 once Plan completes (we know `totalFrames`)\n * - 0.1 + 0.8 \u00D7 framesEncoded / totalFrames during chunk render\n * - 1.0 after Assemble completes\n *\n * Frame counts come from the parsed Lambda result payloads on each\n * `TaskSucceeded` event \u2014 Plan reports `TotalFrames`, RenderChunk reports\n * `FramesEncoded`. The shape mirrors what the handler produces in\n * `events.ts`, so the parser doesn't need to know anything beyond\n * \"JSON.parse this string and grab two fields.\"\n */\n\nimport {\n DescribeExecutionCommand,\n GetExecutionHistoryCommand,\n type HistoryEvent,\n SFNClient,\n} from \"@aws-sdk/client-sfn\";\nimport {\n type BilledLambdaInvocation,\n computeRenderCost,\n type RenderCost,\n} from \"./costAccounting.js\";\n\n/** Options for {@link getRenderProgress}. */\nexport interface GetRenderProgressOptions {\n /** Execution ARN from a {@link renderToLambda} call. */\n executionArn: string;\n /**\n * Default memory size in MB to assume for Lambda invocations when the\n * history event payload doesn't carry it explicitly. Matches the\n * `LambdaMemoryMb` parameter the stack was deployed with.\n */\n defaultMemorySizeMb?: number;\n region?: string;\n /** Test injection seam. */\n sfn?: SFNClient;\n}\n\n/** Render-status discriminant; mirrors Step Functions execution states. */\nexport type RenderStatus =\n | \"RUNNING\"\n | \"SUCCEEDED\"\n | \"FAILED\"\n | \"TIMED_OUT\"\n | \"ABORTED\"\n | \"PENDING_REDRIVE\";\n\nexport interface RenderError {\n /** State name where the failure surfaced (`Plan`, `RenderChunk`, `Assemble`, or `<unknown>`). */\n state: string;\n /** Error class / type as Step Functions reports it. */\n error: string;\n /** Cause string Step Functions surfaces (often a stringified JSON payload from the handler). */\n cause: string;\n}\n\n/** Snapshot of a single render's progress + cost + errors at one point in time. */\nexport interface RenderProgress {\n status: RenderStatus;\n /** `[0, 1]`; see module doc for the math. */\n overallProgress: number;\n framesRendered: number;\n /** `null` until Plan completes. */\n totalFrames: number | null;\n /** Total Lambda invocations scheduled so far (both optimized + raw task integrations). */\n lambdasInvoked: number;\n costs: RenderCost;\n /** Final output object if Assemble succeeded; `null` otherwise. */\n outputFile: { s3Uri: string; bytes: number | null } | null;\n errors: RenderError[];\n /** `true` once the execution has terminated in a non-`SUCCEEDED` state. */\n fatalErrorEncountered: boolean;\n startedAt: string;\n endedAt: string | null;\n}\n\nconst DEFAULT_MEMORY_MB = 10240;\n\n/** Pull a current progress snapshot for one render. */\nexport async function getRenderProgress(opts: GetRenderProgressOptions): Promise<RenderProgress> {\n if (!opts.executionArn) {\n throw new Error(\"[getRenderProgress] executionArn is required\");\n }\n const sfn = opts.sfn ?? new SFNClient({ region: opts.region });\n const memoryMb = opts.defaultMemorySizeMb ?? DEFAULT_MEMORY_MB;\n\n const describe = await sfn.send(\n new DescribeExecutionCommand({ executionArn: opts.executionArn }),\n );\n const status = (describe.status ?? \"RUNNING\") as RenderStatus;\n const startedAt = describe.startDate?.toISOString() ?? new Date(0).toISOString();\n const endedAt = describe.stopDate?.toISOString() ?? null;\n\n const history = await loadFullHistory(sfn, opts.executionArn);\n const summary = summarizeHistory(history, memoryMb);\n\n const costs = computeRenderCost(summary.lambdaInvocations, summary.stateTransitions);\n const overallProgress = computeOverallProgress({\n status,\n totalFrames: summary.totalFrames,\n framesRendered: summary.framesRendered,\n assembleComplete: summary.assembleComplete,\n });\n\n return {\n status,\n overallProgress,\n framesRendered: summary.framesRendered,\n totalFrames: summary.totalFrames,\n lambdasInvoked: summary.lambdasInvoked,\n costs,\n outputFile: summary.outputFile,\n errors: summary.errors,\n fatalErrorEncountered: isTerminalFailure(status),\n startedAt,\n endedAt,\n };\n}\n\nasync function loadFullHistory(sfn: SFNClient, executionArn: string): Promise<HistoryEvent[]> {\n const events: HistoryEvent[] = [];\n let nextToken: string | undefined;\n for (let page = 0; page < 50; page++) {\n const res = await sfn.send(\n new GetExecutionHistoryCommand({\n executionArn,\n maxResults: 1000,\n nextToken,\n reverseOrder: false,\n }),\n );\n if (res.events) events.push(...res.events);\n nextToken = res.nextToken;\n if (!nextToken) break;\n }\n return events;\n}\n\ninterface HistorySummary {\n lambdaInvocations: BilledLambdaInvocation[];\n stateTransitions: number;\n framesRendered: number;\n totalFrames: number | null;\n lambdasInvoked: number;\n assembleComplete: boolean;\n outputFile: { s3Uri: string; bytes: number | null } | null;\n errors: RenderError[];\n}\n\n/**\n * One pass over the history events that pulls every number {@link getRenderProgress}\n * needs. State transitions = the count of events that advance the state\n * machine (entering/exiting states + map iteration completions). Lambda\n * invocations = `LambdaFunctionScheduled` count. Frame totals come from\n * the success-payload of each Lambda invocation.\n */\nfunction summarizeHistory(events: HistoryEvent[], memoryMb: number): HistorySummary {\n let framesRendered = 0;\n let totalFrames: number | null = null;\n let lambdasInvoked = 0;\n let assembleComplete = false;\n let outputFile: HistorySummary[\"outputFile\"] = null;\n let stateTransitions = 0;\n const errors: RenderError[] = [];\n const lambdaInvocations: BilledLambdaInvocation[] = [];\n\n // Track the state name we most recently entered, so we can:\n // - attach the enclosing state to LambdaFunctionFailed errors, and\n // - identify when the Assemble state finished (StateExited.Assemble)\n // without relying on the inner Lambda payload's `Action` field.\n let currentLambdaState: string | null = null;\n\n for (const ev of events) {\n switch (ev.type) {\n case \"TaskStateEntered\":\n case \"MapStateEntered\":\n case \"PassStateEntered\":\n case \"ChoiceStateEntered\":\n case \"SucceedStateEntered\":\n case \"FailStateEntered\":\n case \"WaitStateEntered\":\n case \"ParallelStateEntered\":\n // Step Functions Standard Workflows bill per *state entry*, not per\n // history event. Lambda invocations produce ~5-7 history events\n // each (Scheduled / Started / Succeeded / TaskStateExited / \u2026);\n // counting every event as a transition over-reports cost by 3-5\u00D7.\n stateTransitions++;\n currentLambdaState = ev.stateEnteredEventDetails?.name ?? currentLambdaState;\n break;\n // Optimized `lambda:invoke` task emits Task* events; raw\n // `lambda:invokeFunction.sync` emits LambdaFunction*. Handle both.\n case \"TaskScheduled\":\n if (ev.taskScheduledEventDetails?.resourceType === \"lambda\") {\n lambdasInvoked++;\n }\n break;\n case \"LambdaFunctionScheduled\":\n lambdasInvoked++;\n break;\n case \"TaskSucceeded\": {\n if (ev.taskSucceededEventDetails?.resourceType !== \"lambda\") break;\n const wrapped = parseJson(ev.taskSucceededEventDetails?.output);\n const payload = unwrapLambdaPayload(wrapped);\n const billedDurationMs = inferBilledMs(payload);\n lambdaInvocations.push({\n billedDurationMs,\n memorySizeMb: memoryMb,\n estimated: billedDurationMs === 0,\n });\n applyPayloadFrameCounts(payload, currentLambdaState, (delta) => {\n framesRendered += delta;\n });\n if (payload && typeof payload === \"object\") {\n const obj = payload as Record<string, unknown>;\n if (typeof obj.TotalFrames === \"number\") totalFrames = obj.TotalFrames;\n }\n break;\n }\n case \"LambdaFunctionSucceeded\": {\n const payload = parseJson(ev.lambdaFunctionSucceededEventDetails?.output);\n const billedDurationMs = inferBilledMs(payload);\n lambdaInvocations.push({\n billedDurationMs,\n memorySizeMb: memoryMb,\n estimated: billedDurationMs === 0,\n });\n applyPayloadFrameCounts(payload, currentLambdaState, (delta) => {\n framesRendered += delta;\n });\n if (payload && typeof payload === \"object\") {\n const obj = payload as Record<string, unknown>;\n if (typeof obj.TotalFrames === \"number\") totalFrames = obj.TotalFrames;\n }\n break;\n }\n case \"TaskStateExited\":\n case \"MapStateExited\":\n // Mark the assemble step complete on its state-exit, independent\n // of the inner Lambda payload shape. The Assemble state's\n // ResultSelector pulls FileSize + OutputS3Uri from the Lambda\n // result, so we re-extract them here from the state exit's\n // own output rather than relying on the Lambda payload.\n if (ev.stateExitedEventDetails?.name === \"Assemble\") {\n assembleComplete = true;\n const exitPayload = parseJson(ev.stateExitedEventDetails?.output);\n if (exitPayload && typeof exitPayload === \"object\") {\n const obj = exitPayload as Record<string, unknown>;\n const out = obj.Output as Record<string, unknown> | undefined;\n const outputS3Uri = typeof out?.OutputS3Uri === \"string\" ? out.OutputS3Uri : null;\n const bytes = typeof out?.FileSize === \"number\" ? out.FileSize : null;\n outputFile = outputS3Uri ? { s3Uri: outputS3Uri, bytes } : outputFile;\n }\n }\n break;\n case \"TaskFailed\":\n if (ev.taskFailedEventDetails?.resourceType !== \"lambda\") break;\n errors.push({\n state: currentLambdaState ?? \"<unknown>\",\n error: ev.taskFailedEventDetails?.error ?? \"UNKNOWN\",\n cause: ev.taskFailedEventDetails?.cause ?? \"\",\n });\n break;\n case \"LambdaFunctionFailed\":\n errors.push({\n state: currentLambdaState ?? \"<unknown>\",\n error: ev.lambdaFunctionFailedEventDetails?.error ?? \"UNKNOWN\",\n cause: ev.lambdaFunctionFailedEventDetails?.cause ?? \"\",\n });\n break;\n case \"ExecutionFailed\":\n errors.push({\n state: \"<execution>\",\n error: ev.executionFailedEventDetails?.error ?? \"UNKNOWN\",\n cause: ev.executionFailedEventDetails?.cause ?? \"\",\n });\n break;\n case \"ExecutionAborted\":\n errors.push({\n state: \"<execution>\",\n error: ev.executionAbortedEventDetails?.error ?? \"ABORTED\",\n cause: ev.executionAbortedEventDetails?.cause ?? \"\",\n });\n break;\n case \"ExecutionTimedOut\":\n errors.push({\n state: \"<execution>\",\n error: \"TIMEOUT\",\n cause: ev.executionTimedOutEventDetails?.cause ?? \"\",\n });\n break;\n default:\n break;\n }\n }\n\n return {\n lambdaInvocations,\n stateTransitions,\n framesRendered,\n totalFrames,\n lambdasInvoked,\n assembleComplete,\n outputFile,\n errors,\n };\n}\n\nfunction parseJson(s: string | undefined): unknown {\n if (!s) return null;\n try {\n return JSON.parse(s);\n } catch {\n return null;\n }\n}\n\n/**\n * Optimized `lambda:invoke` wraps the Lambda response as\n * `{ ExecutedVersion, Payload: {\u2026handler payload\u2026}, StatusCode }`. Raw\n * `lambda:invokeFunction.sync` puts the handler payload at the root.\n * Return the inner `Payload` when present so callers read the same fields\n * either way.\n */\nfunction unwrapLambdaPayload(payload: unknown): unknown {\n if (payload && typeof payload === \"object\" && \"Payload\" in payload) {\n const inner = (payload as { Payload: unknown }).Payload;\n if (inner && typeof inner === \"object\") return inner;\n }\n return payload;\n}\n\n/**\n * Bump `framesRendered` only inside the `RenderChunk` state. Plan and\n * Assemble also report `FramesEncoded`, so a state-blind add would\n * double-count once Assemble runs.\n */\nfunction applyPayloadFrameCounts(\n payload: unknown,\n currentLambdaState: string | null,\n bump: (delta: number) => void,\n): void {\n if (currentLambdaState !== \"RenderChunk\") return;\n if (!payload || typeof payload !== \"object\") return;\n const obj = payload as Record<string, unknown>;\n if (typeof obj.FramesEncoded === \"number\") bump(obj.FramesEncoded);\n}\n\n/**\n * Lambda success payloads from our handler include `DurationMs` \u2014 the\n * wall-clock the handler observed. We use it as a best-effort proxy\n * for `BilledDuration` when SFN doesn't expose the latter directly\n * on `LambdaFunctionSucceeded` (the dedicated `BilledDuration` field\n * is in CloudWatch Metrics, not the SFN history payload).\n */\nfunction inferBilledMs(payload: unknown): number {\n if (!payload || typeof payload !== \"object\") return 0;\n const obj = payload as Record<string, unknown>;\n if (typeof obj.DurationMs === \"number\") return obj.DurationMs;\n return 0;\n}\n\ninterface ComputeProgressArgs {\n status: RenderStatus;\n totalFrames: number | null;\n framesRendered: number;\n assembleComplete: boolean;\n}\n\nfunction computeOverallProgress({\n status,\n totalFrames,\n framesRendered,\n assembleComplete,\n}: ComputeProgressArgs): number {\n if (status === \"SUCCEEDED\") return 1;\n if (assembleComplete) return 1;\n if (totalFrames === null) return 0;\n // 10 % Plan + 80 % chunk render + 10 % Assemble.\n const chunkProgress = Math.min(1, framesRendered / totalFrames);\n return 0.1 + 0.8 * chunkProgress;\n}\n\nfunction isTerminalFailure(status: RenderStatus): boolean {\n return status === \"FAILED\" || status === \"TIMED_OUT\" || status === \"ABORTED\";\n}\n", "/**\n * Per-render cost accounting for {@link getRenderProgress}.\n *\n * AWS bills Lambda by **GB-seconds** (billed-duration \u00D7 memory-in-GiB)\n * and Step Functions standard workflows by **state transitions**. Both\n * inputs are recoverable from the SFN execution history without an\n * extra CloudWatch query \u2014 the history events carry\n * `billedDurationInMillis` and `memorySizeInMB` on each Lambda\n * invocation, and the transition count is simply `history.length`\n * filtered to transition-worthy events.\n *\n * The math is documented inline so the constants stay close to the\n * pricing source they came from. Cost is **best-effort**: AWS pricing\n * varies by region + commitment plan; we use on-demand `us-east-1`\n * rates as of 2026-05 and label the result `displayCost` so callers\n * see the dollar value but downstream automation can also read the\n * raw number.\n */\n\n/** On-demand Lambda price, us-east-1, x86_64, on-demand: USD per GB-second. */\nconst LAMBDA_USD_PER_GB_SECOND = 0.0000166667;\n/** Step Functions Standard Workflows, us-east-1: USD per state transition. */\nconst SFN_USD_PER_TRANSITION = 0.000025;\n\n/** Raw history event subset the cost calc cares about. Caller filters from `getExecutionHistory`. */\nexport interface BilledLambdaInvocation {\n /** Millis of Lambda billed duration. Carried on `TaskSucceeded`/`TaskFailed` events. */\n billedDurationMs: number;\n /** Memory size in MB the function was configured with at invocation time. */\n memorySizeMb: number;\n /** `true` if the event payload did NOT carry a billed duration and we fell back to `Duration` or a constant. */\n estimated: boolean;\n}\n\n/** Result of {@link computeRenderCost}. */\nexport interface RenderCost {\n /** USD accrued to date. */\n accruedSoFarUsd: number;\n /** Human-readable USD string, e.g. `\"$0.0214\"`. */\n displayCost: string;\n breakdown: {\n lambdaUsd: number;\n stepFunctionsUsd: number;\n /** S3 transfer + storage cost varies by tier; we don't try to compute it here. */\n s3Estimate: \"not-included\";\n /** `true` if any Lambda invocation fell back to estimated billing. */\n estimated: boolean;\n };\n}\n\n/**\n * Sum Lambda GB-seconds + SFN transitions into an aggregate USD figure.\n *\n * `stateTransitions` is the count of billable state-machine transitions\n * \u2014 every successful state entry transitions once for standard\n * workflows. Express workflows price differently and are out of scope.\n */\nexport function computeRenderCost(\n lambdaInvocations: BilledLambdaInvocation[],\n stateTransitions: number,\n): RenderCost {\n let lambdaUsd = 0;\n let anyEstimated = false;\n for (const inv of lambdaInvocations) {\n const gbSeconds = (inv.memorySizeMb / 1024) * (inv.billedDurationMs / 1000);\n lambdaUsd += gbSeconds * LAMBDA_USD_PER_GB_SECOND;\n if (inv.estimated) anyEstimated = true;\n }\n const stepFunctionsUsd = stateTransitions * SFN_USD_PER_TRANSITION;\n const accruedSoFarUsd = roundUsd(lambdaUsd + stepFunctionsUsd);\n return {\n accruedSoFarUsd,\n displayCost: formatUsd(accruedSoFarUsd),\n breakdown: {\n lambdaUsd: roundUsd(lambdaUsd),\n stepFunctionsUsd: roundUsd(stepFunctionsUsd),\n s3Estimate: \"not-included\",\n estimated: anyEstimated,\n },\n };\n}\n\nfunction roundUsd(usd: number): number {\n // Four decimal places \u2014 enough resolution for per-chunk granularity on\n // a 10 GB Lambda. Anything finer is noise vs AWS' own rounding.\n return Math.round(usd * 10_000) / 10_000;\n}\n\nfunction formatUsd(usd: number): string {\n return `$${usd.toFixed(4)}`;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["existsSync", "mkdirSync", "rmSync", "statSync", "existsSync", "existsSync", "statSync", "uri", "mkdirSync", "rmSync", "mkdtempSync", "rmSync", "statSync", "tmpdir", "join", "S3Client", "statSync", "S3Client", "mkdtempSync", "join", "tmpdir", "rmSync", "InvalidConfigError", "SFNClient", "SFNClient"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploySite.d.ts","sourceRoot":"","sources":["../../src/sdk/deploySite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"deploySite.d.ts","sourceRoot":"","sources":["../../src/sdk/deploySite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAqB,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAIjE,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,oFAAoF;IACpF,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAED,yFAAyF;AACzF,MAAM,WAAW,UAAU;IACzB,oGAAoG;IACpG,MAAM,EAAE,MAAM,CAAC;IACf,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA+C7E"}
|