@remotion/studio-server 4.0.501 → 4.0.503

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 (70) hide show
  1. package/dist/codemods/update-inline-caption-patches.d.ts +1 -11
  2. package/dist/codemods/update-inline-caption-patches.js +2 -183
  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 -720
  5. package/dist/detect-outdated-remotion-skills.d.ts +25 -0
  6. package/dist/detect-outdated-remotion-skills.js +73 -0
  7. package/dist/helpers/custom-editor.d.ts +37 -0
  8. package/dist/helpers/custom-editor.js +167 -0
  9. package/dist/helpers/editor-registry.d.ts +18 -0
  10. package/dist/helpers/editor-registry.js +500 -0
  11. package/dist/helpers/open-in-editor.d.ts +3 -4
  12. package/dist/helpers/open-in-editor.js +5 -75
  13. package/dist/helpers/resolve-composition-component.js +18 -6
  14. package/dist/helpers/resolve-editor.d.ts +27 -0
  15. package/dist/helpers/resolve-editor.js +90 -0
  16. package/dist/helpers/resolve-file-inside-project.d.ts +2 -0
  17. package/dist/helpers/resolve-file-inside-project.js +5 -2
  18. package/dist/index.d.ts +17 -1
  19. package/dist/index.js +9 -1
  20. package/dist/preview-server/api-routes.js +6 -1
  21. package/dist/preview-server/api-types.d.ts +2 -1
  22. package/dist/preview-server/element-install-state.d.ts +19 -0
  23. package/dist/preview-server/element-install-state.js +46 -1
  24. package/dist/preview-server/handler.d.ts +3 -1
  25. package/dist/preview-server/handler.js +3 -2
  26. package/dist/preview-server/parse-body.d.ts +6 -1
  27. package/dist/preview-server/parse-body.js +60 -9
  28. package/dist/preview-server/routes/can-update-default-props.d.ts +1 -1
  29. package/dist/preview-server/routes/can-update-default-props.js +5 -132
  30. package/dist/preview-server/routes/can-update-sequence-props.d.ts +3 -2
  31. package/dist/preview-server/routes/can-update-sequence-props.js +52 -9
  32. package/dist/preview-server/routes/default-editor.d.ts +8 -0
  33. package/dist/preview-server/routes/default-editor.js +131 -0
  34. package/dist/preview-server/routes/delete-effect-keyframe.d.ts +3 -0
  35. package/dist/preview-server/routes/delete-effect-keyframe.js +89 -0
  36. package/dist/preview-server/routes/delete-sequence-keyframe.d.ts +3 -0
  37. package/dist/preview-server/routes/delete-sequence-keyframe.js +82 -0
  38. package/dist/preview-server/routes/element-install-plan.d.ts +32 -0
  39. package/dist/preview-server/routes/element-install-plan.js +122 -0
  40. package/dist/preview-server/routes/insert-element.d.ts +1 -1
  41. package/dist/preview-server/routes/insert-element.js +68 -107
  42. package/dist/preview-server/routes/insert-jsx-element.js +5 -5
  43. package/dist/preview-server/routes/open-in-editor.d.ts +5 -1
  44. package/dist/preview-server/routes/open-in-editor.js +42 -12
  45. package/dist/preview-server/routes/prepare-element-install.d.ts +3 -0
  46. package/dist/preview-server/routes/prepare-element-install.js +25 -0
  47. package/dist/preview-server/routes/safe-element-install-path.d.ts +9 -0
  48. package/dist/preview-server/routes/safe-element-install-path.js +58 -0
  49. package/dist/preview-server/routes/save-props-mutex.d.ts +1 -0
  50. package/dist/preview-server/routes/save-props-mutex.js +11 -0
  51. package/dist/preview-server/sequence-props-watchers.js +2 -1
  52. package/dist/preview-server/start-server.d.ts +2 -0
  53. package/dist/preview-server/start-server.js +1 -0
  54. package/dist/preview-server/studio-protocol/handle-discovery.d.ts +11 -0
  55. package/dist/preview-server/studio-protocol/handle-discovery.js +106 -0
  56. package/dist/preview-server/studio-protocol/handle-install.d.ts +10 -0
  57. package/dist/preview-server/studio-protocol/handle-install.js +147 -0
  58. package/dist/preview-server/studio-protocol/origin-policy.d.ts +11 -0
  59. package/dist/preview-server/studio-protocol/origin-policy.js +43 -0
  60. package/dist/preview-server/studio-protocol/protocol-response.d.ts +7 -0
  61. package/dist/preview-server/studio-protocol/protocol-response.js +13 -0
  62. package/dist/preview-server/update-available.d.ts +6 -0
  63. package/dist/preview-server/update-available.js +30 -4
  64. package/dist/remotion-skill-names.d.ts +2 -0
  65. package/dist/remotion-skill-names.js +16 -0
  66. package/dist/routes.d.ts +3 -1
  67. package/dist/routes.js +19 -157
  68. package/dist/start-studio.d.ts +3 -1
  69. package/dist/start-studio.js +2 -1
  70. package/package.json +9 -8
