@remotion/studio-server 4.0.500 → 4.0.502
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codemods/update-inline-caption-patches.d.ts +1 -0
- package/dist/codemods/update-inline-caption-patches.js +5 -0
- package/dist/codemods/update-sequence-props/update-sequence-props.d.ts +2 -35
- package/dist/codemods/update-sequence-props/update-sequence-props.js +7 -718
- package/dist/helpers/css-shorthand-properties.d.ts +9 -3
- package/dist/helpers/css-shorthand-properties.js +16 -2
- package/dist/helpers/parse-border-radius-shorthand.d.ts +7 -0
- package/dist/helpers/parse-border-radius-shorthand.js +53 -0
- package/dist/helpers/resolve-composition-component.js +80 -52
- package/dist/preview-server/element-install-state.d.ts +16 -0
- package/dist/preview-server/element-install-state.js +43 -1
- package/dist/preview-server/handler.js +1 -1
- package/dist/preview-server/parse-body.d.ts +6 -1
- package/dist/preview-server/parse-body.js +60 -9
- package/dist/preview-server/routes/can-update-default-props.d.ts +1 -1
- package/dist/preview-server/routes/can-update-default-props.js +5 -132
- package/dist/preview-server/routes/can-update-sequence-props.d.ts +3 -2
- package/dist/preview-server/routes/can-update-sequence-props.js +130 -12
- package/dist/preview-server/routes/find-in-file.d.ts +1 -9
- package/dist/preview-server/routes/find-in-file.js +4 -17
- package/dist/preview-server/routes/insert-element.js +40 -20
- package/dist/preview-server/routes/insert-jsx-element.js +5 -5
- package/dist/preview-server/routes/save-sequence-props.js +73 -8
- package/dist/preview-server/sequence-props-watchers.js +2 -1
- package/dist/preview-server/studio-protocol/handle-discovery.d.ts +11 -0
- package/dist/preview-server/studio-protocol/handle-discovery.js +99 -0
- package/dist/preview-server/studio-protocol/handle-install.d.ts +10 -0
- package/dist/preview-server/studio-protocol/handle-install.js +139 -0
- package/dist/preview-server/studio-protocol/origin-policy.d.ts +10 -0
- package/dist/preview-server/studio-protocol/origin-policy.js +40 -0
- package/dist/preview-server/studio-protocol/protocol-response.d.ts +7 -0
- package/dist/preview-server/studio-protocol/protocol-response.js +13 -0
- package/dist/routes.js +14 -154
- package/package.json +9 -7
|
@@ -4,6 +4,7 @@ exports.saveSequencePropsHandler = exports.shouldSuppressHmrForSequencePropEdits
|
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
5
|
const renderer_1 = require("@remotion/renderer");
|
|
6
6
|
const studio_shared_1 = require("@remotion/studio-shared");
|
|
7
|
+
const update_inline_caption_patches_1 = require("../../codemods/update-inline-caption-patches");
|
|
7
8
|
const update_keyframes_1 = require("../../codemods/update-keyframes/update-keyframes");
|
|
8
9
|
const update_sequence_props_1 = require("../../codemods/update-sequence-props/update-sequence-props");
|
|
9
10
|
const file_watcher_1 = require("../../file-watcher");
|
|
@@ -59,8 +60,8 @@ const shouldSuppressHmrForSequencePropEdits = (edits) => {
|
|
|
59
60
|
(edit.sourceEdit === null || edit.sourceEdit === undefined));
|
|
60
61
|
};
|
|
61
62
|
exports.shouldSuppressHmrForSequencePropEdits = shouldSuppressHmrForSequencePropEdits;
|
|
62
|
-
const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyframes, clientId, undoLabel, redoLabel, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
63
|
-
var _a, _b, _c, _d;
|
|
63
|
+
const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyframes, captionPatches = [], clientId, undoLabel, redoLabel, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
64
|
+
var _a, _b, _c, _d, _e;
|
|
64
65
|
const keyframesToAdd = addedKeyframes === null ? [] : addedKeyframes;
|
|
65
66
|
const keyframesToMove = movedKeyframes === null
|
|
66
67
|
? { sequenceKeyframes: [], effectKeyframes: [] }
|
|
@@ -68,11 +69,12 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
68
69
|
const totalKeyframeMoves = keyframesToMove.sequenceKeyframes.length +
|
|
69
70
|
keyframesToMove.effectKeyframes.length;
|
|
70
71
|
if (edits.length === 0 &&
|
|
72
|
+
captionPatches.length === 0 &&
|
|
71
73
|
keyframesToAdd.length === 0 &&
|
|
72
74
|
totalKeyframeMoves === 0) {
|
|
73
75
|
throw new Error('No sequence prop edits to save');
|
|
74
76
|
}
|
|
75
|
-
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[save-sequence-props] Received request with ${edits.length} edit(s), ${keyframesToAdd.length} added keyframe(s), and ${totalKeyframeMoves} moved keyframe(s)`);
|
|
77
|
+
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[save-sequence-props] Received request with ${edits.length} edit(s), ${captionPatches.length} caption patch request(s), ${keyframesToAdd.length} added keyframe(s), and ${totalKeyframeMoves} moved keyframe(s)`);
|
|
76
78
|
const editGroups = new Map();
|
|
77
79
|
for (const [index, edit] of edits.entries()) {
|
|
78
80
|
const parsedValue = parseSequencePropEditValue(edit.value);
|
|
@@ -85,6 +87,7 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
85
87
|
const group = (_a = editGroups.get(absolutePath)) !== null && _a !== void 0 ? _a : {
|
|
86
88
|
fileRelativeToRoot,
|
|
87
89
|
edits: [],
|
|
90
|
+
captionPatches: [],
|
|
88
91
|
addedKeyframes: [],
|
|
89
92
|
movedSequenceKeyframes: [],
|
|
90
93
|
effectKeyframes: [],
|
|
@@ -105,15 +108,33 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
105
108
|
});
|
|
106
109
|
editGroups.set(absolutePath, group);
|
|
107
110
|
}
|
|
111
|
+
for (const captionPatch of captionPatches) {
|
|
112
|
+
const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
|
|
113
|
+
remotionRoot,
|
|
114
|
+
fileName: captionPatch.fileName,
|
|
115
|
+
action: 'modify',
|
|
116
|
+
});
|
|
117
|
+
const group = (_b = editGroups.get(absolutePath)) !== null && _b !== void 0 ? _b : {
|
|
118
|
+
fileRelativeToRoot,
|
|
119
|
+
edits: [],
|
|
120
|
+
captionPatches: [],
|
|
121
|
+
addedKeyframes: [],
|
|
122
|
+
movedSequenceKeyframes: [],
|
|
123
|
+
effectKeyframes: [],
|
|
124
|
+
};
|
|
125
|
+
group.captionPatches.push(captionPatch);
|
|
126
|
+
editGroups.set(absolutePath, group);
|
|
127
|
+
}
|
|
108
128
|
for (const keyframe of keyframesToAdd) {
|
|
109
129
|
const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
|
|
110
130
|
remotionRoot,
|
|
111
131
|
fileName: keyframe.fileName,
|
|
112
132
|
action: 'modify',
|
|
113
133
|
});
|
|
114
|
-
const group = (
|
|
134
|
+
const group = (_c = editGroups.get(absolutePath)) !== null && _c !== void 0 ? _c : {
|
|
115
135
|
fileRelativeToRoot,
|
|
116
136
|
edits: [],
|
|
137
|
+
captionPatches: [],
|
|
117
138
|
addedKeyframes: [],
|
|
118
139
|
movedSequenceKeyframes: [],
|
|
119
140
|
effectKeyframes: [],
|
|
@@ -127,9 +148,10 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
127
148
|
fileName: keyframe.fileName,
|
|
128
149
|
action: 'modify',
|
|
129
150
|
});
|
|
130
|
-
const group = (
|
|
151
|
+
const group = (_d = editGroups.get(absolutePath)) !== null && _d !== void 0 ? _d : {
|
|
131
152
|
fileRelativeToRoot,
|
|
132
153
|
edits: [],
|
|
154
|
+
captionPatches: [],
|
|
133
155
|
addedKeyframes: [],
|
|
134
156
|
movedSequenceKeyframes: [],
|
|
135
157
|
effectKeyframes: [],
|
|
@@ -143,9 +165,10 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
143
165
|
fileName: keyframe.fileName,
|
|
144
166
|
action: 'modify',
|
|
145
167
|
});
|
|
146
|
-
const group = (
|
|
168
|
+
const group = (_e = editGroups.get(absolutePath)) !== null && _e !== void 0 ? _e : {
|
|
147
169
|
fileRelativeToRoot,
|
|
148
170
|
edits: [],
|
|
171
|
+
captionPatches: [],
|
|
149
172
|
addedKeyframes: [],
|
|
150
173
|
movedSequenceKeyframes: [],
|
|
151
174
|
effectKeyframes: [],
|
|
@@ -157,6 +180,7 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
157
180
|
const outputByPath = new Map();
|
|
158
181
|
const resultByIndex = new Map();
|
|
159
182
|
const sequenceKeyframeLogs = [];
|
|
183
|
+
const captionPatchLogs = [];
|
|
160
184
|
const effectKeyframeLogs = [];
|
|
161
185
|
for (const [absolutePath, group] of editGroups) {
|
|
162
186
|
const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
@@ -183,6 +207,32 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
183
207
|
});
|
|
184
208
|
}
|
|
185
209
|
}
|
|
210
|
+
for (const captionPatchRequest of group.captionPatches) {
|
|
211
|
+
const result = (0, update_inline_caption_patches_1.updateInlineCaptionPatches)({
|
|
212
|
+
input: output,
|
|
213
|
+
nodePath: captionPatchRequest.nodePath.nodePath,
|
|
214
|
+
patches: captionPatchRequest.patches,
|
|
215
|
+
});
|
|
216
|
+
output = result.output;
|
|
217
|
+
firstLogLine = Math.min(firstLogLine, result.logLine);
|
|
218
|
+
for (const [index, patch] of captionPatchRequest.patches.entries()) {
|
|
219
|
+
const changedFields = result.changedFields[index];
|
|
220
|
+
if (!changedFields) {
|
|
221
|
+
throw new Error('Could not determine changed caption fields');
|
|
222
|
+
}
|
|
223
|
+
captionPatchLogs.push({
|
|
224
|
+
fileRelativeToRoot: group.fileRelativeToRoot,
|
|
225
|
+
line: result.logLine,
|
|
226
|
+
index: patch.index,
|
|
227
|
+
oldValueString: changedFields
|
|
228
|
+
.map((field) => `${field}: ${patch.before[field]}`)
|
|
229
|
+
.join(', '),
|
|
230
|
+
newValueString: changedFields
|
|
231
|
+
.map((field) => `${field}: ${patch.changes[field]}`)
|
|
232
|
+
.join(', '),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
186
236
|
for (const keyframeGroup of groupBy(group.addedKeyframes, (keyframe) => JSON.stringify(keyframe.nodePath.nodePath))) {
|
|
187
237
|
const [firstSequenceKeyframe] = keyframeGroup;
|
|
188
238
|
if (!firstSequenceKeyframe) {
|
|
@@ -310,7 +360,8 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
310
360
|
}
|
|
311
361
|
const undoMessage = `↩️ ${undoLabel}`;
|
|
312
362
|
const redoMessage = `↪️ ${redoLabel}`;
|
|
313
|
-
const suppressHmr =
|
|
363
|
+
const suppressHmr = captionPatches.length === 0 &&
|
|
364
|
+
(0, exports.shouldSuppressHmrForSequencePropEdits)(edits);
|
|
314
365
|
(0, undo_stack_1.pushTransactionToUndoStack)({
|
|
315
366
|
snapshots,
|
|
316
367
|
logLevel,
|
|
@@ -346,6 +397,20 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
346
397
|
});
|
|
347
398
|
}
|
|
348
399
|
}
|
|
400
|
+
for (const log of captionPatchLogs) {
|
|
401
|
+
(0, log_update_1.logUpdate)({
|
|
402
|
+
fileRelativeToRoot: log.fileRelativeToRoot,
|
|
403
|
+
line: log.line,
|
|
404
|
+
key: `captions[${log.index}]`,
|
|
405
|
+
oldValueString: log.oldValueString,
|
|
406
|
+
newValueString: log.newValueString,
|
|
407
|
+
defaultValueString: null,
|
|
408
|
+
formatted: true,
|
|
409
|
+
logLevel,
|
|
410
|
+
removedProps: [],
|
|
411
|
+
addedProps: [],
|
|
412
|
+
});
|
|
413
|
+
}
|
|
349
414
|
for (const log of sequenceKeyframeLogs) {
|
|
350
415
|
(0, log_update_1.logUpdate)({
|
|
351
416
|
fileRelativeToRoot: log.fileRelativeToRoot,
|
|
@@ -377,7 +442,7 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
|
|
|
377
442
|
}
|
|
378
443
|
(0, undo_stack_1.printUndoHint)(logLevel);
|
|
379
444
|
const statusTargets = [
|
|
380
|
-
...new Map([...edits, ...keyframesToAdd].map((target) => [
|
|
445
|
+
...new Map([...edits, ...captionPatches, ...keyframesToAdd].map((target) => [
|
|
381
446
|
JSON.stringify(target.nodePath),
|
|
382
447
|
target,
|
|
383
448
|
])).values(),
|
|
@@ -85,9 +85,10 @@ const getSequencePropsStatus = ({ fileName, line, column, preferredNodePath, com
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
const status = (0, can_update_sequence_props_1.
|
|
88
|
+
const status = (0, can_update_sequence_props_1.computeSequencePropsStatusFromFilenameByLocation)({
|
|
89
89
|
fileName,
|
|
90
90
|
line,
|
|
91
|
+
column,
|
|
91
92
|
componentIdentity,
|
|
92
93
|
keys,
|
|
93
94
|
assetKeys,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { GitSource } from '@remotion/studio-shared';
|
|
3
|
+
import type { LiveEventsServer } from '../live-events';
|
|
4
|
+
export declare const ELEMENT_INSTALL_TARGET_RESPONSE_WAIT = 250;
|
|
5
|
+
export declare const handleStudioProtocolDiscovery: ({ gitSource, liveEventsServer, remotionRoot, request, response, }: {
|
|
6
|
+
readonly gitSource: GitSource | null;
|
|
7
|
+
readonly liveEventsServer: LiveEventsServer;
|
|
8
|
+
readonly remotionRoot: string;
|
|
9
|
+
readonly request: IncomingMessage;
|
|
10
|
+
readonly response: ServerResponse<IncomingMessage>;
|
|
11
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,99 @@
|
|
|
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.handleStudioProtocolDiscovery = exports.ELEMENT_INSTALL_TARGET_RESPONSE_WAIT = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const studio_shared_1 = require("@remotion/studio-shared");
|
|
9
|
+
const version_1 = require("remotion/version");
|
|
10
|
+
const element_install_state_1 = require("../element-install-state");
|
|
11
|
+
const origin_policy_1 = require("./origin-policy");
|
|
12
|
+
const protocol_response_1 = require("./protocol-response");
|
|
13
|
+
const ELEMENT_INSTALL_FOCUS_MAX_AGE = 5 * 60 * 1000;
|
|
14
|
+
exports.ELEMENT_INSTALL_TARGET_RESPONSE_WAIT = 250;
|
|
15
|
+
const requestInstallTarget = ({ liveEventsServer, }) => {
|
|
16
|
+
const requestId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
17
|
+
liveEventsServer.sendEventToClient({
|
|
18
|
+
type: 'request-element-install-target',
|
|
19
|
+
requestId,
|
|
20
|
+
});
|
|
21
|
+
return requestId;
|
|
22
|
+
};
|
|
23
|
+
const getLiveInstallableTarget = (requestId) => {
|
|
24
|
+
const target = (0, element_install_state_1.getElementInstallTarget)(requestId);
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
if (target === null ||
|
|
27
|
+
now - target.updatedAt >= element_install_state_1.ELEMENT_INSTALL_TARGET_MAX_AGE ||
|
|
28
|
+
target.lastFocusedAt === null ||
|
|
29
|
+
now - target.lastFocusedAt >= ELEMENT_INSTALL_FOCUS_MAX_AGE ||
|
|
30
|
+
!target.canInstall ||
|
|
31
|
+
target.readOnly ||
|
|
32
|
+
target.compositionFile === null ||
|
|
33
|
+
target.compositionId === null) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return target;
|
|
37
|
+
};
|
|
38
|
+
const getProject = ({ gitSource, remotionRoot, }) => (0, studio_shared_1.getProjectName)({
|
|
39
|
+
basename: node_path_1.default.basename,
|
|
40
|
+
gitSource,
|
|
41
|
+
resolvedRemotionRoot: remotionRoot,
|
|
42
|
+
});
|
|
43
|
+
const handleStudioProtocolDiscovery = ({ gitSource, liveEventsServer, remotionRoot, request, response, }) => {
|
|
44
|
+
(0, origin_policy_1.setStudioProtocolCorsHeaders)({ request, response });
|
|
45
|
+
if (!(0, origin_policy_1.isAllowedStudioProtocolOrigin)(request.headers.origin)) {
|
|
46
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
47
|
+
code: 'unsupported-origin',
|
|
48
|
+
message: 'Origin not allowed',
|
|
49
|
+
response,
|
|
50
|
+
status: 403,
|
|
51
|
+
});
|
|
52
|
+
return Promise.resolve();
|
|
53
|
+
}
|
|
54
|
+
if (request.method !== 'GET') {
|
|
55
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
56
|
+
code: 'method-not-allowed',
|
|
57
|
+
message: 'Use GET for Studio Protocol discovery.',
|
|
58
|
+
response,
|
|
59
|
+
status: 405,
|
|
60
|
+
});
|
|
61
|
+
return Promise.resolve();
|
|
62
|
+
}
|
|
63
|
+
const requestId = requestInstallTarget({ liveEventsServer });
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
const target = getLiveInstallableTarget(requestId);
|
|
68
|
+
const issued = target === null ? null : (0, element_install_state_1.issueStudioProtocolTarget)({ now, target });
|
|
69
|
+
response.writeHead(200, {
|
|
70
|
+
'Cache-Control': 'no-store',
|
|
71
|
+
'Content-Type': 'application/json',
|
|
72
|
+
});
|
|
73
|
+
response.end(JSON.stringify({
|
|
74
|
+
protocol: 'remotion-studio-protocol',
|
|
75
|
+
protocolVersion: 1,
|
|
76
|
+
studioVersion: version_1.VERSION,
|
|
77
|
+
capabilities: {
|
|
78
|
+
install: [
|
|
79
|
+
{
|
|
80
|
+
payloadType: 'remotion-element',
|
|
81
|
+
payloadVersions: [1],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
projectName: getProject({ gitSource, remotionRoot }),
|
|
86
|
+
installTarget: issued === null || target === null
|
|
87
|
+
? null
|
|
88
|
+
: {
|
|
89
|
+
id: issued.id,
|
|
90
|
+
expiresAt: issued.expiresAt,
|
|
91
|
+
compositionId: target.compositionId,
|
|
92
|
+
lastFocusedAt: target.lastFocusedAt,
|
|
93
|
+
},
|
|
94
|
+
}));
|
|
95
|
+
resolve();
|
|
96
|
+
}, exports.ELEMENT_INSTALL_TARGET_RESPONSE_WAIT);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
exports.handleStudioProtocolDiscovery = handleStudioProtocolDiscovery;
|
|
@@ -0,0 +1,10 @@
|
|
|
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 handleStudioProtocolInstall: ({ focusStudioTab, liveEventsServer, request, response, }: {
|
|
5
|
+
readonly focusStudioTab: FocusStudioTab;
|
|
6
|
+
readonly liveEventsServer: LiveEventsServer;
|
|
7
|
+
readonly request: IncomingMessage;
|
|
8
|
+
readonly response: ServerResponse<IncomingMessage>;
|
|
9
|
+
}) => Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleStudioProtocolInstall = void 0;
|
|
4
|
+
const studio_protocol_1 = require("@remotion/studio-protocol");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const element_install_state_1 = require("../element-install-state");
|
|
7
|
+
const parse_body_1 = require("../parse-body");
|
|
8
|
+
const origin_policy_1 = require("./origin-policy");
|
|
9
|
+
const protocol_response_1 = require("./protocol-response");
|
|
10
|
+
const studioProtocolInstallRequestSchema = zod_1.z.object({
|
|
11
|
+
protocol: zod_1.z.literal('remotion-studio-protocol'),
|
|
12
|
+
protocolVersion: zod_1.z.literal(1),
|
|
13
|
+
targetId: zod_1.z.string(),
|
|
14
|
+
payload: zod_1.z.unknown(),
|
|
15
|
+
});
|
|
16
|
+
// A valid payload may contain 250,000 JSON characters. Allow room for its
|
|
17
|
+
// UTF-8 representation and the protocol envelope while keeping memory bounded.
|
|
18
|
+
const MAX_STUDIO_PROTOCOL_INSTALL_BODY_SIZE = 1000000;
|
|
19
|
+
const deliverElementInstall = ({ element, focusStudioTab, liveEventsServer, target, }) => {
|
|
20
|
+
if (target.compositionFile === null || target.compositionId === null) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const installRequest = {
|
|
24
|
+
id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
25
|
+
clientId: target.clientId,
|
|
26
|
+
createdAt: Date.now(),
|
|
27
|
+
compositionFile: target.compositionFile,
|
|
28
|
+
compositionId: target.compositionId,
|
|
29
|
+
element,
|
|
30
|
+
position: null,
|
|
31
|
+
};
|
|
32
|
+
const delivered = liveEventsServer.sendEventToClientId(target.clientId, {
|
|
33
|
+
type: 'element-install-request',
|
|
34
|
+
request: installRequest,
|
|
35
|
+
});
|
|
36
|
+
if (delivered) {
|
|
37
|
+
focusStudioTab(target.studioUrl);
|
|
38
|
+
}
|
|
39
|
+
return delivered;
|
|
40
|
+
};
|
|
41
|
+
const handleStudioProtocolInstall = async ({ focusStudioTab, liveEventsServer, request, response, }) => {
|
|
42
|
+
(0, origin_policy_1.setStudioProtocolCorsHeaders)({ request, response });
|
|
43
|
+
if (!(0, origin_policy_1.isAllowedStudioProtocolOrigin)(request.headers.origin)) {
|
|
44
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
45
|
+
code: 'unsupported-origin',
|
|
46
|
+
message: 'Origin not allowed',
|
|
47
|
+
response,
|
|
48
|
+
status: 403,
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (request.method !== 'POST') {
|
|
53
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
54
|
+
code: 'method-not-allowed',
|
|
55
|
+
message: 'Use POST to request an Element installation.',
|
|
56
|
+
response,
|
|
57
|
+
status: 405,
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let body;
|
|
62
|
+
try {
|
|
63
|
+
body = await (0, parse_body_1.parseRequestBody)(request, {
|
|
64
|
+
maxBytes: MAX_STUDIO_PROTOCOL_INSTALL_BODY_SIZE,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error instanceof parse_body_1.RequestBodyTooLargeError) {
|
|
69
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
70
|
+
code: 'request-too-large',
|
|
71
|
+
message: 'The request body is too large.',
|
|
72
|
+
response,
|
|
73
|
+
status: 413,
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
78
|
+
code: 'invalid-request',
|
|
79
|
+
message: 'The request body is not valid JSON.',
|
|
80
|
+
response,
|
|
81
|
+
status: 400,
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const parsedRequest = studioProtocolInstallRequestSchema.safeParse(body);
|
|
86
|
+
if (!parsedRequest.success) {
|
|
87
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
88
|
+
code: 'unsupported-protocol',
|
|
89
|
+
message: 'Invalid Remotion Studio Protocol request.',
|
|
90
|
+
response,
|
|
91
|
+
status: 400,
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const payload = studio_protocol_1.StudioProtocolInternals.parseStudioElementPayload(parsedRequest.data.payload);
|
|
96
|
+
if (payload === null) {
|
|
97
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
98
|
+
code: 'invalid-payload',
|
|
99
|
+
message: 'Invalid Element payload.',
|
|
100
|
+
response,
|
|
101
|
+
status: 400,
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const target = (0, element_install_state_1.consumeStudioProtocolTarget)({
|
|
106
|
+
now: Date.now(),
|
|
107
|
+
targetId: parsedRequest.data.targetId,
|
|
108
|
+
});
|
|
109
|
+
if (target === null) {
|
|
110
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
111
|
+
code: 'target-expired',
|
|
112
|
+
message: 'The selected Studio target is no longer available.',
|
|
113
|
+
response,
|
|
114
|
+
status: 409,
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!deliverElementInstall({
|
|
119
|
+
element: payload.element,
|
|
120
|
+
focusStudioTab,
|
|
121
|
+
liveEventsServer,
|
|
122
|
+
target,
|
|
123
|
+
})) {
|
|
124
|
+
(0, protocol_response_1.writeStudioProtocolError)({
|
|
125
|
+
code: 'target-expired',
|
|
126
|
+
message: 'The selected Remotion Studio tab is no longer connected.',
|
|
127
|
+
response,
|
|
128
|
+
status: 409,
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
133
|
+
response.end(JSON.stringify({
|
|
134
|
+
protocol: 'remotion-studio-protocol',
|
|
135
|
+
protocolVersion: 1,
|
|
136
|
+
status: 'awaiting-confirmation',
|
|
137
|
+
}));
|
|
138
|
+
};
|
|
139
|
+
exports.handleStudioProtocolInstall = handleStudioProtocolInstall;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
export declare const isAllowedStudioProtocolOrigin: (origin: string | undefined) => boolean;
|
|
3
|
+
export declare const setStudioProtocolCorsHeaders: ({ request, response, }: {
|
|
4
|
+
readonly request: IncomingMessage;
|
|
5
|
+
readonly response: ServerResponse<IncomingMessage>;
|
|
6
|
+
}) => void;
|
|
7
|
+
export declare const handleStudioProtocolOptions: ({ request, response, }: {
|
|
8
|
+
readonly request: IncomingMessage;
|
|
9
|
+
readonly response: ServerResponse<IncomingMessage>;
|
|
10
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleStudioProtocolOptions = exports.setStudioProtocolCorsHeaders = exports.isAllowedStudioProtocolOrigin = void 0;
|
|
4
|
+
const isAllowedStudioProtocolOrigin = (origin) => {
|
|
5
|
+
if (!origin) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const url = new URL(origin);
|
|
10
|
+
return ((url.protocol === 'https:' &&
|
|
11
|
+
(url.hostname === 'remotion.dev' ||
|
|
12
|
+
url.hostname === 'www.remotion.dev')) ||
|
|
13
|
+
(url.protocol === 'http:' &&
|
|
14
|
+
(url.hostname === 'localhost' || url.hostname === '127.0.0.1')));
|
|
15
|
+
}
|
|
16
|
+
catch (_a) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
exports.isAllowedStudioProtocolOrigin = isAllowedStudioProtocolOrigin;
|
|
21
|
+
const setStudioProtocolCorsHeaders = ({ request, response, }) => {
|
|
22
|
+
const { origin } = request.headers;
|
|
23
|
+
if (typeof origin !== 'string' || !(0, exports.isAllowedStudioProtocolOrigin)(origin)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
response.setHeader('Access-Control-Allow-Origin', origin);
|
|
27
|
+
response.setHeader('Vary', 'Origin');
|
|
28
|
+
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
29
|
+
response.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
30
|
+
response.setHeader('Access-Control-Max-Age', '600');
|
|
31
|
+
response.setHeader('Access-Control-Allow-Private-Network', 'true');
|
|
32
|
+
};
|
|
33
|
+
exports.setStudioProtocolCorsHeaders = setStudioProtocolCorsHeaders;
|
|
34
|
+
const handleStudioProtocolOptions = ({ request, response, }) => {
|
|
35
|
+
(0, exports.setStudioProtocolCorsHeaders)({ request, response });
|
|
36
|
+
response.writeHead((0, exports.isAllowedStudioProtocolOrigin)(request.headers.origin) ? 204 : 403);
|
|
37
|
+
response.end();
|
|
38
|
+
return Promise.resolve();
|
|
39
|
+
};
|
|
40
|
+
exports.handleStudioProtocolOptions = handleStudioProtocolOptions;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ServerResponse } from 'node:http';
|
|
2
|
+
export declare const writeStudioProtocolError: ({ code, message, response, status, }: {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly response: ServerResponse<import("http").IncomingMessage>;
|
|
6
|
+
readonly status: number;
|
|
7
|
+
}) => void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeStudioProtocolError = void 0;
|
|
4
|
+
const writeStudioProtocolError = ({ code, message, response, status, }) => {
|
|
5
|
+
response.writeHead(status, { 'Content-Type': 'application/json' });
|
|
6
|
+
response.end(JSON.stringify({
|
|
7
|
+
protocol: 'remotion-studio-protocol',
|
|
8
|
+
protocolVersion: 1,
|
|
9
|
+
status: 'error',
|
|
10
|
+
error: { code, message },
|
|
11
|
+
}));
|
|
12
|
+
};
|
|
13
|
+
exports.writeStudioProtocolError = writeStudioProtocolError;
|