@tracecode/harness 0.5.0 → 0.6.0

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +67 -80
  3. package/README.md +31 -4
  4. package/dist/browser.cjs +974 -19
  5. package/dist/browser.cjs.map +1 -1
  6. package/dist/browser.d.cts +3 -2
  7. package/dist/browser.d.ts +3 -2
  8. package/dist/browser.js +974 -19
  9. package/dist/browser.js.map +1 -1
  10. package/dist/cli.cjs +24 -0
  11. package/dist/cli.cjs.map +1 -1
  12. package/dist/cli.js +24 -0
  13. package/dist/cli.js.map +1 -1
  14. package/dist/core.cjs +611 -4
  15. package/dist/core.cjs.map +1 -1
  16. package/dist/core.d.cts +20 -6
  17. package/dist/core.d.ts +20 -6
  18. package/dist/core.js +605 -4
  19. package/dist/core.js.map +1 -1
  20. package/dist/index.cjs +1055 -52
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +3 -3
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +1049 -52
  25. package/dist/index.js.map +1 -1
  26. package/dist/internal/browser.cjs +211 -0
  27. package/dist/internal/browser.cjs.map +1 -1
  28. package/dist/internal/browser.d.cts +62 -6
  29. package/dist/internal/browser.d.ts +62 -6
  30. package/dist/internal/browser.js +210 -0
  31. package/dist/internal/browser.js.map +1 -1
  32. package/dist/javascript.cjs +37 -19
  33. package/dist/javascript.cjs.map +1 -1
  34. package/dist/javascript.d.cts +2 -2
  35. package/dist/javascript.d.ts +2 -2
  36. package/dist/javascript.js +37 -19
  37. package/dist/javascript.js.map +1 -1
  38. package/dist/python.cjs +14 -14
  39. package/dist/python.cjs.map +1 -1
  40. package/dist/python.d.cts +8 -8
  41. package/dist/python.d.ts +8 -8
  42. package/dist/python.js +14 -14
  43. package/dist/python.js.map +1 -1
  44. package/dist/{runtime-types-DtaaAhHL.d.ts → runtime-types-89nchXlY.d.cts} +8 -4
  45. package/dist/{runtime-types--lBQ6rYu.d.cts → runtime-types-CCQ-ZLc9.d.ts} +8 -4
  46. package/dist/{types-DwIYM3Ku.d.cts → types-zyvpJKCi.d.cts} +1 -0
  47. package/dist/{types-DwIYM3Ku.d.ts → types-zyvpJKCi.d.ts} +1 -0
  48. package/package.json +12 -6
  49. package/workers/java/.build/classes/harness/browser/JavaRewriteLibrary.class +0 -0
  50. package/workers/java/java-worker.js +712 -0
  51. package/workers/java/src/harness/browser/JavaRewriteLibrary.java +54 -0
  52. package/workers/javascript/javascript-worker.js +343 -63
  53. package/workers/python/generated-python-harness-snippets.js +3 -3
  54. package/workers/python/pyodide-worker.js +419 -68
  55. package/workers/python/runtime-core.js +52 -3
  56. package/workers/vendor/java-browser-spike-helper.jar +0 -0
  57. package/workers/vendor/java-practice-rewriter.jar +0 -0
  58. package/workers/vendor/java-rewrite-bridge.jar +0 -0
  59. package/workers/vendor/javaparser-core-3.25.10.jar +0 -0
  60. package/workers/vendor/jdk.compiler-17.jar +0 -0
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // packages/harness-core/src/trace-contract.ts
2
- var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-03-13";
2
+ var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-04-21";
3
3
  var TRACE_EVENTS = /* @__PURE__ */ new Set([
4
4
  "line",
5
5
  "call",
@@ -120,7 +120,39 @@ function normalizeAccesses(accesses) {
120
120
  }
121
121
  return payload;
122
122
  }).filter((access) => access !== null);
123
- return normalized.length > 0 ? normalized : void 0;
123
+ if (normalized.length === 0) {
124
+ return void 0;
125
+ }
126
+ const statsByVariable = /* @__PURE__ */ new Map();
127
+ for (const access of normalized) {
128
+ const stats = statsByVariable.get(access.variable) ?? { hasCellRead: false, hasCellWrite: false };
129
+ if (access.kind === "cell-read") stats.hasCellRead = true;
130
+ if (access.kind === "cell-write") stats.hasCellWrite = true;
131
+ statsByVariable.set(access.variable, stats);
132
+ }
133
+ const deduped = /* @__PURE__ */ new Set();
134
+ const collapsed = normalized.filter((access) => {
135
+ const stats = statsByVariable.get(access.variable);
136
+ if (access.kind === "indexed-read" && (stats?.hasCellRead || stats?.hasCellWrite)) {
137
+ return false;
138
+ }
139
+ if (access.kind === "indexed-write" && stats?.hasCellWrite) {
140
+ return false;
141
+ }
142
+ const key = JSON.stringify([
143
+ access.variable,
144
+ access.kind,
145
+ access.indices ?? null,
146
+ access.pathDepth ?? null,
147
+ access.method ?? null
148
+ ]);
149
+ if (deduped.has(key)) {
150
+ return false;
151
+ }
152
+ deduped.add(key);
153
+ return true;
154
+ });
155
+ return collapsed.length > 0 ? collapsed : void 0;
124
156
  }
125
157
  function normalizeRecord(value) {
126
158
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
@@ -184,8 +216,64 @@ function normalizeTraceStep(step) {
184
216
  ...normalizedVisualization ? { visualization: normalizedVisualization } : {}
185
217
  };
186
218
  }
219
+ function collapseTraceAccessNoise(trace) {
220
+ const variablesWithCellAccess = /* @__PURE__ */ new Set();
221
+ const accessStatsByVariable = /* @__PURE__ */ new Map();
222
+ for (const step of trace) {
223
+ for (const access of step.accesses ?? []) {
224
+ const stats = accessStatsByVariable.get(access.variable) ?? {
225
+ hasCellRead: false,
226
+ hasCellWrite: false,
227
+ hasMutatingCall: false,
228
+ hasIndexedWrite: false
229
+ };
230
+ if (access.kind === "cell-read" || access.kind === "cell-write") {
231
+ variablesWithCellAccess.add(access.variable);
232
+ }
233
+ if (access.kind === "cell-read") stats.hasCellRead = true;
234
+ if (access.kind === "cell-write") stats.hasCellWrite = true;
235
+ if (access.kind === "mutating-call") stats.hasMutatingCall = true;
236
+ if (access.kind === "indexed-write") stats.hasIndexedWrite = true;
237
+ accessStatsByVariable.set(access.variable, stats);
238
+ }
239
+ }
240
+ if (variablesWithCellAccess.size === 0) {
241
+ return trace;
242
+ }
243
+ return trace.map((step) => {
244
+ if (!step.accesses?.length) {
245
+ return step;
246
+ }
247
+ const filtered = step.accesses.filter((access) => {
248
+ const stats = accessStatsByVariable.get(access.variable);
249
+ if (variablesWithCellAccess.has(access.variable)) {
250
+ return access.kind !== "indexed-read" && access.kind !== "indexed-write";
251
+ }
252
+ if (access.kind === "mutating-call" && stats?.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
253
+ return false;
254
+ }
255
+ if (access.kind === "indexed-read" && stats?.hasMutatingCall && !stats.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
256
+ return false;
257
+ }
258
+ return true;
259
+ });
260
+ if (filtered.length === step.accesses.length) {
261
+ return step;
262
+ }
263
+ return {
264
+ ...step,
265
+ ...filtered.length > 0 ? { accesses: filtered } : {},
266
+ ...filtered.length === 0 ? { accesses: void 0 } : {}
267
+ };
268
+ });
269
+ }
187
270
  function normalizeRuntimeTraceContract(language, result) {
188
- const normalizedTrace = Array.isArray(result.trace) ? result.trace.map(normalizeTraceStep) : [];
271
+ const rawNormalizedTrace = collapseTraceAccessNoise(
272
+ Array.isArray(result.trace) ? result.trace.map(normalizeTraceStep) : []
273
+ );
274
+ const maxTraceSteps = typeof result.maxTraceSteps === "number" && Number.isFinite(result.maxTraceSteps) ? Math.max(1, Math.floor(result.maxTraceSteps)) : void 0;
275
+ const traceWasClipped = maxTraceSteps !== void 0 && rawNormalizedTrace.length > maxTraceSteps;
276
+ const normalizedTrace = traceWasClipped && maxTraceSteps !== void 0 ? rawNormalizedTrace.slice(0, maxTraceSteps) : rawNormalizedTrace;
189
277
  const lineEventCount = typeof result.lineEventCount === "number" && Number.isFinite(result.lineEventCount) ? Math.floor(result.lineEventCount) : normalizedTrace.filter((step) => step.event === "line").length;
190
278
  const normalizedConsole = Array.isArray(result.consoleOutput) ? result.consoleOutput.map((line) => String(line)) : [];
191
279
  return {
@@ -197,7 +285,7 @@ function normalizeRuntimeTraceContract(language, result) {
197
285
  ...typeof result.errorLine === "number" && Number.isFinite(result.errorLine) ? { errorLine: Math.floor(result.errorLine) } : {},
198
286
  consoleOutput: normalizedConsole,
199
287
  trace: normalizedTrace,
200
- ...result.traceLimitExceeded !== void 0 ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) } : {},
288
+ ...result.traceLimitExceeded !== void 0 || traceWasClipped ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) || traceWasClipped } : {},
201
289
  ...typeof result.timeoutReason === "string" && result.timeoutReason.length > 0 ? { timeoutReason: result.timeoutReason } : {},