@@ -0,0 +1,27 @@
1
+ import type { BuiltInEditor, CustomEditor, DefaultEditor } from '@remotion/renderer';
2
+ import { type ResolvedCustomEditorExecutable } from './custom-editor';
3
+ import type { InstalledEditor } from './editor-registry';
4
+ import type { ProcessAndCommand } from './open-in-editor';
5
+ export type ResolvedEditor = (ProcessAndCommand & {
6
+ type: 'built-in';
7
+ id: BuiltInEditor | null;
8
+ name: string;
9
+ }) | (ResolvedCustomEditorExecutable & {
10
+ type: 'custom';
11
+ id: 'custom';
12
+ name: string;
13
+ editor: CustomEditor;
14
+ });
15
+ type ResolveEditorDependencies = {
16
+ getInstalledEditors: () => Promise<readonly InstalledEditor[]>;
17
+ getLegacyEditors: () => Promise<readonly ProcessAndCommand[]>;
18
+ resolveCustomEditor: (editor: CustomEditor) => ResolvedCustomEditorExecutable | null;
19
+ warn: (message: string) => void;
20
+ warnedEditors: Set<string>;
21
+ };
22
+ export declare const resolveEditor: ({ defaultEditor, logLevel, preferredEditor, }: {
23
+ defaultEditor: DefaultEditor | null;
24
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
25
+ preferredEditor: "cursor" | "custom" | "sublime-text" | "vscode" | "vscodium" | "webstorm" | "windsurf" | "zed" | null;
26
+ }, overrides?: Partial<ResolveEditorDependencies>) => Promise<ResolvedEditor | null>;
27
+ export {};
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveEditor = void 0;
4
+ const renderer_1 = require("@remotion/renderer");
5
+ const custom_editor_1 = require("./custom-editor");
6
+ const editor_registry_1 = require("./editor-registry");
7
+ const open_in_editor_1 = require("./open-in-editor");
8
+ let legacyEditors = null;
9
+ const getLegacyEditors = () => {
10
+ legacyEditors !== null && legacyEditors !== void 0 ? legacyEditors : (legacyEditors = (0, open_in_editor_1.guessEditor)());
11
+ return legacyEditors;
12
+ };
13
+ const warnedEditors = new Set();
14
+ const defaultDependencies = {
15
+ getInstalledEditors: editor_registry_1.getAvailableEditors,
16
+ getLegacyEditors,
17
+ resolveCustomEditor: custom_editor_1.resolveCustomEditorExecutable,
18
+ warn: () => undefined,
19
+ warnedEditors,
20
+ };
21
+ const resolveEditor = async ({ defaultEditor, logLevel, preferredEditor, }, overrides = {}) => {
22
+ var _a;
23
+ const dependencies = {
24
+ ...defaultDependencies,
25
+ warn: (message) => {
26
+ renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, message);
27
+ },
28
+ ...overrides,
29
+ };
30
+ if (preferredEditor === 'custom') {
31
+ if (!defaultEditor || typeof defaultEditor !== 'object') {
32
+ return null;
33
+ }
34
+ const resolvedExecutable = dependencies.resolveCustomEditor(defaultEditor);
35
+ if (!resolvedExecutable) {
36
+ return null;
37
+ }
38
+ return {
39
+ ...resolvedExecutable,
40
+ type: 'custom',
41
+ id: 'custom',
42
+ name: defaultEditor.name,
43
+ editor: defaultEditor,
44
+ };
45
+ }
46
+ if (preferredEditor !== null) {
47
+ const installedEditors = await dependencies.getInstalledEditors();
48
+ const installedEditor = installedEditors.find((editor) => editor.id === preferredEditor);
49
+ return installedEditor ? { ...installedEditor, type: 'built-in' } : null;
50
+ }
51
+ if (defaultEditor && typeof defaultEditor === 'object') {
52
+ const resolvedExecutable = dependencies.resolveCustomEditor(defaultEditor);
53
+ if (resolvedExecutable) {
54
+ return {
55
+ ...resolvedExecutable,
56
+ type: 'custom',
57
+ id: 'custom',
58
+ name: defaultEditor.name,
59
+ editor: defaultEditor,
60
+ };
61
+ }
62
+ const warningId = `custom:${defaultEditor.executable}`;
63
+ if (!dependencies.warnedEditors.has(warningId)) {
64
+ dependencies.warnedEditors.add(warningId);
65
+ dependencies.warn(`The executable for custom editor ${defaultEditor.name} (${defaultEditor.executable}) was not found or is not executable. Falling back to automatic editor detection.`);
66
+ }
67
+ }
68
+ if (typeof defaultEditor === 'string') {
69
+ const installedEditors = await dependencies.getInstalledEditors();
70
+ const installedEditor = installedEditors.find((editor) => editor.id === defaultEditor);
71
+ if (installedEditor) {
72
+ return { ...installedEditor, type: 'built-in' };
73
+ }
74
+ if (!dependencies.warnedEditors.has(defaultEditor)) {
75
+ dependencies.warnedEditors.add(defaultEditor);
76
+ dependencies.warn(`The default editor ${(0, editor_registry_1.getDefaultEditorName)(defaultEditor)} (${defaultEditor}) is not installed. Falling back to automatic editor detection.`);
77
+ }
78
+ }
79
+ const [legacyEditor] = await dependencies.getLegacyEditors();
80
+ if (!legacyEditor) {
81
+ return null;
82
+ }
83
+ return {
84
+ ...legacyEditor,
85
+ type: 'built-in',
86
+ id: null,
87
+ name: (_a = (0, open_in_editor_1.getDisplayNameForEditor)(legacyEditor.command)) !== null && _a !== void 0 ? _a : legacyEditor.command,
88
+ };
89
+ };
90
+ exports.resolveEditor = resolveEditor;
@@ -1,3 +1,5 @@
1
+ export declare class FileOutsideProjectError extends Error {
2
+ }
1
3
  export declare const resolveFileInsideProject: ({ remotionRoot, fileName, action, }: {
2
4
  remotionRoot: string;
3
5
  fileName: string;
@@ -3,9 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.resolveFileInsideProject = void 0;
6
+ exports.resolveFileInsideProject = exports.FileOutsideProjectError = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const formatOutsideProjectError = ({ action, fileName, absolutePath, remotionRoot, }) => `Cannot ${action} a file outside the project: "${fileName}" resolves to "${absolutePath}", but the project root is "${remotionRoot}".`;
9
+ class FileOutsideProjectError extends Error {
10
+ }
11
+ exports.FileOutsideProjectError = FileOutsideProjectError;
9
12
  const resolveFileInsideProject = ({ remotionRoot, fileName, action, }) => {
10
13
  const resolvedRemotionRoot = node_path_1.default.resolve(remotionRoot);
11
14
  const absolutePath = node_path_1.default.resolve(resolvedRemotionRoot, fileName);
@@ -13,7 +16,7 @@ const resolveFileInsideProject = ({ remotionRoot, fileName, action, }) => {
13
16
  if (fileRelativeToRoot === '..' ||
14
17
  fileRelativeToRoot.startsWith(`..${node_path_1.default.sep}`) ||
15
18
  node_path_1.default.isAbsolute(fileRelativeToRoot)) {
16
- throw new Error(formatOutsideProjectError({
19
+ throw new FileOutsideProjectError(formatOutsideProjectError({
17
20
  action,
18
21
  fileName,
19
22
  absolutePath,
package/dist/index.d.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  export { ApiRoutes, CopyStillToClipboardRequest, getDefaultOutLocation, OpenInFileExplorerRequest, } from '@remotion/studio-shared';
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
+ import { detectOutdatedRemotionSkills, parseRemotionSkillVersion } from './detect-outdated-remotion-skills';
5
+ import { remotionSkillNames } from './remotion-skill-names';
6
+ export type { RemotionSkillsScope, RemotionSkillsStatus, } from './detect-outdated-remotion-skills';
7
+ export type { RemotionSkillName } from './remotion-skill-names';
8
+ export { detectOutdatedRemotionSkills, parseRemotionSkillVersion, remotionSkillNames, };
4
9
  export declare const StudioServerInternals: {
5
- startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, remotionRoot, relativePublicDir, bundlerOverride, rspackOverride, webpackOverride, poll, getRenderDefaults, getRenderQueue, getNumberOfAudioTags, queueMethods, previewEntry, gitSource, binariesDirectory, forceIPv4, getAudioLatencyHint, getPreviewSampleRate, enableCrossSiteIsolation, forceNew, rspack, getStudioRuntimeConfig, configFile, }: {
10
+ startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, remotionRoot, relativePublicDir, bundlerOverride, rspackOverride, webpackOverride, poll, getRenderDefaults, getRenderQueue, getNumberOfAudioTags, queueMethods, previewEntry, gitSource, binariesDirectory, forceIPv4, getAudioLatencyHint, getPreviewSampleRate, enableCrossSiteIsolation, forceNew, rspack, getStudioRuntimeConfig, getDefaultEditor, configFile, }: {
6
11
  browserArgs: string;
7
12
  browserFlag: string;
8
13
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -31,6 +36,7 @@ export declare const StudioServerInternals: {
31
36
  forceNew: boolean;
32
37
  rspack: boolean;
33
38
  getStudioRuntimeConfig: () => import("@remotion/studio-shared").StudioRuntimeConfig;
39
+ getDefaultEditor: () => import("@remotion/renderer").DefaultEditor | null;
34
40
  configFile: string | null;
35
41
  }) => Promise<import("./start-studio").StartStudioResult>;
36
42
  getRemotionVersion: () => any;
@@ -120,4 +126,14 @@ export declare const StudioServerInternals: {
120
126
  }) => void;
121
127
  getCompletedClientRenders: () => import("@remotion/studio-shared").CompletedClientRender[];
122
128
  removeCompletedClientRender: (id: string) => void;
129
+ detectOutdatedRemotionSkills: ({ currentVersion, cwd, homeDirectory, }?: {
130
+ currentVersion?: string | undefined;
131
+ cwd?: string | undefined;
132
+ homeDirectory?: string | undefined;
133
+ }) => {
134
+ project: import("./detect-outdated-remotion-skills").RemotionSkillsStatus;
135
+ global: import("./detect-outdated-remotion-skills").RemotionSkillsStatus;
136
+ };
137
+ parseRemotionSkillVersion: (contents: string) => string | null;
138
+ remotionSkillNames: readonly ["remotion-best-practices", "remotion-captions", "remotion-create", "remotion-docs", "remotion-interactivity", "remotion-maps", "remotion-markup", "remotion-multimedia", "remotion-render", "remotion-saas", "remotion-upgrade"];
123
139
  };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StudioServerInternals = exports.getDefaultOutLocation = void 0;
3
+ exports.StudioServerInternals = exports.remotionSkillNames = exports.parseRemotionSkillVersion = exports.detectOutdatedRemotionSkills = exports.getDefaultOutLocation = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const studio_shared_2 = require("@remotion/studio-shared");
6
6
  Object.defineProperty(exports, "getDefaultOutLocation", { enumerable: true, get: function () { return studio_shared_2.getDefaultOutLocation; } });
@@ -9,6 +9,9 @@ const client_render_queue_1 = require("./client-render-queue");
9
9
  const apply_codemod_to_file_1 = require("./codemods/apply-codemod-to-file");
10
10
  const duplicate_composition_1 = require("./codemods/duplicate-composition");
11
11
  const update_default_props_1 = require("./codemods/update-default-props");
12
+ const detect_outdated_remotion_skills_1 = require("./detect-outdated-remotion-skills");
13
+ Object.defineProperty(exports, "detectOutdatedRemotionSkills", { enumerable: true, get: function () { return detect_outdated_remotion_skills_1.detectOutdatedRemotionSkills; } });
14
+ Object.defineProperty(exports, "parseRemotionSkillVersion", { enumerable: true, get: function () { return detect_outdated_remotion_skills_1.parseRemotionSkillVersion; } });
12
15
  const file_watcher_1 = require("./file-watcher");
13
16
  const get_latest_remotion_version_1 = require("./get-latest-remotion-version");
14
17
  const get_installed_dependencies_1 = require("./helpers/get-installed-dependencies");
@@ -18,6 +21,8 @@ const max_timeline_tracks_1 = require("./max-timeline-tracks");
18
21
  const get_package_manager_1 = require("./preview-server/get-package-manager");
19
22
  const live_events_1 = require("./preview-server/live-events");
20
23
  const update_available_1 = require("./preview-server/update-available");
24
+ const remotion_skill_names_1 = require("./remotion-skill-names");
25
+ Object.defineProperty(exports, "remotionSkillNames", { enumerable: true, get: function () { return remotion_skill_names_1.remotionSkillNames; } });
21
26
  const start_studio_1 = require("./start-studio");
22
27
  exports.StudioServerInternals = {
23
28
  startStudio: start_studio_1.startStudio,
@@ -46,4 +51,7 @@ exports.StudioServerInternals = {
46
51
  addCompletedClientRender: client_render_queue_1.addCompletedClientRender,
47
52
  getCompletedClientRenders: client_render_queue_1.getCompletedClientRenders,
48
53
  removeCompletedClientRender: client_render_queue_1.removeCompletedClientRender,
54
+ detectOutdatedRemotionSkills: detect_outdated_remotion_skills_1.detectOutdatedRemotionSkills,
55
+ parseRemotionSkillVersion: detect_outdated_remotion_skills_1.parseRemotionSkillVersion,
56
+ remotionSkillNames: remotion_skill_names_1.remotionSkillNames,
49
57
  };
@@ -12,6 +12,7 @@ const batch_update_keyframe_settings_1 = require("./routes/batch-update-keyframe
12
12
  const cancel_render_1 = require("./routes/cancel-render");
13
13
  const composition_component_info_1 = require("./routes/composition-component-info");
14
14
  const convert_figma_clipboard_to_svg_1 = require("./routes/convert-figma-clipboard-to-svg");
15
+ const default_editor_1 = require("./routes/default-editor");
15
16
  const delete_effect_1 = require("./routes/delete-effect");
16
17
  const delete_jsx_node_1 = require("./routes/delete-jsx-node");
17
18
  const delete_keyframes_1 = require("./routes/delete-keyframes");
@@ -28,6 +29,7 @@ const move_keyframes_1 = require("./routes/move-keyframes");
28
29
  const open_in_editor_1 = require("./routes/open-in-editor");
29
30
  const open_in_file_explorer_1 = require("./routes/open-in-file-explorer");
30
31
  const paste_effects_1 = require("./routes/paste-effects");
32
+ const prepare_element_install_1 = require("./routes/prepare-element-install");
31
33
  const project_info_1 = require("./routes/project-info");
32
34
  const redo_1 = require("./routes/redo");
33
35
  const register_client_render_1 = require("./routes/register-client-render");
@@ -62,9 +64,9 @@ exports.allApiRoutes = {
62
64
  '/api/unsubscribe-from-file-existence': unsubscribe_from_file_existence_1.unsubscribeFromFileExistence,
63
65
  '/api/subscribe-to-file-existence': subscribe_to_file_existence_1.subscribeToFileExistence,
64
66
  '/api/remove-render': remove_render_1.handleRemoveRender,
65
- '/api/open-in-editor': open_in_editor_1.openInEditorHandler,
66
67
  '/api/find-in-file': find_in_file_1.findInFileHandler,
67
68
  '/api/open-in-file-explorer': open_in_file_explorer_1.handleOpenInFileExplorer,
69
+ '/api/open-in-editor': open_in_editor_1.openInEditorHandler,
68
70
  '/api/register-client-render': register_client_render_1.registerClientRenderHandler,
69
71
  '/api/unregister-client-render': unregister_client_render_1.unregisterClientRenderHandler,
70
72
  '/api/update-default-props': update_default_props_1.updateDefaultPropsHandler,
@@ -100,9 +102,12 @@ exports.allApiRoutes = {
100
102
  '/api/rename-static-file': rename_static_file_1.renameStaticFileHandler,
101
103
  '/api/restart-studio': restart_studio_1.handleRestartStudio,
102
104
  '/api/update-public-license': update_public_license_1.updatePublicLicenseHandler,
105
+ '/api/default-editor-info': default_editor_1.getDefaultEditorInfoHandler,
106
+ '/api/update-default-editor': default_editor_1.updateDefaultEditorHandler,
103
107
  '/api/install-package': install_dependency_1.handleInstallPackage,
104
108
  '/api/insert-jsx-element': insert_jsx_element_1.insertJsxElementHandler,
105
109
  '/api/insert-element': insert_element_1.insertElementHandler,
110
+ '/api/prepare-element-install': prepare_element_install_1.prepareElementInstallHandler,
106
111
  '/api/update-element-install-target': update_element_install_target_1.updateElementInstallTargetHandler,
107
112
  '/api/download-remote-asset': download_remote_asset_1.downloadRemoteAssetHandler,
108
113
  '/api/undo': undo_1.undoHandler,
@@ -1,5 +1,5 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import type { LogLevel } from '@remotion/renderer';
2
+ import type { DefaultEditor, LogLevel } from '@remotion/renderer';
3
3
  import type { RenderJobWithCleanup } from '@remotion/studio-shared';
4
4
  export type QueueMethods = {
5
5
  removeJob: (jobId: string) => void;
@@ -22,4 +22,5 @@ export type ApiHandler<ReqData, ResData> = (params: {
22
22
  publicDir: string;
23
23
  binariesDirectory: string | null;
24
24
  configFile: string | null;
25
+ getDefaultEditor: () => DefaultEditor | null;
25
26
  }) => Promise<ResData>;
@@ -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,24 @@ export type ElementInstallTarget = {
10
11
  studioUrl: string;
11
12
  updatedAt: number;
12
13
  };
14
+ type StudioProtocolTarget = {
15
+ readonly id: string;
16
+ readonly origin: string;
17
+ readonly target: ElementInstallTarget;
18
+ readonly expiresAt: number;
19
+ };
13
20
  export declare const updateElementInstallTarget: (newTarget: Omit<ElementInstallTarget, "updatedAt">) => void;
21
+ export declare const getElementInstallTargetByClientId: (clientId: string) => ElementInstallTarget | null;
14
22
  export declare const getElementInstallTarget: (requestId: string | null) => ElementInstallTarget | null;
23
+ export declare const issueStudioProtocolTarget: ({ now, origin, target, }: {
24
+ readonly now: number;
25
+ readonly origin: string;
26
+ readonly target: ElementInstallTarget;
27
+ }) => StudioProtocolTarget;
28
+ export declare const consumeStudioProtocolTarget: ({ now, origin, targetId, }: {
29
+ readonly now: number;
30
+ readonly origin: string;
31
+ readonly targetId: string;
32
+ }) => ElementInstallTarget | null;
15
33
  export declare const clearElementInstallStateForTests: () => void;
34
+ 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,44 @@ const getElementInstallTarget = (requestId) => {
37
45
  return bestTarget;
38
46
  };
39
47
  exports.getElementInstallTarget = getElementInstallTarget;
48
+ const issueStudioProtocolTarget = ({ now, origin, 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
+ origin,
57
+ target: { ...target },
58
+ expiresAt: now + exports.STUDIO_PROTOCOL_TARGET_MAX_AGE,
59
+ };
60
+ studioProtocolTargets.set(issued.id, issued);
61
+ return issued;
62
+ };
63
+ exports.issueStudioProtocolTarget = issueStudioProtocolTarget;
64
+ const consumeStudioProtocolTarget = ({ now, origin, targetId, }) => {
65
+ const issued = studioProtocolTargets.get(targetId);
66
+ studioProtocolTargets.delete(targetId);
67
+ if (issued === undefined ||
68
+ issued.expiresAt <= now ||
69
+ issued.origin !== origin) {
70
+ return null;
71
+ }
72
+ const current = (0, exports.getElementInstallTargetByClientId)(issued.target.clientId);
73
+ if (current === null ||
74
+ now - current.updatedAt >= exports.ELEMENT_INSTALL_TARGET_MAX_AGE ||
75
+ !current.canInstall ||
76
+ current.readOnly ||
77
+ current.compositionFile !== issued.target.compositionFile ||
78
+ current.compositionId !== issued.target.compositionId) {
79
+ return null;
80
+ }
81
+ return issued.target;
82
+ };
83
+ exports.consumeStudioProtocolTarget = consumeStudioProtocolTarget;
40
84
  const clearElementInstallStateForTests = () => {
41
85
  targetsByClientId.clear();
86
+ studioProtocolTargets.clear();
42
87
  };
43
88
  exports.clearElementInstallStateForTests = clearElementInstallStateForTests;
@@ -1,6 +1,7 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { DefaultEditor } from '@remotion/renderer';
2
3
  import type { ApiHandler, QueueMethods } from './api-types';
3
- export declare const handleRequest: <Req, Res>({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, configFile, }: {
4
+ export declare const handleRequest: <Req, Res>({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, configFile, getDefaultEditor, }: {
4
5
  remotionRoot: string;
5
6
  publicDir: string;
6
7
  request: IncomingMessage;
@@ -8,6 +9,7 @@ export declare const handleRequest: <Req, Res>({ remotionRoot, request, response
8
9
  entryPoint: string;
9
10
  binariesDirectory: string | null;
10
11
  configFile: string | null;
12
+ getDefaultEditor: () => DefaultEditor | null;
11
13
  handler: ApiHandler<Req, Res>;
12
14
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
13
15
  methods: QueueMethods;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleRequest = void 0;
4
4
  const parse_body_1 = require("./parse-body");
5
5
  const validate_same_origin_1 = require("./validate-same-origin");
6
- const handleRequest = async ({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, configFile, }) => {
6
+ const handleRequest = async ({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, configFile, getDefaultEditor, }) => {
7
7
  if (request.method === 'OPTIONS') {
8
8
  response.statusCode = 200;
9
9
  response.end();
@@ -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,
@@ -25,6 +25,7 @@ const handleRequest = async ({ remotionRoot, request, response, entryPoint, hand
25
25
  binariesDirectory,
26
26
  publicDir,
27
27
  configFile,
28
+ getDefaultEditor,
28
29
  });
29
30
  response.end(JSON.stringify({
30
31
  success: true,
@@ -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;