@remotion/studio-server 4.0.488 → 4.0.489

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.
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
  }
@@ -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
  }
@@ -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.489",
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.489",
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.489",
33
+ "@remotion/renderer": "4.0.489",
34
+ "@remotion/studio-shared": "4.0.489",
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.489",
43
43
  "eslint": "9.19.0",
44
44
  "@types/node": "20.12.14",
45
45
  "@typescript/native-preview": "7.0.0-dev.20260217.1"