@remotion/lambda 3.1.11 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,6 +31,11 @@ const presignUrl = async ({ region, bucketName, objectKey, checkIfObjectExists =
31
31
  if (err.name === 'NotFound') {
32
32
  return null;
33
33
  }
34
+ if (err.message === 'UnknownError' ||
35
+ err.$metadata
36
+ .httpStatusCode === 403) {
37
+ throw new Error(`Unable to access item "${objectKey}" from bucket "${bucketName}". You must have permission for both "s3:GetObject" and "s3:ListBucket" actions.`);
38
+ }
34
39
  throw err;
35
40
  }
36
41
  }
@@ -27,45 +27,54 @@ const validate_serveurl_1 = require("../shared/validate-serveurl");
27
27
  * @returns {Promise<RenderMediaOnLambdaOutput>} See documentation for detailed structure
28
28
  */
29
29
  const renderMediaOnLambda = async ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, }) => {
30
+ var _a;
30
31
  const actualCodec = (0, validate_lambda_codec_1.validateLambdaCodec)(codec);
31
32
  (0, validate_serveurl_1.validateServeUrl)(serveUrl);
32
33
  (0, validate_frames_per_lambda_1.validateFramesPerLambda)(framesPerLambda !== null && framesPerLambda !== void 0 ? framesPerLambda : null);
33
34
  (0, validate_download_behavior_1.validateDownloadBehavior)(downloadBehavior);
34
35
  const realServeUrl = await (0, convert_to_serve_url_1.convertToServeUrl)(serveUrl, region);
35
- const res = await (0, call_lambda_1.callLambda)({
36
- functionName,
37
- type: constants_1.LambdaRoutines.start,
38
- payload: {
39
- framesPerLambda: framesPerLambda !== null && framesPerLambda !== void 0 ? framesPerLambda : null,
40
- composition,
41
- serveUrl: realServeUrl,
42
- inputProps,
43
- codec: actualCodec,
44
- imageFormat,
45
- crf,
46
- envVariables,
47
- pixelFormat,
48
- proResProfile,
49
- quality,
50
- maxRetries,
51
- privacy,
52
- logLevel: logLevel !== null && logLevel !== void 0 ? logLevel : 'info',
53
- frameRange: frameRange !== null && frameRange !== void 0 ? frameRange : null,
54
- outName: outName !== null && outName !== void 0 ? outName : null,
55
- timeoutInMilliseconds: timeoutInMilliseconds !== null && timeoutInMilliseconds !== void 0 ? timeoutInMilliseconds : 30000,
56
- chromiumOptions: chromiumOptions !== null && chromiumOptions !== void 0 ? chromiumOptions : {},
57
- scale: scale !== null && scale !== void 0 ? scale : 1,
58
- everyNthFrame: everyNthFrame !== null && everyNthFrame !== void 0 ? everyNthFrame : 1,
59
- numberOfGifLoops: numberOfGifLoops !== null && numberOfGifLoops !== void 0 ? numberOfGifLoops : 0,
60
- concurrencyPerLambda: concurrencyPerLambda !== null && concurrencyPerLambda !== void 0 ? concurrencyPerLambda : 1,
61
- downloadBehavior: downloadBehavior !== null && downloadBehavior !== void 0 ? downloadBehavior : { type: 'play-in-browser' },
62
- },
63
- region,
64
- });
65
- return {
66
- renderId: res.renderId,
67
- bucketName: res.bucketName,
68
- };
36
+ try {
37
+ const res = await (0, call_lambda_1.callLambda)({
38
+ functionName,
39
+ type: constants_1.LambdaRoutines.start,
40
+ payload: {
41
+ framesPerLambda: framesPerLambda !== null && framesPerLambda !== void 0 ? framesPerLambda : null,
42
+ composition,
43
+ serveUrl: realServeUrl,
44
+ inputProps,
45
+ codec: actualCodec,
46
+ imageFormat,
47
+ crf,
48
+ envVariables,
49
+ pixelFormat,
50
+ proResProfile,
51
+ quality,
52
+ maxRetries,
53
+ privacy,
54
+ logLevel: logLevel !== null && logLevel !== void 0 ? logLevel : 'info',
55
+ frameRange: frameRange !== null && frameRange !== void 0 ? frameRange : null,
56
+ outName: outName !== null && outName !== void 0 ? outName : null,
57
+ timeoutInMilliseconds: timeoutInMilliseconds !== null && timeoutInMilliseconds !== void 0 ? timeoutInMilliseconds : 30000,
58
+ chromiumOptions: chromiumOptions !== null && chromiumOptions !== void 0 ? chromiumOptions : {},
59
+ scale: scale !== null && scale !== void 0 ? scale : 1,
60
+ everyNthFrame: everyNthFrame !== null && everyNthFrame !== void 0 ? everyNthFrame : 1,
61
+ numberOfGifLoops: numberOfGifLoops !== null && numberOfGifLoops !== void 0 ? numberOfGifLoops : 0,
62
+ concurrencyPerLambda: concurrencyPerLambda !== null && concurrencyPerLambda !== void 0 ? concurrencyPerLambda : 1,
63
+ downloadBehavior: downloadBehavior !== null && downloadBehavior !== void 0 ? downloadBehavior : { type: 'play-in-browser' },
64
+ },
65
+ region,
66
+ });
67
+ return {
68
+ renderId: res.renderId,
69
+ bucketName: res.bucketName,
70
+ };
71
+ }
72
+ catch (err) {
73
+ if ((_a = err.stack) === null || _a === void 0 ? void 0 : _a.includes('UnrecognizedClientException')) {
74
+ throw new Error('UnrecognizedClientException: The AWS credentials provided were probably mixed up. Learn how to fix this issue here: https://remotion.dev/docs/lambda/troubleshooting/unrecognizedclientexception');
75
+ }
76
+ throw err;
77
+ }
69
78
  };