202
290
  lineEventCount: Math.max(0, lineEventCount),
203
291
  traceStepCount: normalizedTrace.length
@@ -256,6 +344,513 @@ function adaptJavaScriptTraceExecutionResult(language, result) {
256
344
  return adaptTraceExecutionResult(language, result);
257
345
  }
258
346
 
347
+ // packages/harness-core/src/trace-adapters/java.ts
348
+ function parseScalar(raw) {
349
+ if (raw === "null") return null;
350
+ if (raw === "true") return true;
351
+ if (raw === "false") return false;
352
+ if (/^-?\d+$/.test(raw)) return Number.parseInt(raw, 10);
353
+ if (/^-?\d+\.\d+$/.test(raw)) return Number.parseFloat(raw);
354
+ if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("[") && raw.endsWith("]") || raw.startsWith("{") && raw.endsWith("}")) {
355
+ try {
356
+ return JSON.parse(raw);
357
+ } catch {
358
+ }
359
+ }
360
+ return raw;
361
+ }
362
+ function parseKeyValuePairs(fragment) {
363
+ const variables = {};
364
+ const matches = Array.from(fragment.matchAll(/\b([A-Za-z_][A-Za-z0-9_.]*)=/g));
365
+ for (let index = 0; index < matches.length; index += 1) {
366
+ const match = matches[index];
367
+ const rawKey = match[1];
368
+ const valueStart = (match.index ?? 0) + match[0].length;
369
+ const valueEnd = index + 1 < matches.length ? matches[index + 1].index ?? fragment.length : fragment.length;
370
+ const rawValue = fragment.slice(valueStart, valueEnd).trim();
371
+ if (!rawKey || rawValue === void 0) continue;
372
+ if (rawKey === "method") continue;
373
+ variables[rawKey.replaceAll(".", "_")] = parseScalar(rawValue);
374
+ }
375
+ return variables;
376
+ }
377
+ function extractLineMetadata(event) {
378
+ const match = event.match(/^line=(\d+)(?:\s+(.*))?$/);
379
+ if (!match) {
380
+ return { line: 1, payload: event };
381
+ }
382
+ return {
383
+ line: Number.parseInt(match[1], 10),
384
+ payload: match[2] ?? ""
385
+ };
386
+ }
387
+ function stripInlineComments(line, inBlockComment) {
388
+ let result = "";
389
+ let index = 0;
390
+ let inBlock = inBlockComment;
391
+ while (index < line.length) {
392
+ const current = line[index];
393
+ const next = index + 1 < line.length ? line[index + 1] : "";
394
+ if (inBlock) {
395
+ if (current === "*" && next === "/") {
396
+ inBlock = false;
397
+ index += 2;
398
+ continue;
399
+ }
400
+ index += 1;
401
+ continue;
402
+ }
403
+ if (current === "/" && next === "*") {
404
+ inBlock = true;
405
+ index += 2;
406
+ continue;
407
+ }
408
+ if (current === "/" && next === "/") {
409
+ break;
410
+ }
411
+ result += current;
412
+ index += 1;
413
+ }
414
+ return { text: result, inBlockComment: inBlock };
415
+ }
416
+ function isMethodDeclarationLine(line) {
417
+ const trimmed = line.trim();
418
+ if (!trimmed) return false;
419
+ if (trimmed.startsWith("@")) return false;
420
+ if (!trimmed.includes("(") || !trimmed.includes(")")) return false;
421
+ if (trimmed.endsWith(";")) return false;
422
+ if (trimmed.includes("->")) return false;
423
+ if (/^(?:if|for|while|switch|catch|do|try|else|return|throw|new)\b/.test(trimmed)) {
424
+ return false;
425
+ }
426
+ if (!/[A-Za-z_][A-Za-z0-9_]*\s*\([^{};]*\)/.test(trimmed)) {
427
+ return false;
428
+ }
429
+ return /(?:\{\s*)?$/.test(trimmed);
430
+ }
431
+ function buildLineRemap(sourceText) {
432
+ if (typeof sourceText !== "string" || sourceText.length === 0) {
433
+ return null;
434
+ }
435
+ const lines = sourceText.split(/\r?\n/);
436
+ const executable = /* @__PURE__ */ new Set();
437
+ let inBlockComment = false;
438
+ for (let index = 0; index < lines.length; index += 1) {
439
+ const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
440
+ inBlockComment = nextInBlockComment;
441
+ if (text.trim().length > 0) {
442
+ executable.add(index + 1);
443
+ }
444
+ }
445
+ if (executable.size === 0) {
446
+ return null;
447
+ }
448
+ const nextExecutableAtOrAfter = new Array(lines.length + 2).fill(null);
449
+ let nextExecutable = null;
450
+ for (let line = lines.length; line >= 1; line -= 1) {
451
+ if (executable.has(line)) {
452
+ nextExecutable = line;
453
+ }
454
+ nextExecutableAtOrAfter[line] = nextExecutable;
455
+ }
456
+ const previousExecutableAtOrBefore = new Array(lines.length + 2).fill(null);
457
+ let previousExecutable = null;
458
+ for (let line = 1; line <= lines.length; line += 1) {
459
+ if (executable.has(line)) {
460
+ previousExecutable = line;
461
+ }
462
+ previousExecutableAtOrBefore[line] = previousExecutable;
463
+ }
464
+ const remap = /* @__PURE__ */ new Map();
465
+ for (let line = 1; line <= lines.length; line += 1) {
466
+ if (executable.has(line)) continue;
467
+ const forward = nextExecutableAtOrAfter[line];
468
+ const backward = previousExecutableAtOrBefore[line];
469
+ const target = forward ?? backward;
470
+ if (target !== null && target !== line) {
471
+ remap.set(line, target);
472
+ }
473
+ }
474
+ return remap;
475
+ }
476
+ function buildMethodDeclarationLineSet(sourceText) {
477
+ if (typeof sourceText !== "string" || sourceText.length === 0) {
478
+ return null;
479
+ }
480
+ const declarationLines = /* @__PURE__ */ new Set();
481
+ const lines = sourceText.split(/\r?\n/);
482
+ let inBlockComment = false;
483
+ for (let index = 0; index < lines.length; index += 1) {
484
+ const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
485
+ inBlockComment = nextInBlockComment;
486
+ if (isMethodDeclarationLine(text)) {
487
+ declarationLines.add(index + 1);
488
+ }
489
+ }
490
+ return declarationLines;
491
+ }
492
+ function parseAccessEvent(payload) {
493
+ const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
494
+ const cellRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
495
+ if (cellRead) {
496
+ return [{
497
+ variable: cellRead[1],
498
+ kind: "cell-read",
499
+ indices: [Number.parseInt(cellRead[2], 10), Number.parseInt(cellRead[3], 10)],
500
+ pathDepth: 2
501
+ }];
502
+ }
503
+ const indexedRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
504
+ if (indexedRead) {
505
+ return [{
506
+ variable: indexedRead[1],
507
+ kind: "indexed-read",
508
+ indices: [Number.parseInt(indexedRead[2], 10)],
509
+ pathDepth: 1
510
+ }];
511
+ }
512
+ const cellWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
513
+ if (cellWrite) {
514
+ return [{
515
+ variable: cellWrite[1],
516
+ kind: "cell-write",
517
+ indices: [Number.parseInt(cellWrite[2], 10), Number.parseInt(cellWrite[3], 10)],
518
+ pathDepth: 2
519
+ }];
520
+ }
521
+ const indexedWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
522
+ if (indexedWrite) {
523
+ if (isEphemeralOutputArrayName(indexedWrite[1])) {
524
+ return void 0;
525
+ }
526
+ return [{
527
+ variable: indexedWrite[1],
528
+ kind: "indexed-write",
529
+ indices: [Number.parseInt(indexedWrite[2], 10)],
530
+ pathDepth: 1
531
+ }];
532
+ }
533
+ const mutatingCall = payload.match(/^mutate ([A-Za-z_][A-Za-z0-9_]*) method=([A-Za-z_][A-Za-z0-9_]*)$/);
534
+ if (mutatingCall) {
535
+ return [{
536
+ variable: mutatingCall[1],
537
+ kind: "mutating-call",
538
+ method: mutatingCall[2],
539
+ pathDepth: 1
540
+ }];
541
+ }
542
+ return void 0;
543
+ }
544
+ function parseStructureState(payload) {
545
+ const match = payload.match(/^state (linked-list|tree) ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
546
+ if (!match) return null;
547
+ return {
548
+ structure: match[1],
549
+ variable: match[2],
550
+ value: JSON.parse(match[3])
551
+ };
552
+ }
553
+ function parseObjectState(payload) {
554
+ const match = payload.match(/^object-state ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
555
+ if (!match) return null;
556
+ return {
557
+ variable: match[1],
558
+ visualization: JSON.parse(match[2])
559
+ };
560
+ }
561
+ function parseObjectFieldEvent(payload) {
562
+ const match = payload.match(/^(access|write) ([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
563
+ if (!match) return null;
564
+ return {
565
+ variable: match[2],
566
+ field: match[3],
567
+ value: parseScalar(match[4])
568
+ };
569
+ }
570
+ function buildFieldVisualization(event) {
571
+ return {
572
+ objectKinds: {
573
+ [event.variable]: "object"
574
+ },
575
+ hashMaps: [
576
+ {
577
+ name: event.variable,
578
+ kind: "object",
579
+ objectClassName: event.field === "next" || event.field === "prev" ? "ListNode" : event.field === "left" || event.field === "right" ? "TreeNode" : void 0,
580
+ objectId: `${event.variable}-object`,
581
+ highlightedKey: event.field,
582
+ entries: [{ key: event.field, value: event.value, highlight: true }]
583
+ }
584
+ ]
585
+ };
586
+ }
587
+ function callStacksEqual(left, right) {
588
+ return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
589
+ }
590
+ function mergeVisualizationPayloads(left, right) {
591
+ if (!left) return right;
592
+ if (!right) return left;
593
+ return {
594
+ ...left,
595
+ ...right,
596
+ objectKinds: {
597
+ ...left.objectKinds ?? {},
598
+ ...right.objectKinds ?? {}
599
+ },
600
+ hashMaps: [
601
+ ...left.hashMaps ?? [],
602
+ ...right.hashMaps ?? []
603
+ ]
604
+ };
605
+ }
606
+ function maybeMergeConsecutiveLineStep(trace, nextStep) {
607
+ if (nextStep.event !== "line") {
608
+ return false;
609
+ }
610
+ const previous = trace.at(-1);
611
+ if (!previous || previous.event !== "line") {
612
+ return false;
613
+ }
614
+ if (previous.line !== nextStep.line || previous.function !== nextStep.function) {
615
+ return false;
616
+ }
617
+ if (!callStacksEqual(previous.callStack, nextStep.callStack)) {
618
+ return false;
619
+ }
620
+ previous.variables = { ...previous.variables ?? {}, ...nextStep.variables ?? {} };
621
+ if (nextStep.accesses?.length) {
622
+ previous.accesses = [...previous.accesses ?? [], ...nextStep.accesses];
623
+ }
624
+ previous.visualization = mergeVisualizationPayloads(previous.visualization, nextStep.visualization);
625
+ return true;
626
+ }
627
+ function filterStructuredVariables(variables) {
628
+ if (!variables) {
629
+ return void 0;
630
+ }
631
+ const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
632
+ const entries = Object.entries(variables).filter(([name, value]) => {
633
+ if (value === null || typeof value !== "object") {
634
+ return false;
635
+ }
636
+ if (!Array.isArray(value)) {
637
+ return true;
638
+ }
639
+ if (Array.isArray(value[0])) {
640
+ return true;
641
+ }
642
+ return !isEphemeralOutputArrayName(name);
643
+ });
644
+ if (entries.length === 0) {
645
+ return void 0;
646
+ }
647
+ return Object.fromEntries(entries);
648
+ }
649
+ function appendJavaTraceStep(trace, step, pendingAccesses) {
650
+ const nextStep = pendingAccesses.length > 0 ? { ...step, accesses: [...pendingAccesses] } : step;
651
+ pendingAccesses.length = 0;
652
+ if (!maybeMergeConsecutiveLineStep(trace, nextStep)) {
653
+ trace.push(nextStep);
654
+ }
655
+ }
656
+ function mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, patch) {
657
+ const candidate = trace.at(-1);
658
+ if (!candidate || candidate.event !== "line") {
659
+ return false;
660
+ }
661
+ if (candidate.line !== line || candidate.function !== currentFunction) {
662
+ return false;
663
+ }
664
+ if (!callStacksEqual(candidate.callStack, currentCallStack)) {
665
+ return false;
666
+ }
667
+ candidate.variables = { ...candidate.variables ?? {}, ...patch.variables ?? {} };
668
+ if (patch.accesses?.length) {
669
+ candidate.accesses = [...candidate.accesses ?? [], ...patch.accesses];
670
+ }
671
+ candidate.visualization = mergeVisualizationPayloads(candidate.visualization, patch.visualization);
672
+ return true;
673
+ }
674
+ function mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses) {
675
+ return mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
676
+ variables: void 0,
677
+ accesses,
678
+ visualization: void 0
679
+ });
680
+ }
681
+ function eventsToRawTrace(events, sourceText) {
682
+ const trace = [];
683
+ const variables = {};
684
+ const objectKinds = {};
685
+ const stack = [];
686
+ const pendingAccesses = [];
687
+ let currentFunction = "<module>";
688
+ const lineRemap = buildLineRemap(sourceText);
689
+ const declarationLines = buildMethodDeclarationLineSet(sourceText);
690
+ for (const rawEvent of events) {
691
+ if (rawEvent === "clear" || rawEvent === "reset") continue;
692
+ const metadata = extractLineMetadata(rawEvent);
693
+ const line = lineRemap?.get(metadata.line) ?? metadata.line;
694
+ const payload = metadata.payload;
695
+ const isDeclarationLine = declarationLines?.has(line) === true;
696
+ if (payload.startsWith("call ")) {
697
+ const match = payload.match(/^call\s+(\S+)(?:\s+(.*))?$/);
698
+ const functionName = match?.[1] ?? currentFunction;
699
+ const argsFragment = match?.[2] ?? "";
700
+ Object.assign(variables, parseKeyValuePairs(argsFragment));
701
+ currentFunction = functionName || currentFunction;
702
+ stack.push({ function: currentFunction, line });
703
+ appendJavaTraceStep(trace, {
704
+ line,
705
+ event: "call",
706
+ function: currentFunction,
707
+ variables: { ...variables },
708
+ callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
709
+ }, pendingAccesses);
710
+ continue;
711
+ }
712
+ if (payload.startsWith("return ")) {
713
+ const match = payload.match(/^return\s+(\S+)$/);
714
+ const functionName = match?.[1] ?? currentFunction;
715
+ const returnVariables = functionName === "<module>" ? { ...variables } : filterStructuredVariables(variables) ?? {};
716
+ appendJavaTraceStep(trace, {
717
+ line,
718
+ event: "return",
719
+ function: functionName || currentFunction,
720
+ variables: returnVariables,
721
+ callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
722
+ }, pendingAccesses);
723
+ stack.pop();
724
+ currentFunction = stack[stack.length - 1]?.function ?? "<module>";
725
+ continue;
726
+ }
727
+ if (isDeclarationLine) {
728
+ continue;
729
+ }
730
+ if (payload.length === 0) {
731
+ appendJavaTraceStep(trace, {
732
+ line,
733
+ event: "line",
734
+ function: currentFunction,
735
+ variables: { ...variables },
736
+ ...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
737
+ }, pendingAccesses);
738
+ continue;
739
+ }
740
+ const structureState = parseStructureState(payload);
741
+ if (structureState) {
742
+ variables[structureState.variable] = structureState.value;
743
+ objectKinds[structureState.variable] = structureState.structure;
744
+ appendJavaTraceStep(trace, {
745
+ line,
746
+ event: "line",
747
+ function: currentFunction,
748
+ variables: { ...variables },
749
+ callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
750
+ visualization: {
751
+ objectKinds: { ...objectKinds }
752
+ }
753
+ }, pendingAccesses);
754
+ continue;
755
+ }
756
+ const objectState = parseObjectState(payload);
757
+ if (objectState) {
758
+ variables[objectState.variable] = { __ref__: objectState.visualization.objectId ?? `${objectState.variable}-object` };
759
+ appendJavaTraceStep(trace, {
760
+ line,
761
+ event: "line",
762
+ function: currentFunction,
763
+ variables: { ...variables },
764
+ callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
765
+ visualization: {
766
+ objectKinds: { [objectState.variable]: "object" },
767
+ hashMaps: [objectState.visualization]
768
+ }
769
+ }, pendingAccesses);
770
+ continue;
771
+ }
772
+ const objectField = parseObjectFieldEvent(payload);
773
+ if (objectField) {
774
+ variables[objectField.variable] = { __ref__: `${objectField.variable}-object` };
775
+ const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
776
+ if (mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
777
+ variables: { ...variables },
778
+ accesses: void 0,
779
+ visualization: buildFieldVisualization(objectField)
780
+ })) {
781
+ continue;
782
+ }
783
+ appendJavaTraceStep(trace, {
784
+ line,
785
+ event: "line",
786
+ function: currentFunction,
787
+ variables: { ...variables },
788
+ callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
789
+ visualization: buildFieldVisualization(objectField)
790
+ }, pendingAccesses);
791
+ continue;
792
+ }
793
+ Object.assign(variables, parseKeyValuePairs(payload));
794
+ const accesses = parseAccessEvent(payload);
795
+ if (accesses) {
796
+ const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
797
+ if (mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses)) {
798
+ continue;
799
+ }
800
+ pendingAccesses.push(...accesses);
801
+ continue;
802
+ }
803
+ appendJavaTraceStep(trace, {
804
+ line,
805
+ event: "line",
806
+ function: currentFunction,
807
+ variables: { ...variables },
808
+ ...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
809
+ }, pendingAccesses);
810
+ }
811
+ if (pendingAccesses.length > 0 && trace.length > 0) {
812
+ const last = trace[trace.length - 1];
813
+ last.accesses = [...last.accesses ?? [], ...pendingAccesses];
814
+ }
815
+ return trace;
816
+ }
817
+ function buildJavaExecutionResult(output, events, executionTimeMs = 0, traceLimitExceeded, timeoutReason, maxTraceSteps, sourceText) {
818
+ const trace = eventsToRawTrace(events, sourceText);
819
+ return {
820
+ success: true,
821
+ output,
822
+ trace,
823
+ executionTimeMs,
824
+ consoleOutput: [],
825
+ ...traceLimitExceeded !== void 0 ? { traceLimitExceeded } : {},
826
+ ...maxTraceSteps !== void 0 ? { maxTraceSteps } : {},
827
+ ...timeoutReason ? { timeoutReason } : {},
828
+ lineEventCount: trace.filter((step) => step.event === "line").length,
829
+ traceStepCount: trace.length
830
+ };
831
+ }
832
+ function normalizeJavaTraceContract(result) {
833
+ const normalized = normalizeRuntimeTraceContract(
834
+ "java",
835
+ buildJavaExecutionResult(
836
+ result.output,
837
+ result.events,
838
+ 0,
839
+ result.traceLimitExceeded,
840
+ result.timeoutReason,
841
+ void 0,
842
+ result.sourceText
843
+ )
844
+ );
845
+ return {
846
+ ...normalized,
847
+ language: "java"
848
+ };
849
+ }
850
+ function adaptJavaTraceExecutionResult(result) {
851
+ return adaptTraceExecutionResult("java", result);
852
+ }
853
+
259
854
  // packages/harness-browser/src/javascript-worker-client.ts
