@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.
Files changed (34) hide show
  1. package/dist/codemods/update-inline-caption-patches.d.ts +1 -0
  2. package/dist/codemods/update-inline-caption-patches.js +5 -0
  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 -718
  5. package/dist/helpers/css-shorthand-properties.d.ts +9 -3
  6. package/dist/helpers/css-shorthand-properties.js +16 -2
  7. package/dist/helpers/parse-border-radius-shorthand.d.ts +7 -0
  8. package/dist/helpers/parse-border-radius-shorthand.js +53 -0
  9. package/dist/helpers/resolve-composition-component.js +80 -52
  10. package/dist/preview-server/element-install-state.d.ts +16 -0
  11. package/dist/preview-server/element-install-state.js +43 -1
  12. package/dist/preview-server/handler.js +1 -1
  13. package/dist/preview-server/parse-body.d.ts +6 -1
  14. package/dist/preview-server/parse-body.js +60 -9
  15. package/dist/preview-server/routes/can-update-default-props.d.ts +1 -1
  16. package/dist/preview-server/routes/can-update-default-props.js +5 -132
  17. package/dist/preview-server/routes/can-update-sequence-props.d.ts +3 -2
  18. package/dist/preview-server/routes/can-update-sequence-props.js +130 -12
  19. package/dist/preview-server/routes/find-in-file.d.ts +1 -9
  20. package/dist/preview-server/routes/find-in-file.js +4 -17
  21. package/dist/preview-server/routes/insert-element.js +40 -20
  22. package/dist/preview-server/routes/insert-jsx-element.js +5 -5
  23. package/dist/preview-server/routes/save-sequence-props.js +73 -8
  24. package/dist/preview-server/sequence-props-watchers.js +2 -1
  25. package/dist/preview-server/studio-protocol/handle-discovery.d.ts +11 -0
  26. package/dist/preview-server/studio-protocol/handle-discovery.js +99 -0
  27. package/dist/preview-server/studio-protocol/handle-install.d.ts +10 -0
  28. package/dist/preview-server/studio-protocol/handle-install.js +139 -0
  29. package/dist/preview-server/studio-protocol/origin-policy.d.ts +10 -0
  30. package/dist/preview-server/studio-protocol/origin-policy.js +40 -0
  31. package/dist/preview-server/studio-protocol/protocol-response.d.ts +7 -0
  32. package/dist/preview-server/studio-protocol/protocol-response.js +13 -0
  33. package/dist/routes.js +14 -154
  34. 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 = (_b = editGroups.get(absolutePath)) !== null && _b !== void 0 ? _b : {
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 = (_c = editGroups.get(absolutePath)) !== null && _c !== void 0 ? _c : {
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 = (_d = editGroups.get(absolutePath)) !== null && _d !== void 0 ? _d : {
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 = (0, exports.shouldSuppressHmrForSequencePropEdits)(edits);
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.computeSequencePropsStatusFromFilenameByLine)({
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;