@cognipilot/rumoca-core 0.9.4 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/rumoca_worker.js CHANGED
@@ -1,3 +1,10 @@
1
+ import {
2
+ ensureParsedSourceRootCache,
3
+ normalizeSimulationSolver,
4
+ renderDaeTextWithRuntime,
5
+ simulateModelWithRuntime,
6
+ } from './rumoca_runtime.js';
7
+
1
8
  // Web Worker for Rumoca WASM with rayon threading support
2
9
  // This worker runs WASM functions that use Atomics.wait (not allowed on main thread)
3
10
 
@@ -7,13 +14,34 @@ const cacheBust = workerUrl.searchParams.get('v') || '';
7
14
  const withCacheBust = (path) =>
8
15
  cacheBust ? `${path}?v=${encodeURIComponent(cacheBust)}` : path;
9
16
 
17
+ // WASM scenario-config commands take JSON-string arguments. Accept either an
18
+ // already-stringified payload field or a structured object from the main thread.
19
+ const jsonArg = (value, emptyDefault = '') =>
20
+ value == null ? emptyDefault : (typeof value === 'string' ? value : JSON.stringify(value));
21
+
22
+ function hasWorkspaceSources(workspaceSources) {
23
+ if (typeof workspaceSources !== 'string') {
24
+ return false;
25
+ }
26
+ const trimmed = workspaceSources.trim();
27
+ return Boolean(trimmed && trimmed !== '{}');
28
+ }
29
+
30
+ function syncWorkspaceSources(workspaceSources) {
31
+ if (typeof sync_workspace_sources !== 'function') {
32
+ throw new Error('Workspace-source simulation not available in this WASM build.');
33
+ }
34
+ sync_workspace_sources(workspaceSources);
35
+ }
36
+
10
37
  let init;
11
38
  let wasm_init;
12
39
  let get_version;
13
40
  let get_builtin_targets;
14
41
  let compile_to_json;
15
- let compile_with_project_sources;
16
- let sync_project_sources;
42
+ let compile_with_workspace_sources;
43
+ let sync_workspace_sources;
44
+ let workspace_effective_source_roots;
17
45
  let get_source_root_statuses;
18
46
  let get_simulation_models;
19
47
  let compile_with_source_roots;
@@ -22,6 +50,7 @@ let clear_source_root_cache;
22
50
  let get_source_root_document_count;
23
51
  let export_parsed_source_roots_binary;
24
52
  let merge_parsed_source_roots_binary;
53
+ let prime_source_root_completion_cache;
25
54
  let lsp_diagnostics;
26
55
  let lsp_hover;
27
56
  let lsp_completion;
@@ -34,52 +63,30 @@ let lsp_semantic_token_legend;
34
63
  let list_classes;
35
64
  let get_class_info;
36
65
  let render_target;
66
+ let scenario_get_simulation_config;
67
+ let scenario_set_simulation_preset;
68
+ let scenario_reset_simulation_preset;
69
+ let scenario_get_visualization_config;
70
+ let scenario_set_visualization_config;
71
+ let scenario_get_codegen_config;
72
+ let scenario_set_codegen_config;
73
+ let scenario_get_source_roots;
74
+ let scenario_set_source_roots;
75
+ let scenario_get_scenario_config;
76
+ let scenario_get_scenario_config_full;
77
+ let scenario_set_scenario_config;
78
+ let scenario_default_scenario_config;
37
79
  let simulate_model = null;
38
- let simulate_model_with_project_sources = null;
80
+ let simulate_model_with_workspace_sources = null;
39
81
  let lower_model_to_solve_json = null;
82
+ let model_parameter_metadata = null;
83
+ let model_parameter_metadata_with_workspace_sources = null;
84
+ let model_parameter_metadata_with_source_roots = null;
85
+ let prepare_gpu_simulation = null;
86
+ let wasmRuntimeModule = null;
40
87
  let wasmModuleLoaded = false;
41
88
  let activeRequestId = null;
42
89
 