260
855
  var EXECUTION_TIMEOUT_MS = 7e3;
261
856
  var INTERVIEW_MODE_TIMEOUT_MS = 5e3;
@@ -582,6 +1177,7 @@ var PYTHON_RUNTIME_PROFILE = {
582
1177
  maxTraceSteps: true,
583
1178
  maxLineEvents: true,
584
1179
  maxSingleLineHits: true,
1180
+ maxStoredEvents: true,
585
1181
  minimalTrace: true
586
1182
  },
587
1183
  fidelity: {
@@ -643,6 +1239,7 @@ var JAVASCRIPT_RUNTIME_PROFILE = {
643
1239
  maxTraceSteps: true,
644
1240
  maxLineEvents: true,
645
1241
  maxSingleLineHits: true,
1242
+ maxStoredEvents: true,
646
1243
  minimalTrace: true
647
1244
  },
648
1245
  fidelity: {
@@ -704,6 +1301,7 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
704
1301
  maxTraceSteps: true,
705
1302
  maxLineEvents: true,
706
1303
  maxSingleLineHits: true,
1304
+ maxStoredEvents: true,
707
1305
  minimalTrace: true
708
1306
  },
709
1307
  fidelity: {
@@ -734,10 +1332,78 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
734
1332
  }
735
1333
  }