70
79
  exports.renderMediaOnLambda = renderMediaOnLambda;
71
80
  /**
@@ -21,36 +21,45 @@ const convert_to_serve_url_1 = require("../shared/convert-to-serve-url");
21
21
  * @returns {Promise<RenderStillOnLambdaOutput>} See documentation for exact response structure.
22
22
  */
23
23
  const renderStillOnLambda = async ({ functionName, serveUrl, inputProps, imageFormat, envVariables, quality, region, maxRetries, composition, privacy, frame, logLevel, outName, timeoutInMilliseconds, chromiumOptions, scale, downloadBehavior, }) => {
24
+ var _a;
24
25
  const realServeUrl = await (0, convert_to_serve_url_1.convertToServeUrl)(serveUrl, region);
25
- const res = await (0, call_lambda_1.callLambda)({
26
- functionName,
27
- type: constants_1.LambdaRoutines.still,
28
- payload: {
29
- composition,
30
- serveUrl: realServeUrl,
31
- inputProps,
32
- imageFormat,
33
- envVariables,
34
- quality,
35
- maxRetries: maxRetries !== null && maxRetries !== void 0 ? maxRetries : constants_1.DEFAULT_MAX_RETRIES,
36
- frame: frame !== null && frame !== void 0 ? frame : 0,
37
- privacy,
38
- attempt: 1,
39
- logLevel: logLevel !== null && logLevel !== void 0 ? logLevel : 'info',
40
- outName: outName !== null && outName !== void 0 ? outName : null,
41
- timeoutInMilliseconds: timeoutInMilliseconds !== null && timeoutInMilliseconds !== void 0 ? timeoutInMilliseconds : 30000,
42
- chromiumOptions: chromiumOptions !== null && chromiumOptions !== void 0 ? chromiumOptions : {},
43
- scale: scale !== null && scale !== void 0 ? scale : 1,
44
- downloadBehavior: downloadBehavior !== null && downloadBehavior !== void 0 ? downloadBehavior : { type: 'play-in-browser' },
45
- },
46
- region,
47
- });
48
- return {
49
- estimatedPrice: res.estimatedPrice,
50
- url: res.output,
51
- sizeInBytes: res.size,
52
- bucketName: res.bucketName,
53
- renderId: res.renderId,
54
- };
26
+ try {
27
+ const res = await (0, call_lambda_1.callLambda)({
28
+ functionName,
29
+ type: constants_1.LambdaRoutines.still,
30
+ payload: {
31
+ composition,
32
+ serveUrl: realServeUrl,
33
+ inputProps,
34
+ imageFormat,
35
+ envVariables,
36
+ quality,
37
+ maxRetries: maxRetries !== null && maxRetries !== void 0 ? maxRetries : constants_1.DEFAULT_MAX_RETRIES,
38
+ frame: frame !== null && frame !== void 0 ? frame : 0,
39
+ privacy,
40
+ attempt: 1,
41
+ logLevel: logLevel !== null && logLevel !== void 0 ? logLevel : 'info',
42
+ outName: outName !== null && outName !== void 0 ? outName : null,
43
+ timeoutInMilliseconds: timeoutInMilliseconds !== null && timeoutInMilliseconds !== void 0 ? timeoutInMilliseconds : 30000,
44
+ chromiumOptions: chromiumOptions !== null && chromiumOptions !== void 0 ? chromiumOptions : {},
45
+ scale: scale !== null && scale !== void 0 ? scale : 1,
46
+ downloadBehavior: downloadBehavior !== null && downloadBehavior !== void 0 ? downloadBehavior : { type: 'play-in-browser' },
47
+ },
48
+ region,
49
+ });
50
+ return {
51
+ estimatedPrice: res.estimatedPrice,
52
+ url: res.output,
53
+ sizeInBytes: res.size,
54
+ bucketName: res.bucketName,
55
+ renderId: res.renderId,
56
+ };
57
+ }
58
+ catch (err) {
59
+ if ((_a = err.stack) === null || _a === void 0 ? void 0 : _a.includes('UnrecognizedClientException')) {
60
+ throw new Error('UnrecognizedClientException: The AWS credentials provided were probably mixed up. Learn how to fix this issue here: https://remotion.dev/docs/lambda/troubleshooting/unrecognizedclientexception');
61
+ }
62
+ throw err;
63
+ }
55
64
  };
