wasm-idle 0.7.1 → 0.7.2

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.
@@ -3,6 +3,7 @@ import { MemFS, untar } from './memory';
3
3
  import { green, yellow, normal } from './color';
4
4
  import { compile, readBuffer } from './wasm';
5
5
  import { clangUrl, lldUrl, rootUrl } from './url';
6
+ import { installGccCompatibilityHeaders } from './gccCompat';
6
7
  import { derived, writable } from 'svelte/store';
7
8
  const clangCommonArgs = [
8
9
  '-disable-free',
@@ -174,7 +175,10 @@ export default class Clang {
174
175
  });
175
176
  this.getModule(clangUrl(this.path), this.progress.clang);
176
177
  this.getModule(lldUrl(this.path), this.progress.lld);
177
- this.ready = this.memfs.ready.then(() => this.hostLogAsync(`Untarring ${rootUrl(this.path)}`, readBuffer(rootUrl(this.path)).then((buffer) => untar(buffer, this.memfs))));
178
+ this.ready = this.memfs.ready.then(async () => {
179
+ await this.hostLogAsync(`Untarring ${rootUrl(this.path)}`, readBuffer(rootUrl(this.path)).then((buffer) => untar(buffer, this.memfs)));
180
+ installGccCompatibilityHeaders(this.memfs);
181
+ });
178
182
  }
