@tracecode/harness 0.6.1 → 0.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tracecode/harness",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "license": "AGPL-3.0-only",
5
5
  "homepage": "https://tracecode.app",
6
6
  "repository": {
@@ -78,13 +78,14 @@
78
78
  "test:trace-adapters": "pnpm exec tsx tests/test-trace-adapters.ts",
79
79
  "test:python-sync": "pnpm exec tsx scripts/generate-python-harness-artifacts.ts --check && pnpm exec tsx tests/test-python-harness-sync.ts",
80
80
  "test:java-sync": "pnpm exec tsx tests/test-java-harness-sync.ts",
81
+ "test:java-runtime": "pnpm exec tsx tests/test-java-runtime.ts",
81
82
  "test:js-runtime": "pnpm exec tsx tests/test-javascript-runtime.ts",
82
83
  "test:python-harness-sync": "pnpm run test:python-sync",
83
84
  "test:java-harness-sync": "pnpm run test:java-sync",
84
85
  "test:javascript-runtime": "pnpm run test:js-runtime",
85
86
  "test:runtime-contract": "pnpm exec tsx tests/test-runtime-contract.ts",
86
- "test:ci": "pnpm typecheck && pnpm test:trace-adapters && pnpm test:python-sync && pnpm test:java-sync && pnpm test:js-runtime && pnpm test:runtime-contract && pnpm test:standalone-boundary && pnpm build && pnpm test:packaged-surface && pnpm test:smoke && pnpm test:asset-sync",
87
- "test": "pnpm typecheck && pnpm test:trace-adapters && pnpm test:python-sync && pnpm test:java-sync && pnpm test:js-runtime && pnpm test:runtime-contract && pnpm test:standalone-boundary && pnpm build && pnpm test:packaged-surface && pnpm test:smoke && pnpm test:browser-harness && pnpm test:asset-sync && pnpm test:example-app && pnpm test:java-example-app && pnpm test:example-app-packaged && pnpm test:java-example-app-packaged"
87
+ "test:ci": "pnpm typecheck && pnpm test:trace-adapters && pnpm test:python-sync && pnpm test:java-sync && pnpm test:java-runtime && pnpm test:js-runtime && pnpm test:runtime-contract && pnpm test:standalone-boundary && pnpm build && pnpm test:packaged-surface && pnpm test:smoke && pnpm test:asset-sync",
88
+ "test": "pnpm typecheck && pnpm test:trace-adapters && pnpm test:python-sync && pnpm test:java-sync && pnpm test:java-runtime && pnpm test:js-runtime && pnpm test:runtime-contract && pnpm test:standalone-boundary && pnpm build && pnpm test:packaged-surface && pnpm test:smoke && pnpm test:browser-harness && pnpm test:asset-sync && pnpm test:example-app && pnpm test:java-example-app && pnpm test:example-app-packaged && pnpm test:java-example-app-packaged"
88
89
  },
