@remotion/lambda-client 4.0.513 → 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
  });
@@ -71122,16 +71123,6 @@ var random = (seed, dummy) => {
71122
71123
  }
71123
71124
  throw new Error("random() argument must be a number or a string");
71124
71125
  };
71125
- function truthy(value) {
71126
- return Boolean(value);
71127
- }
71128
- if (typeof window !== "undefined") {
71129
- window.remotion_renderReady = false;
71130
- if (!window.remotion_delayRenderTimeouts) {
71131
- window.remotion_delayRenderTimeouts = {};
71132
- }
71133
- window.remotion_delayRenderHandles = [];
71134
- }
71135
71126
  var DELAY_RENDER_CALLSTACK_TOKEN = "The delayRender was called:";
71136
71127
  var DELAY_RENDER_RETRIES_LEFT = "Retries left: ";
71137
71128
  var DELAY_RENDER_RETRY_TOKEN = "- Rendering the frame will be retried.";
@@ -72010,6 +72001,9 @@ var serializeScaleValue = ([x, y, z]) => {
72010
72001
  }
72011
72002
  return `${normalizedX} ${normalizedY} ${normalizedZ}`;
72012
72003
  };
72004
+ function truthy(value) {
72005
+ return Boolean(value);
72006
+ }
72013
72007
  var ENABLE_V5_BREAKING_CHANGES = false;
72014
72008
  var validCodecs = [
72015
72009
  "h264",
@@ -72184,6 +72178,7 @@ var customOutName = (renderId, bucketName, name) => {
72184
72178
  };
72185
72179
  };
72186
72180
  var overallProgressKey = (renderId) => `${rendersPrefix(renderId)}/progress.json`;
72181
+ var cancellationKey = (renderId) => `${rendersPrefix(renderId)}/cancel.json`;
72187
72182
  var MAX_FUNCTIONS_PER_RENDER = 200;
72188
72183
  var validateDownloadBehavior = (downloadBehavior) => {
72189
72184
  if (downloadBehavior === null || downloadBehavior === undefined) {
@@ -72202,7 +72197,7 @@ var validateDownloadBehavior = (downloadBehavior) => {
72202
72197
  }
72203
72198
  }
72204
72199
  };
72205
- var VERSION = "4.0.513";
72200
+ var VERSION = "4.0.515";
72206
72201
  var isColorSupported = () => {
72207
72202
  const env = process.env || {};
72208
72203
  const isForceDisabled = "NO_COLOR" in env;
@@ -72556,7 +72551,7 @@ var wrapWithErrorHandling = (fn) => {
72556
72551
  };
72557
72552
  };
72558
72553
  var streamingKey = "remotion_buffer:";
