@renderify/runtime 0.1.0
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/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/runtime.cjs.js +3701 -0
- package/dist/runtime.cjs.js.map +1 -0
- package/dist/runtime.d.mts +226 -0
- package/dist/runtime.d.ts +226 -0
- package/dist/runtime.esm.js +3704 -0
- package/dist/runtime.esm.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,3704 @@
|
|
|
1
|
+
// src/embed.ts
|
|
2
|
+
import { DefaultSecurityChecker } from "@renderify/security";
|
|
3
|
+
|
|
4
|
+
// src/jspm-module-loader.ts
|
|
5
|
+
import { DEFAULT_JSPM_SPECIFIER_OVERRIDES } from "@renderify/ir";
|
|
6
|
+
var NODE_BUILTIN_MODULE_NAMES = /* @__PURE__ */ new Set([
|
|
7
|
+
"assert",
|
|
8
|
+
"buffer",
|
|
9
|
+
"child_process",
|
|
10
|
+
"cluster",
|
|
11
|
+
"console",
|
|
12
|
+
"constants",
|
|
13
|
+
"crypto",
|
|
14
|
+
"dgram",
|
|
15
|
+
"diagnostics_channel",
|
|
16
|
+
"dns",
|
|
17
|
+
"domain",
|
|
18
|
+
"events",
|
|
19
|
+
"fs",
|
|
20
|
+
"http",
|
|
21
|
+
"http2",
|
|
22
|
+
"https",
|
|
23
|
+
"inspector",
|
|
24
|
+
"module",
|
|
25
|
+
"net",
|
|
26
|
+
"os",
|
|
27
|
+
"path",
|
|
28
|
+
"perf_hooks",
|
|
29
|
+
"process",
|
|
30
|
+
"punycode",
|
|
31
|
+
"querystring",
|
|
32
|
+
"readline",
|
|
33
|
+
"repl",
|
|
34
|
+
"stream",
|
|
35
|
+
"string_decoder",
|
|
36
|
+
"sys",
|
|
37
|
+
"timers",
|
|
38
|
+
"tls",
|
|
39
|
+
"tty",
|
|
40
|
+
"url",
|
|
41
|
+
"util",
|
|
42
|
+
"v8",
|
|
43
|
+
"vm",
|
|
44
|
+
"wasi",
|
|
45
|
+
"worker_threads",
|
|
46
|
+
"zlib"
|
|
47
|
+
]);
|
|
48
|
+
function hasSystemImport(value) {
|
|
49
|
+
if (typeof value !== "object" || value === null) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const maybeSystem = value;
|
|
53
|
+
return typeof maybeSystem.import === "function";
|
|
54
|
+
}
|
|
55
|
+
var JspmModuleLoader = class {
|
|
56
|
+
cdnBaseUrl;
|
|
57
|
+
importMap;
|
|
58
|
+
cache = /* @__PURE__ */ new Map();
|
|
59
|
+
constructor(options = {}) {
|
|
60
|
+
this.cdnBaseUrl = this.normalizeCdnBaseUrl(options.cdnBaseUrl);
|
|
61
|
+
this.importMap = options.importMap ?? {};
|
|
62
|
+
}
|
|
63
|
+
async load(specifier) {
|
|
64
|
+
const resolved = this.resolveSpecifier(specifier);
|
|
65
|
+
if (this.cache.has(resolved)) {
|
|
66
|
+
return this.cache.get(resolved);
|
|
67
|
+
}
|
|
68
|
+
const loaded = await this.importWithBestEffort(resolved);
|
|
69
|
+
this.cache.set(resolved, loaded);
|
|
70
|
+
return loaded;
|
|
71
|
+
}
|
|
72
|
+
async unload(specifier) {
|
|
73
|
+
const resolved = this.resolveSpecifier(specifier);
|
|
74
|
+
this.cache.delete(resolved);
|
|
75
|
+
}
|
|
76
|
+
resolveSpecifier(specifier) {
|
|
77
|
+
const normalized = specifier.trim();
|
|
78
|
+
if (normalized.length === 0) {
|
|
79
|
+
throw new Error("Empty module specifier is not supported");
|
|
80
|
+
}
|
|
81
|
+
const mapped = this.importMap[normalized];
|
|
82
|
+
if (mapped) {
|
|
83
|
+
return mapped;
|
|
84
|
+
}
|
|
85
|
+
if (this.isUrl(normalized)) {
|
|
86
|
+
return normalized;
|
|
87
|
+
}
|
|
88
|
+
if (this.isNodeBuiltinSpecifier(normalized)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Node.js builtin modules are not supported in JSPM runtime: ${normalized}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (this.hasUnsupportedScheme(normalized)) {
|
|
94
|
+
const scheme = normalized.slice(0, normalized.indexOf(":"));
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Unsupported module scheme "${scheme}" in specifier: ${normalized}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (normalized.startsWith("npm:")) {
|
|
100
|
+
return this.resolveNpmSpecifier(normalized.slice(4));
|
|
101
|
+
}
|
|
102
|
+
if (this.isBareNpmSpecifier(normalized)) {
|
|
103
|
+
return this.resolveNpmSpecifier(normalized);
|
|
104
|
+
}
|
|
105
|
+
throw new Error(`Unsupported JSPM specifier: ${normalized}`);
|
|
106
|
+
}
|
|
107
|
+
async importWithBestEffort(resolved) {
|
|
108
|
+
const globalValue = globalThis;
|
|
109
|
+
const maybeSystem = typeof globalValue === "object" && globalValue !== null ? globalValue.System : void 0;
|
|
110
|
+
if (hasSystemImport(maybeSystem)) {
|
|
111
|
+
return maybeSystem.import(resolved);
|
|
112
|
+
}
|
|
113
|
+
return import(
|
|
114
|
+
/* webpackIgnore: true */
|
|
115
|
+
resolved
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
isUrl(value) {
|
|
119
|
+
try {
|
|
120
|
+
const parsed = new URL(value);
|
|
121
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
hasUnsupportedScheme(specifier) {
|
|
127
|
+
const schemeMatch = /^([a-zA-Z][a-zA-Z\d+\-.]*):/.exec(specifier);
|
|
128
|
+
if (!schemeMatch) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
const scheme = schemeMatch[1].toLowerCase();
|
|
132
|
+
return scheme !== "http" && scheme !== "https" && scheme !== "npm";
|
|
133
|
+
}
|
|
134
|
+
isBareNpmSpecifier(specifier) {
|
|
135
|
+
if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
if (/\s/.test(specifier)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
return /^[@a-zA-Z0-9][@a-zA-Z0-9._/-]*(?:@[a-zA-Z0-9._-]+)?$/.test(
|
|
142
|
+
specifier
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
isNodeBuiltinSpecifier(specifier) {
|
|
146
|
+
if (specifier.startsWith("node:")) {
|
|
147
|
+
const name = specifier.slice(5).split("/")[0];
|
|
148
|
+
return name.length > 0;
|
|
149
|
+
}
|
|
150
|
+
const target = specifier.startsWith("npm:") ? specifier.slice(4) : specifier;
|
|
151
|
+
const topLevel = this.extractTopLevelPackageName(target);
|
|
152
|
+
return NODE_BUILTIN_MODULE_NAMES.has(topLevel);
|
|
153
|
+
}
|
|
154
|
+
extractTopLevelPackageName(specifier) {
|
|
155
|
+
if (specifier.startsWith("@")) {
|
|
156
|
+
const segments = specifier.split("/");
|
|
157
|
+
if (segments.length < 2) {
|
|
158
|
+
return specifier;
|
|
159
|
+
}
|
|
160
|
+
const scopedName = segments[1].split("@")[0];
|
|
161
|
+
return `${segments[0]}/${scopedName}`;
|
|
162
|
+
}
|
|
163
|
+
const firstSegment = specifier.split("/")[0];
|
|
164
|
+
return firstSegment.split("@")[0];
|
|
165
|
+
}
|
|
166
|
+
resolveNpmSpecifier(specifier) {
|
|
167
|
+
const normalized = specifier.trim();
|
|
168
|
+
if (normalized.length === 0) {
|
|
169
|
+
throw new Error("Empty npm specifier is not supported");
|
|
170
|
+
}
|
|
171
|
+
const override = DEFAULT_JSPM_SPECIFIER_OVERRIDES[normalized];
|
|
172
|
+
if (override) {
|
|
173
|
+
return override;
|
|
174
|
+
}
|
|
175
|
+
return `${this.cdnBaseUrl}/npm:${normalized}`;
|
|
176
|
+
}
|
|
177
|
+
normalizeCdnBaseUrl(input) {
|
|
178
|
+
const raw = input?.trim() || "https://ga.jspm.io";
|
|
179
|
+
const normalized = raw.replace(/\/$/, "");
|
|
180
|
+
return normalized.endsWith("/npm") ? normalized.slice(0, normalized.length - 4) : normalized;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/manager.ts
|
|
185
|
+
import {
|
|
186
|
+
asJsonValue as asJsonValue3,
|
|
187
|
+
cloneJsonValue as cloneJsonValue2,
|
|
188
|
+
createElementNode as createElementNode3,
|
|
189
|
+
createTextNode as createTextNode3,
|
|
190
|
+
resolveRuntimePlanSpecVersion
|
|
191
|
+
} from "@renderify/ir";
|
|
192
|
+
|
|
193
|
+
// src/module-fetch.ts
|
|
194
|
+
import { hashStringFNV1a32Base36 } from "@renderify/ir";
|
|
195
|
+
var DEFAULT_ESM_CDN_BASE = "https://esm.sh";
|
|
196
|
+
function buildRemoteModuleAttemptUrls(url, fallbackCdnBases) {
|
|
197
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
198
|
+
candidates.add(url);
|
|
199
|
+
for (const fallbackBase of fallbackCdnBases) {
|
|
200
|
+
const fallback = toConfiguredFallbackUrl(url, fallbackBase);
|
|
201
|
+
if (fallback) {
|
|
202
|
+
candidates.add(fallback);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return [...candidates];
|
|
206
|
+
}
|
|
207
|
+
function toConfiguredFallbackUrl(url, cdnBase) {
|
|
208
|
+
const normalizedBase = cdnBase.trim().replace(/\/$/, "");
|
|
209
|
+
const specifier = extractJspmNpmSpecifier(url);
|
|
210
|
+
if (!specifier || normalizedBase.length === 0) {
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
if (normalizedBase.includes("esm.sh")) {
|
|
214
|
+
return toEsmFallbackUrl(url, normalizedBase);
|
|
215
|
+
}
|
|
216
|
+
if (normalizedBase.includes("jsdelivr.net")) {
|
|
217
|
+
return `${normalizedBase}/npm/${specifier}`;
|
|
218
|
+
}
|
|
219
|
+
if (normalizedBase.includes("unpkg.com")) {
|
|
220
|
+
const separator = specifier.includes("?") ? "&" : "?";
|
|
221
|
+
return `${normalizedBase}/${specifier}${separator}module`;
|
|
222
|
+
}
|
|
223
|
+
if (normalizedBase.includes("jspm.io")) {
|
|
224
|
+
const root = normalizedBase.endsWith("/npm") ? normalizedBase.slice(0, normalizedBase.length - 4) : normalizedBase;
|
|
225
|
+
return `${root}/npm:${specifier}`;
|
|
226
|
+
}
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
function toEsmFallbackUrl(url, cdnBase = DEFAULT_ESM_CDN_BASE) {
|
|
230
|
+
const specifier = extractJspmNpmSpecifier(url);
|
|
231
|
+
if (!specifier) {
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
const normalizedBase = cdnBase.trim().replace(/\/$/, "");
|
|
235
|
+
if (normalizedBase.length === 0) {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
const aliasQuery = [
|
|
239
|
+
"alias=react:preact/compat,react-dom:preact/compat,react-dom/client:preact/compat,react/jsx-runtime:preact/jsx-runtime,react/jsx-dev-runtime:preact/jsx-runtime",
|
|
240
|
+
"target=es2022"
|
|
241
|
+
].join("&");
|
|
242
|
+
const separator = specifier.includes("?") ? "&" : "?";
|
|
243
|
+
return `${normalizedBase}/${specifier}${separator}${aliasQuery}`;
|
|
244
|
+
}
|
|
245
|
+
function extractJspmNpmSpecifier(url) {
|
|
246
|
+
const prefix = "https://ga.jspm.io/npm:";
|
|
247
|
+
if (!url.startsWith(prefix)) {
|
|
248
|
+
return void 0;
|
|
249
|
+
}
|
|
250
|
+
const specifier = url.slice(prefix.length).trim();
|
|
251
|
+
if (specifier.length === 0) {
|
|
252
|
+
return void 0;
|
|
253
|
+
}
|
|
254
|
+
return specifier;
|
|
255
|
+
}
|
|
256
|
+
function isCssModuleResponse(fetched) {
|
|
257
|
+
return fetched.contentType.includes("text/css") || isCssUrl(fetched.url);
|
|
258
|
+
}
|
|
259
|
+
function isJsonModuleResponse(fetched) {
|
|
260
|
+
return fetched.contentType.includes("application/json") || fetched.contentType.includes("text/json") || isJsonUrl(fetched.url);
|
|
261
|
+
}
|
|
262
|
+
function isJavaScriptModuleResponse(fetched) {
|
|
263
|
+
if (isJavaScriptLikeContentType(fetched.contentType)) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
return isJavaScriptUrl(fetched.url);
|
|
267
|
+
}
|
|
268
|
+
function isJavaScriptLikeContentType(contentType) {
|
|
269
|
+
return contentType.includes("javascript") || contentType.includes("ecmascript") || contentType.includes("typescript") || contentType.includes("module");
|
|
270
|
+
}
|
|
271
|
+
function isBinaryLikeContentType(contentType) {
|
|
272
|
+
return contentType.includes("application/wasm") || contentType.includes("image/") || contentType.includes("font/");
|
|
273
|
+
}
|
|
274
|
+
function isJavaScriptUrl(url) {
|
|
275
|
+
const pathname = toUrlPathname(url);
|
|
276
|
+
return /\.(?:m?js|cjs|jsx|ts|tsx)$/i.test(pathname);
|
|
277
|
+
}
|
|
278
|
+
function isCssUrl(url) {
|
|
279
|
+
const pathname = toUrlPathname(url);
|
|
280
|
+
return /\.css$/i.test(pathname);
|
|
281
|
+
}
|
|
282
|
+
function isJsonUrl(url) {
|
|
283
|
+
const pathname = toUrlPathname(url);
|
|
284
|
+
return /\.json$/i.test(pathname);
|
|
285
|
+
}
|
|
286
|
+
function createCssProxyModuleSource(cssText, sourceUrl) {
|
|
287
|
+
const styleId = `renderify-css-${hashStringFNV1a32Base36(sourceUrl)}`;
|
|
288
|
+
const cssLiteral = JSON.stringify(cssText);
|
|
289
|
+
const styleIdLiteral = JSON.stringify(styleId);
|
|
290
|
+
return [
|
|
291
|
+
"const __css = " + cssLiteral + ";",
|
|
292
|
+
"const __styleId = " + styleIdLiteral + ";",
|
|
293
|
+
'if (typeof document !== "undefined") {',
|
|
294
|
+
" let __style = null;",
|
|
295
|
+
' const __styles = document.querySelectorAll("style[data-renderify-style-id]");',
|
|
296
|
+
" for (const __candidate of __styles) {",
|
|
297
|
+
' if (__candidate.getAttribute("data-renderify-style-id") === __styleId) {',
|
|
298
|
+
" __style = __candidate;",
|
|
299
|
+
" break;",
|
|
300
|
+
" }",
|
|
301
|
+
" }",
|
|
302
|
+
" if (!__style) {",
|
|
303
|
+
' __style = document.createElement("style");',
|
|
304
|
+
' __style.setAttribute("data-renderify-style-id", __styleId);',
|
|
305
|
+
" __style.textContent = __css;",
|
|
306
|
+
" document.head.appendChild(__style);",
|
|
307
|
+
" }",
|
|
308
|
+
"}",
|
|
309
|
+
"export default __css;",
|
|
310
|
+
"export const cssText = __css;"
|
|
311
|
+
].join("\n");
|
|
312
|
+
}
|
|
313
|
+
function createJsonProxyModuleSource(value) {
|
|
314
|
+
return [
|
|
315
|
+
`const __json = ${JSON.stringify(value)};`,
|
|
316
|
+
"export default __json;"
|
|
317
|
+
].join("\n");
|
|
318
|
+
}
|
|
319
|
+
function createTextProxyModuleSource(text) {
|
|
320
|
+
return [
|
|
321
|
+
`const __text = ${JSON.stringify(text)};`,
|
|
322
|
+
"export default __text;",
|
|
323
|
+
"export const text = __text;"
|
|
324
|
+
].join("\n");
|
|
325
|
+
}
|
|
326
|
+
function createUrlProxyModuleSource(url) {
|
|
327
|
+
return [
|
|
328
|
+
`const __assetUrl = ${JSON.stringify(url)};`,
|
|
329
|
+
"export default __assetUrl;",
|
|
330
|
+
"export const assetUrl = __assetUrl;"
|
|
331
|
+
].join("\n");
|
|
332
|
+
}
|
|
333
|
+
async function fetchWithTimeout(url, timeoutMs) {
|
|
334
|
+
if (typeof AbortController === "undefined") {
|
|
335
|
+
return fetch(url);
|
|
336
|
+
}
|
|
337
|
+
const controller = new AbortController();
|
|
338
|
+
const timer = setTimeout(() => {
|
|
339
|
+
controller.abort();
|
|
340
|
+
}, timeoutMs);
|
|
341
|
+
try {
|
|
342
|
+
return await fetch(url, {
|
|
343
|
+
signal: controller.signal
|
|
344
|
+
});
|
|
345
|
+
} finally {
|
|
346
|
+
clearTimeout(timer);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function delay(ms) {
|
|
350
|
+
if (ms <= 0) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
await new Promise((resolve) => {
|
|
354
|
+
setTimeout(resolve, ms);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function toUrlPathname(url) {
|
|
358
|
+
try {
|
|
359
|
+
return new URL(url).pathname;
|
|
360
|
+
} catch {
|
|
361
|
+
return url;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/runtime-environment.ts
|
|
366
|
+
function nowMs() {
|
|
367
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
368
|
+
return performance.now();
|
|
369
|
+
}
|
|
370
|
+
return Date.now();
|
|
371
|
+
}
|
|
372
|
+
function isBrowserRuntime() {
|
|
373
|
+
return typeof window !== "undefined" && typeof document !== "undefined" && typeof navigator !== "undefined";
|
|
374
|
+
}
|
|
375
|
+
function hasVmScript(value) {
|
|
376
|
+
if (typeof value !== "object" || value === null) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
const candidate = value;
|
|
380
|
+
return typeof candidate.Script === "function";
|
|
381
|
+
}
|
|
382
|
+
function hasPreactFactory(value) {
|
|
383
|
+
if (typeof value !== "object" || value === null) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
const candidate = value;
|
|
387
|
+
return typeof candidate.h === "function";
|
|
388
|
+
}
|
|
389
|
+
function getVmSpecifier() {
|
|
390
|
+
return "node:vm";
|
|
391
|
+
}
|
|
392
|
+
function getPreactSpecifier() {
|
|
393
|
+
return "preact";
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/runtime-budget.ts
|
|
397
|
+
function hasExceededBudget(frame) {
|
|
398
|
+
return nowMs() - frame.startedAt > frame.maxExecutionMs;
|
|
399
|
+
}
|
|
400
|
+
function isAborted(signal) {
|
|
401
|
+
return Boolean(signal?.aborted);
|
|
402
|
+
}
|
|
403
|
+
function createAbortError(message) {
|
|
404
|
+
const error = new Error(message);
|
|
405
|
+
error.name = "AbortError";
|
|
406
|
+
return error;
|
|
407
|
+
}
|
|
408
|
+
function throwIfAborted(signal) {
|
|
409
|
+
if (isAborted(signal)) {
|
|
410
|
+
throw createAbortError("Runtime execution aborted");
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function isAbortError(error) {
|
|
414
|
+
return error instanceof Error && error.name === "AbortError";
|
|
415
|
+
}
|
|
416
|
+
async function withRemainingBudget(operation, frame, timeoutMessage) {
|
|
417
|
+
throwIfAborted(frame.signal);
|
|
418
|
+
const remainingMs = frame.maxExecutionMs - (nowMs() - frame.startedAt);
|
|
419
|
+
if (remainingMs <= 0) {
|
|
420
|
+
throw new Error(timeoutMessage);
|
|
421
|
+
}
|
|
422
|
+
let timer;
|
|
423
|
+
let onAbort;
|
|
424
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
425
|
+
timer = setTimeout(() => {
|
|
426
|
+
reject(new Error(timeoutMessage));
|
|
427
|
+
}, remainingMs);
|
|
428
|
+
});
|
|
429
|
+
const signal = frame.signal;
|
|
430
|
+
const abortPromise = signal && new Promise((_resolve, reject) => {
|
|
431
|
+
onAbort = () => {
|
|
432
|
+
reject(createAbortError("Runtime execution aborted"));
|
|
433
|
+
};
|
|
434
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
435
|
+
});
|
|
436
|
+
try {
|
|
437
|
+
const pending = abortPromise ? [operation(), timeoutPromise, abortPromise] : [operation(), timeoutPromise];
|
|
438
|
+
return await Promise.race(pending);
|
|
439
|
+
} finally {
|
|
440
|
+
if (timer !== void 0) {
|
|
441
|
+
clearTimeout(timer);
|
|
442
|
+
}
|
|
443
|
+
if (signal && onAbort) {
|
|
444
|
+
signal.removeEventListener("abort", onAbort);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/runtime-component-runtime.ts
|
|
450
|
+
import {
|
|
451
|
+
asJsonValue,
|
|
452
|
+
cloneJsonValue,
|
|
453
|
+
isRuntimeNode
|
|
454
|
+
} from "@renderify/ir";
|
|
455
|
+
async function createPreactRenderArtifact(input) {
|
|
456
|
+
const preact = await loadPreactModule();
|
|
457
|
+
if (!preact) {
|
|
458
|
+
input.diagnostics.push({
|
|
459
|
+
level: "error",
|
|
460
|
+
code: "RUNTIME_PREACT_UNAVAILABLE",
|
|
461
|
+
message: "source.runtime=preact requested but preact runtime is unavailable"
|
|
462
|
+
});
|
|
463
|
+
return void 0;
|
|
464
|
+
}
|
|
465
|
+
if (isPreactLikeVNode(input.sourceExport)) {
|
|
466
|
+
return {
|
|
467
|
+
mode: "preact-vnode",
|
|
468
|
+
payload: input.sourceExport
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (typeof input.sourceExport !== "function") {
|
|
472
|
+
input.diagnostics.push({
|
|
473
|
+
level: "error",
|
|
474
|
+
code: "RUNTIME_PREACT_EXPORT_INVALID",
|
|
475
|
+
message: "source.runtime=preact requires a component export function"
|
|
476
|
+
});
|
|
477
|
+
return void 0;
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
const vnode = preact.h(
|
|
481
|
+
input.sourceExport,
|
|
482
|
+
input.runtimeInput
|
|
483
|
+
);
|
|
484
|
+
return {
|
|
485
|
+
mode: "preact-vnode",
|
|
486
|
+
payload: vnode
|
|
487
|
+
};
|
|
488
|
+
} catch (error) {
|
|
489
|
+
input.diagnostics.push({
|
|
490
|
+
level: "error",
|
|
491
|
+
code: "RUNTIME_PREACT_VNODE_FAILED",
|
|
492
|
+
message: errorToMessage(error)
|
|
493
|
+
});
|
|
494
|
+
return void 0;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
async function executeComponentFactory(input) {
|
|
498
|
+
if (input.executionProfile !== "isolated-vm") {
|
|
499
|
+
return input.withRemainingBudget(
|
|
500
|
+
async () => input.componentFactory(input.props, input.context, input.children),
|
|
501
|
+
input.timeoutMessage
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
const isolated = await executeComponentInVm(input);
|
|
505
|
+
if (isolated.mode === "isolation-unavailable") {
|
|
506
|
+
if (!input.allowIsolationFallback) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
"isolated-vm profile requested but node:vm is unavailable; fallback is disabled"
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
input.diagnostics.push({
|
|
512
|
+
level: "warning",
|
|
513
|
+
code: "RUNTIME_SANDBOX_UNAVAILABLE",
|
|
514
|
+
message: "isolated-vm profile requested but node:vm is unavailable; falling back to standard execution"
|
|
515
|
+
});
|
|
516
|
+
return input.withRemainingBudget(
|
|
517
|
+
async () => input.componentFactory(input.props, input.context, input.children),
|
|
518
|
+
input.timeoutMessage
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
return isolated.value;
|
|
522
|
+
}
|
|
523
|
+
async function executeComponentInVm(input) {
|
|
524
|
+
const vmModule = await loadVmModule();
|
|
525
|
+
if (!vmModule) {
|
|
526
|
+
return { mode: "isolation-unavailable" };
|
|
527
|
+
}
|
|
528
|
+
const remainingMs = input.maxExecutionMs - (nowMs() - input.startedAt);
|
|
529
|
+
if (remainingMs <= 0) {
|
|
530
|
+
throw new Error("Component execution timed out before sandbox start");
|
|
531
|
+
}
|
|
532
|
+
const serializedFactory = input.componentFactory.toString();
|
|
533
|
+
const sandboxData = {
|
|
534
|
+
props: cloneJsonValue(input.props),
|
|
535
|
+
context: cloneJsonValue(asJsonValue(input.context)),
|
|
536
|
+
children: cloneJsonValue(asJsonValue(input.children))
|
|
537
|
+
};
|
|
538
|
+
const script = new vmModule.Script(
|
|
539
|
+
`'use strict';
|
|
540
|
+
const __component = (${serializedFactory});
|
|
541
|
+
const __result = __component(__input.props, __input.context, __input.children);
|
|
542
|
+
if (__result && typeof __result.then === "function") {
|
|
543
|
+
throw new Error("Async component is not supported in isolated-vm profile");
|
|
544
|
+
}
|
|
545
|
+
__result;`
|
|
546
|
+
);
|
|
547
|
+
const output = script.runInNewContext(
|
|
548
|
+
{
|
|
549
|
+
__input: sandboxData
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
timeout: Math.max(1, Math.floor(remainingMs))
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
if (typeof output === "string") {
|
|
556
|
+
return {
|
|
557
|
+
mode: "isolated",
|
|
558
|
+
value: output
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
if (isRuntimeNode(output)) {
|
|
562
|
+
return {
|
|
563
|
+
mode: "isolated",
|
|
564
|
+
value: output
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
throw new Error("Sandboxed component returned unsupported output");
|
|
568
|
+
}
|
|
569
|
+
function isPreactLikeVNode(value) {
|
|
570
|
+
if (typeof value !== "object" || value === null) {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
const record = value;
|
|
574
|
+
return "type" in record && "props" in record;
|
|
575
|
+
}
|
|
576
|
+
async function loadPreactModule() {
|
|
577
|
+
try {
|
|
578
|
+
const maybePreact = await import(getPreactSpecifier());
|
|
579
|
+
if (!hasPreactFactory(maybePreact)) {
|
|
580
|
+
return void 0;
|
|
581
|
+
}
|
|
582
|
+
return maybePreact;
|
|
583
|
+
} catch {
|
|
584
|
+
return void 0;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
async function loadVmModule() {
|
|
588
|
+
if (typeof process === "undefined" || typeof process.versions !== "object" || process.versions === null || typeof process.versions.node !== "string") {
|
|
589
|
+
return void 0;
|
|
590
|
+
}
|
|
591
|
+
try {
|
|
592
|
+
const maybeVm = await import(getVmSpecifier());
|
|
593
|
+
if (!hasVmScript(maybeVm)) {
|
|
594
|
+
return void 0;
|
|
595
|
+
}
|
|
596
|
+
return maybeVm;
|
|
597
|
+
} catch {
|
|
598
|
+
return void 0;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function errorToMessage(error) {
|
|
602
|
+
if (error instanceof Error) {
|
|
603
|
+
return error.message;
|
|
604
|
+
}
|
|
605
|
+
return String(error);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/runtime-defaults.ts
|
|
609
|
+
import {
|
|
610
|
+
DEFAULT_RUNTIME_PLAN_SPEC_VERSION
|
|
611
|
+
} from "@renderify/ir";
|
|
612
|
+
var FALLBACK_MAX_IMPORTS = 50;
|
|
613
|
+
var FALLBACK_MAX_COMPONENT_INVOCATIONS = 200;
|
|
614
|
+
var FALLBACK_MAX_EXECUTION_MS = 1500;
|
|
615
|
+
var FALLBACK_EXECUTION_PROFILE = "standard";
|
|
616
|
+
var FALLBACK_JSPM_CDN_BASE = "https://ga.jspm.io/npm";
|
|
617
|
+
var FALLBACK_ESM_CDN_BASE = DEFAULT_ESM_CDN_BASE;
|
|
618
|
+
var FALLBACK_ENABLE_DEPENDENCY_PREFLIGHT = true;
|
|
619
|
+
var FALLBACK_FAIL_ON_DEPENDENCY_PREFLIGHT_ERROR = false;
|
|
620
|
+
var FALLBACK_REMOTE_FETCH_TIMEOUT_MS = 12e3;
|
|
621
|
+
var FALLBACK_REMOTE_FETCH_RETRIES = 2;
|
|
622
|
+
var FALLBACK_REMOTE_FETCH_BACKOFF_MS = 150;
|
|
623
|
+
var FALLBACK_REMOTE_FALLBACK_CDN_BASES = [FALLBACK_ESM_CDN_BASE];
|
|
624
|
+
var FALLBACK_SUPPORTED_SPEC_VERSIONS = [
|
|
625
|
+
DEFAULT_RUNTIME_PLAN_SPEC_VERSION
|
|
626
|
+
];
|
|
627
|
+
var FALLBACK_ENFORCE_MODULE_MANIFEST = true;
|
|
628
|
+
var FALLBACK_ALLOW_ISOLATION_FALLBACK = false;
|
|
629
|
+
var FALLBACK_BROWSER_SOURCE_SANDBOX_TIMEOUT_MS = 4e3;
|
|
630
|
+
var FALLBACK_BROWSER_SOURCE_SANDBOX_FAIL_CLOSED = true;
|
|
631
|
+
function normalizeSupportedSpecVersions(versions) {
|
|
632
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
633
|
+
const input = versions && versions.length > 0 ? versions : FALLBACK_SUPPORTED_SPEC_VERSIONS;
|
|
634
|
+
for (const entry of input) {
|
|
635
|
+
if (typeof entry !== "string") {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
const trimmed = entry.trim();
|
|
639
|
+
if (trimmed.length > 0) {
|
|
640
|
+
normalized.add(trimmed);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (normalized.size === 0) {
|
|
644
|
+
normalized.add(DEFAULT_RUNTIME_PLAN_SPEC_VERSION);
|
|
645
|
+
}
|
|
646
|
+
return normalized;
|
|
647
|
+
}
|
|
648
|
+
function normalizeSourceSandboxMode(mode) {
|
|
649
|
+
if (mode === "none" || mode === "worker" || mode === "iframe") {
|
|
650
|
+
return mode;
|
|
651
|
+
}
|
|
652
|
+
return isBrowserRuntime() ? "worker" : "none";
|
|
653
|
+
}
|
|
654
|
+
function normalizeFallbackCdnBases(input) {
|
|
655
|
+
const explicitInput = input !== void 0;
|
|
656
|
+
const candidates = explicitInput ? input : FALLBACK_REMOTE_FALLBACK_CDN_BASES;
|
|
657
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
658
|
+
for (const entry of candidates) {
|
|
659
|
+
if (typeof entry !== "string") {
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
const trimmed = entry.trim().replace(/\/$/, "");
|
|
663
|
+
if (trimmed.length > 0) {
|
|
664
|
+
normalized.add(trimmed);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (normalized.size === 0) {
|
|
668
|
+
return explicitInput ? [] : [FALLBACK_ESM_CDN_BASE];
|
|
669
|
+
}
|
|
670
|
+
return [...normalized];
|
|
671
|
+
}
|
|
672
|
+
function normalizePositiveInteger(value, fallback) {
|
|
673
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
674
|
+
return fallback;
|
|
675
|
+
}
|
|
676
|
+
return value;
|
|
677
|
+
}
|
|
678
|
+
function normalizeNonNegativeInteger(value, fallback) {
|
|
679
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
|
|
680
|
+
return fallback;
|
|
681
|
+
}
|
|
682
|
+
return value;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/runtime-node-resolver.ts
|
|
686
|
+
import {
|
|
687
|
+
asJsonValue as asJsonValue2,
|
|
688
|
+
createElementNode,
|
|
689
|
+
createTextNode,
|
|
690
|
+
isRuntimeNode as isRuntimeNode2
|
|
691
|
+
} from "@renderify/ir";
|
|
692
|
+
|
|
693
|
+
// src/template.ts
|
|
694
|
+
import {
|
|
695
|
+
getValueByPath
|
|
696
|
+
} from "@renderify/ir";
|
|
697
|
+
var TEMPLATE_STRINGIFY_MAX_DEPTH = 8;
|
|
698
|
+
var TEMPLATE_STRINGIFY_MAX_NODES = 256;
|
|
699
|
+
var TEMPLATE_STRINGIFY_MAX_LENGTH = 4096;
|
|
700
|
+
var TEMPLATE_TRUNCATED_MARKER = "[Truncated]";
|
|
701
|
+
var TEMPLATE_CIRCULAR = /* @__PURE__ */ Symbol("renderify-template-circular");
|
|
702
|
+
function resolveProps(props, context, state, event) {
|
|
703
|
+
if (!props) {
|
|
704
|
+
return void 0;
|
|
705
|
+
}
|
|
706
|
+
const resolved = {};
|
|
707
|
+
for (const [key, value] of Object.entries(props)) {
|
|
708
|
+
resolved[key] = resolveJsonValue(value, context, state, event);
|
|
709
|
+
}
|
|
710
|
+
return resolved;
|
|
711
|
+
}
|
|
712
|
+
function resolveJsonValue(value, context, state, event) {
|
|
713
|
+
return resolveJsonValueInternal(value, context, state, event, /* @__PURE__ */ new WeakSet());
|
|
714
|
+
}
|
|
715
|
+
function resolveJsonValueInternal(value, context, state, event, seen) {
|
|
716
|
+
if (typeof value === "string") {
|
|
717
|
+
return interpolateTemplate(value, context, state, event);
|
|
718
|
+
}
|
|
719
|
+
if (Array.isArray(value)) {
|
|
720
|
+
if (seen.has(value)) {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
seen.add(value);
|
|
724
|
+
const resolved = value.map(
|
|
725
|
+
(item) => resolveJsonValueInternal(item, context, state, event, seen)
|
|
726
|
+
);
|
|
727
|
+
seen.delete(value);
|
|
728
|
+
return resolved;
|
|
729
|
+
}
|
|
730
|
+
if (value !== null && typeof value === "object") {
|
|
731
|
+
if (seen.has(value)) {
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
seen.add(value);
|
|
735
|
+
const resolved = {};
|
|
736
|
+
for (const [key, item] of Object.entries(value)) {
|
|
737
|
+
resolved[key] = resolveJsonValueInternal(
|
|
738
|
+
item,
|
|
739
|
+
context,
|
|
740
|
+
state,
|
|
741
|
+
event,
|
|
742
|
+
seen
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
seen.delete(value);
|
|
746
|
+
return resolved;
|
|
747
|
+
}
|
|
748
|
+
return value;
|
|
749
|
+
}
|
|
750
|
+
function interpolateTemplate(template, context, state, event) {
|
|
751
|
+
return template.replace(/{{\s*([^}]+)\s*}}/g, (_match, expression) => {
|
|
752
|
+
const resolved = resolveExpression(expression, context, state, event);
|
|
753
|
+
if (resolved === void 0 || resolved === null) {
|
|
754
|
+
return "";
|
|
755
|
+
}
|
|
756
|
+
if (typeof resolved === "object") {
|
|
757
|
+
return serializeTemplateObject(resolved);
|
|
758
|
+
}
|
|
759
|
+
return String(resolved);
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
function serializeTemplateObject(value) {
|
|
763
|
+
const normalized = toTemplateSerializable(value, 0, /* @__PURE__ */ new WeakSet(), {
|
|
764
|
+
nodes: 0
|
|
765
|
+
});
|
|
766
|
+
if (normalized === TEMPLATE_CIRCULAR || normalized === void 0) {
|
|
767
|
+
return "";
|
|
768
|
+
}
|
|
769
|
+
try {
|
|
770
|
+
const serialized = JSON.stringify(normalized);
|
|
771
|
+
if (typeof serialized !== "string") {
|
|
772
|
+
return "";
|
|
773
|
+
}
|
|
774
|
+
if (serialized.length > TEMPLATE_STRINGIFY_MAX_LENGTH) {
|
|
775
|
+
return `${serialized.slice(0, TEMPLATE_STRINGIFY_MAX_LENGTH)}...`;
|
|
776
|
+
}
|
|
777
|
+
return serialized;
|
|
778
|
+
} catch {
|
|
779
|
+
return "";
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
function toTemplateSerializable(value, depth, seen, budget) {
|
|
783
|
+
if (value === void 0) {
|
|
784
|
+
return void 0;
|
|
785
|
+
}
|
|
786
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
787
|
+
return value;
|
|
788
|
+
}
|
|
789
|
+
if (typeof value === "number") {
|
|
790
|
+
return Number.isFinite(value) ? value : null;
|
|
791
|
+
}
|
|
792
|
+
if (typeof value === "bigint") {
|
|
793
|
+
return value.toString();
|
|
794
|
+
}
|
|
795
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
796
|
+
return String(value);
|
|
797
|
+
}
|
|
798
|
+
if (typeof value !== "object") {
|
|
799
|
+
return String(value);
|
|
800
|
+
}
|
|
801
|
+
if (seen.has(value)) {
|
|
802
|
+
return TEMPLATE_CIRCULAR;
|
|
803
|
+
}
|
|
804
|
+
if (depth >= TEMPLATE_STRINGIFY_MAX_DEPTH || budget.nodes >= TEMPLATE_STRINGIFY_MAX_NODES) {
|
|
805
|
+
return TEMPLATE_TRUNCATED_MARKER;
|
|
806
|
+
}
|
|
807
|
+
seen.add(value);
|
|
808
|
+
budget.nodes += 1;
|
|
809
|
+
if (Array.isArray(value)) {
|
|
810
|
+
const normalizedArray = [];
|
|
811
|
+
for (const entry of value) {
|
|
812
|
+
const normalized = toTemplateSerializable(entry, depth + 1, seen, budget);
|
|
813
|
+
if (normalized === TEMPLATE_CIRCULAR) {
|
|
814
|
+
seen.delete(value);
|
|
815
|
+
return TEMPLATE_CIRCULAR;
|
|
816
|
+
}
|
|
817
|
+
normalizedArray.push(normalized === void 0 ? null : normalized);
|
|
818
|
+
}
|
|
819
|
+
seen.delete(value);
|
|
820
|
+
return normalizedArray;
|
|
821
|
+
}
|
|
822
|
+
const normalizedRecord = {};
|
|
823
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
824
|
+
const normalized = toTemplateSerializable(entry, depth + 1, seen, budget);
|
|
825
|
+
if (normalized === TEMPLATE_CIRCULAR) {
|
|
826
|
+
seen.delete(value);
|
|
827
|
+
return TEMPLATE_CIRCULAR;
|
|
828
|
+
}
|
|
829
|
+
if (normalized !== void 0) {
|
|
830
|
+
normalizedRecord[key] = normalized;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
seen.delete(value);
|
|
834
|
+
return normalizedRecord;
|
|
835
|
+
}
|
|
836
|
+
function resolveExpression(expression, context, state, event) {
|
|
837
|
+
const path = expression.trim();
|
|
838
|
+
if (path.startsWith("state.")) {
|
|
839
|
+
return getValueByPath(state, path.slice(6));
|
|
840
|
+
}
|
|
841
|
+
if (path.startsWith("event.")) {
|
|
842
|
+
return getValueByPath(event, path.slice(6));
|
|
843
|
+
}
|
|
844
|
+
if (path.startsWith("context.")) {
|
|
845
|
+
return getValueByPath(context, path.slice(8));
|
|
846
|
+
}
|
|
847
|
+
if (path.startsWith("vars.")) {
|
|
848
|
+
return getValueByPath(context.variables, path.slice(5));
|
|
849
|
+
}
|
|
850
|
+
return getValueByPath(state, path);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/runtime-node-resolver.ts
|
|
854
|
+
async function resolveRuntimeNode(input) {
|
|
855
|
+
const {
|
|
856
|
+
node,
|
|
857
|
+
moduleManifest,
|
|
858
|
+
context,
|
|
859
|
+
state,
|
|
860
|
+
event,
|
|
861
|
+
diagnostics,
|
|
862
|
+
frame,
|
|
863
|
+
resolver
|
|
864
|
+
} = input;
|
|
865
|
+
if (node.type === "text") {
|
|
866
|
+
return createTextNode(
|
|
867
|
+
interpolateTemplate(node.value, context, state, event)
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
const resolvedChildren = await resolveChildren(
|
|
871
|
+
node.children ?? [],
|
|
872
|
+
resolver.resolveNode
|
|
873
|
+
);
|
|
874
|
+
if (node.type === "element") {
|
|
875
|
+
return {
|
|
876
|
+
...node,
|
|
877
|
+
props: resolveProps(node.props, context, state, event),
|
|
878
|
+
children: resolvedChildren
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
if (frame.componentInvocations >= frame.maxComponentInvocations) {
|
|
882
|
+
diagnostics.push({
|
|
883
|
+
level: "error",
|
|
884
|
+
code: "RUNTIME_COMPONENT_LIMIT_EXCEEDED",
|
|
885
|
+
message: `Component invocation limit exceeded: ${frame.maxComponentInvocations}`
|
|
886
|
+
});
|
|
887
|
+
return createElementNode(
|
|
888
|
+
"div",
|
|
889
|
+
{ "data-renderify-component-limit": node.module },
|
|
890
|
+
[createTextNode("Component invocation limit exceeded")]
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
frame.componentInvocations += 1;
|
|
894
|
+
const resolvedComponentSpecifier = resolver.resolveRuntimeSpecifier(
|
|
895
|
+
node.module,
|
|
896
|
+
moduleManifest,
|
|
897
|
+
diagnostics,
|
|
898
|
+
"component"
|
|
899
|
+
);
|
|
900
|
+
if (!resolvedComponentSpecifier) {
|
|
901
|
+
return createElementNode(
|
|
902
|
+
"div",
|
|
903
|
+
{ "data-renderify-component-error": node.module },
|
|
904
|
+
[createTextNode("Missing module manifest entry for component")]
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
if (!resolver.moduleLoader) {
|
|
908
|
+
diagnostics.push({
|
|
909
|
+
level: "warning",
|
|
910
|
+
code: "RUNTIME_COMPONENT_SKIPPED",
|
|
911
|
+
message: `Component ${resolvedComponentSpecifier} skipped because module loader is missing`
|
|
912
|
+
});
|
|
913
|
+
return createElementNode(
|
|
914
|
+
"div",
|
|
915
|
+
{ "data-renderify-missing-module": node.module },
|
|
916
|
+
resolvedChildren
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
const loaded = await resolver.withRemainingBudget(
|
|
921
|
+
() => resolver.moduleLoader.load(resolvedComponentSpecifier),
|
|
922
|
+
`Component module timed out: ${resolvedComponentSpecifier}`
|
|
923
|
+
);
|
|
924
|
+
const exportName = node.exportName ?? "default";
|
|
925
|
+
const target = selectExportFromNamespace(loaded, exportName);
|
|
926
|
+
if (typeof target !== "function") {
|
|
927
|
+
diagnostics.push({
|
|
928
|
+
level: "error",
|
|
929
|
+
code: "RUNTIME_COMPONENT_INVALID",
|
|
930
|
+
message: `Export ${exportName} from ${resolvedComponentSpecifier} is not callable`
|
|
931
|
+
});
|
|
932
|
+
return createElementNode(
|
|
933
|
+
"div",
|
|
934
|
+
{ "data-renderify-component-error": `${node.module}:${exportName}` },
|
|
935
|
+
[createTextNode("Component export is not callable")]
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
const runtimeContext = {
|
|
939
|
+
...context,
|
|
940
|
+
variables: {
|
|
941
|
+
...context.variables ?? {},
|
|
942
|
+
state,
|
|
943
|
+
event: event ? asJsonValue2(event) : null
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
const produced = await executeComponentFactory({
|
|
947
|
+
componentFactory: target,
|
|
948
|
+
props: resolveProps(node.props, context, state, event) ?? {},
|
|
949
|
+
context: runtimeContext,
|
|
950
|
+
children: resolvedChildren,
|
|
951
|
+
executionProfile: frame.executionProfile,
|
|
952
|
+
maxExecutionMs: frame.maxExecutionMs,
|
|
953
|
+
startedAt: frame.startedAt,
|
|
954
|
+
timeoutMessage: `Component execution timed out: ${node.module}`,
|
|
955
|
+
allowIsolationFallback: resolver.allowIsolationFallback,
|
|
956
|
+
diagnostics,
|
|
957
|
+
withRemainingBudget: resolver.withRemainingBudget
|
|
958
|
+
});
|
|
959
|
+
if (typeof produced === "string") {
|
|
960
|
+
return createTextNode(
|
|
961
|
+
interpolateTemplate(produced, context, state, event)
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
if (isRuntimeNode2(produced)) {
|
|
965
|
+
return resolver.resolveNode(produced);
|
|
966
|
+
}
|
|
967
|
+
diagnostics.push({
|
|
968
|
+
level: "error",
|
|
969
|
+
code: "RUNTIME_COMPONENT_OUTPUT_INVALID",
|
|
970
|
+
message: `Component ${resolvedComponentSpecifier} produced unsupported output`
|
|
971
|
+
});
|
|
972
|
+
return createElementNode(
|
|
973
|
+
"div",
|
|
974
|
+
{ "data-renderify-component-error": node.module },
|
|
975
|
+
[createTextNode("Unsupported component output")]
|
|
976
|
+
);
|
|
977
|
+
} catch (error) {
|
|
978
|
+
diagnostics.push({
|
|
979
|
+
level: "error",
|
|
980
|
+
code: "RUNTIME_COMPONENT_EXEC_FAILED",
|
|
981
|
+
message: `${resolvedComponentSpecifier}: ${resolver.errorToMessage(error)}`
|
|
982
|
+
});
|
|
983
|
+
return createElementNode(
|
|
984
|
+
"div",
|
|
985
|
+
{ "data-renderify-component-error": node.module },
|
|
986
|
+
[createTextNode("Component execution failed")]
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
async function resolveChildren(nodes, resolveNode) {
|
|
991
|
+
const resolved = [];
|
|
992
|
+
for (const child of nodes) {
|
|
993
|
+
resolved.push(await resolveNode(child));
|
|
994
|
+
}
|
|
995
|
+
return resolved;
|
|
996
|
+
}
|
|
997
|
+
function selectExportFromNamespace(moduleNamespace, exportName) {
|
|
998
|
+
if (typeof moduleNamespace !== "object" || moduleNamespace === null) {
|
|
999
|
+
return void 0;
|
|
1000
|
+
}
|
|
1001
|
+
const record = moduleNamespace;
|
|
1002
|
+
return record[exportName];
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/runtime-plan-imports.ts
|
|
1006
|
+
async function resolveRuntimePlanImports(input) {
|
|
1007
|
+
const { imports, maxImports, moduleManifest, diagnostics } = input;
|
|
1008
|
+
for (let i = 0; i < imports.length; i += 1) {
|
|
1009
|
+
if (input.isAborted()) {
|
|
1010
|
+
diagnostics.push({
|
|
1011
|
+
level: "error",
|
|
1012
|
+
code: "RUNTIME_ABORTED",
|
|
1013
|
+
message: "Execution aborted before import resolution"
|
|
1014
|
+
});
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
const specifier = imports[i];
|
|
1018
|
+
const resolvedSpecifier = input.resolveRuntimeSpecifier(
|
|
1019
|
+
specifier,
|
|
1020
|
+
moduleManifest,
|
|
1021
|
+
diagnostics
|
|
1022
|
+
);
|
|
1023
|
+
if (!resolvedSpecifier) {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
if (i >= maxImports) {
|
|
1027
|
+
diagnostics.push({
|
|
1028
|
+
level: "warning",
|
|
1029
|
+
code: "RUNTIME_IMPORT_LIMIT_EXCEEDED",
|
|
1030
|
+
message: `Import skipped because maxImports=${maxImports}: ${specifier}`
|
|
1031
|
+
});
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
if (!input.moduleLoader) {
|
|
1035
|
+
diagnostics.push({
|
|
1036
|
+
level: "warning",
|
|
1037
|
+
code: "RUNTIME_LOADER_MISSING",
|
|
1038
|
+
message: `Import skipped because no module loader is configured: ${resolvedSpecifier}`
|
|
1039
|
+
});
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
if (input.hasExceededBudget()) {
|
|
1043
|
+
diagnostics.push({
|
|
1044
|
+
level: "error",
|
|
1045
|
+
code: "RUNTIME_TIMEOUT",
|
|
1046
|
+
message: `Execution time budget exceeded before importing: ${specifier}`
|
|
1047
|
+
});
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
try {
|
|
1051
|
+
await input.withRemainingBudget(
|
|
1052
|
+
() => input.moduleLoader.load(resolvedSpecifier),
|
|
1053
|
+
`Import timed out: ${resolvedSpecifier}`
|
|
1054
|
+
);
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
if (input.isAbortError(error)) {
|
|
1057
|
+
diagnostics.push({
|
|
1058
|
+
level: "error",
|
|
1059
|
+
code: "RUNTIME_ABORTED",
|
|
1060
|
+
message: `Execution aborted during import: ${resolvedSpecifier}`
|
|
1061
|
+
});
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
diagnostics.push({
|
|
1065
|
+
level: "error",
|
|
1066
|
+
code: "RUNTIME_IMPORT_FAILED",
|
|
1067
|
+
message: `${resolvedSpecifier}: ${input.errorToMessage(error)}`
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// src/runtime-preflight.ts
|
|
1074
|
+
import {
|
|
1075
|
+
collectComponentModules
|
|
1076
|
+
} from "@renderify/ir";
|
|
1077
|
+
async function collectDependencyProbes(plan, parseSourceImportSpecifiers2) {
|
|
1078
|
+
const probes = [];
|
|
1079
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1080
|
+
const pushProbe = (usage, specifier) => {
|
|
1081
|
+
const trimmed = specifier.trim();
|
|
1082
|
+
if (trimmed.length === 0) {
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const key = `${usage}:${trimmed}`;
|
|
1086
|
+
if (seen.has(key)) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
seen.add(key);
|
|
1090
|
+
probes.push({
|
|
1091
|
+
usage,
|
|
1092
|
+
specifier: trimmed
|
|
1093
|
+
});
|
|
1094
|
+
};
|
|
1095
|
+
for (const specifier of plan.imports ?? []) {
|
|
1096
|
+
pushProbe("import", specifier);
|
|
1097
|
+
}
|
|
1098
|
+
for (const specifier of collectComponentModules(plan.root)) {
|
|
1099
|
+
pushProbe("component", specifier);
|
|
1100
|
+
}
|
|
1101
|
+
if (plan.source) {
|
|
1102
|
+
const sourceImports = await parseSourceImportSpecifiers2(plan.source.code);
|
|
1103
|
+
for (const specifier of sourceImports) {
|
|
1104
|
+
pushProbe("source-import", specifier);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return probes;
|
|
1108
|
+
}
|
|
1109
|
+
async function runDependencyPreflight(probes, diagnostics, probeExecutor, options) {
|
|
1110
|
+
const statuses = [];
|
|
1111
|
+
for (const probe of probes) {
|
|
1112
|
+
if (options.isAborted()) {
|
|
1113
|
+
diagnostics.push({
|
|
1114
|
+
level: "error",
|
|
1115
|
+
code: "RUNTIME_ABORTED",
|
|
1116
|
+
message: `Execution aborted during dependency preflight (${probe.usage}:${probe.specifier})`
|
|
1117
|
+
});
|
|
1118
|
+
statuses.push({
|
|
1119
|
+
usage: probe.usage,
|
|
1120
|
+
specifier: probe.specifier,
|
|
1121
|
+
ok: false,
|
|
1122
|
+
message: "Dependency preflight aborted"
|
|
1123
|
+
});
|
|
1124
|
+
return statuses;
|
|
1125
|
+
}
|
|
1126
|
+
if (options.hasExceededBudget()) {
|
|
1127
|
+
diagnostics.push({
|
|
1128
|
+
level: "error",
|
|
1129
|
+
code: "RUNTIME_TIMEOUT",
|
|
1130
|
+
message: `Execution time budget exceeded during dependency preflight (${probe.usage}:${probe.specifier})`
|
|
1131
|
+
});
|
|
1132
|
+
statuses.push({
|
|
1133
|
+
usage: probe.usage,
|
|
1134
|
+
specifier: probe.specifier,
|
|
1135
|
+
ok: false,
|
|
1136
|
+
message: "Dependency preflight timed out"
|
|
1137
|
+
});
|
|
1138
|
+
return statuses;
|
|
1139
|
+
}
|
|
1140
|
+
statuses.push(await probeExecutor(probe));
|
|
1141
|
+
}
|
|
1142
|
+
return statuses;
|
|
1143
|
+
}
|
|
1144
|
+
async function executeDependencyProbe(probe, moduleManifest, diagnostics, executor) {
|
|
1145
|
+
if (probe.usage === "source-import") {
|
|
1146
|
+
const resolved2 = executor.resolveRuntimeSourceSpecifier(
|
|
1147
|
+
probe.specifier,
|
|
1148
|
+
moduleManifest,
|
|
1149
|
+
diagnostics,
|
|
1150
|
+
false
|
|
1151
|
+
);
|
|
1152
|
+
if (resolved2.startsWith("./") || resolved2.startsWith("../") || resolved2.startsWith("/")) {
|
|
1153
|
+
diagnostics.push({
|
|
1154
|
+
level: "error",
|
|
1155
|
+
code: "RUNTIME_PREFLIGHT_SOURCE_IMPORT_RELATIVE_UNRESOLVED",
|
|
1156
|
+
message: `Runtime source entry import must resolve to URL or bare package alias: ${probe.specifier}`
|
|
1157
|
+
});
|
|
1158
|
+
return {
|
|
1159
|
+
usage: probe.usage,
|
|
1160
|
+
specifier: probe.specifier,
|
|
1161
|
+
resolvedSpecifier: resolved2,
|
|
1162
|
+
ok: false,
|
|
1163
|
+
message: "Relative source import could not be resolved"
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
const timeoutMessage = `Dependency preflight timed out: ${probe.specifier}`;
|
|
1167
|
+
const loaderCandidate = executor.resolveSourceImportLoaderCandidate(
|
|
1168
|
+
probe.specifier,
|
|
1169
|
+
moduleManifest
|
|
1170
|
+
);
|
|
1171
|
+
try {
|
|
1172
|
+
if (executor.moduleLoader && loaderCandidate) {
|
|
1173
|
+
await executor.withRemainingBudget(
|
|
1174
|
+
() => executor.moduleLoader.load(loaderCandidate),
|
|
1175
|
+
timeoutMessage
|
|
1176
|
+
);
|
|
1177
|
+
return {
|
|
1178
|
+
usage: probe.usage,
|
|
1179
|
+
specifier: probe.specifier,
|
|
1180
|
+
resolvedSpecifier: loaderCandidate,
|
|
1181
|
+
ok: true
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
if (executor.isHttpUrl(resolved2)) {
|
|
1185
|
+
await executor.withRemainingBudget(async () => {
|
|
1186
|
+
if (executor.canMaterializeBrowserModules()) {
|
|
1187
|
+
await executor.materializeBrowserRemoteModule(
|
|
1188
|
+
resolved2,
|
|
1189
|
+
moduleManifest,
|
|
1190
|
+
diagnostics
|
|
1191
|
+
);
|
|
1192
|
+
} else {
|
|
1193
|
+
await executor.fetchRemoteModuleCodeWithFallback(
|
|
1194
|
+
resolved2,
|
|
1195
|
+
diagnostics
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
}, timeoutMessage);
|
|
1199
|
+
return {
|
|
1200
|
+
usage: probe.usage,
|
|
1201
|
+
specifier: probe.specifier,
|
|
1202
|
+
resolvedSpecifier: resolved2,
|
|
1203
|
+
ok: true
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
if (!executor.moduleLoader) {
|
|
1207
|
+
diagnostics.push({
|
|
1208
|
+
level: "warning",
|
|
1209
|
+
code: "RUNTIME_PREFLIGHT_SKIPPED",
|
|
1210
|
+
message: `Dependency preflight skipped (no module loader): ${probe.usage}:${resolved2}`
|
|
1211
|
+
});
|
|
1212
|
+
return {
|
|
1213
|
+
usage: probe.usage,
|
|
1214
|
+
specifier: probe.specifier,
|
|
1215
|
+
resolvedSpecifier: resolved2,
|
|
1216
|
+
ok: false,
|
|
1217
|
+
message: "Dependency preflight skipped because source import is not loadable without module loader"
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
await executor.withRemainingBudget(
|
|
1221
|
+
() => executor.moduleLoader.load(resolved2),
|
|
1222
|
+
timeoutMessage
|
|
1223
|
+
);
|
|
1224
|
+
return {
|
|
1225
|
+
usage: probe.usage,
|
|
1226
|
+
specifier: probe.specifier,
|
|
1227
|
+
resolvedSpecifier: resolved2,
|
|
1228
|
+
ok: true
|
|
1229
|
+
};
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
if (executor.isAbortError(error)) {
|
|
1232
|
+
diagnostics.push({
|
|
1233
|
+
level: "error",
|
|
1234
|
+
code: "RUNTIME_ABORTED",
|
|
1235
|
+
message: `${probe.specifier}: dependency preflight aborted`
|
|
1236
|
+
});
|
|
1237
|
+
return {
|
|
1238
|
+
usage: probe.usage,
|
|
1239
|
+
specifier: probe.specifier,
|
|
1240
|
+
resolvedSpecifier: resolved2,
|
|
1241
|
+
ok: false,
|
|
1242
|
+
message: "Dependency preflight aborted"
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
diagnostics.push({
|
|
1246
|
+
level: "error",
|
|
1247
|
+
code: "RUNTIME_PREFLIGHT_SOURCE_IMPORT_FAILED",
|
|
1248
|
+
message: `${probe.specifier}: ${executor.errorToMessage(error)}`
|
|
1249
|
+
});
|
|
1250
|
+
return {
|
|
1251
|
+
usage: probe.usage,
|
|
1252
|
+
specifier: probe.specifier,
|
|
1253
|
+
resolvedSpecifier: resolved2,
|
|
1254
|
+
ok: false,
|
|
1255
|
+
message: executor.errorToMessage(error)
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
const resolved = executor.resolveRuntimeSpecifier(
|
|
1260
|
+
probe.specifier,
|
|
1261
|
+
moduleManifest,
|
|
1262
|
+
diagnostics,
|
|
1263
|
+
probe.usage
|
|
1264
|
+
);
|
|
1265
|
+
if (!resolved) {
|
|
1266
|
+
return {
|
|
1267
|
+
usage: probe.usage,
|
|
1268
|
+
specifier: probe.specifier,
|
|
1269
|
+
ok: false,
|
|
1270
|
+
message: "Module manifest resolution failed"
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
if (!executor.moduleLoader) {
|
|
1274
|
+
diagnostics.push({
|
|
1275
|
+
level: "warning",
|
|
1276
|
+
code: "RUNTIME_PREFLIGHT_SKIPPED",
|
|
1277
|
+
message: `Dependency preflight skipped (no module loader): ${probe.usage}:${resolved}`
|
|
1278
|
+
});
|
|
1279
|
+
return {
|
|
1280
|
+
usage: probe.usage,
|
|
1281
|
+
specifier: probe.specifier,
|
|
1282
|
+
resolvedSpecifier: resolved,
|
|
1283
|
+
ok: false,
|
|
1284
|
+
message: "Dependency preflight skipped because module loader is missing"
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
try {
|
|
1288
|
+
await executor.withRemainingBudget(
|
|
1289
|
+
() => executor.moduleLoader.load(resolved),
|
|
1290
|
+
`Dependency preflight timed out: ${resolved}`
|
|
1291
|
+
);
|
|
1292
|
+
return {
|
|
1293
|
+
usage: probe.usage,
|
|
1294
|
+
specifier: probe.specifier,
|
|
1295
|
+
resolvedSpecifier: resolved,
|
|
1296
|
+
ok: true
|
|
1297
|
+
};
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
if (executor.isAbortError(error)) {
|
|
1300
|
+
diagnostics.push({
|
|
1301
|
+
level: "error",
|
|
1302
|
+
code: "RUNTIME_ABORTED",
|
|
1303
|
+
message: `${resolved}: dependency preflight aborted`
|
|
1304
|
+
});
|
|
1305
|
+
return {
|
|
1306
|
+
usage: probe.usage,
|
|
1307
|
+
specifier: probe.specifier,
|
|
1308
|
+
resolvedSpecifier: resolved,
|
|
1309
|
+
ok: false,
|
|
1310
|
+
message: "Dependency preflight aborted"
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
diagnostics.push({
|
|
1314
|
+
level: "error",
|
|
1315
|
+
code: probe.usage === "component" ? "RUNTIME_PREFLIGHT_COMPONENT_FAILED" : "RUNTIME_PREFLIGHT_IMPORT_FAILED",
|
|
1316
|
+
message: `${resolved}: ${executor.errorToMessage(error)}`
|
|
1317
|
+
});
|
|
1318
|
+
return {
|
|
1319
|
+
usage: probe.usage,
|
|
1320
|
+
specifier: probe.specifier,
|
|
1321
|
+
resolvedSpecifier: resolved,
|
|
1322
|
+
ok: false,
|
|
1323
|
+
message: executor.errorToMessage(error)
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// src/runtime-source-utils.ts
|
|
1329
|
+
import {
|
|
1330
|
+
createElementNode as createElementNode2,
|
|
1331
|
+
createTextNode as createTextNode2,
|
|
1332
|
+
isRuntimeNode as isRuntimeNode3,
|
|
1333
|
+
parseRuntimeSourceImportRanges
|
|
1334
|
+
} from "@renderify/ir";
|
|
1335
|
+
function canMaterializeBrowserModules() {
|
|
1336
|
+
return typeof URL !== "undefined" && typeof URL.createObjectURL === "function" && typeof Blob !== "undefined" && typeof fetch === "function";
|
|
1337
|
+
}
|
|
1338
|
+
async function rewriteImportsAsync(code, resolver) {
|
|
1339
|
+
const imports = await parseImportSpecifiersFromSource(code);
|
|
1340
|
+
if (imports.length === 0) {
|
|
1341
|
+
return code;
|
|
1342
|
+
}
|
|
1343
|
+
let rewritten = "";
|
|
1344
|
+
let cursor = 0;
|
|
1345
|
+
for (const item of imports) {
|
|
1346
|
+
rewritten += code.slice(cursor, item.start);
|
|
1347
|
+
rewritten += await resolver(item.specifier);
|
|
1348
|
+
cursor = item.end;
|
|
1349
|
+
}
|
|
1350
|
+
rewritten += code.slice(cursor);
|
|
1351
|
+
return rewritten;
|
|
1352
|
+
}
|
|
1353
|
+
async function parseImportSpecifiersFromSource(source) {
|
|
1354
|
+
return parseRuntimeSourceImportRanges(source);
|
|
1355
|
+
}
|
|
1356
|
+
function createBrowserBlobModuleUrl(code, browserBlobUrls) {
|
|
1357
|
+
const blobUrl = URL.createObjectURL(
|
|
1358
|
+
new Blob([code], { type: "text/javascript" })
|
|
1359
|
+
);
|
|
1360
|
+
browserBlobUrls.add(blobUrl);
|
|
1361
|
+
return blobUrl;
|
|
1362
|
+
}
|
|
1363
|
+
function revokeBrowserBlobUrls(browserBlobUrls) {
|
|
1364
|
+
if (typeof URL === "undefined" || typeof URL.revokeObjectURL !== "function") {
|
|
1365
|
+
browserBlobUrls.clear();
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
for (const blobUrl of browserBlobUrls) {
|
|
1369
|
+
URL.revokeObjectURL(blobUrl);
|
|
1370
|
+
}
|
|
1371
|
+
browserBlobUrls.clear();
|
|
1372
|
+
}
|
|
1373
|
+
function normalizeRuntimeSourceOutput(output) {
|
|
1374
|
+
if (isRuntimeNode3(output)) {
|
|
1375
|
+
return output;
|
|
1376
|
+
}
|
|
1377
|
+
if (typeof output === "string" || typeof output === "number") {
|
|
1378
|
+
return createTextNode2(String(output));
|
|
1379
|
+
}
|
|
1380
|
+
if (Array.isArray(output)) {
|
|
1381
|
+
const normalizedChildren = output.map((entry) => normalizeRuntimeSourceOutput(entry)).filter((entry) => entry !== void 0);
|
|
1382
|
+
return createElementNode2(
|
|
1383
|
+
"div",
|
|
1384
|
+
{ "data-renderify-fragment": "true" },
|
|
1385
|
+
normalizedChildren
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
return void 0;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// src/runtime-specifier.ts
|
|
1392
|
+
function resolveRuntimeSourceSpecifier(input) {
|
|
1393
|
+
const trimmed = input.specifier.trim();
|
|
1394
|
+
const manifestResolved = resolveOptionalManifestSpecifier(
|
|
1395
|
+
trimmed,
|
|
1396
|
+
input.moduleManifest
|
|
1397
|
+
);
|
|
1398
|
+
if (manifestResolved && manifestResolved !== trimmed) {
|
|
1399
|
+
return resolveSourceSpecifierWithLoader(
|
|
1400
|
+
manifestResolved,
|
|
1401
|
+
input.moduleLoader
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
if (!shouldRewriteSpecifier(trimmed)) {
|
|
1405
|
+
return manifestResolved ?? trimmed;
|
|
1406
|
+
}
|
|
1407
|
+
const resolvedFromPolicy = input.requireManifest !== false ? resolveRuntimeSpecifier({
|
|
1408
|
+
specifier: trimmed,
|
|
1409
|
+
moduleManifest: input.moduleManifest,
|
|
1410
|
+
diagnostics: input.diagnostics,
|
|
1411
|
+
usage: "source-import",
|
|
1412
|
+
enforceModuleManifest: input.enforceModuleManifest
|
|
1413
|
+
}) : manifestResolved;
|
|
1414
|
+
if (!resolvedFromPolicy) {
|
|
1415
|
+
return trimmed;
|
|
1416
|
+
}
|
|
1417
|
+
const loaderResolved = resolveSourceSpecifierWithLoader(
|
|
1418
|
+
resolvedFromPolicy,
|
|
1419
|
+
input.moduleLoader
|
|
1420
|
+
);
|
|
1421
|
+
if (loaderResolved !== resolvedFromPolicy) {
|
|
1422
|
+
return loaderResolved;
|
|
1423
|
+
}
|
|
1424
|
+
const jspmBase = (input.jspmCdnBase ?? FALLBACK_JSPM_CDN_BASE).replace(
|
|
1425
|
+
/\/$/,
|
|
1426
|
+
""
|
|
1427
|
+
);
|
|
1428
|
+
if (resolvedFromPolicy.startsWith("npm:")) {
|
|
1429
|
+
return `${jspmBase}/${resolvedFromPolicy.slice(4)}`;
|
|
1430
|
+
}
|
|
1431
|
+
if (isDirectSpecifier(resolvedFromPolicy)) {
|
|
1432
|
+
return resolvedFromPolicy;
|
|
1433
|
+
}
|
|
1434
|
+
if (isBareSpecifier(resolvedFromPolicy)) {
|
|
1435
|
+
return `${jspmBase}/npm:${resolvedFromPolicy}`;
|
|
1436
|
+
}
|
|
1437
|
+
return `${jspmBase}/${resolvedFromPolicy}`;
|
|
1438
|
+
}
|
|
1439
|
+
function resolveRuntimeSpecifier(input) {
|
|
1440
|
+
const trimmed = input.specifier.trim();
|
|
1441
|
+
if (trimmed.length === 0) {
|
|
1442
|
+
input.diagnostics.push({
|
|
1443
|
+
level: "error",
|
|
1444
|
+
code: "RUNTIME_MANIFEST_INVALID",
|
|
1445
|
+
message: `Empty ${input.usage} specifier`
|
|
1446
|
+
});
|
|
1447
|
+
return void 0;
|
|
1448
|
+
}
|
|
1449
|
+
const descriptor = input.moduleManifest?.[trimmed];
|
|
1450
|
+
if (descriptor) {
|
|
1451
|
+
const resolved = descriptor.resolvedUrl.trim();
|
|
1452
|
+
if (resolved.length === 0) {
|
|
1453
|
+
input.diagnostics.push({
|
|
1454
|
+
level: "error",
|
|
1455
|
+
code: "RUNTIME_MANIFEST_INVALID",
|
|
1456
|
+
message: `Manifest entry has empty resolvedUrl for ${trimmed}`
|
|
1457
|
+
});
|
|
1458
|
+
return void 0;
|
|
1459
|
+
}
|
|
1460
|
+
return resolved;
|
|
1461
|
+
}
|
|
1462
|
+
if (!input.enforceModuleManifest || isDirectSpecifier(trimmed)) {
|
|
1463
|
+
return trimmed;
|
|
1464
|
+
}
|
|
1465
|
+
input.diagnostics.push({
|
|
1466
|
+
level: "error",
|
|
1467
|
+
code: "RUNTIME_MANIFEST_MISSING",
|
|
1468
|
+
message: `Missing moduleManifest entry for ${input.usage}: ${trimmed}`
|
|
1469
|
+
});
|
|
1470
|
+
return void 0;
|
|
1471
|
+
}
|
|
1472
|
+
function resolveSourceImportLoaderCandidate(specifier, moduleManifest, moduleLoader) {
|
|
1473
|
+
const manifestResolved = resolveOptionalManifestSpecifier(
|
|
1474
|
+
specifier,
|
|
1475
|
+
moduleManifest
|
|
1476
|
+
);
|
|
1477
|
+
const candidate = (manifestResolved ?? specifier).trim();
|
|
1478
|
+
if (candidate.length === 0) {
|
|
1479
|
+
return void 0;
|
|
1480
|
+
}
|
|
1481
|
+
if (candidate.startsWith("./") || candidate.startsWith("../") || candidate.startsWith("/")) {
|
|
1482
|
+
return void 0;
|
|
1483
|
+
}
|
|
1484
|
+
return resolveSourceSpecifierWithLoader(candidate, moduleLoader);
|
|
1485
|
+
}
|
|
1486
|
+
function resolveOptionalManifestSpecifier(specifier, moduleManifest) {
|
|
1487
|
+
const descriptor = moduleManifest?.[specifier];
|
|
1488
|
+
if (!descriptor) {
|
|
1489
|
+
return specifier;
|
|
1490
|
+
}
|
|
1491
|
+
const resolved = descriptor.resolvedUrl.trim();
|
|
1492
|
+
if (resolved.length === 0) {
|
|
1493
|
+
return void 0;
|
|
1494
|
+
}
|
|
1495
|
+
return resolved;
|
|
1496
|
+
}
|
|
1497
|
+
function resolveSourceSpecifierWithLoader(specifier, loader) {
|
|
1498
|
+
if (loader && hasResolveSpecifier(loader)) {
|
|
1499
|
+
try {
|
|
1500
|
+
return loader.resolveSpecifier(specifier);
|
|
1501
|
+
} catch {
|
|
1502
|
+
return specifier;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
return specifier;
|
|
1506
|
+
}
|
|
1507
|
+
function shouldRewriteSpecifier(specifier) {
|
|
1508
|
+
return !isDirectSpecifier(specifier);
|
|
1509
|
+
}
|
|
1510
|
+
function isDirectSpecifier(specifier) {
|
|
1511
|
+
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") || specifier.startsWith("http://") || specifier.startsWith("https://") || specifier.startsWith("blob:") || specifier.startsWith("data:");
|
|
1512
|
+
}
|
|
1513
|
+
function isHttpUrl(specifier) {
|
|
1514
|
+
return specifier.startsWith("http://") || specifier.startsWith("https://");
|
|
1515
|
+
}
|
|
1516
|
+
function isBareSpecifier(specifier) {
|
|
1517
|
+
return !isDirectSpecifier(specifier) && !specifier.startsWith("npm:");
|
|
1518
|
+
}
|
|
1519
|
+
function hasResolveSpecifier(loader) {
|
|
1520
|
+
if (typeof loader !== "object" || loader === null) {
|
|
1521
|
+
return false;
|
|
1522
|
+
}
|
|
1523
|
+
return "resolveSpecifier" in loader && typeof loader.resolveSpecifier === "function";
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// src/runtime-plan-preflight.ts
|
|
1527
|
+
async function preflightRuntimePlanDependencies(input) {
|
|
1528
|
+
const probes = await collectDependencyProbes(
|
|
1529
|
+
input.plan,
|
|
1530
|
+
parseSourceImportSpecifiers
|
|
1531
|
+
);
|
|
1532
|
+
return runDependencyPreflight(
|
|
1533
|
+
probes,
|
|
1534
|
+
input.diagnostics,
|
|
1535
|
+
(probe) => executeDependencyProbe(
|
|
1536
|
+
probe,
|
|
1537
|
+
input.plan.moduleManifest,
|
|
1538
|
+
input.diagnostics,
|
|
1539
|
+
{
|
|
1540
|
+
moduleLoader: input.moduleLoader,
|
|
1541
|
+
withRemainingBudget: (operation, timeoutMessage) => input.withRemainingBudget(operation, timeoutMessage),
|
|
1542
|
+
resolveRuntimeSourceSpecifier: (specifier, manifest, diagnostics, requireManifest) => input.resolveRuntimeSourceSpecifier(
|
|
1543
|
+
specifier,
|
|
1544
|
+
manifest,
|
|
1545
|
+
diagnostics,
|
|
1546
|
+
requireManifest
|
|
1547
|
+
),
|
|
1548
|
+
resolveSourceImportLoaderCandidate: (specifier, manifest) => input.resolveSourceImportLoaderCandidate(specifier, manifest),
|
|
1549
|
+
resolveRuntimeSpecifier: (specifier, manifest, diagnostics, usage) => input.resolveRuntimeSpecifier(
|
|
1550
|
+
specifier,
|
|
1551
|
+
manifest,
|
|
1552
|
+
diagnostics,
|
|
1553
|
+
usage
|
|
1554
|
+
),
|
|
1555
|
+
isHttpUrl,
|
|
1556
|
+
canMaterializeBrowserModules: () => canMaterializeBrowserModules(),
|
|
1557
|
+
materializeBrowserRemoteModule: (url, manifest, diagnostics) => input.materializeBrowserRemoteModule(url, manifest, diagnostics),
|
|
1558
|
+
fetchRemoteModuleCodeWithFallback: (url, diagnostics) => input.fetchRemoteModuleCodeWithFallback(url, diagnostics),
|
|
1559
|
+
isAbortError: (error) => input.isAbortError(error),
|
|
1560
|
+
errorToMessage: (error) => input.errorToMessage(error)
|
|
1561
|
+
}
|
|
1562
|
+
),
|
|
1563
|
+
{
|
|
1564
|
+
isAborted: () => input.isAborted(),
|
|
1565
|
+
hasExceededBudget: () => input.hasExceededBudget()
|
|
1566
|
+
}
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
async function parseSourceImportSpecifiers(code) {
|
|
1570
|
+
if (code.trim().length === 0) {
|
|
1571
|
+
return [];
|
|
1572
|
+
}
|
|
1573
|
+
const imports = /* @__PURE__ */ new Set();
|
|
1574
|
+
const parsedSpecifiers = await parseImportSpecifiersFromSource(code);
|
|
1575
|
+
for (const entry of parsedSpecifiers) {
|
|
1576
|
+
imports.add(entry.specifier);
|
|
1577
|
+
}
|
|
1578
|
+
return [...imports];
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// src/sandbox.ts
|
|
1582
|
+
async function executeSourceInBrowserSandbox(options) {
|
|
1583
|
+
throwIfAborted2(options.signal);
|
|
1584
|
+
if (options.mode === "worker") {
|
|
1585
|
+
return executeSourceInWorkerSandbox(options);
|
|
1586
|
+
}
|
|
1587
|
+
if (options.mode === "iframe") {
|
|
1588
|
+
return executeSourceInIframeSandbox(options);
|
|
1589
|
+
}
|
|
1590
|
+
throw new Error(`Unsupported runtime source sandbox mode: ${options.mode}`);
|
|
1591
|
+
}
|
|
1592
|
+
async function executeSourceInWorkerSandbox(options) {
|
|
1593
|
+
if (typeof Worker === "undefined" || typeof Blob === "undefined" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function" || typeof URL.revokeObjectURL !== "function") {
|
|
1594
|
+
throw new Error("Worker sandbox is unavailable in this runtime");
|
|
1595
|
+
}
|
|
1596
|
+
const workerSource = [
|
|
1597
|
+
"const CHANNEL = 'runtime-source';",
|
|
1598
|
+
"self.onmessage = async (event) => {",
|
|
1599
|
+
" const request = event.data;",
|
|
1600
|
+
" if (!request || request.renderifySandbox !== CHANNEL) {",
|
|
1601
|
+
" return;",
|
|
1602
|
+
" }",
|
|
1603
|
+
" const safeSend = (payload) => {",
|
|
1604
|
+
" try {",
|
|
1605
|
+
" self.postMessage({ renderifySandbox: CHANNEL, id: request.id, ...payload });",
|
|
1606
|
+
" return true;",
|
|
1607
|
+
" } catch (postError) {",
|
|
1608
|
+
" try {",
|
|
1609
|
+
" const postMessageError = postError && typeof postError === 'object' && 'message' in postError",
|
|
1610
|
+
" ? String(postError.message)",
|
|
1611
|
+
" : String(postError);",
|
|
1612
|
+
" self.postMessage({",
|
|
1613
|
+
" renderifySandbox: CHANNEL,",
|
|
1614
|
+
" id: request.id,",
|
|
1615
|
+
" ok: false,",
|
|
1616
|
+
" error: `Sandbox response is not serializable: ${postMessageError}`,",
|
|
1617
|
+
" });",
|
|
1618
|
+
" } catch {",
|
|
1619
|
+
" // Ignore terminal postMessage failures.",
|
|
1620
|
+
" }",
|
|
1621
|
+
" return false;",
|
|
1622
|
+
" }",
|
|
1623
|
+
" };",
|
|
1624
|
+
" try {",
|
|
1625
|
+
" const moduleUrl = URL.createObjectURL(new Blob([String(request.code ?? '')], { type: 'text/javascript' }));",
|
|
1626
|
+
" try {",
|
|
1627
|
+
" const namespace = await import(moduleUrl);",
|
|
1628
|
+
" const exportName = typeof request.exportName === 'string' && request.exportName.trim().length > 0",
|
|
1629
|
+
" ? request.exportName.trim()",
|
|
1630
|
+
" : 'default';",
|
|
1631
|
+
" const selected = namespace[exportName];",
|
|
1632
|
+
" if (selected === undefined) {",
|
|
1633
|
+
' throw new Error(`Runtime source export "${exportName}" is missing`);',
|
|
1634
|
+
" }",
|
|
1635
|
+
" const output = typeof selected === 'function'",
|
|
1636
|
+
" ? await selected(request.runtimeInput ?? {})",
|
|
1637
|
+
" : selected;",
|
|
1638
|
+
" safeSend({ ok: true, output });",
|
|
1639
|
+
" } finally {",
|
|
1640
|
+
" URL.revokeObjectURL(moduleUrl);",
|
|
1641
|
+
" }",
|
|
1642
|
+
" } catch (error) {",
|
|
1643
|
+
" const message = error && typeof error === 'object' && 'message' in error",
|
|
1644
|
+
" ? String(error.message)",
|
|
1645
|
+
" : String(error);",
|
|
1646
|
+
" safeSend({ ok: false, error: message });",
|
|
1647
|
+
" }",
|
|
1648
|
+
"};"
|
|
1649
|
+
].join("\n");
|
|
1650
|
+
const workerUrl = URL.createObjectURL(
|
|
1651
|
+
new Blob([workerSource], {
|
|
1652
|
+
type: "text/javascript"
|
|
1653
|
+
})
|
|
1654
|
+
);
|
|
1655
|
+
const worker = new Worker(workerUrl, {
|
|
1656
|
+
type: "module",
|
|
1657
|
+
name: "renderify-runtime-source-sandbox"
|
|
1658
|
+
});
|
|
1659
|
+
URL.revokeObjectURL(workerUrl);
|
|
1660
|
+
return new Promise((resolve, reject) => {
|
|
1661
|
+
let settled = false;
|
|
1662
|
+
let onAbort;
|
|
1663
|
+
const cleanup = () => {
|
|
1664
|
+
if (settled) {
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
settled = true;
|
|
1668
|
+
clearTimeout(timer);
|
|
1669
|
+
if (options.signal && onAbort) {
|
|
1670
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
1671
|
+
}
|
|
1672
|
+
worker.removeEventListener("message", onMessage);
|
|
1673
|
+
worker.removeEventListener("error", onError);
|
|
1674
|
+
};
|
|
1675
|
+
const timer = setTimeout(() => {
|
|
1676
|
+
cleanup();
|
|
1677
|
+
worker.terminate();
|
|
1678
|
+
reject(new Error("Worker sandbox timed out"));
|
|
1679
|
+
}, options.timeoutMs);
|
|
1680
|
+
const onMessage = (event) => {
|
|
1681
|
+
const payload = event.data;
|
|
1682
|
+
if (!isRuntimeSandboxResponse(payload, options.request.id)) {
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
cleanup();
|
|
1686
|
+
worker.terminate();
|
|
1687
|
+
if (!payload.ok) {
|
|
1688
|
+
reject(new Error(payload.error ?? "Worker sandbox execution failed"));
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
resolve({
|
|
1692
|
+
output: payload.output
|
|
1693
|
+
});
|
|
1694
|
+
};
|
|
1695
|
+
const onError = (event) => {
|
|
1696
|
+
cleanup();
|
|
1697
|
+
worker.terminate();
|
|
1698
|
+
reject(
|
|
1699
|
+
new Error(
|
|
1700
|
+
event.message || "Worker sandbox terminated with an unknown error"
|
|
1701
|
+
)
|
|
1702
|
+
);
|
|
1703
|
+
};
|
|
1704
|
+
worker.addEventListener("message", onMessage);
|
|
1705
|
+
worker.addEventListener("error", onError);
|
|
1706
|
+
if (options.signal) {
|
|
1707
|
+
if (options.signal.aborted) {
|
|
1708
|
+
cleanup();
|
|
1709
|
+
worker.terminate();
|
|
1710
|
+
reject(createAbortError2("Worker sandbox execution aborted"));
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
onAbort = () => {
|
|
1714
|
+
cleanup();
|
|
1715
|
+
worker.terminate();
|
|
1716
|
+
reject(createAbortError2("Worker sandbox execution aborted"));
|
|
1717
|
+
};
|
|
1718
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
1719
|
+
}
|
|
1720
|
+
worker.postMessage(options.request);
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
async function executeSourceInIframeSandbox(options) {
|
|
1724
|
+
if (typeof document === "undefined" || typeof window === "undefined" || typeof Blob === "undefined" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function" || typeof URL.revokeObjectURL !== "function") {
|
|
1725
|
+
throw new Error("Iframe sandbox is unavailable in this runtime");
|
|
1726
|
+
}
|
|
1727
|
+
const iframe = document.createElement("iframe");
|
|
1728
|
+
iframe.setAttribute("sandbox", "allow-scripts");
|
|
1729
|
+
iframe.style.display = "none";
|
|
1730
|
+
const channel = `renderify-runtime-source-${options.request.id}`;
|
|
1731
|
+
const channelLiteral = JSON.stringify(channel);
|
|
1732
|
+
iframe.srcdoc = [
|
|
1733
|
+
"<!doctype html><html><body><script>",
|
|
1734
|
+
`const CHANNEL = ${channelLiteral};`,
|
|
1735
|
+
"window.addEventListener('message', async (event) => {",
|
|
1736
|
+
" const data = event.data;",
|
|
1737
|
+
" if (!data || data.channel !== CHANNEL) {",
|
|
1738
|
+
" return;",
|
|
1739
|
+
" }",
|
|
1740
|
+
" const request = data.request || {};",
|
|
1741
|
+
" const safeSend = (payload) => {",
|
|
1742
|
+
" try {",
|
|
1743
|
+
" parent.postMessage({ channel: CHANNEL, ...payload }, '*');",
|
|
1744
|
+
" return true;",
|
|
1745
|
+
" } catch (postError) {",
|
|
1746
|
+
" try {",
|
|
1747
|
+
" const postMessageError = postError && typeof postError === 'object' && 'message' in postError",
|
|
1748
|
+
" ? String(postError.message)",
|
|
1749
|
+
" : String(postError);",
|
|
1750
|
+
" parent.postMessage({",
|
|
1751
|
+
" channel: CHANNEL,",
|
|
1752
|
+
" ok: false,",
|
|
1753
|
+
" error: `Sandbox response is not serializable: ${postMessageError}`,",
|
|
1754
|
+
" }, '*');",
|
|
1755
|
+
" } catch {",
|
|
1756
|
+
" // Ignore terminal postMessage failures.",
|
|
1757
|
+
" }",
|
|
1758
|
+
" return false;",
|
|
1759
|
+
" }",
|
|
1760
|
+
" };",
|
|
1761
|
+
" try {",
|
|
1762
|
+
" const moduleUrl = URL.createObjectURL(new Blob([String(request.code ?? '')], { type: 'text/javascript' }));",
|
|
1763
|
+
" try {",
|
|
1764
|
+
" const namespace = await import(moduleUrl);",
|
|
1765
|
+
" const exportName = typeof request.exportName === 'string' && request.exportName.trim().length > 0",
|
|
1766
|
+
" ? request.exportName.trim()",
|
|
1767
|
+
" : 'default';",
|
|
1768
|
+
" const selected = namespace[exportName];",
|
|
1769
|
+
" if (selected === undefined) {",
|
|
1770
|
+
' throw new Error(`Runtime source export "${exportName}" is missing`);',
|
|
1771
|
+
" }",
|
|
1772
|
+
" const output = typeof selected === 'function'",
|
|
1773
|
+
" ? await selected(request.runtimeInput ?? {})",
|
|
1774
|
+
" : selected;",
|
|
1775
|
+
" safeSend({ ok: true, output });",
|
|
1776
|
+
" } finally {",
|
|
1777
|
+
" URL.revokeObjectURL(moduleUrl);",
|
|
1778
|
+
" }",
|
|
1779
|
+
" } catch (error) {",
|
|
1780
|
+
" const message = error && typeof error === 'object' && 'message' in error",
|
|
1781
|
+
" ? String(error.message)",
|
|
1782
|
+
" : String(error);",
|
|
1783
|
+
" safeSend({ ok: false, error: message });",
|
|
1784
|
+
" }",
|
|
1785
|
+
"});",
|
|
1786
|
+
"</script></body></html>"
|
|
1787
|
+
].join("");
|
|
1788
|
+
document.body.appendChild(iframe);
|
|
1789
|
+
return new Promise((resolve, reject) => {
|
|
1790
|
+
let settled = false;
|
|
1791
|
+
let onAbort;
|
|
1792
|
+
const cleanup = () => {
|
|
1793
|
+
if (settled) {
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
settled = true;
|
|
1797
|
+
clearTimeout(timer);
|
|
1798
|
+
if (options.signal && onAbort) {
|
|
1799
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
1800
|
+
}
|
|
1801
|
+
window.removeEventListener("message", onMessage);
|
|
1802
|
+
iframe.removeEventListener("load", onLoad);
|
|
1803
|
+
iframe.remove();
|
|
1804
|
+
};
|
|
1805
|
+
const timer = setTimeout(() => {
|
|
1806
|
+
cleanup();
|
|
1807
|
+
reject(new Error("Iframe sandbox timed out"));
|
|
1808
|
+
}, options.timeoutMs);
|
|
1809
|
+
const onMessage = (event) => {
|
|
1810
|
+
if (event.source !== iframe.contentWindow) {
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
const data = event.data;
|
|
1814
|
+
if (!data || data.channel !== channel) {
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
cleanup();
|
|
1818
|
+
if (!data.ok) {
|
|
1819
|
+
reject(new Error(data.error ?? "Iframe sandbox execution failed"));
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
resolve({
|
|
1823
|
+
output: data.output
|
|
1824
|
+
});
|
|
1825
|
+
};
|
|
1826
|
+
const onLoad = () => {
|
|
1827
|
+
iframe.contentWindow?.postMessage(
|
|
1828
|
+
{ channel, request: options.request },
|
|
1829
|
+
"*"
|
|
1830
|
+
);
|
|
1831
|
+
};
|
|
1832
|
+
window.addEventListener("message", onMessage);
|
|
1833
|
+
iframe.addEventListener("load", onLoad, { once: true });
|
|
1834
|
+
if (options.signal) {
|
|
1835
|
+
if (options.signal.aborted) {
|
|
1836
|
+
cleanup();
|
|
1837
|
+
reject(createAbortError2("Iframe sandbox execution aborted"));
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
onAbort = () => {
|
|
1841
|
+
cleanup();
|
|
1842
|
+
reject(createAbortError2("Iframe sandbox execution aborted"));
|
|
1843
|
+
};
|
|
1844
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
1845
|
+
}
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
function isRuntimeSandboxResponse(value, expectedId) {
|
|
1849
|
+
if (typeof value !== "object" || value === null) {
|
|
1850
|
+
return false;
|
|
1851
|
+
}
|
|
1852
|
+
const candidate = value;
|
|
1853
|
+
return candidate.renderifySandbox === "runtime-source" && candidate.id === expectedId && typeof candidate.ok === "boolean";
|
|
1854
|
+
}
|
|
1855
|
+
function throwIfAborted2(signal) {
|
|
1856
|
+
if (!signal?.aborted) {
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
throw createAbortError2("Runtime sandbox execution aborted");
|
|
1860
|
+
}
|
|
1861
|
+
function createAbortError2(message) {
|
|
1862
|
+
const error = new Error(message);
|
|
1863
|
+
error.name = "AbortError";
|
|
1864
|
+
return error;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
// src/runtime-source-execution.ts
|
|
1868
|
+
async function executeRuntimeSourceRoot(input) {
|
|
1869
|
+
const {
|
|
1870
|
+
plan,
|
|
1871
|
+
source,
|
|
1872
|
+
context,
|
|
1873
|
+
state,
|
|
1874
|
+
event,
|
|
1875
|
+
diagnostics,
|
|
1876
|
+
frame,
|
|
1877
|
+
browserSourceSandboxTimeoutMs,
|
|
1878
|
+
browserSourceSandboxFailClosed
|
|
1879
|
+
} = input;
|
|
1880
|
+
try {
|
|
1881
|
+
const exportName = source.exportName ?? "default";
|
|
1882
|
+
const runtimeInput = {
|
|
1883
|
+
context: input.cloneJsonValue(input.asJsonValue(context)),
|
|
1884
|
+
state: input.cloneJsonValue(state),
|
|
1885
|
+
event: event ? input.cloneJsonValue(input.asJsonValue(event)) : null
|
|
1886
|
+
};
|
|
1887
|
+
const transpiled = await input.withRemainingBudget(
|
|
1888
|
+
() => input.transpileRuntimeSource(source),
|
|
1889
|
+
"Runtime source transpilation timed out"
|
|
1890
|
+
);
|
|
1891
|
+
const rewritten = await input.rewriteSourceImports(
|
|
1892
|
+
transpiled,
|
|
1893
|
+
plan.moduleManifest,
|
|
1894
|
+
diagnostics
|
|
1895
|
+
);
|
|
1896
|
+
const sandboxMode = input.resolveSourceSandboxMode(
|
|
1897
|
+
source,
|
|
1898
|
+
frame.executionProfile
|
|
1899
|
+
);
|
|
1900
|
+
if (sandboxMode !== "none") {
|
|
1901
|
+
try {
|
|
1902
|
+
const sandboxResult = await input.withRemainingBudget(
|
|
1903
|
+
() => executeSourceInBrowserSandbox({
|
|
1904
|
+
mode: sandboxMode,
|
|
1905
|
+
timeoutMs: browserSourceSandboxTimeoutMs,
|
|
1906
|
+
signal: frame.signal,
|
|
1907
|
+
request: {
|
|
1908
|
+
renderifySandbox: "runtime-source",
|
|
1909
|
+
id: `sandbox_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`,
|
|
1910
|
+
code: rewritten,
|
|
1911
|
+
exportName,
|
|
1912
|
+
runtimeInput
|
|
1913
|
+
}
|
|
1914
|
+
}),
|
|
1915
|
+
`Runtime source sandbox (${sandboxMode}) timed out`
|
|
1916
|
+
);
|
|
1917
|
+
const normalized2 = input.normalizeSourceOutput(sandboxResult.output);
|
|
1918
|
+
if (!normalized2) {
|
|
1919
|
+
diagnostics.push({
|
|
1920
|
+
level: "error",
|
|
1921
|
+
code: "RUNTIME_SOURCE_OUTPUT_INVALID",
|
|
1922
|
+
message: "Runtime source output from sandbox is not a supported RuntimeNode payload"
|
|
1923
|
+
});
|
|
1924
|
+
return void 0;
|
|
1925
|
+
}
|
|
1926
|
+
diagnostics.push({
|
|
1927
|
+
level: "info",
|
|
1928
|
+
code: "RUNTIME_SOURCE_SANDBOX_EXECUTED",
|
|
1929
|
+
message: `Runtime source executed in ${sandboxMode} sandbox`
|
|
1930
|
+
});
|
|
1931
|
+
return {
|
|
1932
|
+
root: normalized2
|
|
1933
|
+
};
|
|
1934
|
+
} catch (error) {
|
|
1935
|
+
if (input.isAbortError(error)) {
|
|
1936
|
+
throw error;
|
|
1937
|
+
}
|
|
1938
|
+
const message = input.errorToMessage(error);
|
|
1939
|
+
diagnostics.push({
|
|
1940
|
+
level: browserSourceSandboxFailClosed ? "error" : "warning",
|
|
1941
|
+
code: browserSourceSandboxFailClosed ? "RUNTIME_SOURCE_SANDBOX_FAILED" : "RUNTIME_SOURCE_SANDBOX_FALLBACK",
|
|
1942
|
+
message
|
|
1943
|
+
});
|
|
1944
|
+
if (browserSourceSandboxFailClosed) {
|
|
1945
|
+
throw new Error(
|
|
1946
|
+
`Runtime source sandbox (${sandboxMode}) failed: ${message}`
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
const namespace = await input.withRemainingBudget(
|
|
1952
|
+
() => input.importSourceModuleFromCode(
|
|
1953
|
+
rewritten,
|
|
1954
|
+
plan.moduleManifest,
|
|
1955
|
+
diagnostics
|
|
1956
|
+
),
|
|
1957
|
+
"Runtime source module loading timed out"
|
|
1958
|
+
);
|
|
1959
|
+
const selected = selectExportFromNamespace(namespace, exportName);
|
|
1960
|
+
if (selected === void 0) {
|
|
1961
|
+
diagnostics.push({
|
|
1962
|
+
level: "error",
|
|
1963
|
+
code: "RUNTIME_SOURCE_EXPORT_MISSING",
|
|
1964
|
+
message: `Runtime source export "${exportName}" is missing`
|
|
1965
|
+
});
|
|
1966
|
+
return void 0;
|
|
1967
|
+
}
|
|
1968
|
+
if (input.shouldUsePreactSourceRuntime(source)) {
|
|
1969
|
+
const preactArtifact = await input.createPreactRenderArtifact({
|
|
1970
|
+
sourceExport: selected,
|
|
1971
|
+
runtimeInput,
|
|
1972
|
+
diagnostics
|
|
1973
|
+
});
|
|
1974
|
+
if (preactArtifact) {
|
|
1975
|
+
return {
|
|
1976
|
+
renderArtifact: preactArtifact
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
const produced = typeof selected === "function" ? await input.withRemainingBudget(
|
|
1981
|
+
async () => selected(runtimeInput),
|
|
1982
|
+
"Runtime source export execution timed out"
|
|
1983
|
+
) : selected;
|
|
1984
|
+
const normalized = input.normalizeSourceOutput(produced);
|
|
1985
|
+
if (!normalized) {
|
|
1986
|
+
diagnostics.push({
|
|
1987
|
+
level: "error",
|
|
1988
|
+
code: "RUNTIME_SOURCE_OUTPUT_INVALID",
|
|
1989
|
+
message: "Runtime source output is not a supported RuntimeNode payload"
|
|
1990
|
+
});
|
|
1991
|
+
return void 0;
|
|
1992
|
+
}
|
|
1993
|
+
return {
|
|
1994
|
+
root: normalized
|
|
1995
|
+
};
|
|
1996
|
+
} catch (error) {
|
|
1997
|
+
if (input.isAbortError(error)) {
|
|
1998
|
+
throw error;
|
|
1999
|
+
}
|
|
2000
|
+
diagnostics.push({
|
|
2001
|
+
level: "error",
|
|
2002
|
+
code: "RUNTIME_SOURCE_EXEC_FAILED",
|
|
2003
|
+
message: input.errorToMessage(error)
|
|
2004
|
+
});
|
|
2005
|
+
return void 0;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// src/runtime-source-module-loader.ts
|
|
2010
|
+
var RuntimeSourceModuleLoader = class {
|
|
2011
|
+
moduleManifest;
|
|
2012
|
+
diagnostics;
|
|
2013
|
+
browserModuleUrlCache;
|
|
2014
|
+
browserModuleInflight;
|
|
2015
|
+
remoteFallbackCdnBases;
|
|
2016
|
+
remoteFetchTimeoutMs;
|
|
2017
|
+
remoteFetchRetries;
|
|
2018
|
+
remoteFetchBackoffMs;
|
|
2019
|
+
canMaterializeBrowserModulesFn;
|
|
2020
|
+
rewriteImportsAsyncFn;
|
|
2021
|
+
createBrowserBlobModuleUrlFn;
|
|
2022
|
+
resolveRuntimeSourceSpecifierFn;
|
|
2023
|
+
constructor(options) {
|
|
2024
|
+
this.moduleManifest = options.moduleManifest;
|
|
2025
|
+
this.diagnostics = options.diagnostics;
|
|
2026
|
+
this.browserModuleUrlCache = options.browserModuleUrlCache;
|
|
2027
|
+
this.browserModuleInflight = options.browserModuleInflight;
|
|
2028
|
+
this.remoteFallbackCdnBases = options.remoteFallbackCdnBases;
|
|
2029
|
+
this.remoteFetchTimeoutMs = options.remoteFetchTimeoutMs;
|
|
2030
|
+
this.remoteFetchRetries = options.remoteFetchRetries;
|
|
2031
|
+
this.remoteFetchBackoffMs = options.remoteFetchBackoffMs;
|
|
2032
|
+
this.canMaterializeBrowserModulesFn = options.canMaterializeBrowserModules;
|
|
2033
|
+
this.rewriteImportsAsyncFn = options.rewriteImportsAsync;
|
|
2034
|
+
this.createBrowserBlobModuleUrlFn = options.createBrowserBlobModuleUrl;
|
|
2035
|
+
this.resolveRuntimeSourceSpecifierFn = options.resolveRuntimeSourceSpecifier;
|
|
2036
|
+
}
|
|
2037
|
+
async importSourceModuleFromCode(code) {
|
|
2038
|
+
const isNodeRuntime = typeof process !== "undefined" && process !== null && typeof process.versions === "object" && process.versions !== null && typeof process.versions.node === "string";
|
|
2039
|
+
if (isNodeRuntime && typeof Buffer !== "undefined") {
|
|
2040
|
+
const encoded = Buffer.from(code, "utf8").toString("base64");
|
|
2041
|
+
const dataUrl = `data:text/javascript;base64,${encoded}`;
|
|
2042
|
+
return import(
|
|
2043
|
+
/* webpackIgnore: true */
|
|
2044
|
+
dataUrl
|
|
2045
|
+
);
|
|
2046
|
+
}
|
|
2047
|
+
if (this.canMaterializeBrowserModulesFn()) {
|
|
2048
|
+
const rewrittenEntry = await this.rewriteImportsAsyncFn(
|
|
2049
|
+
code,
|
|
2050
|
+
async (specifier) => this.resolveBrowserImportSpecifier(specifier, void 0)
|
|
2051
|
+
);
|
|
2052
|
+
const entryUrl = this.createBrowserBlobModuleUrlFn(rewrittenEntry);
|
|
2053
|
+
return import(
|
|
2054
|
+
/* webpackIgnore: true */
|
|
2055
|
+
entryUrl
|
|
2056
|
+
);
|
|
2057
|
+
}
|
|
2058
|
+
if (typeof Buffer !== "undefined") {
|
|
2059
|
+
const encoded = Buffer.from(code, "utf8").toString("base64");
|
|
2060
|
+
const dataUrl = `data:text/javascript;base64,${encoded}`;
|
|
2061
|
+
return import(
|
|
2062
|
+
/* webpackIgnore: true */
|
|
2063
|
+
dataUrl
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
throw new Error("No runtime module import strategy is available");
|
|
2067
|
+
}
|
|
2068
|
+
async resolveBrowserImportSpecifier(specifier, parentUrl) {
|
|
2069
|
+
const trimmed = specifier.trim();
|
|
2070
|
+
if (trimmed.length === 0) {
|
|
2071
|
+
return trimmed;
|
|
2072
|
+
}
|
|
2073
|
+
if (trimmed.startsWith("data:") || trimmed.startsWith("blob:")) {
|
|
2074
|
+
return trimmed;
|
|
2075
|
+
}
|
|
2076
|
+
if (isHttpUrl(trimmed)) {
|
|
2077
|
+
return this.materializeBrowserRemoteModule(trimmed);
|
|
2078
|
+
}
|
|
2079
|
+
if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/")) {
|
|
2080
|
+
if (!parentUrl || !isHttpUrl(parentUrl)) {
|
|
2081
|
+
this.diagnostics.push({
|
|
2082
|
+
level: "warning",
|
|
2083
|
+
code: "RUNTIME_SOURCE_IMPORT_UNRESOLVED",
|
|
2084
|
+
message: `Cannot resolve relative source import without parent URL: ${trimmed}`
|
|
2085
|
+
});
|
|
2086
|
+
return trimmed;
|
|
2087
|
+
}
|
|
2088
|
+
const absolute = new URL(trimmed, parentUrl).toString();
|
|
2089
|
+
if (!isHttpUrl(absolute)) {
|
|
2090
|
+
return absolute;
|
|
2091
|
+
}
|
|
2092
|
+
return this.materializeBrowserRemoteModule(absolute);
|
|
2093
|
+
}
|
|
2094
|
+
const resolved = this.resolveRuntimeSourceSpecifierFn(
|
|
2095
|
+
trimmed,
|
|
2096
|
+
this.moduleManifest,
|
|
2097
|
+
this.diagnostics,
|
|
2098
|
+
false
|
|
2099
|
+
);
|
|
2100
|
+
if (isHttpUrl(resolved)) {
|
|
2101
|
+
return this.materializeBrowserRemoteModule(resolved);
|
|
2102
|
+
}
|
|
2103
|
+
if ((resolved.startsWith("./") || resolved.startsWith("../") || resolved.startsWith("/")) && parentUrl && isHttpUrl(parentUrl)) {
|
|
2104
|
+
const absolute = new URL(resolved, parentUrl).toString();
|
|
2105
|
+
if (!isHttpUrl(absolute)) {
|
|
2106
|
+
return absolute;
|
|
2107
|
+
}
|
|
2108
|
+
return this.materializeBrowserRemoteModule(absolute);
|
|
2109
|
+
}
|
|
2110
|
+
return resolved;
|
|
2111
|
+
}
|
|
2112
|
+
async materializeBrowserRemoteModule(url) {
|
|
2113
|
+
const normalizedUrl = url.trim();
|
|
2114
|
+
if (normalizedUrl.length === 0) {
|
|
2115
|
+
return normalizedUrl;
|
|
2116
|
+
}
|
|
2117
|
+
const cachedUrl = this.browserModuleUrlCache.get(normalizedUrl);
|
|
2118
|
+
if (cachedUrl) {
|
|
2119
|
+
return cachedUrl;
|
|
2120
|
+
}
|
|
2121
|
+
const inflight = this.browserModuleInflight.get(normalizedUrl);
|
|
2122
|
+
if (inflight) {
|
|
2123
|
+
return inflight;
|
|
2124
|
+
}
|
|
2125
|
+
const loading = (async () => {
|
|
2126
|
+
const fetched = await this.fetchRemoteModuleCodeWithFallback(normalizedUrl);
|
|
2127
|
+
const rewritten = await this.materializeFetchedModuleSource(fetched);
|
|
2128
|
+
const blobUrl = this.createBrowserBlobModuleUrlFn(rewritten);
|
|
2129
|
+
this.browserModuleUrlCache.set(normalizedUrl, blobUrl);
|
|
2130
|
+
this.browserModuleUrlCache.set(fetched.url, blobUrl);
|
|
2131
|
+
return blobUrl;
|
|
2132
|
+
})();
|
|
2133
|
+
this.browserModuleInflight.set(normalizedUrl, loading);
|
|
2134
|
+
try {
|
|
2135
|
+
return await loading;
|
|
2136
|
+
} finally {
|
|
2137
|
+
this.browserModuleInflight.delete(normalizedUrl);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
async materializeFetchedModuleSource(fetched) {
|
|
2141
|
+
if (isCssModuleResponse(fetched)) {
|
|
2142
|
+
return createCssProxyModuleSource(fetched.code, fetched.url);
|
|
2143
|
+
}
|
|
2144
|
+
if (isJsonModuleResponse(fetched)) {
|
|
2145
|
+
return this.createJsonProxyModuleSource(fetched);
|
|
2146
|
+
}
|
|
2147
|
+
if (!isJavaScriptModuleResponse(fetched)) {
|
|
2148
|
+
this.diagnostics.push({
|
|
2149
|
+
level: "warning",
|
|
2150
|
+
code: "RUNTIME_SOURCE_ASSET_PROXY",
|
|
2151
|
+
message: `Treating non-JS module as proxied asset: ${fetched.url} (${fetched.contentType || "unknown"})`
|
|
2152
|
+
});
|
|
2153
|
+
if (isBinaryLikeContentType(fetched.contentType)) {
|
|
2154
|
+
return createUrlProxyModuleSource(fetched.url);
|
|
2155
|
+
}
|
|
2156
|
+
return createTextProxyModuleSource(fetched.code);
|
|
2157
|
+
}
|
|
2158
|
+
return this.rewriteImportsAsyncFn(
|
|
2159
|
+
fetched.code,
|
|
2160
|
+
async (childSpecifier) => this.resolveBrowserImportSpecifier(childSpecifier, fetched.url)
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
async fetchRemoteModuleCodeWithFallback(url) {
|
|
2164
|
+
const attempts = buildRemoteModuleAttemptUrls(
|
|
2165
|
+
url,
|
|
2166
|
+
this.remoteFallbackCdnBases
|
|
2167
|
+
);
|
|
2168
|
+
if (attempts.length === 0) {
|
|
2169
|
+
throw new Error(`Failed to load module: ${url}`);
|
|
2170
|
+
}
|
|
2171
|
+
const hedgeDelayMs = Math.max(
|
|
2172
|
+
50,
|
|
2173
|
+
Math.min(300, this.remoteFetchBackoffMs || 100)
|
|
2174
|
+
);
|
|
2175
|
+
const fetchTasks = attempts.map(
|
|
2176
|
+
(attempt, index) => this.fetchRemoteModuleAttemptWithRetries(
|
|
2177
|
+
attempt,
|
|
2178
|
+
url,
|
|
2179
|
+
index === 0 ? 0 : hedgeDelayMs * index
|
|
2180
|
+
)
|
|
2181
|
+
);
|
|
2182
|
+
try {
|
|
2183
|
+
return await Promise.any(fetchTasks);
|
|
2184
|
+
} catch (error) {
|
|
2185
|
+
if (error instanceof AggregateError && error.errors.length > 0) {
|
|
2186
|
+
throw error.errors[error.errors.length - 1];
|
|
2187
|
+
}
|
|
2188
|
+
throw error;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
async fetchRemoteModuleAttemptWithRetries(attempt, originalUrl, startDelayMs) {
|
|
2192
|
+
if (startDelayMs > 0) {
|
|
2193
|
+
await delay(startDelayMs);
|
|
2194
|
+
}
|
|
2195
|
+
let lastError;
|
|
2196
|
+
for (let retry = 0; retry <= this.remoteFetchRetries; retry += 1) {
|
|
2197
|
+
try {
|
|
2198
|
+
const response = await fetchWithTimeout(
|
|
2199
|
+
attempt,
|
|
2200
|
+
this.remoteFetchTimeoutMs
|
|
2201
|
+
);
|
|
2202
|
+
if (!response.ok) {
|
|
2203
|
+
throw new Error(
|
|
2204
|
+
`Failed to load module ${attempt}: HTTP ${response.status}`
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
if (attempt !== originalUrl) {
|
|
2208
|
+
this.diagnostics.push({
|
|
2209
|
+
level: "warning",
|
|
2210
|
+
code: "RUNTIME_SOURCE_IMPORT_FALLBACK_USED",
|
|
2211
|
+
message: `Loaded module via fallback URL: ${originalUrl} -> ${attempt}`
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
if (retry > 0) {
|
|
2215
|
+
this.diagnostics.push({
|
|
2216
|
+
level: "warning",
|
|
2217
|
+
code: "RUNTIME_SOURCE_IMPORT_RETRY_SUCCEEDED",
|
|
2218
|
+
message: `Recovered remote module after retry ${retry}: ${attempt}`
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
return {
|
|
2222
|
+
url: response.url || attempt,
|
|
2223
|
+
code: await response.text(),
|
|
2224
|
+
contentType: response.headers.get("content-type")?.toLowerCase() ?? "",
|
|
2225
|
+
requestUrl: attempt
|
|
2226
|
+
};
|
|
2227
|
+
} catch (error) {
|
|
2228
|
+
lastError = error;
|
|
2229
|
+
if (retry >= this.remoteFetchRetries) {
|
|
2230
|
+
break;
|
|
2231
|
+
}
|
|
2232
|
+
await delay(this.remoteFetchBackoffMs * Math.max(1, retry + 1));
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
throw lastError ?? new Error(`Failed to load module: ${attempt}`);
|
|
2236
|
+
}
|
|
2237
|
+
createJsonProxyModuleSource(fetched) {
|
|
2238
|
+
try {
|
|
2239
|
+
const parsed = JSON.parse(fetched.code);
|
|
2240
|
+
return createJsonProxyModuleSource(parsed);
|
|
2241
|
+
} catch (error) {
|
|
2242
|
+
this.diagnostics.push({
|
|
2243
|
+
level: "warning",
|
|
2244
|
+
code: "RUNTIME_SOURCE_JSON_PARSE_FAILED",
|
|
2245
|
+
message: `${fetched.requestUrl}: ${this.errorToMessage(error)}`
|
|
2246
|
+
});
|
|
2247
|
+
return createTextProxyModuleSource(fetched.code);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
errorToMessage(error) {
|
|
2251
|
+
if (error instanceof Error) {
|
|
2252
|
+
return error.message;
|
|
2253
|
+
}
|
|
2254
|
+
return String(error);
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
|
|
2258
|
+
// src/transpiler.ts
|
|
2259
|
+
var RUNTIME_JSX_HELPERS = `
|
|
2260
|
+
function __renderify_runtime_to_nodes(value) {
|
|
2261
|
+
if (value === null || value === undefined || value === false || value === true) {
|
|
2262
|
+
return [];
|
|
2263
|
+
}
|
|
2264
|
+
if (Array.isArray(value)) {
|
|
2265
|
+
const flattened = [];
|
|
2266
|
+
for (const entry of value) {
|
|
2267
|
+
flattened.push(...__renderify_runtime_to_nodes(entry));
|
|
2268
|
+
}
|
|
2269
|
+
return flattened;
|
|
2270
|
+
}
|
|
2271
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
2272
|
+
return [{ type: "text", value: String(value) }];
|
|
2273
|
+
}
|
|
2274
|
+
if (
|
|
2275
|
+
typeof value === "object" &&
|
|
2276
|
+
value !== null &&
|
|
2277
|
+
typeof value.type === "string"
|
|
2278
|
+
) {
|
|
2279
|
+
return [value];
|
|
2280
|
+
}
|
|
2281
|
+
return [{ type: "text", value: String(value) }];
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
function __renderify_runtime_h(type, props, ...children) {
|
|
2285
|
+
const normalizedChildren = __renderify_runtime_to_nodes(children);
|
|
2286
|
+
if (typeof type === "function") {
|
|
2287
|
+
const output = type({ ...(props || {}), children: normalizedChildren });
|
|
2288
|
+
const functionNodes = __renderify_runtime_to_nodes(output);
|
|
2289
|
+
if (functionNodes.length === 1) {
|
|
2290
|
+
return functionNodes[0];
|
|
2291
|
+
}
|
|
2292
|
+
return { type: "element", tag: "div", children: functionNodes };
|
|
2293
|
+
}
|
|
2294
|
+
if (typeof type === "string") {
|
|
2295
|
+
return {
|
|
2296
|
+
type: "element",
|
|
2297
|
+
tag: type,
|
|
2298
|
+
props: props || undefined,
|
|
2299
|
+
children: normalizedChildren,
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
return { type: "text", value: "Unsupported JSX node type" };
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
function __renderify_runtime_fragment(...children) {
|
|
2306
|
+
return __renderify_runtime_to_nodes(children);
|
|
2307
|
+
}
|
|
2308
|
+
`.trim();
|
|
2309
|
+
var BabelRuntimeSourceTranspiler = class {
|
|
2310
|
+
async transpile(input) {
|
|
2311
|
+
if (input.language === "js") {
|
|
2312
|
+
return input.code;
|
|
2313
|
+
}
|
|
2314
|
+
const babel = this.resolveBabel();
|
|
2315
|
+
const presets = [];
|
|
2316
|
+
if (input.language === "ts" || input.language === "tsx") {
|
|
2317
|
+
presets.push("typescript");
|
|
2318
|
+
}
|
|
2319
|
+
if (input.language === "jsx" || input.language === "tsx") {
|
|
2320
|
+
if (input.runtime === "preact") {
|
|
2321
|
+
presets.push([
|
|
2322
|
+
"react",
|
|
2323
|
+
{
|
|
2324
|
+
runtime: "automatic",
|
|
2325
|
+
importSource: "preact"
|
|
2326
|
+
}
|
|
2327
|
+
]);
|
|
2328
|
+
} else {
|
|
2329
|
+
presets.push([
|
|
2330
|
+
"react",
|
|
2331
|
+
{
|
|
2332
|
+
runtime: "classic",
|
|
2333
|
+
pragma: "__renderify_runtime_h",
|
|
2334
|
+
pragmaFrag: "__renderify_runtime_fragment"
|
|
2335
|
+
}
|
|
2336
|
+
]);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
const transformed = babel.transform(input.code, {
|
|
2340
|
+
sourceType: "module",
|
|
2341
|
+
presets,
|
|
2342
|
+
filename: input.filename,
|
|
2343
|
+
babelrc: false,
|
|
2344
|
+
configFile: false,
|
|
2345
|
+
comments: false
|
|
2346
|
+
});
|
|
2347
|
+
if (!transformed.code) {
|
|
2348
|
+
throw new Error("Babel returned empty output");
|
|
2349
|
+
}
|
|
2350
|
+
return transformed.code;
|
|
2351
|
+
}
|
|
2352
|
+
resolveBabel() {
|
|
2353
|
+
const root = globalThis;
|
|
2354
|
+
if (root.Babel && typeof root.Babel.transform === "function") {
|
|
2355
|
+
return root.Babel;
|
|
2356
|
+
}
|
|
2357
|
+
throw new Error(
|
|
2358
|
+
"Babel standalone is not available. Load @babel/standalone in browser or provide sourceTranspiler."
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
static mergeRuntimeHelpers(source, runtime) {
|
|
2362
|
+
if (runtime === "preact") {
|
|
2363
|
+
return source;
|
|
2364
|
+
}
|
|
2365
|
+
return `${source}
|
|
2366
|
+
|
|
2367
|
+
${RUNTIME_JSX_HELPERS}`;
|
|
2368
|
+
}
|
|
2369
|
+
};
|
|
2370
|
+
|
|
2371
|
+
// src/runtime-source-runtime.ts
|
|
2372
|
+
function shouldUsePreactSourceRuntime(source) {
|
|
2373
|
+
return source.runtime === "preact";
|
|
2374
|
+
}
|
|
2375
|
+
function executionProfileToSourceSandboxMode(executionProfile) {
|
|
2376
|
+
if (executionProfile === "sandbox-worker") {
|
|
2377
|
+
return "worker";
|
|
2378
|
+
}
|
|
2379
|
+
if (executionProfile === "sandbox-iframe") {
|
|
2380
|
+
return "iframe";
|
|
2381
|
+
}
|
|
2382
|
+
return void 0;
|
|
2383
|
+
}
|
|
2384
|
+
function resolveSourceSandboxMode(input) {
|
|
2385
|
+
const requested = executionProfileToSourceSandboxMode(input.executionProfile);
|
|
2386
|
+
const mode = requested ?? input.defaultMode;
|
|
2387
|
+
if (mode === "none") {
|
|
2388
|
+
return "none";
|
|
2389
|
+
}
|
|
2390
|
+
if (!input.isBrowserRuntime) {
|
|
2391
|
+
return "none";
|
|
2392
|
+
}
|
|
2393
|
+
if (shouldUsePreactSourceRuntime(input.source)) {
|
|
2394
|
+
if (requested) {
|
|
2395
|
+
throw new Error(
|
|
2396
|
+
`${requested} executionProfile is not supported with source.runtime=preact`
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
return "none";
|
|
2400
|
+
}
|
|
2401
|
+
return mode;
|
|
2402
|
+
}
|
|
2403
|
+
async function transpileRuntimeSource(source, sourceTranspiler) {
|
|
2404
|
+
const mergedSource = BabelRuntimeSourceTranspiler.mergeRuntimeHelpers(
|
|
2405
|
+
source.code,
|
|
2406
|
+
source.runtime
|
|
2407
|
+
);
|
|
2408
|
+
return sourceTranspiler.transpile({
|
|
2409
|
+
code: mergedSource,
|
|
2410
|
+
language: source.language,
|
|
2411
|
+
filename: `renderify-runtime-source.${source.language}`,
|
|
2412
|
+
runtime: source.runtime
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
// src/manager.ts
|
|
2417
|
+
var DefaultRuntimeManager = class {
|
|
2418
|
+
moduleLoader;
|
|
2419
|
+
sourceTranspiler;
|
|
2420
|
+
states = /* @__PURE__ */ new Map();
|
|
2421
|
+
defaultMaxImports;
|
|
2422
|
+
defaultMaxComponentInvocations;
|
|
2423
|
+
defaultMaxExecutionMs;
|
|
2424
|
+
defaultExecutionProfile;
|
|
2425
|
+
supportedPlanSpecVersions;
|
|
2426
|
+
enforceModuleManifest;
|
|
2427
|
+
allowIsolationFallback;
|
|
2428
|
+
browserSourceSandboxMode;
|
|
2429
|
+
browserSourceSandboxTimeoutMs;
|
|
2430
|
+
browserSourceSandboxFailClosed;
|
|
2431
|
+
enableDependencyPreflight;
|
|
2432
|
+
failOnDependencyPreflightError;
|
|
2433
|
+
remoteFetchTimeoutMs;
|
|
2434
|
+
remoteFetchRetries;
|
|
2435
|
+
remoteFetchBackoffMs;
|
|
2436
|
+
remoteFallbackCdnBases;
|
|
2437
|
+
browserModuleUrlCache = /* @__PURE__ */ new Map();
|
|
2438
|
+
browserModuleInflight = /* @__PURE__ */ new Map();
|
|
2439
|
+
browserBlobUrls = /* @__PURE__ */ new Set();
|
|
2440
|
+
initialized = false;
|
|
2441
|
+
constructor(options = {}) {
|
|
2442
|
+
this.moduleLoader = options.moduleLoader;
|
|
2443
|
+
this.sourceTranspiler = options.sourceTranspiler ?? new BabelRuntimeSourceTranspiler();
|
|
2444
|
+
this.defaultMaxImports = options.defaultMaxImports ?? FALLBACK_MAX_IMPORTS;
|
|
2445
|
+
this.defaultMaxComponentInvocations = options.defaultMaxComponentInvocations ?? FALLBACK_MAX_COMPONENT_INVOCATIONS;
|
|
2446
|
+
this.defaultMaxExecutionMs = options.defaultMaxExecutionMs ?? FALLBACK_MAX_EXECUTION_MS;
|
|
2447
|
+
this.defaultExecutionProfile = options.defaultExecutionProfile ?? FALLBACK_EXECUTION_PROFILE;
|
|
2448
|
+
this.supportedPlanSpecVersions = normalizeSupportedSpecVersions(
|
|
2449
|
+
options.supportedPlanSpecVersions
|
|
2450
|
+
);
|
|
2451
|
+
this.enforceModuleManifest = options.enforceModuleManifest ?? FALLBACK_ENFORCE_MODULE_MANIFEST;
|
|
2452
|
+
this.allowIsolationFallback = options.allowIsolationFallback ?? FALLBACK_ALLOW_ISOLATION_FALLBACK;
|
|
2453
|
+
this.browserSourceSandboxMode = normalizeSourceSandboxMode(
|
|
2454
|
+
options.browserSourceSandboxMode
|
|
2455
|
+
);
|
|
2456
|
+
this.browserSourceSandboxTimeoutMs = normalizePositiveInteger(
|
|
2457
|
+
options.browserSourceSandboxTimeoutMs,
|
|
2458
|
+
FALLBACK_BROWSER_SOURCE_SANDBOX_TIMEOUT_MS
|
|
2459
|
+
);
|
|
2460
|
+
this.browserSourceSandboxFailClosed = options.browserSourceSandboxFailClosed ?? FALLBACK_BROWSER_SOURCE_SANDBOX_FAIL_CLOSED;
|
|
2461
|
+
this.enableDependencyPreflight = options.enableDependencyPreflight ?? FALLBACK_ENABLE_DEPENDENCY_PREFLIGHT;
|
|
2462
|
+
this.failOnDependencyPreflightError = options.failOnDependencyPreflightError ?? FALLBACK_FAIL_ON_DEPENDENCY_PREFLIGHT_ERROR;
|
|
2463
|
+
this.remoteFetchTimeoutMs = normalizePositiveInteger(
|
|
2464
|
+
options.remoteFetchTimeoutMs,
|
|
2465
|
+
FALLBACK_REMOTE_FETCH_TIMEOUT_MS
|
|
2466
|
+
);
|
|
2467
|
+
this.remoteFetchRetries = normalizeNonNegativeInteger(
|
|
2468
|
+
options.remoteFetchRetries,
|
|
2469
|
+
FALLBACK_REMOTE_FETCH_RETRIES
|
|
2470
|
+
);
|
|
2471
|
+
this.remoteFetchBackoffMs = normalizeNonNegativeInteger(
|
|
2472
|
+
options.remoteFetchBackoffMs,
|
|
2473
|
+
FALLBACK_REMOTE_FETCH_BACKOFF_MS
|
|
2474
|
+
);
|
|
2475
|
+
this.remoteFallbackCdnBases = normalizeFallbackCdnBases(
|
|
2476
|
+
options.remoteFallbackCdnBases
|
|
2477
|
+
);
|
|
2478
|
+
}
|
|
2479
|
+
async initialize() {
|
|
2480
|
+
if (this.initialized) {
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
this.initialized = true;
|
|
2484
|
+
}
|
|
2485
|
+
async terminate() {
|
|
2486
|
+
this.initialized = false;
|
|
2487
|
+
this.states.clear();
|
|
2488
|
+
this.browserModuleUrlCache.clear();
|
|
2489
|
+
this.browserModuleInflight.clear();
|
|
2490
|
+
this.revokeBrowserBlobUrls();
|
|
2491
|
+
}
|
|
2492
|
+
async execute(input) {
|
|
2493
|
+
return this.executePlan(
|
|
2494
|
+
input.plan,
|
|
2495
|
+
input.context,
|
|
2496
|
+
input.event,
|
|
2497
|
+
input.stateOverride,
|
|
2498
|
+
input.signal
|
|
2499
|
+
);
|
|
2500
|
+
}
|
|
2501
|
+
async probePlan(plan) {
|
|
2502
|
+
this.ensureInitialized();
|
|
2503
|
+
const diagnostics = [];
|
|
2504
|
+
const specVersion = resolveRuntimePlanSpecVersion(plan.specVersion);
|
|
2505
|
+
if (!this.supportedPlanSpecVersions.has(specVersion)) {
|
|
2506
|
+
diagnostics.push({
|
|
2507
|
+
level: "error",
|
|
2508
|
+
code: "RUNTIME_SPEC_VERSION_UNSUPPORTED",
|
|
2509
|
+
message: `Unsupported plan specVersion "${specVersion}". Supported: ${[
|
|
2510
|
+
...this.supportedPlanSpecVersions
|
|
2511
|
+
].join(", ")}`
|
|
2512
|
+
});
|
|
2513
|
+
return {
|
|
2514
|
+
planId: plan.id,
|
|
2515
|
+
diagnostics,
|
|
2516
|
+
dependencies: []
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
const frame = {
|
|
2520
|
+
startedAt: nowMs(),
|
|
2521
|
+
maxExecutionMs: plan.capabilities.maxExecutionMs ?? this.defaultMaxExecutionMs,
|
|
2522
|
+
maxComponentInvocations: plan.capabilities.maxComponentInvocations ?? this.defaultMaxComponentInvocations,
|
|
2523
|
+
componentInvocations: 0,
|
|
2524
|
+
executionProfile: plan.capabilities.executionProfile ?? this.defaultExecutionProfile
|
|
2525
|
+
};
|
|
2526
|
+
const dependencies = await this.preflightPlanDependencies(
|
|
2527
|
+
plan,
|
|
2528
|
+
diagnostics,
|
|
2529
|
+
frame
|
|
2530
|
+
);
|
|
2531
|
+
return {
|
|
2532
|
+
planId: plan.id,
|
|
2533
|
+
diagnostics,
|
|
2534
|
+
dependencies
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
async executePlan(plan, context = {}, event, stateOverride, signal) {
|
|
2538
|
+
this.ensureInitialized();
|
|
2539
|
+
throwIfAborted(signal);
|
|
2540
|
+
const specVersion = resolveRuntimePlanSpecVersion(plan.specVersion);
|
|
2541
|
+
const diagnostics = [];
|
|
2542
|
+
if (!this.supportedPlanSpecVersions.has(specVersion)) {
|
|
2543
|
+
diagnostics.push({
|
|
2544
|
+
level: "error",
|
|
2545
|
+
code: "RUNTIME_SPEC_VERSION_UNSUPPORTED",
|
|
2546
|
+
message: `Unsupported plan specVersion "${specVersion}". Supported: ${[
|
|
2547
|
+
...this.supportedPlanSpecVersions
|
|
2548
|
+
].join(", ")}`
|
|
2549
|
+
});
|
|
2550
|
+
return {
|
|
2551
|
+
planId: plan.id,
|
|
2552
|
+
root: plan.root,
|
|
2553
|
+
diagnostics,
|
|
2554
|
+
state: cloneJsonValue2(this.resolveState(plan, stateOverride)),
|
|
2555
|
+
handledEvent: event,
|
|
2556
|
+
appliedActions: []
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2559
|
+
const state = this.resolveState(plan, stateOverride);
|
|
2560
|
+
const appliedActions = [];
|
|
2561
|
+
const frame = {
|
|
2562
|
+
startedAt: nowMs(),
|
|
2563
|
+
maxExecutionMs: plan.capabilities.maxExecutionMs ?? this.defaultMaxExecutionMs,
|
|
2564
|
+
maxComponentInvocations: plan.capabilities.maxComponentInvocations ?? this.defaultMaxComponentInvocations,
|
|
2565
|
+
componentInvocations: 0,
|
|
2566
|
+
executionProfile: plan.capabilities.executionProfile ?? this.defaultExecutionProfile,
|
|
2567
|
+
signal
|
|
2568
|
+
};
|
|
2569
|
+
const maxImports = plan.capabilities.maxImports ?? this.defaultMaxImports;
|
|
2570
|
+
const imports = plan.imports ?? [];
|
|
2571
|
+
if (this.enableDependencyPreflight) {
|
|
2572
|
+
await this.preflightPlanDependencies(plan, diagnostics, frame);
|
|
2573
|
+
if (this.failOnDependencyPreflightError && diagnostics.some(
|
|
2574
|
+
(item) => item.level === "error" && item.code.startsWith("RUNTIME_PREFLIGHT_")
|
|
2575
|
+
)) {
|
|
2576
|
+
return {
|
|
2577
|
+
planId: plan.id,
|
|
2578
|
+
root: plan.root,
|
|
2579
|
+
diagnostics,
|
|
2580
|
+
state: cloneJsonValue2(state),
|
|
2581
|
+
handledEvent: event,
|
|
2582
|
+
appliedActions
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
await resolveRuntimePlanImports({
|
|
2587
|
+
imports,
|
|
2588
|
+
maxImports,
|
|
2589
|
+
moduleManifest: plan.moduleManifest,
|
|
2590
|
+
diagnostics,
|
|
2591
|
+
moduleLoader: this.moduleLoader,
|
|
2592
|
+
resolveRuntimeSpecifier: (specifier, moduleManifest, runtimeDiagnostics) => this.resolveRuntimeSpecifier(
|
|
2593
|
+
specifier,
|
|
2594
|
+
moduleManifest,
|
|
2595
|
+
runtimeDiagnostics,
|
|
2596
|
+
"import"
|
|
2597
|
+
),
|
|
2598
|
+
isAborted: () => isAborted(frame.signal),
|
|
2599
|
+
hasExceededBudget: () => hasExceededBudget(frame),
|
|
2600
|
+
withRemainingBudget: (operation, timeoutMessage) => withRemainingBudget(operation, frame, timeoutMessage),
|
|
2601
|
+
isAbortError: (error) => isAbortError(error),
|
|
2602
|
+
errorToMessage: (error) => this.errorToMessage(error)
|
|
2603
|
+
});
|
|
2604
|
+
const sourceRoot = plan.source ? await this.resolveSourceRoot(
|
|
2605
|
+
plan,
|
|
2606
|
+
plan.source,
|
|
2607
|
+
context,
|
|
2608
|
+
state,
|
|
2609
|
+
event,
|
|
2610
|
+
diagnostics,
|
|
2611
|
+
frame
|
|
2612
|
+
) : void 0;
|
|
2613
|
+
const sourceRenderArtifact = sourceRoot?.renderArtifact;
|
|
2614
|
+
const resolvedRoot = sourceRoot?.root ? await this.resolveNode(
|
|
2615
|
+
sourceRoot.root,
|
|
2616
|
+
plan.moduleManifest,
|
|
2617
|
+
context,
|
|
2618
|
+
state,
|
|
2619
|
+
event,
|
|
2620
|
+
diagnostics,
|
|
2621
|
+
frame
|
|
2622
|
+
) : await this.resolveNode(
|
|
2623
|
+
plan.root,
|
|
2624
|
+
plan.moduleManifest,
|
|
2625
|
+
context,
|
|
2626
|
+
state,
|
|
2627
|
+
event,
|
|
2628
|
+
diagnostics,
|
|
2629
|
+
frame
|
|
2630
|
+
);
|
|
2631
|
+
return {
|
|
2632
|
+
planId: plan.id,
|
|
2633
|
+
root: resolvedRoot,
|
|
2634
|
+
diagnostics,
|
|
2635
|
+
state: cloneJsonValue2(state),
|
|
2636
|
+
handledEvent: event,
|
|
2637
|
+
appliedActions,
|
|
2638
|
+
...sourceRenderArtifact ? { renderArtifact: sourceRenderArtifact } : {}
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
async compile(plan, options = {}) {
|
|
2642
|
+
const indent = options.pretty ? 2 : 0;
|
|
2643
|
+
return JSON.stringify(plan, null, indent);
|
|
2644
|
+
}
|
|
2645
|
+
getPlanState(planId) {
|
|
2646
|
+
const snapshot = this.states.get(planId);
|
|
2647
|
+
if (!snapshot) {
|
|
2648
|
+
return void 0;
|
|
2649
|
+
}
|
|
2650
|
+
return cloneJsonValue2(snapshot);
|
|
2651
|
+
}
|
|
2652
|
+
setPlanState(planId, snapshot) {
|
|
2653
|
+
this.states.set(planId, cloneJsonValue2(snapshot));
|
|
2654
|
+
}
|
|
2655
|
+
clearPlanState(planId) {
|
|
2656
|
+
this.states.delete(planId);
|
|
2657
|
+
}
|
|
2658
|
+
resolveState(plan, stateOverride) {
|
|
2659
|
+
if (stateOverride) {
|
|
2660
|
+
return cloneJsonValue2(stateOverride);
|
|
2661
|
+
}
|
|
2662
|
+
if (plan.state?.initial) {
|
|
2663
|
+
return cloneJsonValue2(plan.state.initial);
|
|
2664
|
+
}
|
|
2665
|
+
return {};
|
|
2666
|
+
}
|
|
2667
|
+
async preflightPlanDependencies(plan, diagnostics, frame) {
|
|
2668
|
+
return preflightRuntimePlanDependencies({
|
|
2669
|
+
plan,
|
|
2670
|
+
diagnostics,
|
|
2671
|
+
moduleLoader: this.moduleLoader,
|
|
2672
|
+
withRemainingBudget: (operation, timeoutMessage) => withRemainingBudget(operation, frame, timeoutMessage),
|
|
2673
|
+
resolveRuntimeSourceSpecifier: (specifier, manifest, runtimeDiagnostics, requireManifest) => this.resolveRuntimeSourceSpecifier(
|
|
2674
|
+
specifier,
|
|
2675
|
+
manifest,
|
|
2676
|
+
runtimeDiagnostics,
|
|
2677
|
+
requireManifest
|
|
2678
|
+
),
|
|
2679
|
+
resolveSourceImportLoaderCandidate: (specifier, manifest) => this.resolveSourceImportLoaderCandidate(specifier, manifest),
|
|
2680
|
+
resolveRuntimeSpecifier: (specifier, manifest, runtimeDiagnostics, usage) => this.resolveRuntimeSpecifier(
|
|
2681
|
+
specifier,
|
|
2682
|
+
manifest,
|
|
2683
|
+
runtimeDiagnostics,
|
|
2684
|
+
usage
|
|
2685
|
+
),
|
|
2686
|
+
materializeBrowserRemoteModule: (url, manifest, runtimeDiagnostics) => this.createSourceModuleLoader(
|
|
2687
|
+
manifest,
|
|
2688
|
+
runtimeDiagnostics
|
|
2689
|
+
).materializeBrowserRemoteModule(url),
|
|
2690
|
+
fetchRemoteModuleCodeWithFallback: (url, runtimeDiagnostics) => this.createSourceModuleLoader(
|
|
2691
|
+
void 0,
|
|
2692
|
+
runtimeDiagnostics
|
|
2693
|
+
).fetchRemoteModuleCodeWithFallback(url),
|
|
2694
|
+
isAbortError: (error) => isAbortError(error),
|
|
2695
|
+
errorToMessage: (error) => this.errorToMessage(error),
|
|
2696
|
+
isAborted: () => isAborted(frame.signal),
|
|
2697
|
+
hasExceededBudget: () => hasExceededBudget(frame)
|
|
2698
|
+
});
|
|
2699
|
+
}
|
|
2700
|
+
async resolveSourceRoot(plan, source, context, state, event, diagnostics, frame) {
|
|
2701
|
+
return executeRuntimeSourceRoot({
|
|
2702
|
+
plan,
|
|
2703
|
+
source,
|
|
2704
|
+
context,
|
|
2705
|
+
state,
|
|
2706
|
+
event,
|
|
2707
|
+
diagnostics,
|
|
2708
|
+
frame: {
|
|
2709
|
+
executionProfile: frame.executionProfile,
|
|
2710
|
+
signal: frame.signal
|
|
2711
|
+
},
|
|
2712
|
+
browserSourceSandboxTimeoutMs: this.browserSourceSandboxTimeoutMs,
|
|
2713
|
+
browserSourceSandboxFailClosed: this.browserSourceSandboxFailClosed,
|
|
2714
|
+
withRemainingBudget: (operation, timeoutMessage) => withRemainingBudget(operation, frame, timeoutMessage),
|
|
2715
|
+
transpileRuntimeSource: (runtimeSource) => transpileRuntimeSource(runtimeSource, this.sourceTranspiler),
|
|
2716
|
+
rewriteSourceImports: async (code, manifest, runtimeDiagnostics) => this.rewriteImportsAsync(
|
|
2717
|
+
code,
|
|
2718
|
+
async (specifier) => this.resolveRuntimeSourceSpecifier(
|
|
2719
|
+
specifier,
|
|
2720
|
+
manifest,
|
|
2721
|
+
runtimeDiagnostics
|
|
2722
|
+
)
|
|
2723
|
+
),
|
|
2724
|
+
resolveSourceSandboxMode: (runtimeSource, executionProfile) => resolveSourceSandboxMode({
|
|
2725
|
+
source: runtimeSource,
|
|
2726
|
+
executionProfile,
|
|
2727
|
+
defaultMode: this.browserSourceSandboxMode,
|
|
2728
|
+
isBrowserRuntime: isBrowserRuntime()
|
|
2729
|
+
}),
|
|
2730
|
+
importSourceModuleFromCode: (code, manifest, runtimeDiagnostics) => this.createSourceModuleLoader(
|
|
2731
|
+
manifest,
|
|
2732
|
+
runtimeDiagnostics
|
|
2733
|
+
).importSourceModuleFromCode(code),
|
|
2734
|
+
normalizeSourceOutput: (output) => this.normalizeSourceOutput(output),
|
|
2735
|
+
shouldUsePreactSourceRuntime,
|
|
2736
|
+
createPreactRenderArtifact: ({
|
|
2737
|
+
sourceExport,
|
|
2738
|
+
runtimeInput,
|
|
2739
|
+
diagnostics: diagnostics2
|
|
2740
|
+
}) => createPreactRenderArtifact({
|
|
2741
|
+
sourceExport,
|
|
2742
|
+
runtimeInput,
|
|
2743
|
+
diagnostics: diagnostics2
|
|
2744
|
+
}),
|
|
2745
|
+
isAbortError: (error) => isAbortError(error),
|
|
2746
|
+
errorToMessage: (error) => this.errorToMessage(error),
|
|
2747
|
+
cloneJsonValue: cloneJsonValue2,
|
|
2748
|
+
asJsonValue: asJsonValue3
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
resolveRuntimeSourceSpecifier(specifier, moduleManifest, diagnostics, requireManifest = true) {
|
|
2752
|
+
return resolveRuntimeSourceSpecifier({
|
|
2753
|
+
specifier,
|
|
2754
|
+
moduleManifest,
|
|
2755
|
+
diagnostics,
|
|
2756
|
+
requireManifest,
|
|
2757
|
+
enforceModuleManifest: this.enforceModuleManifest,
|
|
2758
|
+
moduleLoader: this.moduleLoader,
|
|
2759
|
+
jspmCdnBase: FALLBACK_JSPM_CDN_BASE
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
resolveSourceImportLoaderCandidate(specifier, moduleManifest) {
|
|
2763
|
+
return resolveSourceImportLoaderCandidate(
|
|
2764
|
+
specifier,
|
|
2765
|
+
moduleManifest,
|
|
2766
|
+
this.moduleLoader
|
|
2767
|
+
);
|
|
2768
|
+
}
|
|
2769
|
+
resolveRuntimeSpecifier(specifier, moduleManifest, diagnostics, usage) {
|
|
2770
|
+
return resolveRuntimeSpecifier({
|
|
2771
|
+
specifier,
|
|
2772
|
+
moduleManifest,
|
|
2773
|
+
diagnostics,
|
|
2774
|
+
usage,
|
|
2775
|
+
enforceModuleManifest: this.enforceModuleManifest
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2778
|
+
createSourceModuleLoader(moduleManifest, diagnostics) {
|
|
2779
|
+
return new RuntimeSourceModuleLoader({
|
|
2780
|
+
moduleManifest,
|
|
2781
|
+
diagnostics,
|
|
2782
|
+
browserModuleUrlCache: this.browserModuleUrlCache,
|
|
2783
|
+
browserModuleInflight: this.browserModuleInflight,
|
|
2784
|
+
remoteFallbackCdnBases: this.remoteFallbackCdnBases,
|
|
2785
|
+
remoteFetchTimeoutMs: this.remoteFetchTimeoutMs,
|
|
2786
|
+
remoteFetchRetries: this.remoteFetchRetries,
|
|
2787
|
+
remoteFetchBackoffMs: this.remoteFetchBackoffMs,
|
|
2788
|
+
canMaterializeBrowserModules: () => canMaterializeBrowserModules(),
|
|
2789
|
+
rewriteImportsAsync: (code, resolver) => this.rewriteImportsAsync(code, resolver),
|
|
2790
|
+
createBrowserBlobModuleUrl: (code) => this.createBrowserBlobModuleUrl(code),
|
|
2791
|
+
resolveRuntimeSourceSpecifier: (specifier, manifest, runtimeDiagnostics, requireManifest) => this.resolveRuntimeSourceSpecifier(
|
|
2792
|
+
specifier,
|
|
2793
|
+
manifest,
|
|
2794
|
+
runtimeDiagnostics,
|
|
2795
|
+
requireManifest
|
|
2796
|
+
)
|
|
2797
|
+
});
|
|
2798
|
+
}
|
|
2799
|
+
async materializeFetchedModuleSource(fetched, moduleManifest, diagnostics) {
|
|
2800
|
+
return this.createSourceModuleLoader(
|
|
2801
|
+
moduleManifest,
|
|
2802
|
+
diagnostics
|
|
2803
|
+
).materializeFetchedModuleSource(fetched);
|
|
2804
|
+
}
|
|
2805
|
+
toConfiguredFallbackUrl(url, cdnBase) {
|
|
2806
|
+
return toConfiguredFallbackUrl(url, cdnBase);
|
|
2807
|
+
}
|
|
2808
|
+
toEsmFallbackUrl(url, cdnBase = FALLBACK_ESM_CDN_BASE) {
|
|
2809
|
+
return toEsmFallbackUrl(url, cdnBase);
|
|
2810
|
+
}
|
|
2811
|
+
async rewriteImportsAsync(code, resolver) {
|
|
2812
|
+
return rewriteImportsAsync(code, resolver);
|
|
2813
|
+
}
|
|
2814
|
+
createBrowserBlobModuleUrl(code) {
|
|
2815
|
+
return createBrowserBlobModuleUrl(code, this.browserBlobUrls);
|
|
2816
|
+
}
|
|
2817
|
+
revokeBrowserBlobUrls() {
|
|
2818
|
+
revokeBrowserBlobUrls(this.browserBlobUrls);
|
|
2819
|
+
}
|
|
2820
|
+
normalizeSourceOutput(output) {
|
|
2821
|
+
return normalizeRuntimeSourceOutput(output);
|
|
2822
|
+
}
|
|
2823
|
+
async resolveNode(node, moduleManifest, context, state, event, diagnostics, frame) {
|
|
2824
|
+
throwIfAborted(frame.signal);
|
|
2825
|
+
if (hasExceededBudget(frame)) {
|
|
2826
|
+
diagnostics.push({
|
|
2827
|
+
level: "error",
|
|
2828
|
+
code: "RUNTIME_TIMEOUT",
|
|
2829
|
+
message: "Execution time budget exceeded during node resolution"
|
|
2830
|
+
});
|
|
2831
|
+
return createElementNode3("div", { "data-renderify-timeout": "true" }, [
|
|
2832
|
+
createTextNode3("Runtime execution timed out")
|
|
2833
|
+
]);
|
|
2834
|
+
}
|
|
2835
|
+
return resolveRuntimeNode({
|
|
2836
|
+
node,
|
|
2837
|
+
moduleManifest,
|
|
2838
|
+
context,
|
|
2839
|
+
state,
|
|
2840
|
+
event,
|
|
2841
|
+
diagnostics,
|
|
2842
|
+
frame,
|
|
2843
|
+
resolver: {
|
|
2844
|
+
moduleLoader: this.moduleLoader,
|
|
2845
|
+
allowIsolationFallback: this.allowIsolationFallback,
|
|
2846
|
+
resolveRuntimeSpecifier: (specifier, manifest, runtimeDiagnostics, usage) => this.resolveRuntimeSpecifier(
|
|
2847
|
+
specifier,
|
|
2848
|
+
manifest,
|
|
2849
|
+
runtimeDiagnostics,
|
|
2850
|
+
usage
|
|
2851
|
+
),
|
|
2852
|
+
withRemainingBudget: (operation, timeoutMessage) => withRemainingBudget(operation, frame, timeoutMessage),
|
|
2853
|
+
resolveNode: (nextNode) => this.resolveNode(
|
|
2854
|
+
nextNode,
|
|
2855
|
+
moduleManifest,
|
|
2856
|
+
context,
|
|
2857
|
+
state,
|
|
2858
|
+
event,
|
|
2859
|
+
diagnostics,
|
|
2860
|
+
frame
|
|
2861
|
+
),
|
|
2862
|
+
errorToMessage: (error) => this.errorToMessage(error)
|
|
2863
|
+
}
|
|
2864
|
+
});
|
|
2865
|
+
}
|
|
2866
|
+
ensureInitialized() {
|
|
2867
|
+
if (!this.initialized) {
|
|
2868
|
+
throw new Error("RuntimeManager is not initialized");
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
errorToMessage(error) {
|
|
2872
|
+
if (error instanceof Error) {
|
|
2873
|
+
return error.message;
|
|
2874
|
+
}
|
|
2875
|
+
return String(error);
|
|
2876
|
+
}
|
|
2877
|
+
};
|
|
2878
|
+
|
|
2879
|
+
// src/runtime-manager.types.ts
|
|
2880
|
+
var RuntimeSecurityViolationError = class extends Error {
|
|
2881
|
+
result;
|
|
2882
|
+
constructor(result) {
|
|
2883
|
+
super(`Security policy rejected runtime plan: ${result.issues.join("; ")}`);
|
|
2884
|
+
this.name = "RuntimeSecurityViolationError";
|
|
2885
|
+
this.result = result;
|
|
2886
|
+
}
|
|
2887
|
+
};
|
|
2888
|
+
|
|
2889
|
+
// src/ui-renderer.ts
|
|
2890
|
+
var DefaultUIRenderer = class {
|
|
2891
|
+
mountSessions = /* @__PURE__ */ new WeakMap();
|
|
2892
|
+
async render(result, target) {
|
|
2893
|
+
if (result.renderArtifact?.mode === "preact-vnode") {
|
|
2894
|
+
return this.renderPreactArtifact(result.renderArtifact.payload, target);
|
|
2895
|
+
}
|
|
2896
|
+
const serialized = this.renderNodeWithBindings(result.root);
|
|
2897
|
+
if (typeof document === "undefined") {
|
|
2898
|
+
return serialized.html;
|
|
2899
|
+
}
|
|
2900
|
+
if (!target) {
|
|
2901
|
+
return serialized.html;
|
|
2902
|
+
}
|
|
2903
|
+
const resolvedTarget = this.resolveRenderTarget(target);
|
|
2904
|
+
if (!resolvedTarget) {
|
|
2905
|
+
throw new Error(
|
|
2906
|
+
`Render target not found: ${this.stringifyTarget(target)}`
|
|
2907
|
+
);
|
|
2908
|
+
}
|
|
2909
|
+
const { mountPoint, onRuntimeEvent } = resolvedTarget;
|
|
2910
|
+
this.patchMountPoint(mountPoint, serialized.html);
|
|
2911
|
+
this.syncMountSession(
|
|
2912
|
+
mountPoint,
|
|
2913
|
+
result.planId,
|
|
2914
|
+
serialized.eventBindings,
|
|
2915
|
+
onRuntimeEvent
|
|
2916
|
+
);
|
|
2917
|
+
return mountPoint.innerHTML;
|
|
2918
|
+
}
|
|
2919
|
+
async renderPreactArtifact(payload, target) {
|
|
2920
|
+
if (typeof document !== "undefined" && target) {
|
|
2921
|
+
const resolvedTarget = this.resolveRenderTarget(target);
|
|
2922
|
+
if (!resolvedTarget) {
|
|
2923
|
+
throw new Error(
|
|
2924
|
+
`Render target not found: ${this.stringifyTarget(target)}`
|
|
2925
|
+
);
|
|
2926
|
+
}
|
|
2927
|
+
const preact = await this.loadPreactRenderer();
|
|
2928
|
+
preact.render(payload, resolvedTarget.mountPoint);
|
|
2929
|
+
return resolvedTarget.mountPoint.innerHTML;
|
|
2930
|
+
}
|
|
2931
|
+
const renderToString = await this.loadPreactRenderToString();
|
|
2932
|
+
return renderToString(payload);
|
|
2933
|
+
}
|
|
2934
|
+
renderNode(node) {
|
|
2935
|
+
return this.renderNodeWithBindings(node).html;
|
|
2936
|
+
}
|
|
2937
|
+
renderNodeWithBindings(node) {
|
|
2938
|
+
const context = {
|
|
2939
|
+
nextBindingId: 0,
|
|
2940
|
+
eventBindings: []
|
|
2941
|
+
};
|
|
2942
|
+
return {
|
|
2943
|
+
html: this.renderNodeInternal(node, context),
|
|
2944
|
+
eventBindings: context.eventBindings
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
renderNodeInternal(node, context) {
|
|
2948
|
+
if (node.type === "text") {
|
|
2949
|
+
return escapeHtml(node.value);
|
|
2950
|
+
}
|
|
2951
|
+
if (node.type === "component") {
|
|
2952
|
+
return `<div data-renderify-unresolved-component="${escapeHtml(node.module)}"></div>`;
|
|
2953
|
+
}
|
|
2954
|
+
const children = (node.children ?? []).map((child) => this.renderNodeInternal(child, context)).join("");
|
|
2955
|
+
const normalizedTag = normalizeTagName(node.tag);
|
|
2956
|
+
if (!normalizedTag) {
|
|
2957
|
+
return `<div data-renderify-sanitized-tag="${escapeHtml(node.tag)}">${children}</div>`;
|
|
2958
|
+
}
|
|
2959
|
+
if (BLOCKED_TAG_NAMES.has(normalizedTag)) {
|
|
2960
|
+
return `<div data-renderify-sanitized-tag="${escapeHtml(node.tag)}"></div>`;
|
|
2961
|
+
}
|
|
2962
|
+
const attributes = serializeProps(node.props, context);
|
|
2963
|
+
return `<${normalizedTag}${attributes}>${children}</${normalizedTag}>`;
|
|
2964
|
+
}
|
|
2965
|
+
resolveRenderTarget(target) {
|
|
2966
|
+
if (isInteractiveRenderTarget(target)) {
|
|
2967
|
+
const mountPoint2 = this.resolveTargetElement(target.element);
|
|
2968
|
+
if (!mountPoint2) {
|
|
2969
|
+
return null;
|
|
2970
|
+
}
|
|
2971
|
+
return {
|
|
2972
|
+
mountPoint: mountPoint2,
|
|
2973
|
+
onRuntimeEvent: target.onRuntimeEvent
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
2976
|
+
const mountPoint = this.resolveTargetElement(target);
|
|
2977
|
+
if (!mountPoint) {
|
|
2978
|
+
return null;
|
|
2979
|
+
}
|
|
2980
|
+
return {
|
|
2981
|
+
mountPoint
|
|
2982
|
+
};
|
|
2983
|
+
}
|
|
2984
|
+
resolveTargetElement(target) {
|
|
2985
|
+
if (typeof target !== "string") {
|
|
2986
|
+
return target;
|
|
2987
|
+
}
|
|
2988
|
+
return document.querySelector(target);
|
|
2989
|
+
}
|
|
2990
|
+
patchMountPoint(mountPoint, nextHtml) {
|
|
2991
|
+
const sanitizedHtml = this.sanitizeHtmlForMount(nextHtml);
|
|
2992
|
+
const session = this.mountSessions.get(mountPoint);
|
|
2993
|
+
if (!session) {
|
|
2994
|
+
mountPoint.innerHTML = sanitizedHtml;
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2997
|
+
if (session.html === sanitizedHtml) {
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
const scrollTop = mountPoint.scrollTop;
|
|
3001
|
+
const scrollLeft = mountPoint.scrollLeft;
|
|
3002
|
+
const template = document.createElement("template");
|
|
3003
|
+
template.innerHTML = sanitizedHtml;
|
|
3004
|
+
this.reconcileChildren(mountPoint, template.content);
|
|
3005
|
+
mountPoint.scrollTop = scrollTop;
|
|
3006
|
+
mountPoint.scrollLeft = scrollLeft;
|
|
3007
|
+
}
|
|
3008
|
+
sanitizeHtmlForMount(html) {
|
|
3009
|
+
const template = document.createElement("template");
|
|
3010
|
+
template.innerHTML = html;
|
|
3011
|
+
sanitizeRenderedFragment(template.content);
|
|
3012
|
+
return template.innerHTML;
|
|
3013
|
+
}
|
|
3014
|
+
reconcileChildren(currentParent, nextParent) {
|
|
3015
|
+
const currentChildren = Array.from(currentParent.childNodes);
|
|
3016
|
+
const nextChildren = Array.from(nextParent.childNodes);
|
|
3017
|
+
if (this.shouldUseKeyedReconcile(currentChildren, nextChildren)) {
|
|
3018
|
+
this.reconcileKeyedChildren(currentParent, currentChildren, nextChildren);
|
|
3019
|
+
return;
|
|
3020
|
+
}
|
|
3021
|
+
const commonLength = Math.min(currentChildren.length, nextChildren.length);
|
|
3022
|
+
for (let i = 0; i < commonLength; i += 1) {
|
|
3023
|
+
this.reconcileNode(currentChildren[i], nextChildren[i]);
|
|
3024
|
+
}
|
|
3025
|
+
for (let i = currentChildren.length - 1; i >= nextChildren.length; i -= 1) {
|
|
3026
|
+
currentChildren[i].remove();
|
|
3027
|
+
}
|
|
3028
|
+
for (let i = commonLength; i < nextChildren.length; i += 1) {
|
|
3029
|
+
currentParent.appendChild(nextChildren[i].cloneNode(true));
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
shouldUseKeyedReconcile(currentChildren, nextChildren) {
|
|
3033
|
+
return currentChildren.some((node) => this.getNodeKey(node) !== null) || nextChildren.some((node) => this.getNodeKey(node) !== null);
|
|
3034
|
+
}
|
|
3035
|
+
reconcileKeyedChildren(currentParent, currentChildren, nextChildren) {
|
|
3036
|
+
const currentByKey = /* @__PURE__ */ new Map();
|
|
3037
|
+
for (const node of currentChildren) {
|
|
3038
|
+
const key = this.getNodeKey(node);
|
|
3039
|
+
if (!key || currentByKey.has(key)) {
|
|
3040
|
+
continue;
|
|
3041
|
+
}
|
|
3042
|
+
currentByKey.set(key, node);
|
|
3043
|
+
}
|
|
3044
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
3045
|
+
const desiredNodes = [];
|
|
3046
|
+
for (let index = 0; index < nextChildren.length; index += 1) {
|
|
3047
|
+
const nextChild = nextChildren[index];
|
|
3048
|
+
const nextKey = this.getNodeKey(nextChild);
|
|
3049
|
+
let candidate;
|
|
3050
|
+
if (nextKey) {
|
|
3051
|
+
const keyed = currentByKey.get(nextKey);
|
|
3052
|
+
if (keyed && !consumed.has(keyed) && this.areNodesCompatible(keyed, nextChild)) {
|
|
3053
|
+
candidate = keyed;
|
|
3054
|
+
}
|
|
3055
|
+
} else {
|
|
3056
|
+
const positional = currentChildren[index];
|
|
3057
|
+
if (positional && !consumed.has(positional) && this.getNodeKey(positional) === null && this.areNodesCompatible(positional, nextChild)) {
|
|
3058
|
+
candidate = positional;
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
if (candidate) {
|
|
3062
|
+
this.reconcileNode(candidate, nextChild);
|
|
3063
|
+
consumed.add(candidate);
|
|
3064
|
+
desiredNodes.push(candidate);
|
|
3065
|
+
continue;
|
|
3066
|
+
}
|
|
3067
|
+
desiredNodes.push(nextChild.cloneNode(true));
|
|
3068
|
+
}
|
|
3069
|
+
this.applyDesiredChildren(currentParent, desiredNodes);
|
|
3070
|
+
}
|
|
3071
|
+
applyDesiredChildren(currentParent, desiredNodes) {
|
|
3072
|
+
const desiredSet = new Set(desiredNodes);
|
|
3073
|
+
for (const child of Array.from(currentParent.childNodes)) {
|
|
3074
|
+
if (!desiredSet.has(child)) {
|
|
3075
|
+
child.remove();
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
for (let index = 0; index < desiredNodes.length; index += 1) {
|
|
3079
|
+
const desiredNode = desiredNodes[index];
|
|
3080
|
+
const nodeAtIndex = currentParent.childNodes[index] ?? null;
|
|
3081
|
+
if (desiredNode.parentNode !== currentParent) {
|
|
3082
|
+
currentParent.insertBefore(desiredNode, nodeAtIndex);
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3085
|
+
if (nodeAtIndex !== desiredNode) {
|
|
3086
|
+
currentParent.insertBefore(desiredNode, nodeAtIndex);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
getNodeKey(node) {
|
|
3091
|
+
if (typeof Element === "undefined" || !(node instanceof Element)) {
|
|
3092
|
+
return null;
|
|
3093
|
+
}
|
|
3094
|
+
const key = node.getAttribute("data-renderify-key") ?? node.getAttribute("key");
|
|
3095
|
+
if (!key || key.trim().length === 0) {
|
|
3096
|
+
return null;
|
|
3097
|
+
}
|
|
3098
|
+
return key;
|
|
3099
|
+
}
|
|
3100
|
+
areNodesCompatible(currentNode, nextNode) {
|
|
3101
|
+
if (currentNode.nodeType !== nextNode.nodeType) {
|
|
3102
|
+
return false;
|
|
3103
|
+
}
|
|
3104
|
+
if (typeof Element === "undefined" || !(currentNode instanceof Element) || !(nextNode instanceof Element)) {
|
|
3105
|
+
return true;
|
|
3106
|
+
}
|
|
3107
|
+
return currentNode.tagName === nextNode.tagName;
|
|
3108
|
+
}
|
|
3109
|
+
reconcileNode(currentNode, nextNode) {
|
|
3110
|
+
if (currentNode.nodeType !== nextNode.nodeType) {
|
|
3111
|
+
currentNode.replaceWith(nextNode.cloneNode(true));
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
if (currentNode.nodeType === Node.TEXT_NODE) {
|
|
3115
|
+
if (currentNode.textContent !== nextNode.textContent) {
|
|
3116
|
+
currentNode.textContent = nextNode.textContent;
|
|
3117
|
+
}
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
if (!(currentNode instanceof Element) || !(nextNode instanceof Element)) {
|
|
3121
|
+
if (currentNode.textContent !== nextNode.textContent) {
|
|
3122
|
+
currentNode.textContent = nextNode.textContent;
|
|
3123
|
+
}
|
|
3124
|
+
return;
|
|
3125
|
+
}
|
|
3126
|
+
if (currentNode.tagName !== nextNode.tagName) {
|
|
3127
|
+
currentNode.replaceWith(nextNode.cloneNode(true));
|
|
3128
|
+
return;
|
|
3129
|
+
}
|
|
3130
|
+
this.reconcileAttributes(currentNode, nextNode);
|
|
3131
|
+
this.reconcileChildren(currentNode, nextNode);
|
|
3132
|
+
}
|
|
3133
|
+
reconcileAttributes(currentElement, nextElement) {
|
|
3134
|
+
const activeElement = typeof document !== "undefined" ? document.activeElement : void 0;
|
|
3135
|
+
for (const attribute of Array.from(currentElement.attributes)) {
|
|
3136
|
+
if (!nextElement.hasAttribute(attribute.name)) {
|
|
3137
|
+
currentElement.removeAttribute(attribute.name);
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
for (const attribute of Array.from(nextElement.attributes)) {
|
|
3141
|
+
if (this.shouldSkipInteractivePropertySync(
|
|
3142
|
+
currentElement,
|
|
3143
|
+
activeElement,
|
|
3144
|
+
attribute.name
|
|
3145
|
+
)) {
|
|
3146
|
+
continue;
|
|
3147
|
+
}
|
|
3148
|
+
if (currentElement.getAttribute(attribute.name) !== attribute.value) {
|
|
3149
|
+
currentElement.setAttribute(attribute.name, attribute.value);
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
shouldSkipInteractivePropertySync(element, activeElement, attributeName) {
|
|
3154
|
+
if (!activeElement || element !== activeElement) {
|
|
3155
|
+
return false;
|
|
3156
|
+
}
|
|
3157
|
+
if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement)) {
|
|
3158
|
+
return false;
|
|
3159
|
+
}
|
|
3160
|
+
return attributeName === "value" || attributeName === "checked";
|
|
3161
|
+
}
|
|
3162
|
+
syncMountSession(mountPoint, planId, eventBindings, onRuntimeEvent) {
|
|
3163
|
+
const existing = this.mountSessions.get(mountPoint);
|
|
3164
|
+
const runtimeEvents = /* @__PURE__ */ new Map();
|
|
3165
|
+
const delegatedEventTypes = /* @__PURE__ */ new Set();
|
|
3166
|
+
for (const binding of eventBindings) {
|
|
3167
|
+
runtimeEvents.set(binding.bindingId, binding.runtimeEvent);
|
|
3168
|
+
delegatedEventTypes.add(binding.domEvent);
|
|
3169
|
+
}
|
|
3170
|
+
const session = existing ?? {
|
|
3171
|
+
html: "",
|
|
3172
|
+
planId,
|
|
3173
|
+
runtimeEvents,
|
|
3174
|
+
listeners: /* @__PURE__ */ new Map(),
|
|
3175
|
+
onRuntimeEvent
|
|
3176
|
+
};
|
|
3177
|
+
session.html = mountPoint.innerHTML;
|
|
3178
|
+
session.planId = planId;
|
|
3179
|
+
session.runtimeEvents = runtimeEvents;
|
|
3180
|
+
session.onRuntimeEvent = onRuntimeEvent;
|
|
3181
|
+
this.syncDelegatedListeners(mountPoint, session, delegatedEventTypes);
|
|
3182
|
+
this.mountSessions.set(mountPoint, session);
|
|
3183
|
+
}
|
|
3184
|
+
syncDelegatedListeners(mountPoint, session, delegatedEventTypes) {
|
|
3185
|
+
for (const [eventType, listener] of session.listeners.entries()) {
|
|
3186
|
+
if (!delegatedEventTypes.has(eventType)) {
|
|
3187
|
+
mountPoint.removeEventListener(eventType, listener);
|
|
3188
|
+
session.listeners.delete(eventType);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
for (const eventType of delegatedEventTypes) {
|
|
3192
|
+
if (session.listeners.has(eventType)) {
|
|
3193
|
+
continue;
|
|
3194
|
+
}
|
|
3195
|
+
const listener = (event) => {
|
|
3196
|
+
this.handleDelegatedRuntimeEvent(mountPoint, session, eventType, event);
|
|
3197
|
+
};
|
|
3198
|
+
mountPoint.addEventListener(eventType, listener);
|
|
3199
|
+
session.listeners.set(eventType, listener);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
handleDelegatedRuntimeEvent(mountPoint, session, domEvent, event) {
|
|
3203
|
+
const eventTarget = event.target;
|
|
3204
|
+
if (!eventTarget || !(eventTarget instanceof Element)) {
|
|
3205
|
+
return;
|
|
3206
|
+
}
|
|
3207
|
+
const bindingAttribute = getBindingAttributeName(domEvent);
|
|
3208
|
+
const matched = eventTarget.closest(`[${bindingAttribute}]`);
|
|
3209
|
+
if (!matched || !(matched instanceof Element) || !mountPoint.contains(matched)) {
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
const bindingId = matched.getAttribute(bindingAttribute);
|
|
3213
|
+
if (!bindingId) {
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
const runtimeEvent = session.runtimeEvents.get(bindingId);
|
|
3217
|
+
if (!runtimeEvent) {
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
const dispatchRequest = {
|
|
3221
|
+
planId: session.planId,
|
|
3222
|
+
event: runtimeEvent
|
|
3223
|
+
};
|
|
3224
|
+
if (typeof CustomEvent === "function") {
|
|
3225
|
+
mountPoint.dispatchEvent(
|
|
3226
|
+
new CustomEvent(
|
|
3227
|
+
"renderify:runtime-event",
|
|
3228
|
+
{
|
|
3229
|
+
bubbles: true,
|
|
3230
|
+
composed: true,
|
|
3231
|
+
detail: dispatchRequest
|
|
3232
|
+
}
|
|
3233
|
+
)
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
3236
|
+
if (session.onRuntimeEvent) {
|
|
3237
|
+
void Promise.resolve(session.onRuntimeEvent(dispatchRequest)).catch(
|
|
3238
|
+
() => {
|
|
3239
|
+
}
|
|
3240
|
+
);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
async loadPreactRenderer() {
|
|
3244
|
+
const loaded = await import(getPreactSpecifier2());
|
|
3245
|
+
if (!isPreactRendererLike(loaded)) {
|
|
3246
|
+
throw new Error("Failed to load preact renderer from `preact` package");
|
|
3247
|
+
}
|
|
3248
|
+
return loaded;
|
|
3249
|
+
}
|
|
3250
|
+
async loadPreactRenderToString() {
|
|
3251
|
+
const loaded = await import(getPreactRenderToStringSpecifier());
|
|
3252
|
+
if (!isPreactRenderToStringLike(loaded)) {
|
|
3253
|
+
throw new Error(
|
|
3254
|
+
"Failed to load preact-render-to-string from `preact-render-to-string` package"
|
|
3255
|
+
);
|
|
3256
|
+
}
|
|
3257
|
+
if (typeof loaded === "function") {
|
|
3258
|
+
return loaded;
|
|
3259
|
+
}
|
|
3260
|
+
if (typeof loaded.default === "function") {
|
|
3261
|
+
return loaded.default;
|
|
3262
|
+
}
|
|
3263
|
+
return loaded.render;
|
|
3264
|
+
}
|
|
3265
|
+
stringifyTarget(target) {
|
|
3266
|
+
if (typeof target === "string") {
|
|
3267
|
+
return target;
|
|
3268
|
+
}
|
|
3269
|
+
if (isInteractiveRenderTarget(target)) {
|
|
3270
|
+
if (typeof target.element === "string") {
|
|
3271
|
+
return target.element;
|
|
3272
|
+
}
|
|
3273
|
+
return "[interactive-target-element]";
|
|
3274
|
+
}
|
|
3275
|
+
return "[target-element]";
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
function isPreactRendererLike(value) {
|
|
3279
|
+
if (typeof value !== "object" || value === null) {
|
|
3280
|
+
return false;
|
|
3281
|
+
}
|
|
3282
|
+
const candidate = value;
|
|
3283
|
+
return typeof candidate.render === "function";
|
|
3284
|
+
}
|
|
3285
|
+
function isPreactRenderToStringLike(value) {
|
|
3286
|
+
if (typeof value === "function") {
|
|
3287
|
+
return true;
|
|
3288
|
+
}
|
|
3289
|
+
if (typeof value !== "object" || value === null) {
|
|
3290
|
+
return false;
|
|
3291
|
+
}
|
|
3292
|
+
const candidate = value;
|
|
3293
|
+
return typeof candidate.default === "function" || typeof candidate.render === "function";
|
|
3294
|
+
}
|
|
3295
|
+
function isInteractiveRenderTarget(value) {
|
|
3296
|
+
if (typeof value === "string") {
|
|
3297
|
+
return false;
|
|
3298
|
+
}
|
|
3299
|
+
if (typeof HTMLElement !== "undefined" && value instanceof HTMLElement) {
|
|
3300
|
+
return false;
|
|
3301
|
+
}
|
|
3302
|
+
return typeof value === "object" && value !== null && "element" in value && (typeof value.element === "string" || typeof HTMLElement !== "undefined" && value.element instanceof HTMLElement);
|
|
3303
|
+
}
|
|
3304
|
+
function getPreactSpecifier2() {
|
|
3305
|
+
return "preact";
|
|
3306
|
+
}
|
|
3307
|
+
function getPreactRenderToStringSpecifier() {
|
|
3308
|
+
return "preact-render-to-string";
|
|
3309
|
+
}
|
|
3310
|
+
function getBindingAttributeName(domEvent) {
|
|
3311
|
+
return `data-renderify-event-${domEvent}`;
|
|
3312
|
+
}
|
|
3313
|
+
var BLOCKED_TAG_NAMES = /* @__PURE__ */ new Set([
|
|
3314
|
+
"script",
|
|
3315
|
+
"style",
|
|
3316
|
+
"iframe",
|
|
3317
|
+
"object",
|
|
3318
|
+
"embed",
|
|
3319
|
+
"link",
|
|
3320
|
+
"meta",
|
|
3321
|
+
"base",
|
|
3322
|
+
"form"
|
|
3323
|
+
]);
|
|
3324
|
+
var BLOCKED_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
|
|
3325
|
+
"srcdoc",
|
|
3326
|
+
"innerhtml",
|
|
3327
|
+
"dangerouslysetinnerhtml"
|
|
3328
|
+
]);
|
|
3329
|
+
var URL_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
|
|
3330
|
+
"href",
|
|
3331
|
+
"src",
|
|
3332
|
+
"xlink:href",
|
|
3333
|
+
"action",
|
|
3334
|
+
"formaction",
|
|
3335
|
+
"poster"
|
|
3336
|
+
]);
|
|
3337
|
+
var SAFE_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
3338
|
+
var UNSAFE_STYLE_PATTERNS = [
|
|
3339
|
+
/\bexpression\s*\(/i,
|
|
3340
|
+
/\burl\s*\(/i,
|
|
3341
|
+
/\bjavascript\s*:/i,
|
|
3342
|
+
/\bdata\s*:/i,
|
|
3343
|
+
/@import/i,
|
|
3344
|
+
/\bbehavior\s*:/i,
|
|
3345
|
+
/-moz-binding/i
|
|
3346
|
+
];
|
|
3347
|
+
function sanitizeTagName(tag) {
|
|
3348
|
+
const normalized = normalizeTagName(tag);
|
|
3349
|
+
if (!normalized) {
|
|
3350
|
+
return void 0;
|
|
3351
|
+
}
|
|
3352
|
+
if (BLOCKED_TAG_NAMES.has(normalized)) {
|
|
3353
|
+
return void 0;
|
|
3354
|
+
}
|
|
3355
|
+
return normalized;
|
|
3356
|
+
}
|
|
3357
|
+
function normalizeTagName(tag) {
|
|
3358
|
+
const normalized = tag.trim().toLowerCase();
|
|
3359
|
+
if (!/^[a-z][a-z0-9-]*$/.test(normalized)) {
|
|
3360
|
+
return void 0;
|
|
3361
|
+
}
|
|
3362
|
+
return normalized;
|
|
3363
|
+
}
|
|
3364
|
+
function isSafeAttributeName(attributeName) {
|
|
3365
|
+
return /^[A-Za-z_:][A-Za-z0-9:._-]*$/.test(attributeName);
|
|
3366
|
+
}
|
|
3367
|
+
function isSafeAttributeUrl(value) {
|
|
3368
|
+
const trimmed = value.trim();
|
|
3369
|
+
if (trimmed.length === 0) {
|
|
3370
|
+
return false;
|
|
3371
|
+
}
|
|
3372
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
3373
|
+
return true;
|
|
3374
|
+
}
|
|
3375
|
+
const lowered = trimmed.toLowerCase();
|
|
3376
|
+
if (lowered.startsWith("javascript:") || lowered.startsWith("vbscript:") || lowered.startsWith("data:")) {
|
|
3377
|
+
return false;
|
|
3378
|
+
}
|
|
3379
|
+
if (/^[a-z][a-z0-9+.-]*:/.test(lowered)) {
|
|
3380
|
+
try {
|
|
3381
|
+
const parsed = new URL(trimmed);
|
|
3382
|
+
return SAFE_URL_PROTOCOLS.has(parsed.protocol);
|
|
3383
|
+
} catch {
|
|
3384
|
+
return false;
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
return true;
|
|
3388
|
+
}
|
|
3389
|
+
function serializeProps(props, context) {
|
|
3390
|
+
if (!props) {
|
|
3391
|
+
return "";
|
|
3392
|
+
}
|
|
3393
|
+
const attributes = [];
|
|
3394
|
+
let targetIsBlank = false;
|
|
3395
|
+
let relProvided = false;
|
|
3396
|
+
for (const [key, rawValue] of Object.entries(props)) {
|
|
3397
|
+
if (!isSafeAttributeName(key)) {
|
|
3398
|
+
continue;
|
|
3399
|
+
}
|
|
3400
|
+
const normalizedKey = key.toLowerCase();
|
|
3401
|
+
if (BLOCKED_ATTRIBUTE_NAMES.has(normalizedKey)) {
|
|
3402
|
+
continue;
|
|
3403
|
+
}
|
|
3404
|
+
if (key === "key") {
|
|
3405
|
+
if (typeof rawValue === "string" || typeof rawValue === "number") {
|
|
3406
|
+
attributes.push(
|
|
3407
|
+
` data-renderify-key="${escapeHtml(String(rawValue))}"`
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
continue;
|
|
3411
|
+
}
|
|
3412
|
+
const eventSpec = parseRuntimeEventProp(key, rawValue);
|
|
3413
|
+
if (eventSpec) {
|
|
3414
|
+
const bindingId = `evt_${String(++context.nextBindingId)}`;
|
|
3415
|
+
context.eventBindings.push({
|
|
3416
|
+
bindingId,
|
|
3417
|
+
domEvent: eventSpec.domEvent,
|
|
3418
|
+
runtimeEvent: eventSpec.runtimeEvent
|
|
3419
|
+
});
|
|
3420
|
+
attributes.push(
|
|
3421
|
+
` ${getBindingAttributeName(eventSpec.domEvent)}="${escapeHtml(bindingId)}"`
|
|
3422
|
+
);
|
|
3423
|
+
continue;
|
|
3424
|
+
}
|
|
3425
|
+
if (URL_ATTRIBUTE_NAMES.has(normalizedKey)) {
|
|
3426
|
+
if (typeof rawValue !== "string" || !isSafeAttributeUrl(rawValue)) {
|
|
3427
|
+
continue;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
if (normalizedKey === "style") {
|
|
3431
|
+
if (typeof rawValue !== "string") {
|
|
3432
|
+
continue;
|
|
3433
|
+
}
|
|
3434
|
+
const sanitizedStyle = sanitizeInlineStyle(rawValue);
|
|
3435
|
+
if (!sanitizedStyle) {
|
|
3436
|
+
continue;
|
|
3437
|
+
}
|
|
3438
|
+
attributes.push(` style="${escapeHtml(sanitizedStyle)}"`);
|
|
3439
|
+
continue;
|
|
3440
|
+
}
|
|
3441
|
+
if (key.startsWith("on")) {
|
|
3442
|
+
continue;
|
|
3443
|
+
}
|
|
3444
|
+
if (normalizedKey === "target" && String(rawValue).trim() === "_blank") {
|
|
3445
|
+
targetIsBlank = true;
|
|
3446
|
+
}
|
|
3447
|
+
if (normalizedKey === "rel") {
|
|
3448
|
+
relProvided = true;
|
|
3449
|
+
}
|
|
3450
|
+
if (typeof rawValue === "boolean") {
|
|
3451
|
+
if (rawValue) {
|
|
3452
|
+
attributes.push(` ${key}`);
|
|
3453
|
+
}
|
|
3454
|
+
continue;
|
|
3455
|
+
}
|
|
3456
|
+
if (rawValue === null || typeof rawValue === "object") {
|
|
3457
|
+
attributes.push(` ${key}='${escapeHtml(JSON.stringify(rawValue))}'`);
|
|
3458
|
+
continue;
|
|
3459
|
+
}
|
|
3460
|
+
attributes.push(` ${key}="${escapeHtml(String(rawValue))}"`);
|
|
3461
|
+
}
|
|
3462
|
+
if (targetIsBlank && !relProvided) {
|
|
3463
|
+
attributes.push(' rel="noopener noreferrer"');
|
|
3464
|
+
}
|
|
3465
|
+
return attributes.join("");
|
|
3466
|
+
}
|
|
3467
|
+
function sanitizeInlineStyle(style) {
|
|
3468
|
+
const normalized = style.trim();
|
|
3469
|
+
if (normalized.length === 0) {
|
|
3470
|
+
return void 0;
|
|
3471
|
+
}
|
|
3472
|
+
const inspected = normalizeStyleForSecurityInspection(normalized);
|
|
3473
|
+
if (inspected.length === 0) {
|
|
3474
|
+
return void 0;
|
|
3475
|
+
}
|
|
3476
|
+
for (const pattern of UNSAFE_STYLE_PATTERNS) {
|
|
3477
|
+
if (pattern.test(inspected)) {
|
|
3478
|
+
return void 0;
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
return normalized;
|
|
3482
|
+
}
|
|
3483
|
+
function normalizeStyleForSecurityInspection(style) {
|
|
3484
|
+
const withoutComments = style.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
3485
|
+
const decodedEscapes = decodeCssEscapes(withoutComments).replace(/\0/g, "");
|
|
3486
|
+
return decodedEscapes.replace(/\s+/g, " ").trim().toLowerCase();
|
|
3487
|
+
}
|
|
3488
|
+
function decodeCssEscapes(input) {
|
|
3489
|
+
return input.replace(
|
|
3490
|
+
/\\(?:([0-9a-fA-F]{1,6})(?:\r\n|[ \t\r\n\f])?|(.))/g,
|
|
3491
|
+
(_match, hexCodePoint, escapedChar) => {
|
|
3492
|
+
if (hexCodePoint) {
|
|
3493
|
+
const parsed = Number.parseInt(hexCodePoint, 16);
|
|
3494
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1114111) {
|
|
3495
|
+
return "";
|
|
3496
|
+
}
|
|
3497
|
+
return String.fromCodePoint(parsed);
|
|
3498
|
+
}
|
|
3499
|
+
return escapedChar;
|
|
3500
|
+
}
|
|
3501
|
+
);
|
|
3502
|
+
}
|
|
3503
|
+
function sanitizeRenderedFragment(fragment) {
|
|
3504
|
+
sanitizeRenderedSubtree(fragment);
|
|
3505
|
+
}
|
|
3506
|
+
function sanitizeRenderedSubtree(root) {
|
|
3507
|
+
const elements = Array.from(root.querySelectorAll("*"));
|
|
3508
|
+
for (const element of elements) {
|
|
3509
|
+
if (!element.isConnected) {
|
|
3510
|
+
continue;
|
|
3511
|
+
}
|
|
3512
|
+
const tagName = element.tagName.toLowerCase();
|
|
3513
|
+
if (!sanitizeTagName(tagName)) {
|
|
3514
|
+
replaceBlockedElement(element, tagName);
|
|
3515
|
+
continue;
|
|
3516
|
+
}
|
|
3517
|
+
sanitizeElementAttributes(element);
|
|
3518
|
+
const shadowRoot = element.shadowRoot;
|
|
3519
|
+
if (shadowRoot) {
|
|
3520
|
+
sanitizeRenderedSubtree(shadowRoot);
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
function replaceBlockedElement(element, tagName) {
|
|
3525
|
+
const replacement = element.ownerDocument.createElement("div");
|
|
3526
|
+
replacement.setAttribute("data-renderify-sanitized-tag", tagName);
|
|
3527
|
+
element.replaceWith(replacement);
|
|
3528
|
+
}
|
|
3529
|
+
function sanitizeElementAttributes(element) {
|
|
3530
|
+
let targetIsBlank = false;
|
|
3531
|
+
let relProvided = false;
|
|
3532
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
3533
|
+
const key = attribute.name;
|
|
3534
|
+
const value = attribute.value;
|
|
3535
|
+
const normalizedKey = key.toLowerCase();
|
|
3536
|
+
if (!isSafeAttributeName(key) || BLOCKED_ATTRIBUTE_NAMES.has(normalizedKey)) {
|
|
3537
|
+
element.removeAttribute(key);
|
|
3538
|
+
continue;
|
|
3539
|
+
}
|
|
3540
|
+
if (normalizedKey.startsWith("on")) {
|
|
3541
|
+
element.removeAttribute(key);
|
|
3542
|
+
continue;
|
|
3543
|
+
}
|
|
3544
|
+
if (URL_ATTRIBUTE_NAMES.has(normalizedKey) && !isSafeAttributeUrl(value)) {
|
|
3545
|
+
element.removeAttribute(key);
|
|
3546
|
+
continue;
|
|
3547
|
+
}
|
|
3548
|
+
if (normalizedKey === "style") {
|
|
3549
|
+
const sanitizedStyle = sanitizeInlineStyle(value);
|
|
3550
|
+
if (!sanitizedStyle) {
|
|
3551
|
+
element.removeAttribute(key);
|
|
3552
|
+
continue;
|
|
3553
|
+
}
|
|
3554
|
+
if (sanitizedStyle !== value) {
|
|
3555
|
+
element.setAttribute(key, sanitizedStyle);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
if (normalizedKey === "target" && value.trim() === "_blank") {
|
|
3559
|
+
targetIsBlank = true;
|
|
3560
|
+
}
|
|
3561
|
+
if (normalizedKey === "rel") {
|
|
3562
|
+
relProvided = true;
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
if (targetIsBlank && !relProvided) {
|
|
3566
|
+
element.setAttribute("rel", "noopener noreferrer");
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
function parseRuntimeEventProp(propName, value) {
|
|
3570
|
+
if (!/^on[A-Z]/.test(propName)) {
|
|
3571
|
+
return void 0;
|
|
3572
|
+
}
|
|
3573
|
+
const domEvent = propName.slice(2).toLowerCase();
|
|
3574
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(domEvent)) {
|
|
3575
|
+
return void 0;
|
|
3576
|
+
}
|
|
3577
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
3578
|
+
return {
|
|
3579
|
+
domEvent,
|
|
3580
|
+
runtimeEvent: {
|
|
3581
|
+
type: value.trim()
|
|
3582
|
+
}
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
if (!isJsonObject(value)) {
|
|
3586
|
+
return void 0;
|
|
3587
|
+
}
|
|
3588
|
+
const eventType = value.type;
|
|
3589
|
+
if (typeof eventType !== "string" || eventType.trim().length === 0) {
|
|
3590
|
+
return void 0;
|
|
3591
|
+
}
|
|
3592
|
+
const payload = value.payload;
|
|
3593
|
+
const runtimeEvent = {
|
|
3594
|
+
type: eventType.trim()
|
|
3595
|
+
};
|
|
3596
|
+
if (isJsonObject(payload)) {
|
|
3597
|
+
runtimeEvent.payload = payload;
|
|
3598
|
+
}
|
|
3599
|
+
return {
|
|
3600
|
+
domEvent,
|
|
3601
|
+
runtimeEvent
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
function isJsonObject(value) {
|
|
3605
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3606
|
+
}
|
|
3607
|
+
function escapeHtml(value) {
|
|
3608
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3609
|
+
}
|
|
3610
|
+
|
|
3611
|
+
// src/embed.ts
|
|
3612
|
+
var EMBED_TARGET_RENDER_LOCKS = /* @__PURE__ */ new WeakMap();
|
|
3613
|
+
async function renderPlanInBrowser(plan, options = {}) {
|
|
3614
|
+
const renderOperation = async () => {
|
|
3615
|
+
const ui = options.ui ?? new DefaultUIRenderer();
|
|
3616
|
+
const runtime = options.runtime ?? new DefaultRuntimeManager({
|
|
3617
|
+
moduleLoader: new JspmModuleLoader(),
|
|
3618
|
+
...options.runtimeOptions ?? {}
|
|
3619
|
+
});
|
|
3620
|
+
const security = options.security ?? new DefaultSecurityChecker();
|
|
3621
|
+
const shouldInitializeRuntime = options.autoInitializeRuntime !== false || options.runtime === void 0;
|
|
3622
|
+
const shouldTerminateRuntime = options.autoTerminateRuntime !== false && options.runtime === void 0;
|
|
3623
|
+
security.initialize(options.securityInitialization);
|
|
3624
|
+
if (shouldInitializeRuntime) {
|
|
3625
|
+
await runtime.initialize();
|
|
3626
|
+
}
|
|
3627
|
+
try {
|
|
3628
|
+
const securityResult = await security.checkPlan(plan);
|
|
3629
|
+
if (!securityResult.safe) {
|
|
3630
|
+
throw new RuntimeSecurityViolationError(securityResult);
|
|
3631
|
+
}
|
|
3632
|
+
const execution = await runtime.execute({
|
|
3633
|
+
plan,
|
|
3634
|
+
context: options.context,
|
|
3635
|
+
signal: options.signal
|
|
3636
|
+
});
|
|
3637
|
+
const html = await ui.render(execution, options.target);
|
|
3638
|
+
return {
|
|
3639
|
+
html,
|
|
3640
|
+
execution,
|
|
3641
|
+
security: securityResult,
|
|
3642
|
+
runtime
|
|
3643
|
+
};
|
|
3644
|
+
} finally {
|
|
3645
|
+
if (shouldTerminateRuntime) {
|
|
3646
|
+
await runtime.terminate();
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
};
|
|
3650
|
+
const shouldSerialize = options.serializeTargetRenders !== false;
|
|
3651
|
+
const targetElement = shouldSerialize ? resolveEmbedRenderTargetElement(options.target) : void 0;
|
|
3652
|
+
if (targetElement) {
|
|
3653
|
+
return withEmbedTargetRenderLock(targetElement, renderOperation);
|
|
3654
|
+
}
|
|
3655
|
+
return renderOperation();
|
|
3656
|
+
}
|
|
3657
|
+
async function withEmbedTargetRenderLock(target, operation) {
|
|
3658
|
+
const previousLock = EMBED_TARGET_RENDER_LOCKS.get(target) ?? Promise.resolve();
|
|
3659
|
+
let releaseCurrentLock;
|
|
3660
|
+
const currentLock = new Promise((resolve) => {
|
|
3661
|
+
releaseCurrentLock = resolve;
|
|
3662
|
+
});
|
|
3663
|
+
const queuedLock = previousLock.catch(() => void 0).then(() => currentLock);
|
|
3664
|
+
EMBED_TARGET_RENDER_LOCKS.set(target, queuedLock);
|
|
3665
|
+
await previousLock.catch(() => void 0);
|
|
3666
|
+
try {
|
|
3667
|
+
return await operation();
|
|
3668
|
+
} finally {
|
|
3669
|
+
releaseCurrentLock?.();
|
|
3670
|
+
if (EMBED_TARGET_RENDER_LOCKS.get(target) === queuedLock) {
|
|
3671
|
+
EMBED_TARGET_RENDER_LOCKS.delete(target);
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
function resolveEmbedRenderTargetElement(target) {
|
|
3676
|
+
if (typeof document === "undefined" || !target) {
|
|
3677
|
+
return void 0;
|
|
3678
|
+
}
|
|
3679
|
+
if (typeof target === "string") {
|
|
3680
|
+
return document.querySelector(target) ?? void 0;
|
|
3681
|
+
}
|
|
3682
|
+
if (typeof HTMLElement !== "undefined" && target instanceof HTMLElement) {
|
|
3683
|
+
return target;
|
|
3684
|
+
}
|
|
3685
|
+
if (isInteractiveRenderTargetValue(target)) {
|
|
3686
|
+
if (typeof target.element === "string") {
|
|
3687
|
+
return document.querySelector(target.element) ?? void 0;
|
|
3688
|
+
}
|
|
3689
|
+
return target.element;
|
|
3690
|
+
}
|
|
3691
|
+
return void 0;
|
|
3692
|
+
}
|
|
3693
|
+
function isInteractiveRenderTargetValue(target) {
|
|
3694
|
+
return typeof target === "object" && target !== null && "element" in target && (typeof target.element === "string" || typeof HTMLElement !== "undefined" && target.element instanceof HTMLElement);
|
|
3695
|
+
}
|
|
3696
|
+
export {
|
|
3697
|
+
BabelRuntimeSourceTranspiler,
|
|
3698
|
+
DefaultRuntimeManager,
|
|
3699
|
+
DefaultUIRenderer,
|
|
3700
|
+
JspmModuleLoader,
|
|
3701
|
+
RuntimeSecurityViolationError,
|
|
3702
|
+
renderPlanInBrowser
|
|
3703
|
+
};
|
|
3704
|
+
//# sourceMappingURL=runtime.esm.js.map
|