@remotion/lambda 3.2.29 → 3.2.31

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.
@@ -31,6 +31,10 @@ export declare type RenderMediaOnLambdaInput = {
31
31
  downloadBehavior?: DownloadBehavior | null;
32
32
  muted?: boolean;
33
33
  overwrite?: boolean;
34
+ webhook?: {
35
+ url: string;
36
+ secret: string | null;
37
+ };
34
38
  };
35
39
  export declare type RenderMediaOnLambdaOutput = {
36
40
  renderId: string;
@@ -53,10 +57,11 @@ export declare type RenderMediaOnLambdaOutput = {
53
57
  * @param params.region The AWS region in which the media should be rendered.
54
58
  * @param params.maxRetries How often rendering a chunk may fail before the media render gets aborted. Default "1"
55
59
  * @param params.logLevel Level of logging that Lambda function should perform. Default "info".
60
+ * @param params.webhook Configuration for webhook called upon completion or timeout of the render.
56
61
  * @returns {Promise<RenderMediaOnLambdaOutput>} See documentation for detailed structure
57
62
  */
58
- export declare const renderMediaOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
63
+ export declare const renderMediaOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, webhook, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
59
64
  /**
60
65
  * @deprecated Renamed to renderMediaOnLambda()
61
66
  */
62
- export declare const renderVideoOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
67
+ export declare const renderVideoOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, webhook, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
@@ -26,9 +26,10 @@ const validate_serveurl_1 = require("../shared/validate-serveurl");
26
26
  * @param params.region The AWS region in which the media should be rendered.
27
27
  * @param params.maxRetries How often rendering a chunk may fail before the media render gets aborted. Default "1"
28
28
  * @param params.logLevel Level of logging that Lambda function should perform. Default "info".
29
+ * @param params.webhook Configuration for webhook called upon completion or timeout of the render.
29
30
  * @returns {Promise<RenderMediaOnLambdaOutput>} See documentation for detailed structure
30
31
  */
31
- const renderMediaOnLambda = async ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, }) => {
32
+ const renderMediaOnLambda = async ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, webhook, }) => {
32
33
  var _a;
33
34
  const actualCodec = (0, validate_lambda_codec_1.validateLambdaCodec)(codec);
34
35
  (0, validate_serveurl_1.validateServeUrl)(serveUrl);
@@ -69,6 +70,7 @@ const renderMediaOnLambda = async ({ functionName, serveUrl, inputProps, codec,
69
70
  muted: muted !== null && muted !== void 0 ? muted : false,
70
71
  version: version_1.VERSION,
71
72
  overwrite: overwrite !== null && overwrite !== void 0 ? overwrite : false,
73
+ webhook: webhook !== null && webhook !== void 0 ? webhook : null,
72
74
  },
73
75
  region,
74
76
  });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @description Throws if the signature of the finish webhook is missing or inauthentic
3
+ * @link https://remotion.dev/docs/lambda/validate-webhook-signature
4
+ * @param params.secret The secret used for signing the webhook
5
+ * @param params.body The body that was received by the endpoint
6
+ * @param params.signatureHeader The `X-Remotion-Signature` header
7
+ * @returns {void}
8
+ */
9
+ export declare const validateWebhookSignature: ({ secret, body, signatureHeader, }: {
10
+ secret: string;
11
+ body: string;
12
+ signatureHeader: string;
13
+ }) => void;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateWebhookSignature = void 0;
4
+ /**
5
+ * @description Throws if the signature of the finish webhook is missing or inauthentic
6
+ * @link https://remotion.dev/docs/lambda/validate-webhook-signature
7
+ * @param params.secret The secret used for signing the webhook
8
+ * @param params.body The body that was received by the endpoint
9
+ * @param params.signatureHeader The `X-Remotion-Signature` header
10
+ * @returns {void}
11
+ */
12
+ const validateWebhookSignature = ({ secret, body, signatureHeader, }) => {
13
+ if (!secret) {
14
+ throw new TypeError("No 'secret' was provided to validateWebhookSignature().");
15
+ }
16
+ if (!body) {
17
+ throw new TypeError("No 'body' was provided to validateWebhookSignature().");
18
+ }
19
+ if (typeof require === 'undefined') {
20
+ throw new Error('validateWebhookSignature can only be called from Node.JS');
21
+ }
22
+ const Crypto = require('crypto');
23
+ const hmac = Crypto.createHmac('sha512', secret);
24
+ const signature = `sha512=${hmac.update(JSON.stringify(body)).digest('hex')}`;
25
+ if (!signatureHeader || signatureHeader === 'NO_SECRET_PROVIDED') {
26
+ throw new Error('No webhook signature was provided');
27
+ }
28
+ if (signatureHeader !== signature) {
29
+ throw new Error('Signatures do not match');
30
+ }
31
+ };
32
+ exports.validateWebhookSignature = validateWebhookSignature;
@@ -24,6 +24,8 @@ declare type LambdaCommandLineOptions = {
24
24
  ['architecture']: LambdaArchitecture;
25
25
  ['custom-role-arn']: string | undefined;
26
26
  privacy: Privacy;
27
+ webhook: string | undefined;
28
+ ['webhook-secret']: string | undefined;
27
29
  };