56
65
  exports.renderStillOnLambda = renderStillOnLambda;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findOutputFileInBucket = void 0;
4
4
  const client_s3_1 = require("@aws-sdk/client-s3");
5
+ const suggested_policy_1 = require("../../api/iam-validation/suggested-policy");
5
6
  const aws_clients_1 = require("../../shared/aws-clients");
6
7
  const expected_out_name_1 = require("./expected-out-name");
7
8
  const get_output_url_from_metadata_1 = require("./get-output-url-from-metadata");
@@ -26,6 +27,11 @@ const findOutputFileInBucket = async ({ region, renderMetadata, bucketName, }) =
26
27
  if (err.name === 'NotFound') {
27
28
  return null;
28
29
  }
30
+ if (err.message === 'UnknownError' ||
31
+ err.$metadata
32
+ .httpStatusCode === 403) {
33
+ throw new Error(`Unable to access item "${expectedOutData.key}" from bucket "${expectedOutData.renderBucketName}". The "${suggested_policy_1.ROLE_NAME}" role must have permission for both "s3:GetObject" and "s3:ListBucket" actions.`);
34
+ }
29
35
  throw err;
30
36
  }
31
37
  };
@@ -39,6 +39,27 @@ const timer_1 = require("./helpers/timer");
39
39
  const validate_composition_1 = require("./helpers/validate-composition");
40
40
  const write_lambda_error_1 = require("./helpers/write-lambda-error");
41
41
  const write_post_render_data_1 = require("./helpers/write-post-render-data");
