@remotion/lambda-client 4.0.514 → 4.0.515

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,6 +1,7 @@
1
- import type { CallFunctionOptions, CloudProvider, OnMessage, ServerlessRoutines } from '@remotion/serverless-client';
1
+ import type { CallFunctionOptions, CloudProvider, GetBinaryPayloadSink, OnMessage, ServerlessRoutines } from '@remotion/serverless-client';
2
2
  export declare const parseJsonOrThrowSource: (data: Uint8Array<ArrayBufferLike>, type: string) => any;
3
3
  export declare const callFunctionWithStreamingImplementation: <Provider extends CloudProvider<string, Record<string, unknown>, Record<string, unknown>, string, object>, T extends ServerlessRoutines>(options: CallFunctionOptions<T, Provider> & {
4
4
  receivedStreamingPayload: OnMessage<Provider>;
5
5
  retriesRemaining: number;
6
+ getBinaryPayloadSink: GetBinaryPayloadSink | null;
6
7
  }) => Promise<void>;
@@ -0,0 +1,19 @@
1
+ import type { ProviderSpecifics } from '@remotion/serverless-client';
2
+ import { type AwsProvider } from './aws-provider';
3
+ import type { AwsRegion } from './regions';
4
+ import type { RequestHandler } from './types';
5
+ export type CancelRenderOnLambdaInput = {
6
+ region: AwsRegion;
7
+ bucketName: string;
8
+ renderId: string;
9
+ forcePathStyle?: boolean;
10
+ requestHandler?: RequestHandler;
11
+ };
12
+ type InternalCancelRenderOnLambdaInput = Omit<CancelRenderOnLambdaInput, 'forcePathStyle' | 'requestHandler'> & {
13
+ forcePathStyle: boolean;
14
+ requestHandler: RequestHandler | null;
15
+ providerSpecifics: ProviderSpecifics<AwsProvider>;
16
+ };
17
+ export declare const internalCancelRenderOnLambda: (input: InternalCancelRenderOnLambdaInput) => Promise<void>;
18
+ export declare const cancelRenderOnLambda: (input: CancelRenderOnLambdaInput) => Promise<void>;
19
+ export {};
@@ -4,6 +4,7 @@ import { getCloudWatchLogsClient, getIamClient, getLambdaClient, getServiceQuota
4
4
  import { awsImplementation } from './aws-provider';
5
5
  import { getAwsPartitionMetadata, getAwsRegionMetadata } from './aws-region-metadata';
6
6
  import { parseJsonOrThrowSource } from './call-lambda-streaming';
7
+ import { internalCancelRenderOnLambda } from './cancel-render-on-lambda';
7
8
  import { cleanItems } from './clean-items';
8
9
  import { DEFAULT_CLOUDWATCH_RETENTION_PERIOD, DEFAULT_EPHEMERAL_STORAGE_IN_MB, MAX_EPHEMERAL_STORAGE_IN_MB } from './constants';
9
10
  import { convertToServeUrlImplementation } from './convert-to-serve-url';
@@ -32,6 +33,7 @@ import { validateServeUrl } from './validate-serveurl';
32
33
  export type { CustomCredentials, DeleteAfter, WebhookErrorPayload, WebhookPayload, WebhookSuccessPayload, WebhookTimeoutPayload, } from '@remotion/serverless-client';
33
34
  export { appRouterWebhook, NextWebhookArgs } from './app-router-webhook';
34
35
  export { AwsProvider } from './aws-provider';
36
+ export { cancelRenderOnLambda, type CancelRenderOnLambdaInput, } from './cancel-render-on-lambda';
35
37
  export type { AwsBillingCurrency, AwsPartition, AwsRegionMetadata, } from './aws-region-metadata';
36
38
  export type { RenderProgress } from './constants';
37
39
  export { deleteFunction, DeleteFunctionInput } from './delete-function';
@@ -76,6 +78,7 @@ export declare const LambdaClientInternals: {
76
78
  makeLambdaRenderMediaPayload: typeof makeLambdaRenderMediaPayload;
77
79
  renderMediaOnLambdaOptionalToRequired: typeof renderMediaOnLambdaOptionalToRequired;
78
80
  internalDeleteRender: typeof internalDeleteRender;
81
+ internalCancelRenderOnLambda: typeof internalCancelRenderOnLambda;
79
82
  internalGetSites: typeof internalGetSites;
80
83
  getLifeCycleRules: typeof getLifeCycleRules;
81
84
  awsImplementation: typeof awsImplementation;
package/dist/cjs/index.js CHANGED
@@ -69842,6 +69842,7 @@ __export(exports_src, {
69842
69842
  estimatePrice: () => estimatePrice,
69843
69843
  deleteRender: () => deleteRender,
69844
69844
  deleteFunction: () => deleteFunction,
69845
+ cancelRenderOnLambda: () => cancelRenderOnLambda,
69845
69846
  appRouterWebhook: () => appRouterWebhook,
69846
69847
  LambdaClientInternals: () => LambdaClientInternals
69847
69848
  });
@@ -72177,6 +72178,7 @@ var customOutName = (renderId, bucketName, name) => {
72177
72178
  };
72178
72179
  };
72179
72180
  var overallProgressKey = (renderId) => `${rendersPrefix(renderId)}/progress.json`;
72181
+ var cancellationKey = (renderId) => `${rendersPrefix(renderId)}/cancel.json`;
72180
72182
  var MAX_FUNCTIONS_PER_RENDER = 200;
72181
72183
  var validateDownloadBehavior = (downloadBehavior) => {
72182
72184
  if (downloadBehavior === null || downloadBehavior === undefined) {
@@ -72195,7 +72197,7 @@ var validateDownloadBehavior = (downloadBehavior) => {
72195
72197
  }
72196
72198
  }
72197
72199
  };
72198
- var VERSION = "4.0.514";
72200
+ var VERSION = "4.0.515";
72199
72201
  var isColorSupported = () => {
72200
72202
  const env = process.env || {};
72201
72203
  const isForceDisabled = "NO_COLOR" in env;
@@ -72549,7 +72551,7 @@ var wrapWithErrorHandling = (fn) => {
72549
72551
  };
72550
72552
  };
72551
72553
  var streamingKey = "remotion_buffer:";
72552
- var makeStreamer = (onMessage) => {
72554
+ var makeStreamer = (onMessage, getPayloadSink = null) => {
72553
72555
  const separator = new Uint8Array(streamingKey.length);
72554
72556
  for (let i = 0;i < streamingKey.length; i++) {
72555
72557
  separator[i] = streamingKey.charCodeAt(i);
@@ -72557,6 +72559,7 @@ var makeStreamer = (onMessage) => {
72557
72559
  let unprocessedBuffers = [];
72558
72560
  let outputBuffer = new Uint8Array(0);
72559
72561
  let missingData = null;
72562
+ let activeSink = null;
72560
72563
  const findSeparatorIndex = () => {
72561
72564
  let searchIndex = 0;
72562
72565
  while (true) {
@@ -72615,6 +72618,36 @@ var makeStreamer = (onMessage) => {
72615
72618
  }
72616
72619
  const length = Number(lengthString);
72617
72620
  const status = Number(statusString);
72621
+ const statusType = status === 1 ? "error" : "success";
72622
+ const sink = getPayloadSink ? getPayloadSink(statusType, nonceString, length) : null;
72623
+ if (sink) {
72624
+ const payloadStart = separatorIndex + 1;
72625
+ const available = Math.min(outputBuffer.length - payloadStart, length);
72626
+ const payloadPiece = outputBuffer.subarray(payloadStart, payloadStart + available);
72627
+ const rest = new Uint8Array(outputBuffer.subarray(payloadStart + available));
72628
+ outputBuffer = new Uint8Array(0);
72629
+ missingData = null;
72630
+ return (async () => {
72631
+ if (available > 0) {
72632
+ await sink.write(payloadPiece);
72633
+ }
72634
+ if (available < length) {
72635
+ activeSink = {
72636
+ sink,
72637
+ remaining: length - available,
72638
+ nonce: nonceString,
72639
+ statusType
72640
+ };
72641
+ return;
72642
+ }
72643
+ await sink.end();
72644
+ onMessage(statusType, nonceString, new Uint8Array(0));
72645
+ if (rest.length > 0) {
72646
+ outputBuffer = rest;
72647
+ return processInput();
72648
+ }
72649
+ })();
72650
+ }
72618
72651
  const dataLength = outputBuffer.length - separatorIndex - 1;
72619
72652
  if (dataLength < length) {
72620
72653
  missingData = {
@@ -72626,9 +72659,33 @@ var makeStreamer = (onMessage) => {
72626
72659
  onMessage(status === 1 ? "error" : "success", nonceString, data);
72627
72660
  missingData = null;
72628
72661
  outputBuffer = outputBuffer.subarray(separatorIndex + Number(lengthString) + 1);
72629
- processInput();
72662
+ return processInput();
72630
72663
  };
72631
- const onData = (data) => {
72664
+ const feedSink = async (data) => {
72665
+ if (!activeSink) {
72666
+ throw new Error("No active sink");
72667
+ }
72668
+ const take = Math.min(activeSink.remaining, data.length);
72669
+ await activeSink.sink.write(data.subarray(0, take));
72670
+ activeSink.remaining -= take;
72671
+ if (activeSink.remaining > 0) {
72672
+ return null;
72673
+ }
72674
+ const { sink, nonce, statusType } = activeSink;
72675
+ activeSink = null;
72676
+ await sink.end();
72677
+ onMessage(statusType, nonce, new Uint8Array(0));
72678
+ const leftover = data.subarray(take);
72679
+ return leftover.length > 0 ? leftover : null;
72680
+ };
72681
+ const onDataInner = (data) => {
72682
+ if (activeSink) {
72683
+ return feedSink(data).then((leftover) => {
72684
+ if (leftover) {
72685
+ return onDataInner(leftover);
72686
+ }
72687
+ });
72688
+ }
72632
72689
  unprocessedBuffers.push(data);
72633
72690
  if (missingData) {
72634
72691
  missingData.dataMissing -= data.length;
@@ -72645,7 +72702,15 @@ var makeStreamer = (onMessage) => {
72645
72702
  }
72646
72703
  outputBuffer = newBuffer;
72647
72704
  unprocessedBuffers = [];
72648
- processInput();
72705
+ return processInput();
72706
+ };
72707
+ let chain = Promise.resolve();
72708
+ const onData = (data) => {
72709
+ if (!getPayloadSink) {
72710
+ return onDataInner(data);
72711
+ }
72712
+ chain = chain.then(() => onDataInner(data));
72713
+ return chain;
72649
72714
  };
72650
72715
  return {
72651
72716
  onData,
@@ -72653,6 +72718,15 @@ var makeStreamer = (onMessage) => {
72653
72718
  clear: () => {
72654
72719
  unprocessedBuffers = [];
72655
72720
  outputBuffer = new Uint8Array(0);
72721
+ if (activeSink) {
72722
+ const { sink } = activeSink;
72723
+ activeSink = null;
72724
+ try {
72725
+ Promise.resolve(sink.end()).catch(() => {
72726
+ return;
72727
+ });
72728
+ } catch {}
72729
+ }
72656
72730
  }
72657
72731
  };
72658
72732
  };
@@ -74192,6 +74266,41 @@ var messageTypeIdToMessageType = (messageTypeId) => {
74192
74266
  }
74193
74267
  return types.type;
74194
74268
  };
74269
+ var binaryPayloadSinkForStreamer = (getBinaryPayloadSink) => {
74270
+ return (_statusType, nonce, length) => {
74271
+ const messageType = messageTypeIdToMessageType(nonce);
74272
+ if (formatMap[messageType] !== "binary") {
74273
+ return null;
74274
+ }
74275
+ return getBinaryPayloadSink({
74276
+ messageType,
74277
+ length
74278
+ });
74279
+ };
74280
+ };
74281
+ var writeCancellationSignal = async ({
74282
+ bucketName,
74283
+ renderId,
74284
+ region,
74285
+ expectedBucketOwner,
74286
+ providerSpecifics,
74287
+ forcePathStyle,
74288
+ requestHandler
74289
+ }) => {
74290
+ await providerSpecifics.writeFile({
74291
+ bucketName,
74292
+ key: cancellationKey(renderId),
74293
+ body: JSON.stringify({ cancelledAt: Date.now() }),
74294
+ region,
74295
+ privacy: "private",
74296
+ expectedBucketOwner,
74297
+ downloadBehavior: null,
74298
+ customCredentials: null,
74299
+ forcePathStyle,
74300
+ storageClass: null,
74301
+ requestHandler
74302
+ });
74303
+ };
74195
74304
  var ENABLE_V5_BREAKING_CHANGES2 = NoReactInternals.ENABLE_V5_BREAKING_CHANGES;
74196
74305
  var serializeJSONWithSpecialTypes2 = NoReactInternals.serializeJSONWithSpecialTypes;
74197
74306
  var validateFps2 = NoReactInternals.validateFps;
@@ -74663,7 +74772,8 @@ var callLambdaWithStreamingWithoutRetry = async ({
74663
74772
  region,
74664
74773
  timeoutInTest,
74665
74774
  receivedStreamingPayload,
74666
- requestHandler
74775
+ requestHandler,
74776
+ getBinaryPayloadSink
74667
74777
  }) => {
74668
74778
  const res = await invokeStreamOrTimeout({
74669
74779
  functionName,
@@ -74684,7 +74794,7 @@ var callLambdaWithStreamingWithoutRetry = async ({
74684
74794
  }
74685
74795
  };
74686
74796
  receivedStreamingPayload(message);
74687
- });
74797
+ }, getBinaryPayloadSink ? binaryPayloadSinkForStreamer(getBinaryPayloadSink) : null);
74688
74798
  const dumpBuffers = () => {
74689
74799
  clear();
74690
74800
  };
@@ -74692,25 +74802,28 @@ var callLambdaWithStreamingWithoutRetry = async ({
74692
74802
  globalThis._dumpUnreleasedBuffers.addListener("dump-unreleased-buffers", dumpBuffers);
74693
74803
  }
74694
74804
  const events = res.EventStream;
74695
- for await (const event of events) {
74696
- if (event.PayloadChunk && event.PayloadChunk.Payload) {
74697
- onData(event.PayloadChunk.Payload);
74698
- }
74699
- if (event.InvokeComplete) {
74700
- if (event.InvokeComplete.ErrorCode) {
74701
- const { consoleDomain } = getAwsRegionMetadata(region);
74702
- const logs = `https://${region}.${consoleDomain}/cloudwatch/home?region=${region}#logsV2:logs-insights$3FqueryDetail$3D~(end~0~start~-3600~timeType~'RELATIVE~unit~'seconds~editorString~'fields*20*40timestamp*2c*20*40requestId*2c*20*40message*0a*7c*20filter*20*40requestId*20like*20*${res.$metadata.requestId}*22*0a*7c*20sort*20*40timestamp*20asc~source~(~'*2faws*2flambda*2f${functionName}))`;
74703
- if (event.InvokeComplete.ErrorCode === "Unhandled") {
74704
- throw new Error(`Lambda function ${functionName} failed with an unhandled error: ${event.InvokeComplete.ErrorDetails} See ${logs} to see the logs of this invocation.`);
74805
+ try {
74806
+ for await (const event of events) {
74807
+ if (event.PayloadChunk && event.PayloadChunk.Payload) {
74808
+ await onData(event.PayloadChunk.Payload);
74809
+ }
74810
+ if (event.InvokeComplete) {
74811
+ if (event.InvokeComplete.ErrorCode) {
74812
+ const { consoleDomain } = getAwsRegionMetadata(region);
74813
+ const logs = `https://${region}.${consoleDomain}/cloudwatch/home?region=${region}#logsV2:logs-insights$3FqueryDetail$3D~(end~0~start~-3600~timeType~'RELATIVE~unit~'seconds~editorString~'fields*20*40timestamp*2c*20*40requestId*2c*20*40message*0a*7c*20filter*20*40requestId*20like*20*${res.$metadata.requestId}*22*0a*7c*20sort*20*40timestamp*20asc~source~(~'*2faws*2flambda*2f${functionName}))`;
74814
+ if (event.InvokeComplete.ErrorCode === "Unhandled") {
74815
+ throw new Error(`Lambda function ${functionName} failed with an unhandled error: ${event.InvokeComplete.ErrorDetails} See ${logs} to see the logs of this invocation.`);
74816
+ }
74817
+ throw new Error(`Lambda function ${functionName} failed with error code ${event.InvokeComplete.ErrorCode}: ${event.InvokeComplete.ErrorDetails}. See ${logs} to see the logs of this invocation.`);
74705
74818
  }
74706
- throw new Error(`Lambda function ${functionName} failed with error code ${event.InvokeComplete.ErrorCode}: ${event.InvokeComplete.ErrorDetails}. See ${logs} to see the logs of this invocation.`);
74707
74819
  }
74708
74820
  }
74821
+ } finally {
74822
+ if (globalThis._dumpUnreleasedBuffers) {
74823
+ globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74824
+ }
74825
+ clear();
74709
74826
  }
74710
- if (globalThis._dumpUnreleasedBuffers) {
74711
- globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74712
- }
74713
- clear();
74714
74827
  };
74715
74828
  var callFunctionWithStreamingImplementation = async (options) => {
74716
74829
  try {
@@ -76611,6 +76724,42 @@ var awsImplementation = {
76611
76724
  getBucketPrefix: () => REMOTION_BUCKET_PREFIX
76612
76725
  };
76613
76726
 
76727
+ // src/cancel-render-on-lambda.ts
76728
+ var internalCancelRenderOnLambda = async (input) => {
76729
+ const expectedBucketOwner = await input.providerSpecifics.getAccountId({
76730
+ region: input.region
76731
+ });
76732
+ const progress = await getOverallProgressFromStorage({
76733
+ bucketName: input.bucketName,
76734
+ expectedBucketOwner,
76735
+ region: input.region,
76736
+ renderId: input.renderId,
76737
+ providerSpecifics: input.providerSpecifics,
76738
+ forcePathStyle: input.forcePathStyle,
76739
+ requestHandler: input.requestHandler
76740
+ });
76741
+ if (progress.cancellationEnabled !== true) {
76742
+ throw new Error(`Cannot cancel render ${input.renderId}: The render was not started with enableCancellation: true.`);
76743
+ }
76744
+ await writeCancellationSignal({
76745
+ bucketName: input.bucketName,
76746
+ renderId: input.renderId,
76747
+ region: input.region,
76748
+ expectedBucketOwner,
76749
+ providerSpecifics: input.providerSpecifics,
76750
+ forcePathStyle: input.forcePathStyle,
76751
+ requestHandler: input.requestHandler
76752
+ });
76753
+ };
76754
+ var cancelRenderOnLambda = (input) => {
76755
+ return internalCancelRenderOnLambda({
76756
+ ...input,
76757
+ forcePathStyle: input.forcePathStyle ?? false,
76758
+ requestHandler: input.requestHandler ?? null,
76759
+ providerSpecifics: awsImplementation
76760
+ });
76761
+ };
76762
+
76614
76763
  // src/p-limit.ts
76615
76764
  var pLimit = (concurrency) => {
76616
76765
  const queue = [];
@@ -76907,6 +77056,7 @@ var validateServeUrl = (serveUrl) => {
76907
77056
 
76908
77057
  // src/make-lambda-payload.ts
76909
77058
  var makeLambdaRenderMediaPayload = async ({
77059
+ enableCancellation,
76910
77060
  rendererFunctionName,
76911
77061
  frameRange,
76912
77062
  framesPerLambda,
@@ -76987,6 +77137,7 @@ var makeLambdaRenderMediaPayload = async ({
76987
77137
  logLevel
76988
77138
  });
76989
77139
  return {
77140
+ enableCancellation,
76990
77141
  rendererFunctionName,
76991
77142
  framesPerLambda,
76992
77143
  concurrency,
@@ -77201,6 +77352,7 @@ var internalRenderMediaOnLambdaRaw = async (input) => {
77201
77352
  };
77202
77353
  var renderMediaOnLambdaOptionalToRequired = (options) => {
77203
77354
  return {
77355
+ enableCancellation: options.enableCancellation ?? false,
77204
77356
  offthreadVideoThreads: options.offthreadVideoThreads ?? null,
77205
77357
  audioBitrate: options.audioBitrate ?? null,
77206
77358
  audioCodec: options.audioCodec ?? null,
@@ -77308,6 +77460,7 @@ var innerRenderStillOnLambda = async (input) => {
77308
77460
  },
77309
77461
  timeoutInTest: 120000,
77310
77462
  retriesRemaining: input.maxRetries,
77463
+ getBinaryPayloadSink: null,
77311
77464
  requestHandler: input.requestHandler
77312
77465
  }).then(() => {
77313
77466
  reject(new Error("Expected response to be streamed"));
@@ -77854,6 +78007,7 @@ var LambdaClientInternals = {
77854
78007
  makeLambdaRenderMediaPayload,
77855
78008
  renderMediaOnLambdaOptionalToRequired,
77856
78009
  internalDeleteRender,
78010
+ internalCancelRenderOnLambda,
77857
78011
  internalGetSites,
77858
78012
  getLifeCycleRules,
77859
78013
  awsImplementation,
@@ -7,6 +7,7 @@ import type { AwsRegion } from './regions';
7
7
  import type { RenderStillOnLambdaNonNullInput } from './render-still-on-lambda';
8
8
  import type { RequestHandler } from './types';
9
9
  export type InnerRenderMediaOnLambdaInput = {
10
+ enableCancellation: boolean;
10
11
  region: AwsRegion;
11
12
  functionName: string;
12
13
  serveUrl: string;
@@ -58,7 +59,7 @@ export type InnerRenderMediaOnLambdaInput = {
58
59
  requestHandler: RequestHandler | null;
59
60
  isProduction: boolean | null;
60
61
  } & ToOptions<Omit<typeof BrowserSafeApis.optionsMap.renderMediaOnLambda, 'apiKey'>>;
61
- export declare const makeLambdaRenderMediaPayload: ({ rendererFunctionName, frameRange, framesPerLambda, concurrency, forceBucketName: bucketName, codec, composition, serveUrl, imageFormat, inputProps, region, crf, envVariables, pixelFormat, proResProfile, x264Preset, gopSize, maxRetries, privacy, logLevel, outName, timeoutInMilliseconds, chromiumOptions, scale, everyNthFrame, numberOfGifLoops, audioBitrate, concurrencyPerLambda, audioCodec, forceHeight, forceWidth, forceFps, forceDurationInFrames, webhook, videoBitrate, encodingMaxRate, encodingBufferSize, downloadBehavior, muted, overwrite, jpegQuality, offthreadVideoCacheSizeInBytes, mediaCacheSizeInBytes, deleteAfter, colorSpace, preferLossless, forcePathStyle, metadata, licenseKey, offthreadVideoThreads, storageClass, requestHandler, isProduction, sampleRate, }: InnerRenderMediaOnLambdaInput) => Promise<ServerlessStartPayload<AwsProvider>>;
62
+ export declare const makeLambdaRenderMediaPayload: ({ enableCancellation, rendererFunctionName, frameRange, framesPerLambda, concurrency, forceBucketName: bucketName, codec, composition, serveUrl, imageFormat, inputProps, region, crf, envVariables, pixelFormat, proResProfile, x264Preset, gopSize, maxRetries, privacy, logLevel, outName, timeoutInMilliseconds, chromiumOptions, scale, everyNthFrame, numberOfGifLoops, audioBitrate, concurrencyPerLambda, audioCodec, forceHeight, forceWidth, forceFps, forceDurationInFrames, webhook, videoBitrate, encodingMaxRate, encodingBufferSize, downloadBehavior, muted, overwrite, jpegQuality, offthreadVideoCacheSizeInBytes, mediaCacheSizeInBytes, deleteAfter, colorSpace, preferLossless, forcePathStyle, metadata, licenseKey, offthreadVideoThreads, storageClass, requestHandler, isProduction, sampleRate, }: InnerRenderMediaOnLambdaInput) => Promise<ServerlessStartPayload<AwsProvider>>;
62
63
  export declare const getRenderProgressPayload: ({ bucketName, renderId, s3OutputProvider, logLevel, forcePathStyle, }: GetRenderProgressInput) => ServerlessStatusPayload<AwsProvider>;
63
64
  export declare const makeLambdaRenderStillPayload: ({ serveUrl, inputProps, imageFormat, envVariables, jpegQuality, region, maxRetries, composition, privacy, frame, logLevel, outName, timeoutInMilliseconds, chromiumOptions, scale, downloadBehavior, forceHeight, forceWidth, forceFps, forceDurationInFrames, forceBucketName, offthreadVideoCacheSizeInBytes, deleteAfter, forcePathStyle, licenseKey, storageClass, requestHandler, offthreadVideoThreads, mediaCacheSizeInBytes, isProduction, }: RenderStillOnLambdaNonNullInput) => Promise<{
64
65
  type: ServerlessRoutines.still;
@@ -5,6 +5,7 @@ import type { InnerRenderMediaOnLambdaInput } from './make-lambda-payload';
5
5
  import type { AwsRegion } from './regions';
6
6
  import type { RequestHandler } from './types';
7
7
  export type RenderMediaOnLambdaInput = {
8
+ enableCancellation?: boolean;
8
9
  region: AwsRegion;
9
10
  functionName: string;
10
11
  serveUrl: string;
@@ -71520,6 +71520,7 @@ var customOutName = (renderId, bucketName, name) => {
71520
71520
  };
71521
71521
  };
71522
71522
  var overallProgressKey = (renderId) => `${rendersPrefix(renderId)}/progress.json`;
71523
+ var cancellationKey = (renderId) => `${rendersPrefix(renderId)}/cancel.json`;
71523
71524
  var MAX_FUNCTIONS_PER_RENDER = 200;
71524
71525
  var validateDownloadBehavior = (downloadBehavior) => {
71525
71526
  if (downloadBehavior === null || downloadBehavior === undefined) {
@@ -71538,7 +71539,7 @@ var validateDownloadBehavior = (downloadBehavior) => {
71538
71539
  }
71539
71540
  }
71540
71541
  };
71541
- var VERSION = "4.0.514";
71542
+ var VERSION = "4.0.515";
71542
71543
  var isColorSupported = () => {
71543
71544
  const env = process.env || {};
71544
71545
  const isForceDisabled = "NO_COLOR" in env;
@@ -71892,7 +71893,7 @@ var wrapWithErrorHandling = (fn) => {
71892
71893
  };
71893
71894
  };
71894
71895
  var streamingKey = "remotion_buffer:";
71895
- var makeStreamer = (onMessage) => {
71896
+ var makeStreamer = (onMessage, getPayloadSink = null) => {
71896
71897
  const separator = new Uint8Array(streamingKey.length);
71897
71898
  for (let i = 0;i < streamingKey.length; i++) {
71898
71899
  separator[i] = streamingKey.charCodeAt(i);
@@ -71900,6 +71901,7 @@ var makeStreamer = (onMessage) => {
71900
71901
  let unprocessedBuffers = [];
71901
71902
  let outputBuffer = new Uint8Array(0);
71902
71903
  let missingData = null;
71904
+ let activeSink = null;
71903
71905
  const findSeparatorIndex = () => {
71904
71906
  let searchIndex = 0;
71905
71907
  while (true) {
@@ -71958,6 +71960,36 @@ var makeStreamer = (onMessage) => {
71958
71960
  }
71959
71961
  const length = Number(lengthString);
71960
71962
  const status = Number(statusString);
71963
+ const statusType = status === 1 ? "error" : "success";
71964
+ const sink = getPayloadSink ? getPayloadSink(statusType, nonceString, length) : null;
71965
+ if (sink) {
71966
+ const payloadStart = separatorIndex + 1;
71967
+ const available = Math.min(outputBuffer.length - payloadStart, length);
71968
+ const payloadPiece = outputBuffer.subarray(payloadStart, payloadStart + available);
71969
+ const rest = new Uint8Array(outputBuffer.subarray(payloadStart + available));
71970
+ outputBuffer = new Uint8Array(0);
71971
+ missingData = null;
71972
+ return (async () => {
71973
+ if (available > 0) {
71974
+ await sink.write(payloadPiece);
71975
+ }
71976
+ if (available < length) {
71977
+ activeSink = {
71978
+ sink,
71979
+ remaining: length - available,
71980
+ nonce: nonceString,
71981
+ statusType
71982
+ };
71983
+ return;
71984
+ }
71985
+ await sink.end();
71986
+ onMessage(statusType, nonceString, new Uint8Array(0));
71987
+ if (rest.length > 0) {
71988
+ outputBuffer = rest;
71989
+ return processInput();
71990
+ }
71991
+ })();
71992
+ }
71961
71993
  const dataLength = outputBuffer.length - separatorIndex - 1;
71962
71994
  if (dataLength < length) {
71963
71995
  missingData = {
@@ -71969,9 +72001,33 @@ var makeStreamer = (onMessage) => {
71969
72001
  onMessage(status === 1 ? "error" : "success", nonceString, data);
71970
72002
  missingData = null;
71971
72003
  outputBuffer = outputBuffer.subarray(separatorIndex + Number(lengthString) + 1);
71972
- processInput();
72004
+ return processInput();
71973
72005
  };
71974
- const onData = (data) => {
72006
+ const feedSink = async (data) => {
72007
+ if (!activeSink) {
72008
+ throw new Error("No active sink");
72009
+ }
72010
+ const take = Math.min(activeSink.remaining, data.length);
72011
+ await activeSink.sink.write(data.subarray(0, take));
72012
+ activeSink.remaining -= take;
72013
+ if (activeSink.remaining > 0) {
72014
+ return null;
72015
+ }
72016
+ const { sink, nonce, statusType } = activeSink;
72017
+ activeSink = null;
72018
+ await sink.end();
72019
+ onMessage(statusType, nonce, new Uint8Array(0));
72020
+ const leftover = data.subarray(take);
72021
+ return leftover.length > 0 ? leftover : null;
72022
+ };
72023
+ const onDataInner = (data) => {
72024
+ if (activeSink) {
72025
+ return feedSink(data).then((leftover) => {
72026
+ if (leftover) {
72027
+ return onDataInner(leftover);
72028
+ }
72029
+ });
72030
+ }
71975
72031
  unprocessedBuffers.push(data);
71976
72032
  if (missingData) {
71977
72033
  missingData.dataMissing -= data.length;
@@ -71988,7 +72044,15 @@ var makeStreamer = (onMessage) => {
71988
72044
  }
71989
72045
  outputBuffer = newBuffer;
71990
72046
  unprocessedBuffers = [];
71991
- processInput();
72047
+ return processInput();
72048
+ };
72049
+ let chain = Promise.resolve();
72050
+ const onData = (data) => {
72051
+ if (!getPayloadSink) {
72052
+ return onDataInner(data);
72053
+ }
72054
+ chain = chain.then(() => onDataInner(data));
72055
+ return chain;
71992
72056
  };
71993
72057
  return {
71994
72058
  onData,
@@ -71996,6 +72060,15 @@ var makeStreamer = (onMessage) => {
71996
72060
  clear: () => {
71997
72061
  unprocessedBuffers = [];
71998
72062
  outputBuffer = new Uint8Array(0);
72063
+ if (activeSink) {
72064
+ const { sink } = activeSink;
72065
+ activeSink = null;
72066
+ try {
72067
+ Promise.resolve(sink.end()).catch(() => {
72068
+ return;
72069
+ });
72070
+ } catch {}
72071
+ }
71999
72072
  }
72000
72073
  };
72001
72074
  };
@@ -73535,6 +73608,41 @@ var messageTypeIdToMessageType = (messageTypeId) => {
73535
73608
  }
73536
73609
  return types.type;
73537
73610
  };
73611
+ var binaryPayloadSinkForStreamer = (getBinaryPayloadSink) => {
73612
+ return (_statusType, nonce, length) => {
73613
+ const messageType = messageTypeIdToMessageType(nonce);
73614
+ if (formatMap[messageType] !== "binary") {
73615
+ return null;
73616
+ }
73617
+ return getBinaryPayloadSink({
73618
+ messageType,
73619
+ length
73620
+ });
73621
+ };
73622
+ };
73623
+ var writeCancellationSignal = async ({
73624
+ bucketName,
73625
+ renderId,
73626
+ region,
73627
+ expectedBucketOwner,
73628
+ providerSpecifics,
73629
+ forcePathStyle,
73630
+ requestHandler
73631
+ }) => {
73632
+ await providerSpecifics.writeFile({
73633
+ bucketName,
73634
+ key: cancellationKey(renderId),
73635
+ body: JSON.stringify({ cancelledAt: Date.now() }),
73636
+ region,
73637
+ privacy: "private",
73638
+ expectedBucketOwner,
73639
+ downloadBehavior: null,
73640
+ customCredentials: null,
73641
+ forcePathStyle,
73642
+ storageClass: null,
73643
+ requestHandler
73644
+ });
73645
+ };
73538
73646
  var ENABLE_V5_BREAKING_CHANGES2 = NoReactInternals.ENABLE_V5_BREAKING_CHANGES;
73539
73647
  var serializeJSONWithSpecialTypes2 = NoReactInternals.serializeJSONWithSpecialTypes;
73540
73648
  var validateFps2 = NoReactInternals.validateFps;
@@ -74006,7 +74114,8 @@ var callLambdaWithStreamingWithoutRetry = async ({
74006
74114
  region,
74007
74115
  timeoutInTest,
74008
74116
  receivedStreamingPayload,
74009
- requestHandler
74117
+ requestHandler,
74118
+ getBinaryPayloadSink
74010
74119
  }) => {
74011
74120
  const res = await invokeStreamOrTimeout({
74012
74121
  functionName,
@@ -74027,7 +74136,7 @@ var callLambdaWithStreamingWithoutRetry = async ({
74027
74136
  }
74028
74137
  };
74029
74138
  receivedStreamingPayload(message);
74030
- });
74139
+ }, getBinaryPayloadSink ? binaryPayloadSinkForStreamer(getBinaryPayloadSink) : null);
74031
74140
  const dumpBuffers = () => {
74032
74141
  clear();
74033
74142
  };
@@ -74035,25 +74144,28 @@ var callLambdaWithStreamingWithoutRetry = async ({
74035
74144
  globalThis._dumpUnreleasedBuffers.addListener("dump-unreleased-buffers", dumpBuffers);
74036
74145
  }
74037
74146
  const events = res.EventStream;
74038
- for await (const event of events) {
74039
- if (event.PayloadChunk && event.PayloadChunk.Payload) {
74040
- onData(event.PayloadChunk.Payload);
74041
- }
74042
- if (event.InvokeComplete) {
74043
- if (event.InvokeComplete.ErrorCode) {
74044
- const { consoleDomain } = getAwsRegionMetadata(region);
74045
- const logs = `https://${region}.${consoleDomain}/cloudwatch/home?region=${region}#logsV2:logs-insights$3FqueryDetail$3D~(end~0~start~-3600~timeType~'RELATIVE~unit~'seconds~editorString~'fields*20*40timestamp*2c*20*40requestId*2c*20*40message*0a*7c*20filter*20*40requestId*20like*20*${res.$metadata.requestId}*22*0a*7c*20sort*20*40timestamp*20asc~source~(~'*2faws*2flambda*2f${functionName}))`;
74046
- if (event.InvokeComplete.ErrorCode === "Unhandled") {
74047
- throw new Error(`Lambda function ${functionName} failed with an unhandled error: ${event.InvokeComplete.ErrorDetails} See ${logs} to see the logs of this invocation.`);
74147
+ try {
74148
+ for await (const event of events) {
74149
+ if (event.PayloadChunk && event.PayloadChunk.Payload) {
74150
+ await onData(event.PayloadChunk.Payload);
74151
+ }
74152
+ if (event.InvokeComplete) {
74153
+ if (event.InvokeComplete.ErrorCode) {
74154
+ const { consoleDomain } = getAwsRegionMetadata(region);
74155
+ const logs = `https://${region}.${consoleDomain}/cloudwatch/home?region=${region}#logsV2:logs-insights$3FqueryDetail$3D~(end~0~start~-3600~timeType~'RELATIVE~unit~'seconds~editorString~'fields*20*40timestamp*2c*20*40requestId*2c*20*40message*0a*7c*20filter*20*40requestId*20like*20*${res.$metadata.requestId}*22*0a*7c*20sort*20*40timestamp*20asc~source~(~'*2faws*2flambda*2f${functionName}))`;
74156
+ if (event.InvokeComplete.ErrorCode === "Unhandled") {
74157
+ throw new Error(`Lambda function ${functionName} failed with an unhandled error: ${event.InvokeComplete.ErrorDetails} See ${logs} to see the logs of this invocation.`);
74158
+ }
74159
+ throw new Error(`Lambda function ${functionName} failed with error code ${event.InvokeComplete.ErrorCode}: ${event.InvokeComplete.ErrorDetails}. See ${logs} to see the logs of this invocation.`);
74048
74160
  }
74049
- throw new Error(`Lambda function ${functionName} failed with error code ${event.InvokeComplete.ErrorCode}: ${event.InvokeComplete.ErrorDetails}. See ${logs} to see the logs of this invocation.`);
74050
74161
  }
74051
74162
  }
74163
+ } finally {
74164
+ if (globalThis._dumpUnreleasedBuffers) {
74165
+ globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74166
+ }
74167
+ clear();
74052
74168
  }
74053
- if (globalThis._dumpUnreleasedBuffers) {
74054
- globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74055
- }
74056
- clear();
74057
74169
  };
74058
74170
  var callFunctionWithStreamingImplementation = async (options) => {
74059
74171
  try {
@@ -76438,6 +76550,42 @@ var awsImplementation = {
76438
76550
  getBucketPrefix: () => REMOTION_BUCKET_PREFIX
76439
76551
  };
76440
76552
 
76553
+ // src/cancel-render-on-lambda.ts
76554
+ var internalCancelRenderOnLambda = async (input) => {
76555
+ const expectedBucketOwner = await input.providerSpecifics.getAccountId({
76556
+ region: input.region
76557
+ });
76558
+ const progress = await getOverallProgressFromStorage({
76559
+ bucketName: input.bucketName,
76560
+ expectedBucketOwner,
76561
+ region: input.region,
76562
+ renderId: input.renderId,
76563
+ providerSpecifics: input.providerSpecifics,
76564
+ forcePathStyle: input.forcePathStyle,
76565
+ requestHandler: input.requestHandler
76566
+ });
76567
+ if (progress.cancellationEnabled !== true) {
76568
+ throw new Error(`Cannot cancel render ${input.renderId}: The render was not started with enableCancellation: true.`);
76569
+ }
76570
+ await writeCancellationSignal({
76571
+ bucketName: input.bucketName,
76572
+ renderId: input.renderId,
76573
+ region: input.region,
76574
+ expectedBucketOwner,
76575
+ providerSpecifics: input.providerSpecifics,
76576
+ forcePathStyle: input.forcePathStyle,
76577
+ requestHandler: input.requestHandler
76578
+ });
76579
+ };
76580
+ var cancelRenderOnLambda = (input) => {
76581
+ return internalCancelRenderOnLambda({
76582
+ ...input,
76583
+ forcePathStyle: input.forcePathStyle ?? false,
76584
+ requestHandler: input.requestHandler ?? null,
76585
+ providerSpecifics: awsImplementation
76586
+ });
76587
+ };
76588
+
76441
76589
  // src/p-limit.ts
76442
76590
  var pLimit = (concurrency) => {
76443
76591
  const queue = [];
@@ -76734,6 +76882,7 @@ var validateServeUrl = (serveUrl) => {
76734
76882
 
76735
76883
  // src/make-lambda-payload.ts
76736
76884
  var makeLambdaRenderMediaPayload = async ({
76885
+ enableCancellation,
76737
76886
  rendererFunctionName,
76738
76887
  frameRange,
76739
76888
  framesPerLambda,
@@ -76814,6 +76963,7 @@ var makeLambdaRenderMediaPayload = async ({
76814
76963
  logLevel
76815
76964
  });
76816
76965
  return {
76966
+ enableCancellation,
76817
76967
  rendererFunctionName,
76818
76968
  framesPerLambda,
76819
76969
  concurrency,
@@ -77028,6 +77178,7 @@ var internalRenderMediaOnLambdaRaw = async (input) => {
77028
77178
  };
77029
77179
  var renderMediaOnLambdaOptionalToRequired = (options) => {
77030
77180
  return {
77181
+ enableCancellation: options.enableCancellation ?? false,
77031
77182
  offthreadVideoThreads: options.offthreadVideoThreads ?? null,
77032
77183
  audioBitrate: options.audioBitrate ?? null,
77033
77184
  audioCodec: options.audioCodec ?? null,
@@ -77135,6 +77286,7 @@ var innerRenderStillOnLambda = async (input) => {
77135
77286
  },
77136
77287
  timeoutInTest: 120000,
77137
77288
  retriesRemaining: input.maxRetries,
77289
+ getBinaryPayloadSink: null,
77138
77290
  requestHandler: input.requestHandler
77139
77291
  }).then(() => {
77140
77292
  reject(new Error("Expected response to be streamed"));
@@ -77802,6 +77954,7 @@ var LambdaClientInternals = {
77802
77954
  makeLambdaRenderMediaPayload,
77803
77955
  renderMediaOnLambdaOptionalToRequired,
77804
77956
  internalDeleteRender,
77957
+ internalCancelRenderOnLambda,
77805
77958
  internalGetSites,
77806
77959
  getLifeCycleRules,
77807
77960
  awsImplementation,
@@ -77843,6 +77996,7 @@ export {
77843
77996
  estimatePrice,
77844
77997
  deleteRender,
77845
77998
  deleteFunction,
77999
+ cancelRenderOnLambda,
77846
78000
  appRouterWebhook,
77847
78001
  LambdaClientInternals
77848
78002
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/lambda-client"
4
4
  },
5
5
  "name": "@remotion/lambda-client",
6
- "version": "4.0.514",
6
+ "version": "4.0.515",
7
7
  "main": "dist/cjs/index.js",
8
8
  "scripts": {
9
9
  "formatting": "oxfmt src --check",
@@ -26,10 +26,10 @@
26
26
  "@aws-sdk/lib-storage": "3.986.0",
27
27
  "mime-types": "2.1.34",
28
28
  "@aws-sdk/credential-provider-ini": "3.972.5",
29
- "@remotion/serverless-client": "4.0.514",
29
+ "@remotion/serverless-client": "4.0.515",
30
30
  "@types/express": "5.0.1",
31
31
  "express": "4.21.0",
32
- "@remotion/eslint-config-internal": "4.0.514",
32
+ "@remotion/eslint-config-internal": "4.0.515",
33
33
  "eslint": "9.19.0",
34
34
  "next": "16.2.11",
35
35
  "@types/mime-types": "2.1.1",