@ox-content/code-play 3.0.0-alpha.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 +97 -0
- package/dist/boot.d.mts +16 -0
- package/dist/boot.d.mts.map +1 -0
- package/dist/boot.mjs +20 -0
- package/dist/boot.mjs.map +1 -0
- package/dist/browser.d.mts +2 -0
- package/dist/browser.mjs +1846 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/client.mjs +1071 -0
- package/dist/client.mjs.map +1 -0
- package/dist/config.d.mts +221 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/hydrate.d.mts +67 -0
- package/dist/hydrate.d.mts.map +1 -0
- package/dist/hydrate.mjs +3 -0
- package/dist/hydrate2.d.mts +2 -0
- package/dist/hydrate2.mjs +387 -0
- package/dist/hydrate2.mjs.map +1 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +6 -0
- package/dist/payload.mjs +419 -0
- package/dist/payload.mjs.map +1 -0
- package/dist/plugin.d.mts +8 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +2 -0
- package/dist/plugin2.mjs +524 -0
- package/dist/plugin2.mjs.map +1 -0
- package/package.json +71 -0
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,1071 @@
|
|
|
1
|
+
import { a as escapeHtml, l as mergeConfig, p as resolveLanguage, u as resolveCodePlayOptions } from "./payload.mjs";
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __exportAll = (all, no_symbols) => {
|
|
5
|
+
let target = {};
|
|
6
|
+
for (var name in all) __defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
11
|
+
return target;
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/timing.ts
|
|
15
|
+
var PhaseTracker = class {
|
|
16
|
+
startedAt;
|
|
17
|
+
phases = [];
|
|
18
|
+
current;
|
|
19
|
+
constructor(now = nowMs) {
|
|
20
|
+
this.now = now;
|
|
21
|
+
this.startedAt = now();
|
|
22
|
+
}
|
|
23
|
+
now;
|
|
24
|
+
start(id, label) {
|
|
25
|
+
this.stop();
|
|
26
|
+
this.current = {
|
|
27
|
+
id,
|
|
28
|
+
label,
|
|
29
|
+
startMs: this.now() - this.startedAt
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
stop() {
|
|
33
|
+
if (!this.current) return;
|
|
34
|
+
const durationMs = Math.max(0, this.now() - this.startedAt - this.current.startMs);
|
|
35
|
+
this.phases.push({
|
|
36
|
+
id: this.current.id,
|
|
37
|
+
label: this.current.label,
|
|
38
|
+
startMs: this.current.startMs,
|
|
39
|
+
durationMs
|
|
40
|
+
});
|
|
41
|
+
this.current = void 0;
|
|
42
|
+
}
|
|
43
|
+
report() {
|
|
44
|
+
this.stop();
|
|
45
|
+
return {
|
|
46
|
+
totalMs: Math.max(0, this.now() - this.startedAt),
|
|
47
|
+
phases: this.phases.slice()
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function nowMs() {
|
|
52
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
53
|
+
}
|
|
54
|
+
function emptyTiming() {
|
|
55
|
+
return {
|
|
56
|
+
totalMs: 0,
|
|
57
|
+
phases: []
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/framework.ts
|
|
62
|
+
const RUNTIMES = {
|
|
63
|
+
vue: {
|
|
64
|
+
specifier: "vue",
|
|
65
|
+
cdn: "https://esm.sh/vue@3"
|
|
66
|
+
},
|
|
67
|
+
react: {
|
|
68
|
+
specifier: "react",
|
|
69
|
+
cdn: "https://esm.sh/react@19"
|
|
70
|
+
},
|
|
71
|
+
svelte: {
|
|
72
|
+
specifier: "svelte",
|
|
73
|
+
cdn: "https://esm.sh/svelte@5"
|
|
74
|
+
},
|
|
75
|
+
solid: {
|
|
76
|
+
specifier: "solid-js",
|
|
77
|
+
cdn: "https://esm.sh/solid-js@1"
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
async function runFramework(request) {
|
|
81
|
+
const tracker = new PhaseTracker();
|
|
82
|
+
tracker.start("compile", "Compile preview");
|
|
83
|
+
const framework = request.definition.framework ?? "vue";
|
|
84
|
+
const html = buildPreviewDocument(framework, request.code);
|
|
85
|
+
tracker.stop();
|
|
86
|
+
return {
|
|
87
|
+
status: "ok",
|
|
88
|
+
stdio: [],
|
|
89
|
+
diagnostics: [],
|
|
90
|
+
provenance: {
|
|
91
|
+
compile: {
|
|
92
|
+
host: "local",
|
|
93
|
+
runtime: `${framework}-preview`
|
|
94
|
+
},
|
|
95
|
+
execute: {
|
|
96
|
+
host: "iframe",
|
|
97
|
+
runtime: framework,
|
|
98
|
+
sandbox: "srcdoc"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
timing: tracker.report(),
|
|
102
|
+
preview: {
|
|
103
|
+
kind: "html",
|
|
104
|
+
html
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function buildPreviewDocument(framework, code) {
|
|
109
|
+
const runtime = RUNTIMES[framework];
|
|
110
|
+
return `<!doctype html>
|
|
111
|
+
<html>
|
|
112
|
+
<head>
|
|
113
|
+
<meta charset="utf-8">
|
|
114
|
+
<title>${escapeHtml(framework)} preview</title>
|
|
115
|
+
<script type="importmap">${JSON.stringify({ imports: { [runtime.specifier]: runtime.cdn } })}<\/script>
|
|
116
|
+
<style>html,body{margin:0;padding:1rem;font:14px/1.5 system-ui,sans-serif;}</style>
|
|
117
|
+
</head>
|
|
118
|
+
<body>
|
|
119
|
+
<div id="app"></div>
|
|
120
|
+
<script type="module">
|
|
121
|
+
${indentPreview(code)}
|
|
122
|
+
<\/script>
|
|
123
|
+
</body>
|
|
124
|
+
</html>
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
function indentPreview(code) {
|
|
128
|
+
return code.replace(/<\/script/gi, "<\\/script").split("\n").map((line) => ` ${line}`).join("\n");
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/stdio.ts
|
|
132
|
+
var StdioBuffer = class {
|
|
133
|
+
events = [];
|
|
134
|
+
startedAt;
|
|
135
|
+
constructor(startedAt = 0) {
|
|
136
|
+
this.startedAt = startedAt;
|
|
137
|
+
}
|
|
138
|
+
push(stream, text, timestampMs = elapsed(this.startedAt)) {
|
|
139
|
+
const event = {
|
|
140
|
+
stream,
|
|
141
|
+
text,
|
|
142
|
+
timestampMs
|
|
143
|
+
};
|
|
144
|
+
this.events.push(event);
|
|
145
|
+
return event;
|
|
146
|
+
}
|
|
147
|
+
snapshot() {
|
|
148
|
+
return this.events.slice();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
function joinStream(events, stream) {
|
|
152
|
+
return events.filter((event) => event.stream === stream).map((event) => event.text).join("");
|
|
153
|
+
}
|
|
154
|
+
function selectStream(events, stream) {
|
|
155
|
+
return events.filter((event) => event.stream === stream);
|
|
156
|
+
}
|
|
157
|
+
function withStdioText(result) {
|
|
158
|
+
return {
|
|
159
|
+
...result,
|
|
160
|
+
stdout: joinStream(result.stdio, "stdout"),
|
|
161
|
+
stderr: joinStream(result.stdio, "stderr")
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function formatConsoleArgs(args) {
|
|
165
|
+
return `${args.map(formatConsoleArg).join(" ")}\n`;
|
|
166
|
+
}
|
|
167
|
+
function formatConsoleArg(value) {
|
|
168
|
+
if (typeof value === "string") return value;
|
|
169
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
170
|
+
if (value === void 0) return "undefined";
|
|
171
|
+
if (value === null) return "null";
|
|
172
|
+
try {
|
|
173
|
+
return JSON.stringify(value);
|
|
174
|
+
} catch {
|
|
175
|
+
return Object.prototype.toString.call(value);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function elapsed(startedAt) {
|
|
179
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
180
|
+
return Math.max(0, now - startedAt);
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/go.ts
|
|
184
|
+
async function runGo(request, mode) {
|
|
185
|
+
const tracker = new PhaseTracker();
|
|
186
|
+
const stdio = new StdioBuffer(tracker.startedAt);
|
|
187
|
+
const params = new URLSearchParams({
|
|
188
|
+
version: "2",
|
|
189
|
+
body: request.code,
|
|
190
|
+
withVet: request.config.withVet === false ? "false" : "true"
|
|
191
|
+
});
|
|
192
|
+
tracker.start(mode === "typecheck" ? "typecheck" : "compile", mode === "typecheck" ? "Typecheck" : "Compile");
|
|
193
|
+
const response = await request.transport.request({
|
|
194
|
+
url: request.endpoints.go,
|
|
195
|
+
method: "POST",
|
|
196
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
197
|
+
body: params.toString(),
|
|
198
|
+
signal: request.signal
|
|
199
|
+
});
|
|
200
|
+
tracker.start("collect", "Collect output");
|
|
201
|
+
const parsed = parseResponse$2(response.text);
|
|
202
|
+
const diagnostics = [...parseGoErrors(parsed.Errors ?? "", "go"), ...parseGoErrors(parsed.VetErrors ?? "", "vet")];
|
|
203
|
+
for (const event of parsed.Events ?? []) {
|
|
204
|
+
const stream = event.Kind === "stderr" ? "stderr" : "stdout";
|
|
205
|
+
stdio.push(stream, event.Message ?? "");
|
|
206
|
+
}
|
|
207
|
+
if (parsed.Errors) stdio.push("stderr", parsed.Errors);
|
|
208
|
+
tracker.stop();
|
|
209
|
+
return {
|
|
210
|
+
status: diagnostics.some((item) => item.severity === "error") || !response.ok ? "error" : "ok",
|
|
211
|
+
stdio: stdio.snapshot(),
|
|
212
|
+
diagnostics,
|
|
213
|
+
provenance: {
|
|
214
|
+
compile: {
|
|
215
|
+
host: hostFromUrl$3(request.endpoints.go),
|
|
216
|
+
runtime: "go"
|
|
217
|
+
},
|
|
218
|
+
execute: mode === "execute" ? {
|
|
219
|
+
host: hostFromUrl$3(request.endpoints.go),
|
|
220
|
+
runtime: "go-playground",
|
|
221
|
+
sandbox: "playground"
|
|
222
|
+
} : void 0
|
|
223
|
+
},
|
|
224
|
+
timing: tracker.report()
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function parseResponse$2(text) {
|
|
228
|
+
try {
|
|
229
|
+
return JSON.parse(text);
|
|
230
|
+
} catch {
|
|
231
|
+
return { Errors: text };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function parseGoErrors(output, source) {
|
|
235
|
+
if (!output.trim()) return [];
|
|
236
|
+
return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
237
|
+
const match = /^(?:prog\.go:)?(\d+)(?::(\d+))?:\s*(.*)$/.exec(line);
|
|
238
|
+
return {
|
|
239
|
+
message: match?.[3] ?? line,
|
|
240
|
+
severity: "error",
|
|
241
|
+
line: match?.[1] ? Number(match[1]) : void 0,
|
|
242
|
+
column: match?.[2] ? Number(match[2]) : void 0,
|
|
243
|
+
source
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function hostFromUrl$3(url) {
|
|
248
|
+
try {
|
|
249
|
+
return new URL(url, "https://code-play.local").host || url;
|
|
250
|
+
} catch {
|
|
251
|
+
return url;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/transport.ts
|
|
256
|
+
function createFetchTransport(fetchImpl = fetch) {
|
|
257
|
+
return { async request(input) {
|
|
258
|
+
const response = await fetchImpl(input.url, {
|
|
259
|
+
method: input.method,
|
|
260
|
+
headers: input.headers,
|
|
261
|
+
body: input.body,
|
|
262
|
+
signal: input.signal
|
|
263
|
+
});
|
|
264
|
+
return {
|
|
265
|
+
ok: response.ok,
|
|
266
|
+
status: response.status,
|
|
267
|
+
text: await response.text()
|
|
268
|
+
};
|
|
269
|
+
} };
|
|
270
|
+
}
|
|
271
|
+
function createMemoryTransport(handler) {
|
|
272
|
+
return { async request(input) {
|
|
273
|
+
if (input.signal?.aborted) throw abortError();
|
|
274
|
+
return handler(input);
|
|
275
|
+
} };
|
|
276
|
+
}
|
|
277
|
+
var MissingTransportError = class extends Error {
|
|
278
|
+
constructor(host) {
|
|
279
|
+
super(`No Code Play transport is configured for ${host}.`);
|
|
280
|
+
this.name = "MissingTransportError";
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
function abortError() {
|
|
284
|
+
const error = /* @__PURE__ */ new Error("The Code Play run was cancelled.");
|
|
285
|
+
error.name = "AbortError";
|
|
286
|
+
return error;
|
|
287
|
+
}
|
|
288
|
+
function isAbortError(error) {
|
|
289
|
+
return Boolean(error && typeof error === "object" && "name" in error && error.name === "AbortError");
|
|
290
|
+
}
|
|
291
|
+
function createUnavailableTransport() {
|
|
292
|
+
return { request(input) {
|
|
293
|
+
if (input.signal?.aborted) return Promise.reject(abortError());
|
|
294
|
+
return Promise.reject(new MissingTransportError(input.url));
|
|
295
|
+
} };
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/javascript-sandbox.ts
|
|
299
|
+
const JS_SANDBOX_FLAGS = "allow-scripts";
|
|
300
|
+
function embedJson(value) {
|
|
301
|
+
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
302
|
+
}
|
|
303
|
+
function buildJavaScriptSandboxDocument(code, messageId) {
|
|
304
|
+
return `<!doctype html><html><head><meta charset="utf-8"></head><body><script>
|
|
305
|
+
(function () {
|
|
306
|
+
var id = ${embedJson(messageId)};
|
|
307
|
+
var stdout = [];
|
|
308
|
+
var stderr = [];
|
|
309
|
+
function format(args) {
|
|
310
|
+
return Array.prototype.map.call(args, function (value) {
|
|
311
|
+
if (typeof value === "string") return value;
|
|
312
|
+
if (value === undefined) return "undefined";
|
|
313
|
+
if (value === null) return "null";
|
|
314
|
+
try { return JSON.stringify(value); } catch (error) { return String(value); }
|
|
315
|
+
}).join(" ") + "\\n";
|
|
316
|
+
}
|
|
317
|
+
var consoleLike = {
|
|
318
|
+
log: function () { stdout.push(format(arguments)); },
|
|
319
|
+
info: function () { stdout.push(format(arguments)); },
|
|
320
|
+
warn: function () { stderr.push(format(arguments)); },
|
|
321
|
+
error: function () { stderr.push(format(arguments)); }
|
|
322
|
+
};
|
|
323
|
+
try {
|
|
324
|
+
var run = new Function("console", ${embedJson(`"use strict";\n${code}`)});
|
|
325
|
+
var value = run(consoleLike);
|
|
326
|
+
parent.postMessage({
|
|
327
|
+
id: id,
|
|
328
|
+
stdout: stdout,
|
|
329
|
+
stderr: stderr,
|
|
330
|
+
value: value === undefined ? undefined : String(value)
|
|
331
|
+
}, "*");
|
|
332
|
+
} catch (error) {
|
|
333
|
+
var message = error && error.message ? String(error.message) : String(error);
|
|
334
|
+
parent.postMessage({ id: id, stdout: stdout, stderr: stderr, error: message }, "*");
|
|
335
|
+
}
|
|
336
|
+
})();
|
|
337
|
+
<\/script></body></html>`;
|
|
338
|
+
}
|
|
339
|
+
function applySandboxStreams(stdio, message) {
|
|
340
|
+
for (const text of message.stdout ?? []) stdio.push("stdout", text);
|
|
341
|
+
for (const text of message.stderr ?? []) stdio.push("stderr", text);
|
|
342
|
+
}
|
|
343
|
+
async function executeInSandboxIframe(code, timeoutMs, stdio, signal) {
|
|
344
|
+
if (typeof document === "undefined" || typeof window === "undefined") throw new Error("JavaScript sandbox iframe needs a document.");
|
|
345
|
+
if (signal?.aborted) throw abortError();
|
|
346
|
+
const messageId = `ox-code-play-${Math.random().toString(36).slice(2)}`;
|
|
347
|
+
return new Promise((resolve, reject) => {
|
|
348
|
+
const frame = document.createElement("iframe");
|
|
349
|
+
frame.setAttribute("sandbox", JS_SANDBOX_FLAGS);
|
|
350
|
+
frame.setAttribute("title", "Code Play JavaScript sandbox");
|
|
351
|
+
frame.hidden = true;
|
|
352
|
+
const cleanup = () => {
|
|
353
|
+
window.clearTimeout(timer);
|
|
354
|
+
window.removeEventListener("message", onMessage);
|
|
355
|
+
signal?.removeEventListener("abort", onAbort);
|
|
356
|
+
frame.remove();
|
|
357
|
+
};
|
|
358
|
+
const onAbort = () => {
|
|
359
|
+
cleanup();
|
|
360
|
+
reject(abortError());
|
|
361
|
+
};
|
|
362
|
+
const onMessage = (event) => {
|
|
363
|
+
if (event.source !== frame.contentWindow || event.data?.id !== messageId) return;
|
|
364
|
+
cleanup();
|
|
365
|
+
applySandboxStreams(stdio, event.data);
|
|
366
|
+
if (event.data.error) {
|
|
367
|
+
reject(new Error(event.data.error));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
resolve(event.data.value);
|
|
371
|
+
};
|
|
372
|
+
const timer = window.setTimeout(() => {
|
|
373
|
+
cleanup();
|
|
374
|
+
reject(Object.assign(/* @__PURE__ */ new Error("JavaScript execution timed out."), { code: "ERR_SCRIPT_EXECUTION_TIMEOUT" }));
|
|
375
|
+
}, timeoutMs);
|
|
376
|
+
window.addEventListener("message", onMessage);
|
|
377
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
378
|
+
frame.srcdoc = buildJavaScriptSandboxDocument(code, messageId);
|
|
379
|
+
document.body.append(frame);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
//#endregion
|
|
383
|
+
//#region src/runtime-host.ts
|
|
384
|
+
/** True when the current isolate can load `node:vm`. */
|
|
385
|
+
function hasNodeVm() {
|
|
386
|
+
return typeof process !== "undefined" && Boolean(process.versions?.node);
|
|
387
|
+
}
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region src/javascript.ts
|
|
390
|
+
async function runJavaScript(request) {
|
|
391
|
+
const tracker = new PhaseTracker();
|
|
392
|
+
tracker.start("execute", "Execute");
|
|
393
|
+
const stdio = new StdioBuffer(tracker.startedAt);
|
|
394
|
+
const provenance = { execute: {
|
|
395
|
+
host: "local",
|
|
396
|
+
runtime: hasNodeVm() ? "node:vm" : "iframe",
|
|
397
|
+
sandbox: hasNodeVm() ? "vm" : "srcdoc"
|
|
398
|
+
} };
|
|
399
|
+
try {
|
|
400
|
+
const value = await executeScript(request.code, request.timeoutMs, stdio, request.signal);
|
|
401
|
+
tracker.stop();
|
|
402
|
+
return {
|
|
403
|
+
status: "ok",
|
|
404
|
+
stdio: stdio.snapshot(),
|
|
405
|
+
diagnostics: [],
|
|
406
|
+
provenance,
|
|
407
|
+
timing: tracker.report(),
|
|
408
|
+
value: value === void 0 ? void 0 : String(value)
|
|
409
|
+
};
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (isAbortError(error) || request.signal?.aborted) throw error;
|
|
412
|
+
tracker.stop();
|
|
413
|
+
const diagnostic = toDiagnostic(error);
|
|
414
|
+
stdio.push("stderr", `${diagnostic.message}\n`);
|
|
415
|
+
return {
|
|
416
|
+
status: isTimeout(error) ? "timeout" : "error",
|
|
417
|
+
stdio: stdio.snapshot(),
|
|
418
|
+
diagnostics: [diagnostic],
|
|
419
|
+
provenance,
|
|
420
|
+
timing: tracker.report()
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function executeScript(code, timeoutMs, stdio, signal) {
|
|
425
|
+
const consoleLike = {
|
|
426
|
+
log: (...args) => stdio.push("stdout", formatConsoleArgs(args)),
|
|
427
|
+
info: (...args) => stdio.push("stdout", formatConsoleArgs(args)),
|
|
428
|
+
warn: (...args) => stdio.push("stderr", formatConsoleArgs(args)),
|
|
429
|
+
error: (...args) => stdio.push("stderr", formatConsoleArgs(args))
|
|
430
|
+
};
|
|
431
|
+
if (signal?.aborted) throw abortError();
|
|
432
|
+
if (javascriptExecuteRuntime(hasNodeVm(), typeof document !== "undefined") === "vm") {
|
|
433
|
+
const vm = await import("node:vm");
|
|
434
|
+
const context = vm.createContext({ console: consoleLike });
|
|
435
|
+
return vm.runInContext(code, context, {
|
|
436
|
+
timeout: timeoutMs,
|
|
437
|
+
displayErrors: true
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
return executeInSandboxIframe(code, timeoutMs, stdio, signal);
|
|
441
|
+
}
|
|
442
|
+
function javascriptExecuteRuntime(hasVm, hasDocument) {
|
|
443
|
+
if (hasVm) return "vm";
|
|
444
|
+
if (hasDocument) return "iframe";
|
|
445
|
+
throw new Error("JavaScript execute needs node:vm or a document for the sandbox iframe.");
|
|
446
|
+
}
|
|
447
|
+
function isTimeout(error) {
|
|
448
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT");
|
|
449
|
+
}
|
|
450
|
+
function toDiagnostic(error) {
|
|
451
|
+
if (isErrorLike(error)) return {
|
|
452
|
+
message: error.message,
|
|
453
|
+
severity: "error",
|
|
454
|
+
source: "javascript"
|
|
455
|
+
};
|
|
456
|
+
return {
|
|
457
|
+
message: String(error),
|
|
458
|
+
severity: "error",
|
|
459
|
+
source: "javascript"
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function isErrorLike(error) {
|
|
463
|
+
return Boolean(error && typeof error === "object" && "message" in error && typeof error.message === "string" && error.message.length > 0);
|
|
464
|
+
}
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/remote.ts
|
|
467
|
+
async function runRemote(request) {
|
|
468
|
+
const tracker = new PhaseTracker();
|
|
469
|
+
const stdio = new StdioBuffer(tracker.startedAt);
|
|
470
|
+
const endpoint = request.enabled.endpoint;
|
|
471
|
+
const language = request.definition.remote?.pistonLanguage ?? request.definition.id;
|
|
472
|
+
if (!endpoint) {
|
|
473
|
+
tracker.stop();
|
|
474
|
+
return {
|
|
475
|
+
status: "unsupported",
|
|
476
|
+
stdio: [],
|
|
477
|
+
diagnostics: [{
|
|
478
|
+
message: `${request.definition.name} execution needs a configured HTTP executor. Pass languages.${request.definition.id}.endpoint (Piston-compatible).`,
|
|
479
|
+
severity: "error",
|
|
480
|
+
source: "code-play"
|
|
481
|
+
}],
|
|
482
|
+
provenance: {},
|
|
483
|
+
timing: tracker.report()
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
tracker.start("queue", "Queue");
|
|
487
|
+
tracker.start("compile", "Compile / execute");
|
|
488
|
+
const response = await request.transport.request({
|
|
489
|
+
url: joinEndpoint(endpoint, "execute"),
|
|
490
|
+
method: "POST",
|
|
491
|
+
headers: { "Content-Type": "application/json" },
|
|
492
|
+
body: JSON.stringify({
|
|
493
|
+
language,
|
|
494
|
+
version: request.config.version ?? request.definition.remote?.pistonVersion ?? "*",
|
|
495
|
+
files: [{ content: request.code }]
|
|
496
|
+
}),
|
|
497
|
+
signal: request.signal
|
|
498
|
+
});
|
|
499
|
+
tracker.start("collect", "Collect output");
|
|
500
|
+
const parsed = parseResponse$1(response.text);
|
|
501
|
+
if (parsed.compile?.stdout) stdio.push("stdout", parsed.compile.stdout);
|
|
502
|
+
if (parsed.compile?.stderr) stdio.push("stderr", parsed.compile.stderr);
|
|
503
|
+
if (parsed.run?.stdout) stdio.push("stdout", parsed.run.stdout);
|
|
504
|
+
if (parsed.run?.stderr) stdio.push("stderr", parsed.run.stderr);
|
|
505
|
+
if (parsed.message && !parsed.run && !parsed.compile) stdio.push("stderr", `${parsed.message}\n`);
|
|
506
|
+
const compileFailed = (parsed.compile?.code ?? 0) !== 0;
|
|
507
|
+
const runFailed = (parsed.run?.code ?? 0) !== 0 || Boolean(parsed.run?.signal);
|
|
508
|
+
const failed = !response.ok || compileFailed || runFailed || Boolean(parsed.message && !parsed.run);
|
|
509
|
+
tracker.stop();
|
|
510
|
+
return {
|
|
511
|
+
status: failed ? "error" : "ok",
|
|
512
|
+
stdio: stdio.snapshot(),
|
|
513
|
+
diagnostics: failed ? [{
|
|
514
|
+
message: parsed.message ?? parsed.run?.stderr ?? parsed.compile?.stderr ?? "Remote execution failed.",
|
|
515
|
+
severity: "error",
|
|
516
|
+
source: language
|
|
517
|
+
}] : [],
|
|
518
|
+
provenance: {
|
|
519
|
+
compile: parsed.compile ? {
|
|
520
|
+
host: hostFromUrl$2(endpoint),
|
|
521
|
+
runtime: language,
|
|
522
|
+
sandbox: "piston"
|
|
523
|
+
} : void 0,
|
|
524
|
+
execute: {
|
|
525
|
+
host: hostFromUrl$2(endpoint),
|
|
526
|
+
runtime: language,
|
|
527
|
+
sandbox: "piston"
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
timing: tracker.report()
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function parseResponse$1(text) {
|
|
534
|
+
try {
|
|
535
|
+
return JSON.parse(text);
|
|
536
|
+
} catch {
|
|
537
|
+
return { message: text };
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function joinEndpoint(endpoint, action) {
|
|
541
|
+
const trimmed = endpoint.replace(/\/+$/, "");
|
|
542
|
+
return trimmed.endsWith(action) ? trimmed : `${trimmed}/${action}`;
|
|
543
|
+
}
|
|
544
|
+
function hostFromUrl$2(url) {
|
|
545
|
+
try {
|
|
546
|
+
return new URL(url, "https://code-play.local").host || url;
|
|
547
|
+
} catch {
|
|
548
|
+
return url;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/rust.ts
|
|
553
|
+
async function runRust(request, mode) {
|
|
554
|
+
const tracker = new PhaseTracker();
|
|
555
|
+
const stdio = new StdioBuffer(tracker.startedAt);
|
|
556
|
+
const crateType = resolveCrateType(request.code, String(request.config.crateType ?? "auto"));
|
|
557
|
+
const body = {
|
|
558
|
+
channel: request.config.channel ?? "stable",
|
|
559
|
+
mode: request.config.mode ?? "debug",
|
|
560
|
+
edition: String(request.config.edition ?? "2024"),
|
|
561
|
+
crateType,
|
|
562
|
+
tests: false,
|
|
563
|
+
code: request.code,
|
|
564
|
+
backtrace: false
|
|
565
|
+
};
|
|
566
|
+
tracker.start(mode === "typecheck" ? "typecheck" : "compile", mode === "typecheck" ? "Typecheck" : "Compile");
|
|
567
|
+
const response = await request.transport.request({
|
|
568
|
+
url: request.endpoints.rust,
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "Content-Type": "application/json" },
|
|
571
|
+
body: JSON.stringify(body),
|
|
572
|
+
signal: request.signal
|
|
573
|
+
});
|
|
574
|
+
tracker.start("collect", "Collect output");
|
|
575
|
+
const parsed = parseResponse(response.text);
|
|
576
|
+
const diagnostics = parseRustcDiagnostics(parsed.stderr ?? parsed.error ?? "");
|
|
577
|
+
if (parsed.stdout) stdio.push("stdout", parsed.stdout);
|
|
578
|
+
if (parsed.stderr) stdio.push("stderr", parsed.stderr);
|
|
579
|
+
if (parsed.error && !parsed.stderr) stdio.push("stderr", parsed.error);
|
|
580
|
+
const success = parsed.success === true && response.ok;
|
|
581
|
+
const compileFailed = diagnostics.some((item) => item.severity === "error") || !success;
|
|
582
|
+
tracker.stop();
|
|
583
|
+
return {
|
|
584
|
+
status: compileFailed ? "error" : "ok",
|
|
585
|
+
stdio: stdio.snapshot(),
|
|
586
|
+
diagnostics,
|
|
587
|
+
provenance: {
|
|
588
|
+
compile: {
|
|
589
|
+
host: hostFromUrl$1(request.endpoints.rust),
|
|
590
|
+
runtime: "rustc",
|
|
591
|
+
version: String(request.config.channel ?? "stable"),
|
|
592
|
+
target: crateType
|
|
593
|
+
},
|
|
594
|
+
execute: mode === "execute" ? {
|
|
595
|
+
host: hostFromUrl$1(request.endpoints.rust),
|
|
596
|
+
runtime: "rust-playground",
|
|
597
|
+
sandbox: "playground"
|
|
598
|
+
} : void 0
|
|
599
|
+
},
|
|
600
|
+
timing: tracker.report()
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function resolveCrateType(code, configured) {
|
|
604
|
+
if (configured === "bin" || configured === "lib") return configured;
|
|
605
|
+
return /(?:^|\b)(?:async\s+)?fn\s+main\s*\(/.test(code) ? "bin" : "lib";
|
|
606
|
+
}
|
|
607
|
+
function parseResponse(text) {
|
|
608
|
+
try {
|
|
609
|
+
return JSON.parse(text);
|
|
610
|
+
} catch {
|
|
611
|
+
return {
|
|
612
|
+
success: false,
|
|
613
|
+
error: text
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function parseRustcDiagnostics(stderr) {
|
|
618
|
+
const diagnostics = [];
|
|
619
|
+
for (const match of stderr.matchAll(/^(error|warning|note)(?:\[([^\]]+)\])?:\s+(.*)$/gm)) {
|
|
620
|
+
const severity = match[1] === "warning" ? "warning" : match[1] === "note" ? "info" : "error";
|
|
621
|
+
diagnostics.push({
|
|
622
|
+
message: match[3] ?? "",
|
|
623
|
+
severity,
|
|
624
|
+
source: match[2] ? `rustc ${match[2]}` : "rustc"
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
return diagnostics;
|
|
628
|
+
}
|
|
629
|
+
function hostFromUrl$1(url) {
|
|
630
|
+
try {
|
|
631
|
+
return new URL(url, "https://code-play.local").host || url;
|
|
632
|
+
} catch {
|
|
633
|
+
return url;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/strip-typescript.ts
|
|
638
|
+
/**
|
|
639
|
+
* Conservative TypeScript-to-JavaScript stripper for documentation samples.
|
|
640
|
+
* Full checking goes through tsgo; this path only needs to run the snippet.
|
|
641
|
+
*/
|
|
642
|
+
function stripTypeScript(code) {
|
|
643
|
+
return code.replace(/^\s*import\s+type\s+.*$/gm, "").replace(/^\s*export\s+type\s+\w[\s\S]*?;\s*$/gm, "").replace(/^\s*type\s+\w[\s\S]*?;\s*$/gm, "").replace(/^\s*(?:export\s+)?interface\s+\w[\s\S]*?\{[\s\S]*?\n\}\s*$/gm, "").replace(/\s+as\s+const\b/g, "").replace(/\s+as\s+[^=,;)\n]+/g, "").replace(/\s+satisfies\s+[^=,;)\n]+/g, "").replace(/\)\s*:\s*[^{;=\n]+/g, ")").replace(/([?]?)\s*:\s*[^,)=;{\n]+/g, "$1");
|
|
644
|
+
}
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/typescript.ts
|
|
647
|
+
function resolveTypecheckBackend(hasVm, typecheckUrl) {
|
|
648
|
+
if (typecheckUrl && !hasVm) return "endpoint";
|
|
649
|
+
if (!hasVm) return "unavailable";
|
|
650
|
+
return "tsgo";
|
|
651
|
+
}
|
|
652
|
+
async function typecheckTypeScript(request) {
|
|
653
|
+
const tracker = new PhaseTracker();
|
|
654
|
+
tracker.start("typecheck", "Typecheck");
|
|
655
|
+
const backend = resolveTypecheckBackend(hasNodeVm(), request.endpoints.typecheck);
|
|
656
|
+
if (backend === "endpoint") return typecheckViaEndpoint(request, tracker);
|
|
657
|
+
if (backend === "unavailable") {
|
|
658
|
+
tracker.stop();
|
|
659
|
+
return {
|
|
660
|
+
status: "unsupported",
|
|
661
|
+
stdio: [],
|
|
662
|
+
diagnostics: [{
|
|
663
|
+
message: "Typecheck needs a reachable endpoints.typecheck. The Vite /__ox-code-play/typecheck proxy exists only during vite dev.",
|
|
664
|
+
severity: "error",
|
|
665
|
+
source: "tsgo"
|
|
666
|
+
}],
|
|
667
|
+
provenance: { compile: {
|
|
668
|
+
host: "local",
|
|
669
|
+
runtime: "tsgo"
|
|
670
|
+
} },
|
|
671
|
+
timing: tracker.report()
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
try {
|
|
675
|
+
const diagnostics = await typecheckWithTsgo(request.code, String(request.config.tsgoCommand ?? "tsgo"));
|
|
676
|
+
tracker.stop();
|
|
677
|
+
return {
|
|
678
|
+
status: diagnostics.some((item) => item.severity === "error") ? "error" : "ok",
|
|
679
|
+
stdio: [],
|
|
680
|
+
diagnostics,
|
|
681
|
+
provenance: { compile: {
|
|
682
|
+
host: "local",
|
|
683
|
+
runtime: "tsgo"
|
|
684
|
+
} },
|
|
685
|
+
timing: tracker.report()
|
|
686
|
+
};
|
|
687
|
+
} catch (error) {
|
|
688
|
+
if (isAbortError(error) || request.signal?.aborted) throw error;
|
|
689
|
+
tracker.stop();
|
|
690
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
691
|
+
return {
|
|
692
|
+
status: message.includes("ENOENT") ? "unsupported" : "error",
|
|
693
|
+
stdio: [],
|
|
694
|
+
diagnostics: [{
|
|
695
|
+
message: message.includes("ENOENT") ? "tsgo is not available. Install @typescript/native-preview or set languages.typescript.config.tsgoCommand." : message,
|
|
696
|
+
severity: "error",
|
|
697
|
+
source: "tsgo"
|
|
698
|
+
}],
|
|
699
|
+
provenance: { compile: {
|
|
700
|
+
host: "local",
|
|
701
|
+
runtime: "tsgo"
|
|
702
|
+
} },
|
|
703
|
+
timing: tracker.report()
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
async function runTypeScript(request) {
|
|
708
|
+
const tracker = new PhaseTracker();
|
|
709
|
+
const stdio = new StdioBuffer(tracker.startedAt);
|
|
710
|
+
tracker.start("compile", "Strip types");
|
|
711
|
+
const javascript = stripTypeScript(request.code);
|
|
712
|
+
tracker.start("execute", "Execute");
|
|
713
|
+
try {
|
|
714
|
+
const value = await executeScript(javascript, request.timeoutMs, stdio, request.signal);
|
|
715
|
+
tracker.stop();
|
|
716
|
+
return {
|
|
717
|
+
status: "ok",
|
|
718
|
+
stdio: stdio.snapshot(),
|
|
719
|
+
diagnostics: [],
|
|
720
|
+
provenance: {
|
|
721
|
+
compile: {
|
|
722
|
+
host: "local",
|
|
723
|
+
runtime: "strip-types"
|
|
724
|
+
},
|
|
725
|
+
execute: {
|
|
726
|
+
host: "local",
|
|
727
|
+
runtime: hasNodeVm() ? "node:vm" : "iframe",
|
|
728
|
+
sandbox: hasNodeVm() ? "vm" : "srcdoc"
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
timing: tracker.report(),
|
|
732
|
+
value: value === void 0 ? void 0 : String(value)
|
|
733
|
+
};
|
|
734
|
+
} catch (error) {
|
|
735
|
+
if (isAbortError(error) || request.signal?.aborted) throw error;
|
|
736
|
+
tracker.stop();
|
|
737
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
738
|
+
stdio.push("stderr", `${message}\n`);
|
|
739
|
+
return {
|
|
740
|
+
status: "error",
|
|
741
|
+
stdio: stdio.snapshot(),
|
|
742
|
+
diagnostics: [{
|
|
743
|
+
message,
|
|
744
|
+
severity: "error",
|
|
745
|
+
source: "javascript"
|
|
746
|
+
}],
|
|
747
|
+
provenance: {
|
|
748
|
+
compile: {
|
|
749
|
+
host: "local",
|
|
750
|
+
runtime: "strip-types"
|
|
751
|
+
},
|
|
752
|
+
execute: {
|
|
753
|
+
host: "local",
|
|
754
|
+
runtime: hasNodeVm() ? "node:vm" : "iframe",
|
|
755
|
+
sandbox: hasNodeVm() ? "vm" : "srcdoc"
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
timing: tracker.report()
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
async function typecheckViaEndpoint(request, tracker) {
|
|
763
|
+
const url = request.endpoints.typecheck ?? "";
|
|
764
|
+
const response = await request.transport.request({
|
|
765
|
+
url,
|
|
766
|
+
method: "POST",
|
|
767
|
+
headers: { "Content-Type": "application/json" },
|
|
768
|
+
body: JSON.stringify({
|
|
769
|
+
language: "typescript",
|
|
770
|
+
code: request.code,
|
|
771
|
+
config: request.config
|
|
772
|
+
}),
|
|
773
|
+
signal: request.signal
|
|
774
|
+
});
|
|
775
|
+
tracker.stop();
|
|
776
|
+
return adapterResultFromTypecheckResponse(response, url, tracker);
|
|
777
|
+
}
|
|
778
|
+
function typecheckEndpointFailureMessage(status, text) {
|
|
779
|
+
if (status === 404 || status === 405) return "Typecheck needs a reachable endpoints.typecheck. The Vite /__ox-code-play/typecheck proxy exists only during vite dev.";
|
|
780
|
+
return text.trim() || "Typecheck endpoint failed.";
|
|
781
|
+
}
|
|
782
|
+
function adapterResultFromTypecheckResponse(response, url, tracker) {
|
|
783
|
+
if (!response.ok) return {
|
|
784
|
+
status: "error",
|
|
785
|
+
stdio: [],
|
|
786
|
+
diagnostics: [{
|
|
787
|
+
message: typecheckEndpointFailureMessage(response.status, response.text),
|
|
788
|
+
severity: "error",
|
|
789
|
+
source: "tsgo"
|
|
790
|
+
}],
|
|
791
|
+
provenance: { compile: {
|
|
792
|
+
host: hostFromUrl(url),
|
|
793
|
+
runtime: "tsgo"
|
|
794
|
+
} },
|
|
795
|
+
timing: tracker.report()
|
|
796
|
+
};
|
|
797
|
+
try {
|
|
798
|
+
return JSON.parse(response.text);
|
|
799
|
+
} catch {
|
|
800
|
+
return {
|
|
801
|
+
status: "error",
|
|
802
|
+
stdio: [],
|
|
803
|
+
diagnostics: [{
|
|
804
|
+
message: response.text || "Typecheck endpoint failed.",
|
|
805
|
+
severity: "error",
|
|
806
|
+
source: "tsgo"
|
|
807
|
+
}],
|
|
808
|
+
provenance: { compile: {
|
|
809
|
+
host: hostFromUrl(url),
|
|
810
|
+
runtime: "tsgo"
|
|
811
|
+
} },
|
|
812
|
+
timing: tracker.report()
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async function typecheckWithTsgo(code, command = "tsgo") {
|
|
817
|
+
const [{ mkdtemp, rm, writeFile }, { tmpdir }, { join }, { execFile }, { promisify }] = await Promise.all([
|
|
818
|
+
import("node:fs/promises"),
|
|
819
|
+
import("node:os"),
|
|
820
|
+
import("node:path"),
|
|
821
|
+
import("node:child_process"),
|
|
822
|
+
import("node:util")
|
|
823
|
+
]);
|
|
824
|
+
const execFileAsync = promisify(execFile);
|
|
825
|
+
const dir = await mkdtemp(join(tmpdir(), "ox-code-play-"));
|
|
826
|
+
const file = join(dir, "snippet.ts");
|
|
827
|
+
await writeFile(file, code);
|
|
828
|
+
try {
|
|
829
|
+
await execFileAsync(command, [
|
|
830
|
+
"--noEmit",
|
|
831
|
+
"--pretty",
|
|
832
|
+
"false",
|
|
833
|
+
"--strict",
|
|
834
|
+
file
|
|
835
|
+
], {
|
|
836
|
+
cwd: dir,
|
|
837
|
+
maxBuffer: 1048576
|
|
838
|
+
});
|
|
839
|
+
return [];
|
|
840
|
+
} catch (error) {
|
|
841
|
+
const output = commandOutput(error);
|
|
842
|
+
if (error.code === "ENOENT") throw error;
|
|
843
|
+
return parseTsgoOutput(output);
|
|
844
|
+
} finally {
|
|
845
|
+
await rm(dir, {
|
|
846
|
+
recursive: true,
|
|
847
|
+
force: true
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
function parseTsgoOutput(output) {
|
|
852
|
+
const diagnostics = [];
|
|
853
|
+
for (const match of output.matchAll(/^(?:.*[\\/])?snippet\.ts\((\d+),(\d+)\):\s+(error|warning|info)\s+TS\d+:\s+(.*)$/gm)) diagnostics.push({
|
|
854
|
+
message: match[4] ?? output,
|
|
855
|
+
severity: match[3] === "warning" ? "warning" : match[3] === "info" ? "info" : "error",
|
|
856
|
+
line: Number(match[1]),
|
|
857
|
+
column: Number(match[2]),
|
|
858
|
+
source: "tsgo"
|
|
859
|
+
});
|
|
860
|
+
if (diagnostics.length === 0 && output.trim()) diagnostics.push({
|
|
861
|
+
message: output.trim(),
|
|
862
|
+
severity: "error",
|
|
863
|
+
source: "tsgo"
|
|
864
|
+
});
|
|
865
|
+
return diagnostics;
|
|
866
|
+
}
|
|
867
|
+
function commandOutput(error) {
|
|
868
|
+
if (!error || typeof error !== "object") return String(error);
|
|
869
|
+
const value = error;
|
|
870
|
+
return [
|
|
871
|
+
value.stdout,
|
|
872
|
+
value.stderr,
|
|
873
|
+
value.message
|
|
874
|
+
].filter((part) => typeof part === "string" && part.trim().length > 0).join("\n").trim();
|
|
875
|
+
}
|
|
876
|
+
function hostFromUrl(url) {
|
|
877
|
+
try {
|
|
878
|
+
return new URL(url, "https://code-play.local").host || url;
|
|
879
|
+
} catch {
|
|
880
|
+
return url;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
//#endregion
|
|
884
|
+
//#region src/adapters.ts
|
|
885
|
+
async function executeAdapter(request) {
|
|
886
|
+
if (!request.enabled.execute) return capabilityDisabled(request, "execute");
|
|
887
|
+
switch (request.definition.backend) {
|
|
888
|
+
case "javascript": return runJavaScript(request);
|
|
889
|
+
case "typescript": return runTypeScript(request);
|
|
890
|
+
case "framework": return runFramework(request);
|
|
891
|
+
case "rust-playground": return runRust(request, "execute");
|
|
892
|
+
case "go-playground": return runGo(request, "execute");
|
|
893
|
+
case "remote": return runRemote(request);
|
|
894
|
+
default: return capabilityDisabled(request, "execute");
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
async function typecheckAdapter(request) {
|
|
898
|
+
if (!request.enabled.typecheck || !request.definition.capabilities.typecheck) return capabilityDisabled(request, "typecheck");
|
|
899
|
+
switch (request.definition.backend) {
|
|
900
|
+
case "typescript": return typecheckTypeScript(request);
|
|
901
|
+
case "rust-playground": return runRust(request, "typecheck");
|
|
902
|
+
case "go-playground": return runGo(request, "typecheck");
|
|
903
|
+
default: return capabilityDisabled(request, "typecheck");
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
function capabilityDisabled(request, action) {
|
|
907
|
+
return {
|
|
908
|
+
status: "unsupported",
|
|
909
|
+
stdio: [],
|
|
910
|
+
diagnostics: [{
|
|
911
|
+
message: `${request.definition.name} ${action} is not enabled.`,
|
|
912
|
+
severity: "error",
|
|
913
|
+
source: "code-play"
|
|
914
|
+
}],
|
|
915
|
+
provenance: {},
|
|
916
|
+
timing: {
|
|
917
|
+
totalMs: 0,
|
|
918
|
+
phases: []
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
//#endregion
|
|
923
|
+
//#region src/result.ts
|
|
924
|
+
function errorMessage(error) {
|
|
925
|
+
return error instanceof Error && error.message ? error.message : String(error);
|
|
926
|
+
}
|
|
927
|
+
function friendlyTransportMessage(error) {
|
|
928
|
+
const message = errorMessage(error);
|
|
929
|
+
if ((error instanceof TypeError || error instanceof Error && error.name === "TypeError") && /failed to fetch|networkerror|load failed|network request failed/i.test(message)) return "The executor could not be reached from this page (often CORS). Set endpoints to a host that allows browser POST, or use the Vite dev proxy.";
|
|
930
|
+
return message;
|
|
931
|
+
}
|
|
932
|
+
function errorResult(message, source = "code-play", status = "error") {
|
|
933
|
+
return withStdioText({
|
|
934
|
+
status,
|
|
935
|
+
stdio: [],
|
|
936
|
+
diagnostics: [{
|
|
937
|
+
message,
|
|
938
|
+
severity: status === "cancelled" ? "info" : "error",
|
|
939
|
+
source
|
|
940
|
+
}],
|
|
941
|
+
provenance: {},
|
|
942
|
+
timing: emptyTiming()
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
//#endregion
|
|
946
|
+
//#region src/session.ts
|
|
947
|
+
var CodePlaySession = class {
|
|
948
|
+
language;
|
|
949
|
+
code;
|
|
950
|
+
config;
|
|
951
|
+
lastResult;
|
|
952
|
+
/** Last run's concatenated stdout (same as `lastResult.stdout`). */
|
|
953
|
+
get stdout() {
|
|
954
|
+
return this.lastResult?.stdout ?? "";
|
|
955
|
+
}
|
|
956
|
+
/** Last run's concatenated stderr (same as `lastResult.stderr`). */
|
|
957
|
+
get stderr() {
|
|
958
|
+
return this.lastResult?.stderr ?? "";
|
|
959
|
+
}
|
|
960
|
+
enabled;
|
|
961
|
+
timeoutMs;
|
|
962
|
+
transport;
|
|
963
|
+
endpoints;
|
|
964
|
+
loadTypeScript;
|
|
965
|
+
listeners = /* @__PURE__ */ new Map();
|
|
966
|
+
abort;
|
|
967
|
+
constructor(input) {
|
|
968
|
+
this.language = input.definition;
|
|
969
|
+
this.enabled = input.enabled;
|
|
970
|
+
this.code = input.code;
|
|
971
|
+
this.config = mergeConfig(input.definition.id, input.enabled, input.config);
|
|
972
|
+
this.timeoutMs = input.timeoutMs;
|
|
973
|
+
this.transport = input.transport;
|
|
974
|
+
this.endpoints = input.endpoints;
|
|
975
|
+
this.loadTypeScript = input.loadTypeScript;
|
|
976
|
+
}
|
|
977
|
+
on(event, listener) {
|
|
978
|
+
const bucket = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
979
|
+
bucket.add(listener);
|
|
980
|
+
this.listeners.set(event, bucket);
|
|
981
|
+
return () => bucket.delete(listener);
|
|
982
|
+
}
|
|
983
|
+
setCode(code) {
|
|
984
|
+
this.code = code;
|
|
985
|
+
}
|
|
986
|
+
setConfig(config) {
|
|
987
|
+
this.config = {
|
|
988
|
+
...this.config,
|
|
989
|
+
...config
|
|
990
|
+
};
|
|
991
|
+
this.emit("config", this.config);
|
|
992
|
+
}
|
|
993
|
+
async run() {
|
|
994
|
+
return this.dispatch("execute");
|
|
995
|
+
}
|
|
996
|
+
async typecheck() {
|
|
997
|
+
return this.dispatch("typecheck");
|
|
998
|
+
}
|
|
999
|
+
cancel() {
|
|
1000
|
+
this.abort?.abort();
|
|
1001
|
+
}
|
|
1002
|
+
async dispatch(action) {
|
|
1003
|
+
this.abort?.abort();
|
|
1004
|
+
this.abort = new AbortController();
|
|
1005
|
+
const { signal } = this.abort;
|
|
1006
|
+
const request = {
|
|
1007
|
+
definition: this.language,
|
|
1008
|
+
enabled: this.enabled,
|
|
1009
|
+
code: this.code,
|
|
1010
|
+
config: this.config,
|
|
1011
|
+
timeoutMs: this.timeoutMs,
|
|
1012
|
+
transport: this.transport,
|
|
1013
|
+
loadTypeScript: this.loadTypeScript,
|
|
1014
|
+
endpoints: this.endpoints,
|
|
1015
|
+
signal
|
|
1016
|
+
};
|
|
1017
|
+
try {
|
|
1018
|
+
const result = withStdioText(action === "typecheck" ? await typecheckAdapter(request) : await executeAdapter(request));
|
|
1019
|
+
return this.finish(result);
|
|
1020
|
+
} catch (error) {
|
|
1021
|
+
if (signal.aborted || isAbortError(error)) return this.finish(errorResult("Run cancelled.", "code-play", "cancelled"));
|
|
1022
|
+
return this.finish(errorResult(friendlyTransportMessage(error)));
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
finish(result) {
|
|
1026
|
+
this.lastResult = result;
|
|
1027
|
+
for (const event of result.stdio) this.emit("stdio", event);
|
|
1028
|
+
this.emit("result", result);
|
|
1029
|
+
return result;
|
|
1030
|
+
}
|
|
1031
|
+
emit(event, value) {
|
|
1032
|
+
for (const listener of this.listeners.get(event) ?? []) listener(value);
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
//#endregion
|
|
1036
|
+
//#region src/client.ts
|
|
1037
|
+
var client_exports = /* @__PURE__ */ __exportAll({ createCodePlay: () => createCodePlay });
|
|
1038
|
+
function createCodePlay(options = {}) {
|
|
1039
|
+
const resolved = resolveCodePlayOptions(options);
|
|
1040
|
+
const transport = options.transport ?? defaultTransport();
|
|
1041
|
+
return {
|
|
1042
|
+
options: resolved,
|
|
1043
|
+
hasLanguage(language) {
|
|
1044
|
+
const definition = resolveLanguage(language);
|
|
1045
|
+
return Boolean(definition && resolved.languages.has(definition.id));
|
|
1046
|
+
},
|
|
1047
|
+
createSession(input) {
|
|
1048
|
+
const definition = resolveLanguage(input.language);
|
|
1049
|
+
if (!definition) throw new Error(`Unknown Code Play language: ${input.language}.`);
|
|
1050
|
+
const enabled = resolved.languages.get(definition.id);
|
|
1051
|
+
if (!enabled) throw new Error(`${definition.name} is not enabled. Pass languages.${definition.id}: true to createCodePlay().`);
|
|
1052
|
+
return new CodePlaySession({
|
|
1053
|
+
...input,
|
|
1054
|
+
definition,
|
|
1055
|
+
enabled,
|
|
1056
|
+
timeoutMs: resolved.timeoutMs,
|
|
1057
|
+
transport,
|
|
1058
|
+
endpoints: resolved.endpoints,
|
|
1059
|
+
loadTypeScript: options.loadTypeScript
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
function defaultTransport() {
|
|
1065
|
+
if (typeof fetch === "function") return createFetchTransport();
|
|
1066
|
+
return createUnavailableTransport();
|
|
1067
|
+
}
|
|
1068
|
+
//#endregion
|
|
1069
|
+
export { errorResult as a, createMemoryTransport as c, withStdioText as d, PhaseTracker as f, errorMessage as i, joinStream as l, createCodePlay as n, JS_SANDBOX_FLAGS as o, CodePlaySession as r, createFetchTransport as s, client_exports as t, selectStream as u };
|
|
1070
|
+
|
|
1071
|
+
//# sourceMappingURL=client.mjs.map
|