736
1334
  };
1335
+ var JAVA_RUNTIME_PROFILE = {
1336
+ language: "java",
1337
+ maturity: "experimental",
1338
+ capabilities: {
1339
+ execution: {
1340
+ styles: {
1341
+ function: true,
1342
+ solutionMethod: true,
1343
+ opsClass: true,
1344
+ script: false,
1345
+ interviewMode: true
1346
+ },
1347
+ timeouts: {
1348
+ clientTimeouts: true,
1349
+ runtimeTimeouts: true
1350
+ }
1351
+ },
1352
+ tracing: {
1353
+ supported: true,
1354
+ events: {
1355
+ line: true,
1356
+ call: true,
1357
+ return: true,
1358
+ exception: true,
1359
+ stdout: false,
1360
+ timeout: true
1361
+ },
1362
+ controls: {
1363
+ maxTraceSteps: true,
1364
+ maxLineEvents: false,
1365
+ maxSingleLineHits: false,
1366
+ maxStoredEvents: true,
1367
+ minimalTrace: false
1368
+ },
1369
+ fidelity: {
1370
+ preciseLineMapping: true,
1371
+ stableFunctionNames: true,
1372
+ callStack: true
1373
+ }
1374
+ },
1375
+ diagnostics: {
1376
+ compileErrors: true,
1377
+ runtimeErrors: true,
1378
+ mappedErrorLines: false,
1379
+ stackTraces: true
1380
+ },
1381
+ structures: {
1382
+ treeNodeRefs: true,
1383
+ listNodeRefs: true,
1384
+ mapSerialization: true,
1385
+ setSerialization: true,
1386
+ graphSerialization: false,
1387
+ cycleReferences: true
1388
+ },
1389
+ visualization: {
1390
+ runtimePayloads: true,
1391
+ objectKinds: true,
1392
+ hashMaps: true,
1393
+ stepVisualization: true
1394
+ }
1395
+ },
1396
+ notes: [
1397
+ "Java currently supports the browser-local Java 17 lane for function, solution-method, and ops-class execution.",
1398
+ "Interview-mode Java reuses the same browser-local execution path and remains experimental in the first harness slice.",
1399
+ "Script-style Java is not enabled yet."
1400
+ ]
1401
+ };
737
1402
  var LANGUAGE_RUNTIME_PROFILES = {
738
1403
  python: PYTHON_RUNTIME_PROFILE,
739
1404
  javascript: JAVASCRIPT_RUNTIME_PROFILE,
740
- typescript: TYPESCRIPT_RUNTIME_PROFILE
1405
+ typescript: TYPESCRIPT_RUNTIME_PROFILE,
1406
+ java: JAVA_RUNTIME_PROFILE
741
1407
  };