43
- // Lazy diffsol (stiff/implicit) addon — a separate relaxed-SIMD module, loaded
44
- // only when a BDF solve is requested, so this worker (and the package) stay
45
- // universal. Returns the addon's exports, or rejects on browsers without
46
- // relaxed-SIMD.
47
- let diffsolAddonPromise = null;
48
- function loadDiffsolAddon() {
49
- if (!diffsolAddonPromise) {
50
- diffsolAddonPromise = (async () => {
51
- const mod = await import(withCacheBust('./rumoca_bind_wasm_diffsol.js'));
52
- await mod.default();
53
- return mod;
54
- })();
55
- diffsolAddonPromise.catch(() => {
56
- diffsolAddonPromise = null;
57
- });
58
- }
59
- return diffsolAddonPromise;
60
- }
61
-
62
- // Run a stiff (BDF/diffsol) simulation: the main module lowers the model to a
63
- // `{ solve_model, t_end, dt }` payload, the addon simulates it. Returns the
64
- // same JSON string shape as `simulate_model`.
65
- async function simulateViaDiffsol(source, model, tEnd, dt) {
66
- if (typeof lower_model_to_solve_json !== 'function') {
67
- throw new Error('this build cannot lower models for the diffsol addon');
68
- }
69
- let addon;
70
- try {
71
- addon = await loadDiffsolAddon();
72
- } catch (err) {
73
- throw new Error(
74
- 'the stiff (BDF/diffsol) solver needs a browser with WebAssembly '
75
- + 'relaxed-SIMD (Chrome 114+, Firefox 120+, Safari 16.4+). '
76
- + `Use RK45 instead. (${err && err.message ? err.message : err})`,
77
- );
78
- }
79
- const prep = lower_model_to_solve_json(source, model, tEnd, dt);
80
- return addon.simulate_solve_model_diffsol(prep);
81
- }
82
-
83
90
  function canUseSharedWasmThreads() {
84
91
  return typeof self.crossOriginIsolated === 'boolean'
85
92
  && self.crossOriginIsolated
@@ -94,8 +101,9 @@ async function loadWasmModule() {
94
101
  get_version = mod.get_version;
95
102
  get_builtin_targets = mod.get_builtin_targets;
96
103
  compile_to_json = mod.compile_to_json;
97
- compile_with_project_sources = mod.compile_with_project_sources;
98
- sync_project_sources = mod.sync_project_sources;
104
+ compile_with_workspace_sources = mod.compile_with_workspace_sources;
105
+ sync_workspace_sources = mod.sync_workspace_sources;
106
+ workspace_effective_source_roots = mod.workspace_effective_source_roots;
99
107
  get_source_root_statuses = mod.get_source_root_statuses;
100
108
  get_simulation_models = mod.get_simulation_models;
101
109
  compile_with_source_roots = mod.compile_with_source_roots;
@@ -104,6 +112,7 @@ async function loadWasmModule() {
104
112
  get_source_root_document_count = mod.get_source_root_document_count;
105
113
  export_parsed_source_roots_binary = mod.export_parsed_source_roots_binary;
106
114
  merge_parsed_source_roots_binary = mod.merge_parsed_source_roots_binary;
115
+ prime_source_root_completion_cache = mod.prime_source_root_completion_cache;
107
116
  lsp_diagnostics = mod.lsp_diagnostics;
108
117
  lsp_hover = mod.lsp_hover;
109
118
  lsp_completion = mod.lsp_completion;
@@ -116,28 +125,66 @@ async function loadWasmModule() {
116
125
  list_classes = mod.list_classes;
117
126
  get_class_info = mod.get_class_info;
118
127
  render_target = mod.render_target;
128
+ scenario_get_simulation_config = mod.scenario_get_simulation_config;
129
+ scenario_set_simulation_preset = mod.scenario_set_simulation_preset;
130
+ scenario_reset_simulation_preset = mod.scenario_reset_simulation_preset;
131
+ scenario_get_visualization_config = mod.scenario_get_visualization_config;
132
+ scenario_set_visualization_config = mod.scenario_set_visualization_config;
133
+ scenario_get_codegen_config = mod.scenario_get_codegen_config;
134
+ scenario_set_codegen_config = mod.scenario_set_codegen_config;
135
+ scenario_get_source_roots = mod.scenario_get_source_roots;
136
+ scenario_set_source_roots = mod.scenario_set_source_roots;
137
+ scenario_get_scenario_config = mod.scenario_get_scenario_config;
138
+ scenario_get_scenario_config_full = mod.scenario_get_scenario_config_full;
139
+ scenario_set_scenario_config = mod.scenario_set_scenario_config;
140
+ scenario_default_scenario_config = mod.scenario_default_scenario_config;
119
141
  if (typeof mod.simulate_model === 'function') {
120
142
  simulate_model = mod.simulate_model;
121
143
  }
122
- if (typeof mod.simulate_model_with_project_sources === 'function') {
123
- simulate_model_with_project_sources = mod.simulate_model_with_project_sources;
124
- }
144
+ simulate_model_with_workspace_sources = mod.simulate_model_with_workspace_sources;
125
145
  if (typeof mod.lower_model_to_solve_json === 'function') {
126
146
  lower_model_to_solve_json = mod.lower_model_to_solve_json;
127
147
  }
148
+ if (typeof mod.model_parameter_metadata === 'function') {
149
+ model_parameter_metadata = mod.model_parameter_metadata;
150
+ }
151
+ if (typeof mod.model_parameter_metadata_with_workspace_sources === 'function') {
152
+ model_parameter_metadata_with_workspace_sources = mod.model_parameter_metadata_with_workspace_sources;
153
+ }
154
+ if (typeof mod.model_parameter_metadata_with_source_roots === 'function') {
155
+ model_parameter_metadata_with_source_roots = mod.model_parameter_metadata_with_source_roots;
156
+ }
157
+ if (typeof mod.prepare_gpu_simulation === 'function') {
158
+ prepare_gpu_simulation = mod.prepare_gpu_simulation;
159
+ }
160
+ wasmRuntimeModule = {
161
+ compile: compile_to_json,
162
+ lower_model_to_solve_json,
163
+ merge_parsed_source_roots_binary,
164
+ model_parameter_metadata,
165
+ model_parameter_metadata_with_source_roots,
166
+ model_parameter_metadata_with_workspace_sources,
167
+ prime_source_root_completion_cache,
168
+ prepare_gpu_simulation,
169
+ render_target,
170
+ simulate_model,
171
+ };
128
172
  wasmModuleLoaded = true;
129
173
  }
130
174
 
131
175
  let initialized = false;
176
+ const debugWorkerLogs = false;
132
177
 
133
178
  // Intercept console.log to forward progress messages to main thread
134
179
  const originalLog = console.log;
135
180
  console.log = function(...args) {
136
- originalLog.apply(console, args);
181
+ if (debugWorkerLogs) {
182
+ originalLog.apply(console, args);
183
+ }
137
184
  // Forward WASM progress messages to main thread
138
185
  const message = args.join(' ');
139
186
  if (message.includes('[WASM]') && message.includes('parsing')) {
140
- // Extract progress info: "[WASM] wasm::project: parsing 50/500 (10%)"
187
+ // Extract progress info: "[WASM] wasm::workspace: parsing 50/500 (10%)"
141
188
  const scopeMatch = message.match(/\[WASM\]\s+([^:]+(?:::[^:]+)*):\s+/);
142
189
  const match = message.match(/parsing (\d+)\/(\d+) \((\d+)%\)/);
143
190
  if (match) {
@@ -159,20 +206,20 @@ async function initialize() {
159
206
  if (initialized) return true;
160
207
 
161
208
  try {
162
- console.log('[Worker] Loading WASM module...');
209
+ if (debugWorkerLogs) console.log('[Worker] Loading WASM module...');
163
210
  await loadWasmModule();
164
211
  await init({ module_or_path: withCacheBust('./rumoca_bind_wasm_bg.wasm') });
165
212
 
166
213
  const requestedThreads = navigator.hardwareConcurrency || 4;
167
214
  const numThreads = canUseSharedWasmThreads() ? requestedThreads : 0;
168
215
  if (numThreads > 0) {
169
- console.log('[Worker] Initializing thread pool...');
216
+ if (debugWorkerLogs) console.log('[Worker] Initializing thread pool...');
170
217
  } else {
171
- console.warn('[Worker] Shared WASM threads unavailable; using single-thread mode.');
218
+ if (debugWorkerLogs) console.warn('[Worker] Shared WASM threads unavailable; using single-thread mode.');
172
219
  }
173
220
  await wasm_init(numThreads);
174
221
  if (numThreads > 0) {
175
- console.log(`[Worker] Thread pool initialized with ${numThreads} threads`);
222
+ if (debugWorkerLogs) console.log(`[Worker] Thread pool initialized with ${numThreads} threads`);
176
223
  }
177
224
  initialized = true;
178
225
  return true;
@@ -211,23 +258,35 @@ self.onmessage = async (e) => {
211
258
  switch (action) {
212
259
  case 'languageCommand': {
213
260
  const payload = e.data.payload || {};
214
- if (typeof sync_project_sources === 'function' && typeof payload.projectSources === 'string') {
215
- sync_project_sources(payload.projectSources);
261
+ if (typeof sync_workspace_sources === 'function' && typeof payload.workspaceSources === 'string') {
262
+ sync_workspace_sources(payload.workspaceSources);
216
263
  }
217
264
  switch (command) {
218
265
  case 'rumoca.language.getSourceRootDocumentCount':
219
266
  result = get_source_root_document_count();
220
267
  break;
221
268
  case 'rumoca.language.diagnostics':
269
+ await ensureParsedSourceRootCache(
270
+ wasmRuntimeModule,
271
+ payload.sourceRootCacheUrl || '',
272
+ );
222
273
  result = lsp_diagnostics(payload.source || '');
223
274
  break;
224
275
  case 'rumoca.language.hover':
225
276
  result = lsp_hover(payload.source || '', payload.line, payload.character);
226
277
  break;
227
278
  case 'rumoca.language.completion':
279
+ await ensureParsedSourceRootCache(
280
+ wasmRuntimeModule,
281
+ payload.sourceRootCacheUrl || '',
282
+ );
228
283
  result = lsp_completion(payload.source || '', payload.line, payload.character);
229
284
  break;
230
285
  case 'rumoca.language.completionWithTiming':
286
+ await ensureParsedSourceRootCache(
287
+ wasmRuntimeModule,
288
+ payload.sourceRootCacheUrl || '',
289
+ );
231
290
  result = lsp_completion_with_timing(payload.source || '', payload.line, payload.character);
232
291
  break;
233
292
  case 'rumoca.language.definition':
@@ -263,53 +322,206 @@ self.onmessage = async (e) => {
263
322
  }
264
323
  break;
265
324
  }
266
- case 'projectCommand': {
325
+ case 'scenarioCommand': {
267
326
  const payload = e.data.payload || {};
268
327
  switch (command) {
269
- case 'rumoca.project.getSimulationModels':
328
+ case 'rumoca.scenario.getSimulationModels':
270
329
  result = get_simulation_models(payload.source || '', payload.defaultModel || '');
271
330
  break;
272
- case 'rumoca.project.startSimulation':
331
+ case 'rumoca.scenario.getSimulationConfig':
332
+ result = scenario_get_simulation_config(
333
+ jsonArg(payload.workspaceSources),
334
+ payload.model || '',
335
+ jsonArg(payload.fallback),
336
+ );
337
+ break;
338
+ case 'rumoca.scenario.setSimulationPreset':
339
+ result = scenario_set_simulation_preset(
340
+ jsonArg(payload.workspaceSources),
341
+ payload.model || '',
342
+ jsonArg(payload.preset, 'null'),
343
+ );
344
+ break;
345
+ case 'rumoca.scenario.resetSimulationPreset':
346
+ result = scenario_reset_simulation_preset(
347
+ jsonArg(payload.workspaceSources),
348
+ payload.model || '',
349
+ );
350
+ break;
351
+ case 'rumoca.scenario.getVisualizationConfig':
352
+ result = scenario_get_visualization_config(
353
+ jsonArg(payload.workspaceSources),
354
+ payload.model || '',
355
+ );
356
+ break;
357
+ case 'rumoca.scenario.setVisualizationConfig':
358
+ result = scenario_set_visualization_config(
359
+ jsonArg(payload.workspaceSources),
360
+ payload.model || '',
361
+ jsonArg(payload.views, 'null'),
362
+ );
363
+ break;
364
+ case 'rumoca.scenario.getCodegenConfig':
365
+ result = scenario_get_codegen_config(
366
+ jsonArg(payload.workspaceSources),
367
+ payload.model || '',
368
+ );
369
+ break;
370
+ case 'rumoca.scenario.setCodegenConfig':
371
+ result = scenario_set_codegen_config(
372
+ jsonArg(payload.workspaceSources),
373
+ payload.model || '',
374
+ jsonArg(payload.config, 'null'),
375
+ );
376
+ break;
377
+ case 'rumoca.scenario.getSourceRoots':
378
+ result = scenario_get_source_roots(
379
+ jsonArg(payload.workspaceSources),
380
+ payload.model || '',
381
+ payload.task || '',
382
+ );
383
+ break;
384
+ case 'rumoca.scenario.setSourceRoots':
385
+ result = scenario_set_source_roots(
386
+ jsonArg(payload.workspaceSources),
387
+ payload.model || '',
388
+ jsonArg(payload.config, 'null'),
389
+ );
390
+ break;
391
+ case 'rumoca.scenario.getScenarioConfig':
392
+ result = scenario_get_scenario_config(
393
+ jsonArg(payload.workspaceSources),
394
+ payload.path || payload.uri || '',
395
+ );
396
+ break;
397
+ case 'rumoca.scenario.getScenarioConfigFull':
398
+ result = scenario_get_scenario_config_full(
399
+ jsonArg(payload.workspaceSources),
400
+ payload.path || payload.uri || '',
401
+ );
402
+ break;
403
+ case 'rumoca.scenario.setScenarioConfig':
404
+ result = scenario_set_scenario_config(
405
+ payload.path || payload.uri || '',
406
+ jsonArg(payload.config, 'null'),
407
+ );
408
+ break;
409
+ case 'rumoca.scenario.defaultScenarioConfig':
410
+ result = scenario_default_scenario_config(
411
+ jsonArg(payload.workspaceSources),
412
+ payload.model || '',
413
+ payload.task || '',
414
+ );
415
+ break;
416
+ case 'rumoca.scenario.startSimulation':
273
417
  if (!simulate_model) {
274
418
  throw new Error('Simulation not available in this WASM build. Rebuild with rumoca-sim (diffsol feature enabled).');
275
419
  }
276
- if ((payload.solver || '') === 'bdf') {
277
- // Stiff path: route to the lazy diffsol addon (the
278
- // main module can't simulate bdf — it has no diffsol
279
- // runtime). Project-local sources aren't supported on
280
- // this path yet.
281
- result = await simulateViaDiffsol(
420
+ if (typeof payload.sourceRoots === 'string' && payload.sourceRoots.trim() && payload.sourceRoots.trim() !== '{}') {
421
+ load_source_roots(payload.sourceRoots);
422
+ }
423
+ {
424
+ const solver = normalizeSimulationSolver(payload.solver);
425
+ const useWorkspaceSources = hasWorkspaceSources(payload.workspaceSources);
426
+ const parameterOverrides = payload.parameterOverrides && typeof payload.parameterOverrides === 'object'
427
+ ? payload.parameterOverrides
428
+ : {};
429
+ if (useWorkspaceSources && solver === 'bdf') {
430
+ syncWorkspaceSources(payload.workspaceSources);
431
+ result = JSON.stringify(await simulateModelWithRuntime({
432
+ wasm: wasmRuntimeModule,
433
+ pkgBase: './',
434
+ source: payload.source || '',
435
+ modelName: payload.modelName || 'Model',
436
+ tEnd: payload.tEnd || 1.0,
437
+ dt: payload.dt || 0,
438
+ solver,
439
+ sourceRootCacheUrl: payload.sourceRootCacheUrl || '',
440
+ parameterOverrides,
441
+ }));
442
+ } else if (useWorkspaceSources) {
443
+ if (typeof simulate_model_with_workspace_sources !== 'function') {
444
+ throw new Error('Workspace-source simulation not available in this WASM build.');
445
+ }
446
+ result = simulate_model_with_workspace_sources(
447
+ payload.source || '',
448
+ payload.modelName || 'Model',
449
+ payload.workspaceSources,
450
+ payload.tEnd || 1.0,
451
+ payload.dt || 0,
452
+ solver,
453
+ jsonArg(parameterOverrides, '{}'),
454
+ );
455
+ } else {
456
+ result = JSON.stringify(await simulateModelWithRuntime({
457
+ wasm: wasmRuntimeModule,
458
+ pkgBase: './',
459
+ source: payload.source || '',
460
+ modelName: payload.modelName || 'Model',
461
+ tEnd: payload.tEnd || 1.0,
462
+ dt: payload.dt || 0,
463
+ solver,
464
+ sourceRootCacheUrl: payload.sourceRootCacheUrl || '',
465
+ parameterOverrides,
466
+ }));
467
+ }
468
+ }
469
+ break;
470
+ case 'rumoca.model.parameterMetadata':
471
+ if (typeof payload.sourceRoots === 'string' && payload.sourceRoots.trim() && payload.sourceRoots.trim() !== '{}') {
472
+ if (typeof model_parameter_metadata_with_source_roots !== 'function') {
473
+ throw new Error('Source-root parameter metadata is not available in this WASM build.');
474
+ }
475
+ result = model_parameter_metadata_with_source_roots(
282
476
  payload.source || '',
283
477
  payload.modelName || 'Model',
284
- payload.tEnd || 1.0,
285
- payload.dt || 0,
478
+ payload.sourceRoots,
286
479
  );
287
- } else if (
288
- simulate_model_with_project_sources
289
- && typeof payload.projectSources === 'string'
290
- && payload.projectSources.trim()
291
- && payload.projectSources.trim() !== '{}'
292
- ) {
293
- result = simulate_model_with_project_sources(
480
+ } else if (hasWorkspaceSources(payload.workspaceSources)) {
481
+ if (typeof model_parameter_metadata_with_workspace_sources !== 'function') {
482
+ throw new Error('Workspace parameter metadata is not available in this WASM build.');
483
+ }
484
+ result = model_parameter_metadata_with_workspace_sources(
294
485
  payload.source || '',
295
486
  payload.modelName || 'Model',
296
- payload.projectSources,
297
- payload.tEnd || 1.0,
298
- payload.dt || 0,
299
- payload.solver || 'auto',
487
+ payload.workspaceSources,
300
488
  );
301
489
  } else {
302
- result = simulate_model(
490
+ if (typeof model_parameter_metadata !== 'function') {
491
+ throw new Error('Parameter metadata is not available in this WASM build.');
492
+ }
493
+ result = model_parameter_metadata(
303
494
  payload.source || '',
304
495
  payload.modelName || 'Model',
305
- payload.tEnd || 1.0,
306
- payload.dt || 0,
307
- payload.solver || 'auto',
308
496
  );
309
497
  }
310
498
  break;
499
+ case 'rumoca.scenario.renderDaeText':
500
+ result = await renderDaeTextWithRuntime({
501
+ wasm: wasmRuntimeModule,
502
+ source: payload.source || '',
503
+ modelName: payload.modelName || 'Model',
504
+ sourceRootCacheUrl: payload.sourceRootCacheUrl || '',
505
+ });
506
+ break;
507
+ case 'rumoca.scenario.prepareGpuSimulation':
508
+ await ensureParsedSourceRootCache(
509
+ wasmRuntimeModule,
510
+ payload.sourceRootCacheUrl || '',
511
+ );
512
+ if (typeof payload.source !== 'string') {
513
+ throw new Error('prepareGpuSimulation requires source text');
514
+ }
515
+ if (typeof prepare_gpu_simulation !== 'function') {
516
+ throw new Error('prepare_gpu_simulation missing in this WASM build');
517
+ }
518
+ result = prepare_gpu_simulation(
519
+ payload.source || '',
520
+ payload.modelName || 'Model',
521
+ );
522
+ break;
311
523
  default:
312
- throw new Error(`Unknown project command: ${command}`);
524
+ throw new Error(`Unknown scenario command: ${command}`);
313
525
  }
314
526
  break;
315
527
  }
@@ -325,11 +537,11 @@ self.onmessage = async (e) => {
325
537
  case 'rumoca.workspace.compile':
326
538
  result = compile_to_json(payload.source || '', payload.modelName || 'Model');
327
539
  break;
328
- case 'rumoca.workspace.compileWithProjectSources':
329
- result = compile_with_project_sources(
540
+ case 'rumoca.workspace.compileWithWorkspaceSources':
541
+ result = compile_with_workspace_sources(
330
542
  payload.source || '',
331
543
  payload.modelName || 'Model',
332
- payload.projectSources || '{}',
544
+ payload.workspaceSources || '{}',
333
545
  );
334
546
  break;
335
547
  case 'rumoca.workspace.compileWithSourceRoots':
@@ -345,6 +557,15 @@ self.onmessage = async (e) => {
345
557
  case 'rumoca.workspace.getSourceRootStatuses':
346
558
  result = get_source_root_statuses();
347
559
  break;
560
+ case 'rumoca.workspace.effectiveSourceRoots':
561
+ if (typeof workspace_effective_source_roots !== 'function') {
562
+ throw new Error('workspace_effective_source_roots missing in this WASM build');
563
+ }
564
+ result = workspace_effective_source_roots(
565
+ payload.workspaceSources || '{}',
566
+ payload.focusPath || payload.path || '',
567
+ );
568
+ break;
348
569
  case 'rumoca.workspace.exportParsedSourceRootsBinary':
349
570
  result = export_parsed_source_roots_binary(payload.urisJson || '[]');
350
571
  break;
@@ -388,7 +609,6 @@ self.onmessage = async (e) => {
388
609
  self.postMessage({ id, success: true, result });
389
610
  } catch (e) {
390
611
  activeRequestId = null;
391
- console.error('[Worker] Error:', e);
392
612
  self.postMessage({ id, error: e.message || String(e) });
393
613
  }
394
614
  };