@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.
@@ -0,0 +1,1846 @@
1
+ //#region src/catalog.ts
2
+ var rustSchema = [
3
+ {
4
+ key: "channel",
5
+ label: "Channel",
6
+ type: "select",
7
+ options: [
8
+ {
9
+ value: "stable",
10
+ label: "stable"
11
+ },
12
+ {
13
+ value: "beta",
14
+ label: "beta"
15
+ },
16
+ {
17
+ value: "nightly",
18
+ label: "nightly"
19
+ }
20
+ ],
21
+ default: "stable"
22
+ },
23
+ {
24
+ key: "edition",
25
+ label: "Edition",
26
+ type: "select",
27
+ options: [
28
+ {
29
+ value: "2018",
30
+ label: "2018"
31
+ },
32
+ {
33
+ value: "2021",
34
+ label: "2021"
35
+ },
36
+ {
37
+ value: "2024",
38
+ label: "2024"
39
+ }
40
+ ],
41
+ default: "2024"
42
+ },
43
+ {
44
+ key: "mode",
45
+ label: "Mode",
46
+ type: "select",
47
+ options: [{
48
+ value: "debug",
49
+ label: "debug"
50
+ }, {
51
+ value: "release",
52
+ label: "release"
53
+ }],
54
+ default: "debug"
55
+ },
56
+ {
57
+ key: "crateType",
58
+ label: "Crate type",
59
+ type: "select",
60
+ options: [
61
+ {
62
+ value: "auto",
63
+ label: "auto"
64
+ },
65
+ {
66
+ value: "bin",
67
+ label: "bin"
68
+ },
69
+ {
70
+ value: "lib",
71
+ label: "lib"
72
+ }
73
+ ],
74
+ default: "auto"
75
+ }
76
+ ];
77
+ var tsSchema = [
78
+ {
79
+ key: "strict",
80
+ label: "Strict",
81
+ type: "boolean",
82
+ default: true
83
+ },
84
+ {
85
+ key: "target",
86
+ label: "Target",
87
+ type: "select",
88
+ options: [
89
+ {
90
+ value: "ES2020",
91
+ label: "ES2020"
92
+ },
93
+ {
94
+ value: "ES2022",
95
+ label: "ES2022"
96
+ },
97
+ {
98
+ value: "ESNext",
99
+ label: "ESNext"
100
+ }
101
+ ],
102
+ default: "ES2022"
103
+ },
104
+ {
105
+ key: "jsx",
106
+ label: "JSX",
107
+ type: "select",
108
+ options: [{
109
+ value: "react-jsx",
110
+ label: "react-jsx"
111
+ }, {
112
+ value: "preserve",
113
+ label: "preserve"
114
+ }],
115
+ default: "react-jsx"
116
+ }
117
+ ];
118
+ function remote(id, name, aliases, pistonLanguage, extra = []) {
119
+ return {
120
+ id,
121
+ name,
122
+ aliases,
123
+ capabilities: {
124
+ execute: true,
125
+ typecheck: false
126
+ },
127
+ defaultConfig: { version: "*" },
128
+ configSchema: [{
129
+ key: "version",
130
+ label: "Runtime version",
131
+ type: "string",
132
+ default: "*"
133
+ }, ...extra],
134
+ backend: "remote",
135
+ remote: { pistonLanguage }
136
+ };
137
+ }
138
+ function framework(id, name, aliases) {
139
+ return {
140
+ id,
141
+ name,
142
+ aliases,
143
+ capabilities: {
144
+ execute: true,
145
+ typecheck: false
146
+ },
147
+ defaultConfig: {},
148
+ configSchema: [],
149
+ backend: "framework",
150
+ framework: id
151
+ };
152
+ }
153
+ var LANGUAGE_CATALOG = [
154
+ {
155
+ id: "typescript",
156
+ name: "TypeScript",
157
+ aliases: [
158
+ "ts",
159
+ "tsx",
160
+ "mts",
161
+ "cts"
162
+ ],
163
+ capabilities: {
164
+ execute: true,
165
+ typecheck: true
166
+ },
167
+ defaultConfig: {
168
+ strict: true,
169
+ target: "ES2022",
170
+ jsx: "react-jsx"
171
+ },
172
+ configSchema: tsSchema,
173
+ backend: "typescript"
174
+ },
175
+ {
176
+ id: "javascript",
177
+ name: "JavaScript",
178
+ aliases: [
179
+ "js",
180
+ "jsx",
181
+ "mjs",
182
+ "cjs"
183
+ ],
184
+ capabilities: {
185
+ execute: true,
186
+ typecheck: false
187
+ },
188
+ defaultConfig: {},
189
+ configSchema: [],
190
+ backend: "javascript"
191
+ },
192
+ {
193
+ id: "rust",
194
+ name: "Rust",
195
+ aliases: ["rs"],
196
+ capabilities: {
197
+ execute: true,
198
+ typecheck: true
199
+ },
200
+ defaultConfig: {
201
+ channel: "stable",
202
+ edition: "2024",
203
+ mode: "debug",
204
+ crateType: "auto"
205
+ },
206
+ configSchema: rustSchema,
207
+ backend: "rust-playground"
208
+ },
209
+ {
210
+ id: "go",
211
+ name: "Go",
212
+ aliases: ["golang"],
213
+ capabilities: {
214
+ execute: true,
215
+ typecheck: true
216
+ },
217
+ defaultConfig: { withVet: true },
218
+ configSchema: [{
219
+ key: "withVet",
220
+ label: "Run vet",
221
+ type: "boolean",
222
+ default: true
223
+ }],
224
+ backend: "go-playground"
225
+ },
226
+ framework("vue", "Vue", ["vue"]),
227
+ framework("react", "React", ["react"]),
228
+ framework("svelte", "Svelte", ["svelte"]),
229
+ framework("solid", "Solid", ["solid"]),
230
+ remote("python", "Python", ["py"], "python"),
231
+ remote("php", "PHP", [], "php"),
232
+ remote("ruby", "Ruby", ["rb"], "ruby"),
233
+ remote("sh", "sh", [
234
+ "bash",
235
+ "shell",
236
+ "zsh"
237
+ ], "bash", [{
238
+ key: "shell",
239
+ label: "Shell",
240
+ type: "select",
241
+ options: [{
242
+ value: "bash",
243
+ label: "bash"
244
+ }, {
245
+ value: "sh",
246
+ label: "sh"
247
+ }],
248
+ default: "bash"
249
+ }]),
250
+ remote("java", "Java", [], "java"),
251
+ remote("swift", "Swift", [], "swift"),
252
+ remote("kotlin", "Kotlin", ["kt"], "kotlin"),
253
+ remote("c", "C", [], "c"),
254
+ remote("cpp", "C++", [
255
+ "c++",
256
+ "cc",
257
+ "cxx"
258
+ ], "c++", [{
259
+ key: "std",
260
+ label: "Standard",
261
+ type: "select",
262
+ options: [
263
+ {
264
+ value: "c++17",
265
+ label: "C++17"
266
+ },
267
+ {
268
+ value: "c++20",
269
+ label: "C++20"
270
+ },
271
+ {
272
+ value: "c++23",
273
+ label: "C++23"
274
+ }
275
+ ],
276
+ default: "c++20"
277
+ }]),
278
+ remote("zig", "Zig", [], "zig"),
279
+ remote("haskell", "Haskell", ["hs"], "haskell"),
280
+ remote("ocaml", "OCaml", ["ml"], "ocaml"),
281
+ remote("csharp", "C#", ["cs", "c#"], "csharp"),
282
+ remote("elixir", "Elixir", ["ex"], "elixir"),
283
+ remote("fsharp", "F#", ["fs", "f#"], "fsharp"),
284
+ remote("clojure", "Clojure", ["clj", "cloujure"], "clojure"),
285
+ remote("scheme", "Scheme", ["scm"], "scheme"),
286
+ remote("moonbit", "MoonBit", ["mbt"], "moonbit"),
287
+ remote("lean", "Lean", ["lean4"], "lean"),
288
+ remote("rocq", "Rocq", ["coq"], "coq")
289
+ ];
290
+ var byId = new Map(LANGUAGE_CATALOG.map((language) => [language.id, language]));
291
+ var byAlias = /* @__PURE__ */ new Map();
292
+ for (const language of LANGUAGE_CATALOG) {
293
+ byAlias.set(language.id, language);
294
+ for (const alias of language.aliases) byAlias.set(alias.toLowerCase(), language);
295
+ }
296
+ function resolveLanguage(input) {
297
+ return byAlias.get(input.trim().toLowerCase()) ?? byId.get(input.trim().toLowerCase());
298
+ }
299
+ //#endregion
300
+ //#region src/config.ts
301
+ var DEFAULT_ENDPOINTS = {
302
+ rust: "https://play.rust-lang.org/execute",
303
+ go: "https://play.golang.org/compile"
304
+ };
305
+ var DEFAULT_VIEWERS = {
306
+ config: true,
307
+ stdio: true,
308
+ stderr: true,
309
+ provenance: true,
310
+ timing: true
311
+ };
312
+ function resolveCodePlayOptions(options = {}) {
313
+ return {
314
+ languages: resolveEnabledLanguages(options.languages ?? {}),
315
+ timeoutMs: options.timeoutMs ?? 1e4,
316
+ ui: options.ui ?? "default",
317
+ viewers: {
318
+ ...DEFAULT_VIEWERS,
319
+ ...options.viewers
320
+ },
321
+ endpoints: {
322
+ ...DEFAULT_ENDPOINTS,
323
+ ...options.endpoints
324
+ },
325
+ srcDir: options.srcDir,
326
+ outDir: options.outDir,
327
+ base: normalizeBase(options.base ?? "/"),
328
+ proxy: options.proxy ?? true
329
+ };
330
+ }
331
+ function resolveEnabledLanguages(languages) {
332
+ const resolved = /* @__PURE__ */ new Map();
333
+ for (const [key, enable] of Object.entries(languages)) {
334
+ if (enable === false) continue;
335
+ const definition = resolveLanguage(key);
336
+ if (!definition) throw new Error(`Unknown Code Play language: ${key}.`);
337
+ const explicit = enable === true ? {} : enable;
338
+ resolved.set(definition.id, {
339
+ id: definition.id,
340
+ execute: explicit.execute ?? definition.capabilities.execute,
341
+ typecheck: explicit.typecheck ?? definition.capabilities.typecheck,
342
+ endpoint: explicit.endpoint,
343
+ config: {
344
+ ...definition.defaultConfig,
345
+ ...explicit.config
346
+ }
347
+ });
348
+ }
349
+ return resolved;
350
+ }
351
+ function mergeConfig(languageId, enabled, override) {
352
+ return {
353
+ ...resolveLanguage(languageId)?.defaultConfig,
354
+ ...enabled?.config,
355
+ ...override
356
+ };
357
+ }
358
+ function normalizeBase(base) {
359
+ if (base === "/" || base === "") return "/";
360
+ return base.endsWith("/") ? base : `${base}/`;
361
+ }
362
+ //#endregion
363
+ //#region src/escape.ts
364
+ function escapeHtml(value) {
365
+ return value.replace(/[&<>"']/g, (char) => {
366
+ switch (char) {
367
+ case "&": return "&amp;";
368
+ case "<": return "&lt;";
369
+ case ">": return "&gt;";
370
+ case "\"": return "&quot;";
371
+ default: return "&#39;";
372
+ }
373
+ });
374
+ }
375
+ //#endregion
376
+ //#region src/timing.ts
377
+ var PhaseTracker = class {
378
+ startedAt;
379
+ phases = [];
380
+ current;
381
+ constructor(now = nowMs) {
382
+ this.now = now;
383
+ this.startedAt = now();
384
+ }
385
+ now;
386
+ start(id, label) {
387
+ this.stop();
388
+ this.current = {
389
+ id,
390
+ label,
391
+ startMs: this.now() - this.startedAt
392
+ };
393
+ }
394
+ stop() {
395
+ if (!this.current) return;
396
+ const durationMs = Math.max(0, this.now() - this.startedAt - this.current.startMs);
397
+ this.phases.push({
398
+ id: this.current.id,
399
+ label: this.current.label,
400
+ startMs: this.current.startMs,
401
+ durationMs
402
+ });
403
+ this.current = void 0;
404
+ }
405
+ report() {
406
+ this.stop();
407
+ return {
408
+ totalMs: Math.max(0, this.now() - this.startedAt),
409
+ phases: this.phases.slice()
410
+ };
411
+ }
412
+ };
413
+ function nowMs() {
414
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
415
+ }
416
+ function emptyTiming() {
417
+ return {
418
+ totalMs: 0,
419
+ phases: []
420
+ };
421
+ }
422
+ //#endregion
423
+ //#region src/framework.ts
424
+ var RUNTIMES = {
425
+ vue: {
426
+ specifier: "vue",
427
+ cdn: "https://esm.sh/vue@3"
428
+ },
429
+ react: {
430
+ specifier: "react",
431
+ cdn: "https://esm.sh/react@19"
432
+ },
433
+ svelte: {
434
+ specifier: "svelte",
435
+ cdn: "https://esm.sh/svelte@5"
436
+ },
437
+ solid: {
438
+ specifier: "solid-js",
439
+ cdn: "https://esm.sh/solid-js@1"
440
+ }
441
+ };
442
+ async function runFramework(request) {
443
+ const tracker = new PhaseTracker();
444
+ tracker.start("compile", "Compile preview");
445
+ const framework = request.definition.framework ?? "vue";
446
+ const html = buildPreviewDocument(framework, request.code);
447
+ tracker.stop();
448
+ return {
449
+ status: "ok",
450
+ stdio: [],
451
+ diagnostics: [],
452
+ provenance: {
453
+ compile: {
454
+ host: "local",
455
+ runtime: `${framework}-preview`
456
+ },
457
+ execute: {
458
+ host: "iframe",
459
+ runtime: framework,
460
+ sandbox: "srcdoc"
461
+ }
462
+ },
463
+ timing: tracker.report(),
464
+ preview: {
465
+ kind: "html",
466
+ html
467
+ }
468
+ };
469
+ }
470
+ function buildPreviewDocument(framework, code) {
471
+ const runtime = RUNTIMES[framework];
472
+ return `<!doctype html>
473
+ <html>
474
+ <head>
475
+ <meta charset="utf-8">
476
+ <title>${escapeHtml(framework)} preview</title>
477
+ <script type="importmap">${JSON.stringify({ imports: { [runtime.specifier]: runtime.cdn } })}<\/script>
478
+ <style>html,body{margin:0;padding:1rem;font:14px/1.5 system-ui,sans-serif;}</style>
479
+ </head>
480
+ <body>
481
+ <div id="app"></div>
482
+ <script type="module">
483
+ ${indentPreview(code)}
484
+ <\/script>
485
+ </body>
486
+ </html>
487
+ `;
488
+ }
489
+ function indentPreview(code) {
490
+ return code.replace(/<\/script/gi, "<\\/script").split("\n").map((line) => ` ${line}`).join("\n");
491
+ }
492
+ //#endregion
493
+ //#region src/stdio.ts
494
+ var StdioBuffer = class {
495
+ events = [];
496
+ startedAt;
497
+ constructor(startedAt = 0) {
498
+ this.startedAt = startedAt;
499
+ }
500
+ push(stream, text, timestampMs = elapsed(this.startedAt)) {
501
+ const event = {
502
+ stream,
503
+ text,
504
+ timestampMs
505
+ };
506
+ this.events.push(event);
507
+ return event;
508
+ }
509
+ snapshot() {
510
+ return this.events.slice();
511
+ }
512
+ };
513
+ function joinStream(events, stream) {
514
+ return events.filter((event) => event.stream === stream).map((event) => event.text).join("");
515
+ }
516
+ function selectStream(events, stream) {
517
+ return events.filter((event) => event.stream === stream);
518
+ }
519
+ function withStdioText(result) {
520
+ return {
521
+ ...result,
522
+ stdout: joinStream(result.stdio, "stdout"),
523
+ stderr: joinStream(result.stdio, "stderr")
524
+ };
525
+ }
526
+ function formatConsoleArgs(args) {
527
+ return `${args.map(formatConsoleArg).join(" ")}\n`;
528
+ }
529
+ function formatConsoleArg(value) {
530
+ if (typeof value === "string") return value;
531
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
532
+ if (value === void 0) return "undefined";
533
+ if (value === null) return "null";
534
+ try {
535
+ return JSON.stringify(value);
536
+ } catch {
537
+ return Object.prototype.toString.call(value);
538
+ }
539
+ }
540
+ function elapsed(startedAt) {
541
+ const now = typeof performance !== "undefined" ? performance.now() : Date.now();
542
+ return Math.max(0, now - startedAt);
543
+ }
544
+ //#endregion
545
+ //#region src/go.ts
546
+ async function runGo(request, mode) {
547
+ const tracker = new PhaseTracker();
548
+ const stdio = new StdioBuffer(tracker.startedAt);
549
+ const params = new URLSearchParams({
550
+ version: "2",
551
+ body: request.code,
552
+ withVet: request.config.withVet === false ? "false" : "true"
553
+ });
554
+ tracker.start(mode === "typecheck" ? "typecheck" : "compile", mode === "typecheck" ? "Typecheck" : "Compile");
555
+ const response = await request.transport.request({
556
+ url: request.endpoints.go,
557
+ method: "POST",
558
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
559
+ body: params.toString(),
560
+ signal: request.signal
561
+ });
562
+ tracker.start("collect", "Collect output");
563
+ const parsed = parseResponse$2(response.text);
564
+ const diagnostics = [...parseGoErrors(parsed.Errors ?? "", "go"), ...parseGoErrors(parsed.VetErrors ?? "", "vet")];
565
+ for (const event of parsed.Events ?? []) {
566
+ const stream = event.Kind === "stderr" ? "stderr" : "stdout";
567
+ stdio.push(stream, event.Message ?? "");
568
+ }
569
+ if (parsed.Errors) stdio.push("stderr", parsed.Errors);
570
+ tracker.stop();
571
+ return {
572
+ status: diagnostics.some((item) => item.severity === "error") || !response.ok ? "error" : "ok",
573
+ stdio: stdio.snapshot(),
574
+ diagnostics,
575
+ provenance: {
576
+ compile: {
577
+ host: hostFromUrl$3(request.endpoints.go),
578
+ runtime: "go"
579
+ },
580
+ execute: mode === "execute" ? {
581
+ host: hostFromUrl$3(request.endpoints.go),
582
+ runtime: "go-playground",
583
+ sandbox: "playground"
584
+ } : void 0
585
+ },
586
+ timing: tracker.report()
587
+ };
588
+ }
589
+ function parseResponse$2(text) {
590
+ try {
591
+ return JSON.parse(text);
592
+ } catch {
593
+ return { Errors: text };
594
+ }
595
+ }
596
+ function parseGoErrors(output, source) {
597
+ if (!output.trim()) return [];
598
+ return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
599
+ const match = /^(?:prog\.go:)?(\d+)(?::(\d+))?:\s*(.*)$/.exec(line);
600
+ return {
601
+ message: match?.[3] ?? line,
602
+ severity: "error",
603
+ line: match?.[1] ? Number(match[1]) : void 0,
604
+ column: match?.[2] ? Number(match[2]) : void 0,
605
+ source
606
+ };
607
+ });
608
+ }
609
+ function hostFromUrl$3(url) {
610
+ try {
611
+ return new URL(url, "https://code-play.local").host || url;
612
+ } catch {
613
+ return url;
614
+ }
615
+ }
616
+ //#endregion
617
+ //#region src/transport.ts
618
+ function createFetchTransport(fetchImpl = fetch) {
619
+ return { async request(input) {
620
+ const response = await fetchImpl(input.url, {
621
+ method: input.method,
622
+ headers: input.headers,
623
+ body: input.body,
624
+ signal: input.signal
625
+ });
626
+ return {
627
+ ok: response.ok,
628
+ status: response.status,
629
+ text: await response.text()
630
+ };
631
+ } };
632
+ }
633
+ var MissingTransportError = class extends Error {
634
+ constructor(host) {
635
+ super(`No Code Play transport is configured for ${host}.`);
636
+ this.name = "MissingTransportError";
637
+ }
638
+ };
639
+ function abortError() {
640
+ const error = /* @__PURE__ */ new Error("The Code Play run was cancelled.");
641
+ error.name = "AbortError";
642
+ return error;
643
+ }
644
+ function isAbortError(error) {
645
+ return Boolean(error && typeof error === "object" && "name" in error && error.name === "AbortError");
646
+ }
647
+ function createUnavailableTransport() {
648
+ return { request(input) {
649
+ if (input.signal?.aborted) return Promise.reject(abortError());
650
+ return Promise.reject(new MissingTransportError(input.url));
651
+ } };
652
+ }
653
+ //#endregion
654
+ //#region src/javascript-sandbox.ts
655
+ var JS_SANDBOX_FLAGS = "allow-scripts";
656
+ function embedJson(value) {
657
+ return JSON.stringify(value).replace(/</g, "\\u003c");
658
+ }
659
+ function buildJavaScriptSandboxDocument(code, messageId) {
660
+ return `<!doctype html><html><head><meta charset="utf-8"></head><body><script>
661
+ (function () {
662
+ var id = ${embedJson(messageId)};
663
+ var stdout = [];
664
+ var stderr = [];
665
+ function format(args) {
666
+ return Array.prototype.map.call(args, function (value) {
667
+ if (typeof value === "string") return value;
668
+ if (value === undefined) return "undefined";
669
+ if (value === null) return "null";
670
+ try { return JSON.stringify(value); } catch (error) { return String(value); }
671
+ }).join(" ") + "\\n";
672
+ }
673
+ var consoleLike = {
674
+ log: function () { stdout.push(format(arguments)); },
675
+ info: function () { stdout.push(format(arguments)); },
676
+ warn: function () { stderr.push(format(arguments)); },
677
+ error: function () { stderr.push(format(arguments)); }
678
+ };
679
+ try {
680
+ var run = new Function("console", ${embedJson(`"use strict";\n${code}`)});
681
+ var value = run(consoleLike);
682
+ parent.postMessage({
683
+ id: id,
684
+ stdout: stdout,
685
+ stderr: stderr,
686
+ value: value === undefined ? undefined : String(value)
687
+ }, "*");
688
+ } catch (error) {
689
+ var message = error && error.message ? String(error.message) : String(error);
690
+ parent.postMessage({ id: id, stdout: stdout, stderr: stderr, error: message }, "*");
691
+ }
692
+ })();
693
+ <\/script></body></html>`;
694
+ }
695
+ function applySandboxStreams(stdio, message) {
696
+ for (const text of message.stdout ?? []) stdio.push("stdout", text);
697
+ for (const text of message.stderr ?? []) stdio.push("stderr", text);
698
+ }
699
+ async function executeInSandboxIframe(code, timeoutMs, stdio, signal) {
700
+ if (typeof document === "undefined" || typeof window === "undefined") throw new Error("JavaScript sandbox iframe needs a document.");
701
+ if (signal?.aborted) throw abortError();
702
+ const messageId = `ox-code-play-${Math.random().toString(36).slice(2)}`;
703
+ return new Promise((resolve, reject) => {
704
+ const frame = document.createElement("iframe");
705
+ frame.setAttribute("sandbox", JS_SANDBOX_FLAGS);
706
+ frame.setAttribute("title", "Code Play JavaScript sandbox");
707
+ frame.hidden = true;
708
+ const cleanup = () => {
709
+ window.clearTimeout(timer);
710
+ window.removeEventListener("message", onMessage);
711
+ signal?.removeEventListener("abort", onAbort);
712
+ frame.remove();
713
+ };
714
+ const onAbort = () => {
715
+ cleanup();
716
+ reject(abortError());
717
+ };
718
+ const onMessage = (event) => {
719
+ if (event.source !== frame.contentWindow || event.data?.id !== messageId) return;
720
+ cleanup();
721
+ applySandboxStreams(stdio, event.data);
722
+ if (event.data.error) {
723
+ reject(new Error(event.data.error));
724
+ return;
725
+ }
726
+ resolve(event.data.value);
727
+ };
728
+ const timer = window.setTimeout(() => {
729
+ cleanup();
730
+ reject(Object.assign(/* @__PURE__ */ new Error("JavaScript execution timed out."), { code: "ERR_SCRIPT_EXECUTION_TIMEOUT" }));
731
+ }, timeoutMs);
732
+ window.addEventListener("message", onMessage);
733
+ signal?.addEventListener("abort", onAbort, { once: true });
734
+ frame.srcdoc = buildJavaScriptSandboxDocument(code, messageId);
735
+ document.body.append(frame);
736
+ });
737
+ }
738
+ //#endregion
739
+ //#region src/runtime-host.ts
740
+ /** True when the current isolate can load `node:vm`. */
741
+ function hasNodeVm() {
742
+ return typeof process !== "undefined" && Boolean(process.versions?.node);
743
+ }
744
+ //#endregion
745
+ //#region src/javascript.ts
746
+ async function runJavaScript(request) {
747
+ const tracker = new PhaseTracker();
748
+ tracker.start("execute", "Execute");
749
+ const stdio = new StdioBuffer(tracker.startedAt);
750
+ const provenance = { execute: {
751
+ host: "local",
752
+ runtime: hasNodeVm() ? "node:vm" : "iframe",
753
+ sandbox: hasNodeVm() ? "vm" : "srcdoc"
754
+ } };
755
+ try {
756
+ const value = await executeScript(request.code, request.timeoutMs, stdio, request.signal);
757
+ tracker.stop();
758
+ return {
759
+ status: "ok",
760
+ stdio: stdio.snapshot(),
761
+ diagnostics: [],
762
+ provenance,
763
+ timing: tracker.report(),
764
+ value: value === void 0 ? void 0 : String(value)
765
+ };
766
+ } catch (error) {
767
+ if (isAbortError(error) || request.signal?.aborted) throw error;
768
+ tracker.stop();
769
+ const diagnostic = toDiagnostic(error);
770
+ stdio.push("stderr", `${diagnostic.message}\n`);
771
+ return {
772
+ status: isTimeout(error) ? "timeout" : "error",
773
+ stdio: stdio.snapshot(),
774
+ diagnostics: [diagnostic],
775
+ provenance,
776
+ timing: tracker.report()
777
+ };
778
+ }
779
+ }
780
+ async function executeScript(code, timeoutMs, stdio, signal) {
781
+ const consoleLike = {
782
+ log: (...args) => stdio.push("stdout", formatConsoleArgs(args)),
783
+ info: (...args) => stdio.push("stdout", formatConsoleArgs(args)),
784
+ warn: (...args) => stdio.push("stderr", formatConsoleArgs(args)),
785
+ error: (...args) => stdio.push("stderr", formatConsoleArgs(args))
786
+ };
787
+ if (signal?.aborted) throw abortError();
788
+ if (javascriptExecuteRuntime(hasNodeVm(), typeof document !== "undefined") === "vm") {
789
+ const vm = await import("node:vm");
790
+ const context = vm.createContext({ console: consoleLike });
791
+ return vm.runInContext(code, context, {
792
+ timeout: timeoutMs,
793
+ displayErrors: true
794
+ });
795
+ }
796
+ return executeInSandboxIframe(code, timeoutMs, stdio, signal);
797
+ }
798
+ function javascriptExecuteRuntime(hasVm, hasDocument) {
799
+ if (hasVm) return "vm";
800
+ if (hasDocument) return "iframe";
801
+ throw new Error("JavaScript execute needs node:vm or a document for the sandbox iframe.");
802
+ }
803
+ function isTimeout(error) {
804
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT");
805
+ }
806
+ function toDiagnostic(error) {
807
+ if (isErrorLike(error)) return {
808
+ message: error.message,
809
+ severity: "error",
810
+ source: "javascript"
811
+ };
812
+ return {
813
+ message: String(error),
814
+ severity: "error",
815
+ source: "javascript"
816
+ };
817
+ }
818
+ function isErrorLike(error) {
819
+ return Boolean(error && typeof error === "object" && "message" in error && typeof error.message === "string" && error.message.length > 0);
820
+ }
821
+ //#endregion
822
+ //#region src/remote.ts
823
+ async function runRemote(request) {
824
+ const tracker = new PhaseTracker();
825
+ const stdio = new StdioBuffer(tracker.startedAt);
826
+ const endpoint = request.enabled.endpoint;
827
+ const language = request.definition.remote?.pistonLanguage ?? request.definition.id;
828
+ if (!endpoint) {
829
+ tracker.stop();
830
+ return {
831
+ status: "unsupported",
832
+ stdio: [],
833
+ diagnostics: [{
834
+ message: `${request.definition.name} execution needs a configured HTTP executor. Pass languages.${request.definition.id}.endpoint (Piston-compatible).`,
835
+ severity: "error",
836
+ source: "code-play"
837
+ }],
838
+ provenance: {},
839
+ timing: tracker.report()
840
+ };
841
+ }
842
+ tracker.start("queue", "Queue");
843
+ tracker.start("compile", "Compile / execute");
844
+ const response = await request.transport.request({
845
+ url: joinEndpoint(endpoint, "execute"),
846
+ method: "POST",
847
+ headers: { "Content-Type": "application/json" },
848
+ body: JSON.stringify({
849
+ language,
850
+ version: request.config.version ?? request.definition.remote?.pistonVersion ?? "*",
851
+ files: [{ content: request.code }]
852
+ }),
853
+ signal: request.signal
854
+ });
855
+ tracker.start("collect", "Collect output");
856
+ const parsed = parseResponse$1(response.text);
857
+ if (parsed.compile?.stdout) stdio.push("stdout", parsed.compile.stdout);
858
+ if (parsed.compile?.stderr) stdio.push("stderr", parsed.compile.stderr);
859
+ if (parsed.run?.stdout) stdio.push("stdout", parsed.run.stdout);
860
+ if (parsed.run?.stderr) stdio.push("stderr", parsed.run.stderr);
861
+ if (parsed.message && !parsed.run && !parsed.compile) stdio.push("stderr", `${parsed.message}\n`);
862
+ const compileFailed = (parsed.compile?.code ?? 0) !== 0;
863
+ const runFailed = (parsed.run?.code ?? 0) !== 0 || Boolean(parsed.run?.signal);
864
+ const failed = !response.ok || compileFailed || runFailed || Boolean(parsed.message && !parsed.run);
865
+ tracker.stop();
866
+ return {
867
+ status: failed ? "error" : "ok",
868
+ stdio: stdio.snapshot(),
869
+ diagnostics: failed ? [{
870
+ message: parsed.message ?? parsed.run?.stderr ?? parsed.compile?.stderr ?? "Remote execution failed.",
871
+ severity: "error",
872
+ source: language
873
+ }] : [],
874
+ provenance: {
875
+ compile: parsed.compile ? {
876
+ host: hostFromUrl$2(endpoint),
877
+ runtime: language,
878
+ sandbox: "piston"
879
+ } : void 0,
880
+ execute: {
881
+ host: hostFromUrl$2(endpoint),
882
+ runtime: language,
883
+ sandbox: "piston"
884
+ }
885
+ },
886
+ timing: tracker.report()
887
+ };
888
+ }
889
+ function parseResponse$1(text) {
890
+ try {
891
+ return JSON.parse(text);
892
+ } catch {
893
+ return { message: text };
894
+ }
895
+ }
896
+ function joinEndpoint(endpoint, action) {
897
+ const trimmed = endpoint.replace(/\/+$/, "");
898
+ return trimmed.endsWith(action) ? trimmed : `${trimmed}/${action}`;
899
+ }
900
+ function hostFromUrl$2(url) {
901
+ try {
902
+ return new URL(url, "https://code-play.local").host || url;
903
+ } catch {
904
+ return url;
905
+ }
906
+ }
907
+ //#endregion
908
+ //#region src/rust.ts
909
+ async function runRust(request, mode) {
910
+ const tracker = new PhaseTracker();
911
+ const stdio = new StdioBuffer(tracker.startedAt);
912
+ const crateType = resolveCrateType(request.code, String(request.config.crateType ?? "auto"));
913
+ const body = {
914
+ channel: request.config.channel ?? "stable",
915
+ mode: request.config.mode ?? "debug",
916
+ edition: String(request.config.edition ?? "2024"),
917
+ crateType,
918
+ tests: false,
919
+ code: request.code,
920
+ backtrace: false
921
+ };
922
+ tracker.start(mode === "typecheck" ? "typecheck" : "compile", mode === "typecheck" ? "Typecheck" : "Compile");
923
+ const response = await request.transport.request({
924
+ url: request.endpoints.rust,
925
+ method: "POST",
926
+ headers: { "Content-Type": "application/json" },
927
+ body: JSON.stringify(body),
928
+ signal: request.signal
929
+ });
930
+ tracker.start("collect", "Collect output");
931
+ const parsed = parseResponse(response.text);
932
+ const diagnostics = parseRustcDiagnostics(parsed.stderr ?? parsed.error ?? "");
933
+ if (parsed.stdout) stdio.push("stdout", parsed.stdout);
934
+ if (parsed.stderr) stdio.push("stderr", parsed.stderr);
935
+ if (parsed.error && !parsed.stderr) stdio.push("stderr", parsed.error);
936
+ const success = parsed.success === true && response.ok;
937
+ const compileFailed = diagnostics.some((item) => item.severity === "error") || !success;
938
+ tracker.stop();
939
+ return {
940
+ status: compileFailed ? "error" : "ok",
941
+ stdio: stdio.snapshot(),
942
+ diagnostics,
943
+ provenance: {
944
+ compile: {
945
+ host: hostFromUrl$1(request.endpoints.rust),
946
+ runtime: "rustc",
947
+ version: String(request.config.channel ?? "stable"),
948
+ target: crateType
949
+ },
950
+ execute: mode === "execute" ? {
951
+ host: hostFromUrl$1(request.endpoints.rust),
952
+ runtime: "rust-playground",
953
+ sandbox: "playground"
954
+ } : void 0
955
+ },
956
+ timing: tracker.report()
957
+ };
958
+ }
959
+ function resolveCrateType(code, configured) {
960
+ if (configured === "bin" || configured === "lib") return configured;
961
+ return /(?:^|\b)(?:async\s+)?fn\s+main\s*\(/.test(code) ? "bin" : "lib";
962
+ }
963
+ function parseResponse(text) {
964
+ try {
965
+ return JSON.parse(text);
966
+ } catch {
967
+ return {
968
+ success: false,
969
+ error: text
970
+ };
971
+ }
972
+ }
973
+ function parseRustcDiagnostics(stderr) {
974
+ const diagnostics = [];
975
+ for (const match of stderr.matchAll(/^(error|warning|note)(?:\[([^\]]+)\])?:\s+(.*)$/gm)) {
976
+ const severity = match[1] === "warning" ? "warning" : match[1] === "note" ? "info" : "error";
977
+ diagnostics.push({
978
+ message: match[3] ?? "",
979
+ severity,
980
+ source: match[2] ? `rustc ${match[2]}` : "rustc"
981
+ });
982
+ }
983
+ return diagnostics;
984
+ }
985
+ function hostFromUrl$1(url) {
986
+ try {
987
+ return new URL(url, "https://code-play.local").host || url;
988
+ } catch {
989
+ return url;
990
+ }
991
+ }
992
+ //#endregion
993
+ //#region src/strip-typescript.ts
994
+ /**
995
+ * Conservative TypeScript-to-JavaScript stripper for documentation samples.
996
+ * Full checking goes through tsgo; this path only needs to run the snippet.
997
+ */
998
+ function stripTypeScript(code) {
999
+ 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");
1000
+ }
1001
+ //#endregion
1002
+ //#region src/typescript.ts
1003
+ function resolveTypecheckBackend(hasVm, typecheckUrl) {
1004
+ if (typecheckUrl && !hasVm) return "endpoint";
1005
+ if (!hasVm) return "unavailable";
1006
+ return "tsgo";
1007
+ }
1008
+ async function typecheckTypeScript(request) {
1009
+ const tracker = new PhaseTracker();
1010
+ tracker.start("typecheck", "Typecheck");
1011
+ const backend = resolveTypecheckBackend(hasNodeVm(), request.endpoints.typecheck);
1012
+ if (backend === "endpoint") return typecheckViaEndpoint(request, tracker);
1013
+ if (backend === "unavailable") {
1014
+ tracker.stop();
1015
+ return {
1016
+ status: "unsupported",
1017
+ stdio: [],
1018
+ diagnostics: [{
1019
+ message: "Typecheck needs a reachable endpoints.typecheck. The Vite /__ox-code-play/typecheck proxy exists only during vite dev.",
1020
+ severity: "error",
1021
+ source: "tsgo"
1022
+ }],
1023
+ provenance: { compile: {
1024
+ host: "local",
1025
+ runtime: "tsgo"
1026
+ } },
1027
+ timing: tracker.report()
1028
+ };
1029
+ }
1030
+ try {
1031
+ const diagnostics = await typecheckWithTsgo(request.code, String(request.config.tsgoCommand ?? "tsgo"));
1032
+ tracker.stop();
1033
+ return {
1034
+ status: diagnostics.some((item) => item.severity === "error") ? "error" : "ok",
1035
+ stdio: [],
1036
+ diagnostics,
1037
+ provenance: { compile: {
1038
+ host: "local",
1039
+ runtime: "tsgo"
1040
+ } },
1041
+ timing: tracker.report()
1042
+ };
1043
+ } catch (error) {
1044
+ if (isAbortError(error) || request.signal?.aborted) throw error;
1045
+ tracker.stop();
1046
+ const message = error instanceof Error ? error.message : String(error);
1047
+ return {
1048
+ status: message.includes("ENOENT") ? "unsupported" : "error",
1049
+ stdio: [],
1050
+ diagnostics: [{
1051
+ message: message.includes("ENOENT") ? "tsgo is not available. Install @typescript/native-preview or set languages.typescript.config.tsgoCommand." : message,
1052
+ severity: "error",
1053
+ source: "tsgo"
1054
+ }],
1055
+ provenance: { compile: {
1056
+ host: "local",
1057
+ runtime: "tsgo"
1058
+ } },
1059
+ timing: tracker.report()
1060
+ };
1061
+ }
1062
+ }
1063
+ async function runTypeScript(request) {
1064
+ const tracker = new PhaseTracker();
1065
+ const stdio = new StdioBuffer(tracker.startedAt);
1066
+ tracker.start("compile", "Strip types");
1067
+ const javascript = stripTypeScript(request.code);
1068
+ tracker.start("execute", "Execute");
1069
+ try {
1070
+ const value = await executeScript(javascript, request.timeoutMs, stdio, request.signal);
1071
+ tracker.stop();
1072
+ return {
1073
+ status: "ok",
1074
+ stdio: stdio.snapshot(),
1075
+ diagnostics: [],
1076
+ provenance: {
1077
+ compile: {
1078
+ host: "local",
1079
+ runtime: "strip-types"
1080
+ },
1081
+ execute: {
1082
+ host: "local",
1083
+ runtime: hasNodeVm() ? "node:vm" : "iframe",
1084
+ sandbox: hasNodeVm() ? "vm" : "srcdoc"
1085
+ }
1086
+ },
1087
+ timing: tracker.report(),
1088
+ value: value === void 0 ? void 0 : String(value)
1089
+ };
1090
+ } catch (error) {
1091
+ if (isAbortError(error) || request.signal?.aborted) throw error;
1092
+ tracker.stop();
1093
+ const message = error instanceof Error ? error.message : String(error);
1094
+ stdio.push("stderr", `${message}\n`);
1095
+ return {
1096
+ status: "error",
1097
+ stdio: stdio.snapshot(),
1098
+ diagnostics: [{
1099
+ message,
1100
+ severity: "error",
1101
+ source: "javascript"
1102
+ }],
1103
+ provenance: {
1104
+ compile: {
1105
+ host: "local",
1106
+ runtime: "strip-types"
1107
+ },
1108
+ execute: {
1109
+ host: "local",
1110
+ runtime: hasNodeVm() ? "node:vm" : "iframe",
1111
+ sandbox: hasNodeVm() ? "vm" : "srcdoc"
1112
+ }
1113
+ },
1114
+ timing: tracker.report()
1115
+ };
1116
+ }
1117
+ }
1118
+ async function typecheckViaEndpoint(request, tracker) {
1119
+ const url = request.endpoints.typecheck ?? "";
1120
+ const response = await request.transport.request({
1121
+ url,
1122
+ method: "POST",
1123
+ headers: { "Content-Type": "application/json" },
1124
+ body: JSON.stringify({
1125
+ language: "typescript",
1126
+ code: request.code,
1127
+ config: request.config
1128
+ }),
1129
+ signal: request.signal
1130
+ });
1131
+ tracker.stop();
1132
+ return adapterResultFromTypecheckResponse(response, url, tracker);
1133
+ }
1134
+ function typecheckEndpointFailureMessage(status, text) {
1135
+ if (status === 404 || status === 405) return "Typecheck needs a reachable endpoints.typecheck. The Vite /__ox-code-play/typecheck proxy exists only during vite dev.";
1136
+ return text.trim() || "Typecheck endpoint failed.";
1137
+ }
1138
+ function adapterResultFromTypecheckResponse(response, url, tracker) {
1139
+ if (!response.ok) return {
1140
+ status: "error",
1141
+ stdio: [],
1142
+ diagnostics: [{
1143
+ message: typecheckEndpointFailureMessage(response.status, response.text),
1144
+ severity: "error",
1145
+ source: "tsgo"
1146
+ }],
1147
+ provenance: { compile: {
1148
+ host: hostFromUrl(url),
1149
+ runtime: "tsgo"
1150
+ } },
1151
+ timing: tracker.report()
1152
+ };
1153
+ try {
1154
+ return JSON.parse(response.text);
1155
+ } catch {
1156
+ return {
1157
+ status: "error",
1158
+ stdio: [],
1159
+ diagnostics: [{
1160
+ message: response.text || "Typecheck endpoint failed.",
1161
+ severity: "error",
1162
+ source: "tsgo"
1163
+ }],
1164
+ provenance: { compile: {
1165
+ host: hostFromUrl(url),
1166
+ runtime: "tsgo"
1167
+ } },
1168
+ timing: tracker.report()
1169
+ };
1170
+ }
1171
+ }
1172
+ async function typecheckWithTsgo(code, command = "tsgo") {
1173
+ const [{ mkdtemp, rm, writeFile }, { tmpdir }, { join }, { execFile }, { promisify }] = await Promise.all([
1174
+ import("node:fs/promises"),
1175
+ import("node:os"),
1176
+ import("node:path"),
1177
+ import("node:child_process"),
1178
+ import("node:util")
1179
+ ]);
1180
+ const execFileAsync = promisify(execFile);
1181
+ const dir = await mkdtemp(join(tmpdir(), "ox-code-play-"));
1182
+ const file = join(dir, "snippet.ts");
1183
+ await writeFile(file, code);
1184
+ try {
1185
+ await execFileAsync(command, [
1186
+ "--noEmit",
1187
+ "--pretty",
1188
+ "false",
1189
+ "--strict",
1190
+ file
1191
+ ], {
1192
+ cwd: dir,
1193
+ maxBuffer: 1048576
1194
+ });
1195
+ return [];
1196
+ } catch (error) {
1197
+ const output = commandOutput(error);
1198
+ if (error.code === "ENOENT") throw error;
1199
+ return parseTsgoOutput(output);
1200
+ } finally {
1201
+ await rm(dir, {
1202
+ recursive: true,
1203
+ force: true
1204
+ });
1205
+ }
1206
+ }
1207
+ function parseTsgoOutput(output) {
1208
+ const diagnostics = [];
1209
+ for (const match of output.matchAll(/^(?:.*[\\/])?snippet\.ts\((\d+),(\d+)\):\s+(error|warning|info)\s+TS\d+:\s+(.*)$/gm)) diagnostics.push({
1210
+ message: match[4] ?? output,
1211
+ severity: match[3] === "warning" ? "warning" : match[3] === "info" ? "info" : "error",
1212
+ line: Number(match[1]),
1213
+ column: Number(match[2]),
1214
+ source: "tsgo"
1215
+ });
1216
+ if (diagnostics.length === 0 && output.trim()) diagnostics.push({
1217
+ message: output.trim(),
1218
+ severity: "error",
1219
+ source: "tsgo"
1220
+ });
1221
+ return diagnostics;
1222
+ }
1223
+ function commandOutput(error) {
1224
+ if (!error || typeof error !== "object") return String(error);
1225
+ const value = error;
1226
+ return [
1227
+ value.stdout,
1228
+ value.stderr,
1229
+ value.message
1230
+ ].filter((part) => typeof part === "string" && part.trim().length > 0).join("\n").trim();
1231
+ }
1232
+ function hostFromUrl(url) {
1233
+ try {
1234
+ return new URL(url, "https://code-play.local").host || url;
1235
+ } catch {
1236
+ return url;
1237
+ }
1238
+ }
1239
+ //#endregion
1240
+ //#region src/adapters.ts
1241
+ async function executeAdapter(request) {
1242
+ if (!request.enabled.execute) return capabilityDisabled(request, "execute");
1243
+ switch (request.definition.backend) {
1244
+ case "javascript": return runJavaScript(request);
1245
+ case "typescript": return runTypeScript(request);
1246
+ case "framework": return runFramework(request);
1247
+ case "rust-playground": return runRust(request, "execute");
1248
+ case "go-playground": return runGo(request, "execute");
1249
+ case "remote": return runRemote(request);
1250
+ default: return capabilityDisabled(request, "execute");
1251
+ }
1252
+ }
1253
+ async function typecheckAdapter(request) {
1254
+ if (!request.enabled.typecheck || !request.definition.capabilities.typecheck) return capabilityDisabled(request, "typecheck");
1255
+ switch (request.definition.backend) {
1256
+ case "typescript": return typecheckTypeScript(request);
1257
+ case "rust-playground": return runRust(request, "typecheck");
1258
+ case "go-playground": return runGo(request, "typecheck");
1259
+ default: return capabilityDisabled(request, "typecheck");
1260
+ }
1261
+ }
1262
+ function capabilityDisabled(request, action) {
1263
+ return {
1264
+ status: "unsupported",
1265
+ stdio: [],
1266
+ diagnostics: [{
1267
+ message: `${request.definition.name} ${action} is not enabled.`,
1268
+ severity: "error",
1269
+ source: "code-play"
1270
+ }],
1271
+ provenance: {},
1272
+ timing: {
1273
+ totalMs: 0,
1274
+ phases: []
1275
+ }
1276
+ };
1277
+ }
1278
+ //#endregion
1279
+ //#region src/result.ts
1280
+ function errorMessage(error) {
1281
+ return error instanceof Error && error.message ? error.message : String(error);
1282
+ }
1283
+ function friendlyTransportMessage(error) {
1284
+ const message = errorMessage(error);
1285
+ 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.";
1286
+ return message;
1287
+ }
1288
+ function errorResult(message, source = "code-play", status = "error") {
1289
+ return withStdioText({
1290
+ status,
1291
+ stdio: [],
1292
+ diagnostics: [{
1293
+ message,
1294
+ severity: status === "cancelled" ? "info" : "error",
1295
+ source
1296
+ }],
1297
+ provenance: {},
1298
+ timing: emptyTiming()
1299
+ });
1300
+ }
1301
+ //#endregion
1302
+ //#region src/session.ts
1303
+ var CodePlaySession = class {
1304
+ language;
1305
+ code;
1306
+ config;
1307
+ lastResult;
1308
+ /** Last run's concatenated stdout (same as `lastResult.stdout`). */
1309
+ get stdout() {
1310
+ return this.lastResult?.stdout ?? "";
1311
+ }
1312
+ /** Last run's concatenated stderr (same as `lastResult.stderr`). */
1313
+ get stderr() {
1314
+ return this.lastResult?.stderr ?? "";
1315
+ }
1316
+ enabled;
1317
+ timeoutMs;
1318
+ transport;
1319
+ endpoints;
1320
+ loadTypeScript;
1321
+ listeners = /* @__PURE__ */ new Map();
1322
+ abort;
1323
+ constructor(input) {
1324
+ this.language = input.definition;
1325
+ this.enabled = input.enabled;
1326
+ this.code = input.code;
1327
+ this.config = mergeConfig(input.definition.id, input.enabled, input.config);
1328
+ this.timeoutMs = input.timeoutMs;
1329
+ this.transport = input.transport;
1330
+ this.endpoints = input.endpoints;
1331
+ this.loadTypeScript = input.loadTypeScript;
1332
+ }
1333
+ on(event, listener) {
1334
+ const bucket = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
1335
+ bucket.add(listener);
1336
+ this.listeners.set(event, bucket);
1337
+ return () => bucket.delete(listener);
1338
+ }
1339
+ setCode(code) {
1340
+ this.code = code;
1341
+ }
1342
+ setConfig(config) {
1343
+ this.config = {
1344
+ ...this.config,
1345
+ ...config
1346
+ };
1347
+ this.emit("config", this.config);
1348
+ }
1349
+ async run() {
1350
+ return this.dispatch("execute");
1351
+ }
1352
+ async typecheck() {
1353
+ return this.dispatch("typecheck");
1354
+ }
1355
+ cancel() {
1356
+ this.abort?.abort();
1357
+ }
1358
+ async dispatch(action) {
1359
+ this.abort?.abort();
1360
+ this.abort = new AbortController();
1361
+ const { signal } = this.abort;
1362
+ const request = {
1363
+ definition: this.language,
1364
+ enabled: this.enabled,
1365
+ code: this.code,
1366
+ config: this.config,
1367
+ timeoutMs: this.timeoutMs,
1368
+ transport: this.transport,
1369
+ loadTypeScript: this.loadTypeScript,
1370
+ endpoints: this.endpoints,
1371
+ signal
1372
+ };
1373
+ try {
1374
+ const result = withStdioText(action === "typecheck" ? await typecheckAdapter(request) : await executeAdapter(request));
1375
+ return this.finish(result);
1376
+ } catch (error) {
1377
+ if (signal.aborted || isAbortError(error)) return this.finish(errorResult("Run cancelled.", "code-play", "cancelled"));
1378
+ return this.finish(errorResult(friendlyTransportMessage(error)));
1379
+ }
1380
+ }
1381
+ finish(result) {
1382
+ this.lastResult = result;
1383
+ for (const event of result.stdio) this.emit("stdio", event);
1384
+ this.emit("result", result);
1385
+ return result;
1386
+ }
1387
+ emit(event, value) {
1388
+ for (const listener of this.listeners.get(event) ?? []) listener(value);
1389
+ }
1390
+ };
1391
+ //#endregion
1392
+ //#region src/client.ts
1393
+ function createCodePlay(options = {}) {
1394
+ const resolved = resolveCodePlayOptions(options);
1395
+ const transport = options.transport ?? defaultTransport();
1396
+ return {
1397
+ options: resolved,
1398
+ hasLanguage(language) {
1399
+ const definition = resolveLanguage(language);
1400
+ return Boolean(definition && resolved.languages.has(definition.id));
1401
+ },
1402
+ createSession(input) {
1403
+ const definition = resolveLanguage(input.language);
1404
+ if (!definition) throw new Error(`Unknown Code Play language: ${input.language}.`);
1405
+ const enabled = resolved.languages.get(definition.id);
1406
+ if (!enabled) throw new Error(`${definition.name} is not enabled. Pass languages.${definition.id}: true to createCodePlay().`);
1407
+ return new CodePlaySession({
1408
+ ...input,
1409
+ definition,
1410
+ enabled,
1411
+ timeoutMs: resolved.timeoutMs,
1412
+ transport,
1413
+ endpoints: resolved.endpoints,
1414
+ loadTypeScript: options.loadTypeScript
1415
+ });
1416
+ }
1417
+ };
1418
+ }
1419
+ function defaultTransport() {
1420
+ if (typeof fetch === "function") return createFetchTransport();
1421
+ return createUnavailableTransport();
1422
+ }
1423
+ //#endregion
1424
+ //#region src/payload.ts
1425
+ function decodePayload(value) {
1426
+ const json = new TextDecoder().decode(base64ToBytes(value));
1427
+ const parsed = JSON.parse(json);
1428
+ if (!parsed || typeof parsed !== "object" || typeof parsed.language !== "string") throw new Error("Invalid Code Play payload.");
1429
+ return {
1430
+ ...parsed,
1431
+ viewers: {
1432
+ ...DEFAULT_VIEWERS,
1433
+ ...parsed.viewers
1434
+ }
1435
+ };
1436
+ }
1437
+ function base64ToBytes(value) {
1438
+ if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64"));
1439
+ const binary = atob(value);
1440
+ const bytes = new Uint8Array(binary.length);
1441
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
1442
+ return bytes;
1443
+ }
1444
+ //#endregion
1445
+ //#region src/hydrate-action.ts
1446
+ function readPlayPayload(encoded) {
1447
+ try {
1448
+ return decodePayload(encoded);
1449
+ } catch {
1450
+ return;
1451
+ }
1452
+ }
1453
+ async function runPlayAction(input) {
1454
+ input.setBusy(true);
1455
+ try {
1456
+ input.onResult(await input.action());
1457
+ } catch (error) {
1458
+ input.onError(error);
1459
+ } finally {
1460
+ input.setBusy(false);
1461
+ }
1462
+ }
1463
+ //#endregion
1464
+ //#region src/styles.ts
1465
+ var CODE_PLAY_STYLES = `
1466
+ .ox-code-play {
1467
+ border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 16%, transparent));
1468
+ border-radius: 12px;
1469
+ background: var(--octc-color-bg-alt, var(--octc-color-bg, Canvas));
1470
+ color: var(--octc-color-text, CanvasText);
1471
+ overflow: hidden;
1472
+ margin: 1.25rem 0;
1473
+ }
1474
+ .ox-code-play__toolbar {
1475
+ display: flex;
1476
+ flex-wrap: wrap;
1477
+ gap: 0.5rem;
1478
+ align-items: center;
1479
+ padding: 0.6rem 0.8rem;
1480
+ border-bottom: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 12%, transparent));
1481
+ }
1482
+ .ox-code-play__lang {
1483
+ font: 600 0.8rem/1.2 ui-sans-serif, system-ui, sans-serif;
1484
+ margin-right: auto;
1485
+ color: var(--octc-color-text, CanvasText);
1486
+ }
1487
+ .ox-code-play__toolbar button {
1488
+ appearance: none;
1489
+ border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 20%, transparent));
1490
+ background: color-mix(in srgb, var(--octc-color-text, CanvasText) 8%, transparent);
1491
+ color: var(--octc-color-text, CanvasText);
1492
+ border-radius: 999px;
1493
+ padding: 0.25rem 0.75rem;
1494
+ font: 600 0.75rem/1.4 ui-sans-serif, system-ui, sans-serif;
1495
+ cursor: pointer;
1496
+ }
1497
+ .ox-code-play__toolbar button:disabled { opacity: 0.55; cursor: progress; }
1498
+ .ox-code-play .ox-code { margin: 0; }
1499
+ .ox-code-play__source pre { margin: 0; border: 0; border-radius: 0; }
1500
+ .ox-code-play__tabs {
1501
+ display: flex;
1502
+ gap: 0.25rem;
1503
+ padding: 0.4rem 0.7rem 0;
1504
+ }
1505
+ .ox-code-play__tabs button {
1506
+ appearance: none;
1507
+ border: 0;
1508
+ background: transparent;
1509
+ color: var(--octc-color-text, CanvasText);
1510
+ padding: 0.35rem 0.55rem;
1511
+ border-radius: 8px 8px 0 0;
1512
+ font: 600 0.75rem/1.2 ui-sans-serif, system-ui, sans-serif;
1513
+ cursor: pointer;
1514
+ }
1515
+ .ox-code-play__tabs button[aria-selected="true"] {
1516
+ background: color-mix(in srgb, var(--octc-color-text, CanvasText) 10%, transparent);
1517
+ }
1518
+ .ox-code-play__panel { padding: 0.7rem 0.8rem 0.9rem; color: var(--octc-color-text, CanvasText); }
1519
+ .ox-code-play__panel pre {
1520
+ background: transparent !important;
1521
+ color: inherit !important;
1522
+ padding: 0 !important;
1523
+ margin: 0 !important;
1524
+ border: 0 !important;
1525
+ border-radius: 0 !important;
1526
+ }
1527
+ .ox-code-play__empty { margin: 0; opacity: 0.7; font-size: 0.85rem; }
1528
+ .ox-code-play__stdio { font: 12px/1.45 ui-monospace, SFMono-Regular, monospace; }
1529
+ .ox-code-play__stdio-line { display: grid; grid-template-columns: 8.5rem 1fr; gap: 0.6rem; white-space: pre-wrap; }
1530
+ .ox-code-play__stdio-line--stderr { color: var(--octc-danger, #b42318); }
1531
+ .ox-code-play__stdio-line--stdin { opacity: 0.75; }
1532
+ .ox-code-play__stdio-meta { opacity: 0.6; }
1533
+ .ox-code-play__config { display: grid; gap: 0.55rem; }
1534
+ .ox-code-play__field { display: grid; gap: 0.25rem; font-size: 0.85rem; }
1535
+ .ox-code-play__field input, .ox-code-play__field select {
1536
+ font: inherit;
1537
+ padding: 0.3rem 0.45rem;
1538
+ border-radius: 8px;
1539
+ border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 18%, transparent));
1540
+ background: var(--octc-color-bg, Canvas);
1541
+ color: var(--octc-color-text, CanvasText);
1542
+ }
1543
+ .ox-code-play__provenance { display: grid; gap: 0.6rem; margin: 0; }
1544
+ .ox-code-play__provenance dt { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.65; }
1545
+ .ox-code-play__provenance dd { margin: 0.15rem 0 0; }
1546
+ .ox-code-play__phase { margin: 0.45rem 0; }
1547
+ .ox-code-play__phase-meta { display: flex; justify-content: space-between; font-size: 0.8rem; }
1548
+ .ox-code-play__phase-bar {
1549
+ height: 0.35rem;
1550
+ margin-top: 0.25rem;
1551
+ border-radius: 999px;
1552
+ background: var(--octc-color-primary, var(--octc-accent, #4f46e5));
1553
+ }
1554
+ .ox-code-play__timing-total { margin: 0 0 0.4rem; font-weight: 600; }
1555
+ .ox-code-play__diags { margin: 0 0 0.6rem; padding-left: 1.1rem; }
1556
+ .ox-code-play__diag--error { color: var(--octc-danger, #b42318); }
1557
+ .ox-code-play__diag--warning { color: var(--octc-warning, #b54708); }
1558
+ .ox-code-play--compact .ox-code-play__tabs { display: none; }
1559
+ .ox-code-play--compact .ox-code-play__panel[data-panel]:not([data-panel="stdio"]):not([data-panel="stderr"]) { display: none; }
1560
+ `;
1561
+ //#endregion
1562
+ //#region src/viewers.ts
1563
+ function renderStdioHtml(events) {
1564
+ if (events.length === 0) return `<p class="ox-code-play__stdio ox-code-play__empty">No stdio yet.</p>`;
1565
+ return `<div class="ox-code-play__stdio" role="log">${events.map((event) => {
1566
+ const time = event.timestampMs.toFixed(1);
1567
+ return `<div class="ox-code-play__stdio-line ox-code-play__stdio-line--${event.stream}"><span class="ox-code-play__stdio-meta">${escapeHtml(event.stream)} +${escapeHtml(time)}ms</span><span class="ox-code-play__stdio-text">${escapeHtml(event.text)}</span></div>`;
1568
+ }).join("")}</div>`;
1569
+ }
1570
+ /** Dedicated stderr viewer: stderr chunks plus error/warning diagnostics. */
1571
+ function renderStderrHtml(result) {
1572
+ const chunks = selectStream(result?.stdio ?? [], "stderr");
1573
+ const diagnostics = (result?.diagnostics ?? []).filter((diagnostic) => diagnostic.severity === "error" || diagnostic.severity === "warning");
1574
+ if (chunks.length === 0 && diagnostics.length === 0) return `<p class="ox-code-play__empty">No stderr.</p>`;
1575
+ const stream = chunks.length === 0 ? "" : renderStdioHtml(chunks);
1576
+ if (diagnostics.length === 0) return stream;
1577
+ return `${stream}<ul class="ox-code-play__diags">${diagnostics.map((diagnostic) => {
1578
+ const place = diagnostic.line ? `:${diagnostic.line}${diagnostic.column ? `:${diagnostic.column}` : ""}` : "";
1579
+ return `<li class="ox-code-play__diag ox-code-play__diag--${diagnostic.severity}">${escapeHtml(diagnostic.severity)}${escapeHtml(place)} ${escapeHtml(diagnostic.message)}</li>`;
1580
+ }).join("")}</ul>`;
1581
+ }
1582
+ function renderConfigHtml(schema, config) {
1583
+ if (schema.length === 0) return `<p class="ox-code-play__empty">This language has no editable config.</p>`;
1584
+ return `<form class="ox-code-play__config">${schema.map((field) => {
1585
+ const value = config[field.key] ?? field.default ?? "";
1586
+ const label = `<label class="ox-code-play__field"><span>${escapeHtml(field.label)}</span>${renderField(field, value)}</label>`;
1587
+ return field.description ? `${label}<p class="ox-code-play__field-help">${escapeHtml(field.description)}</p>` : label;
1588
+ }).join("")}</form>`;
1589
+ }
1590
+ function renderProvenanceHtml(provenance) {
1591
+ if (!provenance?.compile && !provenance?.execute) return `<p class="ox-code-play__empty">No provenance yet. Run or type-check a sample.</p>`;
1592
+ return `<dl class="ox-code-play__provenance">${renderLocation("Compiled", provenance.compile)}${renderLocation("Executed", provenance.execute)}</dl>`;
1593
+ }
1594
+ function renderTimingHtml(timing) {
1595
+ if (!timing || timing.phases.length === 0) return `<p class="ox-code-play__empty">No timing yet. Run or type-check a sample.</p>`;
1596
+ const rows = timing.phases.map((phase) => {
1597
+ const width = timing.totalMs > 0 ? Math.max(2, phase.durationMs / timing.totalMs * 100) : 0;
1598
+ return `<div class="ox-code-play__phase"><div class="ox-code-play__phase-meta"><span>${escapeHtml(phase.label)}</span><span>${phase.durationMs.toFixed(1)}ms</span></div><div class="ox-code-play__phase-bar" style="width:${width.toFixed(1)}%"></div></div>`;
1599
+ }).join("");
1600
+ return `<div class="ox-code-play__timing"><p class="ox-code-play__timing-total">Total ${timing.totalMs.toFixed(1)}ms</p>${rows}</div>`;
1601
+ }
1602
+ function renderDiagnosticsHtml(result) {
1603
+ if (!result?.diagnostics.length) return "";
1604
+ return `<ul class="ox-code-play__diags">${result.diagnostics.map((diagnostic) => {
1605
+ const place = diagnostic.line ? `:${diagnostic.line}${diagnostic.column ? `:${diagnostic.column}` : ""}` : "";
1606
+ return `<li class="ox-code-play__diag ox-code-play__diag--${diagnostic.severity}">${escapeHtml(diagnostic.severity)}${escapeHtml(place)} ${escapeHtml(diagnostic.message)}</li>`;
1607
+ }).join("")}</ul>`;
1608
+ }
1609
+ function renderField(field, value) {
1610
+ const name = escapeHtml(field.key);
1611
+ if (field.type === "boolean") return `<input type="checkbox" name="${name}" ${value ? "checked" : ""}>`;
1612
+ if (field.type === "select") return `<select name="${name}">${(field.options ?? []).map((option) => `<option value="${escapeHtml(option.value)}" ${String(value) === option.value ? "selected" : ""}>${escapeHtml(option.label)}</option>`).join("")}</select>`;
1613
+ return `<input type="${field.type === "number" ? "number" : "text"}" name="${name}" value="${escapeHtml(String(value))}">`;
1614
+ }
1615
+ function renderLocation(label, location) {
1616
+ if (!location) return "";
1617
+ const details = [
1618
+ location.runtime,
1619
+ location.version,
1620
+ location.sandbox,
1621
+ location.target
1622
+ ].filter(Boolean).join(" · ");
1623
+ return `<div><dt>${escapeHtml(label)}</dt><dd><strong>${escapeHtml(location.host)}</strong>${details ? ` <span>${escapeHtml(details)}</span>` : ""}</dd></div>`;
1624
+ }
1625
+ //#endregion
1626
+ //#region src/ui.ts
1627
+ function renderPlayUi(state) {
1628
+ const definition = resolveLanguage(state.payload.language);
1629
+ const preset = state.payload.ui === "headless" ? "headless" : state.payload.ui;
1630
+ if (preset === "headless") return "";
1631
+ const panel = state.panel ?? "stdio";
1632
+ const canTypecheck = state.payload.capabilities.typecheck;
1633
+ const tabs = renderTabs(state, panel);
1634
+ const viewers = state.payload.viewers;
1635
+ return `<div class="ox-code-play ox-code-play--${preset}" data-ox-code-play-ui>
1636
+ <div class="ox-code-play__toolbar">
1637
+ <span class="ox-code-play__lang">${escapeHtml(definition?.name ?? state.payload.language)}</span>
1638
+ <button type="button" data-ox-action="run"${actionButtonAttrs("run", Boolean(state.busy))}>Run</button>
1639
+ ${canTypecheck ? `<button type="button" data-ox-action="typecheck"${actionButtonAttrs("typecheck", Boolean(state.busy))}>Typecheck</button>` : ""}
1640
+ <button type="button" data-ox-action="cancel"${actionButtonAttrs("cancel", Boolean(state.busy))}>Cancel</button>
1641
+ </div>
1642
+ <div class="ox-code-play__source"></div>
1643
+ ${tabs}
1644
+ ${viewers.stdio ? `<div class="ox-code-play__panel" data-panel="stdio">${renderDiagnosticsHtml(state.result)}${renderStdioHtml(state.result?.stdio ?? [])}</div>` : ""}
1645
+ ${viewers.stderr ? `<div class="ox-code-play__panel" data-panel="stderr"${hidden(panel, "stderr", preset)}>${renderStderrHtml(state.result)}</div>` : ""}
1646
+ <div class="ox-code-play__panel" data-panel="config"${hidden(panel, "config", preset)}>${renderConfigHtml(definition?.configSchema ?? [], state.payload.config)}</div>
1647
+ <div class="ox-code-play__panel" data-panel="provenance"${hidden(panel, "provenance", preset)}>${renderProvenanceHtml(state.result?.provenance)}</div>
1648
+ <div class="ox-code-play__panel" data-panel="timing"${hidden(panel, "timing", preset)}>${renderTimingHtml(state.result?.timing)}</div>
1649
+ </div>`;
1650
+ }
1651
+ function renderTabs(state, panel) {
1652
+ if (state.payload.ui === "compact") return "";
1653
+ const viewers = state.payload.viewers;
1654
+ const buttons = [
1655
+ viewers.stdio ? tab("stdio", "stdio", panel) : "",
1656
+ viewers.stderr ? tab("stderr", "stderr", panel) : "",
1657
+ viewers.config ? tab("config", "config", panel) : "",
1658
+ viewers.provenance ? tab("provenance", "provenance", panel) : "",
1659
+ viewers.timing ? tab("timing", "timing", panel) : ""
1660
+ ].filter(Boolean).join("");
1661
+ return buttons ? `<div class="ox-code-play__tabs" role="tablist">${buttons}</div>` : "";
1662
+ }
1663
+ function tab(id, label, selected) {
1664
+ return `<button type="button" role="tab" data-ox-panel="${id}" aria-selected="${selected === id ? "true" : "false"}">${label}</button>`;
1665
+ }
1666
+ function actionButtonAttrs(action, busy) {
1667
+ const state = actionBusyState(action, busy);
1668
+ return `${state.disabled ? " disabled" : ""}${state.hidden ? " hidden" : ""}`;
1669
+ }
1670
+ function actionBusyState(action, busy) {
1671
+ if (action === "cancel") return {
1672
+ disabled: !busy,
1673
+ hidden: !busy
1674
+ };
1675
+ return {
1676
+ disabled: busy,
1677
+ hidden: false
1678
+ };
1679
+ }
1680
+ function applyActionBusy(root, busy) {
1681
+ for (const button of root.querySelectorAll("button[data-ox-action]")) {
1682
+ const state = actionBusyState(button.dataset.oxAction ?? "", busy);
1683
+ button.disabled = state.disabled;
1684
+ button.hidden = state.hidden;
1685
+ }
1686
+ }
1687
+ function hidden(current, id, preset) {
1688
+ if (preset === "compact" && (id === "stdio" || id === "stderr")) return "";
1689
+ return current === id ? "" : " hidden";
1690
+ }
1691
+ //#endregion
1692
+ //#region src/hydrate.ts
1693
+ var STYLE_ID = "ox-code-play-styles";
1694
+ function hydrateCodePlay(root = defaultRoot(), options = {}) {
1695
+ ensureStyles();
1696
+ const client = options.client ?? createCodePlayFromPayloads(root);
1697
+ for (const element of queryWidgets(root)) try {
1698
+ mountCodePlay(element, { client });
1699
+ } catch {}
1700
+ }
1701
+ function mountCodePlay(element, options = {}) {
1702
+ if (!(element instanceof HTMLElement) || element.dataset.oxCodePlayMounted === "true") return;
1703
+ const payload = readPlayPayload(element.getAttribute("data-ox-code-play") ?? "");
1704
+ if (!payload || payload.ui === "headless") return;
1705
+ const client = options.client ?? createCodePlay({
1706
+ languages: { [payload.language]: true },
1707
+ endpoints: payload.endpoints
1708
+ });
1709
+ const source = element.innerHTML;
1710
+ const ui = document.createElement("div");
1711
+ ui.innerHTML = renderPlayUi({ payload });
1712
+ const widget = ui.firstElementChild;
1713
+ if (!widget) return;
1714
+ const sourceSlot = widget.querySelector(".ox-code-play__source");
1715
+ if (sourceSlot) sourceSlot.innerHTML = source;
1716
+ element.replaceChildren(widget);
1717
+ element.dataset.oxCodePlayMounted = "true";
1718
+ bindWidget(element, payload, client);
1719
+ }
1720
+ function bindWidget(element, payload, client) {
1721
+ let current = payload;
1722
+ const session = client.createSession({
1723
+ language: payload.language,
1724
+ code: payload.code,
1725
+ config: payload.config
1726
+ });
1727
+ const runButton = element.querySelector("[data-ox-action=\"run\"]");
1728
+ const checkButton = element.querySelector("[data-ox-action=\"typecheck\"]");
1729
+ const cancelButton = element.querySelector("[data-ox-action=\"cancel\"]");
1730
+ runButton?.addEventListener("click", () => void run("execute"));
1731
+ checkButton?.addEventListener("click", () => void run("typecheck"));
1732
+ cancelButton?.addEventListener("click", () => session.cancel());
1733
+ element.addEventListener("click", (event) => {
1734
+ const target = event.target;
1735
+ if (!(target instanceof HTMLElement)) return;
1736
+ const panel = target.dataset.oxPanel;
1737
+ if (panel) showPanel(element, panel);
1738
+ });
1739
+ element.addEventListener("change", (event) => {
1740
+ const form = event.target?.closest("form");
1741
+ if (!form) return;
1742
+ session.setConfig(readForm(form));
1743
+ current = {
1744
+ ...current,
1745
+ config: session.config
1746
+ };
1747
+ });
1748
+ async function run(action) {
1749
+ await runPlayAction({
1750
+ action: () => action === "typecheck" ? session.typecheck() : session.run(),
1751
+ setBusy: (busy) => applyActionBusy(element, busy),
1752
+ onResult: (result) => paintResult(element, current, result),
1753
+ onError: (error) => paintResult(element, current, errorResult(errorMessage(error)))
1754
+ });
1755
+ }
1756
+ }
1757
+ function paintResult(element, _payload, result) {
1758
+ const stdio = element.querySelector("[data-panel=\"stdio\"]");
1759
+ if (stdio) {
1760
+ stdio.innerHTML = `${renderDiagnosticsHtml(result)}${renderStdioHtml(result.stdio)}`;
1761
+ if (result.preview) {
1762
+ const frame = document.createElement("iframe");
1763
+ frame.setAttribute("sandbox", JS_SANDBOX_FLAGS);
1764
+ frame.srcdoc = result.preview.html;
1765
+ frame.title = "Code Play preview";
1766
+ frame.style.width = "100%";
1767
+ frame.style.minHeight = "12rem";
1768
+ frame.style.border = "0";
1769
+ stdio.append(frame);
1770
+ }
1771
+ }
1772
+ const provenance = element.querySelector("[data-panel=\"provenance\"]");
1773
+ if (provenance) provenance.innerHTML = renderProvenanceHtml(result.provenance);
1774
+ const timing = element.querySelector("[data-panel=\"timing\"]");
1775
+ if (timing) timing.innerHTML = renderTimingHtml(result.timing);
1776
+ const stderr = element.querySelector("[data-panel=\"stderr\"]");
1777
+ if (stderr) stderr.innerHTML = renderStderrHtml(result);
1778
+ showPanel(element, Boolean(stderr) && (Boolean(result.stderr) || result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) ? "stderr" : "stdio");
1779
+ }
1780
+ function showPanel(element, panel) {
1781
+ const compact = Boolean(element.querySelector(".ox-code-play--compact"));
1782
+ for (const tab of element.querySelectorAll("[data-ox-panel]")) tab.setAttribute("aria-selected", tab.getAttribute("data-ox-panel") === panel ? "true" : "false");
1783
+ for (const node of element.querySelectorAll(".ox-code-play__panel")) {
1784
+ const id = node.dataset.panel;
1785
+ if (compact && (id === "stdio" || id === "stderr")) {
1786
+ node.hidden = false;
1787
+ continue;
1788
+ }
1789
+ node.hidden = id !== panel;
1790
+ }
1791
+ }
1792
+ function readForm(form) {
1793
+ const data = new FormData(form);
1794
+ const config = {};
1795
+ for (const [key, value] of data.entries()) config[key] = value === "on" ? true : value;
1796
+ for (const input of form.querySelectorAll("input[type=\"checkbox\"]")) config[input.name] = input.checked;
1797
+ return config;
1798
+ }
1799
+ function createCodePlayFromPayloads(root) {
1800
+ const languages = {};
1801
+ let endpoints;
1802
+ for (const element of queryWidgets(root)) try {
1803
+ const payload = decodePayload(element.getAttribute("data-ox-code-play") ?? "");
1804
+ languages[payload.language] = true;
1805
+ endpoints = payload.endpoints ?? endpoints;
1806
+ } catch {}
1807
+ return createCodePlay({
1808
+ languages,
1809
+ endpoints
1810
+ });
1811
+ }
1812
+ function queryWidgets(root) {
1813
+ return [...root.querySelectorAll("[data-ox-code-play]")];
1814
+ }
1815
+ function ensureStyles() {
1816
+ if (typeof document === "undefined" || document.getElementById(STYLE_ID)) return;
1817
+ const style = document.createElement("style");
1818
+ style.id = STYLE_ID;
1819
+ style.textContent = CODE_PLAY_STYLES;
1820
+ document.head.append(style);
1821
+ }
1822
+ function defaultRoot() {
1823
+ if (typeof document === "undefined") throw new Error("hydrateCodePlay() needs a DOM root.");
1824
+ return document;
1825
+ }
1826
+ //#endregion
1827
+ //#region src/boot.ts
1828
+ /**
1829
+ * Mount every `[data-ox-code-play]` widget. The SSG client (`ox-code-play.js`)
1830
+ * calls this on load; apps that import `@ox-content/code-play/client` call it
1831
+ * themselves.
1832
+ */
1833
+ function bootCodePlay(hydrate = hydrateCodePlay, doc = typeof document === "undefined" ? void 0 : document) {
1834
+ if (!doc) return;
1835
+ const run = () => hydrate();
1836
+ if (doc.readyState === "loading") {
1837
+ doc.addEventListener("DOMContentLoaded", run, { once: true });
1838
+ return;
1839
+ }
1840
+ run();
1841
+ }
1842
+ //#endregion
1843
+ //#region src/browser.ts
1844
+ bootCodePlay();
1845
+ //#endregion
1846
+ export { bootCodePlay };