72559
- var makeStreamer = (onMessage) => {
72554
+ var makeStreamer = (onMessage, getPayloadSink = null) => {
72560
72555
  const separator = new Uint8Array(streamingKey.length);
72561
72556
  for (let i = 0;i < streamingKey.length; i++) {
72562
72557
  separator[i] = streamingKey.charCodeAt(i);
@@ -72564,6 +72559,7 @@ var makeStreamer = (onMessage) => {
72564
72559
  let unprocessedBuffers = [];
72565
72560
  let outputBuffer = new Uint8Array(0);
72566
72561
  let missingData = null;
72562
+ let activeSink = null;
72567
72563
  const findSeparatorIndex = () => {
72568
72564
  let searchIndex = 0;
72569
72565
  while (true) {
@@ -72622,6 +72618,36 @@ var makeStreamer = (onMessage) => {
72622
72618
  }
72623
72619
  const length = Number(lengthString);
72624
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
+ }
72625
72651
  const dataLength = outputBuffer.length - separatorIndex - 1;
72626
72652
  if (dataLength < length) {
72627
72653
  missingData = {
@@ -72633,9 +72659,33 @@ var makeStreamer = (onMessage) => {
72633
72659
  onMessage(status === 1 ? "error" : "success", nonceString, data);
72634
72660
  missingData = null;
72635
72661
  outputBuffer = outputBuffer.subarray(separatorIndex + Number(lengthString) + 1);
72636
- processInput();
72662
+ return processInput();
72637
72663
  };
72638
- 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
+ }
72639
72689
  unprocessedBuffers.push(data);
72640
72690
  if (missingData) {
72641
72691
  missingData.dataMissing -= data.length;
@@ -72652,7 +72702,15 @@ var makeStreamer = (onMessage) => {
72652
72702
  }
72653
72703
  outputBuffer = newBuffer;
72654
72704
  unprocessedBuffers = [];
72655
- 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;
72656
72714
  };
72657
72715
  return {
72658
72716
  onData,
@@ -72660,6 +72718,15 @@ var makeStreamer = (onMessage) => {
72660
72718
  clear: () => {
72661
72719
  unprocessedBuffers = [];
72662
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
+ }
72663
72730
  }
72664
72731
  };
72665
72732
  };
@@ -74199,6 +74266,41 @@ var messageTypeIdToMessageType = (messageTypeId) => {
74199
74266
  }
74200
74267
  return types.type;
74201
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
+ };
74202
74304
  var ENABLE_V5_BREAKING_CHANGES2 = NoReactInternals.ENABLE_V5_BREAKING_CHANGES;
74203
74305
  var serializeJSONWithSpecialTypes2 = NoReactInternals.serializeJSONWithSpecialTypes;
74204
74306
  var validateFps2 = NoReactInternals.validateFps;
@@ -74670,7 +74772,8 @@ var callLambdaWithStreamingWithoutRetry = async ({
74670
74772
  region,
74671
74773
  timeoutInTest,
74672
74774
  receivedStreamingPayload,
74673
- requestHandler
74775
+ requestHandler,
74776
+ getBinaryPayloadSink
74674
74777
  }) => {
74675
74778
  const res = await invokeStreamOrTimeout({
74676
74779
  functionName,
@@ -74691,7 +74794,7 @@ var callLambdaWithStreamingWithoutRetry = async ({
74691
74794
  }
74692
74795
  };
74693
74796
  receivedStreamingPayload(message);
74694
- });
74797
+ }, getBinaryPayloadSink ? binaryPayloadSinkForStreamer(getBinaryPayloadSink) : null);
74695
74798
  const dumpBuffers = () => {
74696
74799
  clear();
74697
74800
  };
@@ -74699,25 +74802,28 @@ var callLambdaWithStreamingWithoutRetry = async ({
74699
74802
  globalThis._dumpUnreleasedBuffers.addListener("dump-unreleased-buffers", dumpBuffers);
74700
74803
  }
74701
74804
  const events = res.EventStream;
74702
- for await (const event of events) {
74703
- if (event.PayloadChunk && event.PayloadChunk.Payload) {
74704
- onData(event.PayloadChunk.Payload);
74705
- }
74706
- if (event.InvokeComplete) {
74707
- if (event.InvokeComplete.ErrorCode) {
74708
- const { consoleDomain } = getAwsRegionMetadata(region);
74709
- 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}))`;
74710
- if (event.InvokeComplete.ErrorCode === "Unhandled") {
74711
- 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.`);
74712
74818
  }
74713
- 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.`);
74714
74819
  }
74715
74820
  }
74821
+ } finally {
74822
+ if (globalThis._dumpUnreleasedBuffers) {
74823
+ globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74824
+ }
74825
+ clear();
74716
74826
  }
74717
- if (globalThis._dumpUnreleasedBuffers) {
74718
- globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74719
- }
74720
- clear();
74721
74827
  };
74722
74828
  var callFunctionWithStreamingImplementation = async (options) => {
74723
74829
  try {
@@ -76618,6 +76724,42 @@ var awsImplementation = {
76618
76724
  getBucketPrefix: () => REMOTION_BUCKET_PREFIX
76619
76725
  };
76620
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
+
76621
76763
  // src/p-limit.ts
76622
76764
  var pLimit = (concurrency) => {
76623
76765
  const queue = [];
@@ -76914,6 +77056,7 @@ var validateServeUrl = (serveUrl) => {
76914
77056
 
76915
77057
  // src/make-lambda-payload.ts
76916
77058
  var makeLambdaRenderMediaPayload = async ({
77059
+ enableCancellation,
76917
77060
  rendererFunctionName,
76918
77061
  frameRange,
76919
77062
  framesPerLambda,
@@ -76994,6 +77137,7 @@ var makeLambdaRenderMediaPayload = async ({
76994
77137
  logLevel
76995
77138
  });
76996
77139
  return {
77140
+ enableCancellation,
76997
77141
  rendererFunctionName,
76998
77142
  framesPerLambda,
76999
77143
  concurrency,
@@ -77208,6 +77352,7 @@ var internalRenderMediaOnLambdaRaw = async (input) => {
77208
77352
  };
77209
77353
  var renderMediaOnLambdaOptionalToRequired = (options) => {
77210
77354
  return {
77355
+ enableCancellation: options.enableCancellation ?? false,
77211
77356
  offthreadVideoThreads: options.offthreadVideoThreads ?? null,
77212
77357
  audioBitrate: options.audioBitrate ?? null,
77213
77358
  audioCodec: options.audioCodec ?? null,
@@ -77315,6 +77460,7 @@ var innerRenderStillOnLambda = async (input) => {
77315
77460
  },
77316
77461
  timeoutInTest: 120000,
77317
77462
  retriesRemaining: input.maxRetries,
77463
+ getBinaryPayloadSink: null,
77318
77464
  requestHandler: input.requestHandler
77319
77465
  }).then(() => {
77320
77466
  reject(new Error("Expected response to be streamed"));
@@ -77861,6 +78007,7 @@ var LambdaClientInternals = {
77861
78007
  makeLambdaRenderMediaPayload,
77862
78008
  renderMediaOnLambdaOptionalToRequired,
77863
78009
  internalDeleteRender,
78010
+ internalCancelRenderOnLambda,
77864
78011
  internalGetSites,
77865
78012
  getLifeCycleRules,
77866
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;
@@ -70465,16 +70465,6 @@ var random = (seed, dummy) => {
70465
70465
  }
70466
70466
  throw new Error("random() argument must be a number or a string");
70467
70467
  };
70468
- function truthy(value) {
70469
- return Boolean(value);
70470
- }
70471
- if (typeof window !== "undefined") {
70472
- window.remotion_renderReady = false;
70473
- if (!window.remotion_delayRenderTimeouts) {
70474
- window.remotion_delayRenderTimeouts = {};
70475
- }
70476
- window.remotion_delayRenderHandles = [];
70477
- }
70478
70468
  var DELAY_RENDER_CALLSTACK_TOKEN = "The delayRender was called:";
70479
70469
  var DELAY_RENDER_RETRIES_LEFT = "Retries left: ";
70480
70470
  var DELAY_RENDER_RETRY_TOKEN = "- Rendering the frame will be retried.";
@@ -71353,6 +71343,9 @@ var serializeScaleValue = ([x, y, z]) => {
71353
71343
  }
71354
71344
  return `${normalizedX} ${normalizedY} ${normalizedZ}`;
71355
71345
  };
71346
+ function truthy(value) {
71347
+ return Boolean(value);
71348
+ }
71356
71349
  var ENABLE_V5_BREAKING_CHANGES = false;
71357
71350
  var validCodecs = [
71358
71351
  "h264",
@@ -71527,6 +71520,7 @@ var customOutName = (renderId, bucketName, name) => {
71527
71520
  };
71528
71521
  };
71529
71522
  var overallProgressKey = (renderId) => `${rendersPrefix(renderId)}/progress.json`;
71523
+ var cancellationKey = (renderId) => `${rendersPrefix(renderId)}/cancel.json`;
71530
71524
  var MAX_FUNCTIONS_PER_RENDER = 200;
71531
71525
  var validateDownloadBehavior = (downloadBehavior) => {
71532
71526
  if (downloadBehavior === null || downloadBehavior === undefined) {
@@ -71545,7 +71539,7 @@ var validateDownloadBehavior = (downloadBehavior) => {
71545
71539
  }
71546
71540
  }
71547
71541
  };
71548
- var VERSION = "4.0.513";
71542
+ var VERSION = "4.0.515";
71549
71543
  var isColorSupported = () => {
71550
71544
  const env = process.env || {};
71551
71545
  const isForceDisabled = "NO_COLOR" in env;
@@ -71899,7 +71893,7 @@ var wrapWithErrorHandling = (fn) => {
71899
71893
  };
71900
71894
  };
71901
71895
  var streamingKey = "remotion_buffer:";
71902
- var makeStreamer = (onMessage) => {
71896
+ var makeStreamer = (onMessage, getPayloadSink = null) => {
71903
71897
  const separator = new Uint8Array(streamingKey.length);
71904
71898
  for (let i = 0;i < streamingKey.length; i++) {
71905
71899
  separator[i] = streamingKey.charCodeAt(i);
@@ -71907,6 +71901,7 @@ var makeStreamer = (onMessage) => {
71907
71901
  let unprocessedBuffers = [];
71908
71902
  let outputBuffer = new Uint8Array(0);
71909
71903
  let missingData = null;
71904
+ let activeSink = null;
71910
71905
  const findSeparatorIndex = () => {
71911
71906
  let searchIndex = 0;
71912
71907
  while (true) {
@@ -71965,6 +71960,36 @@ var makeStreamer = (onMessage) => {
71965
71960
  }
71966
71961
  const length = Number(lengthString);
71967
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
+ }
71968
71993
  const dataLength = outputBuffer.length - separatorIndex - 1;
71969
71994
  if (dataLength < length) {
71970
71995
  missingData = {
@@ -71976,9 +72001,33 @@ var makeStreamer = (onMessage) => {
71976
72001
  onMessage(status === 1 ? "error" : "success", nonceString, data);
71977
72002
  missingData = null;
71978
72003
  outputBuffer = outputBuffer.subarray(separatorIndex + Number(lengthString) + 1);
71979
- processInput();
72004
+ return processInput();
71980
72005
  };
71981
- 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
+ }
71982
72031
  unprocessedBuffers.push(data);
71983
72032
  if (missingData) {
71984
72033
  missingData.dataMissing -= data.length;
@@ -71995,7 +72044,15 @@ var makeStreamer = (onMessage) => {
71995
72044
  }
71996
72045
  outputBuffer = newBuffer;
71997
72046
  unprocessedBuffers = [];
71998
- 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;
71999
72056
  };
72000
72057
  return {
72001
72058
  onData,
@@ -72003,6 +72060,15 @@ var makeStreamer = (onMessage) => {
72003
72060
  clear: () => {
72004
72061
  unprocessedBuffers = [];
72005
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
+ }
72006
72072
  }
72007
72073
  };
72008
72074
  };
@@ -73542,6 +73608,41 @@ var messageTypeIdToMessageType = (messageTypeId) => {
73542
73608
  }
73543
73609
  return types.type;
73544
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
+ };
73545
73646
  var ENABLE_V5_BREAKING_CHANGES2 = NoReactInternals.ENABLE_V5_BREAKING_CHANGES;
73546
73647
  var serializeJSONWithSpecialTypes2 = NoReactInternals.serializeJSONWithSpecialTypes;
73547
73648
  var validateFps2 = NoReactInternals.validateFps;
@@ -74013,7 +74114,8 @@ var callLambdaWithStreamingWithoutRetry = async ({
74013
74114
  region,
74014
74115
  timeoutInTest,
74015
74116
  receivedStreamingPayload,
74016
- requestHandler
74117
+ requestHandler,
74118
+ getBinaryPayloadSink
74017
74119
  }) => {
74018
74120
  const res = await invokeStreamOrTimeout({
74019
74121
  functionName,
@@ -74034,7 +74136,7 @@ var callLambdaWithStreamingWithoutRetry = async ({
74034
74136
  }
74035
74137
  };
74036
74138
  receivedStreamingPayload(message);
74037
- });
74139
+ }, getBinaryPayloadSink ? binaryPayloadSinkForStreamer(getBinaryPayloadSink) : null);
74038
74140
  const dumpBuffers = () => {
74039
74141
  clear();
74040
74142
  };
@@ -74042,25 +74144,28 @@ var callLambdaWithStreamingWithoutRetry = async ({
74042
74144
  globalThis._dumpUnreleasedBuffers.addListener("dump-unreleased-buffers", dumpBuffers);
74043
74145
  }
74044
74146
  const events = res.EventStream;
74045
- for await (const event of events) {
74046
- if (event.PayloadChunk && event.PayloadChunk.Payload) {
74047
- onData(event.PayloadChunk.Payload);
74048
- }
74049
- if (event.InvokeComplete) {
74050
- if (event.InvokeComplete.ErrorCode) {
74051
- const { consoleDomain } = getAwsRegionMetadata(region);
74052
- 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}))`;
74053
- if (event.InvokeComplete.ErrorCode === "Unhandled") {
74054
- 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.`);
74055
74160
  }
74056
- 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.`);
74057
74161
  }
74058
74162
  }
