@remotion/studio-server 4.0.500 → 4.0.502

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.
Files changed (34) hide show
  1. package/dist/codemods/update-inline-caption-patches.d.ts +1 -0
  2. package/dist/codemods/update-inline-caption-patches.js +5 -0
  3. package/dist/codemods/update-sequence-props/update-sequence-props.d.ts +2 -35
  4. package/dist/codemods/update-sequence-props/update-sequence-props.js +7 -718
  5. package/dist/helpers/css-shorthand-properties.d.ts +9 -3
  6. package/dist/helpers/css-shorthand-properties.js +16 -2
  7. package/dist/helpers/parse-border-radius-shorthand.d.ts +7 -0
  8. package/dist/helpers/parse-border-radius-shorthand.js +53 -0
  9. package/dist/helpers/resolve-composition-component.js +80 -52
  10. package/dist/preview-server/element-install-state.d.ts +16 -0
  11. package/dist/preview-server/element-install-state.js +43 -1
  12. package/dist/preview-server/handler.js +1 -1
  13. package/dist/preview-server/parse-body.d.ts +6 -1
  14. package/dist/preview-server/parse-body.js +60 -9
  15. package/dist/preview-server/routes/can-update-default-props.d.ts +1 -1
  16. package/dist/preview-server/routes/can-update-default-props.js +5 -132
  17. package/dist/preview-server/routes/can-update-sequence-props.d.ts +3 -2
  18. package/dist/preview-server/routes/can-update-sequence-props.js +130 -12
  19. package/dist/preview-server/routes/find-in-file.d.ts +1 -9
  20. package/dist/preview-server/routes/find-in-file.js +4 -17
  21. package/dist/preview-server/routes/insert-element.js +40 -20
  22. package/dist/preview-server/routes/insert-jsx-element.js +5 -5
  23. package/dist/preview-server/routes/save-sequence-props.js +73 -8
  24. package/dist/preview-server/sequence-props-watchers.js +2 -1
  25. package/dist/preview-server/studio-protocol/handle-discovery.d.ts +11 -0
  26. package/dist/preview-server/studio-protocol/handle-discovery.js +99 -0
  27. package/dist/preview-server/studio-protocol/handle-install.d.ts +10 -0
  28. package/dist/preview-server/studio-protocol/handle-install.js +139 -0
  29. package/dist/preview-server/studio-protocol/origin-policy.d.ts +10 -0
  30. package/dist/preview-server/studio-protocol/origin-policy.js +40 -0
  31. package/dist/preview-server/studio-protocol/protocol-response.d.ts +7 -0
  32. package/dist/preview-server/studio-protocol/protocol-response.js +13 -0
  33. package/dist/routes.js +14 -154
  34. package/package.json +9 -7
@@ -3,20 +3,26 @@ export type CssShorthandProperty = {
3
3
  readonly parentKey: string;
4
4
  readonly shorthand: string;
5
5
  readonly longhands: readonly string[];
6
- readonly parse: (value: string) => ParsedCssShorthand | null;
6
+ readonly parse: (value: unknown) => ParsedCssShorthand | null;
7
7
  readonly isUnsupportedProperty: (propertyName: string) => boolean;
8
8
  };
9
9
  export declare const cssShorthandProperties: readonly [{
10
10
  readonly parentKey: "style";
11
11
  readonly shorthand: "background";
12
12
  readonly longhands: readonly ["backgroundColor", "backgroundImage", "backgroundPosition", "backgroundSize", "backgroundRepeat", "backgroundOrigin", "backgroundClip", "backgroundAttachment"];
13
- readonly parse: (value: string) => import("./parse-background-shorthand").ParsedBackgroundShorthand | null;
13
+ readonly parse: (value: unknown) => import("./parse-background-shorthand").ParsedBackgroundShorthand | null;
14
14
  readonly isUnsupportedProperty: () => false;
15
15
  }, {
16
16
  readonly parentKey: "style";
17
17
  readonly shorthand: "border";
18
18
  readonly longhands: readonly ["borderWidth", "borderStyle", "borderColor"];
19
- readonly parse: (value: string) => import("./parse-border-shorthand").ParsedBorderShorthand | null;
19
+ readonly parse: (value: unknown) => import("./parse-border-shorthand").ParsedBorderShorthand | null;
20
+ readonly isUnsupportedProperty: (propertyName: string) => boolean;
21
+ }, {
22
+ readonly parentKey: "style";
23
+ readonly shorthand: "borderRadius";
24
+ readonly longhands: readonly ["borderTopLeftRadius", "borderTopRightRadius", "borderBottomRightRadius", "borderBottomLeftRadius"];
25
+ readonly parse: (value: unknown) => import("./parse-border-radius-shorthand").ParsedBorderRadiusShorthand | null;
20
26
  readonly isUnsupportedProperty: (propertyName: string) => boolean;
21
27
  }];