742
1408
  var SUPPORTED_LANGUAGES = Object.freeze(
743
1409
  Object.keys(LANGUAGE_RUNTIME_PROFILES)
@@ -814,13 +1480,307 @@ function createJavaScriptRuntimeClient(runtimeLanguage, workerClient) {
814
1480
  return new JavaScriptRuntimeClient(runtimeLanguage, workerClient);
815
1481
  }
816
1482
 
1483
+ // packages/harness-browser/src/java-worker-client.ts
1484
+ var TRACING_TIMEOUT_MS2 = 25e3;
1485
+ var INIT_TIMEOUT_MS2 = 25e3;
1486
+ var MESSAGE_TIMEOUT_MS2 = 3e4;
1487
+ var WORKER_READY_TIMEOUT_MS2 = 1e4;
1488
+ var JavaWorkerClient = class {
1489
+ constructor(options) {
1490
+ this.options = options;
1491
+ this.debug = options.debug ?? process.env.NODE_ENV === "development";
1492
+ }
1493
+ worker = null;
1494
+ pendingMessages = /* @__PURE__ */ new Map();
1495
+ messageId = 0;
1496
+ isInitializing = false;
1497
+ initPromise = null;
1498
+ workerReadyPromise = null;
1499
+ workerReadyResolve = null;
1500
+ workerReadyReject = null;
1501
+ debug;
1502
+ isSupported() {
1503
+ return typeof Worker !== "undefined";
1504
+ }
1505
+ getWorker() {
1506
+ if (this.worker) return this.worker;
1507
+ if (!this.isSupported()) {
1508
+ throw new Error("Web Workers are not supported in this environment");
1509
+ }
1510
+ this.workerReadyPromise = new Promise((resolve, reject) => {
1511
+ this.workerReadyResolve = resolve;
1512
+ this.workerReadyReject = (error) => reject(error);
1513
+ });
1514
+ const workerUrl = this.debug && !this.options.workerUrl.includes("?") ? `${this.options.workerUrl}?dev=${Date.now()}` : this.options.workerUrl;
1515
+ this.worker = new Worker(workerUrl);
1516
+ this.worker.onmessage = (event) => {
1517
+ const { id, type, payload } = event.data;
1518
+ if (type === "worker-ready") {
1519
+ this.workerReadyResolve?.();
1520
+ this.workerReadyResolve = null;
1521
+ this.workerReadyReject = null;
1522
+ return;
1523
+ }
1524
+ if (!id) return;
1525
+ const pending = this.pendingMessages.get(id);
1526
+ if (!pending) return;
1527
+ this.pendingMessages.delete(id);
1528
+ if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
1529
+ if (type === "error") {
1530
+ pending.reject(new Error(payload.error));
1531
+ return;
1532
+ }
1533
+ pending.resolve(payload);
1534
+ };
1535
+ this.worker.onerror = (error) => {
1536
+ const workerError = new Error(error.message || "Java worker error");
1537
+ this.workerReadyReject?.(workerError);
1538
+ this.workerReadyResolve = null;
1539
+ this.workerReadyReject = null;
1540
+ for (const [, pending] of this.pendingMessages) {
1541
+ if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
1542
+ pending.reject(workerError);
1543
+ }
1544
+ this.pendingMessages.clear();
1545
+ this.terminateAndReset(workerError);
1546
+ };
1547
+ return this.worker;
1548
+ }
1549
+ async waitForWorkerReady() {
1550
+ const readyPromise = this.workerReadyPromise;
1551
+ if (!readyPromise) return;
1552
+ await new Promise((resolve, reject) => {
1553
+ let settled = false;
1554
+ const timeoutId = globalThis.setTimeout(() => {
1555
+ if (settled) return;
1556
+ settled = true;
1557
+ const timeoutError = new Error(
1558
+ `Java worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS2 / 1e3)}s)`
1559
+ );
1560
+ this.terminateAndReset(timeoutError);
1561
+ reject(timeoutError);
1562
+ }, WORKER_READY_TIMEOUT_MS2);
1563
+ readyPromise.then(() => {
1564
+ if (settled) return;
1565
+ settled = true;
1566
+ globalThis.clearTimeout(timeoutId);
1567
+ resolve();
1568
+ }).catch((error) => {
1569
+ if (settled) return;
1570
+ settled = true;
1571
+ globalThis.clearTimeout(timeoutId);
1572
+ reject(error instanceof Error ? error : new Error(String(error)));
1573
+ });
1574
+ });
1575
+ }
1576
+ async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS2) {
1577
+ const worker = this.getWorker();
1578
+ await this.waitForWorkerReady();
1579
+ const id = String(++this.messageId);
1580
+ return new Promise((resolve, reject) => {
1581
+ this.pendingMessages.set(id, {
1582
+ resolve,
1583
+ reject
1584
+ });
1585
+ const timeoutId = globalThis.setTimeout(() => {
1586
+ const pending2 = this.pendingMessages.get(id);
1587
+ if (!pending2) return;
1588
+ this.pendingMessages.delete(id);
1589
+ pending2.reject(new Error(`Worker request timed out: ${type}`));
1590
+ }, timeoutMs);
1591
+ const pending = this.pendingMessages.get(id);
1592
+ if (pending) pending.timeoutId = timeoutId;
1593
+ worker.postMessage({ id, type, payload });
1594
+ });
1595
+ }
1596
+ async executeWithTimeout(executor, timeoutMs) {
1597
+ return new Promise((resolve, reject) => {
1598
+ let settled = false;
1599
+ const timeoutId = globalThis.setTimeout(() => {
1600
+ if (settled) return;
1601
+ settled = true;
1602
+ this.terminateAndReset();
1603
+ reject(
1604
+ new Error(
1605
+ `Java execution timed out after ${Math.round(timeoutMs / 1e3)} seconds.`
1606
+ )
1607
+ );
1608
+ }, timeoutMs);
1609
+ executor().then((result) => {
1610
+ if (settled) return;
1611
+ settled = true;
1612
+ globalThis.clearTimeout(timeoutId);
1613
+ resolve(result);
1614
+ }).catch((error) => {
1615
+ if (settled) return;
1616
+ settled = true;
1617
+ globalThis.clearTimeout(timeoutId);
1618
+ reject(error);
1619
+ });
1620
+ });
1621
+ }
1622
+ terminateAndReset(reason = new Error("Worker was terminated")) {
1623
+ this.workerReadyReject?.(reason);
1624
+ if (this.worker) {
1625
+ this.worker.terminate();
1626
+ this.worker = null;
1627
+ }
1628
+ this.initPromise = null;
1629
+ this.isInitializing = false;
1630
+ this.workerReadyPromise = null;
1631
+ this.workerReadyResolve = null;
1632
+ this.workerReadyReject = null;
1633
+ for (const [, pending] of this.pendingMessages) {
1634
+ if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
1635
+ pending.reject(reason);
1636
+ }
1637
+ this.pendingMessages.clear();
1638
+ }
1639
+ async init() {
1640
+ if (this.initPromise) return this.initPromise;
1641
+ if (this.isInitializing) {
1642
+ await new Promise((resolve) => globalThis.setTimeout(resolve, 100));
1643
+ return this.init();
1644
+ }
1645
+ this.isInitializing = true;
1646
+ this.initPromise = this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
1647
+ try {
1648
+ return await this.initPromise;
1649
+ } catch (error) {
1650
+ this.initPromise = null;
1651
+ throw error;
1652
+ } finally {
1653
+ this.isInitializing = false;
1654
+ }
1655
+ }
1656
+ async executeWithTracing(code, functionName, inputs, options, executionStyle) {
1657
+ await this.init();
1658
+ return this.executeWithTimeout(
1659
+ () => this.sendMessage(
1660
+ "execute-with-tracing",
1661
+ { code, functionName, inputs, options, executionStyle },
1662
+ TRACING_TIMEOUT_MS2 + 5e3
1663
+ ),
1664
+ TRACING_TIMEOUT_MS2
1665
+ );
1666
+ }
1667
+ async executeCode(code, functionName, inputs, options, executionStyle) {
1668
+ const result = await this.executeWithTracing(code, functionName, inputs, options, executionStyle);
1669
+ if (!result.success) {
1670
+ return {
1671
+ success: false,
1672
+ output: null,
1673
+ error: result.error ?? "Java execution failed",
1674
+ ...result.errorLine !== void 0 ? { errorLine: result.errorLine } : {},
1675
+ consoleOutput: result.consoleOutput
1676
+ };
1677
+ }
1678
+ return {
1679
+ success: true,
1680
+ output: result.output,
1681
+ consoleOutput: result.consoleOutput
1682
+ };
1683
+ }
1684
+ async executeCodeInterviewMode(code, functionName, inputs, options, executionStyle) {
1685
+ return this.executeCode(code, functionName, inputs, options, executionStyle);
1686
+ }
1687
+ terminate() {
1688
+ this.terminateAndReset();
1689
+ }
1690
+ };
1691
+
1692
+ // packages/harness-browser/src/java-runtime-client.ts
1693
+ var JavaRuntimeClient = class {
1694
+ constructor(workerClient) {
1695
+ this.workerClient = workerClient;
1696
+ }
1697
+ async init() {
1698
+ return this.workerClient.init();
1699
+ }
1700
+ async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
1701
+ assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
1702
+ request: "trace",
1703
+ executionStyle,
1704
+ functionName
1705
+ });
1706
+ const rawResult = await this.workerClient.executeWithTracing(
1707
+ code,
1708
+ functionName ?? "",
1709
+ inputs,
1710
+ options,
1711
+ executionStyle
1712
+ );
1713
+ if (!rawResult.success) {
1714
+ return {
1715
+ success: false,
1716
+ error: rawResult.error ?? "Java tracing failed",
1717
+ ...rawResult.errorLine !== void 0 ? { errorLine: rawResult.errorLine } : {},
1718
+ trace: [],
1719
+ executionTimeMs: rawResult.executionTimeMs,
1720
+ consoleOutput: rawResult.consoleOutput,
1721
+ ...rawResult.traceLimitExceeded !== void 0 ? { traceLimitExceeded: rawResult.traceLimitExceeded } : {},
1722
+ ...rawResult.timeoutReason ? { timeoutReason: rawResult.timeoutReason } : {},
1723
+ lineEventCount: 0,
1724
+ traceStepCount: 0
1725
+ };
1726
+ }
1727
+ const adapted = adaptJavaTraceExecutionResult(
1728
+ buildJavaExecutionResult(
1729
+ rawResult.output,
1730
+ rawResult.events,
1731
+ rawResult.executionTimeMs,
1732
+ rawResult.traceLimitExceeded,
1733
+ rawResult.timeoutReason,
1734
+ void 0,
1735
+ rawResult.sourceText
1736
+ )
1737
+ );
1738
+ return {
1739
+ ...adapted,
1740
+ consoleOutput: rawResult.consoleOutput,
1741
+ executionTimeMs: rawResult.executionTimeMs
1742
+ };
1743
+ }
1744
+ async executeCode(code, functionName, inputs, executionStyle = "function") {
1745
+ assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
1746
+ request: "execute",
1747
+ executionStyle,
1748
+ functionName
1749
+ });
1750
+ return this.workerClient.executeCode(
1751
+ code,
1752
+ functionName,
1753
+ inputs,
1754
+ void 0,
1755
+ executionStyle
1756
+ );
1757
+ }
1758
+ async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
1759
+ assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
1760
+ request: "interview",
1761
+ executionStyle,
1762
+ functionName
1763
+ });
1764
+ return this.workerClient.executeCodeInterviewMode(
1765
+ code,
1766
+ functionName,
1767
+ inputs,
1768
+ void 0,
1769
+ executionStyle
1770
+ );
1771
+ }
1772
+ };
1773
+ function createJavaRuntimeClient(workerClient) {
1774
+ return new JavaRuntimeClient(workerClient);
1775
+ }
1776
+
817
1777
  // packages/harness-browser/src/pyodide-worker-client.ts