74163
+ } finally {
74164
+ if (globalThis._dumpUnreleasedBuffers) {
74165
+ globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74166
+ }
74167
+ clear();
74059
74168
  }
74060
- if (globalThis._dumpUnreleasedBuffers) {
74061
- globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74062
- }
74063
- clear();
74064
74169
  };
74065
74170
  var callFunctionWithStreamingImplementation = async (options) => {
74066
74171
  try {
@@ -76445,6 +76550,42 @@ var awsImplementation = {
76445
76550
  getBucketPrefix: () => REMOTION_BUCKET_PREFIX
76446
76551
  };
76447
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
+
76448
76589
  // src/p-limit.ts
76449
76590
  var pLimit = (concurrency) => {
76450
76591
  const queue = [];
@@ -76741,6 +76882,7 @@ var validateServeUrl = (serveUrl) => {
76741
76882
 
76742
76883
  // src/make-lambda-payload.ts
76743
76884
  var makeLambdaRenderMediaPayload = async ({
76885
+ enableCancellation,
76744
76886
  rendererFunctionName,
76745
76887
  frameRange,
76746
76888
  framesPerLambda,
@@ -76821,6 +76963,7 @@ var makeLambdaRenderMediaPayload = async ({
76821
76963
  logLevel
76822
76964
  });
76823
76965
  return {
76966
+ enableCancellation,
76824
76967
  rendererFunctionName,
76825
76968
  framesPerLambda,
76826
76969
  concurrency,
@@ -77035,6 +77178,7 @@ var internalRenderMediaOnLambdaRaw = async (input) => {
77035
77178
  };
77036
77179
  var renderMediaOnLambdaOptionalToRequired = (options) => {
77037
77180
  return {
77181
+ enableCancellation: options.enableCancellation ?? false,
77038
77182
  offthreadVideoThreads: options.offthreadVideoThreads ?? null,
77039
77183
  audioBitrate: options.audioBitrate ?? null,
77040
77184
  audioCodec: options.audioCodec ?? null,
@@ -77142,6 +77286,7 @@ var innerRenderStillOnLambda = async (input) => {
77142
77286
  },
77143
77287
  timeoutInTest: 120000,
77144
77288
  retriesRemaining: input.maxRetries,
77289
+ getBinaryPayloadSink: null,
77145
77290
  requestHandler: input.requestHandler
77146
77291
  }).then(() => {
77147
77292
  reject(new Error("Expected response to be streamed"));
@@ -77809,6 +77954,7 @@ var LambdaClientInternals = {
77809
77954
  makeLambdaRenderMediaPayload,
77810
77955
  renderMediaOnLambdaOptionalToRequired,
77811
77956
  internalDeleteRender,
77957
+ internalCancelRenderOnLambda,
77812
77958
  internalGetSites,
77813
77959
  getLifeCycleRules,
77814
77960
  awsImplementation,
@@ -77850,6 +77996,7 @@ export {
77850
77996
  estimatePrice,
77851
77997
  deleteRender,
77852
77998
  deleteFunction,
77999
+ cancelRenderOnLambda,
77853
78000
  appRouterWebhook,
77854
78001
  LambdaClientInternals
77855
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.513",
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.513",
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.513",
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",