179
183
  hostLog(message) {
180
184
  if (!this.log)
@@ -1,4 +1,4 @@
1
- export declare const memfsUrl: (prefix: string) => string;
2
- export declare const clangUrl: (prefix: string) => string;
3
- export declare const lldUrl: (prefix: string) => string;
4
- export declare const rootUrl: (prefix: string) => string;
1
+ export declare const memfsUrl: (baseUrl: string) => string;
2
+ export declare const clangUrl: (baseUrl: string) => string;
3
+ export declare const lldUrl: (baseUrl: string) => string;
4
+ export declare const rootUrl: (baseUrl: string) => string;
package/dist/clang/url.js CHANGED
@@ -1,4 +1,5 @@
1
- export const memfsUrl = (prefix) => prefix + '/clang/bin/memfs.zip';
2
- export const clangUrl = (prefix) => prefix + '/clang/bin/clang.zip';
3
- export const lldUrl = (prefix) => prefix + '/clang/bin/lld.zip';
4
- export const rootUrl = (prefix) => prefix + '/clang/bin/sysroot.tar.zip';
1
+ const normalizeBaseUrl = (baseUrl) => baseUrl ? (baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`) : '/clang/';
2
+ export const memfsUrl = (baseUrl) => `${normalizeBaseUrl(baseUrl)}bin/memfs.zip`;
3
+ export const clangUrl = (baseUrl) => `${normalizeBaseUrl(baseUrl)}bin/clang.zip`;
4
+ export const lldUrl = (baseUrl) => `${normalizeBaseUrl(baseUrl)}bin/lld.zip`;
5
+ export const rootUrl = (baseUrl) => `${normalizeBaseUrl(baseUrl)}bin/sysroot.tar.zip`;
@@ -1,13 +1,17 @@
1
1
  import { MonacoLanguageClient } from '@hancomac/monaco-languageclient';
2
2
  import type * as Monaco from 'monaco-editor';
3
3
  import { type ClangdStatus } from './config';
4
+ import { WorkerAssetBridge } from '../playground/assetBridge';
5
+ import { type ResolvedRuntimeAssetConfig, type RuntimeAssetConfig } from '../playground/assets';
4
6
  export declare class ClangdSession {
5
7
  Monaco: typeof Monaco;
6
8
  baseUrl: string;
9
+ assetConfig: ResolvedRuntimeAssetConfig;
10
+ assetBridge: WorkerAssetBridge | null;
7
11
  onStatus?: (status: ClangdStatus) => void;
8
12
  worker: Worker | null;
9
13
  languageClient: MonacoLanguageClient | null;
10
- constructor(MonacoModule: typeof Monaco, baseUrl: string, onStatus?: (status: ClangdStatus) => void);
14
+ constructor(MonacoModule: typeof Monaco, baseUrl: string | RuntimeAssetConfig, onStatus?: (status: ClangdStatus) => void);
11
15
  createModel(value: string): Monaco.editor.ITextModel;
12
16
  start(): Promise<void>;
13
17
  dispose(): void;
@@ -1,18 +1,23 @@
1
1
  import { CloseAction, ErrorAction, MonacoLanguageClient, MonacoServices } from '@hancomac/monaco-languageclient';
2
- import { CLANGD_CPP_FILE_URI, CLANGD_WORKSPACE_URI, normalizeClangdBaseUrl } from './config';
2
+ import { CLANGD_CPP_FILE_URI, CLANGD_WORKSPACE_URI } from './config';
3
3
  import ClangdWorker from './worker?worker';
4
+ import { WorkerAssetBridge } from '../playground/assetBridge';
5
+ import { resolveRuntimeAssetConfig } from '../playground/assets';
4
6
  import { BrowserMessageReader, BrowserMessageWriter } from '../utils/vscodeJsonrpcBrowser';
5
7
  let servicesInstalled = false;
6
8
  export class ClangdSession {
7
9
  Monaco;
8
10
  baseUrl;
11
+ assetConfig;
12
+ assetBridge = null;
9
13
  onStatus;
10
14
  worker = null;
11
15
  languageClient = null;
12
16
  constructor(MonacoModule, baseUrl, onStatus) {
13
17
  this.Monaco = MonacoModule;
14
18
  this.onStatus = onStatus;
15
- this.baseUrl = normalizeClangdBaseUrl(baseUrl);
19
+ this.assetConfig = resolveRuntimeAssetConfig('clangd', { clangd: typeof baseUrl === 'string' ? { baseUrl } : baseUrl }, globalThis.location?.href || '');
20
+ this.baseUrl = this.assetConfig.baseUrl;
16
21
  if (!servicesInstalled) {
17
22
  MonacoServices.install(MonacoModule);
18
23
  servicesInstalled = true;
@@ -29,12 +34,17 @@ export class ClangdSession {
29
34
  this.onStatus?.({ state: 'loading' });
30
35
  const worker = new ClangdWorker();
31
36
  this.worker = worker;
37
+ this.assetBridge = new WorkerAssetBridge(worker, 'clangd', this.assetConfig, {
38
+ set: (value) => this.onStatus?.({ state: 'loading', loaded: value, total: 1 })
39
+ });
32
40
  await new Promise((resolve, reject) => {
33
41
  const cleanup = () => {
34
42
  worker.removeEventListener('message', handleMessage);
35
43
  worker.removeEventListener('error', handleError);
36
44
  };
37
45
  const handleMessage = (event) => {
46
+ if (this.assetBridge?.handleMessage(event))
47
+ return;
38
48
  const { type, value, max, message } = event.data || {};
39
49
  if (type === 'progress') {
40
50
  this.onStatus?.({ state: 'loading', loaded: value, total: max });
@@ -56,7 +66,14 @@ export class ClangdSession {
56
66
  };
57
67
  worker.addEventListener('message', handleMessage);
58
68
  worker.addEventListener('error', handleError);
59
- worker.postMessage({ type: 'init', baseUrl: this.baseUrl });
69
+ worker.postMessage({
70
+ type: 'init',
71
+ baseUrl: this.baseUrl,
72
+ assets: {
73
+ baseUrl: this.assetConfig.baseUrl,
74
+ useAssetBridge: this.assetConfig.useAssetBridge
75
+ }
76
+ });
60
77
  });
61
78
  const reader = new BrowserMessageReader(worker);
62
79
  const writer = new BrowserMessageWriter(worker);
@@ -90,5 +107,6 @@ export class ClangdSession {
90
107
  this.languageClient = null;
91
108
  this.worker?.terminate();
92
109
  this.worker = null;
110
+ this.assetBridge = null;
93
111
  }
94
112
  }
@@ -1,6 +1,8 @@
1
1
  /// <reference lib="WebWorker" />
2
2
  import { CLANGD_CPP_FILE_PATH, CLANGD_WORKSPACE_PATH, createClangdCompileFlags, normalizeClangdBaseUrl } from './config';
3
+ import { writeGccCompatibilityHeaders } from '../clang/gccCompat';
3
4
  import { JsonStream } from './jsonStream';
5
+ import { configureWorkerRuntimeAssets, handleWorkerAssetMessage, loadWorkerRuntimeAsset } from '../playground/worker/assets';
4
6
  import { BrowserMessageReader, BrowserMessageWriter } from '../utils/vscodeJsonrpcBrowser';
5
7
  const textEncoder = new TextEncoder();
6
8
  const jsonStream = new JsonStream();
@@ -49,62 +51,44 @@ const syncWorkspaceFile = (filePath) => {
49
51
  clangdRuntime.FS.writeFile(normalizedFilePath, '');
50
52
  };
51
53
  self.addEventListener('message', async (event) => {
54
+ if (handleWorkerAssetMessage(event.data))
55
+ return;
52
56
  if (event.data?.type === 'sync-file' && typeof event.data?.name === 'string') {
53
57
  syncWorkspaceFile(event.data.name);
54
58
  return;
55
59
  }
56
60
  if (event.data?.type !== 'init')
57
61
  return;
58
- const baseUrl = normalizeClangdBaseUrl(event.data.baseUrl);
62
+ const runtimeAssets = event.data.assets;
63
+ const normalizedBaseUrl = normalizeClangdBaseUrl(event.data.baseUrl || '');
64
+ const baseUrl = runtimeAssets?.baseUrl || (normalizedBaseUrl ? `${normalizedBaseUrl}/` : '/clangd/');
65
+ configureWorkerRuntimeAssets({
66
+ baseUrl,
67
+ useAssetBridge: !!runtimeAssets?.useAssetBridge
68
+ });
59
69
  try {
60
- const jsResponse = await fetch(`${baseUrl}/clangd.js`);
61
- if (!jsResponse.ok) {
62
- throw new Error(`Failed to load clangd.js: ${jsResponse.status}`);
63
- }
64
- const jsSource = await jsResponse.text();
70
+ const jsSource = new TextDecoder().decode((await loadWorkerRuntimeAsset('clangd.js')).bytes);
71
+ self.postMessage({ type: 'progress', value: 1, max: 3 });
65
72
  const jsDataUrl = URL.createObjectURL(new Blob([jsSource], { type: 'text/javascript;charset=utf-8' }));
66
73
  const jsModule = import(/* @vite-ignore */ jsDataUrl);
67
- const wasmResponse = await fetch(`${baseUrl}/clangd.wasm.gz`);
68
- if (!wasmResponse.ok) {
69
- throw new Error(`Failed to load clangd.wasm.gz: ${wasmResponse.status}`);
70
- }
71
- const wasmSize = +(wasmResponse.headers.get('Content-Length') || 0);
72
- const wasmReader = wasmResponse.body?.getReader();
73
- const chunks = [];
74
- let receivedLength = 0;
75
- if (wasmReader) {
76
- while (true) {
77
- const { done, value } = await wasmReader.read();
78
- if (done)
79
- break;
80
- if (!value)
81
- continue;
82
- chunks.push(Uint8Array.from(value));
83
- receivedLength += value.length;
84
- self.postMessage({
85
- type: 'progress',
86
- value: receivedLength,
87
- ...(wasmSize > 0 ? { max: wasmSize } : {})
88
- });
74
+ const compressedWasmBytes = (await loadWorkerRuntimeAsset('clangd.wasm.gz')).bytes;
75
+ self.postMessage({ type: 'progress', value: 2, max: 3 });
76
+ let wasmBytes = compressedWasmBytes;
77
+ if (compressedWasmBytes[0] === 0x1f && compressedWasmBytes[1] === 0x8b) {
78
+ if (typeof DecompressionStream === 'undefined') {
79
+ throw new Error('Failed to decompress clangd.wasm.gz: DecompressionStream is unavailable');
89
80
  }
81
+ const compressedWasmCopy = new Uint8Array(compressedWasmBytes.byteLength);
82
+ compressedWasmCopy.set(compressedWasmBytes);
83
+ const stream = new Blob([compressedWasmCopy.buffer])
84
+ .stream()
85
+ .pipeThrough(new DecompressionStream('gzip'));
86
+ wasmBytes = new Uint8Array(await new Response(stream).arrayBuffer());
90
87
  }
91
- else {
92
- const wasmBytes = new Uint8Array(await wasmResponse.arrayBuffer());
93
- chunks.push(wasmBytes);
94
- receivedLength = wasmBytes.byteLength;
95
- self.postMessage({
96
- type: 'progress',
97
- value: receivedLength,
98
- ...(wasmSize > 0 ? { max: wasmSize } : {})
99
- });
100
- }
101
- const wasmBytes = new Uint8Array(receivedLength);
102
- let offset = 0;
103
- for (const chunk of chunks) {
104
- wasmBytes.set(chunk, offset);
105
- offset += chunk.length;
106
- }
107
- const wasmBlob = new Blob([wasmBytes.buffer], { type: 'application/wasm' });
88
+ self.postMessage({ type: 'progress', value: 3, max: 3 });
89
+ const wasmBlobBytes = new Uint8Array(wasmBytes.byteLength);
90
+ wasmBlobBytes.set(wasmBytes);
91
+ const wasmBlob = new Blob([wasmBlobBytes.buffer], { type: 'application/wasm' });
108
92
  const wasmDataUrl = URL.createObjectURL(wasmBlob);
109
93
  const { default: Clangd } = await jsModule;
110
94
  clangdRuntime = await Clangd({
@@ -119,6 +103,7 @@ self.addEventListener('message', async (event) => {
119
103
  onAbort
120
104
  });
121
105
  clangdRuntime.FS.mkdirTree(CLANGD_WORKSPACE_PATH);
106
+ writeGccCompatibilityHeaders(clangdRuntime.FS, '/usr');
122
107
  syncWorkspaceFile(CLANGD_CPP_FILE_PATH);
123
108
  clangdRuntime.FS.writeFile(`${CLANGD_WORKSPACE_PATH}/.clangd`, JSON.stringify({
124
109
  CompileFlags: {
@@ -135,7 +120,7 @@ self.addEventListener('message', async (event) => {
135
120
  stdinChunks.push(`Content-Length: ${body.length}\r\n`, '\r\n', body);
136
121
  resolveStdinReady();
137
122
  });
138
- self.postMessage({ type: 'ready', value: receivedLength });
123
+ self.postMessage({ type: 'ready', value: wasmBytes.byteLength });
139
124
  }
140
125
  catch (error) {
141
126
  self.postMessage({
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import Terminal from './terminal';
2
2
  export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, createDebugSessionController, MonacoDebugView, attachMonacoDebugActions, selectInlineDebugLocals } from './debug';
3
- export { getEditorLanguageServer, getCppLanguageServer, getPythonLanguageServer, resolveCppLanguageServerBaseUrl, resolvePythonLanguageServerBaseUrl } from './lsp';
3
+ export { getEditorLanguageServer, getCppLanguageServer, getPythonLanguageServer, resolveCppLanguageServerBaseUrl, resolveCppLanguageServerRuntimeAssetConfig, resolvePythonLanguageServerBaseUrl } from './lsp';
4
4
  import playground, { createPlaygroundBinding } from './playground';
5
5
  export default Terminal;
6
6
  export { createPlaygroundBinding, playground };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import Terminal from './terminal';
2
2
  export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, createDebugSessionController, MonacoDebugView, attachMonacoDebugActions, selectInlineDebugLocals } from './debug';
3
- export { getEditorLanguageServer, getCppLanguageServer, getPythonLanguageServer, resolveCppLanguageServerBaseUrl, resolvePythonLanguageServerBaseUrl } from './lsp';
3
+ export { getEditorLanguageServer, getCppLanguageServer, getPythonLanguageServer, resolveCppLanguageServerBaseUrl, resolveCppLanguageServerRuntimeAssetConfig, resolvePythonLanguageServerBaseUrl } from './lsp';
4
4
  import playground, { createPlaygroundBinding } from './playground';
5
5
  export default Terminal;
6
6
  export { createPlaygroundBinding, playground };
@@ -1,8 +1,9 @@
1
- import { resolveCppLanguageServerBaseUrl } from '../runtime';
1
+ import { resolveCppLanguageServerRuntimeAssetConfig } from '../runtime';
2
2
  import ClangdWorker from '../../clangd/worker?worker';
3
+ import { WorkerAssetBridge } from '../../playground/assetBridge';
3
4
  import { BrowserMessageReader, BrowserMessageWriter } from '../../utils/vscodeJsonrpcBrowser';
4
5
  const currentUrl = () => globalThis.location?.href || '';
5
- async function createServer(baseUrl) {
6
+ async function createServer(assetConfig) {
6
7
  let resolveReady = () => { };
7
8
  let rejectReady = (_error) => { };
8
9
  const ready = new Promise((resolve, reject) => {
@@ -10,11 +11,14 @@ async function createServer(baseUrl) {
10
11
  rejectReady = reject;
11
12
  });
12
13
  const worker = new ClangdWorker();
14
+ const assetBridge = new WorkerAssetBridge(worker, 'clangd', assetConfig);
13
15
  const cleanup = () => {
14
16
  worker.removeEventListener('message', readyListener);
15
17
  worker.removeEventListener('error', errorListener);
16
18
  };
17
19
  const readyListener = (event) => {
20
+ if (assetBridge.handleMessage(event))
21
+ return;
18
22
  switch (event.data?.type) {
19
23
  case 'ready': {
20
24
  cleanup();
@@ -34,13 +38,20 @@ async function createServer(baseUrl) {
34
38
  };
35
39
  worker.addEventListener('message', readyListener);
36
40
  worker.addEventListener('error', errorListener);
37
- worker.postMessage({ type: 'init', baseUrl });
41
+ worker.postMessage({
42
+ type: 'init',
43
+ baseUrl: assetConfig.baseUrl,
44
+ assets: {
45
+ baseUrl: assetConfig.baseUrl,
46
+ useAssetBridge: assetConfig.useAssetBridge
47
+ }
48
+ });
38
49
  await ready;
39
50
  return worker;
40
51
  }
41
52
  export async function getCppLanguageServer(options) {
42
- const baseUrl = resolveCppLanguageServerBaseUrl(options, currentUrl());
43
- const worker = await createServer(baseUrl);
53
+ const assetConfig = resolveCppLanguageServerRuntimeAssetConfig(options, currentUrl());
54
+ const worker = await createServer(assetConfig);
44
55
  const reader = new BrowserMessageReader(worker);
45
56
  const writer = new BrowserMessageWriter(worker);
46
57
  return {
@@ -1,5 +1,5 @@
1
1
  export { getEditorLanguageServer } from './registry';
2
- export { resolveCppLanguageServerBaseUrl, resolvePythonLanguageServerBaseUrl } from './runtime';
2
+ export { resolveCppLanguageServerBaseUrl, resolveCppLanguageServerRuntimeAssetConfig, resolvePythonLanguageServerBaseUrl } from './runtime';
3
3
  export { getCppLanguageServer } from './cpp/server';
4
4
  export { getPythonLanguageServer } from './python/server';
5
5
  export type { EditorLanguageServerHandle, EditorLanguageServerRuntimeOptions } from './types';
package/dist/lsp/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { getEditorLanguageServer } from './registry';
2
- export { resolveCppLanguageServerBaseUrl, resolvePythonLanguageServerBaseUrl } from './runtime';
2
+ export { resolveCppLanguageServerBaseUrl, resolveCppLanguageServerRuntimeAssetConfig, resolvePythonLanguageServerBaseUrl } from './runtime';
3
3
  export { getCppLanguageServer } from './cpp/server';
4
4
  export { getPythonLanguageServer } from './python/server';
@@ -1,3 +1,5 @@
1
1
  import type { EditorLanguageServerRuntimeOptions } from './types';
2
+ import { type ResolvedRuntimeAssetConfig } from '../playground/assets';
2
3
  export declare function resolveCppLanguageServerBaseUrl(options: string | EditorLanguageServerRuntimeOptions | undefined, currentUrl?: string): string;
4
+ export declare function resolveCppLanguageServerRuntimeAssetConfig(options: string | EditorLanguageServerRuntimeOptions | undefined, currentUrl?: string): ResolvedRuntimeAssetConfig;
3
5
  export declare function resolvePythonLanguageServerBaseUrl(options: string | EditorLanguageServerRuntimeOptions | undefined, currentUrl?: string): string;
@@ -1,3 +1,4 @@
1
+ import { resolveRuntimeAssetConfig } from '../playground/assets';
1
2
  const normalizeBaseUrl = (baseUrl, currentUrl = '') => {
2
3
  const normalized = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
3
4
  return currentUrl ? new URL(normalized, currentUrl).href : normalized;
@@ -5,16 +6,16 @@ const normalizeBaseUrl = (baseUrl, currentUrl = '') => {
5
6
  const normalizeRootUrl = (rootUrl) => rootUrl.endsWith('/') ? rootUrl.slice(0, -1) : rootUrl;
6
7
  const resolveRuntimeRootBaseUrl = (rootUrl, suffix, currentUrl = '') => normalizeBaseUrl(`${normalizeRootUrl(rootUrl) || ''}${suffix}`, currentUrl);
7
8
  export function resolveCppLanguageServerBaseUrl(options, currentUrl = '') {
9
+ return resolveCppLanguageServerRuntimeAssetConfig(options, currentUrl).baseUrl;
10
+ }
11
+ export function resolveCppLanguageServerRuntimeAssetConfig(options, currentUrl = '') {
8
12
  if (typeof options === 'string') {
9
- return resolveRuntimeRootBaseUrl(options, '/clangd/', currentUrl);
10
- }
11
- if (options?.cpp?.baseUrl) {
12
- return normalizeBaseUrl(options.cpp.baseUrl, currentUrl);
13
- }
14
- if (options?.rootUrl) {
15
- return resolveRuntimeRootBaseUrl(options.rootUrl, '/clangd/', currentUrl);
13
+ return resolveRuntimeAssetConfig('clangd', options, currentUrl);
16
14
  }
17
- return normalizeBaseUrl('/clangd/', currentUrl);
15
+ return resolveRuntimeAssetConfig('clangd', {
16
+ rootUrl: options?.rootUrl,
17
+ clangd: options?.cpp
18
+ }, currentUrl);
18
19
  }
19
20
  export function resolvePythonLanguageServerBaseUrl(options, currentUrl = '') {
20
21
  if (typeof options === 'string') {
@@ -1,4 +1,5 @@
1
1
  import type { MessageTransports } from '@hancomac/monaco-languageclient';
2
+ import type { RuntimeAssetLoader } from '../playground/assets';
2
3
  export interface EditorLanguageServerHandle {
3
4
  transport: string | MessageTransports;
4
5
  syncFile?: (path: string) => void;
@@ -8,6 +9,7 @@ export interface EditorLanguageServerRuntimeOptions {
8
9
  rootUrl?: string;
9
10
  cpp?: {
10
11
  baseUrl?: string;
12
+ loader?: RuntimeAssetLoader;
11
13
  };
12
14
  python?: {
13
15
  baseUrl?: string;
@@ -1,9 +1,15 @@
1
- import { JAVA_RUNTIME_LOAD_ASSETS, PYTHON_RUNTIME_LOAD_ASSETS } from './assets';
1
+ import { CLANGD_RUNTIME_LOAD_ASSETS, CLANG_RUNTIME_LOAD_ASSETS, JAVA_RUNTIME_LOAD_ASSETS, PYTHON_RUNTIME_LOAD_ASSETS } from './assets';
2
2
  const encoder = new TextEncoder();
3
3
  const transferBuffer = (bytes) => bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength
4
4
  ? bytes.buffer
5
5
  : bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
6
- const expectedAssetsForRuntime = (runtime) => new Set(runtime === 'python' ? [...PYTHON_RUNTIME_LOAD_ASSETS] : [...JAVA_RUNTIME_LOAD_ASSETS]);
6
+ const expectedAssetsForRuntime = (runtime) => new Set(runtime === 'python'
7
+ ? [...PYTHON_RUNTIME_LOAD_ASSETS]
8
+ : runtime === 'java'
9
+ ? [...JAVA_RUNTIME_LOAD_ASSETS]
10
+ : runtime === 'clang'
11
+ ? [...CLANG_RUNTIME_LOAD_ASSETS]
12
+ : [...CLANGD_RUNTIME_LOAD_ASSETS]);
7
13
  class RuntimeLoadProgress {
8
14
  fractions = new Map();
9
15
  expectedAssets;
@@ -1,4 +1,4 @@
1
- export type RuntimeAssetRuntime = 'python' | 'java';
1
+ export type RuntimeAssetRuntime = 'python' | 'java' | 'clang' | 'clangd';
2
2
  export interface RuntimeAssetLoadRequest {
3
3
  runtime: RuntimeAssetRuntime;
4
4
  asset: string;
@@ -42,6 +42,8 @@ export interface PlaygroundRuntimeAssets {
42
42
  rootUrl?: string;
43
43
  python?: RuntimeAssetConfig;
44
44
  java?: RuntimeAssetConfig;
45
+ clang?: RuntimeAssetConfig;
46
+ clangd?: RuntimeAssetConfig;
45
47
  rust?: RustRuntimeAssetConfig;
46
48
  go?: GoRuntimeAssetConfig;
47
49
  ocaml?: OcamlRuntimeAssetConfig;
@@ -68,6 +70,8 @@ export interface ResolvedRuntimeAssetConfig {
68
70
  }
69
71
  export declare const PYTHON_RUNTIME_LOAD_ASSETS: readonly ["pyodide.asm.js", "pyodide-lock.json", "pyodide.asm.wasm", "python_stdlib.zip"];
70
72
  export declare const JAVA_RUNTIME_LOAD_ASSETS: readonly ["compiler.wasm-runtime.js", "compiler.wasm", "compile-classlib-teavm.bin", "runtime-classlib-teavm.bin"];
73
+ export declare const CLANG_RUNTIME_LOAD_ASSETS: readonly ["bin/memfs.zip", "bin/clang.zip", "bin/lld.zip", "bin/sysroot.tar.zip"];
74
+ export declare const CLANGD_RUNTIME_LOAD_ASSETS: readonly ["clangd.js", "clangd.wasm.gz"];
71
75
  export declare function resolveRuntimeAssetConfig(runtime: RuntimeAssetRuntime, options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): ResolvedRuntimeAssetConfig;
72
76
  export declare function resolveRustCompilerUrl(options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): any;
73
77
  export declare function resolveGoCompilerUrl(options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): any;
@@ -12,15 +12,23 @@ export const JAVA_RUNTIME_LOAD_ASSETS = [
12
12
  'compile-classlib-teavm.bin',
13
13
  'runtime-classlib-teavm.bin'
14
14
  ];
15
+ export const CLANG_RUNTIME_LOAD_ASSETS = [
16
+ 'bin/memfs.zip',
17
+ 'bin/clang.zip',
18
+ 'bin/lld.zip',
19
+ 'bin/sysroot.tar.zip'
20
+ ];
21
+ export const CLANGD_RUNTIME_LOAD_ASSETS = ['clangd.js', 'clangd.wasm.gz'];
15
22
  const PYTHON_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/python/';
16
23
  const JAVA_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/java/';
24
+ const CLANG_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/clang/';
25
+ const CLANGD_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/clangd/';
17
26
  const normalizeBaseUrl = (baseUrl, currentUrl = '') => {
18
27
  const normalized = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
19
28
  return currentUrl ? new URL(normalized, currentUrl).href : normalized;
20
29
  };
21
30
  const resolveConfiguredUrl = (url, currentUrl = '') => currentUrl ? new URL(url, currentUrl).href : url;
22
31
  const normalizeRootUrl = (rootUrl) => rootUrl.endsWith('/') ? rootUrl.slice(0, -1) : rootUrl;
23
- const isLocalHostname = (hostname) => hostname === 'localhost' || hostname === '127.0.0.1';
24
32
  const resolvePythonBaseUrl = (rootUrl = '', currentUrl = '') => normalizeBaseUrl(`${normalizeRootUrl(rootUrl) || ''}/pyodide/`, currentUrl);
25
33
  const resolvePythonRuntimeAssetConfig = (options, currentUrl = '') => {
26
34
  if (typeof options === 'string') {
@@ -93,10 +101,85 @@ const resolveJavaRuntimeAssetConfig = (options, currentUrl = '') => {
93
101
  useAssetBridge: false
94
102
  };
95
103
  };
104
+ const resolveClangRuntimeAssetConfig = (options, currentUrl = '') => {
105
+ if (typeof options === 'string') {
106
+ return {
107
+ baseUrl: normalizeBaseUrl(`${normalizeRootUrl(options) || ''}/clang/`, currentUrl),
108
+ useAssetBridge: false
109
+ };
110
+ }
111
+ const runtimeConfig = options?.clang;
112
+ if (runtimeConfig?.baseUrl) {
113
+ return {
114
+ baseUrl: normalizeBaseUrl(runtimeConfig.baseUrl, currentUrl),
115
+ loader: runtimeConfig.loader,
116
+ useAssetBridge: !!runtimeConfig.loader
117
+ };
118
+ }
119
+ if (options?.rootUrl) {
120
+ return {
121
+ baseUrl: normalizeBaseUrl(`${normalizeRootUrl(options.rootUrl) || ''}/clang/`, currentUrl),
122
+ loader: runtimeConfig?.loader,
123
+ useAssetBridge: !!runtimeConfig?.loader
124
+ };
125
+ }
126
+ if (runtimeConfig?.loader) {
127
+ return {
128
+ baseUrl: CLANG_VIRTUAL_BASE_URL,
129
+ loader: runtimeConfig.loader,
130
+ useAssetBridge: true
131
+ };
132
+ }
133
+ return {
134
+ baseUrl: normalizeBaseUrl('/clang/', currentUrl),
135
+ useAssetBridge: false
136
+ };
137
+ };
138
+ const resolveClangdRuntimeAssetConfig = (options, currentUrl = '') => {
139
+ if (typeof options === 'string') {
140
+ return {
141
+ baseUrl: normalizeBaseUrl(`${normalizeRootUrl(options) || ''}/clangd/`, currentUrl),
142
+ useAssetBridge: false
143
+ };
144
+ }
145
+ const runtimeConfig = options?.clangd;
146
+ if (runtimeConfig?.baseUrl) {
147
+ return {
148
+ baseUrl: normalizeBaseUrl(runtimeConfig.baseUrl, currentUrl),
149
+ loader: runtimeConfig.loader,
150
+ useAssetBridge: !!runtimeConfig.loader
151
+ };
152
+ }
153
+ if (options?.rootUrl) {
154
+ return {
155
+ baseUrl: normalizeBaseUrl(`${normalizeRootUrl(options.rootUrl) || ''}/clangd/`, currentUrl),
156
+ loader: runtimeConfig?.loader,
157
+ useAssetBridge: !!runtimeConfig?.loader
158
+ };
159
+ }
160
+ if (runtimeConfig?.loader) {
161
+ return {
162
+ baseUrl: CLANGD_VIRTUAL_BASE_URL,
163
+ loader: runtimeConfig.loader,
164
+ useAssetBridge: true
165
+ };
166
+ }
167
+ return {
168
+ baseUrl: normalizeBaseUrl('/clangd/', currentUrl),
169
+ useAssetBridge: false
170
+ };
171
+ };
96
172
  export function resolveRuntimeAssetConfig(runtime, options, currentUrl = '') {
97
- return runtime === 'python'
98
- ? resolvePythonRuntimeAssetConfig(options, currentUrl)
99
- : resolveJavaRuntimeAssetConfig(options, currentUrl);
173
+ switch (runtime) {
174
+ case 'python':
175
+ return resolvePythonRuntimeAssetConfig(options, currentUrl);
176
+ case 'java':
177
+ return resolveJavaRuntimeAssetConfig(options, currentUrl);
178
+ case 'clang':
179
+ return resolveClangRuntimeAssetConfig(options, currentUrl);
180
+ case 'clangd':
181
+ return resolveClangdRuntimeAssetConfig(options, currentUrl);
182
+ }
100
183
  }
101
184
  export function resolveRustCompilerUrl(options, currentUrl = '') {
102
185
  const configuredCompilerUrl = (typeof options === 'object' && options?.rust?.compilerUrl) ||
@@ -206,49 +289,7 @@ export function resolveTinyGoHostCompileUrls(options, currentUrl = '') {
206
289
  if (configuredHostCompileUrl) {
207
290
  return [resolveConfiguredUrl(configuredHostCompileUrl, currentUrl)];
208
291
  }
209
- const urls = [];
210
- const pushUrl = (url) => {
211
- const resolvedUrl = resolveConfiguredUrl(url, currentUrl);
212
- if (!urls.includes(resolvedUrl)) {
213
- urls.push(resolvedUrl);
214
- }
215
- };
216
- let currentPageUrl = null;
217
- try {
218
- currentPageUrl = currentUrl ? new URL(currentUrl) : null;
219
- }
220
- catch {
221
- currentPageUrl = null;
222
- }
223
- const canAutoDeriveHostCompileUrls = currentPageUrl !== null &&
224
- isLocalHostname(currentPageUrl.hostname) &&
225
- (currentPageUrl.protocol === 'http:' || currentPageUrl.protocol === 'https:');
226
- if (!canAutoDeriveHostCompileUrls || currentPageUrl === null) {
227
- return urls;
228
- }
229
- if (typeof options === 'string') {
230
- pushUrl(`${normalizeRootUrl(options) || ''}/api/tinygo/compile`);
231
- }
232
- else if (options?.rootUrl) {
233
- pushUrl(`${normalizeRootUrl(options.rootUrl) || ''}/api/tinygo/compile`);
234
- }
235
- else {
236
- const currentPathname = currentPageUrl.pathname.endsWith('/')
237
- ? currentPageUrl.pathname
238
- : `${currentPageUrl.pathname}/`;
239
- pushUrl(`${currentPageUrl.origin}${currentPathname}api/tinygo/compile`);
240
- }
241
- const moduleUrl = resolveTinyGoModuleUrl(options, currentUrl);
242
- if (moduleUrl) {
243
- const moduleAssetUrl = new URL(moduleUrl, currentUrl || undefined);
244
- if (moduleAssetUrl.protocol === 'http:' || moduleAssetUrl.protocol === 'https:') {
245
- pushUrl(new URL('../api/tinygo/compile', moduleAssetUrl).toString());
246
- }
247
- }
248
- if (currentPageUrl.port !== '4175') {
249
- pushUrl(`${currentPageUrl.protocol}//${currentPageUrl.hostname}:4175/api/tinygo/compile`);
250
- }
251
- return urls;
292
+ return [];
252
293
  }
253
294
  export function resolveTinyGoHostCompileUrl(options, currentUrl = '') {
254
295
  return resolveTinyGoHostCompileUrls(options, currentUrl)[0] || '';
@@ -1,5 +1,6 @@
1
1
  import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
2
- import type { PlaygroundRuntimeAssets } from './assets';
2
+ import { WorkerAssetBridge } from './assetBridge';
3
+ import { type PlaygroundRuntimeAssets } from './assets';
3
4
  import type { Sandbox } from './sandbox';
4
5
  import type { Writable } from 'svelte/store';
5
6
  declare class Clang implements Sandbox {
@@ -21,9 +22,10 @@ declare class Clang implements Sandbox {
21
22
  waitingForInput: boolean;
22
23
  pendingEof: boolean;
23
24
  exit: boolean;
25
+ assetBridge: WorkerAssetBridge | null;
24
26
  activeReject: ((reason?: string) => void) | null;
25
27
  constructor(language: 'C' | 'CPP');
26
- load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions, _progress?: {
28
+ load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions, progress?: {
27
29
  set?: (value: number) => void;
28
30
  } | import('svelte/store').Writable<number>): Promise<void>;
29
31
  write(input: string): void;