818
1778
  var EXECUTION_TIMEOUT_MS2 = 1e4;
819
1779
  var INTERVIEW_MODE_TIMEOUT_MS2 = 5e3;
820
- var TRACING_TIMEOUT_MS2 = 3e4;
821
- var INIT_TIMEOUT_MS2 = 12e4;
822
- var MESSAGE_TIMEOUT_MS2 = 2e4;
823
- var WORKER_READY_TIMEOUT_MS2 = 1e4;
1780
+ var TRACING_TIMEOUT_MS3 = 3e4;
1781
+ var INIT_TIMEOUT_MS3 = 12e4;
1782
+ var MESSAGE_TIMEOUT_MS3 = 2e4;
1783
+ var WORKER_READY_TIMEOUT_MS3 = 1e4;
824
1784
  var PyodideWorkerClient = class {
825
1785
  constructor(options) {
826
1786
  this.options = options;
@@ -910,14 +1870,14 @@ var PyodideWorkerClient = class {
910
1870
  if (settled) return;
911
1871
  settled = true;
912
1872
  const timeoutError = new Error(
913
- `Python worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS2 / 1e3)}s)`
1873
+ `Python worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS3 / 1e3)}s)`
914
1874
  );
915
1875
  if (this.debug) {
916
- console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs: WORKER_READY_TIMEOUT_MS2 });
1876
+ console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs: WORKER_READY_TIMEOUT_MS3 });
917
1877
  }
918
1878
  this.terminateAndReset(timeoutError);
919
1879
  reject(timeoutError);
