typewright 0.2.1
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 +168 -0
- package/SPEC.md +327 -0
- package/dist/chunk-CZQO7TTL.js +1533 -0
- package/dist/chunk-CZQO7TTL.js.map +1 -0
- package/dist/chunk-KNEKNZXK.js +286 -0
- package/dist/chunk-KNEKNZXK.js.map +1 -0
- package/dist/chunk-M6IVEMXO.js +1398 -0
- package/dist/chunk-M6IVEMXO.js.map +1 -0
- package/dist/chunk-NJ7PJKHN.js +652 -0
- package/dist/chunk-NJ7PJKHN.js.map +1 -0
- package/dist/commands-Z3ndUSza.d.cts +221 -0
- package/dist/commands-Z3ndUSza.d.ts +221 -0
- package/dist/core/index.cjs +2952 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +410 -0
- package/dist/core/index.d.ts +410 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/mdx/index.cjs +662 -0
- package/dist/mdx/index.cjs.map +1 -0
- package/dist/mdx/index.d.cts +252 -0
- package/dist/mdx/index.d.ts +252 -0
- package/dist/mdx/index.js +3 -0
- package/dist/mdx/index.js.map +1 -0
- package/dist/react/index.cjs +7439 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +140 -0
- package/dist/react/index.d.ts +140 -0
- package/dist/react/index.js +3644 -0
- package/dist/react/index.js.map +1 -0
- package/dist/streaming/index.cjs +1494 -0
- package/dist/streaming/index.cjs.map +1 -0
- package/dist/streaming/index.d.cts +64 -0
- package/dist/streaming/index.d.ts +64 -0
- package/dist/streaming/index.js +4 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/types-CX1GOx0Y.d.cts +319 -0
- package/dist/types-CX1GOx0Y.d.ts +319 -0
- package/package.json +128 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/sandbox/host.ts
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null;
|
|
6
|
+
}
|
|
7
|
+
function shouldAcceptMessage(event, ctx) {
|
|
8
|
+
if (event.source !== ctx.source) return false;
|
|
9
|
+
const data = event.data;
|
|
10
|
+
if (!isRecord(data)) return false;
|
|
11
|
+
return data.token === ctx.token;
|
|
12
|
+
}
|
|
13
|
+
function dispatchInbound(data, handlers) {
|
|
14
|
+
const type = data.type;
|
|
15
|
+
if (type === "ready") {
|
|
16
|
+
handlers.onReady?.();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (type === "host") {
|
|
20
|
+
handlers.onHost?.(data.payload);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (typeof data.id === "number") {
|
|
24
|
+
handlers.onResult?.(data.id, {
|
|
25
|
+
html: typeof data.html === "string" ? data.html : void 0,
|
|
26
|
+
svg: typeof data.svg === "string" ? data.svg : void 0,
|
|
27
|
+
height: typeof data.height === "number" ? data.height : void 0,
|
|
28
|
+
error: typeof data.error === "string" ? data.error : void 0
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function buildEvaluateMessage(code, props) {
|
|
33
|
+
return { code, props: props ?? {} };
|
|
34
|
+
}
|
|
35
|
+
var MODULE_BINDINGS = 'var h=self.__tw.h,host=self.__tw.host,components=self.__tw.components,props=self.__tw.props,root=document.getElementById("tw-root");';
|
|
36
|
+
function buildSandboxCsp(extra) {
|
|
37
|
+
const base = "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: https:; font-src data:";
|
|
38
|
+
const trimmed = extra?.trim();
|
|
39
|
+
return trimmed ? `${base}; ${trimmed}` : base;
|
|
40
|
+
}
|
|
41
|
+
function createChannelToken() {
|
|
42
|
+
const cryptoObj = globalThis.crypto;
|
|
43
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
|
44
|
+
const bytes = new Uint8Array(16);
|
|
45
|
+
cryptoObj.getRandomValues(bytes);
|
|
46
|
+
let out = "";
|
|
47
|
+
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
return `${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
51
|
+
}
|
|
52
|
+
function neutralizeScriptClose(source) {
|
|
53
|
+
return source.replace(/<\/(script)/gi, "<\\/$1");
|
|
54
|
+
}
|
|
55
|
+
function buildSandboxSrcdoc(params) {
|
|
56
|
+
const { token, csp, engineSource } = params;
|
|
57
|
+
const bootstrap = SANDBOX_BOOTSTRAP.replace("__TW_TOKEN__", JSON.stringify(token));
|
|
58
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${csp}"><style>html,body{margin:0;padding:0;background:transparent;color:inherit;font:14px/1.5 system-ui,-apple-system,sans-serif}#tw-root{display:block}</style></head><body><div id="tw-root"></div>` + (engineSource ? `<script>
|
|
59
|
+
${neutralizeScriptClose(engineSource)}
|
|
60
|
+
</script>` : "") + `<script>
|
|
61
|
+
${bootstrap}
|
|
62
|
+
</script></body></html>`;
|
|
63
|
+
}
|
|
64
|
+
function createSandbox(opts = {}) {
|
|
65
|
+
if (typeof document === "undefined") {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"typewright/mdx: createSandbox requires a DOM (browser or jsdom); none was found."
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const token = createChannelToken();
|
|
71
|
+
const csp = buildSandboxCsp(opts.csp);
|
|
72
|
+
const srcdoc = buildSandboxSrcdoc({ token, csp, engineSource: opts.mermaidEngine });
|
|
73
|
+
const timeoutMs = opts.timeoutMs ?? 8e3;
|
|
74
|
+
const iframe = document.createElement("iframe");
|
|
75
|
+
iframe.setAttribute("sandbox", "allow-scripts");
|
|
76
|
+
iframe.setAttribute("aria-hidden", "true");
|
|
77
|
+
iframe.setAttribute("tabindex", "-1");
|
|
78
|
+
iframe.setAttribute("title", "typewright sandbox");
|
|
79
|
+
iframe.style.cssText = "position:absolute;width:0;height:0;border:0;visibility:hidden;pointer-events:none;";
|
|
80
|
+
iframe.srcdoc = srcdoc;
|
|
81
|
+
const container = opts.container ?? document.body ?? document.documentElement;
|
|
82
|
+
container.appendChild(iframe);
|
|
83
|
+
let destroyed = false;
|
|
84
|
+
let ready = false;
|
|
85
|
+
let nextId = 1;
|
|
86
|
+
const outbox = [];
|
|
87
|
+
const pending = /* @__PURE__ */ new Map();
|
|
88
|
+
function flush() {
|
|
89
|
+
if (!ready || destroyed) return;
|
|
90
|
+
const win = iframe.contentWindow;
|
|
91
|
+
if (!win) return;
|
|
92
|
+
while (outbox.length > 0) {
|
|
93
|
+
const msg = outbox.shift();
|
|
94
|
+
if (!msg) continue;
|
|
95
|
+
try {
|
|
96
|
+
win.postMessage(msg, "*");
|
|
97
|
+
} catch {
|
|
98
|
+
const id = msg.id;
|
|
99
|
+
if (typeof id === "number") {
|
|
100
|
+
settle(id, {
|
|
101
|
+
error: "sandbox payload is not serializable \u2014 MDX components/props must be structured-cloneable (functions are not supported across the sandbox)"
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function send(msg) {
|
|
108
|
+
outbox.push({ ...msg, token });
|
|
109
|
+
flush();
|
|
110
|
+
}
|
|
111
|
+
function settle(id, result) {
|
|
112
|
+
const entry = pending.get(id);
|
|
113
|
+
if (!entry) return;
|
|
114
|
+
pending.delete(id);
|
|
115
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
116
|
+
entry.resolve(result);
|
|
117
|
+
}
|
|
118
|
+
function request(kind, extra) {
|
|
119
|
+
if (destroyed) return Promise.resolve({ error: "sandbox destroyed" });
|
|
120
|
+
const id = nextId++;
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
const timer = timeoutMs > 0 ? setTimeout(
|
|
123
|
+
() => settle(id, { error: `sandbox ${kind} timed out after ${timeoutMs}ms` }),
|
|
124
|
+
timeoutMs
|
|
125
|
+
) : void 0;
|
|
126
|
+
pending.set(id, { resolve, timer });
|
|
127
|
+
send({ type: kind, id, ...extra });
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function onMessage(event) {
|
|
131
|
+
if (destroyed) return;
|
|
132
|
+
if (!shouldAcceptMessage(event, { source: iframe.contentWindow, token })) return;
|
|
133
|
+
dispatchInbound(event.data, {
|
|
134
|
+
onReady: () => {
|
|
135
|
+
ready = true;
|
|
136
|
+
flush();
|
|
137
|
+
},
|
|
138
|
+
onHost: (payload) => {
|
|
139
|
+
opts.onHostMessage?.(payload);
|
|
140
|
+
},
|
|
141
|
+
onResult: (id, result) => settle(id, result)
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
window.addEventListener("message", onMessage);
|
|
145
|
+
return {
|
|
146
|
+
async evaluate(code, props) {
|
|
147
|
+
const r = await request("evaluate", buildEvaluateMessage(code, props));
|
|
148
|
+
return { html: r.html, height: r.height, error: r.error };
|
|
149
|
+
},
|
|
150
|
+
async renderMermaid(src) {
|
|
151
|
+
const r = await request("mermaid", { src });
|
|
152
|
+
return { svg: r.svg, height: r.height, error: r.error };
|
|
153
|
+
},
|
|
154
|
+
destroy() {
|
|
155
|
+
if (destroyed) return;
|
|
156
|
+
destroyed = true;
|
|
157
|
+
window.removeEventListener("message", onMessage);
|
|
158
|
+
for (const [, entry] of pending) {
|
|
159
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
160
|
+
entry.resolve({ error: "sandbox destroyed" });
|
|
161
|
+
}
|
|
162
|
+
pending.clear();
|
|
163
|
+
iframe.remove();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
var SANDBOX_BOOTSTRAP = `
|
|
168
|
+
(function () {
|
|
169
|
+
var TOKEN = __TW_TOKEN__;
|
|
170
|
+
var root = document.getElementById('tw-root') || document.body;
|
|
171
|
+
|
|
172
|
+
function post(msg) { msg.token = TOKEN; parent.postMessage(msg, '*'); }
|
|
173
|
+
function measure() {
|
|
174
|
+
var de = document.documentElement, b = document.body;
|
|
175
|
+
return Math.max(
|
|
176
|
+
de ? de.scrollHeight : 0,
|
|
177
|
+
b ? b.scrollHeight : 0,
|
|
178
|
+
root ? root.scrollHeight : 0
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
function host(payload) { post({ type: 'host', payload: payload }); }
|
|
182
|
+
|
|
183
|
+
function appendChildren(el, children) {
|
|
184
|
+
for (var i = 0; i < children.length; i++) {
|
|
185
|
+
var c = children[i];
|
|
186
|
+
if (c == null || c === false || c === true) continue;
|
|
187
|
+
if (Object.prototype.toString.call(c) === '[object Array]') { appendChildren(el, c); continue; }
|
|
188
|
+
if (typeof Node !== 'undefined' && c instanceof Node) { el.appendChild(c); continue; }
|
|
189
|
+
el.appendChild(document.createTextNode(String(c)));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Hyperscript used by compiled MDX: builds real DOM (no innerHTML string
|
|
193
|
+
// concatenation), returns an element. A function tag is a component.
|
|
194
|
+
function h(tag, props) {
|
|
195
|
+
var children = Array.prototype.slice.call(arguments, 2);
|
|
196
|
+
if (typeof tag === 'function') {
|
|
197
|
+
var p = {}; for (var pk in (props || {})) p[pk] = props[pk]; p.children = children;
|
|
198
|
+
return tag(p);
|
|
199
|
+
}
|
|
200
|
+
var el = document.createElement(String(tag));
|
|
201
|
+
if (props) {
|
|
202
|
+
for (var key in props) {
|
|
203
|
+
var v = props[key];
|
|
204
|
+
if (v == null || v === false) continue;
|
|
205
|
+
if (key === 'className' || key === 'class') { el.setAttribute('class', String(v)); continue; }
|
|
206
|
+
if (key === 'style' && v && typeof v === 'object') {
|
|
207
|
+
for (var s in v) { try { el.style[s] = v[s]; } catch (e) {} }
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (key.slice(0, 2) === 'on') continue; // never wire inline handlers
|
|
211
|
+
if (v === true) { el.setAttribute(key, ''); continue; }
|
|
212
|
+
el.setAttribute(key, String(v));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
appendChildren(el, children);
|
|
216
|
+
return el;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
var RT = { h: h, host: host, props: {}, components: {}, done: null, error: null };
|
|
220
|
+
self.__tw = RT;
|
|
221
|
+
|
|
222
|
+
RT.done = function (id, out) {
|
|
223
|
+
Promise.resolve(out).then(function (value) {
|
|
224
|
+
try {
|
|
225
|
+
if (typeof Node !== 'undefined' && value instanceof Node) root.appendChild(value);
|
|
226
|
+
else if (typeof value === 'string') root.innerHTML = value;
|
|
227
|
+
// else: the module mounted itself into root
|
|
228
|
+
} catch (e) {}
|
|
229
|
+
post({ type: 'result', id: id, html: root.innerHTML, height: measure() });
|
|
230
|
+
}, function (err) {
|
|
231
|
+
post({ type: 'result', id: id, error: String((err && err.message) || err) });
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
RT.error = function (id, err) {
|
|
235
|
+
post({ type: 'result', id: id, error: String((err && err.message) || err) });
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
function runModule(id, code, props, components) {
|
|
239
|
+
RT.props = props || {};
|
|
240
|
+
RT.components = components || {};
|
|
241
|
+
// Inline-script injection (allowed by CSP 'unsafe-inline'; no eval). Setting
|
|
242
|
+
// textContent \u2014 not innerHTML \u2014 makes any '</script>' in code inert.
|
|
243
|
+
var wrapped =
|
|
244
|
+
'"use strict";(function(){' +
|
|
245
|
+
'${MODULE_BINDINGS}' +
|
|
246
|
+
'try{var __out=(function(){' + code + '\\n})();self.__tw.done(' + id + ',__out);}' +
|
|
247
|
+
'catch(__e){self.__tw.error(' + id + ',__e);}})();';
|
|
248
|
+
var s = document.createElement('script');
|
|
249
|
+
s.textContent = wrapped;
|
|
250
|
+
(document.body || document.documentElement).appendChild(s);
|
|
251
|
+
if (s.parentNode) s.parentNode.removeChild(s);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function renderMermaid(id, src) {
|
|
255
|
+
try {
|
|
256
|
+
var engine = null;
|
|
257
|
+
if (typeof self.__twMermaidRender === 'function') {
|
|
258
|
+
engine = self.__twMermaidRender;
|
|
259
|
+
} else if (self.mermaid && typeof self.mermaid.render === 'function') {
|
|
260
|
+
engine = function (s) {
|
|
261
|
+
return Promise.resolve(self.mermaid.render('tw-mmd-' + id, s)).then(function (r) {
|
|
262
|
+
return typeof r === 'string' ? r : (r && r.svg) || '';
|
|
263
|
+
});
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (!engine) { post({ type: 'result', id: id, error: 'no mermaid engine injected in sandbox' }); return; }
|
|
267
|
+
Promise.resolve(engine(src)).then(function (svg) {
|
|
268
|
+
try { root.innerHTML = svg || ''; } catch (e) {}
|
|
269
|
+
post({ type: 'result', id: id, svg: svg || '', height: measure() });
|
|
270
|
+
}, function (err) {
|
|
271
|
+
post({ type: 'result', id: id, error: String((err && err.message) || err) });
|
|
272
|
+
});
|
|
273
|
+
} catch (err) {
|
|
274
|
+
post({ type: 'result', id: id, error: String((err && err.message) || err) });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
window.addEventListener('message', function (event) {
|
|
279
|
+
if (event.source !== parent) return; // only the host frame
|
|
280
|
+
var data = event.data;
|
|
281
|
+
if (!data || typeof data !== 'object' || data.token !== TOKEN) return; // token gate
|
|
282
|
+
// The host component map travels under props.components (see
|
|
283
|
+
// buildEvaluateMessage); bind it as the module components map.
|
|
284
|
+
if (data.type === 'evaluate') runModule(data.id, data.code, data.props, data.props && data.props.components);
|
|
285
|
+
else if (data.type === 'mermaid') renderMermaid(data.id, data.src);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
post({ type: 'ready' });
|
|
289
|
+
})();
|
|
290
|
+
`;
|
|
291
|
+
|
|
292
|
+
// src/mdx/transform.ts
|
|
293
|
+
function resolveTransform(t) {
|
|
294
|
+
if (t === void 0) {
|
|
295
|
+
return async () => {
|
|
296
|
+
throw new Error("no MDX transform configured");
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
if (typeof t === "function") {
|
|
300
|
+
return async (code, meta) => t(code, meta ?? {});
|
|
301
|
+
}
|
|
302
|
+
switch (t) {
|
|
303
|
+
case "constrained":
|
|
304
|
+
return async (code) => compileConstrained(code);
|
|
305
|
+
case "wasm-esbuild":
|
|
306
|
+
return (code, meta) => transformWithEsbuild(code, meta);
|
|
307
|
+
case "wasm-swc":
|
|
308
|
+
return (code, meta) => transformWithSwc(code, meta);
|
|
309
|
+
default: {
|
|
310
|
+
const never = t;
|
|
311
|
+
throw new Error(`typewright/mdx: unknown MDX transform "${String(never)}"`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function importOptionalPeer(name) {
|
|
316
|
+
const specifier = name;
|
|
317
|
+
try {
|
|
318
|
+
return await import(
|
|
319
|
+
/* @vite-ignore */
|
|
320
|
+
specifier
|
|
321
|
+
);
|
|
322
|
+
} catch (cause) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`typewright/mdx: this MDX transform needs the optional peer dependency "${name}", which is not installed. Install it (e.g. \`npm i ${name}\`) or choose a different MdxTransform ("constrained" has zero dependencies).`,
|
|
325
|
+
{ cause }
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function loaderFor(meta) {
|
|
330
|
+
return /\.tsx?$/.test(meta?.filename ?? "") ? "tsx" : "jsx";
|
|
331
|
+
}
|
|
332
|
+
async function transformWithEsbuild(code, meta) {
|
|
333
|
+
const esbuild = await importOptionalPeer("esbuild-wasm");
|
|
334
|
+
const transform = esbuild.transform;
|
|
335
|
+
if (typeof transform !== "function") {
|
|
336
|
+
throw new Error('typewright/mdx: "esbuild-wasm" is installed but exposes no transform().');
|
|
337
|
+
}
|
|
338
|
+
const result = await transform(code, {
|
|
339
|
+
loader: loaderFor(meta),
|
|
340
|
+
jsx: "automatic",
|
|
341
|
+
format: "esm",
|
|
342
|
+
sourcefile: meta?.filename
|
|
343
|
+
});
|
|
344
|
+
return result.code;
|
|
345
|
+
}
|
|
346
|
+
async function transformWithSwc(code, meta) {
|
|
347
|
+
const swc = await importOptionalPeer("@swc/wasm-web");
|
|
348
|
+
const transform = swc.transform;
|
|
349
|
+
if (typeof transform !== "function") {
|
|
350
|
+
throw new Error('typewright/mdx: "@swc/wasm-web" is installed but exposes no transform().');
|
|
351
|
+
}
|
|
352
|
+
const tsx = loaderFor(meta) === "tsx";
|
|
353
|
+
const result = await transform(code, {
|
|
354
|
+
filename: meta?.filename,
|
|
355
|
+
jsc: {
|
|
356
|
+
parser: tsx ? { syntax: "typescript", tsx: true } : { syntax: "ecmascript", jsx: true },
|
|
357
|
+
transform: { react: { runtime: "automatic" } }
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
return result.code;
|
|
361
|
+
}
|
|
362
|
+
var TransformError = class extends Error {
|
|
363
|
+
constructor(message, index) {
|
|
364
|
+
super(`constrained MDX transform: ${message} (at offset ${index})`);
|
|
365
|
+
this.name = "TransformError";
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
var IDENT_START = /[A-Za-z_$]/;
|
|
369
|
+
var IDENT_PART = /[A-Za-z0-9_$]/;
|
|
370
|
+
var NAME_PART = /[A-Za-z0-9._-]/;
|
|
371
|
+
function compileConstrained(source) {
|
|
372
|
+
const { body, components } = new ConstrainedParser(source).parse();
|
|
373
|
+
const parts = [];
|
|
374
|
+
for (const name of components) {
|
|
375
|
+
parts.push(`const ${name} = components[${JSON.stringify(name)}];`);
|
|
376
|
+
}
|
|
377
|
+
parts.push(`return ${childrenExpr(body)};`);
|
|
378
|
+
return parts.join("\n");
|
|
379
|
+
}
|
|
380
|
+
function stripModuleLines(source) {
|
|
381
|
+
const kept = [];
|
|
382
|
+
for (const line of source.split("\n")) {
|
|
383
|
+
if (/^\s*(import|export)\b/.test(line)) {
|
|
384
|
+
kept.push("");
|
|
385
|
+
} else {
|
|
386
|
+
kept.push(line);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return kept.join("\n");
|
|
390
|
+
}
|
|
391
|
+
var ConstrainedParser = class {
|
|
392
|
+
src;
|
|
393
|
+
pos = 0;
|
|
394
|
+
components = /* @__PURE__ */ new Set();
|
|
395
|
+
constructor(rawSource) {
|
|
396
|
+
this.src = stripModuleLines(rawSource);
|
|
397
|
+
}
|
|
398
|
+
parse() {
|
|
399
|
+
const body = this.parseChildren(null);
|
|
400
|
+
this.skipInsignificantWhitespace();
|
|
401
|
+
if (this.pos < this.src.length) {
|
|
402
|
+
throw new TransformError(`unexpected "${this.src[this.pos]}"`, this.pos);
|
|
403
|
+
}
|
|
404
|
+
return { body, components: [...this.components] };
|
|
405
|
+
}
|
|
406
|
+
parseChildren(closeTag) {
|
|
407
|
+
const children = [];
|
|
408
|
+
for (; ; ) {
|
|
409
|
+
if (this.pos >= this.src.length) {
|
|
410
|
+
if (closeTag !== null) {
|
|
411
|
+
throw new TransformError(`unclosed <${closeTag}>`, this.pos);
|
|
412
|
+
}
|
|
413
|
+
return children;
|
|
414
|
+
}
|
|
415
|
+
const ch = this.src[this.pos];
|
|
416
|
+
if (ch === "<") {
|
|
417
|
+
if (this.src[this.pos + 1] === "/") {
|
|
418
|
+
if (closeTag === null) {
|
|
419
|
+
throw new TransformError("unexpected closing tag", this.pos);
|
|
420
|
+
}
|
|
421
|
+
return children;
|
|
422
|
+
}
|
|
423
|
+
children.push(this.parseElement());
|
|
424
|
+
} else if (ch === "{") {
|
|
425
|
+
children.push({ kind: "expr", source: this.parseBracedExpr() });
|
|
426
|
+
} else {
|
|
427
|
+
const text = this.parseText();
|
|
428
|
+
if (text.value.length > 0) children.push(text);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
parseText() {
|
|
433
|
+
let out = "";
|
|
434
|
+
while (this.pos < this.src.length) {
|
|
435
|
+
const ch = this.src[this.pos];
|
|
436
|
+
if (ch === "<" || ch === "{") break;
|
|
437
|
+
out += ch;
|
|
438
|
+
this.pos++;
|
|
439
|
+
}
|
|
440
|
+
return { kind: "text", value: out };
|
|
441
|
+
}
|
|
442
|
+
parseElement() {
|
|
443
|
+
const start = this.pos;
|
|
444
|
+
this.expect("<");
|
|
445
|
+
const name = this.parseName();
|
|
446
|
+
if (isComponentName(name)) this.components.add(componentRoot(name, start));
|
|
447
|
+
const attrs = [];
|
|
448
|
+
for (; ; ) {
|
|
449
|
+
this.skipWhitespace();
|
|
450
|
+
const ch = this.src[this.pos];
|
|
451
|
+
if (ch === void 0) throw new TransformError("unterminated tag", start);
|
|
452
|
+
if (ch === "/") {
|
|
453
|
+
this.expect("/");
|
|
454
|
+
this.expect(">");
|
|
455
|
+
return { kind: "element", name, attrs, children: [] };
|
|
456
|
+
}
|
|
457
|
+
if (ch === ">") {
|
|
458
|
+
this.pos++;
|
|
459
|
+
const children = this.parseChildren(name);
|
|
460
|
+
this.expect("<");
|
|
461
|
+
this.expect("/");
|
|
462
|
+
this.skipWhitespace();
|
|
463
|
+
const closeName = this.parseName();
|
|
464
|
+
if (closeName !== name) {
|
|
465
|
+
throw new TransformError(`mismatched closing tag </${closeName}> for <${name}>`, this.pos);
|
|
466
|
+
}
|
|
467
|
+
this.skipWhitespace();
|
|
468
|
+
this.expect(">");
|
|
469
|
+
return { kind: "element", name, attrs, children };
|
|
470
|
+
}
|
|
471
|
+
attrs.push(this.parseAttr());
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
parseAttr() {
|
|
475
|
+
const name = this.parseName();
|
|
476
|
+
this.skipWhitespace();
|
|
477
|
+
if (this.src[this.pos] !== "=") {
|
|
478
|
+
return { name };
|
|
479
|
+
}
|
|
480
|
+
this.pos++;
|
|
481
|
+
this.skipWhitespace();
|
|
482
|
+
const ch = this.src[this.pos];
|
|
483
|
+
if (ch === '"' || ch === "'") {
|
|
484
|
+
return { name, value: { kind: "string", value: this.parseStringLiteral(ch) } };
|
|
485
|
+
}
|
|
486
|
+
if (ch === "{") {
|
|
487
|
+
return { name, value: { kind: "expr", source: this.parseBracedExpr() } };
|
|
488
|
+
}
|
|
489
|
+
throw new TransformError(`attribute "${name}" must be a string literal or a {expression}`, this.pos);
|
|
490
|
+
}
|
|
491
|
+
parseName() {
|
|
492
|
+
const start = this.pos;
|
|
493
|
+
const first = this.src[this.pos];
|
|
494
|
+
if (first === void 0 || !/[A-Za-z]/.test(first)) {
|
|
495
|
+
throw new TransformError("expected a tag/attribute name", this.pos);
|
|
496
|
+
}
|
|
497
|
+
let out = first;
|
|
498
|
+
this.pos++;
|
|
499
|
+
while (this.pos < this.src.length && NAME_PART.test(this.src[this.pos])) {
|
|
500
|
+
out += this.src[this.pos];
|
|
501
|
+
this.pos++;
|
|
502
|
+
}
|
|
503
|
+
if (out.endsWith(".") || out.endsWith("-")) {
|
|
504
|
+
throw new TransformError(`invalid name "${out}"`, start);
|
|
505
|
+
}
|
|
506
|
+
return out;
|
|
507
|
+
}
|
|
508
|
+
parseStringLiteral(quote) {
|
|
509
|
+
this.expect(quote);
|
|
510
|
+
let out = "";
|
|
511
|
+
while (this.pos < this.src.length) {
|
|
512
|
+
const ch = this.src[this.pos];
|
|
513
|
+
if (ch === quote) {
|
|
514
|
+
this.pos++;
|
|
515
|
+
return out;
|
|
516
|
+
}
|
|
517
|
+
if (ch === "\\") {
|
|
518
|
+
out += ch;
|
|
519
|
+
this.pos++;
|
|
520
|
+
if (this.pos < this.src.length) {
|
|
521
|
+
out += this.src[this.pos];
|
|
522
|
+
this.pos++;
|
|
523
|
+
}
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
out += ch;
|
|
527
|
+
this.pos++;
|
|
528
|
+
}
|
|
529
|
+
throw new TransformError("unterminated string literal", this.pos);
|
|
530
|
+
}
|
|
531
|
+
/** Consume `{ … }` and return the validated restricted expression source. */
|
|
532
|
+
parseBracedExpr() {
|
|
533
|
+
const start = this.pos;
|
|
534
|
+
this.expect("{");
|
|
535
|
+
let depth = 1;
|
|
536
|
+
let raw = "";
|
|
537
|
+
while (this.pos < this.src.length && depth > 0) {
|
|
538
|
+
const ch = this.src[this.pos];
|
|
539
|
+
if (ch === "{") depth++;
|
|
540
|
+
else if (ch === "}") {
|
|
541
|
+
depth--;
|
|
542
|
+
if (depth === 0) {
|
|
543
|
+
this.pos++;
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
raw += ch;
|
|
548
|
+
this.pos++;
|
|
549
|
+
}
|
|
550
|
+
if (depth !== 0) throw new TransformError("unterminated {expression}", start);
|
|
551
|
+
return validateRestrictedExpr(raw.trim(), start);
|
|
552
|
+
}
|
|
553
|
+
expect(ch) {
|
|
554
|
+
if (this.src[this.pos] !== ch) {
|
|
555
|
+
throw new TransformError(`expected "${ch}"`, this.pos);
|
|
556
|
+
}
|
|
557
|
+
this.pos++;
|
|
558
|
+
}
|
|
559
|
+
skipWhitespace() {
|
|
560
|
+
while (this.pos < this.src.length && /\s/.test(this.src[this.pos])) this.pos++;
|
|
561
|
+
}
|
|
562
|
+
/** Skip trailing whitespace-only content when checking for EOF. */
|
|
563
|
+
skipInsignificantWhitespace() {
|
|
564
|
+
while (this.pos < this.src.length && /\s/.test(this.src[this.pos])) this.pos++;
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
function isComponentName(name) {
|
|
568
|
+
return /^[A-Z]/.test(name);
|
|
569
|
+
}
|
|
570
|
+
var SIMPLE_IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
571
|
+
function componentRoot(name, index) {
|
|
572
|
+
const segments = name.split(".");
|
|
573
|
+
for (const seg of segments) {
|
|
574
|
+
if (!SIMPLE_IDENT.test(seg)) {
|
|
575
|
+
throw new TransformError(
|
|
576
|
+
`unsupported component name "${name}" \u2014 component tags must be dot-separated identifiers`,
|
|
577
|
+
index
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return segments[0];
|
|
582
|
+
}
|
|
583
|
+
function validateRestrictedExpr(expr, index) {
|
|
584
|
+
if (expr.length === 0) {
|
|
585
|
+
throw new TransformError("empty {expression}", index);
|
|
586
|
+
}
|
|
587
|
+
if (expr === "true" || expr === "false" || expr === "null") return expr;
|
|
588
|
+
if (/^-?\d+(\.\d+)?$/.test(expr)) return expr;
|
|
589
|
+
if (isSimpleMemberPath(expr)) return expr;
|
|
590
|
+
throw new TransformError(
|
|
591
|
+
`unsupported expression \`${expr}\` \u2014 only numbers, true/false/null, an identifier, or a simple dotted member (a.b.c) are allowed`,
|
|
592
|
+
index
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
function isSimpleMemberPath(expr) {
|
|
596
|
+
const segments = expr.split(".");
|
|
597
|
+
for (const seg of segments) {
|
|
598
|
+
if (seg.length === 0) return false;
|
|
599
|
+
if (!IDENT_START.test(seg[0])) return false;
|
|
600
|
+
for (let i = 1; i < seg.length; i++) {
|
|
601
|
+
if (!IDENT_PART.test(seg[i])) return false;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
function childrenExpr(children) {
|
|
607
|
+
const rendered = children.map(nodeExpr).filter((s) => s !== null);
|
|
608
|
+
if (rendered.length === 0) return "null";
|
|
609
|
+
if (rendered.length === 1) return rendered[0];
|
|
610
|
+
return `[${rendered.join(", ")}]`;
|
|
611
|
+
}
|
|
612
|
+
function nodeExpr(node) {
|
|
613
|
+
if (node.kind === "text") {
|
|
614
|
+
if (node.value.trim().length === 0 && node.value.indexOf("\n") !== -1) {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
return JSON.stringify(node.value);
|
|
618
|
+
}
|
|
619
|
+
if (node.kind === "expr") {
|
|
620
|
+
return node.source;
|
|
621
|
+
}
|
|
622
|
+
const tag = isComponentName(node.name) ? node.name : JSON.stringify(node.name);
|
|
623
|
+
const props = attrsExpr(node.attrs);
|
|
624
|
+
const kids = node.children.map(nodeExpr).filter((s) => s !== null);
|
|
625
|
+
const args = [tag, props, ...kids];
|
|
626
|
+
return `h(${args.join(", ")})`;
|
|
627
|
+
}
|
|
628
|
+
function attrsExpr(attrs) {
|
|
629
|
+
if (attrs.length === 0) return "null";
|
|
630
|
+
const entries = attrs.map((attr) => {
|
|
631
|
+
const key = JSON.stringify(attr.name);
|
|
632
|
+
if (attr.value === void 0) return `${key}: true`;
|
|
633
|
+
if (attr.value.kind === "string") return `${key}: ${JSON.stringify(attr.value.value)}`;
|
|
634
|
+
return `${key}: ${attr.value.source}`;
|
|
635
|
+
});
|
|
636
|
+
return `{${entries.join(", ")}}`;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/mdx/index.ts
|
|
640
|
+
async function compileAndRun(opts) {
|
|
641
|
+
const compile = resolveTransform(opts.transform);
|
|
642
|
+
const js = await compile(opts.code, opts.meta);
|
|
643
|
+
const owned = opts.controller === void 0;
|
|
644
|
+
const sandbox = opts.controller ?? createSandbox(opts.sandbox);
|
|
645
|
+
try {
|
|
646
|
+
return await sandbox.evaluate(js, opts.props);
|
|
647
|
+
} finally {
|
|
648
|
+
if (owned) sandbox.destroy();
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
exports.buildSandboxCsp = buildSandboxCsp;
|
|
653
|
+
exports.buildSandboxSrcdoc = buildSandboxSrcdoc;
|
|
654
|
+
exports.compileAndRun = compileAndRun;
|
|
655
|
+
exports.compileConstrained = compileConstrained;
|
|
656
|
+
exports.createChannelToken = createChannelToken;
|
|
657
|
+
exports.createSandbox = createSandbox;
|
|
658
|
+
exports.dispatchInbound = dispatchInbound;
|
|
659
|
+
exports.resolveTransform = resolveTransform;
|
|
660
|
+
exports.shouldAcceptMessage = shouldAcceptMessage;
|
|
661
|
+
//# sourceMappingURL=index.cjs.map
|
|
662
|
+
//# sourceMappingURL=index.cjs.map
|