@hyperframes/aws-lambda 0.8.180 → 0.8.183
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{chunk-PK7PO3MH.js → chunk-5FLGGUWE.js} +1 -1
- package/dist/_chunks/{chunk-AK7J3QQN.js → chunk-MPNOI2CS.js} +87 -54
- package/dist/_chunks/{chunk-ZY5YIH5W.js → chunk-SGNVVPIS.js} +187 -17
- package/dist/cdk/index.js +38 -7
- package/dist/events.d.ts +16 -3
- package/dist/handler.d.ts +4 -3
- package/dist/handler.js +2 -2
- package/dist/index.js +3 -3
- package/dist/s3Stitch.d.ts +5 -2
- package/dist/s3Transport.d.ts +20 -8
- package/dist/sdk/index.js +2 -2
- package/package.json +3 -2
- package/prebuilt/handler-zip.json +4 -4
- package/prebuilt/template.yaml +149 -71
- package/scripts/pin-handler-zip.ts +2 -13
- package/scripts/prebuilt-template.mjs +19 -0
- package/scripts/build-zip.test.ts +0 -77
- package/scripts/pin-handler-zip.test.ts +0 -101
- package/scripts/probe-beginframe.test.ts +0 -80
package/dist/events.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import type { DistributedFormat, SerializableDistributedRenderConfig } from "@hyperframes/producer/distributed";
|
|
19
19
|
export type { SerializableDistributedRenderConfig } from "@hyperframes/producer/distributed";
|
|
20
20
|
/** Discriminator for the three roles the one Lambda image fulfills. */
|
|
21
|
-
export type LambdaAction = "plan" | "renderChunk" | "assemble";
|
|
21
|
+
export type LambdaAction = "plan" | "audio" | "renderChunk" | "assemble";
|
|
22
22
|
/** Transport protocol selected for one complete distributed render. */
|
|
23
23
|
export type LambdaPlanProtocol = "v1" | "v2";
|
|
24
24
|
/**
|
|
@@ -29,7 +29,7 @@ export type LambdaPlanProtocol = "v1" | "v2";
|
|
|
29
29
|
* handler unwraps both `event.Payload` and `event.Input` before
|
|
30
30
|
* dispatching.
|
|
31
31
|
*/
|
|
32
|
-
export type LambdaEvent = PlanEvent | RenderChunkEvent | AssembleEvent | {
|
|
32
|
+
export type LambdaEvent = PlanEvent | AudioEvent | RenderChunkEvent | AssembleEvent | {
|
|
33
33
|
Payload: LambdaEvent;
|
|
34
34
|
} | {
|
|
35
35
|
Input: LambdaEvent;
|
|
@@ -57,6 +57,10 @@ export interface PlanV2Event extends PlanEventBase {
|
|
|
57
57
|
PlanProtocol?: "v2";
|
|
58
58
|
}
|
|
59
59
|
export type PlanEvent = PlanV1Event | PlanV2Event;
|
|
60
|
+
/** Makes a v2 plan's audio beside its chunks, from the same project and config as the plan. */
|
|
61
|
+
export interface AudioEvent extends Omit<PlanEventBase, "Action"> {
|
|
62
|
+
Action: "audio";
|
|
63
|
+
}
|
|
60
64
|
/** Activity B: fetch planDir, render one chunk, upload result. */
|
|
61
65
|
interface RenderChunkEventBase {
|
|
62
66
|
Action: "renderChunk";
|
|
@@ -165,8 +169,17 @@ export interface PlanV2LambdaResult extends PlanLambdaResultBase {
|
|
|
165
169
|
PlanProtocol: "v2";
|
|
166
170
|
PlanV2ManifestS3Uri: string;
|
|
167
171
|
PlanV2ArtifactS3Prefix: string;
|
|
172
|
+
/** The plan left its audio to the audio task, which has a worker kept free for it. */
|
|
173
|
+
SeparateAudio: boolean;
|
|
168
174
|
}
|
|
169
175
|
export type PlanLambdaResult = PlanV1LambdaResult | PlanV2LambdaResult;
|
|
176
|
+
export interface AudioLambdaResult {
|
|
177
|
+
Action: "audio";
|
|
178
|
+
HasAudio: boolean;
|
|
179
|
+
/** The track for the assembler to stitch in; `null` without audio or when the plan mixed it. */
|
|
180
|
+
AudioS3Uri: string | null;
|
|
181
|
+
DurationMs: number;
|
|
182
|
+
}
|
|
170
183
|
/** Result of a `renderChunk` invocation. Sized ≤200 bytes per §2.4. */
|
|
171
184
|
export interface RenderChunkLambdaResult {
|
|
172
185
|
Action: "renderChunk";
|
|
@@ -186,4 +199,4 @@ export interface AssembleLambdaResult {
|
|
|
186
199
|
FileSize: number;
|
|
187
200
|
DurationMs: number;
|
|
188
201
|
}
|
|
189
|
-
export type LambdaResult = PlanLambdaResult | RenderChunkLambdaResult | AssembleLambdaResult;
|
|
202
|
+
export type LambdaResult = PlanLambdaResult | AudioLambdaResult | RenderChunkLambdaResult | AssembleLambdaResult;
|
package/dist/handler.d.ts
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { LambdaClient } from "@aws-sdk/client-lambda";
|
|
14
14
|
import { S3Client } from "@aws-sdk/client-s3";
|
|
15
|
-
import { assemble, type ChunkRenderer, plan, planV2WithPublisher } from "@hyperframes/producer/distributed";
|
|
16
|
-
import type { AssembleEvent, LambdaEvent, LambdaResult, PlanEvent, RenderChunkEvent } from "./events.js";
|
|
15
|
+
import { assemble, buildPlanAudio, type ChunkRenderer, plan, planV2WithPublisher } from "@hyperframes/producer/distributed";
|
|
16
|
+
import type { AssembleEvent, AudioEvent, LambdaEvent, LambdaResult, PlanEvent, RenderChunkEvent } from "./events.js";
|
|
17
17
|
/**
|
|
18
18
|
* Optional injection points used by the handler's unit tests. Production
|
|
19
19
|
* callers leave these unset; the real OSS primitives are used. Tests
|
|
@@ -28,6 +28,7 @@ export interface HandlerDeps {
|
|
|
28
28
|
primitives?: {
|
|
29
29
|
plan: typeof plan;
|
|
30
30
|
planV2WithPublisher?: typeof planV2WithPublisher;
|
|
31
|
+
buildPlanAudio?: typeof buildPlanAudio;
|
|
31
32
|
renderChunk: ChunkRenderer;
|
|
32
33
|
assemble: typeof assemble;
|
|
33
34
|
};
|
|
@@ -47,4 +48,4 @@ export interface HandlerDeps {
|
|
|
47
48
|
* shape; unwrap until we hit a discriminated event.
|
|
48
49
|
*/
|
|
49
50
|
export declare function handler(event: LambdaEvent, deps?: HandlerDeps): Promise<LambdaResult>;
|
|
50
|
-
export declare function unwrapEvent(event: LambdaEvent): PlanEvent | RenderChunkEvent | AssembleEvent;
|
|
51
|
+
export declare function unwrapEvent(event: LambdaEvent): PlanEvent | AudioEvent | RenderChunkEvent | AssembleEvent;
|
package/dist/handler.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
resolveChromeExecutablePath,
|
|
7
7
|
resolveChromeSource,
|
|
8
8
|
unwrapEvent
|
|
9
|
-
} from "./_chunks/chunk-
|
|
9
|
+
} from "./_chunks/chunk-MPNOI2CS.js";
|
|
10
10
|
import {
|
|
11
11
|
InvalidConfigError,
|
|
12
12
|
computeRenderCost,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getRenderProgress,
|
|
15
15
|
renderToLambda,
|
|
16
16
|
validateDistributedRenderConfig
|
|
17
|
-
} from "./_chunks/chunk-
|
|
17
|
+
} from "./_chunks/chunk-5FLGGUWE.js";
|
|
18
18
|
import {
|
|
19
19
|
downloadS3ObjectToFile,
|
|
20
20
|
downloadS3ObjectToFileVerified,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
untarDirectory,
|
|
25
25
|
uploadContentAddressedFileToS3,
|
|
26
26
|
uploadFileToS3
|
|
27
|
-
} from "./_chunks/chunk-
|
|
27
|
+
} from "./_chunks/chunk-SGNVVPIS.js";
|
|
28
28
|
import "./_chunks/chunk-YS3KY3C5.js";
|
|
29
29
|
export {
|
|
30
30
|
ChromeBinaryUnavailableError,
|
package/dist/s3Stitch.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ export interface StitchResult {
|
|
|
21
21
|
parts: number;
|
|
22
22
|
downloadedBytes: number;
|
|
23
23
|
}
|
|
24
|
-
/** One faststart mp4 at `destUri` from the stitched mp4 chunks at `chunkUris`,
|
|
25
|
-
export declare function stitchMp4InS3(s3: S3Client, chunkUris: readonly string[], destUri: string, minPartBytes?:
|
|
24
|
+
/** One faststart mp4 at `destUri` from the stitched mp4 chunks at `chunkUris`, plus `audioUri`'s track when given. */
|
|
25
|
+
export declare function stitchMp4InS3(s3: S3Client, chunkUris: readonly string[], destUri: string, { audioUri, minPartBytes }?: {
|
|
26
|
+
audioUri?: string;
|
|
27
|
+
minPartBytes?: number;
|
|
28
|
+
}): Promise<StitchResult>;
|
|
26
29
|
export {};
|
package/dist/s3Transport.d.ts
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
* `/usr/bin`, so a system-binary tar would ENOENT in the actual
|
|
17
17
|
* deployment.
|
|
18
18
|
*/
|
|
19
|
-
import { type S3Client } from "@aws-sdk/client-s3";
|
|
19
|
+
import { type CompletedPart, type CreateMultipartUploadCommandInput, type S3Client } from "@aws-sdk/client-s3";
|
|
20
|
+
/** S3's largest PutObject or CopyObject; bigger objects go multipart. */
|
|
21
|
+
export declare const MAX_SINGLE_PUT_BYTES: number;
|
|
20
22
|
/** Parsed `s3://bucket/key` URI. */
|
|
21
23
|
export interface S3Location {
|
|
22
24
|
bucket: string;
|
|
@@ -35,13 +37,7 @@ export declare function downloadS3ObjectToFileVerified(client: S3Client, uri: st
|
|
|
35
37
|
* SHA-256 and size are the ones the manifest pins.
|
|
36
38
|
*/
|
|
37
39
|
export declare function presignVerifiedS3GetUrl(client: S3Client, uri: string, expectedSha256: string, expectedSize: number, expiresInSeconds: number): Promise<string>;
|
|
38
|
-
/**
|
|
39
|
-
* Upload a local file's contents to an S3 URI using a streaming
|
|
40
|
-
* `PutObjectCommand`. PutObject's 5 GB cap comfortably exceeds the
|
|
41
|
-
* distributed pipeline's 2 GB planDir limit and the typical
|
|
42
|
-
* chunk size (≤ 200 MB), so a single PUT works for every artifact this
|
|
43
|
-
* adapter handles.
|
|
44
|
-
*/
|
|
40
|
+
/** Upload a local file to an S3 URI, streamed: one PutObject up to S3's 5 GB cap, multipart above. */
|
|
45
41
|
export declare function uploadFileToS3(client: S3Client, localPath: string, uri: string, contentType?: string): Promise<void>;
|
|
46
42
|
/**
|
|
47
43
|
* Upload one content-addressed plan-v2 artifact exactly once; S3 verifies the bytes against
|
|
@@ -49,6 +45,22 @@ export declare function uploadFileToS3(client: S3Client, localPath: string, uri:
|
|
|
49
45
|
* and a conflicting one is never overwritten, since a chunk may already be reading that plan.
|
|
50
46
|
*/
|
|
51
47
|
export declare function uploadContentAddressedFileToS3(client: S3Client, localPath: string, uri: string, expectedSha256: string, contentType?: string): Promise<"uploaded" | "reused">;
|
|
48
|
+
export type MultipartTarget = {
|
|
49
|
+
Bucket: string;
|
|
50
|
+
Key: string;
|
|
51
|
+
UploadId: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Creates a multipart upload, lets `sendParts` fill it, then completes it. A failure aborts the
|
|
55
|
+
* upload so no parts linger; `sendParts` must settle every part before it rejects.
|
|
56
|
+
*/
|
|
57
|
+
export declare function multipartUpload(client: S3Client, create: CreateMultipartUploadCommandInput & {
|
|
58
|
+
Bucket: string;
|
|
59
|
+
Key: string;
|
|
60
|
+
}, sendParts: (target: MultipartTarget) => Promise<CompletedPart[]>, complete?: {
|
|
61
|
+
ifNoneMatch?: "*";
|
|
62
|
+
objectSize?: number;
|
|
63
|
+
}): Promise<void>;
|
|
52
64
|
export declare function mapConcurrent<T>(values: readonly T[], concurrency: number, fn: (value: T) => Promise<void>): Promise<void>;
|
|
53
65
|
export declare function sha256File(path: string): Promise<string>;
|
|
54
66
|
/** Put a file into the shared content store once; reusing an aging object restarts its expiry. */
|
package/dist/sdk/index.js
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
validateDistributedRenderConfig,
|
|
11
11
|
validateStepFunctionsInputSize,
|
|
12
12
|
validateVariablesPayload
|
|
13
|
-
} from "../_chunks/chunk-
|
|
14
|
-
import "../_chunks/chunk-
|
|
13
|
+
} from "../_chunks/chunk-5FLGGUWE.js";
|
|
14
|
+
import "../_chunks/chunk-SGNVVPIS.js";
|
|
15
15
|
import "../_chunks/chunk-YS3KY3C5.js";
|
|
16
16
|
|
|
17
17
|
// src/sdk/prebuiltHandler.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/aws-lambda",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.183",
|
|
4
4
|
"description": "AWS Lambda adapter for HyperFrames distributed rendering — handler, client-side SDK, and CDK construct.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"dist/",
|
|
12
12
|
"prebuilt/",
|
|
13
13
|
"scripts/",
|
|
14
|
+
"!scripts/*.test.ts",
|
|
14
15
|
"README.md"
|
|
15
16
|
],
|
|
16
17
|
"type": "module",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"ffprobe-static": "^3.1.0",
|
|
49
50
|
"puppeteer-core": "^25.8.0",
|
|
50
51
|
"tar": "^7.4.3",
|
|
51
|
-
"@hyperframes/producer": "^0.8.
|
|
52
|
+
"@hyperframes/producer": "^0.8.183"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@types/aws-lambda": "^8.10.146",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.8.
|
|
3
|
-
"url": "https://github.com/heygen-com/hyperframes/releases/download/v0.8.
|
|
4
|
-
"sha256": "
|
|
5
|
-
"bytes":
|
|
2
|
+
"version": "0.8.183",
|
|
3
|
+
"url": "https://github.com/heygen-com/hyperframes/releases/download/v0.8.183/hyperframes-lambda-handler.zip",
|
|
4
|
+
"sha256": "1194f61df575fddc48692ca4807ed5d3765e357518a87e20b2086371a25d203f",
|
|
5
|
+
"bytes": 129235569
|
|
6
6
|
}
|
package/prebuilt/template.yaml
CHANGED
|
@@ -324,6 +324,7 @@ Resources:
|
|
|
324
324
|
MaxChunkConcurrency.$: "$.Payload.MaxChunkConcurrency"
|
|
325
325
|
Format.$: "$.Payload.Format"
|
|
326
326
|
HasAudio.$: "$.Payload.HasAudio"
|
|
327
|
+
SeparateAudio.$: "$.Payload.SeparateAudio"
|
|
327
328
|
ResultPath: $.Plan
|
|
328
329
|
Retry:
|
|
329
330
|
- ErrorEquals:
|
|
@@ -395,7 +396,7 @@ Resources:
|
|
|
395
396
|
Choices:
|
|
396
397
|
- Variable: $.Plan.PlanProtocol
|
|
397
398
|
StringEquals: v2
|
|
398
|
-
Next:
|
|
399
|
+
Next: RenderV2
|
|
399
400
|
Default: RenderChunks
|
|
400
401
|
|
|
401
402
|
RenderChunks:
|
|
@@ -511,74 +512,151 @@ Resources:
|
|
|
511
512
|
MaxDelaySeconds: 60
|
|
512
513
|
End: true
|
|
513
514
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
515
|
+
RenderV2:
|
|
516
|
+
# Chunks render video only while the audio task mixes the track AssembleV2 stitches in.
|
|
517
|
+
Type: Parallel
|
|
518
|
+
Branches:
|
|
519
|
+
- StartAt: RenderChunksV2
|
|
520
|
+
States:
|
|
521
|
+
RenderChunksV2:
|
|
522
|
+
Type: Map
|
|
523
|
+
Label: RenderChunksV2
|
|
524
|
+
ItemsPath: $.Iterator.ChunkIndexes
|
|
525
|
+
ItemSelector:
|
|
526
|
+
ChunkIndex.$: "$$.Map.Item.Value"
|
|
527
|
+
PlanV2ManifestS3Uri.$: "$.Plan.PlanV2ManifestS3Uri"
|
|
528
|
+
PlanV2ArtifactS3Prefix.$: "$.Plan.PlanV2ArtifactS3Prefix"
|
|
529
|
+
PlanHash.$: "$.Plan.PlanHash"
|
|
530
|
+
ChunkOutputS3Prefix.$: "$.PlanOutputS3Prefix"
|
|
531
|
+
Format.$: "$.Plan.Format"
|
|
532
|
+
# Chunks past the plan's concurrency queue here instead of being throttled by Lambda.
|
|
533
|
+
MaxConcurrencyPath: $.Plan.MaxChunkConcurrency
|
|
534
|
+
ResultPath: $.Chunks
|
|
535
|
+
ItemProcessor:
|
|
536
|
+
ProcessorConfig:
|
|
537
|
+
Mode: DISTRIBUTED
|
|
538
|
+
ExecutionType: STANDARD
|
|
539
|
+
StartAt: RenderChunkV2
|
|
540
|
+
States:
|
|
541
|
+
RenderChunkV2:
|
|
542
|
+
Type: Task
|
|
543
|
+
Resource: arn:aws:states:::lambda:invoke
|
|
544
|
+
Parameters:
|
|
545
|
+
FunctionName: !GetAtt RenderFunction.Arn
|
|
546
|
+
Payload:
|
|
547
|
+
Action: renderChunk
|
|
548
|
+
PlanProtocol: v2
|
|
549
|
+
ChunkIndex.$: "$.ChunkIndex"
|
|
550
|
+
PlanV2ManifestS3Uri.$: "$.PlanV2ManifestS3Uri"
|
|
551
|
+
PlanV2ArtifactS3Prefix.$: "$.PlanV2ArtifactS3Prefix"
|
|
552
|
+
PlanHash.$: "$.PlanHash"
|
|
553
|
+
ChunkOutputS3Prefix.$: "$.ChunkOutputS3Prefix"
|
|
554
|
+
Format.$: "$.Format"
|
|
555
|
+
ResultSelector:
|
|
556
|
+
ChunkS3Uri.$: "$.Payload.ChunkS3Uri"
|
|
557
|
+
ChunkIndex.$: "$.Payload.ChunkIndex"
|
|
558
|
+
Sha256.$: "$.Payload.Sha256"
|
|
559
|
+
FramesEncoded.$: "$.Payload.FramesEncoded"
|
|
560
|
+
DurationMs.$: "$.Payload.DurationMs"
|
|
561
|
+
Retry:
|
|
562
|
+
- ErrorEquals:
|
|
563
|
+
- FFMPEG_VERSION_MISMATCH
|
|
564
|
+
- PLAN_HASH_MISMATCH
|
|
565
|
+
- S3_URI_NOT_ALLOWED
|
|
566
|
+
- BROWSER_GPU_NOT_SOFTWARE
|
|
567
|
+
- PLAN_TOO_LARGE
|
|
568
|
+
- PlanTooLargeError
|
|
569
|
+
- PLAN_PROTOCOL_UNSUPPORTED
|
|
570
|
+
- PlanProtocolUnsupportedError
|
|
571
|
+
- PLAN_V2_INTEGRITY_UNRECOVERABLE
|
|
572
|
+
- INVALID_VIDEO_METADATA
|
|
573
|
+
- PlanV2IntegrityError
|
|
574
|
+
- PLAN_ARTIFACT_DIGEST_MISMATCH
|
|
575
|
+
- ChromeBinaryUnavailableError
|
|
576
|
+
MaxAttempts: 0
|
|
577
|
+
- ErrorEquals: [Lambda.TooManyRequestsException]
|
|
578
|
+
IntervalSeconds: 5
|
|
579
|
+
MaxAttempts: 10
|
|
580
|
+
BackoffRate: 2
|
|
581
|
+
MaxDelaySeconds: 60
|
|
582
|
+
JitterStrategy: FULL
|
|
583
|
+
- ErrorEquals: [States.ALL]
|
|
584
|
+
IntervalSeconds: 2
|
|
585
|
+
MaxAttempts: 4
|
|
586
|
+
BackoffRate: 2
|
|
587
|
+
MaxDelaySeconds: 60
|
|
588
|
+
End: true
|
|
589
|
+
End: true
|
|
590
|
+
- StartAt: AudioNeeded
|
|
591
|
+
States:
|
|
592
|
+
AudioNeeded:
|
|
593
|
+
Type: Choice
|
|
594
|
+
Choices:
|
|
595
|
+
- Variable: $.Plan.SeparateAudio
|
|
596
|
+
BooleanEquals: true
|
|
597
|
+
Next: Audio
|
|
598
|
+
Default: NoAudio
|
|
599
|
+
NoAudio:
|
|
600
|
+
Type: Pass
|
|
601
|
+
Result:
|
|
602
|
+
HasAudio: false
|
|
603
|
+
AudioS3Uri: null
|
|
604
|
+
ResultPath: $.Audio
|
|
605
|
+
End: true
|
|
606
|
+
Audio:
|
|
607
|
+
# The plan left its audio out: mix it here while the chunks render.
|
|
608
|
+
Type: Task
|
|
609
|
+
Resource: arn:aws:states:::lambda:invoke
|
|
610
|
+
Parameters:
|
|
611
|
+
FunctionName: !GetAtt RenderFunction.Arn
|
|
612
|
+
Payload:
|
|
613
|
+
Action: audio
|
|
614
|
+
ProjectS3Uri.$: "$.ProjectS3Uri"
|
|
615
|
+
PlanOutputS3Prefix.$: "$.PlanOutputS3Prefix"
|
|
616
|
+
Config.$: "$.Config"
|
|
617
|
+
ResultSelector:
|
|
618
|
+
HasAudio.$: "$.Payload.HasAudio"
|
|
619
|
+
AudioS3Uri.$: "$.Payload.AudioS3Uri"
|
|
620
|
+
ResultPath: $.Audio
|
|
621
|
+
Retry:
|
|
622
|
+
- ErrorEquals:
|
|
623
|
+
- FFMPEG_VERSION_MISMATCH
|
|
624
|
+
- PLAN_HASH_MISMATCH
|
|
625
|
+
- S3_URI_NOT_ALLOWED
|
|
626
|
+
- BROWSER_GPU_NOT_SOFTWARE
|
|
627
|
+
- FONT_FETCH_FAILED
|
|
628
|
+
- PLAN_TOO_LARGE
|
|
629
|
+
- PlanTooLargeError
|
|
630
|
+
- PLAN_PROTOCOL_UNSUPPORTED
|
|
631
|
+
- PlanProtocolUnsupportedError
|
|
632
|
+
- PLAN_V2_INTEGRITY_UNRECOVERABLE
|
|
633
|
+
- VIDEO_SOURCE_UNRENDERABLE
|
|
634
|
+
- INVALID_VIDEO_METADATA
|
|
635
|
+
- NOT_MEDIA_PAYLOAD
|
|
636
|
+
- NotMediaPayloadError
|
|
637
|
+
- PlanV2IntegrityError
|
|
638
|
+
- PLAN_ARTIFACT_DIGEST_MISMATCH
|
|
639
|
+
- FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED
|
|
640
|
+
- CHUNK_EXCEEDS_WORKER_TIMEOUT
|
|
641
|
+
- ChromeBinaryUnavailableError
|
|
642
|
+
- SITE_MANIFEST_INVALID
|
|
643
|
+
MaxAttempts: 0
|
|
644
|
+
- ErrorEquals: [Lambda.TooManyRequestsException]
|
|
645
|
+
IntervalSeconds: 5
|
|
646
|
+
MaxAttempts: 10
|
|
647
|
+
BackoffRate: 2
|
|
648
|
+
MaxDelaySeconds: 60
|
|
649
|
+
JitterStrategy: FULL
|
|
650
|
+
- ErrorEquals: [States.ALL]
|
|
651
|
+
IntervalSeconds: 2
|
|
652
|
+
MaxAttempts: 4
|
|
653
|
+
BackoffRate: 2
|
|
654
|
+
MaxDelaySeconds: 60
|
|
655
|
+
End: true
|
|
656
|
+
ResultSelector:
|
|
657
|
+
Chunks.$: "$[0].Chunks"
|
|
658
|
+
Audio.$: "$[1].Audio"
|
|
659
|
+
ResultPath: $.Render
|
|
582
660
|
Next: AssembleV2
|
|
583
661
|
|
|
584
662
|
AssembleV2:
|
|
@@ -592,8 +670,8 @@ Resources:
|
|
|
592
670
|
PlanV2ManifestS3Uri.$: "$.Plan.PlanV2ManifestS3Uri"
|
|
593
671
|
PlanV2ArtifactS3Prefix.$: "$.Plan.PlanV2ArtifactS3Prefix"
|
|
594
672
|
PlanHash.$: "$.Plan.PlanHash"
|
|
595
|
-
ChunkS3Uris.$: "$.Chunks[*].ChunkS3Uri"
|
|
596
|
-
AudioS3Uri
|
|
673
|
+
ChunkS3Uris.$: "$.Render.Chunks[*].ChunkS3Uri"
|
|
674
|
+
AudioS3Uri.$: "$.Render.Audio.AudioS3Uri"
|
|
597
675
|
OutputS3Uri.$: "$.OutputS3Uri"
|
|
598
676
|
Format.$: "$.Plan.Format"
|
|
599
677
|
ResultSelector:
|
|
@@ -11,8 +11,7 @@ import {
|
|
|
11
11
|
type HandlerZipManifest,
|
|
12
12
|
PREBUILT_DIR,
|
|
13
13
|
} from "../src/sdk/prebuiltHandler.js";
|
|
14
|
-
|
|
15
|
-
const CHECKOUT_CODE_URI = "CodeUri: ../../packages/aws-lambda/dist/handler.zip";
|
|
14
|
+
import { writePrebuiltTemplate } from "./prebuilt-template.mjs";
|
|
16
15
|
|
|
17
16
|
export function pinHandlerZip(repoRoot: string): HandlerZipManifest {
|
|
18
17
|
const pkgDir = join(repoRoot, "packages", "aws-lambda");
|
|
@@ -31,18 +30,8 @@ export function pinHandlerZip(repoRoot: string): HandlerZipManifest {
|
|
|
31
30
|
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
32
31
|
writeFileSync(asset.replace(/\.zip$/, ".sha256"), `${sha256} ${HANDLER_ZIP_ASSET}\n`);
|
|
33
32
|
|
|
34
|
-
const template = readFileSync(join(repoRoot, "examples", "aws-lambda", "template.yaml"), "utf-8");
|
|
35
|
-
if (template.split(CHECKOUT_CODE_URI).length !== 2) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
`[pin-handler-zip] expected exactly one "${CHECKOUT_CODE_URI}" in the template`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
33
|
const outDir = join(pkgDir, PREBUILT_DIR);
|
|
41
|
-
|
|
42
|
-
writeFileSync(
|
|
43
|
-
join(outDir, "template.yaml"),
|
|
44
|
-
template.replace(CHECKOUT_CODE_URI, "CodeUri: handler.zip"),
|
|
45
|
-
);
|
|
34
|
+
writePrebuiltTemplate(repoRoot, outDir);
|
|
46
35
|
const manifest: HandlerZipManifest = {
|
|
47
36
|
version: pkg.version,
|
|
48
37
|
url: `${pkg.repository.url}/releases/download/v${pkg.version}/${HANDLER_ZIP_ASSET}`,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const CHECKOUT_CODE_URI = "CodeUri: ../../packages/aws-lambda/dist/handler.zip";
|
|
5
|
+
|
|
6
|
+
/** Writes `<outDir>/template.yaml`: the example SAM template, pointed at a `handler.zip` beside it. */
|
|
7
|
+
export function writePrebuiltTemplate(repoRoot, outDir) {
|
|
8
|
+
const template = readFileSync(join(repoRoot, "examples", "aws-lambda", "template.yaml"), "utf-8");
|
|
9
|
+
if (template.split(CHECKOUT_CODE_URI).length !== 2) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`[prebuilt-template] expected exactly one "${CHECKOUT_CODE_URI}" in the template`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
mkdirSync(outDir, { recursive: true });
|
|
15
|
+
writeFileSync(
|
|
16
|
+
join(outDir, "template.yaml"),
|
|
17
|
+
template.replace(CHECKOUT_CODE_URI, "CodeUri: handler.zip"),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { pathToFileURL } from "node:url";
|
|
7
|
-
import * as esbuild from "esbuild";
|
|
8
|
-
import { HANDLER_BANNER } from "./_handlerBanner.js";
|
|
9
|
-
|
|
10
|
-
// The handler ships as ESM (.mjs) but inlines CJS deps that assume Node's CJS
|
|
11
|
-
// globals exist at module scope: postcss et al. call top-level `require(...)`,
|
|
12
|
-
// and wawoff2's emscripten build reads `__dirname`. A freshly deployed stack
|
|
13
|
-
// crashed on every render (#1932) with "__dirname is not defined in ES module
|
|
14
|
-
// scope"; the fix is the require/__filename/__dirname shim in HANDLER_BANNER.
|
|
15
|
-
//
|
|
16
|
-
// This bundles a fixture touching all three globals with the REAL banner and
|
|
17
|
-
// imports the output, so it catches a dropped/renamed shim behaviourally
|
|
18
|
-
// rather than by grepping for literals (which survives a broken refactor).
|
|
19
|
-
//
|
|
20
|
-
// The import MUST run under Node, not the `bun test` runtime: Bun defines
|
|
21
|
-
// `__dirname`/`__filename` even in ESM, which would mask a missing shim and
|
|
22
|
-
// green-light a broken bundle. Lambda runs Node, so we spawn `node` (guaranteed
|
|
23
|
-
// present in CI alongside bun) to reproduce the deploy target faithfully.
|
|
24
|
-
describe("build-zip handler banner", () => {
|
|
25
|
-
it("shims require/__filename/__dirname so inlined CJS deps import under Node", () => {
|
|
26
|
-
const dir = mkdtempSync(join(tmpdir(), "hf-banner-test-"));
|
|
27
|
-
try {
|
|
28
|
-
const entry = join(dir, "fixture.ts");
|
|
29
|
-
const outfile = join(dir, "out.mjs");
|
|
30
|
-
// Reference each CJS global at module top level, the way inlined deps do.
|
|
31
|
-
// If any shim is missing, importing `out.mjs` throws at eval time.
|
|
32
|
-
writeFileSync(
|
|
33
|
-
entry,
|
|
34
|
-
[
|
|
35
|
-
"const cjsDir = __dirname;",
|
|
36
|
-
"const cjsFile = __filename;",
|
|
37
|
-
"const path = require('node:path');",
|
|
38
|
-
"if (typeof cjsDir !== 'string') throw new Error('__dirname missing');",
|
|
39
|
-
"if (typeof cjsFile !== 'string') throw new Error('__filename missing');",
|
|
40
|
-
"if (typeof path.join !== 'function') throw new Error('require missing');",
|
|
41
|
-
"console.log('BANNER_OK');",
|
|
42
|
-
].join("\n"),
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
esbuild.buildSync({
|
|
46
|
-
bundle: true,
|
|
47
|
-
platform: "node",
|
|
48
|
-
target: "node22",
|
|
49
|
-
format: "esm",
|
|
50
|
-
entryPoints: [entry],
|
|
51
|
-
outfile,
|
|
52
|
-
banner: { js: HANDLER_BANNER },
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Import under real Node — Lambda's runtime — not the bun test runtime.
|
|
56
|
-
const res = spawnSync(
|
|
57
|
-
"node",
|
|
58
|
-
[
|
|
59
|
-
"--input-type=module",
|
|
60
|
-
"-e",
|
|
61
|
-
`await import(${JSON.stringify(pathToFileURL(outfile).href)});`,
|
|
62
|
-
],
|
|
63
|
-
{ encoding: "utf8" },
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
// A missing shim surfaces as a non-zero exit + ReferenceError on stderr.
|
|
67
|
-
// Guard against a silent skip if `node` isn't on PATH (it is in CI).
|
|
68
|
-
expect(res.error).toBeUndefined();
|
|
69
|
-
expect(res.stderr).not.toContain("is not defined in ES module scope");
|
|
70
|
-
expect(res.stderr).not.toContain("Dynamic require");
|
|
71
|
-
expect(res.status).toBe(0);
|
|
72
|
-
expect(res.stdout).toContain("BANNER_OK");
|
|
73
|
-
} finally {
|
|
74
|
-
rmSync(dir, { recursive: true, force: true });
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
});
|