@remotion/studio-server 4.0.488 → 4.0.490

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.
@@ -111,6 +111,9 @@ const makeKeyframedOptions = ({ param, remotionLocalNames, }) => {
111
111
  if (param.clamping.right !== 'extend') {
112
112
  properties.push(b.objectProperty(b.identifier('extrapolateRight'), b.stringLiteral(param.clamping.right)));
113
113
  }
114
+ if (param.output === 'perceptual-scale') {
115
+ properties.push(b.objectProperty(b.identifier('output'), b.stringLiteral(param.output)));
116
+ }
114
117
  }
115
118
  if (keyframedParamNeedsEasingImport(param)) {
116
119
  const easingLocalName = (_a = remotionLocalNames.Easing) !== null && _a !== void 0 ? _a : 'Easing';
@@ -1,5 +1,5 @@
1
1
  import { type KeyframeInterpolationFunction } from '@remotion/studio-shared';
2
- import type { CanUpdateSequencePropStatus, ExtrapolateType, SequenceNodePath, InteractivitySchema } from 'remotion';
2
+ import type { CanUpdateSequencePropStatus, ExtrapolateType, InteractivitySchema, InterpolateOutputOption, SequenceNodePath } from 'remotion';
3
3
  type KeyframeEasing = Extract<CanUpdateSequencePropStatus, {
4
4
  status: 'keyframed';
5
5
  }>['easing'][number];
@@ -17,6 +17,7 @@ export type KeyframeOperation = {
17
17
  right: ExtrapolateType;
18
18
  } | undefined;
19
19
  posterize: number | undefined;
20
+ output: InterpolateOutputOption | undefined;
20
21
  } | {
21
22
  type: 'easing';
22
23
  segmentIndex: number;
@@ -192,11 +192,15 @@ const getInterpolationCalleeForValues = ({ schema, key, staticValue, newValue, }
192
192
  const createFrameExpression = (frame) => {
193
193
  return (0, update_nested_prop_1.parseValueExpression)(frame);
194
194
  };
195
- const createClampOptionsExpression = () => {
196
- return b.objectExpression([
195
+ const createClampOptionsExpression = ({ defaultOutput, }) => {
196
+ const properties = [
197
197
  b.objectProperty(b.identifier('extrapolateLeft'), b.stringLiteral('clamp')),
198
198
  b.objectProperty(b.identifier('extrapolateRight'), b.stringLiteral('clamp')),
199
- ]);
199
+ ];
200
+ if (defaultOutput !== null && defaultOutput !== 'linear') {
201
+ properties.push(b.objectProperty(b.identifier('output'), b.stringLiteral(defaultOutput)));
202
+ }
203
+ return b.objectExpression(properties);
200
204
  };
201
205
  const createEmptyOptionsExpression = () => b.objectExpression([]);
202
206
  const findObjectOptionProperty = (options, propertyName) => {
@@ -451,8 +455,17 @@ const validatePosterize = (posterize) => {
451
455
  throw new Error('Cannot update keyframe settings: posterize must be > 0');
452
456
  }
453
457
  };
454
- const updateKeyframeSettings = ({ expression, clamping, posterize, }) => {
458
+ const validateOutput = (output) => {
459
+ if (output === undefined ||
460
+ output === 'linear' ||
461
+ output === 'perceptual-scale') {
462
+ return;
463
+ }
464
+ throw new Error('Cannot update keyframe settings: output must be "linear" or "perceptual-scale"');
465
+ };
466
+ const updateKeyframeSettings = ({ expression, clamping, posterize, output, }) => {
455
467
  validatePosterize(posterize);
468
+ validateOutput(output);
456
469
  const existing = getInterpolationExpression(expression);
457
470
  if (!existing) {
458
471
  throw new Error('Cannot update keyframe settings on non-keyframed value');
@@ -474,6 +487,7 @@ const updateKeyframeSettings = ({ expression, clamping, posterize, }) => {
474
487
  propertyName: 'extrapolateRight',
475
488
  value: null,
476
489
  });
490
+ setOptionsProperty({ options, propertyName: 'output', value: null });
477
491
  }
478
492
  else if (clamping) {
479
493
  setOptionsProperty({
@@ -487,6 +501,13 @@ const updateKeyframeSettings = ({ expression, clamping, posterize, }) => {
487
501
  value: b.stringLiteral(clamping.right),
488
502
  });
489
503
  }
504
+ if (!isColorInterpolation && output !== undefined) {
505
+ setOptionsProperty({
506
+ options,
507
+ propertyName: 'output',
508
+ value: output === 'linear' ? null : b.stringLiteral(output),
509
+ });
510
+ }
490
511
  setOptionsProperty({
491
512
  options,
492
513
  propertyName: 'posterize',
@@ -617,7 +638,11 @@ const addKeyframe = ({ expression, key, frame, value, schema, }) => {
617
638
  });
618
639
  const extraArgs = callee.type === 'Identifier' && callee.name === 'interpolateColors'
619
640
  ? []
620
- : [createClampOptionsExpression()];
641
+ : [
642
+ createClampOptionsExpression({
643
+ defaultOutput: getDefaultKeyframeOutput({ schema, key }),
644
+ }),
645
+ ];
621
646
  return {
622
647
  expression: createInterpolateExpression({
623
648
  callee,
@@ -744,6 +769,7 @@ const applyKeyframeOperation = ({ expression, key, operation, schema, }) => {
744
769
  expression,
745
770
  clamping: operation.clamping,
746
771
  posterize: operation.posterize,
772
+ output: operation.output,
747
773
  }),
748
774
  introduced: noIntroducedIdentifiers,
749
775
  };
@@ -832,6 +858,14 @@ const findFieldInSchema = (schema, key) => {
832
858
  }
833
859
  return undefined;
834
860
  };
861
+ const getDefaultKeyframeOutput = ({ schema, key, }) => {
862
+ const field = schema ? findFieldInSchema(schema, key) : undefined;
863
+ if (((field === null || field === void 0 ? void 0 : field.type) === 'number' || (field === null || field === void 0 ? void 0 : field.type) === 'scale') &&
864
+ field.defaultKeyframeOutput !== undefined) {
865
+ return field.defaultKeyframeOutput;
866
+ }
867
+ return key === 'style.scale' ? 'perceptual-scale' : null;
868
+ };
835
869
  const getInitialValueForMissingProp = ({ schema, key, newValue, }) => {
836
870
  const field = schema ? findFieldInSchema(schema, key) : undefined;
837
871
  if (field && field.type !== 'hidden' && field.default !== undefined) {
@@ -1,22 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getInstalledInstallablePackages = void 0;
4
- const studio_shared_1 = require("@remotion/studio-shared");
5
4
  const get_installed_dependencies_1 = require("./get-installed-dependencies");
6
5
  const getInstalledInstallablePackages = (remotionRoot) => {
7
- const { dependencies, devDependencies, optionalDependencies } = (0, get_installed_dependencies_1.getInstalledDependencies)(remotionRoot);
8
- const installablePackages = [
6
+ const { dependencies, devDependencies, optionalDependencies, peerDependencies, } = (0, get_installed_dependencies_1.getInstalledDependencies)(remotionRoot);
7
+ return Array.from(new Set([
9
8
  ...dependencies,
10
9
  ...devDependencies,
11
10
  ...optionalDependencies,
12
- ];
13
- const remotionPackages = Object.entries(studio_shared_1.installableMap)
14
- .filter(([, _installable]) => _installable)
15
- .map(([pkg]) => (pkg === 'core' ? 'remotion' : `@remotion/${pkg}`))
16
- .filter((pkg) => installablePackages.includes(pkg));
17
- const installedExtraPackages = studio_shared_1.extraPackages
18
- .map((pkg) => pkg.name)
19
- .filter((pkg) => installablePackages.includes(pkg));
20
- return [...remotionPackages, ...installedExtraPackages];
11
+ ...peerDependencies,
12
+ ]));
21
13
  };
22
14
  exports.getInstalledInstallablePackages = getInstalledInstallablePackages;
@@ -569,6 +569,7 @@ const createSolidElement = ({ localName, width, height, position, }) => {
569
569
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
570
570
  createNumberAttribute('width', width),
571
571
  createNumberAttribute('height', height),
572
+ createStringAttribute('color', 'gray'),
572
573
  createPositionAbsoluteStyleAttribute(position),
573
574
  ], true), null, []);
574
575
  };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { ApiRoutes, CopyStillToClipboardRequest, getDefaultOutLocation, OpenInFi
2
2
  export type { AggregateRenderProgress, BundlingState, CopyingState, DownloadProgress, HotMiddlewareOptions, JobProgressCallback, ModuleMap, PackageManager, ProjectInfo, RenderingProgressInput, RenderJob, RenderJobWithCleanup, RequiredChromiumOptions, StitchingProgressInput, UiOpenGlOptions, } from '@remotion/studio-shared';
3
3
  import { AnsiDiff } from './ansi-diff';
4
4
  export declare const StudioServerInternals: {
5
- startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, }: {
5
+ startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, getStudioRuntimeConfig, }: {
6
6
  browserArgs: string;
7
7
  browserFlag: string;
8
8
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -34,6 +34,7 @@ export declare const StudioServerInternals: {
34
34
  interactivityEnabled: boolean;
35
35
  forceNew: boolean;
36
36
  rspack: boolean;
37
+ getStudioRuntimeConfig: () => import("@remotion/studio-shared").StudioRuntimeConfig;
37
38
  }) => Promise<import("./start-studio").StartStudioResult>;
38
39
  getRemotionVersion: () => any;
39
40
  waitForLiveEventsListener: () => Promise<import("./preview-server/live-events").LiveEventsServer>;
@@ -1,5 +1,6 @@
1
1
  export declare const ELEMENT_INSTALL_TARGET_MAX_AGE = 5000;
2
2
  export type ElementInstallTarget = {
3
+ requestId: string | null;
3
4
  clientId: string;
4
5
  compositionFile: string | null;
5
6
  compositionId: string | null;
@@ -9,5 +10,5 @@ export type ElementInstallTarget = {
9
10
  updatedAt: number;
10
11
  };
11
12
  export declare const updateElementInstallTarget: (newTarget: Omit<ElementInstallTarget, "updatedAt">) => void;
12
- export declare const getElementInstallTarget: () => ElementInstallTarget | null;
13
+ export declare const getElementInstallTarget: (requestId: string | null) => ElementInstallTarget | null;
13
14
  export declare const clearElementInstallStateForTests: () => void;
@@ -19,7 +19,7 @@ const updateElementInstallTarget = (newTarget) => {
19
19
  });
20
20
  };
21
21
  exports.updateElementInstallTarget = updateElementInstallTarget;
22
- const getElementInstallTarget = () => {
22
+ const getElementInstallTarget = (requestId) => {
23
23
  const now = Date.now();
24
24
  let bestTarget = null;
25
25
  for (const [clientId, currentTarget] of targetsByClientId) {
@@ -27,6 +27,9 @@ const getElementInstallTarget = () => {
27
27
  targetsByClientId.delete(clientId);
28
28
  continue;
29
29
  }
30
+ if (requestId !== null && currentTarget.requestId !== requestId) {
31
+ continue;
32
+ }
30
33
  if (bestTarget === null || compareTargets(currentTarget, bestTarget) > 0) {
31
34
  bestTarget = currentTarget;
32
35
  }
@@ -201,6 +201,18 @@ const getExtrapolateType = (node) => {
201
201
  }
202
202
  return null;
203
203
  };
204
+ const getInterpolateOutputOption = (node) => {
205
+ if (node.type === 'StringLiteral') {
206
+ if (node.value === 'linear' || node.value === 'perceptual-scale') {
207
+ return node.value;
208
+ }
209
+ return null;
210
+ }
211
+ if (node.type === 'TSAsExpression') {
212
+ return getInterpolateOutputOption(node.expression);
213
+ }
214
+ return null;
215
+ };
204
216
  const getKeyframeEasing = (node) => (0, parse_keyframe_easing_expression_1.parseKeyframeEasingExpression)(node);
205
217
  const getKeyframeEasingArray = ({ easingNode, segments, }) => {
206
218
  if (segments === 0) {
@@ -248,6 +260,7 @@ const getInterpolationMetadata = (interpolationFunction, callExpression, keyfram
248
260
  easing: Array.from({ length: segments }, () => studio_shared_1.LINEAR_KEYFRAME_EASING),
249
261
  clamping: defaultClamping,
250
262
  posterize: undefined,
263
+ output: undefined,
251
264
  };
252
265
  const optionsArg = callExpression.arguments[3];
253
266
  if (!optionsArg) {
@@ -259,6 +272,7 @@ const getInterpolationMetadata = (interpolationFunction, callExpression, keyfram
259
272
  let { easing } = defaults;
260
273
  let { clamping } = defaults;
261
274
  let posterize;
275
+ let { output } = defaults;
262
276
  for (const property of optionsArg.properties) {
263
277
  if (property.type !== 'ObjectProperty' || property.computed) {
264
278
  return null;
@@ -307,12 +321,24 @@ const getInterpolationMetadata = (interpolationFunction, callExpression, keyfram
307
321
  posterize = parsedPosterize;
308
322
  continue;
309
323
  }
324
+ if (key === 'output') {
325
+ if (interpolationFunction === 'interpolateColors') {
326
+ return null;
327
+ }
328
+ const parsedOutput = getInterpolateOutputOption(value);
329
+ if (!parsedOutput) {
330
+ return null;
331
+ }
332
+ output = parsedOutput;
333
+ continue;
334
+ }
310
335
  return null;
311
336
  }
312
337
  return {
313
338
  easing,
314
339
  clamping,
315
340
  posterize,
341
+ output,
316
342
  };
317
343
  };
318
344
  const getInterpolationKeyframes = (node, ast) => {
@@ -394,6 +420,7 @@ const getInterpolationKeyframes = (node, ast) => {
394
420
  easing: metadata.easing,
395
421
  clamping: metadata.clamping,
396
422
  posterize: metadata.posterize,
423
+ output: metadata.output,
397
424
  };
398
425
  };
399
426
  const isUseCurrentFrameCall = (node) => {
@@ -440,6 +467,7 @@ const getComputedStatus = (node, ast) => {
440
467
  easing: interpolation.easing,
441
468
  clamping: interpolation.clamping,
442
469
  posterize: interpolation.posterize,
470
+ output: interpolation.output,
443
471
  };
444
472
  };
445
473
  exports.getComputedStatus = getComputedStatus;
@@ -1,3 +1,4 @@
1
1
  import { type InstallPackageRequest, type InstallPackageResponse } from '@remotion/studio-shared';
2
2
  import type { ApiHandler } from '../api-types';
3
+ export declare const getPackageInstallSpec: (packageName: string) => string;
3
4
  export declare const handleInstallPackage: ApiHandler<InstallPackageRequest, InstallPackageResponse>;
@@ -1,24 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleInstallPackage = void 0;
3
+ exports.handleInstallPackage = exports.getPackageInstallSpec = void 0;
4
4
  const node_child_process_1 = require("node:child_process");
5
5
  const renderer_1 = require("@remotion/renderer");
6
6
  const studio_shared_1 = require("@remotion/studio-shared");
7
7
  const version_1 = require("remotion/version");
8
8
  const install_command_1 = require("../../helpers/install-command");
9
9
  const get_package_manager_1 = require("../get-package-manager");
10
- const extraPackageNames = studio_shared_1.extraPackages.map((pkg) => pkg.name);
11
- const isExtraPackage = (packageName) => {
12
- return extraPackageNames.includes(packageName);
13
- };
14
10
  const getExtraPackageVersion = (packageName) => {
15
11
  const pkg = studio_shared_1.extraPackages.find((p) => p.name === packageName);
16
12
  return pkg ? pkg.version : null;
17
13
  };
14
+ const getPackageInstallSpec = (packageName) => {
15
+ const extraVersion = getExtraPackageVersion(packageName);
16
+ if (extraVersion) {
17
+ return `${packageName}@${extraVersion}`;
18
+ }
19
+ if (packageName === 'remotion' || packageName.startsWith('@remotion/')) {
20
+ return `${packageName}@${version_1.VERSION}`;
21
+ }
22
+ return packageName;
23
+ };
24
+ exports.getPackageInstallSpec = getPackageInstallSpec;
18
25
  const handleInstallPackage = async ({ logLevel, remotionRoot, input: { packageNames } }) => {
19
26
  for (const packageName of packageNames) {
20
- if (!packageName.startsWith('@remotion/') && !isExtraPackage(packageName)) {
21
- return Promise.reject(new Error(`Package ${packageName} is not allowed to be installed.`));
27
+ if (!(0, studio_shared_1.isValidPackageName)(packageName)) {
28
+ return Promise.reject(new Error(`Package name ${JSON.stringify(packageName)} is invalid.`));
22
29
  }
23
30
  }
24
31
  const manager = (0, get_package_manager_1.getPackageManager)({
@@ -32,14 +39,9 @@ const handleInstallPackage = async ({ logLevel, remotionRoot, input: { packageNa
32
39
  .map((p) => p.path)
33
40
  .join(', ')}). Install dependencies using your favorite manager!`);
34
41
  }
35
- // Build packages with appropriate versions
36
- const packagesWithVersions = packageNames.map((pkg) => {
37
- const extraVersion = getExtraPackageVersion(pkg);
38
- if (extraVersion) {
39
- return `${pkg}@${extraVersion}`;
40
- }
41
- return `${pkg}@${version_1.VERSION}`;
42
- });
42
+ // Remotion packages must match the Studio version and catalogued extra
43
+ // packages use their supported version. Other packages resolve normally.
44
+ const packagesWithVersions = packageNames.map(exports.getPackageInstallSpec);
43
45
  const command = (0, install_command_1.getInstallCommand)({
44
46
  manager: manager.manager,
45
47
  packages: packagesWithVersions,
@@ -35,6 +35,7 @@ const updateEffectKeyframeSettingsHandler = ({ input: { fileName, sequenceNodePa
35
35
  type: 'settings',
36
36
  clamping: settings.clamping,
37
37
  posterize: settings.posterize,
38
+ output: settings.output,
38
39
  }
39
40
  : {
40
41
  type: 'easing',
@@ -9,6 +9,9 @@ const updateElementInstallTargetHandler = ({ input }) => {
9
9
  if (typeof input.clientId !== 'string' || input.clientId.length === 0) {
10
10
  throw new Error('Invalid client ID');
11
11
  }
12
+ if (input.requestId !== null && typeof input.requestId !== 'string') {
13
+ throw new Error('Invalid request ID');
14
+ }
12
15
  if (!isFiniteTimestamp(input.lastFocusedAt)) {
13
16
  throw new Error('Invalid focus timestamp');
14
17
  }
@@ -32,6 +32,7 @@ const updateSequenceKeyframeSettingsHandler = ({ input: { fileName, nodePath, ke
32
32
  type: 'settings',
33
33
  clamping: settings.clamping,
34
34
  posterize: settings.posterize,
35
+ output: settings.output,
35
36
  }
36
37
  : {
37
38
  type: 'easing',
@@ -1,5 +1,5 @@
1
1
  import type { WebpackOverrideFn } from '@remotion/bundler';
2
- import type { GitSource, RenderDefaults, RenderJob } from '@remotion/studio-shared';
2
+ import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
3
3
  import type { QueueMethods } from './api-types';
4
4
  import type { LiveEventsServer } from './live-events';
5
5
  export type StartServerResult = {
@@ -44,4 +44,5 @@ export declare const startServer: (options: {
44
44
  interactivityEnabled: boolean;
45
45
  forceNew: boolean;
46
46
  rspack: boolean;
47
+ getStudioRuntimeConfig: () => StudioRuntimeConfig;
47
48
  }) => Promise<StartServerResult>;
@@ -105,6 +105,7 @@ const startServer = async (options) => {
105
105
  audioLatencyHint: options.audioLatencyHint,
106
106
  previewSampleRate: options.previewSampleRate,
107
107
  enableCrossSiteIsolation: options.enableCrossSiteIsolation,
108
+ getStudioRuntimeConfig: options.getStudioRuntimeConfig,
108
109
  });
109
110
  })
110
111
  .catch((err) => {
@@ -4,6 +4,9 @@ exports.validateSameOrigin = void 0;
4
4
  const validateSameOrigin = (req) => {
5
5
  const { origin, host } = req.headers;
6
6
  if (typeof origin !== 'string') {
7
+ if (req.method === 'GET' || req.method === 'HEAD') {
8
+ return;
9
+ }
7
10
  throw new Error('Request without Origin header not allowed');
8
11
  }
9
12
  if (typeof host !== 'string') {
package/dist/routes.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import type { GitSource, RenderDefaults, RenderJob } from '@remotion/studio-shared';
2
+ import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
3
3
  import type { QueueMethods } from './preview-server/api-types';
4
4
  import type { LiveEventsServer } from './preview-server/live-events';
5
- export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, }: {
5
+ export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, }: {
6
6
  staticHash: string;
7
7
  staticHashPrefix: string;
8
8
  outputHash: string;
@@ -25,4 +25,5 @@ export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash,
25
25
  audioLatencyHint: AudioContextLatencyCategory | null;
26
26
  previewSampleRate: number | null;
27
27
  enableCrossSiteIsolation: boolean;
28
+ getStudioRuntimeConfig: () => StudioRuntimeConfig;
28
29
  }) => Promise<void>;
package/dist/routes.js CHANGED
@@ -58,6 +58,7 @@ const validate_same_origin_1 = require("./preview-server/validate-same-origin");
58
58
  const watch_ignore_next_change_1 = require("./preview-server/watch-ignore-next-change");
59
59
  const loggedStaticFileHints = new Set();
60
60
  const ELEMENT_INSTALL_FOCUS_MAX_AGE = 5 * 60 * 1000;
61
+ const ELEMENT_INSTALL_TARGET_RESPONSE_WAIT = 250;
61
62
  const static404 = (response) => {
62
63
  response.writeHead(404);
63
64
  response.end('The static/ prefix has been changed, this URL is no longer valid.');
@@ -114,34 +115,44 @@ const handleElementInstallOptions = ({ request, response, }) => {
114
115
  response.end();
115
116
  return Promise.resolve();
116
117
  };
117
- const handleElementInstallTarget = ({ request, response, remotionRoot, gitSource, }) => {
118
- var _a, _b, _c, _d, _e;
118
+ const handleElementInstallTarget = ({ liveEventsServer, request, response, remotionRoot, gitSource, }) => {
119
119
  if (!isAllowedElementInstallOrigin(request.headers.origin)) {
120
120
  response.writeHead(403);
121
121
  response.end(JSON.stringify({ success: false, reason: 'Origin not allowed' }));
122
122
  return Promise.resolve();
123
123
  }
124
- const target = (0, element_install_state_1.getElementInstallTarget)();
125
- const now = Date.now();
126
- const targetIsLive = target !== null && now - target.updatedAt < element_install_state_1.ELEMENT_INSTALL_TARGET_MAX_AGE;
127
- const host = (_a = request.headers.host) !== null && _a !== void 0 ? _a : null;
128
- const port = (_b = host === null || host === void 0 ? void 0 : host.split(':').at(-1)) !== null && _b !== void 0 ? _b : null;
129
- setElementInstallCorsHeaders({ request, response });
130
- response.writeHead(200, { 'Content-Type': 'application/json' });
131
- response.end(JSON.stringify({
132
- type: 'remotion-studio',
133
- projectName: (0, studio_shared_1.getProjectName)({
134
- basename: node_path_1.default.basename,
135
- gitSource,
136
- resolvedRemotionRoot: remotionRoot,
137
- }),
138
- port: port === null ? null : Number(port),
139
- lastFocusedAt: (_c = target === null || target === void 0 ? void 0 : target.lastFocusedAt) !== null && _c !== void 0 ? _c : null,
140
- canInstall: target !== null && target.canInstall && targetIsLive,
141
- activeCompositionId: (_d = target === null || target === void 0 ? void 0 : target.compositionId) !== null && _d !== void 0 ? _d : null,
142
- readOnly: (_e = target === null || target === void 0 ? void 0 : target.readOnly) !== null && _e !== void 0 ? _e : false,
143
- }));
144
- return Promise.resolve();
124
+ const requestId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
125
+ liveEventsServer.sendEventToClient({
126
+ type: 'request-element-install-target',
127
+ requestId,
128
+ });
129
+ return new Promise((resolve) => {
130
+ setTimeout(() => {
131
+ var _a, _b, _c, _d, _e;
132
+ const target = (0, element_install_state_1.getElementInstallTarget)(requestId);
133
+ const now = Date.now();
134
+ const targetIsLive = target !== null &&
135
+ now - target.updatedAt < element_install_state_1.ELEMENT_INSTALL_TARGET_MAX_AGE;
136
+ const host = (_a = request.headers.host) !== null && _a !== void 0 ? _a : null;
137
+ const port = (_b = host === null || host === void 0 ? void 0 : host.split(':').at(-1)) !== null && _b !== void 0 ? _b : null;
138
+ setElementInstallCorsHeaders({ request, response });
139
+ response.writeHead(200, { 'Content-Type': 'application/json' });
140
+ response.end(JSON.stringify({
141
+ type: 'remotion-studio',
142
+ projectName: (0, studio_shared_1.getProjectName)({
143
+ basename: node_path_1.default.basename,
144
+ gitSource,
145
+ resolvedRemotionRoot: remotionRoot,
146
+ }),
147
+ port: port === null ? null : Number(port),
148
+ lastFocusedAt: (_c = target === null || target === void 0 ? void 0 : target.lastFocusedAt) !== null && _c !== void 0 ? _c : null,
149
+ canInstall: target !== null && target.canInstall && targetIsLive,
150
+ activeCompositionId: (_d = target === null || target === void 0 ? void 0 : target.compositionId) !== null && _d !== void 0 ? _d : null,
151
+ readOnly: (_e = target === null || target === void 0 ? void 0 : target.readOnly) !== null && _e !== void 0 ? _e : false,
152
+ }));
153
+ resolve();
154
+ }, ELEMENT_INSTALL_TARGET_RESPONSE_WAIT);
155
+ });
145
156
  };
146
157
  const handleRequestElementInstall = async ({ liveEventsServer, request, response, }) => {
147
158
  if (!isAllowedElementInstallOrigin(request.headers.origin)) {
@@ -163,7 +174,7 @@ const handleRequestElementInstall = async ({ liveEventsServer, request, response
163
174
  response.end(JSON.stringify({ success: false, reason: 'Invalid Element payload' }));
164
175
  return;
165
176
  }
166
- const target = (0, element_install_state_1.getElementInstallTarget)();
177
+ const target = (0, element_install_state_1.getElementInstallTarget)(null);
167
178
  const now = Date.now();
168
179
  const targetIsLive = target !== null &&
169
180
  now - target.updatedAt < element_install_state_1.ELEMENT_INSTALL_TARGET_MAX_AGE;
@@ -212,7 +223,7 @@ const handleRequestElementInstall = async ({ liveEventsServer, request, response
212
223
  response.end(JSON.stringify({ success: false, reason: err.message }));
213
224
  }
214
225
  };
215
- const handleFallback = async ({ remotionRoot, hash, response, request, getCurrentInputProps, getEnvVariables, publicDir, getRenderQueue, getRenderDefaults, numberOfAudioTags, audioLatencyHint, previewSampleRate, gitSource, logLevel, enableCrossSiteIsolation, }) => {
226
+ const handleFallback = async ({ remotionRoot, hash, response, request, getCurrentInputProps, getEnvVariables, publicDir, getRenderQueue, getRenderDefaults, numberOfAudioTags, audioLatencyHint, previewSampleRate, gitSource, logLevel, enableCrossSiteIsolation, getStudioRuntimeConfig, }) => {
216
227
  var _a;
217
228
  const acceptsHtml = ((_a = request.headers.accept) !== null && _a !== void 0 ? _a : '').includes('text/html');
218
229
  if (request.method === 'GET' && acceptsHtml) {
@@ -277,6 +288,7 @@ const handleFallback = async ({ remotionRoot, hash, response, request, getCurren
277
288
  mode: 'dev',
278
289
  audioLatencyHint: audioLatencyHint !== null && audioLatencyHint !== void 0 ? audioLatencyHint : 'playback',
279
290
  sampleRate: previewSampleRate,
291
+ studioRuntimeConfig: getStudioRuntimeConfig(),
280
292
  }));
281
293
  };
282
294
  const handleFileSource = async ({ method, remotionRoot, search, response, request, }) => {
@@ -379,7 +391,7 @@ const handleBeep = (_, response) => {
379
391
  readStream.pipe(response);
380
392
  return Promise.resolve();
381
393
  };
382
- const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, }) => {
394
+ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, }) => {
383
395
  const url = new URL(request.url, 'http://localhost');
384
396
  if (url.pathname === '/api/file-source') {
385
397
  return handleFileSource({
@@ -413,6 +425,7 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
413
425
  }
414
426
  if (url.pathname === '/api/element-install-target') {
415
427
  return handleElementInstallTarget({
428
+ liveEventsServer,
416
429
  request,
417
430
  response,
418
431
  remotionRoot,
@@ -496,6 +509,7 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
496
509
  audioLatencyHint,
497
510
  previewSampleRate,
498
511
  enableCrossSiteIsolation,
512
+ getStudioRuntimeConfig,
499
513
  });
500
514
  };
501
515
  exports.handleRoutes = handleRoutes;
@@ -1,12 +1,12 @@
1
1
  import type { WebpackOverrideFn } from '@remotion/bundler';
2
- import type { GitSource, RenderDefaults, RenderJob } from '@remotion/studio-shared';
2
+ import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
3
3
  import type { QueueMethods } from './preview-server/api-types';
4
4
  export type StartStudioResult = {
5
5
  type: 'restarted';
6
6
  } | {
7
7
  type: 'already-running';
8
8
  };
9
- export declare const startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, }: {
9
+ export declare const startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, getStudioRuntimeConfig, }: {
10
10
  browserArgs: string;
11
11
  browserFlag: string;
12
12
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -38,4 +38,5 @@ export declare const startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser
38
38
  interactivityEnabled: boolean;
39
39
  forceNew: boolean;
40
40
  rspack: boolean;
41
+ getStudioRuntimeConfig: () => StudioRuntimeConfig;
41
42
  }) => Promise<StartStudioResult>;
@@ -19,7 +19,7 @@ const public_folder_1 = require("./preview-server/public-folder");
19
19
  const start_server_1 = require("./preview-server/start-server");
20
20
  const server_ready_1 = require("./server-ready");
21
21
  const watch_root_file_1 = require("./watch-root-file");
22
- const startStudio = async ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, }) => {
22
+ const startStudio = async ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, getStudioRuntimeConfig, }) => {
23
23
  try {
24
24
  if (typeof Bun === 'undefined') {
25
25
  process.title = 'node (npx remotion studio)';
@@ -96,6 +96,7 @@ const startStudio = async ({ browserArgs, browserFlag, shouldOpenBrowser, fullEn
96
96
  interactivityEnabled,
97
97
  forceNew,
98
98
  rspack,
99
+ getStudioRuntimeConfig,
99
100
  });
100
101
  if (result.type === 'already-running') {
101
102
  renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `Already running on port ${result.port}.`);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio-server"
4
4
  },
5
5
  "name": "@remotion/studio-server",
6
- "version": "4.0.488",
6
+ "version": "4.0.490",
7
7
  "description": "Run a Remotion Studio with a server backend",
8
8
  "main": "dist",
9
9
  "scripts": {
@@ -27,11 +27,11 @@
27
27
  "@babel/parser": "7.24.1",
28
28
  "semver": "7.5.3",
29
29
  "prettier": "3.8.1",
30
- "remotion": "4.0.488",
30
+ "remotion": "4.0.490",
31
31
  "recast": "0.23.11",
32
- "@remotion/bundler": "4.0.488",
33
- "@remotion/renderer": "4.0.488",
34
- "@remotion/studio-shared": "4.0.488",
32
+ "@remotion/bundler": "4.0.490",
33
+ "@remotion/renderer": "4.0.490",
34
+ "@remotion/studio-shared": "4.0.490",
35
35
  "memfs": "3.4.3",
36
36
  "open": "8.4.2"
37
37
  },
@@ -39,7 +39,7 @@
39
39
  "ast-types": "0.16.1",
40
40
  "react": "19.2.3",
41
41
  "@types/semver": "7.5.3",
42
- "@remotion/eslint-config-internal": "4.0.488",
42
+ "@remotion/eslint-config-internal": "4.0.490",
43
43
  "eslint": "9.19.0",
44
44
  "@types/node": "20.12.14",
45
45
  "@typescript/native-preview": "7.0.0-dev.20260217.1"