42
+ const callFunctionWithRetry = async (payload, retries = 0) => {
43
+ try {
44
+ await (0, aws_clients_1.getLambdaClient)((0, get_current_region_1.getCurrentRegionInFunction)()).send(new client_lambda_1.InvokeCommand({
45
+ FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME,
46
+ // @ts-expect-error
47
+ Payload: JSON.stringify(payload),
48
+ InvocationType: 'Event',
49
+ }), {});
50
+ }
51
+ catch (err) {
52
+ if (err.name === 'ResourceConflictException') {
53
+ if (retries > 10) {
54
+ throw err;
55
+ }
56
+ await new Promise((resolve) => {
57
+ setTimeout(resolve, 1000);
58
+ });
59
+ return callFunctionWithRetry(payload, retries + 1);
60
+ }
61
+ }
62
+ };
42
63
  const innerLaunchHandler = async (params, options) => {
43
64
  var _a, _b;
44
65
  if (params.type !== constants_1.LambdaRoutines.launch) {
@@ -167,12 +188,7 @@ const innerLaunchHandler = async (params, options) => {
167
188
  });
168
189
  await Promise.all(lambdaPayloads.map(async (payload, index) => {
169
190
  const callingLambdaTimer = (0, timer_1.timer)('Calling chunk ' + index);
170
- await (0, aws_clients_1.getLambdaClient)((0, get_current_region_1.getCurrentRegionInFunction)()).send(new client_lambda_1.InvokeCommand({
171
- FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME,
172
- // @ts-expect-error
173
- Payload: JSON.stringify(payload),
174
- InvocationType: 'Event',
175
- }), {});
191
+ await callFunctionWithRetry(payload);
176
192
  callingLambdaTimer.end();
177
193
  }));
178
194
  reqSend.end();
@@ -224,7 +224,7 @@ export declare type RenderMetadata = {
224
224
  renderId: string;
225
225
  outName: OutNameInput | undefined;
226
226
  };
227
- export declare type LambdaVersions = '2022-08-04' | '2022-08-02' | '2022-08-01' | '2022-07-28' | '2022-07-27' | '2022-07-25' | '2022-07-23' | '2022-07-20' | '2022-07-18' | '2022-07-15' | '2022-07-14' | '2022-07-12' | '2022-07-10' | '2022-07-09' | '2022-07-08' | '2022-07-04' | '2022-06-30' | '2022-06-29' | '2022-06-25' | '2022-06-22' | '2022-06-21' | '2022-06-14' | '2022-06-08' | '2022-06-07' | '2022-06-02' | '2022-05-31' | '2022-05-28' | '2022-05-27' | '2022-05-19' | '2022-05-16' | '2022-05-11' | '2022-05-07' | '2022-05-06' | '2022-05-03' | '2022-04-20' | '2022-04-19' | '2022-04-18' | '2022-04-09' | '2022-04-08' | '2022-04-05' | '2022-04-02' | '2022-03-29' | '2022-03-17' | '2022-03-02' | '2022-03-01' | '2022-02-27' | '2022-02-14' | '2022-02-12' | '2022-02-09' | '2022-02-08' | '2022-02-07' | '2022-02-06' | '2022-02-05' | '2022-02-04' | '2022-02-03' | '2022-01-23' | '2022-01-19' | '2022-01-11' | '2022-01-10' | '2022-01-09' | '2022-01-06' | '2022-01-05' | '2021-12-22' | '2021-12-17' | '2021-12-16' | '2021-12-15' | '2021-12-14' | '2021-12-13' | '2021-12-11' | '2021-12-10' | '2021-12-04' | '2021-11-29' | '2021-11-27' | '2021-11-24' | '2021-11-22' | '2021-11-19' | '2021-11-18' | '2021-11-15' | '2021-11-12' | '2021-11-10' | '2021-11-01' | '2021-10-29' | '2021-10-27' | '2021-10-21' | '2021-10-19' | '2021-10-07' | '2021-10-03' | '2021-10-01' | '2021-09-15' | '2021-09-06' | '2021-08-06' | '2021-07-14' | '2021-07-05' | '2021-07-02' | '2021-06-23' | 'n/a';
227
+ export declare type LambdaVersions = '2022-08-08' | '2022-08-04' | '2022-08-02' | '2022-08-01' | '2022-07-28' | '2022-07-27' | '2022-07-25' | '2022-07-23' | '2022-07-20' | '2022-07-18' | '2022-07-15' | '2022-07-14' | '2022-07-12' | '2022-07-10' | '2022-07-09' | '2022-07-08' | '2022-07-04' | '2022-06-30' | '2022-06-29' | '2022-06-25' | '2022-06-22' | '2022-06-21' | '2022-06-14' | '2022-06-08' | '2022-06-07' | '2022-06-02' | '2022-05-31' | '2022-05-28' | '2022-05-27' | '2022-05-19' | '2022-05-16' | '2022-05-11' | '2022-05-07' | '2022-05-06' | '2022-05-03' | '2022-04-20' | '2022-04-19' | '2022-04-18' | '2022-04-09' | '2022-04-08' | '2022-04-05' | '2022-04-02' | '2022-03-29' | '2022-03-17' | '2022-03-02' | '2022-03-01' | '2022-02-27' | '2022-02-14' | '2022-02-12' | '2022-02-09' | '2022-02-08' | '2022-02-07' | '2022-02-06' | '2022-02-05' | '2022-02-04' | '2022-02-03' | '2022-01-23' | '2022-01-19' | '2022-01-11' | '2022-01-10' | '2022-01-09' | '2022-01-06' | '2022-01-05' | '2021-12-22' | '2021-12-17' | '2021-12-16' | '2021-12-15' | '2021-12-14' | '2021-12-13' | '2021-12-11' | '2021-12-10' | '2021-12-04' | '2021-11-29' | '2021-11-27' | '2021-11-24' | '2021-11-22' | '2021-11-19' | '2021-11-18' | '2021-11-15' | '2021-11-12' | '2021-11-10' | '2021-11-01' | '2021-10-29' | '2021-10-27' | '2021-10-21' | '2021-10-19' | '2021-10-07' | '2021-10-03' | '2021-10-01' | '2021-09-15' | '2021-09-06' | '2021-08-06' | '2021-07-14' | '2021-07-05' | '2021-07-02' | '2021-06-23' | 'n/a';
228
228
  export declare const CURRENT_VERSION: LambdaVersions;
229
229
  export declare type PostRenderData = {
230
230
  cost: {
@@ -84,6 +84,6 @@ var LambdaRoutines;
84
84
  LambdaRoutines["renderer"] = "renderer";
85
85
  LambdaRoutines["still"] = "still";
86
86
  })(LambdaRoutines = exports.LambdaRoutines || (exports.LambdaRoutines = {}));
87
- exports.CURRENT_VERSION = '2022-08-04';
87
+ exports.CURRENT_VERSION = '2022-08-08';
88
88
  exports.LAMBDA_CONCURRENCY_LIMIT_QUOTA = 'L-B99A9384';
89
89
  exports.LAMBDA_BURST_LIMIT_QUOTA = 'L-548AE339';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/lambda",
3
- "version": "3.1.11",
3
+ "version": "3.2.0",
4
4
  "description": "Distributed renderer for Remotion based on AWS Lambda",
5
5
  "main": "dist/index.js",
6
6
  "sideEffects": false,
@@ -32,12 +32,12 @@
32
32
  "@aws-sdk/client-service-quotas": "3.58.0",
33
33
  "@aws-sdk/lib-storage": "3.58.0",
34
34
  "@aws-sdk/s3-request-presigner": "3.58.0",
35
- "@remotion/bundler": "3.1.11",
36
- "@remotion/cli": "3.1.11",
37
- "@remotion/renderer": "3.1.11",
35
+ "@remotion/bundler": "3.2.0",
36
+ "@remotion/cli": "3.2.0",
37
+ "@remotion/renderer": "3.2.0",
38
38
  "aws-policies": "^1.0.1",
39
39
  "mime-types": "2.1.34",
40
- "remotion": "3.1.11"
40
+ "remotion": "3.2.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "react": ">=16.8.0",
@@ -62,5 +62,5 @@
62
62
  "publishConfig": {
63
63
  "access": "public"
64
64
  },
65
- "gitHead": "bc4184c9faf944a3bac2fd56bdf990cc74e3ec94"
65
+ "gitHead": "47b188161a9e922a247eaa998e72d37af9137707"
66
66
  }
