@hyperframes/aws-lambda 0.8.177 → 0.8.179

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/sdk/index.js CHANGED
@@ -1,585 +1,51 @@
1
- // src/sdk/deploySite.ts
2
- import { mkdtempSync, rmSync as rmSync2, statSync as statSync2 } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { HeadObjectCommand as HeadObjectCommand2, S3Client } from "@aws-sdk/client-s3";
6
- import { hashProjectDir } from "@hyperframes/producer/distributed";
7
-
8
- // src/s3Transport.ts
9
- import {
10
- createReadStream,
11
- createWriteStream,
12
- existsSync,
13
- mkdirSync,
14
- rmSync,
15
- statSync
16
- } from "node:fs";
17
- import { dirname } from "node:path";
18
1
  import {
19
- GetObjectCommand,
20
- HeadObjectCommand,
21
- PutObjectCommand
22
- } from "@aws-sdk/client-s3";
23
- import * as tar from "tar";
24
- function parseS3Uri(uri) {
25
- if (!uri.startsWith("s3://")) {
26
- throw new Error(`[s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)}`);
27
- }
28
- const rest = uri.slice("s3://".length);
29
- const slash = rest.indexOf("/");
30
- if (slash === -1) {
31
- throw new Error(`[s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}`);
32
- }
33
- const bucket = rest.slice(0, slash);
34
- const key = rest.slice(slash + 1);
35
- if (!bucket || !key) {
36
- throw new Error(`[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)}`);
37
- }
38
- return { bucket, key };
39
- }
40
- function formatS3Uri(loc) {
41
- return `s3://${loc.bucket}/${loc.key}`;
42
- }
43
- async function uploadFileToS3(client, localPath, uri, contentType) {
44
- if (!existsSync(localPath)) {
45
- throw new Error(`[s3Transport] upload source missing: ${localPath}`);
46
- }
47
- const { bucket, key } = parseS3Uri(uri);
48
- const size = statSync(localPath).size;
49
- await client.send(
50
- new PutObjectCommand({
51
- Bucket: bucket,
52
- Key: key,
53
- Body: createReadStream(localPath),
54
- ContentType: contentType,
55
- ContentLength: size
56
- })
57
- );
58
- }
59
- async function tarDirectory(sourceDir, destTarball) {
60
- if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) {
61
- throw new Error(`[s3Transport] tar source must be an existing directory: ${sourceDir}`);
62
- }
63
- mkdirSync(dirname(destTarball), { recursive: true });
64
- await tar.create({ gzip: true, file: destTarball, cwd: sourceDir }, ["."]);
65
- }
66
-
67
- // src/sdk/deploySite.ts
68
- async function deploySite(opts) {
69
- if (!statSync2(opts.projectDir).isDirectory()) {
70
- throw new Error(`[deploySite] projectDir is not a directory: ${opts.projectDir}`);
71
- }
72
- const siteId = opts.siteId ?? hashProjectDir(opts.projectDir);
73
- const key = `sites/${siteId}/project.tar.gz`;
74
- const projectS3Uri = formatS3Uri({ bucket: opts.bucketName, key });
75
- const s3 = opts.s3 ?? new S3Client({ region: opts.region });
76
- const existing = await headObject(s3, opts.bucketName, key);
77
- if (existing) {
78
- return {
79
- siteId,
80
- bucketName: opts.bucketName,
81
- projectS3Uri,
82
- bytes: existing.bytes,
83
- uploadedAt: existing.lastModified,
84
- uploaded: false
85
- };
86
- }
87
- const workdir = mkdtempSync(join(tmpdir(), "hf-deploy-site-"));
88
- try {
89
- const tarball = join(workdir, "project.tar.gz");
90
- await tarDirectory(opts.projectDir, tarball);
91
- const size = statSync2(tarball).size;
92
- await uploadFileToS3(s3, tarball, projectS3Uri, "application/gzip");
93
- return {
94
- siteId,
95
- bucketName: opts.bucketName,
96
- projectS3Uri,
97
- bytes: size,
98
- uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
99
- uploaded: true
100
- };
101
- } finally {
102
- rmSync2(workdir, { recursive: true, force: true });
103
- }
104
- }
105
- async function headObject(s3, bucket, key) {
106
- try {
107
- const res = await s3.send(new HeadObjectCommand2({ Bucket: bucket, Key: key }));
108
- return {
109
- bytes: typeof res.ContentLength === "number" ? res.ContentLength : 0,
110
- lastModified: res.LastModified instanceof Date ? res.LastModified.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
111
- };
112
- } catch (err) {
113
- const status = err.$metadata?.httpStatusCode;
114
- if (status === 404) return null;
115
- const name = err.name;
116
- if (name === "NotFound" || name === "NoSuchKey") return null;
117
- throw err;
118
- }
119
- }
120
-
121
- // src/sdk/renderToLambda.ts
122
- import { randomUUID } from "node:crypto";
123
- import { SFNClient, StartExecutionCommand } from "@aws-sdk/client-sfn";
124
-
125
- // src/formatExtension.ts
126
- var FORMAT_EXTENSIONS = {
127
- mp4: ".mp4",
128
- mov: ".mov",
129
- webm: ".webm",
130
- "png-sequence": ""
131
- };
132
- function formatExtension(format) {
133
- return FORMAT_EXTENSIONS[format];
134
- }
135
-
136
- // src/sdk/validateConfig.ts
137
- import { InvalidConfigError } from "@hyperframes/producer/distributed";
138
- import {
139
- InvalidConfigError as InvalidConfigError2,
2
+ InvalidConfigError,
3
+ MAX_STEP_FUNCTIONS_INPUT_BYTES,
4
+ assertStackReadsSites,
5
+ computeRenderCost,
6
+ deploySite,
7
+ getRenderProgress,
8
+ renderToLambda,
9
+ siteHandleFromId,
140
10
  validateDistributedRenderConfig,
11
+ validateStepFunctionsInputSize,
141
12
  validateVariablesPayload
142
- } from "@hyperframes/producer/distributed";
143
- var MAX_STEP_FUNCTIONS_INPUT_BYTES = 256 * 1024;
144
- var LARGE_VARIABLES_DOCS_URL = "https://hyperframes.heygen.com/deploy/templates-on-lambda#working-with-large-variables";
145
- function validateStepFunctionsInputSize(input) {
146
- let serialized;
147
- try {
148
- serialized = JSON.stringify(input);
149
- } catch (err) {
150
- throw new InvalidConfigError(
151
- "config",
152
- `Step Functions execution input is not JSON-serializable: ${err instanceof Error ? err.message : String(err)}`
153
- );
154
- }
155
- if (serialized === void 0) {
156
- throw new InvalidConfigError(
157
- "config",
158
- "Step Functions execution input is not JSON-serializable (JSON.stringify returned undefined). Check that all fields, including config.variables, are plain JSON values."
159
- );
160
- }
161
- const byteLength = Buffer.byteLength(serialized, "utf8");
162
- if (byteLength > MAX_STEP_FUNCTIONS_INPUT_BYTES) {
163
- throw new InvalidConfigError(
164
- "config",
165
- `Step Functions execution input is ${byteLength} bytes, which exceeds the ${MAX_STEP_FUNCTIONS_INPUT_BYTES}-byte (256 KiB) limit for Standard workflows. Variables are for typed data (strings, numbers, structured records); media assets (images, audio, video) should be passed as URL references the composition resolves at render time, not inlined as base64. See ${LARGE_VARIABLES_DOCS_URL} for the URL-your-assets convention.`
166
- );
167
- }
168
- }
169
-
170
- // src/sdk/renderToLambda.ts
171
- async function renderToLambda(opts) {
172
- validateDistributedRenderConfig(opts.config);
173
- if (!opts.bucketName) {
174
- throw new Error("[renderToLambda] bucketName is required");
175
- }
176
- if (!opts.stateMachineArn) {
177
- throw new Error("[renderToLambda] stateMachineArn is required");
178
- }
179
- if (!opts.siteHandle && !opts.projectDir) {
180
- throw new Error("[renderToLambda] either siteHandle or projectDir must be supplied");
181
- }
182
- const executionName = opts.executionName ?? `hf-render-${randomUUID()}`;
183
- const ext = formatExtension(opts.config.format);
184
- const outputKey = opts.outputKey ?? `renders/${executionName}/output${ext}`;
185
- const planOutputS3Prefix = formatS3Uri({
186
- bucket: opts.bucketName,
187
- key: `renders/${executionName}/`
188
- });
189
- const outputS3Uri = formatS3Uri({ bucket: opts.bucketName, key: outputKey });
190
- const site = opts.siteHandle ?? await deploySite({
191
- projectDir: opts.projectDir,
192
- bucketName: opts.bucketName,
193
- region: opts.region,
194
- s3: opts.s3
195
- });
196
- const input = {
197
- ProjectS3Uri: site.projectS3Uri,
198
- PlanOutputS3Prefix: planOutputS3Prefix,
199
- OutputS3Uri: outputS3Uri,
200
- Config: opts.config,
201
- PlanProtocol: opts.planProtocol ?? "v2"
202
- };
203
- validateStepFunctionsInputSize(input);
204
- const sfn = opts.sfn ?? new SFNClient({ region: opts.region });
205
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
206
- const response = await sfn.send(
207
- new StartExecutionCommand({
208
- stateMachineArn: opts.stateMachineArn,
209
- name: executionName,
210
- input: JSON.stringify(input)
211
- })
212
- );
213
- if (!response.executionArn) {
214
- throw new Error("[renderToLambda] StartExecution returned no executionArn");
215
- }
216
- return {
217
- renderId: executionName,
218
- executionArn: response.executionArn,
219
- bucketName: opts.bucketName,
220
- stateMachineArn: opts.stateMachineArn,
221
- outputS3Uri,
222
- projectS3Uri: site.projectS3Uri,
223
- startedAt
224
- };
225
- }
226
-
227
- // src/sdk/getRenderProgress.ts
228
- import {
229
- DescribeExecutionCommand,
230
- DescribeMapRunCommand,
231
- GetExecutionHistoryCommand,
232
- ListExecutionsCommand,
233
- SFNClient as SFNClient2
234
- } from "@aws-sdk/client-sfn";
235
-
236
- // src/sdk/costAccounting.ts
237
- var LAMBDA_USD_PER_GB_SECOND = 166667e-10;
238
- var SFN_USD_PER_TRANSITION = 25e-6;
239
- function computeRenderCost(lambdaInvocations, stateTransitions) {
240
- let lambdaUsd = 0;
241
- let anyEstimated = false;
242
- for (const inv of lambdaInvocations) {
243
- const gbSeconds = inv.memorySizeMb / 1024 * (inv.billedDurationMs / 1e3);
244
- lambdaUsd += gbSeconds * LAMBDA_USD_PER_GB_SECOND;
245
- if (inv.estimated) anyEstimated = true;
246
- }
247
- const stepFunctionsUsd = stateTransitions * SFN_USD_PER_TRANSITION;
248
- const accruedSoFarUsd = roundUsd(lambdaUsd + stepFunctionsUsd);
249
- return {
250
- accruedSoFarUsd,
251
- displayCost: formatUsd(accruedSoFarUsd),
252
- breakdown: {
253
- lambdaUsd: roundUsd(lambdaUsd),
254
- stepFunctionsUsd: roundUsd(stepFunctionsUsd),
255
- s3Estimate: "not-included",
256
- estimated: anyEstimated
257
- }
258
- };
259
- }
260
- function roundUsd(usd) {
261
- return Math.round(usd * 1e4) / 1e4;
262
- }
263
- function formatUsd(usd) {
264
- return `$${usd.toFixed(4)}`;
265
- }
266
-
267
- // src/sdk/getRenderProgress.ts
268
- var DEFAULT_MEMORY_MB = 10240;
269
- function createProgressSfnClient(config = {}) {
270
- return new SFNClient2({ maxAttempts: 8, ...config });
271
- }
272
- async function getRenderProgress(opts) {
273
- if (!opts.executionArn) {
274
- throw new Error("[getRenderProgress] executionArn is required");
275
- }
276
- const sfn = opts.sfn ?? createProgressSfnClient({ region: opts.region });
277
- const memoryMb = opts.defaultMemorySizeMb ?? DEFAULT_MEMORY_MB;
278
- const describe = await sfn.send(
279
- new DescribeExecutionCommand({ executionArn: opts.executionArn })
280
- );
281
- const status = describe.status ?? "RUNNING";
282
- const startedAt = describe.startDate?.toISOString() ?? (/* @__PURE__ */ new Date(0)).toISOString();
283
- const endedAt = describe.stopDate?.toISOString() ?? null;
284
- const history = await loadFullHistory(sfn, opts.executionArn);
285
- const summary = summarizeHistory(history, memoryMb);
286
- const mapRun = findChunkMapRun(history);
287
- const warnings = [];
288
- if (mapRun?.exited) addChunkResults(summary, mapRun.output, memoryMb);
289
- else if (mapRun) {
290
- await addRunningMapRun(sfn, mapRun.arn, summary, memoryMb).catch((err) => {
291
- if (err?.name !== "AccessDeniedException") throw err;
292
- warnings.push(MAP_RUN_ACCESS_DENIED);
293
- });
294
- }
295
- const costs = computeRenderCost(summary.lambdaInvocations, summary.stateTransitions);
296
- const overallProgress = computeOverallProgress({
297
- status,
298
- totalFrames: summary.totalFrames,
299
- framesRendered: summary.framesRendered,
300
- assembleComplete: summary.assembleComplete
301
- });
302
- return {
303
- status,
304
- overallProgress,
305
- framesRendered: summary.framesRendered,
306
- totalFrames: summary.totalFrames,
307
- lambdasInvoked: summary.lambdasInvoked,
308
- costs,
309
- outputFile: summary.outputFile,
310
- errors: summary.errors,
311
- fatalErrorEncountered: isTerminalFailure(status),
312
- warnings,
313
- startedAt,
314
- endedAt
315
- };
316
- }
317
- async function loadFullHistory(sfn, executionArn) {
318
- const events = [];
319
- let nextToken;
320
- for (let page = 0; page < 50; page++) {
321
- const res = await sfn.send(
322
- new GetExecutionHistoryCommand({
323
- executionArn,
324
- maxResults: 1e3,
325
- nextToken,
326
- reverseOrder: false
327
- })
328
- );
329
- if (res.events) events.push(...res.events);
330
- nextToken = res.nextToken;
331
- if (!nextToken) break;
332
- }
333
- return events;
334
- }
335
- var FAILED_CHUNK_HISTORIES_READ = 5;
336
- var MAP_RUN_ACCESS_DENIED = "Chunk progress is hidden until the chunks finish: this caller lacks states:DescribeMapRun. Re-apply the policy from `hyperframes lambda policies user`.";
337
- function findChunkMapRun(events) {
338
- let run = null;
339
- for (const ev of events) {
340
- const arn = ev.mapRunStartedEventDetails?.mapRunArn;
341
- if (arn) run = { arn, exited: false, output: void 0 };
342
- const exit = ev.stateExitedEventDetails;
343
- if (run && ev.type === "MapStateExited" && isRenderChunksMapState(exit?.name)) {
344
- run = { ...run, exited: true, output: exit?.output };
345
- }
346
- }
347
- return run;
348
- }
349
- function addChunkResults(summary, mapOutput, memoryMb) {
350
- const chunks = parseJson(mapOutput)?.Chunks;
351
- for (const chunk of Array.isArray(chunks) ? chunks : []) {
352
- summary.lambdasInvoked++;
353
- summary.stateTransitions++;
354
- const billedDurationMs = inferBilledMs(chunk);
355
- summary.lambdaInvocations.push({
356
- billedDurationMs,
357
- memorySizeMb: memoryMb,
358
- estimated: billedDurationMs === 0
359
- });
360
- applyPayloadFrameCounts(chunk, "RenderChunk", (delta) => {
361
- summary.framesRendered += delta;
362
- });
363
- }
364
- }
365
- async function addRunningMapRun(sfn, mapRunArn, summary, memoryMb) {
366
- const res = await sfn.send(new DescribeMapRunCommand({ mapRunArn }));
367
- const { total = 0, pending = 0, succeeded = 0, failed = 0 } = res.itemCounts ?? {};
368
- summary.lambdasInvoked += total - pending;
369
- summary.stateTransitions += total - pending;
370
- if (summary.totalFrames !== null && total > 0) {
371
- summary.framesRendered += Math.floor(summary.totalFrames * succeeded / total);
372
- }
373
- if (failed > 0) summary.errors.push(...await failedChunkErrors(sfn, mapRunArn, memoryMb));
374
- }
375
- async function failedChunkErrors(sfn, mapRunArn, memoryMb) {
376
- const failed = await sfn.send(
377
- new ListExecutionsCommand({
378
- mapRunArn,
379
- statusFilter: "FAILED",
380
- maxResults: FAILED_CHUNK_HISTORIES_READ
381
- })
382
- );
383
- const errors = [];
384
- for (const child of failed.executions ?? []) {
385
- if (!child.executionArn) continue;
386
- const childHistory = await loadFullHistory(sfn, child.executionArn);
387
- errors.push(...summarizeHistory(childHistory, memoryMb).errors);
388
- }
389
- return errors;
390
- }
391
- function summarizeHistory(events, memoryMb) {
392
- let framesRendered = 0;
393
- let totalFrames = null;
394
- let lambdasInvoked = 0;
395
- let assembleComplete = false;
396
- let outputFile = null;
397
- let stateTransitions = 0;
398
- const errors = [];
399
- const lambdaInvocations = [];
400
- let currentLambdaState = null;
401
- for (const ev of events) {
402
- switch (ev.type) {
403
- case "TaskStateEntered":
404
- case "MapStateEntered":
405
- case "PassStateEntered":
406
- case "ChoiceStateEntered":
407
- case "SucceedStateEntered":
408
- case "FailStateEntered":
409
- case "WaitStateEntered":
410
- case "ParallelStateEntered":
411
- stateTransitions++;
412
- currentLambdaState = ev.stateEnteredEventDetails?.name ?? currentLambdaState;
413
- break;
414
- // Optimized `lambda:invoke` task emits Task* events; raw
415
- // `lambda:invokeFunction.sync` emits LambdaFunction*. Handle both.
416
- case "TaskScheduled":
417
- if (ev.taskScheduledEventDetails?.resourceType === "lambda") {
418
- lambdasInvoked++;
419
- }
420
- break;
421
- case "LambdaFunctionScheduled":
422
- lambdasInvoked++;
423
- break;
424
- case "TaskSucceeded": {
425
- if (ev.taskSucceededEventDetails?.resourceType !== "lambda") break;
426
- const wrapped = parseJson(ev.taskSucceededEventDetails?.output);
427
- const payload = unwrapLambdaPayload(wrapped);
428
- const billedDurationMs = inferBilledMs(payload);
429
- lambdaInvocations.push({
430
- billedDurationMs,
431
- memorySizeMb: memoryMb,
432
- estimated: billedDurationMs === 0
433
- });
434
- applyPayloadFrameCounts(payload, currentLambdaState, (delta) => {
435
- framesRendered += delta;
436
- });
437
- if (payload && typeof payload === "object") {
438
- const obj = payload;
439
- if (typeof obj.TotalFrames === "number") totalFrames = obj.TotalFrames;
440
- }
441
- break;
442
- }
443
- case "LambdaFunctionSucceeded": {
444
- const payload = parseJson(ev.lambdaFunctionSucceededEventDetails?.output);
445
- const billedDurationMs = inferBilledMs(payload);
446
- lambdaInvocations.push({
447
- billedDurationMs,
448
- memorySizeMb: memoryMb,
449
- estimated: billedDurationMs === 0
450
- });
451
- applyPayloadFrameCounts(payload, currentLambdaState, (delta) => {
452
- framesRendered += delta;
453
- });
454
- if (payload && typeof payload === "object") {
455
- const obj = payload;
456
- if (typeof obj.TotalFrames === "number") totalFrames = obj.TotalFrames;
457
- }
458
- break;
459
- }
460
- case "TaskStateExited":
461
- case "MapStateExited":
462
- if (isAssembleState(ev.stateExitedEventDetails?.name)) {
463
- assembleComplete = true;
464
- const exitPayload = parseJson(ev.stateExitedEventDetails?.output);
465
- if (exitPayload && typeof exitPayload === "object") {
466
- const obj = exitPayload;
467
- const out = obj.Output;
468
- const outputS3Uri = typeof out?.OutputS3Uri === "string" ? out.OutputS3Uri : null;
469
- const bytes = typeof out?.FileSize === "number" ? out.FileSize : null;
470
- outputFile = outputS3Uri ? { s3Uri: outputS3Uri, bytes } : outputFile;
471
- }
472
- }
473
- break;
474
- case "TaskFailed":
475
- if (ev.taskFailedEventDetails?.resourceType !== "lambda") break;
476
- errors.push({
477
- state: currentLambdaState ?? "<unknown>",
478
- error: ev.taskFailedEventDetails?.error ?? "UNKNOWN",
479
- cause: ev.taskFailedEventDetails?.cause ?? ""
480
- });
481
- break;
482
- case "LambdaFunctionFailed":
483
- errors.push({
484
- state: currentLambdaState ?? "<unknown>",
485
- error: ev.lambdaFunctionFailedEventDetails?.error ?? "UNKNOWN",
486
- cause: ev.lambdaFunctionFailedEventDetails?.cause ?? ""
487
- });
488
- break;
489
- case "ExecutionFailed":
490
- errors.push({
491
- state: "<execution>",
492
- error: ev.executionFailedEventDetails?.error ?? "UNKNOWN",
493
- cause: ev.executionFailedEventDetails?.cause ?? ""
494
- });
495
- break;
496
- case "ExecutionAborted":
497
- errors.push({
498
- state: "<execution>",
499
- error: ev.executionAbortedEventDetails?.error ?? "ABORTED",
500
- cause: ev.executionAbortedEventDetails?.cause ?? ""
501
- });
502
- break;
503
- case "ExecutionTimedOut":
504
- errors.push({
505
- state: "<execution>",
506
- error: "TIMEOUT",
507
- cause: ev.executionTimedOutEventDetails?.cause ?? ""
508
- });
509
- break;
510
- default:
511
- break;
512
- }
513
- }
514
- return {
515
- lambdaInvocations,
516
- stateTransitions,
517
- framesRendered,
518
- totalFrames,
519
- lambdasInvoked,
520
- assembleComplete,
521
- outputFile,
522
- errors
523
- };
524
- }
525
- function parseJson(s) {
526
- if (!s) return null;
527
- try {
528
- return JSON.parse(s);
529
- } catch {
530
- return null;
531
- }
532
- }
533
- function unwrapLambdaPayload(payload) {
534
- if (payload && typeof payload === "object" && "Payload" in payload) {
535
- const inner = payload.Payload;
536
- if (inner && typeof inner === "object") return inner;
537
- }
538
- return payload;
539
- }
540
- function applyPayloadFrameCounts(payload, currentLambdaState, bump) {
541
- if (!isRenderChunkState(currentLambdaState)) return;
542
- if (!payload || typeof payload !== "object") return;
543
- const obj = payload;
544
- if (typeof obj.FramesEncoded === "number") bump(obj.FramesEncoded);
545
- }
546
- function isRenderChunkState(name) {
547
- return name === "RenderChunk" || name === "RenderChunkV2";
548
- }
549
- function isRenderChunksMapState(name) {
550
- return name === "RenderChunks" || name === "RenderChunksV2";
551
- }
552
- function isAssembleState(name) {
553
- return name === "Assemble" || name === "AssembleV2";
554
- }
555
- function inferBilledMs(payload) {
556
- if (!payload || typeof payload !== "object") return 0;
557
- const obj = payload;
558
- if (typeof obj.DurationMs === "number") return obj.DurationMs;
559
- return 0;
560
- }
561
- function computeOverallProgress({
562
- status,
563
- totalFrames,
564
- framesRendered,
565
- assembleComplete
566
- }) {
567
- if (status === "SUCCEEDED") return 1;
568
- if (assembleComplete) return 1;
569
- if (totalFrames === null) return 0;
570
- const chunkProgress = Math.min(1, framesRendered / totalFrames);
571
- return 0.1 + 0.8 * chunkProgress;
572
- }
573
- function isTerminalFailure(status) {
574
- return status === "FAILED" || status === "TIMED_OUT" || status === "ABORTED";
13
+ } from "../_chunks/chunk-PK7PO3MH.js";
14
+ import "../_chunks/chunk-ZY5YIH5W.js";
15
+ import "../_chunks/chunk-YS3KY3C5.js";
16
+
17
+ // src/sdk/prebuiltHandler.ts
18
+ import { existsSync, readFileSync } from "node:fs";
19
+ import { dirname, join } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+ var PREBUILT_DIR = "prebuilt";
22
+ var HANDLER_MANIFEST_FILE = "handler-zip.json";
23
+ function adapterPackageRoot() {
24
+ const from = fileURLToPath(import.meta.url);
25
+ for (let dir = dirname(from); dir !== dirname(dir); dir = dirname(dir)) {
26
+ const pkg = join(dir, "package.json");
27
+ if (existsSync(pkg) && JSON.parse(readFileSync(pkg, "utf-8")).name === "@hyperframes/aws-lambda")
28
+ return dir;
29
+ }
30
+ throw new Error(`[aws-lambda] no @hyperframes/aws-lambda package.json above ${from}`);
31
+ }
32
+ function prebuiltHandler(root = adapterPackageRoot()) {
33
+ const dir = join(root, PREBUILT_DIR);
34
+ const manifestPath = join(dir, HANDLER_MANIFEST_FILE);
35
+ if (!existsSync(manifestPath)) return void 0;
36
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
37
+ return { ...manifest, templatePath: join(dir, "template.yaml") };
575
38
  }
