@hyperframes/aws-lambda 0.7.99 → 0.7.100

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.
@@ -1 +1 @@
1
- {"version":3,"file":"HyperframesRenderStack.d.ts","sourceRoot":"","sources":["../../src/cdk/HyperframesRenderStack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAY,aAAa,EAAQ,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,KAAK,GAAG,MAAM,+BAA+B,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,kEAAkE;AAClE,MAAM,WAAW,2BAA2B;IAC1C,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;IAC/E,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oFAAoF;IACpF,YAAY,CAAC,EAAE,WAAW,GAAG,uBAAuB,CAAC;IACrD,kFAAkF;IAClF,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,mBAAmB,CAAC,EAAE,aAAa,CAAC;CACrC;AAOD,qBAAa,sBAAuB,SAAQ,SAAS;IACnD,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC;IACzC,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC;gBAE5B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,2BAAgC;IAoHjF;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;CAkSpC"}
1
+ {"version":3,"file":"HyperframesRenderStack.d.ts","sourceRoot":"","sources":["../../src/cdk/HyperframesRenderStack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAY,aAAa,EAAQ,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,KAAK,GAAG,MAAM,+BAA+B,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,kEAAkE;AAClE,MAAM,WAAW,2BAA2B;IAC1C,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;IAC/E,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oFAAoF;IACpF,YAAY,CAAC,EAAE,WAAW,GAAG,uBAAuB,CAAC;IACrD,kFAAkF;IAClF,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,mBAAmB,CAAC,EAAE,aAAa,CAAC;CACrC;AAOD,qBAAa,sBAAuB,SAAQ,SAAS;IACnD,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC;IACzC,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC;gBAE5B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,2BAAgC;IAoHjF;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;CAoSpC"}
package/dist/cdk/index.js CHANGED
@@ -138,6 +138,8 @@ var HyperframesRenderStack = class extends Construct {
138
138
  "PLAN_V2_INTEGRITY_UNRECOVERABLE",
139
139
  "VIDEO_SOURCE_UNRENDERABLE",
140
140
  "INVALID_VIDEO_METADATA",
141
+ "NOT_MEDIA_PAYLOAD",
142
+ "NotMediaPayloadError",
141
143
  "PlanV2IntegrityError",
142
144
  "PLAN_ARTIFACT_DIGEST_MISMATCH",
143
145
  "FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cdk/HyperframesRenderStack.ts"],
4
- "sourcesContent": ["/**\n * `HyperframesRenderStack` \u2014 aws-cdk-lib L2 construct that emits the same\n * topology as `examples/aws-lambda/template.yaml`.\n *\n * Adopters who embed HyperFrames inside their own CDK app can extend this\n * construct or compose alongside it; the construct exposes its `.bucket`,\n * `.renderFunction`, and `.stateMachine` properties so additional\n * resources (alarms, dashboards, SNS topics) can be wired without\n * re-deriving the ARNs from a stack export.\n *\n * `aws-cdk-lib` and `constructs` are **peerDependencies**. The package\n * still type-checks (and the snapshot test still runs) because they're\n * also `devDependencies`, but adopters who only consume the SDK side of\n * `@hyperframes/aws-lambda` don't pull the CDK tree at runtime.\n *\n * Drift from the SAM template is guarded by the snapshot test\n * (`HyperframesRenderStack.snapshot.test.ts`), which diffs the synthed\n * CloudFormation against the SAM-rendered CloudFormation modulo\n * normalisation.\n */\n\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Duration, RemovalPolicy, Size } from \"aws-cdk-lib\";\nimport * as cloudwatch from \"aws-cdk-lib/aws-cloudwatch\";\nimport * as lambda from \"aws-cdk-lib/aws-lambda\";\nimport * as logs from \"aws-cdk-lib/aws-logs\";\nimport * as s3 from \"aws-cdk-lib/aws-s3\";\nimport * as sfn from \"aws-cdk-lib/aws-stepfunctions\";\nimport * as tasks from \"aws-cdk-lib/aws-stepfunctions-tasks\";\nimport { Construct } from \"constructs\";\n\n/** Construction-time props for {@link HyperframesRenderStack}. */\nexport interface HyperframesRenderStackProps {\n /** Name prefix applied to function / state-machine / alarm names. Default `\"hyperframes\"`. */\n projectName?: string;\n /** Lambda memory in MB. Allowed: 2048..10240 in 1024 steps. Default 10240. */\n lambdaMemoryMb?: 2048 | 3072 | 4096 | 5120 | 6144 | 7168 | 8192 | 9216 | 10240;\n /** Per-invocation Lambda timeout. Default 900 (15 min, Lambda hard cap). */\n lambdaTimeoutSec?: number;\n /** Lambda reserved concurrency cap. `undefined` = unreserved (account default). */\n reservedConcurrency?: number;\n /** Which Chrome runtime was bundled into the handler ZIP. Default `\"sparticuz\"`. */\n chromeSource?: \"sparticuz\" | \"chrome-headless-shell\";\n /** Threshold for the runaway-invocations alarm. Default 1000 invocations/hour. */\n chunkInvocationAlarmThreshold?: number;\n /**\n * Absolute path to the handler ZIP produced by\n * `bun run --cwd packages/aws-lambda build:zip`. Defaults to the\n * package-relative path the build script writes to. Adopters who\n * deploy the published handler ZIP set this explicitly.\n */\n handlerZipPath?: string;\n /** S3 bucket retention policy on stack delete. Default RETAIN. */\n bucketRemovalPolicy?: RemovalPolicy;\n}\n\nconst DEFAULT_MEMORY_MB = 10240;\nconst DEFAULT_TIMEOUT_SEC = 900;\nconst DEFAULT_CHROME_SOURCE = \"sparticuz\";\nconst DEFAULT_ALARM_THRESHOLD = 1000;\n\nexport class HyperframesRenderStack extends Construct {\n /** S3 bucket for plan tarballs, chunk outputs, and final renders. */\n readonly bucket: s3.Bucket;\n /** The single Lambda function dispatching plan / renderChunk / assemble. */\n readonly renderFunction: lambda.Function;\n /** The Step Functions state machine orchestrating the render. */\n readonly stateMachine: sfn.StateMachine;\n\n constructor(scope: Construct, id: string, props: HyperframesRenderStackProps = {}) {\n super(scope, id);\n\n const projectName = props.projectName ?? \"hyperframes\";\n const memorySize = props.lambdaMemoryMb ?? DEFAULT_MEMORY_MB;\n const timeoutSec = props.lambdaTimeoutSec ?? DEFAULT_TIMEOUT_SEC;\n const chromeSource = props.chromeSource ?? DEFAULT_CHROME_SOURCE;\n const alarmThreshold = props.chunkInvocationAlarmThreshold ?? DEFAULT_ALARM_THRESHOLD;\n const handlerZipPath = props.handlerZipPath ?? defaultHandlerZipPath();\n\n this.bucket = new s3.Bucket(this, \"RenderBucket\", {\n removalPolicy: props.bucketRemovalPolicy ?? RemovalPolicy.RETAIN,\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n // `Suspended` is the cheapest mode that still satisfies KMS / replication\n // prerequisites callers can layer on later. Adopters who treat the final\n // mp4 as user-keepable can switch to `Enabled`.\n versioned: false,\n lifecycleRules: [\n {\n id: \"ExpireIntermediates\",\n enabled: true,\n prefix: \"renders/\",\n expiration: Duration.days(7),\n },\n ],\n });\n\n this.renderFunction = new lambda.Function(this, \"RenderFunction\", {\n functionName: `${projectName}-render`,\n runtime: lambda.Runtime.NODEJS_22_X,\n handler: \"handler.handler\",\n code: lambda.Code.fromAsset(handlerZipPath),\n memorySize,\n timeout: Duration.seconds(timeoutSec),\n ephemeralStorageSize: Size.gibibytes(10),\n architecture: lambda.Architecture.X86_64,\n reservedConcurrentExecutions: props.reservedConcurrency,\n tracing: lambda.Tracing.ACTIVE,\n environment: {\n NODE_OPTIONS: \"--enable-source-maps\",\n TMPDIR: \"/tmp\",\n HYPERFRAMES_LAMBDA_CHROME_SOURCE: chromeSource,\n HYPERFRAMES_RENDER_BUCKET: this.bucket.bucketName,\n },\n });\n\n // Scoped S3 perms only \u2014 explicitly NOT `CloudWatchLogsFullAccess`,\n // which would grant `logs:*` on `*` and overscope adopter accounts.\n // SAM's AWSLambdaBasicExecutionRole equivalent is included by the\n // default `new lambda.Function` execution role.\n this.bucket.grantReadWrite(this.renderFunction);\n\n const stateMachineLogGroup = new logs.LogGroup(this, \"RenderStateMachineLogGroup\", {\n logGroupName: `/aws/states/${projectName}-render`,\n retention: logs.RetentionDays.ONE_MONTH,\n removalPolicy: RemovalPolicy.DESTROY,\n });\n\n const definition = this.buildStateMachineDefinition();\n\n this.stateMachine = new sfn.StateMachine(this, \"RenderStateMachine\", {\n stateMachineName: `${projectName}-render`,\n stateMachineType: sfn.StateMachineType.STANDARD,\n definitionBody: sfn.DefinitionBody.fromChainable(definition),\n tracingEnabled: true,\n timeout: Duration.hours(1),\n logs: {\n destination: stateMachineLogGroup,\n level: sfn.LogLevel.ERROR,\n includeExecutionData: false,\n },\n });\n\n this.renderFunction.grantInvoke(this.stateMachine);\n\n new cloudwatch.Alarm(this, \"RenderChunkInvocationAlarm\", {\n alarmName: `${projectName}-runaway-chunk-invocations`,\n alarmDescription:\n \"Fires if RenderChunk Lambda invocations exceed the configured threshold in a 1-hour window.\",\n metric: this.renderFunction.metricInvocations({\n period: Duration.hours(1),\n statistic: cloudwatch.Stats.SUM,\n }),\n threshold: alarmThreshold,\n evaluationPeriods: 1,\n comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,\n treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,\n });\n\n new cloudwatch.Alarm(this, \"RenderFunctionErrorsAlarm\", {\n alarmName: `${projectName}-render-function-errors`,\n alarmDescription: \"Fires if the render Lambda reports any errors in a 5-minute window.\",\n metric: this.renderFunction.metricErrors({\n period: Duration.minutes(5),\n statistic: cloudwatch.Stats.SUM,\n }),\n threshold: 1,\n evaluationPeriods: 1,\n comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,\n treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,\n });\n\n new cloudwatch.Alarm(this, \"RenderStateMachineFailedAlarm\", {\n alarmName: `${projectName}-render-state-machine-failed`,\n alarmDescription: \"Fires when the render state machine reports a failed execution.\",\n metric: this.stateMachine.metricFailed({\n period: Duration.minutes(5),\n statistic: cloudwatch.Stats.SUM,\n }),\n threshold: 1,\n evaluationPeriods: 1,\n comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,\n treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,\n });\n }\n\n /**\n * Build the state-machine chain. Kept in a single method so the SAM\n * template and this construct can be diffed shape-for-shape during\n * the snapshot test.\n */\n private buildStateMachineDefinition(): sfn.IChainable {\n // `ChromeBinaryUnavailableError` is non-retryable: a wedged warm\n // instance keeps returning the same falsy executablePath until the\n // env recycles, so retries just burn the 4\u00D7 15-min budget.\n const NON_RETRYABLE_PLAN = [\n \"FFMPEG_VERSION_MISMATCH\",\n \"PLAN_HASH_MISMATCH\",\n \"S3_URI_NOT_ALLOWED\",\n \"BROWSER_GPU_NOT_SOFTWARE\",\n \"FONT_FETCH_FAILED\",\n \"PLAN_TOO_LARGE\",\n \"PlanTooLargeError\",\n \"PLAN_PROTOCOL_UNSUPPORTED\",\n \"PlanProtocolUnsupportedError\",\n \"PLAN_V2_INTEGRITY_UNRECOVERABLE\",\n \"VIDEO_SOURCE_UNRENDERABLE\",\n \"INVALID_VIDEO_METADATA\",\n \"PlanV2IntegrityError\",\n \"PLAN_ARTIFACT_DIGEST_MISMATCH\",\n \"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED\",\n \"ChromeBinaryUnavailableError\",\n ];\n const NON_RETRYABLE_CHUNK = [\n \"FFMPEG_VERSION_MISMATCH\",\n \"PLAN_HASH_MISMATCH\",\n \"S3_URI_NOT_ALLOWED\",\n \"BROWSER_GPU_NOT_SOFTWARE\",\n \"PLAN_TOO_LARGE\",\n \"PlanTooLargeError\",\n \"PLAN_PROTOCOL_UNSUPPORTED\",\n \"PlanProtocolUnsupportedError\",\n \"PLAN_V2_INTEGRITY_UNRECOVERABLE\",\n \"INVALID_VIDEO_METADATA\",\n \"PlanV2IntegrityError\",\n \"PLAN_ARTIFACT_DIGEST_MISMATCH\",\n \"ChromeBinaryUnavailableError\",\n ];\n const NON_RETRYABLE_ASSEMBLE = [\n \"FFMPEG_VERSION_MISMATCH\",\n \"PLAN_HASH_MISMATCH\",\n \"S3_URI_NOT_ALLOWED\",\n \"PLAN_PROTOCOL_UNSUPPORTED\",\n \"PlanProtocolUnsupportedError\",\n \"PLAN_V2_INTEGRITY_UNRECOVERABLE\",\n \"PlanV2IntegrityError\",\n \"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED\",\n \"PLAN_TOO_LARGE\",\n \"PlanTooLargeError\",\n \"PLAN_ARTIFACT_DIGEST_MISMATCH\",\n \"ChromeBinaryUnavailableError\",\n ];\n\n const plan = new tasks.LambdaInvoke(this, \"Plan\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"plan\",\n \"ProjectS3Uri.$\": \"$.ProjectS3Uri\",\n \"PlanOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Config.$\": \"$.Config\",\n }),\n resultSelector: {\n PlanProtocol: \"v1\",\n \"PlanS3Uri.$\": \"$.Payload.PlanS3Uri\",\n \"PlanHash.$\": \"$.Payload.PlanHash\",\n \"ChunkCount.$\": \"$.Payload.ChunkCount\",\n \"Format.$\": \"$.Payload.Format\",\n \"HasAudio.$\": \"$.Payload.HasAudio\",\n \"AudioS3Uri.$\": \"$.Payload.AudioS3Uri\",\n },\n resultPath: \"$.Plan\",\n });\n plan.addRetry({\n errors: NON_RETRYABLE_PLAN,\n maxAttempts: 0,\n });\n plan.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const planV2 = new tasks.LambdaInvoke(this, \"PlanV2\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"plan\",\n PlanProtocol: \"v2\",\n \"ProjectS3Uri.$\": \"$.ProjectS3Uri\",\n \"PlanOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Config.$\": \"$.Config\",\n }),\n resultSelector: {\n PlanProtocol: \"v2\",\n \"PlanV2ManifestS3Uri.$\": \"$.Payload.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.Payload.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.Payload.PlanHash\",\n \"ChunkCount.$\": \"$.Payload.ChunkCount\",\n \"Format.$\": \"$.Payload.Format\",\n \"HasAudio.$\": \"$.Payload.HasAudio\",\n },\n resultPath: \"$.Plan\",\n });\n planV2.addRetry({ errors: NON_RETRYABLE_PLAN, maxAttempts: 0 });\n planV2.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const buildChunkList = new sfn.Pass(this, \"BuildChunkList\", {\n parameters: {\n \"ChunkIndexes.$\": \"States.ArrayRange(0, States.MathAdd($.Plan.ChunkCount, -1), 1)\",\n },\n resultPath: \"$.Iterator\",\n });\n\n const planProducedZero = new sfn.Fail(this, \"PlanProducedZeroChunks\", {\n error: \"PLAN_TOO_LARGE\",\n cause: \"Plan returned ChunkCount=0 \u2014 non-retryable producer-side invariant violation.\",\n });\n\n const renderChunkTask = new tasks.LambdaInvoke(this, \"RenderChunk\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"renderChunk\",\n \"ChunkIndex.$\": \"$.ChunkIndex\",\n \"PlanS3Uri.$\": \"$.PlanS3Uri\",\n \"PlanHash.$\": \"$.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.ChunkOutputS3Prefix\",\n \"Format.$\": \"$.Format\",\n }),\n resultSelector: {\n \"ChunkS3Uri.$\": \"$.Payload.ChunkS3Uri\",\n \"ChunkIndex.$\": \"$.Payload.ChunkIndex\",\n \"Sha256.$\": \"$.Payload.Sha256\",\n },\n });\n renderChunkTask.addRetry({\n errors: NON_RETRYABLE_CHUNK,\n maxAttempts: 0,\n });\n renderChunkTask.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const renderChunks = new sfn.Map(this, \"RenderChunks\", {\n itemsPath: \"$.Iterator.ChunkIndexes\",\n itemSelector: {\n \"ChunkIndex.$\": \"$$.Map.Item.Value\",\n \"PlanS3Uri.$\": \"$.Plan.PlanS3Uri\",\n \"PlanHash.$\": \"$.Plan.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Format.$\": \"$.Plan.Format\",\n },\n maxConcurrencyPath: \"$.Plan.ChunkCount\",\n resultPath: \"$.Chunks\",\n });\n renderChunks.itemProcessor(renderChunkTask);\n\n const assemble = new tasks.LambdaInvoke(this, \"Assemble\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"assemble\",\n \"PlanS3Uri.$\": \"$.Plan.PlanS3Uri\",\n \"ChunkS3Uris.$\": \"$.Chunks[*].ChunkS3Uri\",\n \"AudioS3Uri.$\": \"$.Plan.AudioS3Uri\",\n \"OutputS3Uri.$\": \"$.OutputS3Uri\",\n \"Format.$\": \"$.Plan.Format\",\n }),\n resultSelector: {\n \"OutputS3Uri.$\": \"$.Payload.OutputS3Uri\",\n \"FramesEncoded.$\": \"$.Payload.FramesEncoded\",\n \"FileSize.$\": \"$.Payload.FileSize\",\n },\n resultPath: \"$.Output\",\n });\n assemble.addRetry({\n errors: NON_RETRYABLE_ASSEMBLE,\n maxAttempts: 0,\n });\n assemble.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const renderChunkV2Task = new tasks.LambdaInvoke(this, \"RenderChunkV2\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"renderChunk\",\n PlanProtocol: \"v2\",\n \"ChunkIndex.$\": \"$.ChunkIndex\",\n \"PlanV2ManifestS3Uri.$\": \"$.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.ChunkOutputS3Prefix\",\n \"Format.$\": \"$.Format\",\n }),\n resultSelector: {\n \"ChunkS3Uri.$\": \"$.Payload.ChunkS3Uri\",\n \"ChunkIndex.$\": \"$.Payload.ChunkIndex\",\n \"Sha256.$\": \"$.Payload.Sha256\",\n },\n });\n renderChunkV2Task.addRetry({ errors: NON_RETRYABLE_CHUNK, maxAttempts: 0 });\n renderChunkV2Task.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const renderChunksV2 = new sfn.Map(this, \"RenderChunksV2\", {\n itemsPath: \"$.Iterator.ChunkIndexes\",\n itemSelector: {\n \"ChunkIndex.$\": \"$$.Map.Item.Value\",\n \"PlanV2ManifestS3Uri.$\": \"$.Plan.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.Plan.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.Plan.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Format.$\": \"$.Plan.Format\",\n },\n maxConcurrencyPath: \"$.Plan.ChunkCount\",\n resultPath: \"$.Chunks\",\n });\n renderChunksV2.itemProcessor(renderChunkV2Task);\n\n const assembleV2 = new tasks.LambdaInvoke(this, \"AssembleV2\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"assemble\",\n PlanProtocol: \"v2\",\n \"PlanV2ManifestS3Uri.$\": \"$.Plan.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.Plan.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.Plan.PlanHash\",\n \"ChunkS3Uris.$\": \"$.Chunks[*].ChunkS3Uri\",\n AudioS3Uri: null,\n \"OutputS3Uri.$\": \"$.OutputS3Uri\",\n \"Format.$\": \"$.Plan.Format\",\n }),\n resultSelector: {\n \"OutputS3Uri.$\": \"$.Payload.OutputS3Uri\",\n \"FramesEncoded.$\": \"$.Payload.FramesEncoded\",\n \"FileSize.$\": \"$.Payload.FileSize\",\n },\n resultPath: \"$.Output\",\n });\n assembleV2.addRetry({ errors: NON_RETRYABLE_ASSEMBLE, maxAttempts: 0 });\n assembleV2.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const selectWorkerProtocol = new sfn.Choice(this, \"SelectWorkerProtocol\")\n .when(\n sfn.Condition.stringEquals(\"$.Plan.PlanProtocol\", \"v2\"),\n renderChunksV2.next(assembleV2),\n )\n .otherwise(renderChunks.next(assemble));\n const assertChunkCount = new sfn.Choice(this, \"AssertChunkCount\")\n .when(sfn.Condition.numberGreaterThan(\"$.Plan.ChunkCount\", 0), selectWorkerProtocol)\n .otherwise(planProducedZero);\n\n plan.next(buildChunkList);\n planV2.next(buildChunkList);\n buildChunkList.next(assertChunkCount);\n\n const unsupportedPlanProtocol = new sfn.Fail(this, \"UnsupportedPlanProtocol\", {\n error: \"PLAN_PROTOCOL_UNSUPPORTED\",\n cause: 'PlanProtocol must be \"v1\", \"v2\", or absent (defaults to v1).',\n });\n return new sfn.Choice(this, \"SelectPlanProtocol\")\n .when(sfn.Condition.stringEquals(\"$.PlanProtocol\", \"v2\"), planV2)\n .when(sfn.Condition.stringEquals(\"$.PlanProtocol\", \"v1\"), plan)\n .when(sfn.Condition.isPresent(\"$.PlanProtocol\"), unsupportedPlanProtocol)\n .otherwise(plan);\n }\n}\n\n/**\n * Default location of the handler ZIP relative to this source file. Two\n * parents up = `packages/aws-lambda/`; the build script writes the ZIP\n * to `packages/aws-lambda/dist/handler.zip`. The package is published with\n * `main: \"./src/index.ts\"`, so this path resolves correctly both in the\n * source tree (during `bun test` / local CDK synth) and in a consumer's\n * `node_modules/@hyperframes/aws-lambda/` install.\n */\nfunction defaultHandlerZipPath(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n return resolve(here, \"..\", \"..\", \"dist\", \"handler.zip\");\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * `HyperframesRenderStack` \u2014 aws-cdk-lib L2 construct that emits the same\n * topology as `examples/aws-lambda/template.yaml`.\n *\n * Adopters who embed HyperFrames inside their own CDK app can extend this\n * construct or compose alongside it; the construct exposes its `.bucket`,\n * `.renderFunction`, and `.stateMachine` properties so additional\n * resources (alarms, dashboards, SNS topics) can be wired without\n * re-deriving the ARNs from a stack export.\n *\n * `aws-cdk-lib` and `constructs` are **peerDependencies**. The package\n * still type-checks (and the snapshot test still runs) because they're\n * also `devDependencies`, but adopters who only consume the SDK side of\n * `@hyperframes/aws-lambda` don't pull the CDK tree at runtime.\n *\n * Drift from the SAM template is guarded by the snapshot test\n * (`HyperframesRenderStack.snapshot.test.ts`), which diffs the synthed\n * CloudFormation against the SAM-rendered CloudFormation modulo\n * normalisation.\n */\n\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Duration, RemovalPolicy, Size } from \"aws-cdk-lib\";\nimport * as cloudwatch from \"aws-cdk-lib/aws-cloudwatch\";\nimport * as lambda from \"aws-cdk-lib/aws-lambda\";\nimport * as logs from \"aws-cdk-lib/aws-logs\";\nimport * as s3 from \"aws-cdk-lib/aws-s3\";\nimport * as sfn from \"aws-cdk-lib/aws-stepfunctions\";\nimport * as tasks from \"aws-cdk-lib/aws-stepfunctions-tasks\";\nimport { Construct } from \"constructs\";\n\n/** Construction-time props for {@link HyperframesRenderStack}. */\nexport interface HyperframesRenderStackProps {\n /** Name prefix applied to function / state-machine / alarm names. Default `\"hyperframes\"`. */\n projectName?: string;\n /** Lambda memory in MB. Allowed: 2048..10240 in 1024 steps. Default 10240. */\n lambdaMemoryMb?: 2048 | 3072 | 4096 | 5120 | 6144 | 7168 | 8192 | 9216 | 10240;\n /** Per-invocation Lambda timeout. Default 900 (15 min, Lambda hard cap). */\n lambdaTimeoutSec?: number;\n /** Lambda reserved concurrency cap. `undefined` = unreserved (account default). */\n reservedConcurrency?: number;\n /** Which Chrome runtime was bundled into the handler ZIP. Default `\"sparticuz\"`. */\n chromeSource?: \"sparticuz\" | \"chrome-headless-shell\";\n /** Threshold for the runaway-invocations alarm. Default 1000 invocations/hour. */\n chunkInvocationAlarmThreshold?: number;\n /**\n * Absolute path to the handler ZIP produced by\n * `bun run --cwd packages/aws-lambda build:zip`. Defaults to the\n * package-relative path the build script writes to. Adopters who\n * deploy the published handler ZIP set this explicitly.\n */\n handlerZipPath?: string;\n /** S3 bucket retention policy on stack delete. Default RETAIN. */\n bucketRemovalPolicy?: RemovalPolicy;\n}\n\nconst DEFAULT_MEMORY_MB = 10240;\nconst DEFAULT_TIMEOUT_SEC = 900;\nconst DEFAULT_CHROME_SOURCE = \"sparticuz\";\nconst DEFAULT_ALARM_THRESHOLD = 1000;\n\nexport class HyperframesRenderStack extends Construct {\n /** S3 bucket for plan tarballs, chunk outputs, and final renders. */\n readonly bucket: s3.Bucket;\n /** The single Lambda function dispatching plan / renderChunk / assemble. */\n readonly renderFunction: lambda.Function;\n /** The Step Functions state machine orchestrating the render. */\n readonly stateMachine: sfn.StateMachine;\n\n constructor(scope: Construct, id: string, props: HyperframesRenderStackProps = {}) {\n super(scope, id);\n\n const projectName = props.projectName ?? \"hyperframes\";\n const memorySize = props.lambdaMemoryMb ?? DEFAULT_MEMORY_MB;\n const timeoutSec = props.lambdaTimeoutSec ?? DEFAULT_TIMEOUT_SEC;\n const chromeSource = props.chromeSource ?? DEFAULT_CHROME_SOURCE;\n const alarmThreshold = props.chunkInvocationAlarmThreshold ?? DEFAULT_ALARM_THRESHOLD;\n const handlerZipPath = props.handlerZipPath ?? defaultHandlerZipPath();\n\n this.bucket = new s3.Bucket(this, \"RenderBucket\", {\n removalPolicy: props.bucketRemovalPolicy ?? RemovalPolicy.RETAIN,\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n // `Suspended` is the cheapest mode that still satisfies KMS / replication\n // prerequisites callers can layer on later. Adopters who treat the final\n // mp4 as user-keepable can switch to `Enabled`.\n versioned: false,\n lifecycleRules: [\n {\n id: \"ExpireIntermediates\",\n enabled: true,\n prefix: \"renders/\",\n expiration: Duration.days(7),\n },\n ],\n });\n\n this.renderFunction = new lambda.Function(this, \"RenderFunction\", {\n functionName: `${projectName}-render`,\n runtime: lambda.Runtime.NODEJS_22_X,\n handler: \"handler.handler\",\n code: lambda.Code.fromAsset(handlerZipPath),\n memorySize,\n timeout: Duration.seconds(timeoutSec),\n ephemeralStorageSize: Size.gibibytes(10),\n architecture: lambda.Architecture.X86_64,\n reservedConcurrentExecutions: props.reservedConcurrency,\n tracing: lambda.Tracing.ACTIVE,\n environment: {\n NODE_OPTIONS: \"--enable-source-maps\",\n TMPDIR: \"/tmp\",\n HYPERFRAMES_LAMBDA_CHROME_SOURCE: chromeSource,\n HYPERFRAMES_RENDER_BUCKET: this.bucket.bucketName,\n },\n });\n\n // Scoped S3 perms only \u2014 explicitly NOT `CloudWatchLogsFullAccess`,\n // which would grant `logs:*` on `*` and overscope adopter accounts.\n // SAM's AWSLambdaBasicExecutionRole equivalent is included by the\n // default `new lambda.Function` execution role.\n this.bucket.grantReadWrite(this.renderFunction);\n\n const stateMachineLogGroup = new logs.LogGroup(this, \"RenderStateMachineLogGroup\", {\n logGroupName: `/aws/states/${projectName}-render`,\n retention: logs.RetentionDays.ONE_MONTH,\n removalPolicy: RemovalPolicy.DESTROY,\n });\n\n const definition = this.buildStateMachineDefinition();\n\n this.stateMachine = new sfn.StateMachine(this, \"RenderStateMachine\", {\n stateMachineName: `${projectName}-render`,\n stateMachineType: sfn.StateMachineType.STANDARD,\n definitionBody: sfn.DefinitionBody.fromChainable(definition),\n tracingEnabled: true,\n timeout: Duration.hours(1),\n logs: {\n destination: stateMachineLogGroup,\n level: sfn.LogLevel.ERROR,\n includeExecutionData: false,\n },\n });\n\n this.renderFunction.grantInvoke(this.stateMachine);\n\n new cloudwatch.Alarm(this, \"RenderChunkInvocationAlarm\", {\n alarmName: `${projectName}-runaway-chunk-invocations`,\n alarmDescription:\n \"Fires if RenderChunk Lambda invocations exceed the configured threshold in a 1-hour window.\",\n metric: this.renderFunction.metricInvocations({\n period: Duration.hours(1),\n statistic: cloudwatch.Stats.SUM,\n }),\n threshold: alarmThreshold,\n evaluationPeriods: 1,\n comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,\n treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,\n });\n\n new cloudwatch.Alarm(this, \"RenderFunctionErrorsAlarm\", {\n alarmName: `${projectName}-render-function-errors`,\n alarmDescription: \"Fires if the render Lambda reports any errors in a 5-minute window.\",\n metric: this.renderFunction.metricErrors({\n period: Duration.minutes(5),\n statistic: cloudwatch.Stats.SUM,\n }),\n threshold: 1,\n evaluationPeriods: 1,\n comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,\n treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,\n });\n\n new cloudwatch.Alarm(this, \"RenderStateMachineFailedAlarm\", {\n alarmName: `${projectName}-render-state-machine-failed`,\n alarmDescription: \"Fires when the render state machine reports a failed execution.\",\n metric: this.stateMachine.metricFailed({\n period: Duration.minutes(5),\n statistic: cloudwatch.Stats.SUM,\n }),\n threshold: 1,\n evaluationPeriods: 1,\n comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,\n treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,\n });\n }\n\n /**\n * Build the state-machine chain. Kept in a single method so the SAM\n * template and this construct can be diffed shape-for-shape during\n * the snapshot test.\n */\n private buildStateMachineDefinition(): sfn.IChainable {\n // `ChromeBinaryUnavailableError` is non-retryable: a wedged warm\n // instance keeps returning the same falsy executablePath until the\n // env recycles, so retries just burn the 4\u00D7 15-min budget.\n const NON_RETRYABLE_PLAN = [\n \"FFMPEG_VERSION_MISMATCH\",\n \"PLAN_HASH_MISMATCH\",\n \"S3_URI_NOT_ALLOWED\",\n \"BROWSER_GPU_NOT_SOFTWARE\",\n \"FONT_FETCH_FAILED\",\n \"PLAN_TOO_LARGE\",\n \"PlanTooLargeError\",\n \"PLAN_PROTOCOL_UNSUPPORTED\",\n \"PlanProtocolUnsupportedError\",\n \"PLAN_V2_INTEGRITY_UNRECOVERABLE\",\n \"VIDEO_SOURCE_UNRENDERABLE\",\n \"INVALID_VIDEO_METADATA\",\n \"NOT_MEDIA_PAYLOAD\",\n \"NotMediaPayloadError\",\n \"PlanV2IntegrityError\",\n \"PLAN_ARTIFACT_DIGEST_MISMATCH\",\n \"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED\",\n \"ChromeBinaryUnavailableError\",\n ];\n const NON_RETRYABLE_CHUNK = [\n \"FFMPEG_VERSION_MISMATCH\",\n \"PLAN_HASH_MISMATCH\",\n \"S3_URI_NOT_ALLOWED\",\n \"BROWSER_GPU_NOT_SOFTWARE\",\n \"PLAN_TOO_LARGE\",\n \"PlanTooLargeError\",\n \"PLAN_PROTOCOL_UNSUPPORTED\",\n \"PlanProtocolUnsupportedError\",\n \"PLAN_V2_INTEGRITY_UNRECOVERABLE\",\n \"INVALID_VIDEO_METADATA\",\n \"PlanV2IntegrityError\",\n \"PLAN_ARTIFACT_DIGEST_MISMATCH\",\n \"ChromeBinaryUnavailableError\",\n ];\n const NON_RETRYABLE_ASSEMBLE = [\n \"FFMPEG_VERSION_MISMATCH\",\n \"PLAN_HASH_MISMATCH\",\n \"S3_URI_NOT_ALLOWED\",\n \"PLAN_PROTOCOL_UNSUPPORTED\",\n \"PlanProtocolUnsupportedError\",\n \"PLAN_V2_INTEGRITY_UNRECOVERABLE\",\n \"PlanV2IntegrityError\",\n \"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED\",\n \"PLAN_TOO_LARGE\",\n \"PlanTooLargeError\",\n \"PLAN_ARTIFACT_DIGEST_MISMATCH\",\n \"ChromeBinaryUnavailableError\",\n ];\n\n const plan = new tasks.LambdaInvoke(this, \"Plan\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"plan\",\n \"ProjectS3Uri.$\": \"$.ProjectS3Uri\",\n \"PlanOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Config.$\": \"$.Config\",\n }),\n resultSelector: {\n PlanProtocol: \"v1\",\n \"PlanS3Uri.$\": \"$.Payload.PlanS3Uri\",\n \"PlanHash.$\": \"$.Payload.PlanHash\",\n \"ChunkCount.$\": \"$.Payload.ChunkCount\",\n \"Format.$\": \"$.Payload.Format\",\n \"HasAudio.$\": \"$.Payload.HasAudio\",\n \"AudioS3Uri.$\": \"$.Payload.AudioS3Uri\",\n },\n resultPath: \"$.Plan\",\n });\n plan.addRetry({\n errors: NON_RETRYABLE_PLAN,\n maxAttempts: 0,\n });\n plan.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const planV2 = new tasks.LambdaInvoke(this, \"PlanV2\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"plan\",\n PlanProtocol: \"v2\",\n \"ProjectS3Uri.$\": \"$.ProjectS3Uri\",\n \"PlanOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Config.$\": \"$.Config\",\n }),\n resultSelector: {\n PlanProtocol: \"v2\",\n \"PlanV2ManifestS3Uri.$\": \"$.Payload.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.Payload.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.Payload.PlanHash\",\n \"ChunkCount.$\": \"$.Payload.ChunkCount\",\n \"Format.$\": \"$.Payload.Format\",\n \"HasAudio.$\": \"$.Payload.HasAudio\",\n },\n resultPath: \"$.Plan\",\n });\n planV2.addRetry({ errors: NON_RETRYABLE_PLAN, maxAttempts: 0 });\n planV2.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const buildChunkList = new sfn.Pass(this, \"BuildChunkList\", {\n parameters: {\n \"ChunkIndexes.$\": \"States.ArrayRange(0, States.MathAdd($.Plan.ChunkCount, -1), 1)\",\n },\n resultPath: \"$.Iterator\",\n });\n\n const planProducedZero = new sfn.Fail(this, \"PlanProducedZeroChunks\", {\n error: \"PLAN_TOO_LARGE\",\n cause: \"Plan returned ChunkCount=0 \u2014 non-retryable producer-side invariant violation.\",\n });\n\n const renderChunkTask = new tasks.LambdaInvoke(this, \"RenderChunk\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"renderChunk\",\n \"ChunkIndex.$\": \"$.ChunkIndex\",\n \"PlanS3Uri.$\": \"$.PlanS3Uri\",\n \"PlanHash.$\": \"$.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.ChunkOutputS3Prefix\",\n \"Format.$\": \"$.Format\",\n }),\n resultSelector: {\n \"ChunkS3Uri.$\": \"$.Payload.ChunkS3Uri\",\n \"ChunkIndex.$\": \"$.Payload.ChunkIndex\",\n \"Sha256.$\": \"$.Payload.Sha256\",\n },\n });\n renderChunkTask.addRetry({\n errors: NON_RETRYABLE_CHUNK,\n maxAttempts: 0,\n });\n renderChunkTask.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const renderChunks = new sfn.Map(this, \"RenderChunks\", {\n itemsPath: \"$.Iterator.ChunkIndexes\",\n itemSelector: {\n \"ChunkIndex.$\": \"$$.Map.Item.Value\",\n \"PlanS3Uri.$\": \"$.Plan.PlanS3Uri\",\n \"PlanHash.$\": \"$.Plan.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Format.$\": \"$.Plan.Format\",\n },\n maxConcurrencyPath: \"$.Plan.ChunkCount\",\n resultPath: \"$.Chunks\",\n });\n renderChunks.itemProcessor(renderChunkTask);\n\n const assemble = new tasks.LambdaInvoke(this, \"Assemble\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"assemble\",\n \"PlanS3Uri.$\": \"$.Plan.PlanS3Uri\",\n \"ChunkS3Uris.$\": \"$.Chunks[*].ChunkS3Uri\",\n \"AudioS3Uri.$\": \"$.Plan.AudioS3Uri\",\n \"OutputS3Uri.$\": \"$.OutputS3Uri\",\n \"Format.$\": \"$.Plan.Format\",\n }),\n resultSelector: {\n \"OutputS3Uri.$\": \"$.Payload.OutputS3Uri\",\n \"FramesEncoded.$\": \"$.Payload.FramesEncoded\",\n \"FileSize.$\": \"$.Payload.FileSize\",\n },\n resultPath: \"$.Output\",\n });\n assemble.addRetry({\n errors: NON_RETRYABLE_ASSEMBLE,\n maxAttempts: 0,\n });\n assemble.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const renderChunkV2Task = new tasks.LambdaInvoke(this, \"RenderChunkV2\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"renderChunk\",\n PlanProtocol: \"v2\",\n \"ChunkIndex.$\": \"$.ChunkIndex\",\n \"PlanV2ManifestS3Uri.$\": \"$.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.ChunkOutputS3Prefix\",\n \"Format.$\": \"$.Format\",\n }),\n resultSelector: {\n \"ChunkS3Uri.$\": \"$.Payload.ChunkS3Uri\",\n \"ChunkIndex.$\": \"$.Payload.ChunkIndex\",\n \"Sha256.$\": \"$.Payload.Sha256\",\n },\n });\n renderChunkV2Task.addRetry({ errors: NON_RETRYABLE_CHUNK, maxAttempts: 0 });\n renderChunkV2Task.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const renderChunksV2 = new sfn.Map(this, \"RenderChunksV2\", {\n itemsPath: \"$.Iterator.ChunkIndexes\",\n itemSelector: {\n \"ChunkIndex.$\": \"$$.Map.Item.Value\",\n \"PlanV2ManifestS3Uri.$\": \"$.Plan.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.Plan.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.Plan.PlanHash\",\n \"ChunkOutputS3Prefix.$\": \"$.PlanOutputS3Prefix\",\n \"Format.$\": \"$.Plan.Format\",\n },\n maxConcurrencyPath: \"$.Plan.ChunkCount\",\n resultPath: \"$.Chunks\",\n });\n renderChunksV2.itemProcessor(renderChunkV2Task);\n\n const assembleV2 = new tasks.LambdaInvoke(this, \"AssembleV2\", {\n lambdaFunction: this.renderFunction,\n payload: sfn.TaskInput.fromObject({\n Action: \"assemble\",\n PlanProtocol: \"v2\",\n \"PlanV2ManifestS3Uri.$\": \"$.Plan.PlanV2ManifestS3Uri\",\n \"PlanV2ArtifactS3Prefix.$\": \"$.Plan.PlanV2ArtifactS3Prefix\",\n \"PlanHash.$\": \"$.Plan.PlanHash\",\n \"ChunkS3Uris.$\": \"$.Chunks[*].ChunkS3Uri\",\n AudioS3Uri: null,\n \"OutputS3Uri.$\": \"$.OutputS3Uri\",\n \"Format.$\": \"$.Plan.Format\",\n }),\n resultSelector: {\n \"OutputS3Uri.$\": \"$.Payload.OutputS3Uri\",\n \"FramesEncoded.$\": \"$.Payload.FramesEncoded\",\n \"FileSize.$\": \"$.Payload.FileSize\",\n },\n resultPath: \"$.Output\",\n });\n assembleV2.addRetry({ errors: NON_RETRYABLE_ASSEMBLE, maxAttempts: 0 });\n assembleV2.addRetry({\n errors: [\"States.ALL\"],\n interval: Duration.seconds(2),\n maxAttempts: 4,\n backoffRate: 2,\n maxDelay: Duration.seconds(60),\n });\n\n const selectWorkerProtocol = new sfn.Choice(this, \"SelectWorkerProtocol\")\n .when(\n sfn.Condition.stringEquals(\"$.Plan.PlanProtocol\", \"v2\"),\n renderChunksV2.next(assembleV2),\n )\n .otherwise(renderChunks.next(assemble));\n const assertChunkCount = new sfn.Choice(this, \"AssertChunkCount\")\n .when(sfn.Condition.numberGreaterThan(\"$.Plan.ChunkCount\", 0), selectWorkerProtocol)\n .otherwise(planProducedZero);\n\n plan.next(buildChunkList);\n planV2.next(buildChunkList);\n buildChunkList.next(assertChunkCount);\n\n const unsupportedPlanProtocol = new sfn.Fail(this, \"UnsupportedPlanProtocol\", {\n error: \"PLAN_PROTOCOL_UNSUPPORTED\",\n cause: 'PlanProtocol must be \"v1\", \"v2\", or absent (defaults to v1).',\n });\n return new sfn.Choice(this, \"SelectPlanProtocol\")\n .when(sfn.Condition.stringEquals(\"$.PlanProtocol\", \"v2\"), planV2)\n .when(sfn.Condition.stringEquals(\"$.PlanProtocol\", \"v1\"), plan)\n .when(sfn.Condition.isPresent(\"$.PlanProtocol\"), unsupportedPlanProtocol)\n .otherwise(plan);\n }\n}\n\n/**\n * Default location of the handler ZIP relative to this source file. Two\n * parents up = `packages/aws-lambda/`; the build script writes the ZIP\n * to `packages/aws-lambda/dist/handler.zip`. The package is published with\n * `main: \"./src/index.ts\"`, so this path resolves correctly both in the\n * source tree (during `bun test` / local CDK synth) and in a consumer's\n * `node_modules/@hyperframes/aws-lambda/` install.\n */\nfunction defaultHandlerZipPath(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n return resolve(here, \"..\", \"..\", \"dist\", \"handler.zip\");\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,QAAQ,EAER,KAAK,aAAa,EAKlB,IAAI,EACJ,mBAAmB,EAOpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,KAAK,EACV,aAAa,EAGb,WAAW,EACX,YAAY,EACZ,SAAS,EAET,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAsBrB;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,OAAO,IAAI,CAAC;QAClB,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;QACjD,WAAW,EAAE,aAAa,CAAC;QAC3B,QAAQ,EAAE,OAAO,QAAQ,CAAC;KAC3B,CAAC;IACF,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA0C3F;AAmCD,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,gBAAgB,GAAG,aAAa,CAsB5F"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,QAAQ,EAER,KAAK,aAAa,EAKlB,IAAI,EACJ,mBAAmB,EAOpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,KAAK,EACV,aAAa,EAGb,WAAW,EACX,YAAY,EACZ,SAAS,EAET,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAsBrB;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,OAAO,IAAI,CAAC;QAClB,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;QACjD,WAAW,EAAE,aAAa,CAAC;QAC3B,QAAQ,EAAE,OAAO,QAAQ,CAAC;KAC3B,CAAC;IACF,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA0C3F;AAoCD,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,gBAAgB,GAAG,aAAa,CAsB5F"}
package/dist/handler.js CHANGED
@@ -417,7 +417,7 @@ async function handler(event, deps) {
417
417
  function normalizeTerminalErrorName(error) {
418
418
  if (!error || typeof error !== "object") return;
419
419
  const candidate = error;
420
- if (candidate.code === "PLAN_PROTOCOL_UNSUPPORTED" || candidate.code === "PLAN_TOO_LARGE" || candidate.code === "PLAN_V2_INTEGRITY_UNRECOVERABLE" || candidate.code === "FONT_FETCH_FAILED" || candidate.code === "FONT_FETCH_UNAVAILABLE" || candidate.code === "VIDEO_SOURCE_UNRENDERABLE" || candidate.code === "VIDEO_EXTRACTION_FAILED" || candidate.code === "INVALID_VIDEO_METADATA") {
420
+ if (candidate.code === "PLAN_PROTOCOL_UNSUPPORTED" || candidate.code === "PLAN_TOO_LARGE" || candidate.code === "PLAN_V2_INTEGRITY_UNRECOVERABLE" || candidate.code === "FONT_FETCH_FAILED" || candidate.code === "FONT_FETCH_UNAVAILABLE" || candidate.code === "NOT_MEDIA_PAYLOAD" || candidate.code === "VIDEO_SOURCE_UNRENDERABLE" || candidate.code === "VIDEO_EXTRACTION_FAILED" || candidate.code === "INVALID_VIDEO_METADATA") {
421
421
  candidate.name = candidate.code;
422
422
  }
423
423
  }
@@ -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/s3PlanV2Publisher.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 ChunkRenderer,\n type ChunkResult,\n type DistributedRenderConfig,\n listPlanV2ArtifactsForTarget,\n materializePlanV2Target,\n plan,\n planV2WithPublisher,\n type PlanResult,\n type PlanV2Artifact,\n type PlanV2Manifest,\n type PlanV2MaterializationTarget,\n readPlanV2Manifest,\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 downloadS3ObjectToFileVerified,\n parseS3Uri,\n tarDirectory,\n untarDirectory,\n uploadFileToS3,\n} from \"./s3Transport.js\";\nimport { S3PlanV2ArtifactPublisher } from \"./s3PlanV2Publisher.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 planV2WithPublisher?: typeof planV2WithPublisher;\n renderChunk: ChunkRenderer;\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 normalizeTerminalErrorName(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 input: summarizeEvent(unwrapped),\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 * AWS Lambda reports `Error.name` to Step Functions, while producer errors\n * expose stable machine codes separately. Normalize workflow-facing codes\n * whose historical class names differ from their orchestration contracts.\n */\n// The explicit error-name mapping is the public Step Functions failure contract.\n// fallow-ignore-next-line complexity\nfunction normalizeTerminalErrorName(error: unknown): void {\n if (!error || typeof error !== \"object\") return;\n const candidate = error as { code?: unknown; name?: string };\n if (\n candidate.code === \"PLAN_PROTOCOL_UNSUPPORTED\" ||\n candidate.code === \"PLAN_TOO_LARGE\" ||\n candidate.code === \"PLAN_V2_INTEGRITY_UNRECOVERABLE\" ||\n candidate.code === \"FONT_FETCH_FAILED\" ||\n candidate.code === \"FONT_FETCH_UNAVAILABLE\" ||\n candidate.code === \"VIDEO_SOURCE_UNRENDERABLE\" ||\n candidate.code === \"VIDEO_EXTRACTION_FAILED\" ||\n candidate.code === \"INVALID_VIDEO_METADATA\"\n ) {\n candidate.name = candidate.code;\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 */\n// Keep event variants together so logs share one redaction and summarization boundary.\n// fallow-ignore-next-line complexity\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 planProtocol: event.PlanProtocol ?? \"v1\",\n format: event.Config.format,\n fps: event.Config.fps,\n };\n case \"renderChunk\":\n return {\n planProtocol: event.PlanProtocol ?? \"v1\",\n ...(event.PlanProtocol === \"v2\"\n ? { planV2ManifestS3Uri: event.PlanV2ManifestS3Uri }\n : { planS3Uri: event.PlanS3Uri }),\n chunkIndex: event.ChunkIndex,\n format: event.Format,\n };\n case \"assemble\":\n return {\n planProtocol: event.PlanProtocol ?? \"v1\",\n ...(event.PlanProtocol === \"v2\"\n ? { planV2ManifestS3Uri: event.PlanV2ManifestS3Uri }\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\n// The v1 handler owns one transactional download, plan, archive, upload, and cleanup lifecycle.\n// fallow-ignore-next-line complexity\nasync function handlePlan(event: PlanEvent, deps?: HandlerDeps): Promise<PlanLambdaResult> {\n if (event.PlanProtocol === \"v2\") {\n return handlePlanV2(event, deps);\n }\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// Plan v2 orchestration is kept as one transactional boundary: stage, CAS upload,\n// and manifest-last publication must stay ordered and fail together.\n// fallow-ignore-next-line complexity\nasync function handlePlanV2(\n event: Extract<PlanEvent, { PlanProtocol: \"v2\" }>,\n deps?: HandlerDeps,\n): Promise<Extract<PlanLambdaResult, { PlanProtocol: \"v2\" }>> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.planV2WithPublisher ?? planV2WithPublisher;\n if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n process.env.PRODUCER_HEADLESS_SHELL_PATH = await resolveChromeExecutablePath();\n }\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-plan-v2-\"));\n const projectArchive = join(work, \"project.tar.gz\");\n const projectDir = join(work, \"project\");\n try {\n await downloadS3ObjectToFile(s3, event.ProjectS3Uri, projectArchive);\n await untarDirectory(projectArchive, projectDir);\n const publisher = new S3PlanV2ArtifactPublisher({\n s3,\n planOutputS3Prefix: event.PlanOutputS3Prefix,\n temporaryRoot: work,\n });\n const manifest: PlanV2Manifest = await primitive(projectDir, { ...event.Config }, publisher, {\n stagingParentDir: work,\n });\n\n return {\n Action: \"plan\",\n PlanProtocol: \"v2\",\n PlanV2ManifestS3Uri: publisher.manifestUri,\n PlanV2ArtifactS3Prefix: publisher.artifactPrefix,\n PlanHash: manifest.planHash,\n ChunkCount: manifest.chunkCount,\n TotalFrames: manifest.totalFrames,\n Fps: manifest.fps,\n Width: manifest.width,\n Height: manifest.height,\n Format: manifest.format,\n HasAudio: manifest.artifacts.some((artifact) => artifact.path === \"audio.aac\"),\n AudioS3Uri: null,\n FfmpegVersion: manifest.ffmpegVersion,\n ProducerVersion: manifest.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 if (event.PlanProtocol === \"v2\") {\n return handleRenderChunkV2(event, deps);\n }\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 CaptureMode: result.captureMode,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\n// The v2 chunk handler deliberately keeps download, verified materialization,\n// render, and upload in one lifecycle so cleanup and errors remain atomic.\n// fallow-ignore-next-line complexity\nasync function handleRenderChunkV2(\n event: Extract<RenderChunkEvent, { PlanProtocol: \"v2\" }>,\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 if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n process.env.PRODUCER_HEADLESS_SHELL_PATH = await resolveChromeExecutablePath();\n }\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-chunk-v2-\"));\n try {\n const planDir = await downloadAndMaterializePlanV2(\n s3,\n event,\n { role: \"chunk\", chunkIndex: event.ChunkIndex },\n work,\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 const result = await primitive(planDir, event.ChunkIndex, chunkOutputBase);\n const chunkUri = await uploadChunkOutput(\n s3,\n result,\n event.ChunkOutputS3Prefix,\n event.ChunkIndex,\n );\n return {\n Action: \"renderChunk\",\n ChunkS3Uri: chunkUri,\n ChunkIndex: event.ChunkIndex,\n Sha256: result.sha256,\n FramesEncoded: result.framesEncoded,\n CaptureMode: result.captureMode,\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 if (event.PlanProtocol === \"v2\") {\n return handleAssembleV2(event, deps);\n }\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\n// Assembly mirrors the chunk lifecycle while adding assembler-only artifacts;\n// keeping the steps local makes its temporary-storage ownership explicit.\n// fallow-ignore-next-line complexity\nasync function handleAssembleV2(\n event: Extract<AssembleEvent, { PlanProtocol: \"v2\" }>,\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 const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-assemble-v2-\"));\n try {\n const planDir = await downloadAndMaterializePlanV2(s3, event, { role: \"assembler\" }, work);\n // `downloadAndMaterializePlanV2` materializes atomically. Audio is\n // assembler-only and lives at the familiar v1-compatible location.\n const audioPath = existsSync(join(planDir, \"audio.aac\")) ? join(planDir, \"audio.aac\") : null;\n const chunkPaths = await downloadChunkObjects(s3, event.ChunkS3Uris, work, event.Format);\n const finalOutput =\n event.Format === \"png-sequence\"\n ? join(work, \"output-frames\")\n : join(work, `output${formatExtension(event.Format)}`);\n const result = await primitive(planDir, chunkPaths, audioPath, finalOutput, {\n cfr: event.Cfr === true,\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 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 downloadAndMaterializePlanV2(\n s3: S3Client,\n event: {\n PlanV2ManifestS3Uri: string;\n PlanV2ArtifactS3Prefix: string;\n PlanHash: string;\n },\n target: PlanV2MaterializationTarget,\n work: string,\n): Promise<string> {\n const transportDir = join(work, \"plan-v2\");\n mkdirSync(transportDir, { recursive: true });\n await downloadS3ObjectToFile(s3, event.PlanV2ManifestS3Uri, join(transportDir, \"plan.json\"));\n const manifest = readPlanV2Manifest(transportDir);\n if (manifest.planHash !== event.PlanHash) {\n throwPlanHashMismatch(event.PlanHash, manifest.planHash);\n }\n const artifacts = listPlanV2ArtifactsForTarget(manifest, target);\n const uniqueArtifacts = [\n ...new Map(artifacts.map((artifact) => [artifact.sha256, artifact])).values(),\n ];\n await mapConcurrent(uniqueArtifacts, 16, async (artifact) => {\n await downloadPlanV2Artifact(s3, event.PlanV2ArtifactS3Prefix, transportDir, artifact);\n });\n const planDir = join(work, \"plan\");\n materializePlanV2Target(transportDir, target, planDir);\n return planDir;\n}\n\nasync function downloadPlanV2Artifact(\n s3: S3Client,\n artifactPrefix: string,\n planV2Dir: string,\n artifact: Readonly<PlanV2Artifact>,\n): Promise<void> {\n await downloadS3ObjectToFileVerified(\n s3,\n planV2BlobUri(artifactPrefix, artifact.sha256),\n planV2BlobPath(planV2Dir, artifact.sha256),\n artifact.sha256,\n );\n}\n\nfunction planV2BlobPath(planV2Dir: string, digest: string): string {\n return join(planV2Dir, \"artifacts\", \"sha256\", digest.slice(0, 2), digest);\n}\n\nfunction planV2BlobUri(prefix: string, digest: string): string {\n return `${trimTrailingSlash(prefix)}/${digest.slice(0, 2)}/${digest}`;\n}\n\nfunction throwPlanHashMismatch(expected: string, actual: string): never {\n const error = new Error(\n `PLAN_HASH_MISMATCH: event PlanHash=${expected} did not match v2 manifest planHash=${actual}`,\n );\n error.name = \"PLAN_HASH_MISMATCH\";\n throw error;\n}\n\nasync function mapConcurrent<T>(\n values: readonly T[],\n concurrency: number,\n fn: (value: T) => Promise<void>,\n): Promise<void> {\n let cursor = 0;\n async function worker(): Promise<void> {\n while (cursor < values.length) {\n const index = cursor++;\n await fn(values[index]!);\n }\n }\n const results = await Promise.allSettled(\n Array.from({ length: Math.min(concurrency, values.length) }, () => worker()),\n );\n const failure = results.find(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n // Do not reject while sibling workers may still be writing into invocation\n // scratch. The caller removes that directory in `finally`; draining the pool\n // first prevents late S3 streams from racing cleanup after another GET fails.\n if (failure) throw failure.reason;\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. */\n// This is an exhaustive event-union projection used only for safe log summaries.\n// fallow-ignore-next-line complexity\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.PlanProtocol === \"v2\"\n ? [event.PlanV2ManifestS3Uri, event.PlanV2ArtifactS3Prefix, event.ChunkOutputS3Prefix]\n : [event.PlanS3Uri, event.ChunkOutputS3Prefix];\n case \"assemble\":\n return [\n ...(event.PlanProtocol === \"v2\"\n ? [event.PlanV2ManifestS3Uri, event.PlanV2ArtifactS3Prefix]\n : [event.PlanS3Uri]),\n ...event.ChunkS3Uris,\n event.OutputS3Uri,\n event.AudioS3Uri,\n ].filter((u): u is string => u != null);\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 { createHash } from \"node:crypto\";\nimport { dirname } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport {\n GetObjectCommand,\n HeadObjectCommand,\n PutObjectCommand,\n type S3Client,\n} 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/** Download and verify an immutable plan-v2 artifact before materialization. */\nexport async function downloadS3ObjectToFileVerified(\n client: S3Client,\n uri: string,\n destPath: string,\n expectedSha256: string,\n): Promise<void> {\n assertSha256(expectedSha256);\n await downloadS3ObjectToFile(client, uri, destPath);\n const actual = await sha256File(destPath);\n if (actual !== expectedSha256) {\n rmSync(destPath, { force: true });\n const error = new Error(\n `[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: ${uri} expected ${expectedSha256}, got ${actual}`,\n );\n error.name = \"PLAN_ARTIFACT_DIGEST_MISMATCH\";\n throw error;\n }\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 * Upload one content-addressed plan-v2 artifact exactly once.\n *\n * Existing objects are reused only when their immutable digest metadata and\n * byte length agree. A conflicting object is never overwritten: doing so\n * could change a plan already being consumed by another chunk invocation.\n */\nexport async function uploadContentAddressedFileToS3(\n client: S3Client,\n localPath: string,\n uri: string,\n expectedSha256: string,\n contentType?: string,\n): Promise<\"uploaded\" | \"reused\"> {\n assertSha256(expectedSha256);\n if (!existsSync(localPath)) {\n throw new Error(`[s3Transport] upload source missing: ${localPath}`);\n }\n const actualSha256 = await sha256File(localPath);\n if (actualSha256 !== expectedSha256) {\n const error = new Error(\n `[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: local artifact ${localPath} expected ${expectedSha256}, got ${actualSha256}`,\n );\n error.name = \"PLAN_ARTIFACT_DIGEST_MISMATCH\";\n throw error;\n }\n\n const { bucket, key } = parseS3Uri(uri);\n const size = statSync(localPath).size;\n const existing = await inspectContentAddressedObject(client, bucket, key, size, expectedSha256);\n if (existing === \"matching\") return \"reused\";\n if (existing === \"conflict\") throwImmutableObjectConflict(uri);\n\n const body = createReadStream(localPath);\n try {\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n ContentLength: size,\n Metadata: { sha256: expectedSha256 },\n ChecksumSHA256: Buffer.from(expectedSha256, \"hex\").toString(\"base64\"),\n // HEAD followed by an unconditional PUT can overwrite a conflicting\n // object published by a concurrent planner. Conditional create makes\n // immutable CAS and fixed-key manifest publication race-safe.\n IfNoneMatch: \"*\",\n }),\n );\n return \"uploaded\";\n } catch (error) {\n if (!isS3PreconditionFailed(error)) throw error;\n const raced = await inspectContentAddressedObject(client, bucket, key, size, expectedSha256);\n if (raced === \"matching\") return \"reused\";\n if (raced === \"conflict\") throwImmutableObjectConflict(uri);\n // The winning object was deleted between the conditional failure and\n // verification. Preserve the service error so the orchestrator may retry.\n throw error;\n } finally {\n // A failed conditional request may reject before consuming the stream.\n // Explicit teardown avoids retaining the source descriptor on a warm\n // Lambda planner.\n body.destroy();\n }\n}\n\nexport async function sha256File(path: string): Promise<string> {\n const hash = createHash(\"sha256\");\n for await (const chunk of createReadStream(path)) {\n hash.update(chunk as Buffer);\n }\n return hash.digest(\"hex\");\n}\n\nfunction assertSha256(value: string): void {\n if (!/^[a-f0-9]{64}$/.test(value)) {\n throw new Error(\n `[s3Transport] expected lowercase SHA-256 digest, got ${JSON.stringify(value)}`,\n );\n }\n}\n\ntype ContentAddressedObjectState = \"missing\" | \"matching\" | \"conflict\";\n\nasync function inspectContentAddressedObject(\n client: S3Client,\n bucket: string,\n key: string,\n expectedSize: number,\n expectedSha256: string,\n): Promise<ContentAddressedObjectState> {\n try {\n const existing = await client.send(\n new HeadObjectCommand({ Bucket: bucket, Key: key, ChecksumMode: \"ENABLED\" }),\n );\n return existing.ContentLength === expectedSize && existing.Metadata?.sha256 === expectedSha256\n ? \"matching\"\n : \"conflict\";\n } catch (error) {\n if (isS3NotFound(error)) return \"missing\";\n throw error;\n }\n}\n\nfunction throwImmutableObjectConflict(uri: string): never {\n const error = new Error(\n `[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: immutable object ${uri} already exists with different digest metadata or size`,\n );\n error.name = \"PLAN_ARTIFACT_DIGEST_MISMATCH\";\n throw error;\n}\n\nfunction isS3NotFound(error: unknown): boolean {\n if (!isRecord(error)) return false;\n const metadata = isRecord(error.$metadata) ? error.$metadata : undefined;\n return (\n error.name === \"NotFound\" || error.name === \"NoSuchKey\" || metadata?.httpStatusCode === 404\n );\n}\n\nfunction isS3PreconditionFailed(error: unknown): boolean {\n if (!isRecord(error)) return false;\n const metadata = isRecord(error.$metadata) ? error.$metadata : undefined;\n return error.name === \"PreconditionFailed\" || metadata?.httpStatusCode === 412;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\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", "import { createHash } from \"node:crypto\";\nimport { mkdirSync, mkdtempSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { S3Client } from \"@aws-sdk/client-s3\";\nimport {\n PlanV2IntegrityError,\n type PlanV2ArtifactPublisher,\n type PlanV2PublishBlob,\n} from \"@hyperframes/producer/distributed\";\nimport { parseS3Uri, uploadContentAddressedFileToS3 } from \"./s3Transport.js\";\n\nexport interface S3PlanV2ArtifactPublisherOptions {\n readonly s3: S3Client;\n /** Validated render output prefix from which all v2 object keys are derived. */\n readonly planOutputS3Prefix: string;\n /** Planner-local scratch parent for the small manifest upload file. */\n readonly temporaryRoot?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction assertSha256(value: unknown, label: string): string {\n if (typeof value !== \"string\" || !/^[a-f0-9]{64}$/.test(value)) {\n throw new PlanV2IntegrityError(`${label} must be a lowercase SHA-256 digest`);\n }\n return value;\n}\n\nfunction manifestDigests(manifestBytes: string): ReadonlySet<string> {\n let value: unknown;\n try {\n value = JSON.parse(manifestBytes);\n } catch {\n throw new PlanV2IntegrityError(\"S3 publisher received invalid manifest JSON\");\n }\n if (!isRecord(value) || !Array.isArray(value.artifacts)) {\n throw new PlanV2IntegrityError(\"S3 publisher manifest requires an artifacts array\");\n }\n return new Set(\n value.artifacts.map((artifact, index) => {\n if (!isRecord(artifact)) {\n throw new PlanV2IntegrityError(`S3 publisher artifacts[${index}] must be an object`);\n }\n return assertSha256(artifact.sha256, `S3 publisher artifacts[${index}].sha256`);\n }),\n );\n}\n\nfunction trimTrailingSlash(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\n/**\n * Manifest-last S3 implementation of the producer's plan-v2 publication seam.\n *\n * Blobs stream from the planner's private frozen directory directly to S3.\n * Successfully uploaded or safely reused digests are tracked so a manifest\n * cannot become visible before all of its references are durable.\n */\nexport class S3PlanV2ArtifactPublisher implements PlanV2ArtifactPublisher {\n readonly artifactPrefix: string;\n readonly manifestUri: string;\n readonly #s3: S3Client;\n readonly #temporaryRoot: string;\n readonly #publishedDigests = new Set<string>();\n #state: \"open\" | \"committed\" | \"aborted\" = \"open\";\n\n constructor(options: Readonly<S3PlanV2ArtifactPublisherOptions>) {\n const outputPrefix = `${trimTrailingSlash(options.planOutputS3Prefix)}/v2`;\n parseS3Uri(outputPrefix);\n this.#s3 = options.s3;\n this.artifactPrefix = `${outputPrefix}/artifacts/sha256`;\n this.manifestUri = `${outputPrefix}/manifest.json`;\n this.#temporaryRoot = options.temporaryRoot ?? tmpdir();\n mkdirSync(this.#temporaryRoot, { recursive: true });\n }\n\n async putBlob(blob: Readonly<PlanV2PublishBlob>): Promise<void> {\n this.#assertOpen(\"publish a blob\");\n const digest = assertSha256(blob.sha256, \"S3 published blob sha256\");\n const sourceSize = statSync(blob.sourcePath).size;\n if (sourceSize !== blob.sizeBytes) {\n throw new PlanV2IntegrityError(\n `S3 published blob size changed for ${digest}: expected ${blob.sizeBytes}, got ${sourceSize}`,\n );\n }\n const uri = `${this.artifactPrefix}/${digest.slice(0, 2)}/${digest}`;\n await uploadContentAddressedFileToS3(this.#s3, blob.sourcePath, uri, digest);\n this.#publishedDigests.add(digest);\n }\n\n async commitManifest(manifestBytes: string): Promise<void> {\n this.#assertOpen(\"commit a manifest\");\n for (const digest of manifestDigests(manifestBytes)) {\n if (!this.#publishedDigests.has(digest)) {\n throw new PlanV2IntegrityError(\n `cannot commit S3 manifest before referenced blob is durable: ${digest}`,\n );\n }\n }\n\n const manifestDigest = createHash(\"sha256\").update(manifestBytes, \"utf8\").digest(\"hex\");\n const stagingDir = mkdtempSync(join(this.#temporaryRoot, \"hf-plan-v2-manifest-\"));\n const manifestPath = join(stagingDir, \"manifest.json\");\n try {\n writeFileSync(manifestPath, manifestBytes, \"utf8\");\n await uploadContentAddressedFileToS3(\n this.#s3,\n manifestPath,\n this.manifestUri,\n manifestDigest,\n \"application/json\",\n );\n this.#state = \"committed\";\n } finally {\n rmSync(stagingDir, { recursive: true, force: true });\n }\n }\n\n async abort(): Promise<void> {\n if (this.#state === \"open\") this.#state = \"aborted\";\n // Remote CAS blobs are immutable and may already be reused by a retry.\n // Without a committed manifest they are unreachable and expire under the\n // render bucket's intermediate-object lifecycle policy.\n }\n\n #assertOpen(operation: string): void {\n if (this.#state !== \"open\") {\n throw new PlanV2IntegrityError(`cannot ${operation} after publisher is ${this.#state}`);\n }\n }\n}\n"],
5
- "mappings": "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",
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 ChunkRenderer,\n type ChunkResult,\n type DistributedRenderConfig,\n listPlanV2ArtifactsForTarget,\n materializePlanV2Target,\n plan,\n planV2WithPublisher,\n type PlanResult,\n type PlanV2Artifact,\n type PlanV2Manifest,\n type PlanV2MaterializationTarget,\n readPlanV2Manifest,\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 downloadS3ObjectToFileVerified,\n parseS3Uri,\n tarDirectory,\n untarDirectory,\n uploadFileToS3,\n} from \"./s3Transport.js\";\nimport { S3PlanV2ArtifactPublisher } from \"./s3PlanV2Publisher.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 planV2WithPublisher?: typeof planV2WithPublisher;\n renderChunk: ChunkRenderer;\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 normalizeTerminalErrorName(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 input: summarizeEvent(unwrapped),\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 * AWS Lambda reports `Error.name` to Step Functions, while producer errors\n * expose stable machine codes separately. Normalize workflow-facing codes\n * whose historical class names differ from their orchestration contracts.\n */\n// The explicit error-name mapping is the public Step Functions failure contract.\n// fallow-ignore-next-line complexity\nfunction normalizeTerminalErrorName(error: unknown): void {\n if (!error || typeof error !== \"object\") return;\n const candidate = error as { code?: unknown; name?: string };\n if (\n candidate.code === \"PLAN_PROTOCOL_UNSUPPORTED\" ||\n candidate.code === \"PLAN_TOO_LARGE\" ||\n candidate.code === \"PLAN_V2_INTEGRITY_UNRECOVERABLE\" ||\n candidate.code === \"FONT_FETCH_FAILED\" ||\n candidate.code === \"FONT_FETCH_UNAVAILABLE\" ||\n candidate.code === \"NOT_MEDIA_PAYLOAD\" ||\n candidate.code === \"VIDEO_SOURCE_UNRENDERABLE\" ||\n candidate.code === \"VIDEO_EXTRACTION_FAILED\" ||\n candidate.code === \"INVALID_VIDEO_METADATA\"\n ) {\n candidate.name = candidate.code;\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 */\n// Keep event variants together so logs share one redaction and summarization boundary.\n// fallow-ignore-next-line complexity\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 planProtocol: event.PlanProtocol ?? \"v1\",\n format: event.Config.format,\n fps: event.Config.fps,\n };\n case \"renderChunk\":\n return {\n planProtocol: event.PlanProtocol ?? \"v1\",\n ...(event.PlanProtocol === \"v2\"\n ? { planV2ManifestS3Uri: event.PlanV2ManifestS3Uri }\n : { planS3Uri: event.PlanS3Uri }),\n chunkIndex: event.ChunkIndex,\n format: event.Format,\n };\n case \"assemble\":\n return {\n planProtocol: event.PlanProtocol ?? \"v1\",\n ...(event.PlanProtocol === \"v2\"\n ? { planV2ManifestS3Uri: event.PlanV2ManifestS3Uri }\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\n// The v1 handler owns one transactional download, plan, archive, upload, and cleanup lifecycle.\n// fallow-ignore-next-line complexity\nasync function handlePlan(event: PlanEvent, deps?: HandlerDeps): Promise<PlanLambdaResult> {\n if (event.PlanProtocol === \"v2\") {\n return handlePlanV2(event, deps);\n }\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// Plan v2 orchestration is kept as one transactional boundary: stage, CAS upload,\n// and manifest-last publication must stay ordered and fail together.\n// fallow-ignore-next-line complexity\nasync function handlePlanV2(\n event: Extract<PlanEvent, { PlanProtocol: \"v2\" }>,\n deps?: HandlerDeps,\n): Promise<Extract<PlanLambdaResult, { PlanProtocol: \"v2\" }>> {\n const started = Date.now();\n const s3 = deps?.s3 ?? getS3Client();\n const primitive = deps?.primitives?.planV2WithPublisher ?? planV2WithPublisher;\n if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n process.env.PRODUCER_HEADLESS_SHELL_PATH = await resolveChromeExecutablePath();\n }\n\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-plan-v2-\"));\n const projectArchive = join(work, \"project.tar.gz\");\n const projectDir = join(work, \"project\");\n try {\n await downloadS3ObjectToFile(s3, event.ProjectS3Uri, projectArchive);\n await untarDirectory(projectArchive, projectDir);\n const publisher = new S3PlanV2ArtifactPublisher({\n s3,\n planOutputS3Prefix: event.PlanOutputS3Prefix,\n temporaryRoot: work,\n });\n const manifest: PlanV2Manifest = await primitive(projectDir, { ...event.Config }, publisher, {\n stagingParentDir: work,\n });\n\n return {\n Action: \"plan\",\n PlanProtocol: \"v2\",\n PlanV2ManifestS3Uri: publisher.manifestUri,\n PlanV2ArtifactS3Prefix: publisher.artifactPrefix,\n PlanHash: manifest.planHash,\n ChunkCount: manifest.chunkCount,\n TotalFrames: manifest.totalFrames,\n Fps: manifest.fps,\n Width: manifest.width,\n Height: manifest.height,\n Format: manifest.format,\n HasAudio: manifest.artifacts.some((artifact) => artifact.path === \"audio.aac\"),\n AudioS3Uri: null,\n FfmpegVersion: manifest.ffmpegVersion,\n ProducerVersion: manifest.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 if (event.PlanProtocol === \"v2\") {\n return handleRenderChunkV2(event, deps);\n }\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 CaptureMode: result.captureMode,\n DurationMs: Date.now() - started,\n };\n } finally {\n cleanupDir(work);\n }\n}\n\n// The v2 chunk handler deliberately keeps download, verified materialization,\n// render, and upload in one lifecycle so cleanup and errors remain atomic.\n// fallow-ignore-next-line complexity\nasync function handleRenderChunkV2(\n event: Extract<RenderChunkEvent, { PlanProtocol: \"v2\" }>,\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 if (!deps?.skipChromeResolution && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {\n process.env.PRODUCER_HEADLESS_SHELL_PATH = await resolveChromeExecutablePath();\n }\n const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-chunk-v2-\"));\n try {\n const planDir = await downloadAndMaterializePlanV2(\n s3,\n event,\n { role: \"chunk\", chunkIndex: event.ChunkIndex },\n work,\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 const result = await primitive(planDir, event.ChunkIndex, chunkOutputBase);\n const chunkUri = await uploadChunkOutput(\n s3,\n result,\n event.ChunkOutputS3Prefix,\n event.ChunkIndex,\n );\n return {\n Action: \"renderChunk\",\n ChunkS3Uri: chunkUri,\n ChunkIndex: event.ChunkIndex,\n Sha256: result.sha256,\n FramesEncoded: result.framesEncoded,\n CaptureMode: result.captureMode,\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 if (event.PlanProtocol === \"v2\") {\n return handleAssembleV2(event, deps);\n }\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\n// Assembly mirrors the chunk lifecycle while adding assembler-only artifacts;\n// keeping the steps local makes its temporary-storage ownership explicit.\n// fallow-ignore-next-line complexity\nasync function handleAssembleV2(\n event: Extract<AssembleEvent, { PlanProtocol: \"v2\" }>,\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 const work = mkdtempSync(join(deps?.tmpRoot ?? tmpdir(), \"hf-lambda-assemble-v2-\"));\n try {\n const planDir = await downloadAndMaterializePlanV2(s3, event, { role: \"assembler\" }, work);\n // `downloadAndMaterializePlanV2` materializes atomically. Audio is\n // assembler-only and lives at the familiar v1-compatible location.\n const audioPath = existsSync(join(planDir, \"audio.aac\")) ? join(planDir, \"audio.aac\") : null;\n const chunkPaths = await downloadChunkObjects(s3, event.ChunkS3Uris, work, event.Format);\n const finalOutput =\n event.Format === \"png-sequence\"\n ? join(work, \"output-frames\")\n : join(work, `output${formatExtension(event.Format)}`);\n const result = await primitive(planDir, chunkPaths, audioPath, finalOutput, {\n cfr: event.Cfr === true,\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 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 downloadAndMaterializePlanV2(\n s3: S3Client,\n event: {\n PlanV2ManifestS3Uri: string;\n PlanV2ArtifactS3Prefix: string;\n PlanHash: string;\n },\n target: PlanV2MaterializationTarget,\n work: string,\n): Promise<string> {\n const transportDir = join(work, \"plan-v2\");\n mkdirSync(transportDir, { recursive: true });\n await downloadS3ObjectToFile(s3, event.PlanV2ManifestS3Uri, join(transportDir, \"plan.json\"));\n const manifest = readPlanV2Manifest(transportDir);\n if (manifest.planHash !== event.PlanHash) {\n throwPlanHashMismatch(event.PlanHash, manifest.planHash);\n }\n const artifacts = listPlanV2ArtifactsForTarget(manifest, target);\n const uniqueArtifacts = [\n ...new Map(artifacts.map((artifact) => [artifact.sha256, artifact])).values(),\n ];\n await mapConcurrent(uniqueArtifacts, 16, async (artifact) => {\n await downloadPlanV2Artifact(s3, event.PlanV2ArtifactS3Prefix, transportDir, artifact);\n });\n const planDir = join(work, \"plan\");\n materializePlanV2Target(transportDir, target, planDir);\n return planDir;\n}\n\nasync function downloadPlanV2Artifact(\n s3: S3Client,\n artifactPrefix: string,\n planV2Dir: string,\n artifact: Readonly<PlanV2Artifact>,\n): Promise<void> {\n await downloadS3ObjectToFileVerified(\n s3,\n planV2BlobUri(artifactPrefix, artifact.sha256),\n planV2BlobPath(planV2Dir, artifact.sha256),\n artifact.sha256,\n );\n}\n\nfunction planV2BlobPath(planV2Dir: string, digest: string): string {\n return join(planV2Dir, \"artifacts\", \"sha256\", digest.slice(0, 2), digest);\n}\n\nfunction planV2BlobUri(prefix: string, digest: string): string {\n return `${trimTrailingSlash(prefix)}/${digest.slice(0, 2)}/${digest}`;\n}\n\nfunction throwPlanHashMismatch(expected: string, actual: string): never {\n const error = new Error(\n `PLAN_HASH_MISMATCH: event PlanHash=${expected} did not match v2 manifest planHash=${actual}`,\n );\n error.name = \"PLAN_HASH_MISMATCH\";\n throw error;\n}\n\nasync function mapConcurrent<T>(\n values: readonly T[],\n concurrency: number,\n fn: (value: T) => Promise<void>,\n): Promise<void> {\n let cursor = 0;\n async function worker(): Promise<void> {\n while (cursor < values.length) {\n const index = cursor++;\n await fn(values[index]!);\n }\n }\n const results = await Promise.allSettled(\n Array.from({ length: Math.min(concurrency, values.length) }, () => worker()),\n );\n const failure = results.find(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n // Do not reject while sibling workers may still be writing into invocation\n // scratch. The caller removes that directory in `finally`; draining the pool\n // first prevents late S3 streams from racing cleanup after another GET fails.\n if (failure) throw failure.reason;\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. */\n// This is an exhaustive event-union projection used only for safe log summaries.\n// fallow-ignore-next-line complexity\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.PlanProtocol === \"v2\"\n ? [event.PlanV2ManifestS3Uri, event.PlanV2ArtifactS3Prefix, event.ChunkOutputS3Prefix]\n : [event.PlanS3Uri, event.ChunkOutputS3Prefix];\n case \"assemble\":\n return [\n ...(event.PlanProtocol === \"v2\"\n ? [event.PlanV2ManifestS3Uri, event.PlanV2ArtifactS3Prefix]\n : [event.PlanS3Uri]),\n ...event.ChunkS3Uris,\n event.OutputS3Uri,\n event.AudioS3Uri,\n ].filter((u): u is string => u != null);\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 { createHash } from \"node:crypto\";\nimport { dirname } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport {\n GetObjectCommand,\n HeadObjectCommand,\n PutObjectCommand,\n type S3Client,\n} 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/** Download and verify an immutable plan-v2 artifact before materialization. */\nexport async function downloadS3ObjectToFileVerified(\n client: S3Client,\n uri: string,\n destPath: string,\n expectedSha256: string,\n): Promise<void> {\n assertSha256(expectedSha256);\n await downloadS3ObjectToFile(client, uri, destPath);\n const actual = await sha256File(destPath);\n if (actual !== expectedSha256) {\n rmSync(destPath, { force: true });\n const error = new Error(\n `[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: ${uri} expected ${expectedSha256}, got ${actual}`,\n );\n error.name = \"PLAN_ARTIFACT_DIGEST_MISMATCH\";\n throw error;\n }\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 * Upload one content-addressed plan-v2 artifact exactly once.\n *\n * Existing objects are reused only when their immutable digest metadata and\n * byte length agree. A conflicting object is never overwritten: doing so\n * could change a plan already being consumed by another chunk invocation.\n */\nexport async function uploadContentAddressedFileToS3(\n client: S3Client,\n localPath: string,\n uri: string,\n expectedSha256: string,\n contentType?: string,\n): Promise<\"uploaded\" | \"reused\"> {\n assertSha256(expectedSha256);\n if (!existsSync(localPath)) {\n throw new Error(`[s3Transport] upload source missing: ${localPath}`);\n }\n const actualSha256 = await sha256File(localPath);\n if (actualSha256 !== expectedSha256) {\n const error = new Error(\n `[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: local artifact ${localPath} expected ${expectedSha256}, got ${actualSha256}`,\n );\n error.name = \"PLAN_ARTIFACT_DIGEST_MISMATCH\";\n throw error;\n }\n\n const { bucket, key } = parseS3Uri(uri);\n const size = statSync(localPath).size;\n const existing = await inspectContentAddressedObject(client, bucket, key, size, expectedSha256);\n if (existing === \"matching\") return \"reused\";\n if (existing === \"conflict\") throwImmutableObjectConflict(uri);\n\n const body = createReadStream(localPath);\n try {\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n ContentLength: size,\n Metadata: { sha256: expectedSha256 },\n ChecksumSHA256: Buffer.from(expectedSha256, \"hex\").toString(\"base64\"),\n // HEAD followed by an unconditional PUT can overwrite a conflicting\n // object published by a concurrent planner. Conditional create makes\n // immutable CAS and fixed-key manifest publication race-safe.\n IfNoneMatch: \"*\",\n }),\n );\n return \"uploaded\";\n } catch (error) {\n if (!isS3PreconditionFailed(error)) throw error;\n const raced = await inspectContentAddressedObject(client, bucket, key, size, expectedSha256);\n if (raced === \"matching\") return \"reused\";\n if (raced === \"conflict\") throwImmutableObjectConflict(uri);\n // The winning object was deleted between the conditional failure and\n // verification. Preserve the service error so the orchestrator may retry.\n throw error;\n } finally {\n // A failed conditional request may reject before consuming the stream.\n // Explicit teardown avoids retaining the source descriptor on a warm\n // Lambda planner.\n body.destroy();\n }\n}\n\nexport async function sha256File(path: string): Promise<string> {\n const hash = createHash(\"sha256\");\n for await (const chunk of createReadStream(path)) {\n hash.update(chunk as Buffer);\n }\n return hash.digest(\"hex\");\n}\n\nfunction assertSha256(value: string): void {\n if (!/^[a-f0-9]{64}$/.test(value)) {\n throw new Error(\n `[s3Transport] expected lowercase SHA-256 digest, got ${JSON.stringify(value)}`,\n );\n }\n}\n\ntype ContentAddressedObjectState = \"missing\" | \"matching\" | \"conflict\";\n\nasync function inspectContentAddressedObject(\n client: S3Client,\n bucket: string,\n key: string,\n expectedSize: number,\n expectedSha256: string,\n): Promise<ContentAddressedObjectState> {\n try {\n const existing = await client.send(\n new HeadObjectCommand({ Bucket: bucket, Key: key, ChecksumMode: \"ENABLED\" }),\n );\n return existing.ContentLength === expectedSize && existing.Metadata?.sha256 === expectedSha256\n ? \"matching\"\n : \"conflict\";\n } catch (error) {\n if (isS3NotFound(error)) return \"missing\";\n throw error;\n }\n}\n\nfunction throwImmutableObjectConflict(uri: string): never {\n const error = new Error(\n `[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: immutable object ${uri} already exists with different digest metadata or size`,\n );\n error.name = \"PLAN_ARTIFACT_DIGEST_MISMATCH\";\n throw error;\n}\n\nfunction isS3NotFound(error: unknown): boolean {\n if (!isRecord(error)) return false;\n const metadata = isRecord(error.$metadata) ? error.$metadata : undefined;\n return (\n error.name === \"NotFound\" || error.name === \"NoSuchKey\" || metadata?.httpStatusCode === 404\n );\n}\n\nfunction isS3PreconditionFailed(error: unknown): boolean {\n if (!isRecord(error)) return false;\n const metadata = isRecord(error.$metadata) ? error.$metadata : undefined;\n return error.name === \"PreconditionFailed\" || metadata?.httpStatusCode === 412;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\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", "import { createHash } from \"node:crypto\";\nimport { mkdirSync, mkdtempSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { S3Client } from \"@aws-sdk/client-s3\";\nimport {\n PlanV2IntegrityError,\n type PlanV2ArtifactPublisher,\n type PlanV2PublishBlob,\n} from \"@hyperframes/producer/distributed\";\nimport { parseS3Uri, uploadContentAddressedFileToS3 } from \"./s3Transport.js\";\n\nexport interface S3PlanV2ArtifactPublisherOptions {\n readonly s3: S3Client;\n /** Validated render output prefix from which all v2 object keys are derived. */\n readonly planOutputS3Prefix: string;\n /** Planner-local scratch parent for the small manifest upload file. */\n readonly temporaryRoot?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction assertSha256(value: unknown, label: string): string {\n if (typeof value !== \"string\" || !/^[a-f0-9]{64}$/.test(value)) {\n throw new PlanV2IntegrityError(`${label} must be a lowercase SHA-256 digest`);\n }\n return value;\n}\n\nfunction manifestDigests(manifestBytes: string): ReadonlySet<string> {\n let value: unknown;\n try {\n value = JSON.parse(manifestBytes);\n } catch {\n throw new PlanV2IntegrityError(\"S3 publisher received invalid manifest JSON\");\n }\n if (!isRecord(value) || !Array.isArray(value.artifacts)) {\n throw new PlanV2IntegrityError(\"S3 publisher manifest requires an artifacts array\");\n }\n return new Set(\n value.artifacts.map((artifact, index) => {\n if (!isRecord(artifact)) {\n throw new PlanV2IntegrityError(`S3 publisher artifacts[${index}] must be an object`);\n }\n return assertSha256(artifact.sha256, `S3 publisher artifacts[${index}].sha256`);\n }),\n );\n}\n\nfunction trimTrailingSlash(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\n/**\n * Manifest-last S3 implementation of the producer's plan-v2 publication seam.\n *\n * Blobs stream from the planner's private frozen directory directly to S3.\n * Successfully uploaded or safely reused digests are tracked so a manifest\n * cannot become visible before all of its references are durable.\n */\nexport class S3PlanV2ArtifactPublisher implements PlanV2ArtifactPublisher {\n readonly artifactPrefix: string;\n readonly manifestUri: string;\n readonly #s3: S3Client;\n readonly #temporaryRoot: string;\n readonly #publishedDigests = new Set<string>();\n #state: \"open\" | \"committed\" | \"aborted\" = \"open\";\n\n constructor(options: Readonly<S3PlanV2ArtifactPublisherOptions>) {\n const outputPrefix = `${trimTrailingSlash(options.planOutputS3Prefix)}/v2`;\n parseS3Uri(outputPrefix);\n this.#s3 = options.s3;\n this.artifactPrefix = `${outputPrefix}/artifacts/sha256`;\n this.manifestUri = `${outputPrefix}/manifest.json`;\n this.#temporaryRoot = options.temporaryRoot ?? tmpdir();\n mkdirSync(this.#temporaryRoot, { recursive: true });\n }\n\n async putBlob(blob: Readonly<PlanV2PublishBlob>): Promise<void> {\n this.#assertOpen(\"publish a blob\");\n const digest = assertSha256(blob.sha256, \"S3 published blob sha256\");\n const sourceSize = statSync(blob.sourcePath).size;\n if (sourceSize !== blob.sizeBytes) {\n throw new PlanV2IntegrityError(\n `S3 published blob size changed for ${digest}: expected ${blob.sizeBytes}, got ${sourceSize}`,\n );\n }\n const uri = `${this.artifactPrefix}/${digest.slice(0, 2)}/${digest}`;\n await uploadContentAddressedFileToS3(this.#s3, blob.sourcePath, uri, digest);\n this.#publishedDigests.add(digest);\n }\n\n async commitManifest(manifestBytes: string): Promise<void> {\n this.#assertOpen(\"commit a manifest\");\n for (const digest of manifestDigests(manifestBytes)) {\n if (!this.#publishedDigests.has(digest)) {\n throw new PlanV2IntegrityError(\n `cannot commit S3 manifest before referenced blob is durable: ${digest}`,\n );\n }\n }\n\n const manifestDigest = createHash(\"sha256\").update(manifestBytes, \"utf8\").digest(\"hex\");\n const stagingDir = mkdtempSync(join(this.#temporaryRoot, \"hf-plan-v2-manifest-\"));\n const manifestPath = join(stagingDir, \"manifest.json\");\n try {\n writeFileSync(manifestPath, manifestBytes, \"utf8\");\n await uploadContentAddressedFileToS3(\n this.#s3,\n manifestPath,\n this.manifestUri,\n manifestDigest,\n \"application/json\",\n );\n this.#state = \"committed\";\n } finally {\n rmSync(stagingDir, { recursive: true, force: true });\n }\n }\n\n async abort(): Promise<void> {\n if (this.#state === \"open\") this.#state = \"aborted\";\n // Remote CAS blobs are immutable and may already be reused by a retry.\n // Without a committed manifest they are unreachable and expire under the\n // render bucket's intermediate-object lifecycle policy.\n }\n\n #assertOpen(operation: string): void {\n if (this.#state !== \"open\") {\n throw new PlanV2IntegrityError(`cannot ${operation} after publisher is ${this.#state}`);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["existsSync", "mkdirSync", "mkdtempSync", "rmSync", "statSync", "tmpdir", "join", "existsSync", "existsSync", "createHash", "mkdirSync", "rmSync", "statSync", "isRecord", "assertSha256", "mkdirSync", "statSync", "createHash", "rmSync", "join", "existsSync", "mkdtempSync", "tmpdir", "trimTrailingSlash", "statSync", "uri", "mkdirSync", "rmSync"]
7
7
  }