|
1 | 1 | import type { JsonValue } from "@renderify/ir"; |
2 | 2 | import type { RuntimeSourceSandboxMode } from "./runtime-manager.types"; |
| 3 | +import { buildIframeSandboxSrcdoc } from "./sandbox-iframe-source"; |
| 4 | +import { buildShadowRealmBridgeSource } from "./sandbox-shadowrealm-bridge-source"; |
| 5 | +import { WORKER_SANDBOX_SOURCE } from "./sandbox-worker-source"; |
3 | 6 |
|
4 | 7 | export interface RuntimeSandboxResult { |
5 | 8 | mode: RuntimeSourceSandboxMode; |
@@ -96,63 +99,8 @@ async function executeSourceInWorkerSandbox( |
96 | 99 | throw new Error("Worker sandbox is unavailable in this runtime"); |
97 | 100 | } |
98 | 101 |
|
99 | | - const workerSource = [ |
100 | | - "const CHANNEL = 'runtime-source';", |
101 | | - "self.onmessage = async (event) => {", |
102 | | - " const request = event.data;", |
103 | | - " if (!request || request.renderifySandbox !== CHANNEL) {", |
104 | | - " return;", |
105 | | - " }", |
106 | | - " const safeSend = (payload) => {", |
107 | | - " try {", |
108 | | - " self.postMessage({ renderifySandbox: CHANNEL, id: request.id, ...payload });", |
109 | | - " return true;", |
110 | | - " } catch (postError) {", |
111 | | - " try {", |
112 | | - " const postMessageError = postError && typeof postError === 'object' && 'message' in postError", |
113 | | - " ? String(postError.message)", |
114 | | - " : String(postError);", |
115 | | - " self.postMessage({", |
116 | | - " renderifySandbox: CHANNEL,", |
117 | | - " id: request.id,", |
118 | | - " ok: false,", |
119 | | - " error: `Sandbox response is not serializable: ${postMessageError}`,", |
120 | | - " });", |
121 | | - " } catch {", |
122 | | - " // Ignore terminal postMessage failures.", |
123 | | - " }", |
124 | | - " return false;", |
125 | | - " }", |
126 | | - " };", |
127 | | - " try {", |
128 | | - " const moduleUrl = URL.createObjectURL(new Blob([String(request.code ?? '')], { type: 'text/javascript' }));", |
129 | | - " try {", |
130 | | - " const namespace = await import(moduleUrl);", |
131 | | - " const exportName = typeof request.exportName === 'string' && request.exportName.trim().length > 0", |
132 | | - " ? request.exportName.trim()", |
133 | | - " : 'default';", |
134 | | - " const selected = namespace[exportName];", |
135 | | - " if (selected === undefined) {", |
136 | | - ' throw new Error(`Runtime source export "${exportName}" is missing`);', |
137 | | - " }", |
138 | | - " const output = typeof selected === 'function'", |
139 | | - " ? await selected(request.runtimeInput ?? {})", |
140 | | - " : selected;", |
141 | | - " safeSend({ ok: true, output });", |
142 | | - " } finally {", |
143 | | - " URL.revokeObjectURL(moduleUrl);", |
144 | | - " }", |
145 | | - " } catch (error) {", |
146 | | - " const message = error && typeof error === 'object' && 'message' in error", |
147 | | - " ? String(error.message)", |
148 | | - " : String(error);", |
149 | | - " safeSend({ ok: false, error: message });", |
150 | | - " }", |
151 | | - "};", |
152 | | - ].join("\n"); |
153 | | - |
154 | 102 | const workerUrl = URL.createObjectURL( |
155 | | - new Blob([workerSource], { |
| 103 | + new Blob([WORKER_SANDBOX_SOURCE], { |
156 | 104 | type: "text/javascript", |
157 | 105 | }), |
158 | 106 | ); |
@@ -262,63 +210,7 @@ async function executeSourceInIframeSandbox( |
262 | 210 |
|
263 | 211 | const channel = `renderify-runtime-source-${options.request.id}`; |
264 | 212 | const channelLiteral = JSON.stringify(channel); |
265 | | - |
266 | | - iframe.srcdoc = [ |
267 | | - "<!doctype html><html><body><script>", |
268 | | - `const CHANNEL = ${channelLiteral};`, |
269 | | - "window.addEventListener('message', async (event) => {", |
270 | | - " const data = event.data;", |
271 | | - " if (!data || data.channel !== CHANNEL) {", |
272 | | - " return;", |
273 | | - " }", |
274 | | - " const request = data.request || {};", |
275 | | - " const safeSend = (payload) => {", |
276 | | - " try {", |
277 | | - " parent.postMessage({ channel: CHANNEL, ...payload }, '*');", |
278 | | - " return true;", |
279 | | - " } catch (postError) {", |
280 | | - " try {", |
281 | | - " const postMessageError = postError && typeof postError === 'object' && 'message' in postError", |
282 | | - " ? String(postError.message)", |
283 | | - " : String(postError);", |
284 | | - " parent.postMessage({", |
285 | | - " channel: CHANNEL,", |
286 | | - " ok: false,", |
287 | | - " error: `Sandbox response is not serializable: ${postMessageError}`,", |
288 | | - " }, '*');", |
289 | | - " } catch {", |
290 | | - " // Ignore terminal postMessage failures.", |
291 | | - " }", |
292 | | - " return false;", |
293 | | - " }", |
294 | | - " };", |
295 | | - " try {", |
296 | | - " const moduleUrl = URL.createObjectURL(new Blob([String(request.code ?? '')], { type: 'text/javascript' }));", |
297 | | - " try {", |
298 | | - " const namespace = await import(moduleUrl);", |
299 | | - " const exportName = typeof request.exportName === 'string' && request.exportName.trim().length > 0", |
300 | | - " ? request.exportName.trim()", |
301 | | - " : 'default';", |
302 | | - " const selected = namespace[exportName];", |
303 | | - " if (selected === undefined) {", |
304 | | - ' throw new Error(`Runtime source export "${exportName}" is missing`);', |
305 | | - " }", |
306 | | - " const output = typeof selected === 'function'", |
307 | | - " ? await selected(request.runtimeInput ?? {})", |
308 | | - " : selected;", |
309 | | - " safeSend({ ok: true, output });", |
310 | | - " } finally {", |
311 | | - " URL.revokeObjectURL(moduleUrl);", |
312 | | - " }", |
313 | | - " } catch (error) {", |
314 | | - " const message = error && typeof error === 'object' && 'message' in error", |
315 | | - " ? String(error.message)", |
316 | | - " : String(error);", |
317 | | - " safeSend({ ok: false, error: message });", |
318 | | - " }", |
319 | | - "});", |
320 | | - "</script></body></html>", |
321 | | - ].join(""); |
| 213 | + iframe.srcdoc = buildIframeSandboxSrcdoc(channelLiteral); |
322 | 214 |
|
323 | 215 | document.body.appendChild(iframe); |
324 | 216 |
|
@@ -421,36 +313,7 @@ async function executeSourceInShadowRealmSandbox( |
421 | 313 | }), |
422 | 314 | ); |
423 | 315 |
|
424 | | - const bridgeCode = [ |
425 | | - `import * as __renderify_ns from ${JSON.stringify(moduleUrl)};`, |
426 | | - "function __renderify_message(error) {", |
427 | | - " return error && typeof error === 'object' && 'message' in error", |
428 | | - " ? String(error.message)", |
429 | | - " : String(error);", |
430 | | - "}", |
431 | | - "export async function __renderify_run(serializedRuntimeInput, exportName) {", |
432 | | - " try {", |
433 | | - " const selectedExportName =", |
434 | | - " typeof exportName === 'string' && exportName.trim().length > 0", |
435 | | - " ? exportName.trim()", |
436 | | - " : 'default';", |
437 | | - " const selected = __renderify_ns[selectedExportName];", |
438 | | - " if (selected === undefined) {", |
439 | | - ' throw new Error(`Runtime source export \\"${selectedExportName}\\" is missing`);', |
440 | | - " }", |
441 | | - " const runtimeInput =", |
442 | | - " typeof serializedRuntimeInput === 'string' && serializedRuntimeInput.length > 0", |
443 | | - " ? JSON.parse(serializedRuntimeInput)", |
444 | | - " : {};", |
445 | | - " const output = typeof selected === 'function'", |
446 | | - " ? await selected(runtimeInput)", |
447 | | - " : selected;", |
448 | | - " return JSON.stringify({ ok: true, output });", |
449 | | - " } catch (error) {", |
450 | | - " return JSON.stringify({ ok: false, error: __renderify_message(error) });", |
451 | | - " }", |
452 | | - "}", |
453 | | - ].join("\n"); |
| 316 | + const bridgeCode = buildShadowRealmBridgeSource(moduleUrl); |
454 | 317 |
|
455 | 318 | const bridgeUrl = URL.createObjectURL( |
456 | 319 | new Blob([bridgeCode], { |
|
0 commit comments