89
90
  "devDependencies": {
90
91
  "@types/node": "^20.0.0",
@@ -14,6 +14,7 @@ const FULL_CLASSPATH = [
14
14
  const DEFAULT_COMPILER_DEBUG_PROFILE = 'full';
15
15
  const DEFAULT_MAX_STORED_EVENTS = 50_000;
16
16
  const IDLE_TIMEOUT_MS = 90_000;
17
+ const SCRIPT_METHOD_NAME = '__tracecodeScript';
17
18
 
18
19
  let workerReadyPromise = null;
19
20
  let idleTimer = null;
@@ -48,6 +49,10 @@ function assertSupportedExecutionStyle(executionStyle) {
48
49
  }
49
50
  }
50
51
 
52
+ function isScriptRequest(payload) {
53
+ return typeof payload?.functionName !== 'string' || payload.functionName.trim().length === 0;
54
+ }
55
+
51
56
  function resolveMaxStoredEvents(options = {}) {
52
57
  const fromStored = Number(options.maxStoredEvents);
53
58
  if (Number.isFinite(fromStored) && fromStored > 0) {
@@ -343,6 +348,25 @@ function indentBlock(source, spaces = 2) {
343
348
  .join('\n');
344
349
  }
345
350
 
351
+ function splitImportPrelude(source) {
352
+ const lines = source.split('\n');
353
+ const importLines = [];
354
+ const bodyLines = [];
355
+ let inImportPrelude = true;
356
+
357
+ for (const line of lines) {
358
+ const trimmed = line.trim();
359
+ if (inImportPrelude && (trimmed === '' || trimmed.startsWith('import '))) {
360
+ importLines.push(line);
361
+ continue;
362
+ }
363
+ inImportPrelude = false;
364
+ bodyLines.push(line);
365
+ }
366
+
367
+ return { importLines, bodyLines };
368
+ }
369
+
346
370
  function normalizeFunctionSource(source) {
347
371
  if (/\bpackage\s+[A-Za-z_][A-Za-z0-9_.]*\s*;/.test(source)) {
348
372
  throw new Error('Java function style should not declare a package; the harness manages package isolation.');
@@ -358,20 +382,7 @@ function normalizeFunctionSource(source) {
358
382
  );
359
383
  }
360
384
 
361
- const lines = source.split('\n');
362
- const importLines = [];
363
- const bodyLines = [];
364
- let inImportPrelude = true;
365
-
366
- for (const line of lines) {
367
- const trimmed = line.trim();
368
- if (inImportPrelude && (trimmed === '' || trimmed.startsWith('import '))) {
369
- importLines.push(line);
370
- continue;
371
- }
372
- inImportPrelude = false;
373
- bodyLines.push(line);
374
- }
385
+ const { importLines, bodyLines } = splitImportPrelude(source);
375
386
 
376
387
  const importBlock = importLines.join('\n').trim();
377
388
  const body = bodyLines.join('\n').trim();
@@ -382,7 +393,39 @@ function normalizeFunctionSource(source) {
382
393
  return `${importBlock ? `${importBlock}\n\n` : ''}class Solution {\n${indentBlock(body, 2)}\n}`;
383
394
  }
384
395
 
396
+ function normalizeScriptSource(source) {
397
+ if (/\bpackage\s+[A-Za-z_][A-Za-z0-9_.]*\s*;/.test(source)) {
398
+ throw new Error('Java script style should not declare a package; the harness manages package isolation.');
399
+ }
400
+
401
+ const { importLines, bodyLines } = splitImportPrelude(source);
402
+ const body = bodyLines.join('\n');
403
+ if (!body.trim()) {
404
+ throw new Error('Java script style requires executable statements and a result assignment.');
405
+ }
406
+
407
+ const prelude = importLines.length > 0 ? `${importLines.join('\n')}\n` : '';
408
+ const wrapperPrefix = `class Solution { Object ${SCRIPT_METHOD_NAME}() { Object result = null; `;
409
+ return `${prelude}${wrapperPrefix}${body}\nreturn result;\n} }`;
410
+ }
411
+
385
412
  function normalizeJavaRequest(payload) {
413
+ if (isScriptRequest(payload)) {
414
+ if (payload.executionStyle !== 'function') {
415
+ throw new Error('Java script-mode execution only supports executionStyle="function".');
416
+ }
417
+
418
+ return {
419
+ ...payload,
420
+ code: normalizeScriptSource(payload.code),
421
+ executionStyle: 'solution-method',
422
+ functionName: SCRIPT_METHOD_NAME,
423
+ sourceText: payload.code,
424
+ userCodeLineCount: payload.code.split(/\r?\n/).length,
425
+ scriptMode: true,
426
+ };
427
+ }
428
+
386
429
  if (payload.executionStyle !== 'function') {
387
430
  return payload;
388
431
  }
@@ -533,35 +576,55 @@ public class ExportsTracecodeWarmup {
533
576
  }
534
577
 
535
578
  async function rewriteSource(payload, requestId) {
536
- const normalizedPayload = normalizeJavaRequest(payload);
537
579
  const rewriteLibraryClass = await getRewriteLibraryClass();
538
580
  const exportsClassName = buildExportsClassName(requestId);
539
581
  const packageName = buildPackageName(requestId);
540
582
  const exportsSource = buildExportsSource(
541
- normalizedPayload.code,
542
- normalizedPayload.functionName,
543
- normalizedPayload.executionStyle,
544
- normalizedPayload.inputs ?? {}
583
+ payload.code,
584
+ payload.functionName,
585
+ payload.executionStyle,
586
+ payload.inputs ?? {}
545
587
  );
546
588
  return rewriteLibraryClass.rewriteSource(
547
- normalizedPayload.code,
548
- normalizedPayload.executionStyle,
549
- normalizedPayload.functionName,
589
+ payload.code,
590
+ payload.executionStyle,
591
+ payload.functionName,
550
592
  exportsSource,
551
593
  exportsClassName,
552
594
  packageName
553
595
  );
554
596
  }
555
597
 
598
+ function normalizeScriptTraceEvents(events, scriptMode, userCodeLineCount) {
599
+ if (!scriptMode || !Array.isArray(events)) return events;
600
+ return events.map((event) => {
601
+ const normalizedEvent = String(event)
602
+ .replace(new RegExp(`\\bcall\\s+${SCRIPT_METHOD_NAME}\\b`, 'g'), 'call <module>')
603
+ .replace(new RegExp(`\\breturn\\s+${SCRIPT_METHOD_NAME}\\b`, 'g'), 'return <module>');
604
+ const match = normalizedEvent.match(/^line=(\d+)\s+return\s+<module>$/);
605
+ if (!match || !Number.isFinite(userCodeLineCount) || userCodeLineCount <= 0) {
606
+ return normalizedEvent;
607
+ }
608
+ const line = Number.parseInt(match[1], 10);
609
+ if (line <= userCodeLineCount) return normalizedEvent;
610
+ return `line=${userCodeLineCount} return <module>`;
611
+ });
612
+ }
613
+
556
614
  async function runJavaRequest(payload, requestId) {
557
615
  assertSupportedExecutionStyle(payload.executionStyle);
558
- if (typeof payload.functionName !== 'string' || payload.functionName.trim().length === 0) {
616
+ if (typeof payload.code !== 'string') {
617
+ throw new Error('`code` must be a string');
618
+ }
619
+ const scriptRequest = isScriptRequest(payload);
620
+ if (!scriptRequest && (typeof payload.functionName !== 'string' || payload.functionName.trim().length === 0)) {
559
621
  throw new Error('Java execution requires a non-empty functionName or class entry name.');
560
622
  }
561
623
 
562
624
  const totalStart = performance.now();
563
625
  const rewriteStart = performance.now();
564
- const rewrittenSource = await rewriteSource(payload, requestId);
626
+ const normalizedPayload = normalizeJavaRequest(payload);
627
+ const rewrittenSource = await rewriteSource(normalizedPayload, requestId);
565
628
  const rewriteEnd = performance.now();
566
629
 
567
630
  const exportsClassName = buildExportsClassName(requestId);
@@ -592,7 +655,12 @@ async function runJavaRequest(payload, requestId) {
592
655
  if (report.success !== true) {
593
656
  return {
594
657
  success: false,
595
- events: Array.isArray(report.events) ? report.events : [],
658
+ events: normalizeScriptTraceEvents(
659
+ Array.isArray(report.events) ? report.events : [],
660
+ normalizedPayload.scriptMode,
661
+ normalizedPayload.userCodeLineCount
662
+ ),
663
+ ...(normalizedPayload.sourceText ? { sourceText: normalizedPayload.sourceText } : {}),
596
664
  executionTimeMs: totalEnd - totalStart,
597
665
  consoleOutput,
598
666
  error:
@@ -618,7 +686,12 @@ async function runJavaRequest(payload, requestId) {
618
686
  return {
619
687
  success: true,
620
688
  output: report.output ? JSON.parse(report.output) : undefined,
621
- events: Array.isArray(report.events) ? report.events : [],
689
+ events: normalizeScriptTraceEvents(
690
+ Array.isArray(report.events) ? report.events : [],
691
+ normalizedPayload.scriptMode,
692
+ normalizedPayload.userCodeLineCount
693
+ ),
694
+ ...(normalizedPayload.sourceText ? { sourceText: normalizedPayload.sourceText } : {}),
622
695
  executionTimeMs: totalEnd - totalStart,
623
696
  consoleOutput,
624
697
  ...(report.traceLimitExceeded !== undefined