@remotion/studio-server 4.0.517 → 4.0.519
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/codemods/delete-jsx-node.d.ts +1 -4
- package/dist/codemods/duplicate-jsx-node.d.ts +11 -0
- package/dist/codemods/duplicate-jsx-node.js +11 -1
- package/dist/codemods/get-node-path-remappings.d.ts +1 -0
- package/dist/codemods/get-node-path-remappings.js +15 -2
- package/dist/codemods/split-video-from-audio.d.ts +1 -4
- package/dist/helpers/open-in-editor.js +1 -1
- package/dist/preview-server/api-routes.d.ts +3 -1
- package/dist/preview-server/api-routes.js +2 -2
- package/dist/preview-server/element-install-state.d.ts +1 -2
- package/dist/preview-server/element-install-state.js +2 -3
- package/dist/preview-server/routes/delete-jsx-node.js +0 -1
- package/dist/preview-server/routes/duplicate-jsx-node.js +71 -39
- package/dist/preview-server/routes/element-install-plan.d.ts +4 -2
- package/dist/preview-server/routes/element-install-plan.js +22 -9
- package/dist/preview-server/routes/insert-element.js +13 -6
- package/dist/preview-server/routes/insert-jsx-element.js +0 -1
- package/dist/preview-server/routes/prepare-element-install.js +7 -2
- package/dist/preview-server/routes/release-notes.d.ts +3 -0
- package/dist/preview-server/routes/release-notes.js +127 -0
- package/dist/preview-server/routes/reorder-sequence.js +0 -1
- package/dist/preview-server/routes/split-jsx-sequence.js +0 -1
- package/dist/preview-server/routes/split-video-from-audio.js +0 -1
- package/dist/preview-server/routes/update-config.d.ts +8 -3
- package/dist/preview-server/routes/update-config.js +114 -15
- package/dist/preview-server/sequence-node-path-mutation.d.ts +0 -2
- package/dist/preview-server/start-server.js +9 -5
- package/dist/preview-server/studio-protocol/handle-discovery.js +22 -3
- package/dist/preview-server/studio-protocol/handle-element-library.d.ts +11 -0
- package/dist/preview-server/studio-protocol/handle-element-library.js +149 -0
- package/dist/preview-server/undo-stack.js +4 -13
- package/dist/routes.js +31 -1
- package/package.json +8 -8
|
@@ -9,10 +9,7 @@ export declare const deleteJsxNodes: ({ input, nodePaths, }: {
|
|
|
9
9
|
formatted: boolean;
|
|
10
10
|
nodeLabels: string[];
|
|
11
11
|
logLines: number[];
|
|
12
|
-
nodePathRemappings:
|
|
13
|
-
oldNodePath: SequenceNodePath;
|
|
14
|
-
newNodePath: SequenceNodePath | null;
|
|
15
|
-
}[];
|
|
12
|
+
nodePathRemappings: import("@remotion/studio-shared").SequenceNodePathRemapping[];
|
|
16
13
|
}>;
|
|
17
14
|
export declare const deleteJsxNode: ({ input, nodePath, }: {
|
|
18
15
|
input: string;
|
|
@@ -12,3 +12,14 @@ export declare const duplicateJsxNode: ({ input, nodePath, prettierConfigOverrid
|
|
|
12
12
|
logLine: number;
|
|
13
13
|
nodePathRemappings: import("@remotion/studio-shared").SequenceNodePathRemapping[];
|
|
14
14
|
}>;
|
|
15
|
+
export declare const duplicateJsxNodes: ({ input, nodePaths, prettierConfigOverride, }: {
|
|
16
|
+
input: string;
|
|
17
|
+
nodePaths: SequenceNodePath[];
|
|
18
|
+
prettierConfigOverride?: Record<string, unknown> | null | undefined;
|
|
19
|
+
}) => Promise<{
|
|
20
|
+
output: string;
|
|
21
|
+
formatted: boolean;
|
|
22
|
+
nodeLabels: string[];
|
|
23
|
+
logLines: number[];
|
|
24
|
+
nodePathRemappings: import("@remotion/studio-shared").SequenceNodePathRemapping[];
|
|
25
|
+
}>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.duplicateJsxNode = exports.duplicateJsxElementAtPath = void 0;
|
|
3
|
+
exports.duplicateJsxNodes = exports.duplicateJsxNode = exports.duplicateJsxElementAtPath = void 0;
|
|
4
4
|
const studio_codemods_1 = require("@remotion/studio-codemods");
|
|
5
5
|
Object.defineProperty(exports, "duplicateJsxElementAtPath", { enumerable: true, get: function () { return studio_codemods_1.duplicateJsxElementAtPath; } });
|
|
6
6
|
const format_file_content_1 = require("./format-file-content");
|
|
@@ -14,3 +14,13 @@ const duplicateJsxNode = ({ input, nodePath, prettierConfigOverride, }) => (0, s
|
|
|
14
14
|
prettierConfigOverride,
|
|
15
15
|
});
|
|
16
16
|
exports.duplicateJsxNode = duplicateJsxNode;
|
|
17
|
+
const duplicateJsxNodes = ({ input, nodePaths, prettierConfigOverride, }) => (0, studio_codemods_1.duplicateJsxNodes)({
|
|
18
|
+
input,
|
|
19
|
+
nodePaths,
|
|
20
|
+
formatFile: ({ contents, prettierConfigOverride: override }) => (0, format_file_content_1.formatFileContent)({
|
|
21
|
+
input: contents,
|
|
22
|
+
prettierConfigOverride: override,
|
|
23
|
+
}),
|
|
24
|
+
prettierConfigOverride,
|
|
25
|
+
});
|
|
26
|
+
exports.duplicateJsxNodes = duplicateJsxNodes;
|
|
@@ -4,6 +4,7 @@ import type { SequenceNodePath } from 'remotion';
|
|
|
4
4
|
export type CapturedJsxNodePath = {
|
|
5
5
|
node: JSXOpeningElement;
|
|
6
6
|
nodePath: SequenceNodePath;
|
|
7
|
+
signature: string;
|
|
7
8
|
};
|
|
8
9
|
export declare const captureJsxNodePaths: (ast: File) => CapturedJsxNodePath[];
|
|
9
10
|
export declare const getNodePathRemappings: ({ ast, captured, output, }: {
|
|
@@ -44,6 +44,7 @@ const captureJsxNodePaths = (ast) => {
|
|
|
44
44
|
captured.push({
|
|
45
45
|
node: path.node,
|
|
46
46
|
nodePath: (0, can_update_sequence_props_1.getNodePathForRecastPath)(path, ast),
|
|
47
|
+
signature: recast.prettyPrint(path.node).code,
|
|
47
48
|
});
|
|
48
49
|
return this.traverse(path);
|
|
49
50
|
},
|
|
@@ -74,15 +75,27 @@ const getNodePathRemappings = ({ ast, captured, output, }) => {
|
|
|
74
75
|
for (let i = 0; i < nodesAfterMutation.length; i++) {
|
|
75
76
|
finalNodePathByNode.set(nodesAfterMutation[i], finalNodePaths[i]);
|
|
76
77
|
}
|
|
77
|
-
const
|
|
78
|
+
const capturedNodes = new Set(captured.map(({ node }) => node));
|
|
79
|
+
const nodePathRemappings = captured.flatMap(({ node, nodePath, signature }) => {
|
|
78
80
|
var _a;
|
|
79
81
|
const newNodePath = (_a = finalNodePathByNode.get(node)) !== null && _a !== void 0 ? _a : null;
|
|
80
82
|
if (newNodePath !== null &&
|
|
81
|
-
JSON.stringify(nodePath) === JSON.stringify(newNodePath)
|
|
83
|
+
JSON.stringify(nodePath) === JSON.stringify(newNodePath) &&
|
|
84
|
+
recast.prettyPrint(node).code === signature) {
|
|
82
85
|
return [];
|
|
83
86
|
}
|
|
84
87
|
return [{ oldNodePath: nodePath, newNodePath }];
|
|
85
88
|
});
|
|
89
|
+
for (const node of nodesAfterMutation) {
|
|
90
|
+
if (capturedNodes.has(node)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const newNodePath = finalNodePathByNode.get(node);
|
|
94
|
+
if (!newNodePath) {
|
|
95
|
+
throw new Error('Could not map inserted JSX node path');
|
|
96
|
+
}
|
|
97
|
+
nodePathRemappings.push({ oldNodePath: null, newNodePath });
|
|
98
|
+
}
|
|
86
99
|
return { finalNodePathByNode, nodePathRemappings };
|
|
87
100
|
};
|
|
88
101
|
exports.getNodePathRemappings = getNodePathRemappings;
|
|
@@ -8,8 +8,5 @@ export declare const splitVideoFromAudio: ({ input, nodePath, prettierConfigOver
|
|
|
8
8
|
formatted: boolean;
|
|
9
9
|
nodeLabel: string;
|
|
10
10
|
logLine: number;
|
|
11
|
-
nodePathRemappings:
|
|
12
|
-
oldNodePath: SequenceNodePath;
|
|
13
|
-
newNodePath: SequenceNodePath | null;
|
|
14
|
-
}[];
|
|
11
|
+
nodePathRemappings: import("@remotion/studio-shared").SequenceNodePathRemapping[];
|
|
15
12
|
}>;
|
|
@@ -255,7 +255,7 @@ function getArgumentsForLineNumber(editor, fileName, lineNumber, colNumber) {
|
|
|
255
255
|
editor.endsWith('/Zed Preview.app/Contents/MacOS/cli');
|
|
256
256
|
const isFolder = node_fs_1.default.existsSync(fileName) && node_fs_1.default.lstatSync(fileName).isDirectory();
|
|
257
257
|
if (isZedEditor) {
|
|
258
|
-
return [fileName + ':' + lineNumber + ':' + colNumber];
|
|
258
|
+
return ['--existing', fileName + ':' + lineNumber + ':' + colNumber];
|
|
259
259
|
}
|
|
260
260
|
switch (editorBasename) {
|
|
261
261
|
case 'atom':
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ApiRoutes } from '@remotion/studio-shared';
|
|
2
2
|
import type { ApiHandler } from './api-types';
|
|
3
|
+
type StandardApiRoute = Exclude<keyof ApiRoutes, '/api/update-config'>;
|
|
3
4
|
export declare const allApiRoutes: {
|
|
4
|
-
[key in
|
|
5
|
+
[key in StandardApiRoute]: ApiHandler<ApiRoutes[key]['Request'], ApiRoutes[key]['Response']>;
|
|
5
6
|
};
|
|
7
|
+
export {};
|
|
@@ -37,6 +37,7 @@ const prepare_element_install_1 = require("./routes/prepare-element-install");
|
|
|
37
37
|
const project_info_1 = require("./routes/project-info");
|
|
38
38
|
const redo_1 = require("./routes/redo");
|
|
39
39
|
const register_client_render_1 = require("./routes/register-client-render");
|
|
40
|
+
const release_notes_1 = require("./routes/release-notes");
|
|
40
41
|
const remotion_skills_info_1 = require("./routes/remotion-skills-info");
|
|
41
42
|
const remove_render_1 = require("./routes/remove-render");
|
|
42
43
|
const rename_static_file_1 = require("./routes/rename-static-file");
|
|
@@ -57,7 +58,6 @@ const unsubscribe_from_default_props_1 = require("./routes/unsubscribe-from-defa
|
|
|
57
58
|
const unsubscribe_from_file_existence_1 = require("./routes/unsubscribe-from-file-existence");
|
|
58
59
|
const unsubscribe_from_sequence_props_1 = require("./routes/unsubscribe-from-sequence-props");
|
|
59
60
|
const update_available_1 = require("./routes/update-available");
|
|
60
|
-
const update_config_1 = require("./routes/update-config");
|
|
61
61
|
const update_default_props_1 = require("./routes/update-default-props");
|
|
62
62
|
const update_effect_keyframe_settings_1 = require("./routes/update-effect-keyframe-settings");
|
|
63
63
|
const update_element_install_target_1 = require("./routes/update-element-install-target");
|
|
@@ -108,12 +108,12 @@ exports.allApiRoutes = {
|
|
|
108
108
|
'/api/split-jsx-sequence': split_jsx_sequence_1.splitJsxSequenceHandler,
|
|
109
109
|
'/api/split-video-from-audio': split_video_from_audio_1.splitVideoFromAudioHandler,
|
|
110
110
|
'/api/update-available': update_available_1.handleUpdate,
|
|
111
|
+
'/api/release-notes': release_notes_1.getReleaseNotesHandler,
|
|
111
112
|
'/api/remotion-skills-info': remotion_skills_info_1.remotionSkillsInfoHandler,
|
|
112
113
|
'/api/project-info': project_info_1.projectInfoHandler,
|
|
113
114
|
'/api/delete-static-file': delete_static_file_1.deleteStaticFileHandler,
|
|
114
115
|
'/api/rename-static-file': rename_static_file_1.renameStaticFileHandler,
|
|
115
116
|
'/api/restart-studio': restart_studio_1.handleRestartStudio,
|
|
116
|
-
'/api/update-config': update_config_1.updateConfigHandler,
|
|
117
117
|
'/api/default-coding-agent-info': default_coding_agent_1.getDefaultCodingAgentInfoHandler,
|
|
118
118
|
'/api/default-editor-info': default_editor_1.getDefaultEditorInfoHandler,
|
|
119
119
|
'/api/install-package': install_dependency_1.handleInstallPackage,
|
|
@@ -5,13 +5,12 @@ export type ElementInstallTarget = {
|
|
|
5
5
|
clientId: string;
|
|
6
6
|
compositionFile: string | null;
|
|
7
7
|
compositionId: string | null;
|
|
8
|
-
canInstall: boolean;
|
|
9
8
|
lastFocusedAt: number | null;
|
|
10
9
|
readOnly: boolean;
|
|
11
10
|
studioUrl: string;
|
|
12
11
|
updatedAt: number;
|
|
13
12
|
};
|
|
14
|
-
export type StudioProtocolTargetPurpose = 'install-element' | 'set-license-key';
|
|
13
|
+
export type StudioProtocolTargetPurpose = 'install-element' | 'set-license-key' | 'add-element-library';
|
|
15
14
|
type StudioProtocolTarget = {
|
|
16
15
|
readonly id: string;
|
|
17
16
|
readonly origin: string;
|
|
@@ -77,11 +77,10 @@ const consumeStudioProtocolTarget = ({ now, origin, purpose, targetId, }) => {
|
|
|
77
77
|
current.readOnly) {
|
|
78
78
|
return null;
|
|
79
79
|
}
|
|
80
|
-
if (purpose
|
|
80
|
+
if (purpose !== 'install-element') {
|
|
81
81
|
return issued.target;
|
|
82
82
|
}
|
|
83
|
-
if (
|
|
84
|
-
current.compositionFile !== issued.target.compositionFile ||
|
|
83
|
+
if (current.compositionFile !== issued.target.compositionFile ||
|
|
85
84
|
current.compositionId !== issued.target.compositionId) {
|
|
86
85
|
return null;
|
|
87
86
|
}
|
|
@@ -65,7 +65,6 @@ const deleteJsxNodeHandler = ({ input: { nodes }, remotionRoot, logLevel }) => {
|
|
|
65
65
|
const nodePathMutation = (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(updates.map((update) => ({
|
|
66
66
|
absolutePath: update.absolutePath,
|
|
67
67
|
remappings: update.nodePathRemappings,
|
|
68
|
-
restoredNodePaths: [],
|
|
69
68
|
})));
|
|
70
69
|
for (const update of updates) {
|
|
71
70
|
const deletedNodeDescription = getDeletedNodeDescription(update.nodeLabels);
|
|
@@ -11,56 +11,88 @@ const sequence_node_path_mutation_1 = require("../sequence-node-path-mutation");
|
|
|
11
11
|
const undo_stack_1 = require("../undo-stack");
|
|
12
12
|
const log_update_1 = require("./log-updates/log-update");
|
|
13
13
|
const source_file_write_queue_1 = require("./source-file-write-queue");
|
|
14
|
-
const duplicateJsxNodeHandler = ({ input: {
|
|
14
|
+
const duplicateJsxNodeHandler = ({ input: { nodes }, remotionRoot, logLevel }) => {
|
|
15
15
|
return (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
16
|
+
var _a;
|
|
16
17
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
if (nodes.length === 0) {
|
|
19
|
+
throw new Error('No JSX nodes were specified for duplication');
|
|
20
|
+
}
|
|
21
|
+
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[duplicate-jsx-node] Received request to duplicate ${nodes.length} JSX node${nodes.length === 1 ? '' : 's'}`);
|
|
22
|
+
const itemsByFileName = new Map();
|
|
23
|
+
for (const item of nodes) {
|
|
24
|
+
const fileItems = (_a = itemsByFileName.get(item.fileName)) !== null && _a !== void 0 ? _a : [];
|
|
25
|
+
fileItems.push(item);
|
|
26
|
+
itemsByFileName.set(item.fileName, fileItems);
|
|
27
|
+
}
|
|
28
|
+
const updates = await Promise.all([...itemsByFileName.entries()].map(async ([fileName, fileItems]) => {
|
|
29
|
+
const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
|
|
30
|
+
remotionRoot,
|
|
31
|
+
fileName,
|
|
32
|
+
action: 'modify',
|
|
33
|
+
});
|
|
34
|
+
const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
35
|
+
const { output, formatted, nodeLabels, logLines, nodePathRemappings } = await (0, duplicate_jsx_node_1.duplicateJsxNodes)({
|
|
36
|
+
input: fileContents,
|
|
37
|
+
nodePaths: fileItems.map((item) => item.nodePath),
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
27
40
|
absolutePath,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
fileRelativeToRoot,
|
|
42
|
+
fileContents,
|
|
43
|
+
formatted,
|
|
44
|
+
logLine: Math.min(...logLines),
|
|
45
|
+
nodeLabels,
|
|
46
|
+
nodePathRemappings,
|
|
47
|
+
output,
|
|
48
|
+
};
|
|
49
|
+
}));
|
|
50
|
+
const nodePathMutation = (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(updates.map((update) => ({
|
|
51
|
+
absolutePath: update.absolutePath,
|
|
52
|
+
remappings: update.nodePathRemappings,
|
|
53
|
+
})));
|
|
54
|
+
const duplicatedNodeDescription = nodes.length === 1
|
|
55
|
+
? updates[0].nodeLabels[0]
|
|
56
|
+
: `${nodes.length} JSX nodes`;
|
|
57
|
+
(0, undo_stack_1.pushTransactionToUndoStack)({
|
|
58
|
+
snapshots: updates.map((update) => ({
|
|
59
|
+
filePath: update.absolutePath,
|
|
60
|
+
oldContents: update.fileContents,
|
|
61
|
+
newContents: null,
|
|
62
|
+
logLine: update.logLine,
|
|
63
|
+
nodePathRemappings: update.nodePathRemappings,
|
|
64
|
+
})),
|
|
36
65
|
logLevel,
|
|
37
66
|
remotionRoot,
|
|
38
|
-
logLine,
|
|
39
67
|
description: {
|
|
40
|
-
undoMessage: `↩️ Duplication of ${
|
|
41
|
-
redoMessage: `↪️ Duplication of ${
|
|
68
|
+
undoMessage: `↩️ Duplication of ${duplicatedNodeDescription}`,
|
|
69
|
+
redoMessage: `↪️ Duplication of ${duplicatedNodeDescription}`,
|
|
42
70
|
},
|
|
43
71
|
entryType: 'duplicate-jsx-node',
|
|
44
72
|
suppressHmrOnFileRestore: false,
|
|
45
|
-
nodePathRemappings,
|
|
46
|
-
});
|
|
47
|
-
(0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
|
|
48
|
-
(0, file_watcher_1.writeFileAndNotifyFileWatchers)({
|
|
49
|
-
file: absolutePath,
|
|
50
|
-
content: output,
|
|
51
|
-
originatorClientId: undefined,
|
|
52
|
-
metadata: { skipSequencePropsUpdate: true },
|
|
53
|
-
});
|
|
54
|
-
const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
|
|
55
|
-
remotionRoot,
|
|
56
|
-
absolutePath,
|
|
57
|
-
line: logLine,
|
|
58
73
|
});
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
(0,
|
|
74
|
+
for (const update of updates) {
|
|
75
|
+
(0, undo_stack_1.suppressUndoStackInvalidation)(update.absolutePath);
|
|
76
|
+
(0, file_watcher_1.writeFileAndNotifyFileWatchers)({
|
|
77
|
+
file: update.absolutePath,
|
|
78
|
+
content: update.output,
|
|
79
|
+
originatorClientId: undefined,
|
|
80
|
+
metadata: { skipSequencePropsUpdate: true },
|
|
81
|
+
});
|
|
82
|
+
const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
|
|
83
|
+
remotionRoot,
|
|
84
|
+
absolutePath: update.absolutePath,
|
|
85
|
+
line: update.logLine,
|
|
86
|
+
});
|
|
87
|
+
const fileDescription = update.nodeLabels.length === 1
|
|
88
|
+
? update.nodeLabels[0]
|
|
89
|
+
: `${update.nodeLabels.length} JSX nodes`;
|
|
90
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${(0, source_file_write_queue_1.getCodemodTimingPrefix)(logLevel)}${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} Duplicated ${fileDescription}`);
|
|
91
|
+
if (!update.formatted) {
|
|
92
|
+
(0, log_update_1.warnAboutPrettierOnce)(logLevel);
|
|
93
|
+
}
|
|
94
|
+
renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[duplicate-jsx-node] Wrote ${update.fileRelativeToRoot}${update.formatted ? ' (formatted)' : ''}`);
|
|
62
95
|
}
|
|
63
|
-
renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[duplicate-jsx-node] Wrote ${fileRelativeToRoot}${formatted ? ' (formatted)' : ''}`);
|
|
64
96
|
(0, undo_stack_1.printUndoHint)(logLevel);
|
|
65
97
|
return {
|
|
66
98
|
success: true,
|
|
@@ -15,17 +15,19 @@ export declare const makeRelativeElementImportPath: ({ fromFile, toFile, }: {
|
|
|
15
15
|
fromFile: string;
|
|
16
16
|
toFile: string;
|
|
17
17
|
}) => string;
|
|
18
|
-
export declare const getElementInstallPlan: ({
|
|
18
|
+
export declare const getElementInstallPlan: ({ destination, element, entryPoint, remotionRoot, }: PrepareElementInstallRequest & {
|
|
19
|
+
entryPoint: string;
|
|
19
20
|
remotionRoot: string;
|
|
20
21
|
}) => Promise<{
|
|
21
22
|
componentName: string;
|
|
23
|
+
destinationCompositionFileName: string;
|
|
22
24
|
elementFileExists: boolean;
|
|
23
25
|
elementFileName: string;
|
|
24
26
|
existingElementSource: string | null;
|
|
25
27
|
expectedFileState: ElementInstallExpectedFileState;
|
|
26
28
|
filePath: string;
|
|
27
29
|
importPath: string;
|
|
28
|
-
location: import("@remotion/studio-codemods").ResolvedCompositionComponentWithFile;
|
|
30
|
+
location: import("@remotion/studio-codemods").ResolvedCompositionComponentWithFile | null;
|
|
29
31
|
safePaths: {
|
|
30
32
|
compositionFileName: string;
|
|
31
33
|
elementFileName: string;
|
|
@@ -9,6 +9,7 @@ const node_fs_1 = require("node:fs");
|
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const studio_protocol_1 = require("@remotion/studio-protocol");
|
|
11
11
|
const resolve_composition_component_1 = require("../../helpers/resolve-composition-component");
|
|
12
|
+
const project_info_1 = require("../project-info");
|
|
12
13
|
const safe_element_install_path_1 = require("./safe-element-install-path");
|
|
13
14
|
const normalizeElementSourceForComparison = (source) => {
|
|
14
15
|
return source.replace(/\r\n/g, '\n').trim();
|
|
@@ -74,27 +75,38 @@ const getExpectedFileState = (existingSource) => {
|
|
|
74
75
|
sourceHash: (0, exports.getElementSourceHash)(existingSource),
|
|
75
76
|
};
|
|
76
77
|
};
|
|
77
|
-
const getElementInstallPlan = async ({
|
|
78
|
+
const getElementInstallPlan = async ({ destination, element, entryPoint, remotionRoot, }) => {
|
|
79
|
+
var _a;
|
|
78
80
|
(0, exports.validateElementForInstallation)(element);
|
|
79
81
|
const componentName = studio_protocol_1.StudioProtocolInternals.getElementComponentNameFromSourceCode(element.sourceCode);
|
|
80
82
|
if (componentName === null) {
|
|
81
83
|
throw new Error('Element source must export exactly one named component');
|
|
82
84
|
}
|
|
83
|
-
const location =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
const location = destination.type === 'current-composition'
|
|
86
|
+
? await (0, resolve_composition_component_1.resolveCompositionComponentWithFile)({
|
|
87
|
+
remotionRoot,
|
|
88
|
+
compositionFile: destination.compositionFile,
|
|
89
|
+
compositionId: destination.compositionId,
|
|
90
|
+
})
|
|
91
|
+
: null;
|
|
92
|
+
if (location !== null && !location.canAddSequence) {
|
|
89
93
|
throw new Error('Cannot insert Element into this composition component');
|
|
90
94
|
}
|
|
91
95
|
const derivedElementFileName = studio_protocol_1.StudioProtocolInternals.makeElementFileNameFromSlug(element.slug);
|
|
92
96
|
if (derivedElementFileName === null) {
|
|
93
97
|
throw new Error('Element slug must produce a safe lowercase .tsx file name');
|
|
94
98
|
}
|
|
99
|
+
const destinationCompositionFile = destination.compositionFile;
|
|
100
|
+
const destinationCompositionFileName = destinationCompositionFile === null
|
|
101
|
+
? (await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint)).rootFile
|
|
102
|
+
: node_path_1.default.resolve(remotionRoot, destinationCompositionFile);
|
|
103
|
+
if (destinationCompositionFileName === null) {
|
|
104
|
+
throw new Error('Could not find the root file of the project');
|
|
105
|
+
}
|
|
106
|
+
const compositionFileName = (_a = location === null || location === void 0 ? void 0 : location.fileName) !== null && _a !== void 0 ? _a : destinationCompositionFileName;
|
|
95
107
|
const safePaths = await (0, safe_element_install_path_1.getSafeElementInstallPaths)({
|
|
96
|
-
compositionFileName
|
|
97
|
-
elementFileName: node_path_1.default.resolve(node_path_1.default.dirname(
|
|
108
|
+
compositionFileName,
|
|
109
|
+
elementFileName: node_path_1.default.resolve(node_path_1.default.dirname(compositionFileName), derivedElementFileName),
|
|
98
110
|
remotionRoot,
|
|
99
111
|
});
|
|
100
112
|
const elementFileExists = (0, node_fs_1.existsSync)(safePaths.elementFileName);
|
|
@@ -103,6 +115,7 @@ const getElementInstallPlan = async ({ compositionFile, compositionId, element,
|
|
|
103
115
|
: null;
|
|
104
116
|
return {
|
|
105
117
|
componentName,
|
|
118
|
+
destinationCompositionFileName,
|
|
106
119
|
elementFileExists,
|
|
107
120
|
elementFileName: safePaths.elementFileName,
|
|
108
121
|
existingElementSource,
|
|
@@ -22,7 +22,7 @@ const hasExpectedFileState = ({ expected, actual, }) => {
|
|
|
22
22
|
}
|
|
23
23
|
return actual.sourceHash === expected.sourceHash;
|
|
24
24
|
};
|
|
25
|
-
const insertElementHandler = ({ input: { compositionFile, compositionId, element, expectedFileState, from, position, overwriteExisting, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
25
|
+
const insertElementHandler = ({ input: { compositionFile, compositionId, element, expectedFileState, from, position, overwriteExisting, }, entryPoint, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
26
26
|
var _a, _b;
|
|
27
27
|
try {
|
|
28
28
|
(0, element_install_plan_1.validateElementInstallPosition)(position);
|
|
@@ -34,9 +34,13 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
|
|
|
34
34
|
const componentOwnsSequence = installationMode === 'component-owned-sequence';
|
|
35
35
|
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[insert-element] Received request for compositionFile="${compositionFile}" compositionId="${compositionId}" element="${element.slug}"`);
|
|
36
36
|
const plan = await (0, element_install_plan_1.getElementInstallPlan)({
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
destination: {
|
|
38
|
+
type: 'current-composition',
|
|
39
|
+
compositionFile,
|
|
40
|
+
compositionId,
|
|
41
|
+
},
|
|
39
42
|
element,
|
|
43
|
+
entryPoint,
|
|
40
44
|
remotionRoot,
|
|
41
45
|
});
|
|
42
46
|
if (expectedFileState !== null &&
|
|
@@ -112,9 +116,13 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
|
|
|
112
116
|
},
|
|
113
117
|
});
|
|
114
118
|
const finalPlan = await (0, element_install_plan_1.getElementInstallPlan)({
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
destination: {
|
|
120
|
+
type: 'current-composition',
|
|
121
|
+
compositionFile,
|
|
122
|
+
compositionId,
|
|
123
|
+
},
|
|
117
124
|
element,
|
|
125
|
+
entryPoint,
|
|
118
126
|
remotionRoot,
|
|
119
127
|
});
|
|
120
128
|
if (finalPlan.safePaths.compositionFileName !==
|
|
@@ -131,7 +139,6 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
|
|
|
131
139
|
{
|
|
132
140
|
absolutePath: inserted.fileName,
|
|
133
141
|
remappings: inserted.nodePathRemappings,
|
|
134
|
-
restoredNodePaths: [],
|
|
135
142
|
},
|
|
136
143
|
]);
|
|
137
144
|
(0, undo_stack_1.pushTransactionToUndoStack)({
|
|
@@ -3,12 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.prepareElementInstallHandler = void 0;
|
|
4
4
|
const element_install_plan_1 = require("./element-install-plan");
|
|
5
5
|
const source_file_write_queue_1 = require("./source-file-write-queue");
|
|
6
|
-
const prepareElementInstallHandler = ({ input, remotionRoot }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
6
|
+
const prepareElementInstallHandler = ({ entryPoint, input, remotionRoot }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
7
7
|
try {
|
|
8
|
-
const plan = await (0, element_install_plan_1.getElementInstallPlan)({
|
|
8
|
+
const plan = await (0, element_install_plan_1.getElementInstallPlan)({
|
|
9
|
+
...input,
|
|
10
|
+
entryPoint,
|
|
11
|
+
remotionRoot,
|
|
12
|
+
});
|
|
9
13
|
return {
|
|
10
14
|
success: true,
|
|
11
15
|
plan: {
|
|
16
|
+
compositionFile: plan.destinationCompositionFileName,
|
|
12
17
|
expectedFileState: plan.expectedFileState,
|
|
13
18
|
filePath: plan.filePath,
|
|
14
19
|
},
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getReleaseNotesHandler = void 0;
|
|
7
|
+
const semver_1 = __importDefault(require("semver"));
|
|
8
|
+
const githubApiVersion = '2022-11-28';
|
|
9
|
+
const releaseNotesTimeout = 5000;
|
|
10
|
+
const releaseNotesCacheDuration = 5 * 60 * 1000;
|
|
11
|
+
const maximumReleaseNotes = 5;
|
|
12
|
+
const releaseNotesCache = new Map();
|
|
13
|
+
const getReleaseNotesHandler = async ({ input }) => {
|
|
14
|
+
const currentVersion = semver_1.default.valid(input.currentVersion);
|
|
15
|
+
const latestVersion = semver_1.default.valid(input.latestVersion);
|
|
16
|
+
if (currentVersion === null || latestVersion === null) {
|
|
17
|
+
throw new Error(`Invalid Remotion version range: ${input.currentVersion} to ${input.latestVersion}`);
|
|
18
|
+
}
|
|
19
|
+
const cacheKey = `${currentVersion}:${latestVersion}`;
|
|
20
|
+
const cachedReleaseNotes = releaseNotesCache.get(cacheKey);
|
|
21
|
+
if (cachedReleaseNotes && cachedReleaseNotes.expiresAt > Date.now()) {
|
|
22
|
+
return cachedReleaseNotes.response;
|
|
23
|
+
}
|
|
24
|
+
const controller = new AbortController();
|
|
25
|
+
const timeout = setTimeout(() => controller.abort(), releaseNotesTimeout);
|
|
26
|
+
try {
|
|
27
|
+
const releaseResponse = await fetch('https://api.github.com/repos/remotion-dev/remotion/releases?per_page=100', {
|
|
28
|
+
headers: {
|
|
29
|
+
accept: 'application/vnd.github+json',
|
|
30
|
+
'user-agent': 'Remotion Studio',
|
|
31
|
+
'x-github-api-version': githubApiVersion,
|
|
32
|
+
},
|
|
33
|
+
signal: controller.signal,
|
|
34
|
+
});
|
|
35
|
+
if (!releaseResponse.ok) {
|
|
36
|
+
return { hasMore: false, releases: [] };
|
|
37
|
+
}
|
|
38
|
+
const githubReleases = (await releaseResponse.json());
|
|
39
|
+
if (!Array.isArray(githubReleases)) {
|
|
40
|
+
return { hasMore: false, releases: [] };
|
|
41
|
+
}
|
|
42
|
+
const matchingReleases = githubReleases
|
|
43
|
+
.flatMap((release) => {
|
|
44
|
+
if (typeof release !== 'object' || release === null) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
const { body, published_at: publishedAt, tag_name: tagName, } = release;
|
|
48
|
+
if (typeof tagName !== 'string') {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const version = semver_1.default.valid(tagName.startsWith('v') ? tagName.slice(1) : tagName);
|
|
52
|
+
if (version === null ||
|
|
53
|
+
!semver_1.default.gt(version, currentVersion) ||
|
|
54
|
+
!semver_1.default.lte(version, latestVersion)) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
body: typeof body === 'string' ? body : null,
|
|
60
|
+
publishedAt: typeof publishedAt === 'string' ? publishedAt : null,
|
|
61
|
+
version,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
})
|
|
65
|
+
.sort((a, b) => semver_1.default.rcompare(a.version, b.version));
|
|
66
|
+
const hasMore = matchingReleases.length > maximumReleaseNotes;
|
|
67
|
+
const releases = await Promise.all(matchingReleases.slice(0, maximumReleaseNotes).map(async (release) => {
|
|
68
|
+
if (release.body === null || release.body.trim() === '') {
|
|
69
|
+
return {
|
|
70
|
+
publishedAt: release.publishedAt,
|
|
71
|
+
releaseNotesHtml: null,
|
|
72
|
+
version: release.version,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const markdownResponse = await fetch('https://api.github.com/markdown', {
|
|
77
|
+
body: JSON.stringify({
|
|
78
|
+
context: 'remotion-dev/remotion',
|
|
79
|
+
mode: 'gfm',
|
|
80
|
+
text: release.body,
|
|
81
|
+
}),
|
|
82
|
+
headers: {
|
|
83
|
+
accept: 'text/html',
|
|
84
|
+
'content-type': 'application/json',
|
|
85
|
+
'user-agent': 'Remotion Studio',
|
|
86
|
+
'x-github-api-version': githubApiVersion,
|
|
87
|
+
},
|
|
88
|
+
method: 'POST',
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
});
|
|
91
|
+
if (!markdownResponse.ok) {
|
|
92
|
+
return {
|
|
93
|
+
publishedAt: release.publishedAt,
|
|
94
|
+
releaseNotesHtml: null,
|
|
95
|
+
version: release.version,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const releaseNotesHtml = await markdownResponse.text();
|
|
99
|
+
return {
|
|
100
|
+
publishedAt: release.publishedAt,
|
|
101
|
+
releaseNotesHtml: releaseNotesHtml || null,
|
|
102
|
+
version: release.version,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (_a) {
|
|
106
|
+
return {
|
|
107
|
+
publishedAt: release.publishedAt,
|
|
108
|
+
releaseNotesHtml: null,
|
|
109
|
+
version: release.version,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}));
|
|
113
|
+
const response = { hasMore, releases };
|
|
114
|
+
releaseNotesCache.set(cacheKey, {
|
|
115
|
+
expiresAt: Date.now() + releaseNotesCacheDuration,
|
|
116
|
+
response,
|
|
117
|
+
});
|
|
118
|
+
return response;
|
|
119
|
+
}
|
|
120
|
+
catch (_a) {
|
|
121
|
+
return { hasMore: false, releases: [] };
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
clearTimeout(timeout);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
exports.getReleaseNotesHandler = getReleaseNotesHandler;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { type ConfigUpdate, type UpdateConfigRequest, type UpdateConfigResponse } from '@remotion/studio-shared';
|
|
1
|
+
import { type ConfigUpdate, type StudioRuntimeConfig, type UpdateConfigRequest, type UpdateConfigResponse } from '@remotion/studio-shared';
|
|
2
2
|
import type { ApiHandler } from '../api-types';
|
|
3
|
-
export declare const updateConfigFile: ({ configContents, updates, }: {
|
|
3
|
+
export declare const updateConfigFile: ({ configContents, existingElementLibraryUrls, updates, }: {
|
|
4
4
|
readonly configContents: string;
|
|
5
|
+
readonly existingElementLibraryUrls: readonly string[];
|
|
5
6
|
readonly updates: ConfigUpdate[];
|
|
6
7
|
}) => string;
|
|
7
|
-
|
|
8
|
+
type UpdateConfigHandlerParams = Parameters<ApiHandler<UpdateConfigRequest, UpdateConfigResponse>>[0] & {
|
|
9
|
+
readonly getStudioRuntimeConfig: () => StudioRuntimeConfig;
|
|
10
|
+
};
|
|
11
|
+
export declare const updateConfigHandler: ({ input, configFile, getStudioRuntimeConfig, }: UpdateConfigHandlerParams) => Promise<UpdateConfigResponse>;
|
|
12
|
+
export {};
|
|
@@ -74,7 +74,7 @@ const validateUpdates = (updates) => {
|
|
|
74
74
|
}
|
|
75
75
|
const { setter, type } = update;
|
|
76
76
|
if (typeof setter !== 'string' ||
|
|
77
|
-
!validSetterName.test(setter) ||
|
|
77
|
+
(!validSetterName.test(setter) && setter !== 'addElementLibrary') ||
|
|
78
78
|
!(setter in studio_shared_1.configMethodLifecycles)) {
|
|
79
79
|
return `Invalid config setter: ${JSON.stringify(setter)}.`;
|
|
80
80
|
}
|
|
@@ -82,6 +82,34 @@ const validateUpdates = (updates) => {
|
|
|
82
82
|
return `The config setter ${setter} was specified more than once.`;
|
|
83
83
|
}
|
|
84
84
|
setters.add(setter);
|
|
85
|
+
if (setter === 'addElementLibrary') {
|
|
86
|
+
if (type !== 'set' ||
|
|
87
|
+
!('value' in update) ||
|
|
88
|
+
typeof update.value !== 'object' ||
|
|
89
|
+
update.value === null ||
|
|
90
|
+
Array.isArray(update.value)) {
|
|
91
|
+
return 'Config.addElementLibrary() expects an object value.';
|
|
92
|
+
}
|
|
93
|
+
const elementLibrary = update.value;
|
|
94
|
+
const { displayName, url } = elementLibrary;
|
|
95
|
+
if (typeof url !== 'string') {
|
|
96
|
+
return 'Config.addElementLibrary() expects an HTTP or HTTPS URL.';
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const parsedUrl = new URL(url);
|
|
100
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
101
|
+
return 'Config.addElementLibrary() expects an HTTP or HTTPS URL.';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (_a) {
|
|
105
|
+
return 'Config.addElementLibrary() expects an HTTP or HTTPS URL.';
|
|
106
|
+
}
|
|
107
|
+
if (displayName !== undefined &&
|
|
108
|
+
(typeof displayName !== 'string' || displayName.trim() === '')) {
|
|
109
|
+
return 'Config.addElementLibrary() expects a non-empty display name.';
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
85
113
|
if (type === 'delete') {
|
|
86
114
|
continue;
|
|
87
115
|
}
|
|
@@ -93,20 +121,64 @@ const validateUpdates = (updates) => {
|
|
|
93
121
|
}
|
|
94
122
|
return null;
|
|
95
123
|
};
|
|
96
|
-
const updateConfigFile = ({ configContents, updates, }) => {
|
|
124
|
+
const updateConfigFile = ({ configContents, existingElementLibraryUrls, updates, }) => {
|
|
97
125
|
const setters = new Set(updates.map(({ setter }) => setter));
|
|
126
|
+
const elementLibraryUrls = new Set();
|
|
127
|
+
for (const url of existingElementLibraryUrls) {
|
|
128
|
+
try {
|
|
129
|
+
elementLibraryUrls.add(new URL(url).href);
|
|
130
|
+
}
|
|
131
|
+
catch (_a) {
|
|
132
|
+
// Runtime config only contains validated URLs.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
let changed = false;
|
|
98
136
|
const ast = (0, parse_ast_1.parseAst)(configContents);
|
|
99
137
|
recast.types.visit(ast.program, {
|
|
100
138
|
visitExpressionStatement(path) {
|
|
101
139
|
const { expression } = path.node;
|
|
102
|
-
if (expression.type
|
|
103
|
-
expression.callee.type
|
|
104
|
-
|
|
105
|
-
expression.callee.object.type
|
|
106
|
-
expression.callee.object.name
|
|
107
|
-
expression.callee.property.type
|
|
108
|
-
|
|
140
|
+
if (expression.type !== 'CallExpression' ||
|
|
141
|
+
expression.callee.type !== 'MemberExpression' ||
|
|
142
|
+
expression.callee.computed ||
|
|
143
|
+
expression.callee.object.type !== 'Identifier' ||
|
|
144
|
+
expression.callee.object.name !== 'Config' ||
|
|
145
|
+
expression.callee.property.type !== 'Identifier') {
|
|
146
|
+
this.traverse(path);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const setter = expression.callee.property.name;
|
|
150
|
+
if (setter === 'addElementLibrary') {
|
|
151
|
+
const argument = expression.arguments[0];
|
|
152
|
+
if ((argument === null || argument === void 0 ? void 0 : argument.type) === 'ObjectExpression') {
|
|
153
|
+
const urlProperty = argument.properties.find((property) => {
|
|
154
|
+
if (property.type !== 'ObjectProperty' || property.computed) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return ((property.key.type === 'Identifier' &&
|
|
158
|
+
property.key.name === 'url') ||
|
|
159
|
+
(property.key.type === 'StringLiteral' &&
|
|
160
|
+
property.key.value === 'url'));
|
|
161
|
+
});
|
|
162
|
+
if ((urlProperty === null || urlProperty === void 0 ? void 0 : urlProperty.type) === 'ObjectProperty' &&
|
|
163
|
+
urlProperty.value.type === 'StringLiteral') {
|
|
164
|
+
try {
|
|
165
|
+
const parsedUrl = new URL(urlProperty.value.value);
|
|
166
|
+
if (parsedUrl.protocol === 'http:' ||
|
|
167
|
+
parsedUrl.protocol === 'https:') {
|
|
168
|
+
elementLibraryUrls.add(parsedUrl.href);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (_a) {
|
|
172
|
+
// Preserve invalid existing config without treating it as a match.
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
this.traverse(path);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (setters.has(setter)) {
|
|
109
180
|
path.prune();
|
|
181
|
+
changed = true;
|
|
110
182
|
return false;
|
|
111
183
|
}
|
|
112
184
|
this.traverse(path);
|
|
@@ -117,22 +189,41 @@ const updateConfigFile = ({ configContents, updates, }) => {
|
|
|
117
189
|
if (update.type === 'delete') {
|
|
118
190
|
continue;
|
|
119
191
|
}
|
|
120
|
-
const
|
|
192
|
+
const { setter, value: initialValue } = update;
|
|
193
|
+
let value = initialValue;
|
|
194
|
+
if (setter === 'addElementLibrary') {
|
|
195
|
+
const elementLibrary = initialValue;
|
|
196
|
+
const { displayName: rawDisplayName, url } = elementLibrary;
|
|
197
|
+
const normalizedUrl = new URL(url).href;
|
|
198
|
+
if (elementLibraryUrls.has(normalizedUrl)) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const displayName = typeof rawDisplayName === 'string' ? rawDisplayName.trim() : null;
|
|
202
|
+
value =
|
|
203
|
+
displayName === null
|
|
204
|
+
? { url: normalizedUrl }
|
|
205
|
+
: { url: normalizedUrl, displayName };
|
|
206
|
+
elementLibraryUrls.add(normalizedUrl);
|
|
207
|
+
}
|
|
208
|
+
const serializedValue = JSON.stringify(value)
|
|
121
209
|
.replaceAll('\u2028', '\\u2028')
|
|
122
210
|
.replaceAll('\u2029', '\\u2029');
|
|
123
|
-
const statement = (0, parse_ast_1.parseAst)(`Config.${
|
|
124
|
-
.
|
|
211
|
+
const statement = (0, parse_ast_1.parseAst)(`Config.${setter}(${serializedValue});`).program
|
|
212
|
+
.body[0];
|
|
125
213
|
const separator = updatedConfig.endsWith('\n') ? '' : '\n';
|
|
126
214
|
updatedConfig += `${separator}${recast.prettyPrint(statement, {
|
|
127
215
|
lineTerminator: '\n',
|
|
128
216
|
quote: 'single',
|
|
129
217
|
useTabs: true,
|
|
130
218
|
}).code}\n`;
|
|
219
|
+
changed = true;
|
|
131
220
|
}
|
|
132
|
-
return updatedConfig;
|
|
221
|
+
return changed ? updatedConfig : configContents;
|
|
133
222
|
};
|
|
134
223
|
exports.updateConfigFile = updateConfigFile;
|
|
135
|
-
const updateConfigHandler = async ({ input, configFile }) => {
|
|
224
|
+
const updateConfigHandler = async ({ input, configFile, getStudioRuntimeConfig, }) => {
|
|
225
|
+
var _a;
|
|
226
|
+
var _b;
|
|
136
227
|
if (configFile === null || configFile === undefined) {
|
|
137
228
|
return {
|
|
138
229
|
success: false,
|
|
@@ -182,9 +273,17 @@ const updateConfigHandler = async ({ input, configFile }) => {
|
|
|
182
273
|
}
|
|
183
274
|
}
|
|
184
275
|
const configContents = (0, node_fs_1.readFileSync)(configFile, 'utf8');
|
|
276
|
+
const updatedConfig = (0, exports.updateConfigFile)({
|
|
277
|
+
configContents,
|
|
278
|
+
existingElementLibraryUrls: (_b = (_a = getStudioRuntimeConfig().elementLibraries) === null || _a === void 0 ? void 0 : _a.map(({ url }) => url)) !== null && _b !== void 0 ? _b : [],
|
|
279
|
+
updates: input.updates,
|
|
280
|
+
});
|
|
281
|
+
if (updatedConfig === configContents) {
|
|
282
|
+
return { success: true };
|
|
283
|
+
}
|
|
185
284
|
(0, file_watcher_1.writeFileAndNotifyFileWatchers)({
|
|
186
285
|
file: configFile,
|
|
187
|
-
content:
|
|
286
|
+
content: updatedConfig,
|
|
188
287
|
originatorClientId: input.clientId,
|
|
189
288
|
metadata: null,
|
|
190
289
|
});
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { SequenceNodePathMutation, SequenceNodePathRemapping } from '@remotion/studio-shared';
|
|
2
|
-
import type { SequenceNodePath } from 'remotion';
|
|
3
2
|
export declare const broadcastSequenceNodePathMutation: (files: {
|
|
4
3
|
absolutePath: string;
|
|
5
4
|
remappings: SequenceNodePathRemapping[];
|
|
6
|
-
restoredNodePaths: SequenceNodePath[];
|
|
7
5
|
}[]) => SequenceNodePathMutation;
|
|
@@ -19,16 +19,20 @@ const startServer = async (options) => {
|
|
|
19
19
|
var _a, _b;
|
|
20
20
|
const desiredPort = (_b = (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : (process.env.PORT ? Number(process.env.PORT) : undefined)) !== null && _b !== void 0 ? _b : undefined;
|
|
21
21
|
const portConfig = renderer_1.RenderInternals.getPortConfig(options.forceIPv4);
|
|
22
|
-
const onPortUnavailable =
|
|
23
|
-
|
|
24
|
-
: async (port) => {
|
|
22
|
+
const onPortUnavailable = async (port) => {
|
|
23
|
+
if (!options.forceNew) {
|
|
25
24
|
const detection = await (0, detect_remotion_server_1.detectRemotionServer)({
|
|
26
25
|
port,
|
|
27
26
|
cwd: options.remotionRoot,
|
|
28
27
|
hostname: portConfig.hostsToTry[0],
|
|
29
28
|
});
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (detection.type === 'match') {
|
|
30
|
+
return 'stop';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel: options.logLevel }, renderer_1.RenderInternals.chalk.gray(`Port ${port} is busy, trying another.`));
|
|
34
|
+
return 'continue';
|
|
35
|
+
};
|
|
32
36
|
let portSelection = await renderer_1.RenderInternals.getDesiredPort({
|
|
33
37
|
desiredPort,
|
|
34
38
|
from: 3000,
|
|
@@ -32,8 +32,7 @@ const getLiveStudioTarget = (requestId) => {
|
|
|
32
32
|
}
|
|
33
33
|
return target;
|
|
34
34
|
};
|
|
35
|
-
const
|
|
36
|
-
target.canInstall &&
|
|
35
|
+
const isElementRequestTarget = (target) => target !== null &&
|
|
37
36
|
target.compositionFile !== null &&
|
|
38
37
|
target.compositionId !== null &&
|
|
39
38
|
target.lastFocusedAt !== null;
|
|
@@ -68,7 +67,7 @@ const handleStudioProtocolDiscovery = ({ gitSource, liveEventsServer, remotionRo
|
|
|
68
67
|
setTimeout(() => {
|
|
69
68
|
const now = Date.now();
|
|
70
69
|
const target = getLiveStudioTarget(requestId);
|
|
71
|
-
const installTarget =
|
|
70
|
+
const installTarget = isElementRequestTarget(target) ? target : null;
|
|
72
71
|
const issuedInstallTarget = installTarget === null
|
|
73
72
|
? null
|
|
74
73
|
: (0, element_install_state_1.issueStudioProtocolTarget)({
|
|
@@ -85,6 +84,14 @@ const handleStudioProtocolDiscovery = ({ gitSource, liveEventsServer, remotionRo
|
|
|
85
84
|
purpose: 'set-license-key',
|
|
86
85
|
target,
|
|
87
86
|
});
|
|
87
|
+
const issuedElementLibraryTarget = target === null
|
|
88
|
+
? null
|
|
89
|
+
: (0, element_install_state_1.issueStudioProtocolTarget)({
|
|
90
|
+
now,
|
|
91
|
+
origin: requestOrigin,
|
|
92
|
+
purpose: 'add-element-library',
|
|
93
|
+
target,
|
|
94
|
+
});
|
|
88
95
|
response.writeHead(200, {
|
|
89
96
|
'Cache-Control': 'no-store',
|
|
90
97
|
'Content-Type': 'application/json',
|
|
@@ -120,6 +127,18 @@ const handleStudioProtocolDiscovery = ({ gitSource, liveEventsServer, remotionRo
|
|
|
120
127
|
lastFocusedAt: target.lastFocusedAt,
|
|
121
128
|
},
|
|
122
129
|
},
|
|
130
|
+
{
|
|
131
|
+
type: 'add-element-library',
|
|
132
|
+
target: issuedElementLibraryTarget === null ||
|
|
133
|
+
target === null ||
|
|
134
|
+
target.lastFocusedAt === null
|
|
135
|
+
? null
|
|
136
|
+
: {
|
|
137
|
+
id: issuedElementLibraryTarget.id,
|
|
138
|
+
expiresAt: issuedElementLibraryTarget.expiresAt,
|
|
139
|
+
lastFocusedAt: target.lastFocusedAt,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
123
142
|
],
|
|
124
143
|
}));
|
|
125
144
|
resolve();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { LiveEventsServer } from '../live-events';
|
|
3
|
+
type FocusStudioTab = (studioUrl: string) => void;
|
|
4
|
+
export declare const handleStudioProtocolElementLibrary: ({ configFile, focusStudioTab, liveEventsServer, request, response, }: {
|
|
5
|
+
readonly configFile: string | null;
|
|
6
|
+
readonly focusStudioTab: FocusStudioTab;
|
|
7
|
+
readonly liveEventsServer: LiveEventsServer;
|
|
8
|
+
readonly request: IncomingMessage;
|
|
9
|
+
readonly response: ServerResponse<IncomingMessage>;
|
|
10
|
+
}) => Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleStudioProtocolElementLibrary = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const element_install_state_1 = require("../element-install-state");
|
|
6
|
+
const parse_body_1 = require("../parse-body");
|
|
7
|
+
const origin_policy_1 = require("./origin-policy");
|
|
8
|
+
const protocol_response_1 = require("./protocol-response");
|
|
9
|
+
const studioProtocolElementLibraryRequestSchema = zod_1.z.object({
|
|
10
|
+
operation: zod_1.z.literal('add-element-library'),
|
|
11
|
+
protocol: zod_1.z.literal('remotion-studio-protocol'),
|
|
12
|
+
protocolVersion: zod_1.z.literal(1),
|
|
13
|
+
targetId: zod_1.z.string().min(1),
|
|
14
|
+
url: zod_1.z.string(),
|
|
15
|
+
displayName: zod_1.z.string().nullable(),
|
|
16
|
+
});
|
|
17
|
+
const MAX_STUDIO_PROTOCOL_ELEMENT_LIBRARY_BODY_SIZE = 16 * 1024;
|
|
18
|
+
const handleStudioProtocolElementLibrary = async ({ configFile, focusStudioTab, liveEventsServer, request, response, }) => {
|
|
19
|
+
var _a;
|
|
20
|
+
var _b;
|
|
21
|
+
(0, origin_policy_1.setStudioProtocolCorsHeaders)({ request, response });
|
|
22
|
+
const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request.headers.origin);
|
|
23
|
+
if (requestOrigin === null) {
|
|
24
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
25
|
+
code: 'unsupported-origin',
|
|
26
|
+
message: 'Origin not allowed',
|
|
27
|
+
response,
|
|
28
|
+
status: 403,
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (request.method !== 'POST') {
|
|
33
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
34
|
+
code: 'method-not-allowed',
|
|
35
|
+
message: 'Use POST to add an Element catalog.',
|
|
36
|
+
response,
|
|
37
|
+
status: 405,
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let body;
|
|
42
|
+
try {
|
|
43
|
+
body = await (0, parse_body_1.parseRequestBody)(request, {
|
|
44
|
+
maxBytes: MAX_STUDIO_PROTOCOL_ELEMENT_LIBRARY_BODY_SIZE,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error instanceof parse_body_1.RequestBodyTooLargeError) {
|
|
49
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
50
|
+
code: 'request-too-large',
|
|
51
|
+
message: 'The request body is too large.',
|
|
52
|
+
response,
|
|
53
|
+
status: 413,
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
58
|
+
code: 'invalid-request',
|
|
59
|
+
message: 'The request body is not valid JSON.',
|
|
60
|
+
response,
|
|
61
|
+
status: 400,
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const parsedRequest = studioProtocolElementLibraryRequestSchema.safeParse(body);
|
|
66
|
+
if (!parsedRequest.success) {
|
|
67
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
68
|
+
code: 'unsupported-protocol',
|
|
69
|
+
message: 'Invalid Remotion Studio Protocol request.',
|
|
70
|
+
response,
|
|
71
|
+
status: 400,
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let normalizedUrl;
|
|
76
|
+
try {
|
|
77
|
+
const parsedUrl = new URL(parsedRequest.data.url);
|
|
78
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
79
|
+
throw new Error('Unsupported protocol');
|
|
80
|
+
}
|
|
81
|
+
normalizedUrl = parsedUrl.href;
|
|
82
|
+
}
|
|
83
|
+
catch (_c) {
|
|
84
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
85
|
+
code: 'invalid-url',
|
|
86
|
+
message: 'The Element catalog URL must be an absolute HTTP or HTTPS URL.',
|
|
87
|
+
response,
|
|
88
|
+
status: 400,
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const displayName = (_b = (_a = parsedRequest.data.displayName) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : null;
|
|
93
|
+
if (displayName === '') {
|
|
94
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
95
|
+
code: 'invalid-display-name',
|
|
96
|
+
message: 'The Element catalog display name must not be empty.',
|
|
97
|
+
response,
|
|
98
|
+
status: 400,
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const target = (0, element_install_state_1.consumeStudioProtocolTarget)({
|
|
103
|
+
now: Date.now(),
|
|
104
|
+
origin: requestOrigin,
|
|
105
|
+
purpose: 'add-element-library',
|
|
106
|
+
targetId: parsedRequest.data.targetId,
|
|
107
|
+
});
|
|
108
|
+
if (target === null) {
|
|
109
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
110
|
+
code: 'target-expired',
|
|
111
|
+
message: 'The selected Studio target is no longer available.',
|
|
112
|
+
response,
|
|
113
|
+
status: 409,
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (configFile === null) {
|
|
118
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
119
|
+
code: 'no-config-file',
|
|
120
|
+
message: 'The selected Studio did not load a Remotion config file.',
|
|
121
|
+
response,
|
|
122
|
+
status: 409,
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const delivered = liveEventsServer.sendEventToClientId(target.clientId, {
|
|
127
|
+
type: 'element-library-add-request',
|
|
128
|
+
url: normalizedUrl,
|
|
129
|
+
displayName,
|
|
130
|
+
origin: requestOrigin,
|
|
131
|
+
});
|
|
132
|
+
if (!delivered) {
|
|
133
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
134
|
+
code: 'target-expired',
|
|
135
|
+
message: 'The selected Remotion Studio tab is no longer connected.',
|
|
136
|
+
response,
|
|
137
|
+
status: 409,
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
focusStudioTab(target.studioUrl);
|
|
142
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
143
|
+
response.end(JSON.stringify({
|
|
144
|
+
protocol: 'remotion-studio-protocol',
|
|
145
|
+
protocolVersion: 1,
|
|
146
|
+
status: 'awaiting-confirmation',
|
|
147
|
+
}));
|
|
148
|
+
};
|
|
149
|
+
exports.handleStudioProtocolElementLibrary = handleStudioProtocolElementLibrary;
|
|
@@ -257,18 +257,10 @@ function popUndo() {
|
|
|
257
257
|
return [
|
|
258
258
|
{
|
|
259
259
|
absolutePath: snapshot.filePath,
|
|
260
|
-
remappings: snapshot.nodePathRemappings.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
return [
|
|
265
|
-
{
|
|
266
|
-
oldNodePath: remapping.newNodePath,
|
|
267
|
-
newNodePath: remapping.oldNodePath,
|
|
268
|
-
},
|
|
269
|
-
];
|
|
270
|
-
}),
|
|
271
|
-
restoredNodePaths: snapshot.nodePathRemappings.flatMap((remapping) => remapping.newNodePath === null ? [remapping.oldNodePath] : []),
|
|
260
|
+
remappings: snapshot.nodePathRemappings.map((remapping) => ({
|
|
261
|
+
oldNodePath: remapping.newNodePath,
|
|
262
|
+
newNodePath: remapping.oldNodePath,
|
|
263
|
+
})),
|
|
272
264
|
},
|
|
273
265
|
];
|
|
274
266
|
});
|
|
@@ -339,7 +331,6 @@ function popRedo() {
|
|
|
339
331
|
{
|
|
340
332
|
absolutePath: snapshot.filePath,
|
|
341
333
|
remappings: snapshot.nodePathRemappings,
|
|
342
|
-
restoredNodePaths: [],
|
|
343
334
|
},
|
|
344
335
|
];
|
|
345
336
|
});
|
package/dist/routes.js
CHANGED
|
@@ -53,8 +53,10 @@ const handler_1 = require("./preview-server/handler");
|
|
|
53
53
|
const public_folder_1 = require("./preview-server/public-folder");
|
|
54
54
|
const app_icon_1 = require("./preview-server/routes/app-icon");
|
|
55
55
|
const open_in_editor_1 = require("./preview-server/routes/open-in-editor");
|
|
56
|
+
const update_config_1 = require("./preview-server/routes/update-config");
|
|
56
57
|
const serve_static_1 = require("./preview-server/serve-static");
|
|
57
58
|
const handle_discovery_1 = require("./preview-server/studio-protocol/handle-discovery");
|
|
59
|
+
const handle_element_library_1 = require("./preview-server/studio-protocol/handle-element-library");
|
|
58
60
|
const handle_install_1 = require("./preview-server/studio-protocol/handle-install");
|
|
59
61
|
const handle_license_key_1 = require("./preview-server/studio-protocol/handle-license-key");
|
|
60
62
|
const origin_policy_1 = require("./preview-server/studio-protocol/origin-policy");
|
|
@@ -321,7 +323,8 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
|
|
|
321
323
|
}
|
|
322
324
|
if (url.pathname === '/api/studio-protocol' ||
|
|
323
325
|
url.pathname === '/api/studio-protocol/install' ||
|
|
324
|
-
url.pathname === '/api/studio-protocol/license-key'
|
|
326
|
+
url.pathname === '/api/studio-protocol/license-key' ||
|
|
327
|
+
url.pathname === '/api/studio-protocol/element-library') {
|
|
325
328
|
if (request.method === 'OPTIONS') {
|
|
326
329
|
return (0, origin_policy_1.handleStudioProtocolOptions)({
|
|
327
330
|
request,
|
|
@@ -348,6 +351,17 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
|
|
|
348
351
|
response,
|
|
349
352
|
});
|
|
350
353
|
}
|
|
354
|
+
if (url.pathname === '/api/studio-protocol/element-library') {
|
|
355
|
+
return (0, handle_element_library_1.handleStudioProtocolElementLibrary)({
|
|
356
|
+
configFile,
|
|
357
|
+
focusStudioTab: (studioUrl) => {
|
|
358
|
+
(0, better_opn_1.focusBrowserTab)({ url: studioUrl }).catch(() => undefined);
|
|
359
|
+
},
|
|
360
|
+
liveEventsServer,
|
|
361
|
+
request,
|
|
362
|
+
response,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
351
365
|
return (0, handle_install_1.handleStudioProtocolInstall)({
|
|
352
366
|
focusStudioTab: (studioUrl) => {
|
|
353
367
|
(0, better_opn_1.focusBrowserTab)({ url: studioUrl }).catch(() => undefined);
|
|
@@ -357,6 +371,22 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
|
|
|
357
371
|
response,
|
|
358
372
|
});
|
|
359
373
|
}
|
|
374
|
+
if (url.pathname === '/api/update-config') {
|
|
375
|
+
return (0, handler_1.handleRequest)({
|
|
376
|
+
remotionRoot,
|
|
377
|
+
entryPoint,
|
|
378
|
+
handler: (params) => (0, update_config_1.updateConfigHandler)({ ...params, getStudioRuntimeConfig }),
|
|
379
|
+
request,
|
|
380
|
+
response,
|
|
381
|
+
logLevel,
|
|
382
|
+
methods,
|
|
383
|
+
binariesDirectory,
|
|
384
|
+
publicDir,
|
|
385
|
+
configFile,
|
|
386
|
+
getDefaultCodingAgent,
|
|
387
|
+
getDefaultEditor,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
360
390
|
for (const [key, value] of Object.entries(api_routes_1.allApiRoutes)) {
|
|
361
391
|
if (url.pathname === key) {
|
|
362
392
|
return (0, handler_1.handleRequest)({
|
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.
|
|
6
|
+
"version": "4.0.519",
|
|
7
7
|
"description": "Run a Remotion Studio with a server backend",
|
|
8
8
|
"main": "dist",
|
|
9
9
|
"scripts": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@remotion/studio-protocol": "4.0.
|
|
26
|
+
"@remotion/studio-protocol": "4.0.519",
|
|
27
27
|
"@babel/types": "7.24.0",
|
|
28
28
|
"@babel/parser": "7.24.1",
|
|
29
29
|
"@svgr/core": "8.1.0",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"semver": "7.5.3",
|
|
33
33
|
"zod": "4.4.3",
|
|
34
34
|
"prettier": "3.8.1",
|
|
35
|
-
"remotion": "4.0.
|
|
35
|
+
"remotion": "4.0.519",
|
|
36
36
|
"recast": "0.23.21",
|
|
37
|
-
"@remotion/bundler": "4.0.
|
|
38
|
-
"@remotion/renderer": "4.0.
|
|
39
|
-
"@remotion/studio-codemods": "4.0.
|
|
40
|
-
"@remotion/studio-shared": "4.0.
|
|
37
|
+
"@remotion/bundler": "4.0.519",
|
|
38
|
+
"@remotion/renderer": "4.0.519",
|
|
39
|
+
"@remotion/studio-codemods": "4.0.519",
|
|
40
|
+
"@remotion/studio-shared": "4.0.519",
|
|
41
41
|
"memfs": "3.4.3",
|
|
42
42
|
"open": "8.4.2"
|
|
43
43
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"ast-types": "0.16.1",
|
|
46
46
|
"react": "19.2.3",
|
|
47
47
|
"@types/semver": "7.5.3",
|
|
48
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
48
|
+
"@remotion/eslint-config-internal": "4.0.519",
|
|
49
49
|
"eslint": "9.19.0",
|
|
50
50
|
"@types/node": "20.12.14",
|
|
51
51
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|