28
30
  export declare const parsedLambdaCli: LambdaCommandLineOptions & minimist.ParsedArgs;
29
31
  export declare const forceFlagProvided: boolean;
@@ -19,7 +19,7 @@ const log_1 = require("../../log");
19
19
  const progress_1 = require("./progress");
20
20
  exports.RENDER_COMMAND = 'render';
21
21
  const renderCommand = async (args) => {
22
- var _a, _b, _c, _d;
22
+ var _a, _b, _c, _d, _e;
23
23
  const serveUrl = args[0];
24
24
  if (!serveUrl) {
25
25
  log_1.Log.error('No serve URL passed.');
@@ -82,6 +82,12 @@ const renderCommand = async (args) => {
82
82
  concurrencyPerLambda: args_1.parsedLambdaCli['concurrency-per-lambda'],
83
83
  muted,
84
84
  overwrite,
85
+ webhook: args_1.parsedLambdaCli.webhook
86
+ ? {
87
+ url: args_1.parsedLambdaCli.webhook,
88
+ secret: (_e = args_1.parsedLambdaCli['webhook-secret']) !== null && _e !== void 0 ? _e : null,
89
+ }
90
+ : undefined,
85
91
  });
86
92
  const totalSteps = downloadName ? 5 : 4;
87
93
  const progressBar = cli_1.CliInternals.createOverwriteableCliOutput(cli_1.CliInternals.quietFlagProvided());
package/dist/client.d.ts CHANGED
@@ -2,7 +2,9 @@ import { getFunctions } from './api/get-functions';
2
2
  import { getRenderProgress } from './api/get-render-progress';
3
3
  import { renderMediaOnLambda, renderVideoOnLambda } from './api/render-media-on-lambda';
4
4
  import { renderStillOnLambda } from './api/render-still-on-lambda';
5
+ import { validateWebhookSignature } from './api/validate-webhook-signature';
5
6
  import type { AwsRegion } from './pricing/aws-regions';
6
7
  import type { RenderProgress } from './shared/constants';
7
- export { renderVideoOnLambda, renderMediaOnLambda, renderStillOnLambda, getRenderProgress, getFunctions, };
8
- export type { AwsRegion, RenderProgress };
8
+ import type { WebhookPayload } from './shared/invoke-webhook';
9
+ export { renderVideoOnLambda, renderMediaOnLambda, renderStillOnLambda, getRenderProgress, getFunctions, validateWebhookSignature, };
10
+ export type { AwsRegion, RenderProgress, WebhookPayload };
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFunctions = exports.getRenderProgress = exports.renderStillOnLambda = exports.renderMediaOnLambda = exports.renderVideoOnLambda = void 0;
3
+ exports.validateWebhookSignature = exports.getFunctions = exports.getRenderProgress = exports.renderStillOnLambda = exports.renderMediaOnLambda = exports.renderVideoOnLambda = void 0;
4
4
  const get_functions_1 = require("./api/get-functions");
5
5
  Object.defineProperty(exports, "getFunctions", { enumerable: true, get: function () { return get_functions_1.getFunctions; } });
6
6
  const get_render_progress_1 = require("./api/get-render-progress");
@@ -10,3 +10,5 @@ Object.defineProperty(exports, "renderMediaOnLambda", { enumerable: true, get: f
10
10
  Object.defineProperty(exports, "renderVideoOnLambda", { enumerable: true, get: function () { return render_media_on_lambda_1.renderVideoOnLambda; } });
11
11
  const render_still_on_lambda_1 = require("./api/render-still-on-lambda");
12
12
  Object.defineProperty(exports, "renderStillOnLambda", { enumerable: true, get: function () { return render_still_on_lambda_1.renderStillOnLambda; } });
13
+ const validate_webhook_signature_1 = require("./api/validate-webhook-signature");
14
+ Object.defineProperty(exports, "validateWebhookSignature", { enumerable: true, get: function () { return validate_webhook_signature_1.validateWebhookSignature; } });
@@ -1,6 +1,6 @@
1
1
  import type { FileNameAndSize } from './get-files-in-folder';
2
2
  export declare type LambdaErrorInfo = {
3
- type: 'renderer' | 'browser' | 'stitcher';
3
+ type: 'renderer' | 'browser' | 'stitcher' | 'webhook';
4
4
  message: string;
5
5
  name: string;
6
6
  stack: string;
@@ -12,6 +12,7 @@ const version_1 = require("remotion/version");
12
12
  const aws_clients_1 = require("../shared/aws-clients");
13
13
  const constants_1 = require("../shared/constants");
14
14
  const docs_url_1 = require("../shared/docs-url");
15
+ const invoke_webhook_1 = require("../shared/invoke-webhook");
15
16
  const make_s3_url_1 = require("../shared/make-s3-url");
16
17
  const validate_frames_per_lambda_1 = require("../shared/validate-frames-per-lambda");
17
18
  const validate_outname_1 = require("../shared/validate-outname");
@@ -68,6 +69,49 @@ const innerLaunchHandler = async (params, options) => {
68
69
  throw new Error('Expected launch type');
69
70
  }
70
71
  const startedDate = Date.now();
72
+ let webhookInvoked = false;
73
+ const webhookDueToTimeout = setTimeout(async () => {
74
+ if (params.webhook && !webhookInvoked) {
75
+ try {
76
+ await (0, invoke_webhook_1.invokeWebhook)({
77
+ url: params.webhook.url,
78
+ secret: params.webhook.secret,
79
+ payload: {
80
+ type: 'timeout',
81
+ renderId: params.renderId,
82
+ expectedBucketOwner: options.expectedBucketOwner,
83
+ bucketName: params.bucketName,
84
+ },
85
+ });
86
+ webhookInvoked = true;
87
+ }
88
+ catch (err) {
89
+ if (process.env.NODE_ENV === 'test') {
90
+ throw err;
91
+ }
92
+ await (0, write_lambda_error_1.writeLambdaError)({
93
+ bucketName: params.bucketName,
94
+ errorInfo: {
95
+ type: 'webhook',
96
+ message: err.message,
97
+ name: err.name,
98
+ stack: err.stack,
99
+ tmpDir: null,
100
+ frame: 0,
101
+ chunk: 0,
102
+ isFatal: false,
103
+ attempt: 1,
104
+ willRetry: false,
105
+ totalAttempts: 1,
106
+ },
107
+ renderId: params.renderId,
108
+ expectedBucketOwner: options.expectedBucketOwner,
109
+ });
110
+ console.log('Failed to invoke webhook:');
111
+ console.log(err);
112
+ }
113
+ }
114
+ }, Math.max(params.timeoutInMilliseconds - 1000, 1000));
71
115
  const [browserInstance, optimization] = await Promise.all([
72
116
  (0, get_browser_instance_1.getBrowserInstance)(renderer_1.RenderInternals.isEqualOrBelowLogLevel(params.logLevel, 'verbose'), params.chromiumOptions),
73
117
  (0, s3_optimization_file_1.getOptimization)({
@@ -378,6 +422,7 @@ const innerLaunchHandler = async (params, options) => {
378
422
  contents,
379
423
  jobs,
380
424
  });
425
+ const outputUrl = (0, get_output_url_from_metadata_1.getOutputUrlFromMetadata)(renderMetadata, params.bucketName, customCredentials);
381
426
  const postRenderData = (0, create_post_render_data_1.createPostRenderData)({
382
427
  expectedBucketOwner: options.expectedBucketOwner,
383
428
  region: (0, get_current_region_1.getCurrentRegionInFunction)(),
@@ -391,7 +436,7 @@ const innerLaunchHandler = async (params, options) => {
391
436
  outputFile: {
392
437
  lastModified: Date.now(),
393
438
  size: outputSize.size,
394
- url: (0, get_output_url_from_metadata_1.getOutputUrlFromMetadata)(renderMetadata, params.bucketName, customCredentials),
439
+ url: outputUrl,
395
440
  },
396
441
  });
397
442
  await finalEncodingProgressProm;
@@ -409,8 +454,54 @@ const innerLaunchHandler = async (params, options) => {
409
454
  customCredentials: null,
410
455
  });
411
456
  await Promise.all([cleanupChunksProm, fs_1.default.promises.rm(outfile)]);
457
+ clearTimeout(webhookDueToTimeout);
458
+ if (params.webhook && !webhookInvoked) {
459
+ try {
460
+ await (0, invoke_webhook_1.invokeWebhook)({
461
+ url: params.webhook.url,
462
+ secret: params.webhook.secret,
463
+ payload: {
464
+ type: 'success',
465
+ renderId: params.renderId,
466
+ expectedBucketOwner: options.expectedBucketOwner,
467
+ bucketName: params.bucketName,
468
+ outputUrl,
469
+ lambdaErrors: postRenderData.errors,
470
+ outputFile: postRenderData.outputFile,
471
+ timeToFinish: postRenderData.timeToFinish,
472
+ },
473
+ });
474
+ webhookInvoked = true;
475
+ }
476
+ catch (err) {
477
+ if (process.env.NODE_ENV === 'test') {
478
+ throw err;
479
+ }
480
+ await (0, write_lambda_error_1.writeLambdaError)({
481
+ bucketName: params.bucketName,
482
+ errorInfo: {
483
+ type: 'webhook',
484
+ message: err.message,
485
+ name: err.name,
486
+ stack: err.stack,
487
+ tmpDir: null,
488
+ frame: 0,
489
+ chunk: 0,
490
+ isFatal: false,
491
+ attempt: 1,
492
+ willRetry: false,
493
+ totalAttempts: 1,
494
+ },
495
+ renderId: params.renderId,
496
+ expectedBucketOwner: options.expectedBucketOwner,
497
+ });
498
+ console.log('Failed to invoke webhook:');
499
+ console.log(err);
500
+ }
501
+ }
412
502
  };
413
503
  const launchHandler = async (params, options) => {
504
+ var _a, _b;
414
505
  if (params.type !== constants_1.LambdaRoutines.launch) {
415
506
  throw new Error('Expected launch type');
416
507
  }
@@ -440,6 +531,50 @@ const launchHandler = async (params, options) => {
440
531
  expectedBucketOwner: options.expectedBucketOwner,
441
532
  renderId: params.renderId,
442
533
  });
534
+ if ((_a = params.webhook) === null || _a === void 0 ? void 0 : _a.url) {
535
+ try {
536
+ await (0, invoke_webhook_1.invokeWebhook)({
537
+ url: params.webhook.url,
538
+ secret: (_b = params.webhook.secret) !== null && _b !== void 0 ? _b : null,
539
+ payload: {
540
+ type: 'error',
541
+ renderId: params.renderId,
542
+ expectedBucketOwner: options.expectedBucketOwner,
543
+ bucketName: params.bucketName,
544
+ errors: [err].map((e) => ({
545
+ message: e.message,
546
+ name: e.name,
547
+ stack: e.stack,
548
+ })),
549
+ },
550
+ });
551
+ }
552
+ catch (error) {
553
+ if (process.env.NODE_ENV === 'test') {
554
+ throw error;
555
+ }
556
+ await (0, write_lambda_error_1.writeLambdaError)({
557
+ bucketName: params.bucketName,
558
+ errorInfo: {
559
+ type: 'webhook',
560
+ message: err.message,
561
+ name: err.name,
562
+ stack: err.stack,
563
+ tmpDir: null,
564
+ frame: 0,
565
+ chunk: 0,
566
+ isFatal: false,
567
+ attempt: 1,
568
+ willRetry: false,
569
+ totalAttempts: 1,
570
+ },
571
+ renderId: params.renderId,
572
+ expectedBucketOwner: options.expectedBucketOwner,
573
+ });
574
+ console.log('Failed to invoke webhook:');
575
+ console.log(error);
576
+ }
577
+ }
443
578
  }
444
579
  };
445
580
  exports.launchHandler = launchHandler;
@@ -136,6 +136,13 @@ const renderHandler = async (params, options, logs) => {
136
136
  downloadMap,
137
137
  muted: params.muted,
138
138
  enforceAudioTrack: true,
139
+ onSlowestFrames: (slowestFrames) => {
140
+ console.log();
141
+ console.log(`Slowest frames:`);
142
+ slowestFrames.forEach(({ frame, time }) => {
143
+ console.log(`Frame ${frame} (${time.toFixed(3)}ms)`);
144
+ });
145
+ },
139
146
  })
140
147
  .then(() => resolve())
141
148
  .catch((err) => reject(err));
@@ -63,6 +63,7 @@ const startHandler = async (params, options) => {
63
63
  downloadBehavior: params.downloadBehavior,
64
64
  muted: params.muted,
65
65
  overwrite: params.overwrite,
66
+ webhook: params.webhook,
66
67
  };
67
68
  await (0, aws_clients_1.getLambdaClient)((0, get_current_region_1.getCurrentRegionInFunction)()).send(new client_lambda_1.InvokeCommand({
68
69
  FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME,
package/dist/index.d.ts CHANGED
@@ -32,11 +32,13 @@ import type { RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput } from './api/
32
32
  import { renderMediaOnLambda, renderVideoOnLambda } from './api/render-media-on-lambda';
33
33
  import type { RenderStillOnLambdaInput, RenderStillOnLambdaOutput } from './api/render-still-on-lambda';
34
34
  import { renderStillOnLambda } from './api/render-still-on-lambda';
35
+ import { validateWebhookSignature } from './api/validate-webhook-signature';
35
36
  import type { LambdaLSInput, LambdaLsReturnType } from './functions/helpers/io';
36
37
  import { LambdaInternals } from './internals';
37
38
  import type { AwsRegion } from './pricing/aws-regions';
38
39
  import type { CustomCredentials } from './shared/aws-clients';
39
40
  import type { RenderProgress } from './shared/constants';
41
+ import type { WebhookPayload } from './shared/invoke-webhook';
40
42
  import type { LambdaArchitecture } from './shared/validate-architecture';
41
- export { deleteSite, deployFunction, deploySite, downloadMedia, downloadVideo, getFunctions, getUserPolicy, getRolePolicy, getSites, getOrCreateBucket, getRenderProgress, renderVideoOnLambda, renderMediaOnLambda, simulatePermissions, deleteFunction, getFunctionInfo, estimatePrice, LambdaInternals, renderStillOnLambda, getRegions, getAwsClient, presignUrl, deleteRender, };
42
- export type { AwsRegion, RenderProgress, DeploySiteInput, DeploySiteOutput, LambdaLsReturnType, LambdaLSInput, DeleteSiteInput, DeleteSiteOutput, EstimatePriceInput, DeployFunctionInput, DeployFunctionOutput, DeleteFunctionInput, GetFunctionInfoInput, FunctionInfo, GetFunctionsInput, GetSitesInput, GetSitesOutput, DownloadMediaInput, DownloadMediaOutput, GetOrCreateBucketInput, GetOrCreateBucketOutput, GetRenderInput, RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput, RenderStillOnLambdaInput, RenderStillOnLambdaOutput, SimulatePermissionsInput, SimulatePermissionsOutput, GetAwsClientInput, GetAwsClientOutput, LambdaArchitecture, CustomCredentials, };
43
+ export { deleteSite, deployFunction, deploySite, downloadMedia, downloadVideo, getFunctions, getUserPolicy, getRolePolicy, getSites, getOrCreateBucket, getRenderProgress, renderVideoOnLambda, renderMediaOnLambda, simulatePermissions, deleteFunction, getFunctionInfo, estimatePrice, LambdaInternals, renderStillOnLambda, getRegions, getAwsClient, presignUrl, deleteRender, validateWebhookSignature, };
44
+ export type { AwsRegion, RenderProgress, DeploySiteInput, DeploySiteOutput, LambdaLsReturnType, LambdaLSInput, DeleteSiteInput, DeleteSiteOutput, EstimatePriceInput, DeployFunctionInput, DeployFunctionOutput, DeleteFunctionInput, GetFunctionInfoInput, FunctionInfo, GetFunctionsInput, GetSitesInput, GetSitesOutput, DownloadMediaInput, DownloadMediaOutput, GetOrCreateBucketInput, GetOrCreateBucketOutput, GetRenderInput, RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput, RenderStillOnLambdaInput, RenderStillOnLambdaOutput, SimulatePermissionsInput, SimulatePermissionsOutput, GetAwsClientInput, GetAwsClientOutput, LambdaArchitecture, CustomCredentials, WebhookPayload, };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deleteRender = exports.presignUrl = exports.getAwsClient = exports.getRegions = exports.renderStillOnLambda = exports.LambdaInternals = exports.estimatePrice = exports.getFunctionInfo = exports.deleteFunction = exports.simulatePermissions = exports.renderMediaOnLambda = exports.renderVideoOnLambda = exports.getRenderProgress = exports.getOrCreateBucket = exports.getSites = exports.getRolePolicy = exports.getUserPolicy = exports.getFunctions = exports.downloadVideo = exports.downloadMedia = exports.deploySite = exports.deployFunction = exports.deleteSite = void 0;
3
+ exports.validateWebhookSignature = exports.deleteRender = exports.presignUrl = exports.getAwsClient = exports.getRegions = exports.renderStillOnLambda = exports.LambdaInternals = exports.estimatePrice = exports.getFunctionInfo = exports.deleteFunction = exports.simulatePermissions = exports.renderMediaOnLambda = exports.renderVideoOnLambda = exports.getRenderProgress = exports.getOrCreateBucket = exports.getSites = exports.getRolePolicy = exports.getUserPolicy = exports.getFunctions = exports.downloadVideo = exports.downloadMedia = exports.deploySite = exports.deployFunction = exports.deleteSite = void 0;
4
4
  const delete_function_1 = require("./api/delete-function");
5
5
  Object.defineProperty(exports, "deleteFunction", { enumerable: true, get: function () { return delete_function_1.deleteFunction; } });
6
6
  const delete_render_1 = require("./api/delete-render");
@@ -42,5 +42,7 @@ Object.defineProperty(exports, "renderMediaOnLambda", { enumerable: true, get: f
42
42
  Object.defineProperty(exports, "renderVideoOnLambda", { enumerable: true, get: function () { return render_media_on_lambda_1.renderVideoOnLambda; } });
43
43
  const render_still_on_lambda_1 = require("./api/render-still-on-lambda");
44
44
  Object.defineProperty(exports, "renderStillOnLambda", { enumerable: true, get: function () { return render_still_on_lambda_1.renderStillOnLambda; } });
45
+ const validate_webhook_signature_1 = require("./api/validate-webhook-signature");
46
+ Object.defineProperty(exports, "validateWebhookSignature", { enumerable: true, get: function () { return validate_webhook_signature_1.validateWebhookSignature; } });
45
47
  const internals_1 = require("./internals");
46
48
  Object.defineProperty(exports, "LambdaInternals", { enumerable: true, get: function () { return internals_1.LambdaInternals; } });
@@ -93,6 +93,10 @@ export declare enum LambdaRoutines {
93
93
  renderer = "renderer",
94
94
  still = "still"
95
95
  }
96
+ declare type WebhookOption = null | {
97
+ url: string;
98
+ secret: string | null;
99
+ };
96
100
  export declare type LambdaPayloads = {
97
101
  info: {
98
102
  type: LambdaRoutines.info;
@@ -125,6 +129,7 @@ export declare type LambdaPayloads = {
125
129
  muted: boolean;
126
130
  version: string;
127
131
  overwrite: boolean;
132
+ webhook: WebhookOption;
128
133
  };
129
134
  launch: {
130
135
  type: LambdaRoutines.launch;
@@ -155,6 +160,7 @@ export declare type LambdaPayloads = {
155
160
  downloadBehavior: DownloadBehavior;
156
161
  muted: boolean;
157
162
  overwrite: boolean;
163
+ webhook: WebhookOption;
158
164
  };
159
165
  status: {
160
166
  type: LambdaRoutines.status;
@@ -304,3 +310,4 @@ export declare type RenderProgress = {
304
310
  export declare type Privacy = 'public' | 'private' | 'no-acl';
305
311
  export declare const LAMBDA_CONCURRENCY_LIMIT_QUOTA = "L-B99A9384";
306
312
  export declare const LAMBDA_BURST_LIMIT_QUOTA = "L-548AE339";
313
+ export {};
@@ -0,0 +1,44 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import http from 'http';
4
+ import https from 'https';
5
+ import type { EnhancedErrorInfo } from '../functions/helpers/write-lambda-error';
6
+ /**
7
+ * @description Calculates cryptographically secure signature for webhooks using Hmac.
8
+ * @link https://remotion.dev/docs/lambda/webhooks#validate-webhooks
9
+ * @param payload Stringified request body to encode in the signature.
10
+ * @param secret User-provided webhook secret used to sign the request.
11
+ * @returns {string} Calculated signature
12
+ */
13
+ export declare function calculateSignature(payload: string, secret: string | null): string;
14
+ declare type DynamicWebhookPayload = {
15
+ type: 'error';
16
+ errors: {
17
+ message: string;
18
+ name: string;
19
+ stack: string;
20
+ }[];
21
+ } | {
22
+ type: 'success';
23
+ lambdaErrors: EnhancedErrorInfo[];
24
+ outputUrl: string | undefined;
25
+ outputFile: string | undefined;
26
+ timeToFinish: number | undefined;
27
+ } | {
28
+ type: 'timeout';
29
+ };
30
+ export declare type WebhookPayload = {
31
+ renderId: string;
32
+ expectedBucketOwner: string;
33
+ bucketName: string;
34
+ } & DynamicWebhookPayload;
35
+ export declare const mockableHttpClients: {
36
+ http: typeof http.request;
37
+ https: typeof https.request;
38
+ };
39
+ export declare function invokeWebhook({ payload, secret, url, }: {
40
+ payload: WebhookPayload;
41
+ url: string;
42
+ secret: string | null;
43
+ }): Promise<void>;
44
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.invokeWebhook = exports.mockableHttpClients = exports.calculateSignature = void 0;
30
+ const Crypto = __importStar(require("crypto"));
31
+ const http_1 = __importDefault(require("http"));
32
+ const https_1 = __importDefault(require("https"));
33
+ /**
34
+ * @description Calculates cryptographically secure signature for webhooks using Hmac.
35
+ * @link https://remotion.dev/docs/lambda/webhooks#validate-webhooks
36
+ * @param payload Stringified request body to encode in the signature.
37
+ * @param secret User-provided webhook secret used to sign the request.
38
+ * @returns {string} Calculated signature
39
+ */
40
+ function calculateSignature(payload, secret) {
41
+ if (!secret) {
42
+ return 'NO_SECRET_PROVIDED';
43
+ }
44
+ const hmac = Crypto.createHmac('sha512', secret);
45
+ const signature = 'sha512=' + hmac.update(payload).digest('hex');
46
+ return signature;
47
+ }
48
+ exports.calculateSignature = calculateSignature;
49
+ const getWebhookClient = (url) => {
50
+ if (url.startsWith('https://')) {
51
+ return exports.mockableHttpClients.https;
52
+ }
53
+ if (url.startsWith('http://')) {
54
+ return exports.mockableHttpClients.http;
55
+ }
56
+ throw new Error('Can only request URLs starting with http:// or https://');
57
+ };
58
+ exports.mockableHttpClients = {
59
+ http: http_1.default.request,
60
+ https: https_1.default.request,
61
+ };
62
+ function invokeWebhook({ payload, secret, url, }) {
63
+ const jsonPayload = JSON.stringify(payload);
64
+ return new Promise((resolve, reject) => {
65
+ const req = getWebhookClient(url)(url, {
66
+ method: 'POST',
67
+ headers: {
68
+ 'Content-Type': 'application/json',
69
+ 'Content-Length': jsonPayload.length,
70
+ 'X-Remotion-Mode': 'production',
71
+ 'X-Remotion-Signature': calculateSignature(jsonPayload, secret),
72
+ 'X-Remotion-Status': payload.type,
73
+ },
74
+ timeout: 5000,
75
+ }, (res) => {
76
+ if (res.statusCode && res.statusCode > 299) {
77
+ reject(new Error(`Sent a webhook but got a status code of ${res.statusCode} with message '${res.statusMessage}'`));
78
+ return;
79
+ }
80
+ resolve();
81
+ });
82
+ req.write(jsonPayload);
83
+ req.on('error', (err) => {
84
+ reject(err);
85
+ });
86
+ req.end();
87
+ });
88
+ }
89
+ exports.invokeWebhook = invokeWebhook;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/lambda",
3
- "version": "3.2.29",
3
+ "version": "3.2.31",
4
4
  "description": "Distributed renderer for Remotion based on AWS Lambda",
5
5
  "main": "dist/index.js",
6
6
  "sideEffects": false,
@@ -32,12 +32,12 @@
32
32
  "@aws-sdk/client-service-quotas": "3.58.0",
33
33
  "@aws-sdk/lib-storage": "3.58.0",
34
34
  "@aws-sdk/s3-request-presigner": "3.58.0",
35
- "@remotion/bundler": "3.2.29",
36
- "@remotion/cli": "3.2.29",
37
- "@remotion/renderer": "3.2.29",
35
+ "@remotion/bundler": "3.2.31",
36
+ "@remotion/cli": "3.2.31",
37
+ "@remotion/renderer": "3.2.31",
38
38
  "aws-policies": "^1.0.1",
39
39
  "mime-types": "2.1.34",
40
- "remotion": "3.2.29"
40
+ "remotion": "3.2.31"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "react": ">=16.8.0",
@@ -62,5 +62,5 @@
62
62
  "publishConfig": {
63
63
  "access": "public"
64
64
  },
65
- "gitHead": "9a2ff4d68324af8d1fe55ebdf94bdadccf446a10"
65
+ "gitHead": "f011b454d78903e548c32f548d8fef642c5ff7a6"
66
66
  }
Binary file
@@ -1,8 +0,0 @@
1
- import type { AwsRegion } from '../../client';
2
- import type { LambdaRoutines } from '../../shared/constants';
3
- export declare const getCloudwatchStreamUrl: ({ region, functionName, method, renderId, }: {
4
- region: AwsRegion;
5
- functionName: string;
6
- method: LambdaRoutines;
7
- renderId: string;
8
- }) => string;
@@ -1,7 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCloudwatchStreamUrl = void 0;
4
- const getCloudwatchStreamUrl = ({ region, functionName, method, renderId, }) => {
5
- return `https://${region}.console.aws.amazon.com/cloudwatch/home?region=${region}#logsV2:log-groups/log-group/$252Faws$252Flambda$252F${functionName}/log-events$3FfilterPattern$3D$2522method$253D${method}$252CrenderId$253D${renderId}$2522`;
6
- };
7
- exports.getCloudwatchStreamUrl = getCloudwatchStreamUrl;