@remotion/lambda-client 4.0.514 → 4.0.516

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
  });
@@ -71182,7 +71183,7 @@ var serializeJSONWithSpecialTypes = ({
71182
71183
  setUsed = true;
71183
71184
  return value;
71184
71185
  }
71185
- if (typeof item === "string" && staticBase !== null && item.startsWith(staticBase)) {
71186
+ if (typeof item === "string" && staticBase !== null && staticBase !== "" && item.startsWith(staticBase)) {
71186
71187
  customFileUsed = true;
71187
71188
  return `${FILE_TOKEN}${item.replace(staticBase + "/", "")}`;
71188
71189
  }
@@ -71193,13 +71194,25 @@ var serializeJSONWithSpecialTypes = ({
71193
71194
  throw new Error("Could not serialize the passed input props to JSON: " + err.message);
71194
71195
  }
71195
71196
  };
71197
+ var resolveFileTokenToUrl = (value) => {
71198
+ const encodedName = value.replace(FILE_TOKEN, "");
71199
+ let name = encodedName;
71200
+ try {
71201
+ name = encodedName.split("/").map(decodeURIComponent).join("/");
71202
+ } catch {}
71203
+ const matchingStaticFile = window.remotion_staticFiles?.find((file) => file.name === name);
71204
+ if (matchingStaticFile) {
71205
+ return matchingStaticFile.src;
71206
+ }
71207
+ return `${window.remotion_staticBase}/${encodedName}`;
71208
+ };
71196
71209
  var deserializeJSONWithSpecialTypes = (data) => {
71197
71210
  return JSON.parse(data, (_, value) => {
71198
71211
  if (typeof value === "string" && value.startsWith(DATE_TOKEN)) {
71199
71212
  return new Date(value.replace(DATE_TOKEN, ""));
71200
71213
  }
71201
71214
  if (typeof value === "string" && value.startsWith(FILE_TOKEN)) {
71202
- return `${window.remotion_staticBase}/${value.replace(FILE_TOKEN, "")}`;
71215
+ return resolveFileTokenToUrl(value);
71203
71216
  }
71204
71217
  return value;
71205
71218
  });
@@ -72177,6 +72190,7 @@ var customOutName = (renderId, bucketName, name) => {
72177
72190
  };
72178
72191
  };
72179
72192
  var overallProgressKey = (renderId) => `${rendersPrefix(renderId)}/progress.json`;
72193
+ var cancellationKey = (renderId) => `${rendersPrefix(renderId)}/cancel.json`;
72180
72194
  var MAX_FUNCTIONS_PER_RENDER = 200;
72181
72195
  var validateDownloadBehavior = (downloadBehavior) => {
72182
72196
  if (downloadBehavior === null || downloadBehavior === undefined) {
@@ -72195,7 +72209,7 @@ var validateDownloadBehavior = (downloadBehavior) => {
72195
72209
  }
72196
72210
  }
72197
72211
  };
72198
- var VERSION = "4.0.514";
72212
+ var VERSION = "4.0.516";
72199
72213
  var isColorSupported = () => {
72200
72214
  const env = process.env || {};
72201
72215
  const isForceDisabled = "NO_COLOR" in env;
@@ -72549,7 +72563,7 @@ var wrapWithErrorHandling = (fn) => {
72549
72563
  };
72550
72564
  };
72551
72565
  var streamingKey = "remotion_buffer:";
72552
- var makeStreamer = (onMessage) => {
72566
+ var makeStreamer = (onMessage, getPayloadSink = null) => {
72553
72567
  const separator = new Uint8Array(streamingKey.length);
72554
72568
  for (let i = 0;i < streamingKey.length; i++) {
72555
72569
  separator[i] = streamingKey.charCodeAt(i);
@@ -72557,6 +72571,7 @@ var makeStreamer = (onMessage) => {
72557
72571
  let unprocessedBuffers = [];
72558
72572
  let outputBuffer = new Uint8Array(0);
72559
72573
  let missingData = null;
72574
+ let activeSink = null;
72560
72575
  const findSeparatorIndex = () => {
72561
72576
  let searchIndex = 0;
72562
72577
  while (true) {
@@ -72615,6 +72630,36 @@ var makeStreamer = (onMessage) => {
72615
72630
  }
72616
72631
  const length = Number(lengthString);
72617
72632
  const status = Number(statusString);
72633
+ const statusType = status === 1 ? "error" : "success";
72634
+ const sink = getPayloadSink ? getPayloadSink(statusType, nonceString, length) : null;
72635
+ if (sink) {
72636
+ const payloadStart = separatorIndex + 1;
72637
+ const available = Math.min(outputBuffer.length - payloadStart, length);
72638
+ const payloadPiece = outputBuffer.subarray(payloadStart, payloadStart + available);
72639
+ const rest = new Uint8Array(outputBuffer.subarray(payloadStart + available));
72640
+ outputBuffer = new Uint8Array(0);
72641
+ missingData = null;
72642
+ return (async () => {
72643
+ if (available > 0) {
72644
+ await sink.write(payloadPiece);
72645
+ }
72646
+ if (available < length) {
72647
+ activeSink = {
72648
+ sink,
72649
+ remaining: length - available,
72650
+ nonce: nonceString,
72651
+ statusType
72652
+ };
72653
+ return;
72654
+ }
72655
+ await sink.end();
72656
+ onMessage(statusType, nonceString, new Uint8Array(0));
72657
+ if (rest.length > 0) {
72658
+ outputBuffer = rest;
72659
+ return processInput();
72660
+ }
72661
+ })();
72662
+ }
72618
72663
  const dataLength = outputBuffer.length - separatorIndex - 1;
72619
72664
  if (dataLength < length) {
72620
72665
  missingData = {
@@ -72626,9 +72671,33 @@ var makeStreamer = (onMessage) => {
72626
72671
  onMessage(status === 1 ? "error" : "success", nonceString, data);
72627
72672
  missingData = null;
72628
72673
  outputBuffer = outputBuffer.subarray(separatorIndex + Number(lengthString) + 1);
72629
- processInput();
72674
+ return processInput();
72630
72675
  };
72631
- const onData = (data) => {
72676
+ const feedSink = async (data) => {
72677
+ if (!activeSink) {
72678
+ throw new Error("No active sink");
72679
+ }
72680
+ const take = Math.min(activeSink.remaining, data.length);
72681
+ await activeSink.sink.write(data.subarray(0, take));
72682
+ activeSink.remaining -= take;
72683
+ if (activeSink.remaining > 0) {
72684
+ return null;
72685
+ }
72686
+ const { sink, nonce, statusType } = activeSink;
72687
+ activeSink = null;
72688
+ await sink.end();
72689
+ onMessage(statusType, nonce, new Uint8Array(0));
72690
+ const leftover = data.subarray(take);
72691
+ return leftover.length > 0 ? leftover : null;
72692
+ };
72693
+ const onDataInner = (data) => {
72694
+ if (activeSink) {
72695
+ return feedSink(data).then((leftover) => {
72696
+ if (leftover) {
72697
+ return onDataInner(leftover);
72698
+ }
72699
+ });
72700
+ }
72632
72701
  unprocessedBuffers.push(data);
72633
72702
  if (missingData) {
72634
72703
  missingData.dataMissing -= data.length;
@@ -72645,7 +72714,15 @@ var makeStreamer = (onMessage) => {
72645
72714
  }
72646
72715
  outputBuffer = newBuffer;
72647
72716
  unprocessedBuffers = [];
72648
- processInput();
72717
+ return processInput();
72718
+ };
72719
+ let chain = Promise.resolve();
72720
+ const onData = (data) => {
72721
+ if (!getPayloadSink) {
72722
+ return onDataInner(data);
72723
+ }
72724
+ chain = chain.then(() => onDataInner(data));
72725
+ return chain;
72649
72726
  };
72650
72727
  return {
72651
72728
  onData,
@@ -72653,6 +72730,15 @@ var makeStreamer = (onMessage) => {
72653
72730
  clear: () => {
72654
72731
  unprocessedBuffers = [];
72655
72732
  outputBuffer = new Uint8Array(0);
72733
+ if (activeSink) {
72734
+ const { sink } = activeSink;
72735
+ activeSink = null;
72736
+ try {
72737
+ Promise.resolve(sink.end()).catch(() => {
72738
+ return;
72739
+ });
72740
+ } catch {}
72741
+ }
72656
72742
  }
72657
72743
  };
72658
72744
  };
@@ -74192,6 +74278,41 @@ var messageTypeIdToMessageType = (messageTypeId) => {
74192
74278
  }
74193
74279
  return types.type;
74194
74280
  };
74281
+ var binaryPayloadSinkForStreamer = (getBinaryPayloadSink) => {
74282
+ return (_statusType, nonce, length) => {
74283
+ const messageType = messageTypeIdToMessageType(nonce);
74284
+ if (formatMap[messageType] !== "binary") {
74285
+ return null;
74286
+ }
74287
+ return getBinaryPayloadSink({
74288
+ messageType,
74289
+ length
74290
+ });
74291
+ };
74292
+ };
74293
+ var writeCancellationSignal = async ({
74294
+ bucketName,
74295
+ renderId,
74296
+ region,
74297
+ expectedBucketOwner,
74298
+ providerSpecifics,
74299
+ forcePathStyle,
74300
+ requestHandler
74301
+ }) => {
74302
+ await providerSpecifics.writeFile({
74303
+ bucketName,
74304
+ key: cancellationKey(renderId),
74305
+ body: JSON.stringify({ cancelledAt: Date.now() }),
74306
+ region,
74307
+ privacy: "private",
74308
+ expectedBucketOwner,
74309
+ downloadBehavior: null,
74310
+ customCredentials: null,
74311
+ forcePathStyle,
74312
+ storageClass: null,
74313
+ requestHandler
74314
+ });
74315
+ };
74195
74316
  var ENABLE_V5_BREAKING_CHANGES2 = NoReactInternals.ENABLE_V5_BREAKING_CHANGES;
74196
74317
  var serializeJSONWithSpecialTypes2 = NoReactInternals.serializeJSONWithSpecialTypes;
74197
74318
  var validateFps2 = NoReactInternals.validateFps;
@@ -74663,7 +74784,8 @@ var callLambdaWithStreamingWithoutRetry = async ({
74663
74784
  region,
74664
74785
  timeoutInTest,
74665
74786
  receivedStreamingPayload,
74666
- requestHandler
74787
+ requestHandler,
74788
+ getBinaryPayloadSink
74667
74789
  }) => {
74668
74790
  const res = await invokeStreamOrTimeout({
74669
74791
  functionName,
@@ -74684,7 +74806,7 @@ var callLambdaWithStreamingWithoutRetry = async ({
74684
74806
  }
74685
74807
  };
74686
74808
  receivedStreamingPayload(message);
74687
- });
74809
+ }, getBinaryPayloadSink ? binaryPayloadSinkForStreamer(getBinaryPayloadSink) : null);
74688
74810
  const dumpBuffers = () => {
74689
74811
  clear();
74690
74812
  };
@@ -74692,25 +74814,28 @@ var callLambdaWithStreamingWithoutRetry = async ({
74692
74814
  globalThis._dumpUnreleasedBuffers.addListener("dump-unreleased-buffers", dumpBuffers);
74693
74815
  }
74694
74816
  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.`);
74817
+ try {
74818
+ for await (const event of events) {
74819
+ if (event.PayloadChunk && event.PayloadChunk.Payload) {
74820
+ await onData(event.PayloadChunk.Payload);
74821
+ }
74822
+ if (event.InvokeComplete) {
74823
+ if (event.InvokeComplete.ErrorCode) {
74824
+ const { consoleDomain } = getAwsRegionMetadata(region);
74825
+ 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}))`;
74826
+ if (event.InvokeComplete.ErrorCode === "Unhandled") {
74827
+ throw new Error(`Lambda function ${functionName} failed with an unhandled error: ${event.InvokeComplete.ErrorDetails} See ${logs} to see the logs of this invocation.`);
74828
+ }
74829
+ 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
74830
  }
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
74831
  }
74708
74832
  }
74833
+ } finally {
74834
+ if (globalThis._dumpUnreleasedBuffers) {
74835
+ globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74836
+ }
74837
+ clear();
74709
74838
  }
74710
- if (globalThis._dumpUnreleasedBuffers) {
74711
- globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74712
- }
74713
- clear();
74714
74839
  };
74715
74840
  var callFunctionWithStreamingImplementation = async (options) => {
74716
74841
  try {
@@ -76611,6 +76736,42 @@ var awsImplementation = {
76611
76736
  getBucketPrefix: () => REMOTION_BUCKET_PREFIX
76612
76737
  };
76613
76738
 
76739
+ // src/cancel-render-on-lambda.ts
76740
+ var internalCancelRenderOnLambda = async (input) => {
76741
+ const expectedBucketOwner = await input.providerSpecifics.getAccountId({
76742
+ region: input.region
76743
+ });
76744
+ const progress = await getOverallProgressFromStorage({
76745
+ bucketName: input.bucketName,
76746
+ expectedBucketOwner,
76747
+ region: input.region,
76748
+ renderId: input.renderId,
76749
+ providerSpecifics: input.providerSpecifics,
76750
+ forcePathStyle: input.forcePathStyle,
76751
+ requestHandler: input.requestHandler
76752
+ });
76753
+ if (progress.cancellationEnabled !== true) {
76754
+ throw new Error(`Cannot cancel render ${input.renderId}: The render was not started with enableCancellation: true.`);
76755
+ }
76756
+ await writeCancellationSignal({
76757
+ bucketName: input.bucketName,
76758
+ renderId: input.renderId,
76759
+ region: input.region,
76760
+ expectedBucketOwner,
76761
+ providerSpecifics: input.providerSpecifics,
76762
+ forcePathStyle: input.forcePathStyle,
76763
+ requestHandler: input.requestHandler
76764
+ });
76765
+ };
76766
+ var cancelRenderOnLambda = (input) => {
76767
+ return internalCancelRenderOnLambda({
76768
+ ...input,
76769
+ forcePathStyle: input.forcePathStyle ?? false,
76770
+ requestHandler: input.requestHandler ?? null,
76771
+ providerSpecifics: awsImplementation
76772
+ });
76773
+ };
76774
+
76614
76775
  // src/p-limit.ts
76615
76776
  var pLimit = (concurrency) => {
76616
76777
  const queue = [];
@@ -76907,6 +77068,7 @@ var validateServeUrl = (serveUrl) => {
76907
77068
 
76908
77069
  // src/make-lambda-payload.ts
76909
77070
  var makeLambdaRenderMediaPayload = async ({
77071
+ enableCancellation,
76910
77072
  rendererFunctionName,
76911
77073
  frameRange,
76912
77074
  framesPerLambda,
@@ -76987,6 +77149,7 @@ var makeLambdaRenderMediaPayload = async ({
76987
77149
  logLevel
76988
77150
  });
76989
77151
  return {
77152
+ enableCancellation,
76990
77153
  rendererFunctionName,
76991
77154
  framesPerLambda,
76992
77155
  concurrency,
@@ -77201,6 +77364,7 @@ var internalRenderMediaOnLambdaRaw = async (input) => {
77201
77364
  };
77202
77365
  var renderMediaOnLambdaOptionalToRequired = (options) => {
77203
77366
  return {
77367
+ enableCancellation: options.enableCancellation ?? false,
77204
77368
  offthreadVideoThreads: options.offthreadVideoThreads ?? null,
77205
77369
  audioBitrate: options.audioBitrate ?? null,
77206
77370
  audioCodec: options.audioCodec ?? null,
@@ -77308,6 +77472,7 @@ var innerRenderStillOnLambda = async (input) => {
77308
77472
  },
77309
77473
  timeoutInTest: 120000,
77310
77474
  retriesRemaining: input.maxRetries,
77475
+ getBinaryPayloadSink: null,
77311
77476
  requestHandler: input.requestHandler
77312
77477
  }).then(() => {
77313
77478
  reject(new Error("Expected response to be streamed"));
@@ -77854,6 +78019,7 @@ var LambdaClientInternals = {
77854
78019
  makeLambdaRenderMediaPayload,
77855
78020
  renderMediaOnLambdaOptionalToRequired,
77856
78021
  internalDeleteRender,
78022
+ internalCancelRenderOnLambda,
77857
78023
  internalGetSites,
77858
78024
  getLifeCycleRules,
77859
78025
  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;
@@ -70525,7 +70525,7 @@ var serializeJSONWithSpecialTypes = ({
70525
70525
  setUsed = true;
70526
70526
  return value;
70527
70527
  }
70528
- if (typeof item === "string" && staticBase !== null && item.startsWith(staticBase)) {
70528
+ if (typeof item === "string" && staticBase !== null && staticBase !== "" && item.startsWith(staticBase)) {
70529
70529
  customFileUsed = true;
70530
70530
  return `${FILE_TOKEN}${item.replace(staticBase + "/", "")}`;
70531
70531
  }
@@ -70536,13 +70536,25 @@ var serializeJSONWithSpecialTypes = ({
70536
70536
  throw new Error("Could not serialize the passed input props to JSON: " + err.message);
70537
70537
  }
70538
70538
  };
70539
+ var resolveFileTokenToUrl = (value) => {
70540
+ const encodedName = value.replace(FILE_TOKEN, "");
70541
+ let name = encodedName;
70542
+ try {
70543
+ name = encodedName.split("/").map(decodeURIComponent).join("/");
70544
+ } catch {}
70545
+ const matchingStaticFile = window.remotion_staticFiles?.find((file) => file.name === name);
70546
+ if (matchingStaticFile) {
70547
+ return matchingStaticFile.src;
70548
+ }
70549
+ return `${window.remotion_staticBase}/${encodedName}`;
70550
+ };
70539
70551
  var deserializeJSONWithSpecialTypes = (data) => {
70540
70552
  return JSON.parse(data, (_, value) => {
70541
70553
  if (typeof value === "string" && value.startsWith(DATE_TOKEN)) {
70542
70554
  return new Date(value.replace(DATE_TOKEN, ""));
70543
70555
  }
70544
70556
  if (typeof value === "string" && value.startsWith(FILE_TOKEN)) {
70545
- return `${window.remotion_staticBase}/${value.replace(FILE_TOKEN, "")}`;
70557
+ return resolveFileTokenToUrl(value);
70546
70558
  }
70547
70559
  return value;
70548
70560
  });
@@ -71520,6 +71532,7 @@ var customOutName = (renderId, bucketName, name) => {
71520
71532
  };
71521
71533
  };
71522
71534
  var overallProgressKey = (renderId) => `${rendersPrefix(renderId)}/progress.json`;
71535
+ var cancellationKey = (renderId) => `${rendersPrefix(renderId)}/cancel.json`;
71523
71536
  var MAX_FUNCTIONS_PER_RENDER = 200;
71524
71537
  var validateDownloadBehavior = (downloadBehavior) => {
71525
71538
  if (downloadBehavior === null || downloadBehavior === undefined) {
@@ -71538,7 +71551,7 @@ var validateDownloadBehavior = (downloadBehavior) => {
71538
71551
  }
71539
71552
  }
71540
71553
  };
71541
- var VERSION = "4.0.514";
71554
+ var VERSION = "4.0.516";
71542
71555
  var isColorSupported = () => {
71543
71556
  const env = process.env || {};
71544
71557
  const isForceDisabled = "NO_COLOR" in env;
@@ -71892,7 +71905,7 @@ var wrapWithErrorHandling = (fn) => {
71892
71905
  };
71893
71906
  };
71894
71907
  var streamingKey = "remotion_buffer:";
71895
- var makeStreamer = (onMessage) => {
71908
+ var makeStreamer = (onMessage, getPayloadSink = null) => {
71896
71909
  const separator = new Uint8Array(streamingKey.length);
71897
71910
  for (let i = 0;i < streamingKey.length; i++) {
71898
71911
  separator[i] = streamingKey.charCodeAt(i);
@@ -71900,6 +71913,7 @@ var makeStreamer = (onMessage) => {
71900
71913
  let unprocessedBuffers = [];
71901
71914
  let outputBuffer = new Uint8Array(0);
71902
71915
  let missingData = null;
71916
+ let activeSink = null;
71903
71917
  const findSeparatorIndex = () => {
71904
71918
  let searchIndex = 0;
71905
71919
  while (true) {
@@ -71958,6 +71972,36 @@ var makeStreamer = (onMessage) => {
71958
71972
  }
71959
71973
  const length = Number(lengthString);
71960
71974
  const status = Number(statusString);
71975
+ const statusType = status === 1 ? "error" : "success";
71976
+ const sink = getPayloadSink ? getPayloadSink(statusType, nonceString, length) : null;
71977
+ if (sink) {
71978
+ const payloadStart = separatorIndex + 1;
71979
+ const available = Math.min(outputBuffer.length - payloadStart, length);
71980
+ const payloadPiece = outputBuffer.subarray(payloadStart, payloadStart + available);
71981
+ const rest = new Uint8Array(outputBuffer.subarray(payloadStart + available));
71982
+ outputBuffer = new Uint8Array(0);
71983
+ missingData = null;
71984
+ return (async () => {
71985
+ if (available > 0) {
71986
+ await sink.write(payloadPiece);
71987
+ }
71988
+ if (available < length) {
71989
+ activeSink = {
71990
+ sink,
71991
+ remaining: length - available,
71992
+ nonce: nonceString,
71993
+ statusType
71994
+ };
71995
+ return;
71996
+ }
71997
+ await sink.end();
71998
+ onMessage(statusType, nonceString, new Uint8Array(0));
71999
+ if (rest.length > 0) {
72000
+ outputBuffer = rest;
72001
+ return processInput();
72002
+ }
72003
+ })();
72004
+ }
71961
72005
  const dataLength = outputBuffer.length - separatorIndex - 1;
71962
72006
  if (dataLength < length) {
71963
72007
  missingData = {
@@ -71969,9 +72013,33 @@ var makeStreamer = (onMessage) => {
71969
72013
  onMessage(status === 1 ? "error" : "success", nonceString, data);
71970
72014
  missingData = null;
71971
72015
  outputBuffer = outputBuffer.subarray(separatorIndex + Number(lengthString) + 1);
71972
- processInput();
72016
+ return processInput();
71973
72017
  };
71974
- const onData = (data) => {
72018
+ const feedSink = async (data) => {
72019
+ if (!activeSink) {
72020
+ throw new Error("No active sink");
72021
+ }
72022
+ const take = Math.min(activeSink.remaining, data.length);
72023
+ await activeSink.sink.write(data.subarray(0, take));
72024
+ activeSink.remaining -= take;
72025
+ if (activeSink.remaining > 0) {
72026
+ return null;
72027
+ }
72028
+ const { sink, nonce, statusType } = activeSink;
72029
+ activeSink = null;
72030
+ await sink.end();
72031
+ onMessage(statusType, nonce, new Uint8Array(0));
72032
+ const leftover = data.subarray(take);
72033
+ return leftover.length > 0 ? leftover : null;
72034
+ };
72035
+ const onDataInner = (data) => {
72036
+ if (activeSink) {
72037
+ return feedSink(data).then((leftover) => {
72038
+ if (leftover) {
72039
+ return onDataInner(leftover);
72040
+ }
72041
+ });
72042
+ }
71975
72043
  unprocessedBuffers.push(data);
71976
72044
  if (missingData) {
71977
72045
  missingData.dataMissing -= data.length;
@@ -71988,7 +72056,15 @@ var makeStreamer = (onMessage) => {
71988
72056
  }
71989
72057
  outputBuffer = newBuffer;
71990
72058
  unprocessedBuffers = [];
71991
- processInput();
72059
+ return processInput();
72060
+ };
72061
+ let chain = Promise.resolve();
72062
+ const onData = (data) => {
72063
+ if (!getPayloadSink) {
72064
+ return onDataInner(data);
72065
+ }
72066
+ chain = chain.then(() => onDataInner(data));
72067
+ return chain;
71992
72068
  };
71993
72069
  return {
71994
72070
  onData,
@@ -71996,6 +72072,15 @@ var makeStreamer = (onMessage) => {
71996
72072
  clear: () => {
71997
72073
  unprocessedBuffers = [];
71998
72074
  outputBuffer = new Uint8Array(0);
72075
+ if (activeSink) {
72076
+ const { sink } = activeSink;
72077
+ activeSink = null;
72078
+ try {
72079
+ Promise.resolve(sink.end()).catch(() => {
72080
+ return;
72081
+ });
72082
+ } catch {}
72083
+ }
71999
72084
  }
72000
72085
  };
72001
72086
  };
@@ -73535,6 +73620,41 @@ var messageTypeIdToMessageType = (messageTypeId) => {
73535
73620
  }
73536
73621
  return types.type;
73537
73622
  };
73623
+ var binaryPayloadSinkForStreamer = (getBinaryPayloadSink) => {
73624
+ return (_statusType, nonce, length) => {
73625
+ const messageType = messageTypeIdToMessageType(nonce);
73626
+ if (formatMap[messageType] !== "binary") {
73627
+ return null;
73628
+ }
73629
+ return getBinaryPayloadSink({
73630
+ messageType,
73631
+ length
73632
+ });
73633
+ };
73634
+ };
73635
+ var writeCancellationSignal = async ({
73636
+ bucketName,
73637
+ renderId,
73638
+ region,
73639
+ expectedBucketOwner,
73640
+ providerSpecifics,
73641
+ forcePathStyle,
73642
+ requestHandler
73643
+ }) => {
73644
+ await providerSpecifics.writeFile({
73645
+ bucketName,
73646
+ key: cancellationKey(renderId),
73647
+ body: JSON.stringify({ cancelledAt: Date.now() }),
73648
+ region,
73649
+ privacy: "private",
73650
+ expectedBucketOwner,
73651
+ downloadBehavior: null,
73652
+ customCredentials: null,
73653
+ forcePathStyle,
73654
+ storageClass: null,
73655
+ requestHandler
73656
+ });
73657
+ };
73538
73658
  var ENABLE_V5_BREAKING_CHANGES2 = NoReactInternals.ENABLE_V5_BREAKING_CHANGES;
73539
73659
  var serializeJSONWithSpecialTypes2 = NoReactInternals.serializeJSONWithSpecialTypes;
73540
73660
  var validateFps2 = NoReactInternals.validateFps;
@@ -74006,7 +74126,8 @@ var callLambdaWithStreamingWithoutRetry = async ({
74006
74126
  region,
74007
74127
  timeoutInTest,
74008
74128
  receivedStreamingPayload,
74009
- requestHandler
74129
+ requestHandler,
74130
+ getBinaryPayloadSink
74010
74131
  }) => {
74011
74132
  const res = await invokeStreamOrTimeout({
74012
74133
  functionName,
@@ -74027,7 +74148,7 @@ var callLambdaWithStreamingWithoutRetry = async ({
74027
74148
  }
74028
74149
  };
74029
74150
  receivedStreamingPayload(message);
74030
- });
74151
+ }, getBinaryPayloadSink ? binaryPayloadSinkForStreamer(getBinaryPayloadSink) : null);
74031
74152
  const dumpBuffers = () => {
74032
74153
  clear();
74033
74154
  };
@@ -74035,25 +74156,28 @@ var callLambdaWithStreamingWithoutRetry = async ({
74035
74156
  globalThis._dumpUnreleasedBuffers.addListener("dump-unreleased-buffers", dumpBuffers);
74036
74157
  }
74037
74158
  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.`);
74159
+ try {
74160
+ for await (const event of events) {
74161
+ if (event.PayloadChunk && event.PayloadChunk.Payload) {
74162
+ await onData(event.PayloadChunk.Payload);
74163
+ }
74164
+ if (event.InvokeComplete) {
74165
+ if (event.InvokeComplete.ErrorCode) {
74166
+ const { consoleDomain } = getAwsRegionMetadata(region);
74167
+ 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}))`;
74168
+ if (event.InvokeComplete.ErrorCode === "Unhandled") {
74169
+ throw new Error(`Lambda function ${functionName} failed with an unhandled error: ${event.InvokeComplete.ErrorDetails} See ${logs} to see the logs of this invocation.`);
74170
+ }
74171
+ 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
74172
  }
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
74173
  }
74051
74174
  }
74175
+ } finally {
74176
+ if (globalThis._dumpUnreleasedBuffers) {
74177
+ globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74178
+ }
74179
+ clear();
74052
74180
  }
74053
- if (globalThis._dumpUnreleasedBuffers) {
74054
- globalThis._dumpUnreleasedBuffers.removeListener("dump-unreleased-buffers", dumpBuffers);
74055
- }
74056
- clear();
74057
74181
  };
74058
74182
  var callFunctionWithStreamingImplementation = async (options) => {
74059
74183
  try {
@@ -76438,6 +76562,42 @@ var awsImplementation = {
76438
76562
  getBucketPrefix: () => REMOTION_BUCKET_PREFIX
76439
76563
  };
76440
76564
 
76565
+ // src/cancel-render-on-lambda.ts
76566
+ var internalCancelRenderOnLambda = async (input) => {
76567
+ const expectedBucketOwner = await input.providerSpecifics.getAccountId({
76568
+ region: input.region
76569
+ });
76570
+ const progress = await getOverallProgressFromStorage({
76571
+ bucketName: input.bucketName,
76572
+ expectedBucketOwner,
76573
+ region: input.region,
76574
+ renderId: input.renderId,
76575
+ providerSpecifics: input.providerSpecifics,
76576
+ forcePathStyle: input.forcePathStyle,
76577
+ requestHandler: input.requestHandler
76578
+ });
76579
+ if (progress.cancellationEnabled !== true) {
76580
+ throw new Error(`Cannot cancel render ${input.renderId}: The render was not started with enableCancellation: true.`);
76581
+ }
76582
+ await writeCancellationSignal({
76583
+ bucketName: input.bucketName,
76584
+ renderId: input.renderId,
76585
+ region: input.region,
76586
+ expectedBucketOwner,
76587
+ providerSpecifics: input.providerSpecifics,
76588
+ forcePathStyle: input.forcePathStyle,
76589
+ requestHandler: input.requestHandler
76590
+ });
76591
+ };
76592
+ var cancelRenderOnLambda = (input) => {
76593
+ return internalCancelRenderOnLambda({
76594
+ ...input,
76595
+ forcePathStyle: input.forcePathStyle ?? false,
76596
+ requestHandler: input.requestHandler ?? null,
76597
+ providerSpecifics: awsImplementation
76598
+ });
76599
+ };
76600
+
76441
76601
  // src/p-limit.ts
76442
76602
  var pLimit = (concurrency) => {
76443
76603
  const queue = [];
@@ -76734,6 +76894,7 @@ var validateServeUrl = (serveUrl) => {
76734
76894
 
76735
76895
  // src/make-lambda-payload.ts
76736
76896
  var makeLambdaRenderMediaPayload = async ({
76897
+ enableCancellation,
76737
76898
  rendererFunctionName,
76738
76899
  frameRange,
76739
76900
  framesPerLambda,
@@ -76814,6 +76975,7 @@ var makeLambdaRenderMediaPayload = async ({
76814
76975
  logLevel
76815
76976
  });
76816
76977
  return {
76978
+ enableCancellation,
76817
76979
  rendererFunctionName,
76818
76980
  framesPerLambda,
76819
76981
  concurrency,
@@ -77028,6 +77190,7 @@ var internalRenderMediaOnLambdaRaw = async (input) => {
77028
77190
  };
77029
77191
  var renderMediaOnLambdaOptionalToRequired = (options) => {
77030
77192
  return {
77193
+ enableCancellation: options.enableCancellation ?? false,
77031
77194
  offthreadVideoThreads: options.offthreadVideoThreads ?? null,
77032
77195
  audioBitrate: options.audioBitrate ?? null,
77033
77196
  audioCodec: options.audioCodec ?? null,
@@ -77135,6 +77298,7 @@ var innerRenderStillOnLambda = async (input) => {
77135
77298
  },
77136
77299
  timeoutInTest: 120000,
77137
77300
  retriesRemaining: input.maxRetries,
77301
+ getBinaryPayloadSink: null,
77138
77302
  requestHandler: input.requestHandler
77139
77303
  }).then(() => {
77140
77304
  reject(new Error("Expected response to be streamed"));
@@ -77802,6 +77966,7 @@ var LambdaClientInternals = {
77802
77966
  makeLambdaRenderMediaPayload,
77803
77967
  renderMediaOnLambdaOptionalToRequired,
77804
77968
  internalDeleteRender,
77969
+ internalCancelRenderOnLambda,
77805
77970
  internalGetSites,
77806
77971
  getLifeCycleRules,
77807
77972
  awsImplementation,
@@ -77843,6 +78008,7 @@ export {
77843
78008
  estimatePrice,
77844
78009
  deleteRender,
77845
78010
  deleteFunction,
78011
+ cancelRenderOnLambda,
77846
78012
  appRouterWebhook,
77847
78013
  LambdaClientInternals
77848
78014
  };
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.516",
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.516",
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.516",
33
33
  "eslint": "9.19.0",
34
34
  "next": "16.2.11",
35
35
  "@types/mime-types": "2.1.1",