920
- }, WORKER_READY_TIMEOUT_MS2);
1880
+ }, WORKER_READY_TIMEOUT_MS3);
921
1881
  readyPromise.then(() => {
922
1882
  if (settled) return;
923
1883
  settled = true;
@@ -934,7 +1894,7 @@ var PyodideWorkerClient = class {
934
1894
  /**
935
1895
  * Send a message to the worker and wait for a response
936
1896
  */
937
- async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS2) {
1897
+ async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS3) {
938
1898
  const worker = this.getWorker();
939
1899
  await this.waitForWorkerReady();
940
1900
  const id = String(++this.messageId);
@@ -1018,7 +1978,7 @@ var PyodideWorkerClient = class {
1018
1978
  this.isInitializing = true;
1019
1979
  this.initPromise = (async () => {
1020
1980
  try {
1021
- return await this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
1981
+ return await this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
1022
1982
  } catch (error) {
1023
1983
  const message = error instanceof Error ? error.message : String(error);
1024
1984
  const shouldRetry = message.includes("Worker request timed out: init") || message.includes("Worker was terminated") || message.includes("Worker error") || message.includes("failed to initialize in time");
@@ -1029,7 +1989,7 @@ var PyodideWorkerClient = class {
1029
1989
  console.warn("[PyodideWorkerClient] init failed, resetting worker and retrying once", { message });
1030
1990
  }
1031
1991
  this.terminateAndReset();
1032
- return this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
1992
+ return this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
1033
1993
  }
1034
1994
  })();
1035
1995
  try {
@@ -1056,9 +2016,9 @@ var PyodideWorkerClient = class {
1056
2016
  inputs,
1057
2017
  executionStyle,
1058
2018
  options
1059
- }, TRACING_TIMEOUT_MS2 + 5e3),
2019
+ }, TRACING_TIMEOUT_MS3 + 5e3),
1060
2020
  // Message timeout slightly longer than execution timeout
1061
- TRACING_TIMEOUT_MS2
2021
+ TRACING_TIMEOUT_MS3
1062
2022
  );
1063
2023
  } catch (error) {
1064
2024
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -1068,7 +2028,7 @@ var PyodideWorkerClient = class {
1068
2028
  success: false,
1069
2029
  error: errorMessage,
1070
2030
  trace: [],
1071
- executionTimeMs: TRACING_TIMEOUT_MS2,
2031
+ executionTimeMs: TRACING_TIMEOUT_MS3,
1072
2032
  consoleOutput: [],
1073
2033
  traceLimitExceeded: true,
1074
2034
  timeoutReason: "client-timeout",
@@ -1224,6 +2184,7 @@ var DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS = Object.freeze({
1224
2184
  pythonRuntimeCore: "pyodide/runtime-core.js",
1225
2185
  pythonSnippets: "generated-python-harness-snippets.js",
1226
2186
  javascriptWorker: "javascript-worker.js",
2187
+ javaWorker: "java-worker.js",
1227
2188
  typescriptCompiler: "vendor/typescript.js"
1228
2189
  });
1229
2190
  function isExplicitAssetPath(pathname) {
@@ -1257,6 +2218,7 @@ function resolveBrowserHarnessAssets(options = {}) {
1257
2218
  assetBaseUrl,
1258
2219
  assets.javascriptWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javascriptWorker
1259
2220
  ),
2221
+ javaWorker: resolveAssetPath(assetBaseUrl, assets.javaWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javaWorker),
1260
2222
  typescriptCompiler: resolveAssetPath(
1261
2223
  assetBaseUrl,
1262
2224
  assets.typescriptCompiler ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.typescriptCompiler
@@ -1270,6 +2232,7 @@ var BrowserHarnessRuntime = class {
1270
2232
  supportedLanguages = SUPPORTED_LANGUAGES;
1271
2233
  pythonWorkerClient;
1272
2234
  javaScriptWorkerClient;
2235
+ javaWorkerClient;
1273
2236
  clients;
1274
2237
  constructor(options = {}) {
1275
2238
  this.assets = resolveBrowserHarnessAssets(options);
@@ -1281,10 +2244,15 @@ var BrowserHarnessRuntime = class {
1281
2244
  workerUrl: this.assets.javascriptWorker,
1282
2245
  debug: options.debug
1283
2246
  });
2247
+ this.javaWorkerClient = new JavaWorkerClient({
2248
+ workerUrl: this.assets.javaWorker,
2249
+ debug: options.debug
2250
+ });
1284
2251
  this.clients = {
1285
2252
  python: createPythonRuntimeClient(this.pythonWorkerClient),
1286
2253
  javascript: createJavaScriptRuntimeClient("javascript", this.javaScriptWorkerClient),
1287
- typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient)
2254
+ typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient),
2255
+ java: createJavaRuntimeClient(this.javaWorkerClient)
1288
2256
  };
1289
2257
  }
1290
2258
  getClient(language) {
@@ -1308,11 +2276,16 @@ var BrowserHarnessRuntime = class {
1308
2276
  this.pythonWorkerClient.terminate();
1309
2277
  return;
1310
2278
  }
2279
+ if (language === "java") {
2280
+ this.javaWorkerClient.terminate();
2281
+ return;
2282
+ }
1311
2283
  this.javaScriptWorkerClient.terminate();
1312
2284
  }
1313
2285
  dispose() {
1314
2286
  this.pythonWorkerClient.terminate();
1315
2287
  this.javaScriptWorkerClient.terminate();
2288
+ this.javaWorkerClient.terminate();
1316
2289
  }
1317
2290
  };
1318
2291
  function createBrowserHarness(options = {}) {
@@ -1415,7 +2388,7 @@ def _serialize(obj, depth=0, node_refs=None):
1415
2388
  except TypeError:
1416
2389
  sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]
1417
2390
  return {"__type__": "set", "values": sorted_vals}
1418
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
2391
+ elif isinstance(obj, TreeNode):
1419
2392
  obj_ref = id(obj)
1420
2393
  if obj_ref in node_refs:
1421
2394
  return {"__ref__": node_refs[obj_ref]}
@@ -1431,7 +2404,7 @@ def _serialize(obj, depth=0, node_refs=None):
1431
2404
  if hasattr(obj, 'right'):
1432
2405
  result["right"] = _serialize(obj.right, depth + 1, node_refs)
1433
2406
  return result
1434
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
2407
+ elif isinstance(obj, ListNode):
1435
2408
  obj_ref = id(obj)
1436
2409
  if obj_ref in node_refs:
1437
2410
  return {"__ref__": node_refs[obj_ref]}
@@ -1509,14 +2482,14 @@ def _serialize(obj, depth=0):
1509
2482
  return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
1510
2483
  except TypeError:
1511
2484
  return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
1512
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
2485
+ elif isinstance(obj, TreeNode):
1513
2486
  result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
1514
2487
  if hasattr(obj, 'left'):
1515
2488
  result["left"] = _serialize(obj.left, depth + 1)
1516
2489
  if hasattr(obj, 'right'):
1517
2490
  result["right"] = _serialize(obj.right, depth + 1)
1518
2491
  return result
1519
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
2492
+ elif isinstance(obj, ListNode):
1520
2493
  result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
1521
2494
  result["next"] = _serialize(obj.next, depth + 1)
1522
2495
  return result
@@ -1591,14 +2564,14 @@ def _serialize(obj, depth=0):
1591
2564
  return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
1592
2565
  except TypeError:
1593
2566
  return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
1594
- elif hasattr(obj, 'val') and (hasattr(obj, 'left') or hasattr(obj, 'right')):
2567
+ elif isinstance(obj, TreeNode):
1595
2568
  result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
1596
2569
  if hasattr(obj, 'left'):
1597
2570
  result["left"] = _serialize(obj.left, depth + 1)
1598
2571
  if hasattr(obj, 'right'):
1599
2572
  result["right"] = _serialize(obj.right, depth + 1)
1600
2573
  return result
1601
- elif hasattr(obj, 'val') and hasattr(obj, 'next'):
2574
+ elif isinstance(obj, ListNode):
1602
2575
  result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
1603
2576
  result["next"] = _serialize(obj.next, depth + 1)
1604
2577
  return result
@@ -1630,14 +2603,14 @@ def _serialize(obj, depth=0):
1630
2603
  return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
1631
2604
  except TypeError:
1632
2605
  return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
1633
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
2606
+ elif isinstance(obj, TreeNode):
1634
2607
  result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
1635
2608
  if hasattr(obj, 'left'):
1636
2609
  result["left"] = _serialize(obj.left, depth + 1)
1637
2610
  if hasattr(obj, 'right'):
1638
2611
  result["right"] = _serialize(obj.right, depth + 1)
1639
2612
  return result
1640
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
2613
+ elif isinstance(obj, ListNode):
1641
2614
  result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
1642
2615
  result["next"] = _serialize(obj.next, depth + 1)
1643
2616
  return result
@@ -1865,7 +2838,7 @@ def _serialize(obj, depth=0, node_refs=None):
1865
2838
  except TypeError:
1866
2839
  sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]
1867
2840
  return {"__type__": "set", "values": sorted_vals}
1868
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
2841
+ elif isinstance(obj, TreeNode):
1869
2842
  obj_ref = id(obj)
1870
2843
  if obj_ref in node_refs:
1871
2844
  return {"__ref__": node_refs[obj_ref]}
@@ -1881,7 +2854,7 @@ def _serialize(obj, depth=0, node_refs=None):
1881
2854
  if hasattr(obj, 'right'):
1882
2855
  result["right"] = _serialize(obj.right, depth + 1, node_refs)
1883
2856
  return result
1884
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
2857
+ elif isinstance(obj, ListNode):
1885
2858
  obj_ref = id(obj)
1886
2859
  if obj_ref in node_refs:
1887
2860
  return {"__ref__": node_refs[obj_ref]}
@@ -1959,14 +2932,14 @@ def _serialize(obj, depth=0):
1959
2932
  return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
1960
2933
  except TypeError:
1961
2934
  return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
1962
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
2935
+ elif isinstance(obj, TreeNode):
1963
2936
  result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
1964
2937
  if hasattr(obj, 'left'):
1965
2938
  result["left"] = _serialize(obj.left, depth + 1)
1966
2939
  if hasattr(obj, 'right'):
1967
2940
  result["right"] = _serialize(obj.right, depth + 1)
1968
2941
  return result
1969
- elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
2942
+ elif isinstance(obj, ListNode):
1970
2943
  result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