576
39
  export {
577
- InvalidConfigError2 as InvalidConfigError,
40
+ InvalidConfigError,
578
41
  MAX_STEP_FUNCTIONS_INPUT_BYTES,
42
+ assertStackReadsSites,
579
43
  computeRenderCost,
580
44
  deploySite,
581
45
  getRenderProgress,
46
+ prebuiltHandler,
582
47
  renderToLambda,
48
+ siteHandleFromId,
583
49
  validateDistributedRenderConfig,
584
50
  validateStepFunctionsInputSize,
585
51
  validateVariablesPayload
@@ -0,0 +1,17 @@
1
+ export declare const HANDLER_ZIP_ASSET = "hyperframes-lambda-handler.zip";
2
+ export declare const PREBUILT_DIR = "prebuilt";
3
+ export declare const HANDLER_MANIFEST_FILE = "handler-zip.json";
4
+ export interface HandlerZipManifest {
5
+ version: string;
6
+ url: string;
7
+ sha256: string;
8
+ bytes: number;
9
+ }
10
+ export interface PrebuiltHandler extends HandlerZipManifest {
11
+ /** SAM template whose `CodeUri` is `handler.zip` in the same directory. */
12
+ templatePath: string;
13
+ }
14
+ /** This package's root, found by walking up from the running module (source or bundle). */
15
+ export declare function adapterPackageRoot(): string;
16
+ /** The pinned handler for this install, or undefined when this build carries none. */
17
+ export declare function prebuiltHandler(root?: string): PrebuiltHandler | undefined;
@@ -17,12 +17,12 @@
17
17
  * execution name so the caller can predict the final object URL.
18
18
  */
19
19
  import { SFNClient } from "@aws-sdk/client-sfn";
20
- import type { S3Client } from "@aws-sdk/client-s3";
20
+ import { S3Client } from "@aws-sdk/client-s3";
21
21
  import type { LambdaPlanProtocol, SerializableDistributedRenderConfig } from "../events.js";
22
22
  import { type SiteHandle } from "./deploySite.js";
23
23
  /** Options for {@link renderToLambda}. */
24
24
  export interface RenderToLambdaOptions {
25
- /** Local project directory. Required when `siteHandle` is not supplied. */
25
+ /** Local project directory. Required without `siteHandle`; with one, re-uploads expired files. */
26
26
  projectDir?: string;
27
27
  /** Re-use an existing `deploySite` upload (skips tar+S3 PUT). */
28
28
  siteHandle?: SiteHandle;
@@ -68,3 +68,9 @@ export interface RenderHandle {
68
68
  startedAt: string;
69
69
  }
70
70
  export declare function renderToLambda(opts: RenderToLambdaOptions): Promise<RenderHandle>;
71
+ /** An older stack's plan step would try to untar a site manifest and fail after retries. */
72
+ export declare function assertStackReadsSites(opts: {
73
+ stateMachineArn: string;
74
+ region?: string;
75
+ sfn?: SFNClient;
76
+ }): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import type { S3Client } from "@aws-sdk/client-s3";
2
+ import type { RemoteProjectFile } from "@hyperframes/producer/distributed";
3
+ /** Stacks that read site manifests list this as a terminal plan error; the SDK checks for it. */
4
+ export declare const SITE_MANIFEST_INVALID = "SITE_MANIFEST_INVALID";
5
+ export interface SiteFile {
6
+ /** `/`-separated, relative to the project root. */
7
+ readonly path: string;
8
+ readonly sha256: string;
9
+ readonly sizeBytes: number;
10
+ }
11
+ export declare function siteManifestUri(bucket: string, siteId: string): string;
12
+ /** Sites uploaded before site manifests are one tarball; the plan step still reads them. */
13
+ export declare function siteTarballUri(bucket: string, siteId: string): string;
14
+ export declare function isSiteManifestUri(uri: string): boolean;
15
+ export declare function serializeSiteManifest(files: readonly SiteFile[]): string;
16
+ export declare function parseSiteManifest(text: string): SiteFile[];
17
+ /** Rebuild the project under `projectDir`, SHA-256 verified; videos can stay remote, as URLs. */
18
+ export declare function downloadSiteFiles(s3: S3Client, manifestUri: string, projectDir: string, leaveVideosRemote: boolean): Promise<Record<string, RemoteProjectFile>>;