22
28
  export declare const getCssShorthandForLonghand: ({ parentKey, longhand, }: {
@@ -2,15 +2,28 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCssShorthandsForUpdates = exports.getCssShorthandForLonghand = exports.cssShorthandProperties = void 0;
4
4
  const parse_background_shorthand_1 = require("./parse-background-shorthand");
5
+ const parse_border_radius_shorthand_1 = require("./parse-border-radius-shorthand");
5
6
  const parse_border_shorthand_1 = require("./parse-border-shorthand");
6
7
  const borderSidePropertyRegex = /^border(?:Top|Right|Bottom|Left)(?:Width|Style|Color)?$/;
7
8
  const borderShorthand = {
8
9
  parentKey: 'style',
9
10
  shorthand: 'border',
10
11
  longhands: ['borderWidth', 'borderStyle', 'borderColor'],
11
- parse: parse_border_shorthand_1.parseBorderShorthand,
12
+ parse: (value) => typeof value === 'string' ? (0, parse_border_shorthand_1.parseBorderShorthand)(value) : null,
12
13
  isUnsupportedProperty: (propertyName) => borderSidePropertyRegex.test(propertyName),
13
14
  };
15
+ const borderRadiusShorthand = {
16
+ parentKey: 'style',
17
+ shorthand: 'borderRadius',
18
+ longhands: [
19
+ 'borderTopLeftRadius',
20
+ 'borderTopRightRadius',
21
+ 'borderBottomRightRadius',
22
+ 'borderBottomLeftRadius',
23
+ ],
24
+ parse: parse_border_radius_shorthand_1.parseBorderRadiusShorthand,
25
+ isUnsupportedProperty: (propertyName) => /^border(?:StartStart|StartEnd|EndStart|EndEnd)Radius$/.test(propertyName),
26
+ };
14
27
  const backgroundShorthand = {
15
28
  parentKey: 'style',
16
29
  shorthand: 'background',
@@ -24,12 +37,13 @@ const backgroundShorthand = {
24
37
  'backgroundClip',
25
38
  'backgroundAttachment',
26
39
  ],
27
- parse: parse_background_shorthand_1.parseBackgroundShorthand,
40
+ parse: (value) => typeof value === 'string' ? (0, parse_background_shorthand_1.parseBackgroundShorthand)(value) : null,
28
41
  isUnsupportedProperty: () => false,
29
42
  };
30
43
  exports.cssShorthandProperties = [
31
44
  backgroundShorthand,
32
45
  borderShorthand,
46
+ borderRadiusShorthand,
33
47
  ];
34
48
  const getCssShorthandForLonghand = ({ parentKey, longhand, }) => {
35
49
  var _a;
@@ -0,0 +1,7 @@
1
+ export type ParsedBorderRadiusShorthand = {
2
+ borderTopLeftRadius: number;
3
+ borderTopRightRadius: number;
4
+ borderBottomRightRadius: number;
5
+ borderBottomLeftRadius: number;
6
+ };
7
+ export declare const parseBorderRadiusShorthand: (value: unknown) => ParsedBorderRadiusShorthand | null;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseBorderRadiusShorthand = void 0;
4
+ const parsePixelRadius = (value) => {
5
+ if (value === '0') {
6
+ return 0;
7
+ }
8
+ const match = value.match(/^(\d+(?:\.\d+)?|\.\d+)px$/i);
9
+ return match ? Number(match[1]) : null;
10
+ };
11
+ const expandBorderRadius = (values) => {
12
+ if (values.length === 1) {
13
+ return [values[0], values[0], values[0], values[0]];
14
+ }
15
+ if (values.length === 2) {
16
+ return [values[0], values[1], values[0], values[1]];
17
+ }
18
+ if (values.length === 3) {
19
+ return [values[0], values[1], values[2], values[1]];
20
+ }
21
+ return [values[0], values[1], values[2], values[3]];
22
+ };
23
+ const parseBorderRadiusShorthand = (value) => {
24
+ let values;
25
+ if (typeof value === 'number') {
26
+ if (!Number.isFinite(value) || value < 0) {
27
+ return null;
28
+ }
29
+ values = [value];
30
+ }
31
+ else if (typeof value === 'string') {
32
+ const tokens = value.trim().split(/\s+/);
33
+ if (tokens.length < 1 || tokens.length > 4) {
34
+ return null;
35
+ }
36
+ const parsed = tokens.map(parsePixelRadius);
37
+ if (parsed.some((radius) => radius === null)) {
38
+ return null;
39
+ }
40
+ values = parsed;
41
+ }
42
+ else {
43
+ return null;
44
+ }
45
+ const [topLeft, topRight, bottomRight, bottomLeft] = expandBorderRadius(values);
46
+ return {
47
+ borderTopLeftRadius: topLeft,
48
+ borderTopRightRadius: topRight,
49
+ borderBottomRightRadius: bottomRight,
50
+ borderBottomLeftRadius: bottomLeft,
51
+ };
52
+ };
53
+ exports.parseBorderRadiusShorthand = parseBorderRadiusShorthand;
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.insertJsxElementIntoComposition = exports.resolveCompositionComponent = exports.resolveCompositionComponentWithFile = void 0;
40
40
  const node_fs_1 = __importDefault(require("node:fs"));
41
41
  const node_path_1 = __importDefault(require("node:path"));
42
+ const studio_codemods_1 = require("@remotion/studio-codemods");
42
43
  const studio_shared_1 = require("@remotion/studio-shared");
43
44
  const recast = __importStar(require("recast"));
44
45
  const no_react_1 = require("remotion/no-react");
@@ -247,9 +248,6 @@ const findReExportTargets = ({ ast, exportName, }) => {
247
248
  visitExportNamedDeclaration(astPath) {
248
249
  var _a;
249
250
  const node = astPath.node;
250
- if (typeof ((_a = node.source) === null || _a === void 0 ? void 0 : _a.value) !== 'string') {
251
- return false;
252
- }
253
251
  for (const specifier of node.specifiers) {
254
252
  if (specifier.type !== 'ExportSpecifier') {
255
253
  continue;
@@ -262,6 +260,18 @@ const findReExportTargets = ({ ast, exportName, }) => {
262
260
  if (!localName) {
263
261
  continue;
264
262
  }
263
+ // Support barrel files that import a component and export it in a
264
+ // separate declaration. See https://github.com/remotion-dev/remotion/issues/9172.
265
+ if (typeof ((_a = node.source) === null || _a === void 0 ? void 0 : _a.value) !== 'string') {
266
+ const importTarget = findImportTarget({
267
+ ast,
268
+ componentName: localName,
269
+ });
270
+ if (importTarget) {
271
+ targets.push(importTarget);
272
+ }
273
+ continue;
274
+ }
265
275
  targets.push({
266
276
  importPath: node.source.value,
267
277
  exportName: localName === 'default' ? 'default' : localName,
@@ -313,6 +323,9 @@ const locationFromNode = (node) => {
313
323
  };
314
324
  const findLocalSymbolLocation = ({ ast, name, }) => {
315
325
  let location = null;
326
+ // Recast can omit the declaration location for exported functions and
327
+ // classes, including components resolved through barrel files. The identifier
328
+ // keeps its location. See https://github.com/remotion-dev/remotion/issues/9172.
316
329
  recast.types.visit(ast, {
317
330
  visitVariableDeclarator(astPath) {
318
331
  if (location) {
@@ -320,7 +333,7 @@ const findLocalSymbolLocation = ({ ast, name, }) => {
320
333
  }
321
334
  const { node } = astPath;
322
335
  if (node.id.type === 'Identifier' && node.id.name === name) {
323
- location = locationFromNode(node);
336
+ location = locationFromNode(node.id);
324
337
  return false;
325
338
  }
326
339
  this.traverse(astPath);
@@ -333,7 +346,7 @@ const findLocalSymbolLocation = ({ ast, name, }) => {
333
346
  }
334
347
  const { node } = astPath;
335
348
  if (((_a = node.id) === null || _a === void 0 ? void 0 : _a.name) === name) {
336
- location = locationFromNode(node);
349
+ location = locationFromNode(node.id);
337
350
  return false;
338
351
  }
339
352
  this.traverse(astPath);
@@ -346,7 +359,7 @@ const findLocalSymbolLocation = ({ ast, name, }) => {
346
359
  }
347
360
  const { node } = astPath;
348
361
  if (((_a = node.id) === null || _a === void 0 ? void 0 : _a.name) === name) {
349
- location = locationFromNode(node);
362
+ location = locationFromNode(node.id);
350
363
  return false;
351
364
  }
352
365
  this.traverse(astPath);
@@ -1427,56 +1440,71 @@ const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile,
1427
1440
  remotionRoot,
1428
1441
  fileName: location.fileName,
1429
1442
  });
1430
- const ast = (0, parse_ast_1.parseAst)(input);
1431
- if (element.type === 'composition' &&
1432
- element.compositionId === compositionId) {
1433
- throw new Error('Cannot insert a composition into itself');
1434
- }
1435
- const sequenceWrapper = element.type === 'composition'
1436
- ? {
1437
- dimensions: { width: element.width, height: element.height },
1438
- durationInFrames: element.durationInFrames,
1439
- name: element.compositionId,
1443
+ let finalFile;
1444
+ let logLine;
1445
+ if (element.type === 'solid' && from === null && wrapInSequence === null) {
1446
+ const inserted = (0, studio_codemods_1.insertSolidIntoSource)({
1447
+ exportName: location.exportName,
1448
+ height: element.height,
1440
1449
  position: element.position,
1441
- from,
1450
+ source: input,
1451
+ width: element.width,
1452
+ });
1453
+ finalFile = inserted.output;
1454
+ logLine = inserted.line;
1455
+ }
1456
+ else {
1457
+ const ast = (0, parse_ast_1.parseAst)(input);
1458
+ if (element.type === 'composition' &&
1459
+ element.compositionId === compositionId) {
1460
+ throw new Error('Cannot insert a composition into itself');
1442
1461
  }
1443
- : from === null ||
1444
- element.type === 'asset' ||
1445
- element.type === 'svg' ||
1446
- element.type === 'component'
1447
- ? wrapInSequence
1448
- : {
1449
- dimensions: null,
1450
- durationInFrames: null,
1451
- name: null,
1462
+ const sequenceWrapper = element.type === 'composition'
1463
+ ? {
1464
+ dimensions: { width: element.width, height: element.height },
1465
+ durationInFrames: element.durationInFrames,
1466
+ name: element.compositionId,
1452
1467
  position: element.position,
1453
1468
  from,
1454
- };
1455
- const elementToInsert = await createInsertableJsxElement({
1456
- addPositionStyleToComponent: sequenceWrapper === null,
1457
- ast,
1458
- destinationFileName: location.fileName,
1459
- element,
1460
- from,
1461
- remotionRoot,
1462
- });
1463
- const finalElementToInsert = sequenceWrapper
1464
- ? createSequenceWrappedElement({
1465
- child: elementToInsert,
1466
- dimensions: sequenceWrapper.dimensions,
1467
- durationInFrames: (_a = sequenceWrapper.durationInFrames) !== null && _a !== void 0 ? _a : null,
1468
- from: sequenceWrapper.from,
1469
- name: sequenceWrapper.name,
1470
- position: sequenceWrapper.position,
1471
- sequenceLocalName: ensureSequenceImport(ast),
1472
- })
1473
- : elementToInsert;
1474
- const logLine = addElementToComponentRoot({
1475
- ast,
1476
- exportName: location.exportName,
1477
- element: finalElementToInsert,
1478
- });
1479
- const finalFile = (0, parse_ast_1.serializeAst)(ast);
1469
+ }
1470
+ : from === null ||
1471
+ element.type === 'asset' ||
1472
+ element.type === 'svg' ||
1473
+ element.type === 'component'
1474
+ ? wrapInSequence
1475
+ : {
1476
+ dimensions: null,
1477
+ durationInFrames: null,
1478
+ name: null,
1479
+ position: element.position,
1480
+ from,
1481
+ };
1482
+ const elementToInsert = await createInsertableJsxElement({
1483
+ addPositionStyleToComponent: sequenceWrapper === null,
1484
+ ast,
1485
+ destinationFileName: location.fileName,
1486
+ element,
1487
+ from,
1488
+ remotionRoot,
1489
+ });
1490
+ const finalElementToInsert = sequenceWrapper
1491
+ ? createSequenceWrappedElement({
1492
+ child: elementToInsert,
1493
+ dimensions: sequenceWrapper.dimensions,
1494
+ durationInFrames: (_a = sequenceWrapper.durationInFrames) !== null && _a !== void 0 ? _a : null,
1495
+ from: sequenceWrapper.from,
1496
+ name: sequenceWrapper.name,
1497
+ position: sequenceWrapper.position,
1498
+ sequenceLocalName: ensureSequenceImport(ast),
1499
+ })
1500
+ : elementToInsert;
1501
+ logLine = addElementToComponentRoot({
1502
+ ast,
1503
+ exportName: location.exportName,
1504
+ element: finalElementToInsert,
1505
+ });
1506
+ finalFile = (0, parse_ast_1.serializeAst)(ast);
1507
+ }
1480
1508
  const { output, formatted } = await (0, format_file_content_1.formatFileContent)({
1481
1509
  input: finalFile,
1482
1510
  prettierConfigOverride,
@@ -1,4 +1,5 @@
1
1
  export declare const ELEMENT_INSTALL_TARGET_MAX_AGE = 5000;
2
+ export declare const STUDIO_PROTOCOL_TARGET_MAX_AGE = 4000;
2
3
  export type ElementInstallTarget = {
3
4
  requestId: string | null;
4
5
  clientId: string;
@@ -10,6 +11,21 @@ export type ElementInstallTarget = {
10
11
  studioUrl: string;
11
12
  updatedAt: number;
12
13
  };
14
+ type StudioProtocolTarget = {
15
+ readonly id: string;
16
+ readonly target: ElementInstallTarget;
17
+ readonly expiresAt: number;
18
+ };
13
19
  export declare const updateElementInstallTarget: (newTarget: Omit<ElementInstallTarget, "updatedAt">) => void;
20
+ export declare const getElementInstallTargetByClientId: (clientId: string) => ElementInstallTarget | null;
14
21
  export declare const getElementInstallTarget: (requestId: string | null) => ElementInstallTarget | null;
22
+ export declare const issueStudioProtocolTarget: ({ now, target, }: {
23
+ readonly now: number;
24
+ readonly target: ElementInstallTarget;
25
+ }) => StudioProtocolTarget;
26
+ export declare const consumeStudioProtocolTarget: ({ now, targetId, }: {
27
+ readonly now: number;
28
+ readonly targetId: string;
29
+ }) => ElementInstallTarget | null;
15
30
  export declare const clearElementInstallStateForTests: () => void;
31
+ export {};
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.clearElementInstallStateForTests = exports.getElementInstallTarget = exports.updateElementInstallTarget = exports.ELEMENT_INSTALL_TARGET_MAX_AGE = void 0;
3
+ exports.clearElementInstallStateForTests = exports.consumeStudioProtocolTarget = exports.issueStudioProtocolTarget = exports.getElementInstallTarget = exports.getElementInstallTargetByClientId = exports.updateElementInstallTarget = exports.STUDIO_PROTOCOL_TARGET_MAX_AGE = exports.ELEMENT_INSTALL_TARGET_MAX_AGE = void 0;
4
+ const node_crypto_1 = require("node:crypto");
4
5
  exports.ELEMENT_INSTALL_TARGET_MAX_AGE = 5000;
6
+ exports.STUDIO_PROTOCOL_TARGET_MAX_AGE = 4000;
5
7
  const targetsByClientId = new Map();
8
+ const studioProtocolTargets = new Map();
6
9
  const compareTargets = (a, b) => {
7
10
  var _a, _b;
8
11
  const aFocusedAt = (_a = a.lastFocusedAt) !== null && _a !== void 0 ? _a : 0;
@@ -19,6 +22,11 @@ const updateElementInstallTarget = (newTarget) => {
19
22
  });
20
23
  };
21
24
  exports.updateElementInstallTarget = updateElementInstallTarget;
25
+ const getElementInstallTargetByClientId = (clientId) => {
26
+ var _a;
27
+ return (_a = targetsByClientId.get(clientId)) !== null && _a !== void 0 ? _a : null;
28
+ };
29
+ exports.getElementInstallTargetByClientId = getElementInstallTargetByClientId;
22
30
  const getElementInstallTarget = (requestId) => {
23
31
  const now = Date.now();
24
32
  let bestTarget = null;
@@ -37,7 +45,41 @@ const getElementInstallTarget = (requestId) => {
37
45
  return bestTarget;
38
46
  };
39
47
  exports.getElementInstallTarget = getElementInstallTarget;
48
+ const issueStudioProtocolTarget = ({ now, target, }) => {
49
+ for (const [id, existing] of studioProtocolTargets) {
50
+ if (existing.expiresAt <= now) {
51
+ studioProtocolTargets.delete(id);
52
+ }
53
+ }
54
+ const issued = {
55
+ id: (0, node_crypto_1.randomUUID)(),
56
+ target: { ...target },
57
+ expiresAt: now + exports.STUDIO_PROTOCOL_TARGET_MAX_AGE,
58
+ };
59
+ studioProtocolTargets.set(issued.id, issued);
60
+ return issued;
61
+ };
62
+ exports.issueStudioProtocolTarget = issueStudioProtocolTarget;
63
+ const consumeStudioProtocolTarget = ({ now, targetId, }) => {
64
+ const issued = studioProtocolTargets.get(targetId);
65
+ studioProtocolTargets.delete(targetId);
66
+ if (issued === undefined || issued.expiresAt <= now) {
67
+ return null;
68
+ }
69
+ const current = (0, exports.getElementInstallTargetByClientId)(issued.target.clientId);
70
+ if (current === null ||
71
+ now - current.updatedAt >= exports.ELEMENT_INSTALL_TARGET_MAX_AGE ||
72
+ !current.canInstall ||
73
+ current.readOnly ||
74
+ current.compositionFile !== issued.target.compositionFile ||
75
+ current.compositionId !== issued.target.compositionId) {
76
+ return null;
77
+ }
78
+ return issued.target;
79
+ };
80
+ exports.consumeStudioProtocolTarget = consumeStudioProtocolTarget;
40
81
  const clearElementInstallStateForTests = () => {
41
82
  targetsByClientId.clear();
83
+ studioProtocolTargets.clear();
42
84
  };
43
85
  exports.clearElementInstallStateForTests = clearElementInstallStateForTests;
@@ -13,7 +13,7 @@ const handleRequest = async ({ remotionRoot, request, response, entryPoint, hand
13
13
  response.setHeader('content-type', 'application/json');
14
14
  response.writeHead(200);
15
15
  try {
16
- const body = (await (0, parse_body_1.parseRequestBody)(request));
16
+ const body = (await (0, parse_body_1.parseRequestBody)(request, { maxBytes: null }));
17
17
  const outputData = await handler({
18
18
  entryPoint,
19
19
  remotionRoot,
@@ -1,2 +1,7 @@
1
1
  import type { IncomingMessage } from 'node:http';
2
- export declare const parseRequestBody: (req: IncomingMessage) => Promise<unknown>;
2
+ export declare class RequestBodyTooLargeError extends Error {
3
+ constructor(maxBytes: number);
4
+ }
5
+ export declare const parseRequestBody: (req: IncomingMessage, { maxBytes, }: {
6
+ readonly maxBytes: number | null;
7
+ }) => Promise<unknown>;
@@ -1,15 +1,66 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseRequestBody = void 0;
4
- const parseRequestBody = async (req) => {
5
- const body = await new Promise((_resolve) => {
3
+ exports.parseRequestBody = exports.RequestBodyTooLargeError = void 0;
4
+ class RequestBodyTooLargeError extends Error {
5
+ constructor(maxBytes) {
6
+ super(`Request body exceeds the limit of ${maxBytes} bytes`);
7
+ this.name = 'RequestBodyTooLargeError';
8
+ }
9
+ }
10
+ exports.RequestBodyTooLargeError = RequestBodyTooLargeError;
11
+ const parseRequestBody = async (req, { maxBytes, }) => {
12
+ const body = await new Promise((resolve, reject) => {
13
+ const decoder = new TextDecoder();
14
+ const encoder = new TextEncoder();
6
15
  let data = '';
7
- req.on('data', (chunk) => {
8
- data += chunk;
9
- });
10
- req.on('end', () => {
11
- _resolve(data.toString());
12
- });
16
+ let receivedBytes = 0;
17
+ let settled = false;
18
+ const cleanup = () => {
19
+ req.off('aborted', onAborted);
20
+ req.off('data', onData);
21
+ req.off('end', onEnd);
22
+ req.off('error', onError);
23
+ };
24
+ const rejectOnce = (error) => {
25
+ if (settled) {
26
+ return;
27
+ }
28
+ settled = true;
29
+ cleanup();
30
+ reject(error);
31
+ };
32
+ const onAborted = () => rejectOnce(new Error('Request body was aborted'));
33
+ const onData = (chunk) => {
34
+ receivedBytes +=
35
+ typeof chunk === 'string'
36
+ ? encoder.encode(chunk).byteLength
37
+ : chunk.byteLength;
38
+ if (maxBytes !== null && receivedBytes > maxBytes) {
39
+ rejectOnce(new RequestBodyTooLargeError(maxBytes));
40
+ // Discard the rest without buffering it. Keep request stream errors from
41
+ // becoming unhandled after the parser listeners have been removed.
42
+ req.on('error', () => undefined);
43
+ req.resume();
44
+ return;
45
+ }
46
+ data +=
47
+ typeof chunk === 'string'
48
+ ? decoder.decode() + chunk
49
+ : decoder.decode(chunk, { stream: true });
50
+ };
51
+ const onEnd = () => {
52
+ if (settled) {
53
+ return;
54
+ }
55
+ settled = true;
56
+ cleanup();
57
+ resolve(data + decoder.decode());
58
+ };
59
+ const onError = (error) => rejectOnce(error);
60
+ req.on('aborted', onAborted);
61
+ req.on('data', onData);
62
+ req.on('end', onEnd);
63
+ req.on('error', onError);
13
64
  });
14
65
  return JSON.parse(body);
15
66
  };
@@ -1,6 +1,6 @@
1
1
  import type { CanUpdateDefaultPropsResponse } from '@remotion/studio-shared';
2
+ export { computeCanUpdateDefaultPropsFromContent } from '@remotion/studio-codemods';
2
3
  export declare const checkIfTypeScriptFile: (file: string) => void;
3
- export declare const computeCanUpdateDefaultPropsFromContent: (content: string, compositionId: string) => CanUpdateDefaultPropsResponse;
4
4
  export declare const computeCanUpdateDefaultProps: ({ compositionId, remotionRoot, entryPoint, }: {
5
5
  compositionId: string;
6
6
  remotionRoot: string;