1971
2944
  result["next"] = _serialize(obj.next, depth + 1)
1972
2945
  return result
@@ -2041,14 +3014,14 @@ def _serialize(obj, depth=0):
2041
3014
  return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
2042
3015
  except TypeError:
2043
3016
  return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
2044
- elif hasattr(obj, 'val') and (hasattr(obj, 'left') or hasattr(obj, 'right')):
3017
+ elif isinstance(obj, TreeNode):
2045
3018
  result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
2046
3019
  if hasattr(obj, 'left'):
2047
3020
  result["left"] = _serialize(obj.left, depth + 1)
2048
3021
  if hasattr(obj, 'right'):
2049
3022
  result["right"] = _serialize(obj.right, depth + 1)
2050
3023
  return result
2051
- elif hasattr(obj, 'val') and hasattr(obj, 'next'):
3024
+ elif isinstance(obj, ListNode):
2052
3025
  result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
2053
3026
  result["next"] = _serialize(obj.next, depth + 1)
2054
3027
  return result
@@ -2142,16 +3115,17 @@ function createConsoleProxy(output) {
2142
3115
  }
2143
3116
  function isLikelyTreeNodeValue(value) {
2144
3117
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2145
- const hasValue = "val" in value || "value" in value;
2146
- const hasTreeLinks = "left" in value || "right" in value;
2147
- return hasValue && hasTreeLinks;
3118
+ const record = value;
3119
+ if (record.__type__ === "TreeNode") return true;
3120
+ const ctor = value.constructor;
3121
+ return ctor?.name === "TreeNode";
2148
3122
  }
2149
3123
  function isLikelyListNodeValue(value) {
2150
3124
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2151
- const hasValue = "val" in value || "value" in value;
2152
- const hasTreeLinks = "left" in value || "right" in value;
2153
- const hasListLinks = "next" in value || "prev" in value;
2154
- return hasValue && hasListLinks && !hasTreeLinks;
3125
+ const record = value;
3126
+ if (record.__type__ === "ListNode") return true;
3127
+ const ctor = value.constructor;
3128
+ return ctor?.name === "ListNode";
2155
3129
  }
2156
3130
  function getCustomClassName(value) {
2157
3131
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -2206,22 +3180,25 @@ function serializeValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet(),
2206
3180
  const nodePrefix = isTree ? "tree" : "list";
2207
3181
  const nodeId = `${nodePrefix}-${nodeRefState.nextId++}`;
2208
3182
  nodeRefState.ids.set(objectValue, nodeId);
2209
- if (isTree) {
2210
- return {
2211
- __type__: "TreeNode",
2212
- __id__: nodeId,
2213
- val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, depth + 1, seen, nodeRefState),
2214
- left: serializeValue(nodeValue.left ?? null, depth + 1, seen, nodeRefState),
2215
- right: serializeValue(nodeValue.right ?? null, depth + 1, seen, nodeRefState)
2216
- };
2217
- }
2218
- return {
3183
+ const out2 = isTree ? {
3184
+ __type__: "TreeNode",
3185
+ __id__: nodeId,
3186
+ val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, depth + 1, seen, nodeRefState),
3187
+ left: serializeValue(nodeValue.left ?? null, depth + 1, seen, nodeRefState),
3188
+ right: serializeValue(nodeValue.right ?? null, depth + 1, seen, nodeRefState)
3189
+ } : {
2219
3190
  __type__: "ListNode",
2220
3191
  __id__: nodeId,
2221
3192
  val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, depth + 1, seen, nodeRefState),
2222
3193
  next: serializeValue(nodeValue.next ?? null, depth + 1, seen, nodeRefState),
2223
3194
  ..."prev" in nodeValue ? { prev: serializeValue(nodeValue.prev ?? null, depth + 1, seen, nodeRefState) } : {}
2224
3195
  };
3196
+ const skipped = isTree ? /* @__PURE__ */ new Set(["__id__", "__type__", "__class__", "val", "value", "left", "right"]) : /* @__PURE__ */ new Set(["__id__", "__type__", "__class__", "val", "value", "next", "prev"]);
3197
+ for (const [k, v] of Object.entries(nodeValue)) {
3198
+ if (skipped.has(k)) continue;
3199
+ out2[k] = serializeValue(v, depth + 1, seen, nodeRefState);
3200
+ }
3201
+ return out2;
2225
3202
  }
2226
3203
  const customClassName = getCustomClassName(value);
2227
3204
  if (customClassName) {
@@ -2385,21 +3362,31 @@ function materializeTreeInput(value) {
2385
3362
  }
2386
3363
  const record = value;
2387
3364
  if (isLikelyTreeNodeValue(record)) {
2388
- return {
3365
+ const node = {
2389
3366
  val: record.val ?? record.value ?? null,
2390
3367
  value: record.val ?? record.value ?? null,
2391
3368
  left: materializeTreeInput(record.left ?? null),
2392
3369
  right: materializeTreeInput(record.right ?? null)
2393
3370
  };
3371
+ for (const [key, nested] of Object.entries(record)) {
3372
+ if (key === "__id__" || key === "__type__" || key === "val" || key === "value" || key === "left" || key === "right") continue;
3373
+ node[key] = materializeTreeInput(nested);
3374
+ }
3375
+ return node;
2394
3376
  }
2395
3377
  const taggedRecord = value;
2396
3378
  if (taggedRecord.__type__ === "TreeNode") {
2397
- return {
3379
+ const node = {
2398
3380
  val: taggedRecord.val ?? taggedRecord.value ?? null,
2399
3381
  value: taggedRecord.val ?? taggedRecord.value ?? null,
2400
3382
  left: materializeTreeInput(taggedRecord.left ?? null),
2401
3383
  right: materializeTreeInput(taggedRecord.right ?? null)
2402
3384
  };
3385
+ for (const [key, nested] of Object.entries(taggedRecord)) {
3386
+ if (key === "__id__" || key === "__type__" || key === "val" || key === "value" || key === "left" || key === "right") continue;
3387
+ node[key] = materializeTreeInput(nested);
3388
+ }
3389
+ return node;
2403
3390
  }
2404
3391
  return value;
2405
3392
  }
@@ -2443,6 +3430,10 @@ function materializeListInput(value, refs = /* @__PURE__ */ new Map(), materiali
2443
3430
  refs.set(taggedRecord.__id__, node);
2444
3431
  }
2445
3432
  node.next = materializeListInput(taggedRecord.next ?? null, refs, materialized);
3433
+ for (const [key, nested] of Object.entries(taggedRecord)) {
3434
+ if (key === "__id__" || key === "__type__" || key === "__class__" || key === "val" || key === "value" || key === "next") continue;
3435
+ node[key] = materializeListInput(nested, refs, materialized);
3436
+ }
2446
3437
  return node;
2447
3438
  }
2448
3439
  return value;
@@ -2806,9 +3797,13 @@ export {
2806
3797
  TEMPLATE_PYTHON_TRACE_SERIALIZE_FUNCTION,
2807
3798
  TYPESCRIPT_RUNTIME_DECLARATIONS,
2808
3799
  adaptJavaScriptTraceExecutionResult,
3800
+ adaptJavaTraceExecutionResult as adaptJavaSpikeTraceExecutionResult,
3801
+ adaptJavaTraceExecutionResult,
2809
3802
  adaptPythonTraceExecutionResult,
2810
3803
  adaptTraceExecutionResult,
2811
3804
  assertRuntimeRequestSupported,
3805
+ buildJavaExecutionResult,
3806
+ buildJavaExecutionResult as buildJavaSpikeExecutionResult,
2812
3807
  createBrowserHarness,
2813
3808
  executeJavaScriptCode,
2814
3809
  executeJavaScriptWithTracing,
@@ -2820,6 +3815,8 @@ export {
2820
3815
  getSupportedLanguageProfiles,
2821
3816
  identifyConversions,
2822
3817
  isLanguageSupported,
3818
+ normalizeJavaTraceContract as normalizeJavaSpikeTraceContract,
3819
+ normalizeJavaTraceContract,
2823
3820
  normalizeRuntimeTraceContract,
2824
3821
  resolveBrowserHarnessAssets,
2825
3822
  stableStringifyRuntimeTraceContract,