@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/CHANGELOG.md +7 -0
- package/README.md +4 -3
- package/dist/browser.cjs +4 -4
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +4 -4
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/workers/java/java-worker.js +99 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tracecode/harness",
|
|
3
|
-
"version": "0.6.
|
|
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
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
583
|
+
payload.code,
|
|
584
|
+
payload.functionName,
|
|
585
|
+
payload.executionStyle,
|
|
586
|
+
payload.inputs ?? {}
|
|
545
587
|
);
|
|
546
588
|
return rewriteLibraryClass.rewriteSource(
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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.
|
|
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
|
|
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:
|
|
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:
|
|
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
|