Binary file
@@ -1,6 +0,0 @@
1
- export declare const formatBytes: (number: number, options?: Intl.NumberFormatOptions & {
2
- locale: string;
3
- bits?: boolean;
4
- binary?: boolean;
5
- signed: boolean;
6
- }) => string;
@@ -1,103 +0,0 @@
1
- 'use strict';
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatBytes = void 0;
4
- const BYTE_UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
5
- const BIBYTE_UNITS = [
6
- 'B',
7
- 'kiB',
8
- 'MiB',
9
- 'GiB',
10
- 'TiB',
11
- 'PiB',
12
- 'EiB',
13
- 'ZiB',
14
- 'YiB',
15
- ];
16
- const BIT_UNITS = [
17
- 'b',
18
- 'kbit',
19
- 'Mbit',
20
- 'Gbit',
21
- 'Tbit',
22
- 'Pbit',
23
- 'Ebit',
24
- 'Zbit',
25
- 'Ybit',
26
- ];
27
- const BIBIT_UNITS = [
28
- 'b',
29
- 'kibit',
30
- 'Mibit',
31
- 'Gibit',
32
- 'Tibit',
33
- 'Pibit',
34
- 'Eibit',
35
- 'Zibit',
36
- 'Yibit',
37
- ];
38
- /*
39
- Formats the given number using `Number#toLocaleString`.
40
- - If locale is a string, the value is expected to be a locale-key (for example: `de`).
41
- - If locale is true, the system default locale is used for translation.
42
- - If no value for locale is specified, the number is returned unmodified.
43
- */
44
- const toLocaleString = (number, locale, options) => {
45
- if (typeof locale === 'string' || Array.isArray(locale)) {
46
- return number.toLocaleString(locale, options);
47
- }
48
- if (locale === true || options !== undefined) {
49
- return number.toLocaleString(undefined, options);
50
- }
51
- return String(number);
52
- };
53
- const formatBytes = (number, options = {
54
- locale: 'en-US',
55
- signed: false,
56
- maximumFractionDigits: 1,
57
- }) => {
58
- if (!Number.isFinite(number)) {
59
- throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`);
60
- }
61
- options = { bits: false, binary: false, ...options };
62
- const UNITS = options.bits
63
- ? options.binary
64
- ? BIBIT_UNITS
65
- : BIT_UNITS
66
- : options.binary
67
- ? BIBYTE_UNITS
68
- : BYTE_UNITS;
69
- if (options.signed && number === 0) {
70
- return `0 $ {
71
- UNITS[0]
72
- }`;
73
- }
74
- const isNegative = number < 0;
75
- const prefix = isNegative ? '-' : options.signed ? '+' : '';
76
- if (isNegative) {
77
- number = -number;
78
- }
79
- let localeOptions;
80
- if (options.minimumFractionDigits !== undefined) {
81
- localeOptions = {
82
- minimumFractionDigits: options.minimumFractionDigits,
83
- };
84
- }
85
- if (options.maximumFractionDigits !== undefined) {
86
- localeOptions = {
87
- maximumFractionDigits: options.maximumFractionDigits,
88
- ...localeOptions,
89
- };
90
- }
91
- if (number < 1) {
92
- const numString = toLocaleString(number, options.locale, localeOptions);
93
- return prefix + numString + ' ' + UNITS[0];
94
- }
95
- const exponent = Math.min(Math.floor(options.binary
96
- ? Math.log(number) / Math.log(1024)
97
- : Math.log10(number) / 3), UNITS.length - 1);
98
- number /= (options.binary ? 1024 : 1000) ** exponent;
99
- const numberString = toLocaleString(Number(number), options.locale, localeOptions);
100
- const unit = UNITS[exponent];
101
- return prefix + numberString + ' ' + unit;
102
- };
103
- exports.formatBytes = formatBytes;
@@ -1 +0,0 @@
1
- export declare const chunk: <T>(input: T[], size: number) => T[][];
@@ -1,11 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.chunk = void 0;
4
- const chunk = (input, size) => {
5
- return input.reduce((arr, item, idx) => {
6
- return idx % size === 0
7
- ? [...arr, [item]]
8
- : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]];
9
- }, []);
10
- };
11
- exports.chunk = chunk;