@tracecode/harness 0.4.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 (55) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/LICENSE +674 -0
  3. package/README.md +266 -0
  4. package/dist/browser.cjs +1352 -0
  5. package/dist/browser.cjs.map +1 -0
  6. package/dist/browser.d.cts +49 -0
  7. package/dist/browser.d.ts +49 -0
  8. package/dist/browser.js +1317 -0
  9. package/dist/browser.js.map +1 -0
  10. package/dist/cli.cjs +70 -0
  11. package/dist/cli.cjs.map +1 -0
  12. package/dist/cli.d.cts +1 -0
  13. package/dist/cli.d.ts +1 -0
  14. package/dist/cli.js +70 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/core.cjs +286 -0
  17. package/dist/core.cjs.map +1 -0
  18. package/dist/core.d.cts +69 -0
  19. package/dist/core.d.ts +69 -0
  20. package/dist/core.js +254 -0
  21. package/dist/core.js.map +1 -0
  22. package/dist/index.cjs +2603 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.cts +6 -0
  25. package/dist/index.d.ts +6 -0
  26. package/dist/index.js +2538 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/internal/browser.cjs +647 -0
  29. package/dist/internal/browser.cjs.map +1 -0
  30. package/dist/internal/browser.d.cts +143 -0
  31. package/dist/internal/browser.d.ts +143 -0
  32. package/dist/internal/browser.js +617 -0
  33. package/dist/internal/browser.js.map +1 -0
  34. package/dist/javascript.cjs +549 -0
  35. package/dist/javascript.cjs.map +1 -0
  36. package/dist/javascript.d.cts +11 -0
  37. package/dist/javascript.d.ts +11 -0
  38. package/dist/javascript.js +518 -0
  39. package/dist/javascript.js.map +1 -0
  40. package/dist/python.cjs +744 -0
  41. package/dist/python.cjs.map +1 -0
  42. package/dist/python.d.cts +97 -0
  43. package/dist/python.d.ts +97 -0
  44. package/dist/python.js +698 -0
  45. package/dist/python.js.map +1 -0
  46. package/dist/runtime-types-C7d1LFbx.d.ts +85 -0
  47. package/dist/runtime-types-Dvgn07z9.d.cts +85 -0
  48. package/dist/types-Bzr1Ohcf.d.cts +96 -0
  49. package/dist/types-Bzr1Ohcf.d.ts +96 -0
  50. package/package.json +89 -0
  51. package/workers/javascript/javascript-worker.js +2918 -0
  52. package/workers/python/generated-python-harness-snippets.js +20 -0
  53. package/workers/python/pyodide-worker.js +1197 -0
  54. package/workers/python/runtime-core.js +1529 -0
  55. package/workers/vendor/typescript.js +200276 -0
@@ -0,0 +1,2918 @@
1
+ /**
2
+ * JavaScript Runtime Worker
3
+ *
4
+ * Executes JavaScript user code off the main thread.
5
+ * Message contract mirrors the Python worker so runtime adapters can share
6
+ * the same high-level interface.
7
+ */
8
+
9
+ const WORKER_DEBUG = (() => {
10
+ try {
11
+ return typeof self !== 'undefined' && typeof self.location?.search === 'string' && self.location.search.includes('dev=');
12
+ } catch {
13
+ return false;
14
+ }
15
+ })();
16
+
17
+ let isInitialized = false;
18
+ let isLoading = false;
19
+ let typeScriptLoadPromise = null;
20
+ const INTERVIEW_GUARD_DEFAULTS = Object.freeze({
21
+ maxTraceSteps: 8000,
22
+ maxLineEvents: 4000,
23
+ maxSingleLineHits: 3000,
24
+ maxCallDepth: 2000,
25
+ });
26
+ const TYPESCRIPT_COMPILER_URLS = [
27
+ './vendor/typescript.js',
28
+ 'https://cdn.jsdelivr.net/npm/typescript@5.9.2/lib/typescript.js',
29
+ 'https://unpkg.com/typescript@5.9.2/lib/typescript.js',
30
+ ];
31
+
32
+ const JAVASCRIPT_RUNTIME_PRELUDE = `
33
+ if (typeof globalThis.ListNode !== 'function') {
34
+ globalThis.ListNode = class ListNode {
35
+ constructor(val = 0, next = null) {
36
+ this.val = val;
37
+ this.next = next;
38
+ }
39
+ };
40
+ }
41
+ if (typeof globalThis.TreeNode !== 'function') {
42
+ globalThis.TreeNode = class TreeNode {
43
+ constructor(val = 0, left = null, right = null) {
44
+ this.val = val;
45
+ this.left = left;
46
+ this.right = right;
47
+ }
48
+ };
49
+ }
50
+ `;
51
+
52
+ const TYPESCRIPT_RUNTIME_DECLARATIONS = `
53
+ declare class ListNode {
54
+ val: any;
55
+ next: ListNode | SerializedListNode | SerializedRef | null;
56
+ prev?: ListNode | SerializedListNode | SerializedRef | null;
57
+ constructor(val?: any, next?: ListNode | null);
58
+ }
59
+
60
+ declare class TreeNode {
61
+ val: any;
62
+ left: TreeNode | SerializedTreeNode | SerializedRef | null;
63
+ right: TreeNode | SerializedTreeNode | SerializedRef | null;
64
+ constructor(val?: any, left?: TreeNode | null, right?: TreeNode | null);
65
+ }
66
+
67
+ type SerializedRef = { __ref__: string };
68
+
69
+ type SerializedListNode = {
70
+ __id__?: string;
71
+ __type__?: 'ListNode';
72
+ val?: any;
73
+ next?: SerializedListNode | SerializedRef | ListNode | null;
74
+ prev?: SerializedListNode | SerializedRef | ListNode | null;
75
+ };
76
+
77
+ type SerializedTreeNode = {
78
+ __id__?: string;
79
+ __type__?: 'TreeNode';
80
+ val?: any;
81
+ left?: SerializedTreeNode | SerializedRef | TreeNode | null;
82
+ right?: SerializedTreeNode | SerializedRef | TreeNode | null;
83
+ };
84
+ `;
85
+
86
+ function performanceNow() {
87
+ if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
88
+ return performance.now();
89
+ }
90
+ return Date.now();
91
+ }
92
+
93
+ function formatConsoleArg(value) {
94
+ if (typeof value === 'string') return value;
95
+ if (typeof value === 'number' || typeof value === 'boolean' || value === null || value === undefined) {
96
+ return String(value);
97
+ }
98
+ try {
99
+ return JSON.stringify(value);
100
+ } catch {
101
+ return String(value);
102
+ }
103
+ }
104
+
105
+ function createConsoleProxy(output) {
106
+ const capture = (...args) => {
107
+ output.push(args.map(formatConsoleArg).join(' '));
108
+ };
109
+
110
+ return {
111
+ log: capture,
112
+ info: capture,
113
+ warn: capture,
114
+ error: capture,
115
+ debug: capture,
116
+ };
117
+ }
118
+
119
+ function isLikelyTreeNodeValue(value) {
120
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
121
+ const hasValue =
122
+ Object.prototype.hasOwnProperty.call(value, 'val') ||
123
+ Object.prototype.hasOwnProperty.call(value, 'value');
124
+ const hasTreeLinks =
125
+ Object.prototype.hasOwnProperty.call(value, 'left') ||
126
+ Object.prototype.hasOwnProperty.call(value, 'right');
127
+ return hasValue && hasTreeLinks;
128
+ }
129
+
130
+ function isLikelyListNodeValue(value) {
131
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
132
+ const hasValue =
133
+ Object.prototype.hasOwnProperty.call(value, 'val') ||
134
+ Object.prototype.hasOwnProperty.call(value, 'value');
135
+ const hasTreeLinks =
136
+ Object.prototype.hasOwnProperty.call(value, 'left') ||
137
+ Object.prototype.hasOwnProperty.call(value, 'right');
138
+ const hasListLinks =
139
+ Object.prototype.hasOwnProperty.call(value, 'next') ||
140
+ Object.prototype.hasOwnProperty.call(value, 'prev');
141
+ return hasValue && hasListLinks && !hasTreeLinks;
142
+ }
143
+
144
+ function serializeValue(
145
+ value,
146
+ depth = 0,
147
+ seen = new WeakSet(),
148
+ nodeRefState = { ids: new Map(), nextId: 1 }
149
+ ) {
150
+ if (depth > 48) return '<max depth>';
151
+ if (value === null || value === undefined) return value;
152
+
153
+ const valueType = typeof value;
154
+ if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') {
155
+ return value;
156
+ }
157
+ if (valueType === 'bigint') {
158
+ return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
159
+ }
160
+ if (valueType === 'function') {
161
+ return '<function>';
162
+ }
163
+ if (Array.isArray(value)) {
164
+ return value.map((item) => serializeValue(item, depth + 1, seen, nodeRefState));
165
+ }
166
+ if (value instanceof Set) {
167
+ return {
168
+ __type__: 'set',
169
+ values: [...value].map((item) => serializeValue(item, depth + 1, seen, nodeRefState)),
170
+ };
171
+ }
172
+ if (value instanceof Map) {
173
+ return {
174
+ __type__: 'map',
175
+ entries: [...value.entries()].map(([k, v]) => [
176
+ serializeValue(k, depth + 1, seen, nodeRefState),
177
+ serializeValue(v, depth + 1, seen, nodeRefState),
178
+ ]),
179
+ };
180
+ }
181
+ if (valueType === 'object') {
182
+ if (isLikelyTreeNodeValue(value) || isLikelyListNodeValue(value)) {
183
+ const existingId = nodeRefState.ids.get(value);
184
+ if (existingId) {
185
+ return { __ref__: existingId };
186
+ }
187
+ const nodePrefix = isLikelyTreeNodeValue(value) ? 'tree' : 'list';
188
+ const nodeId = `${nodePrefix}-${nodeRefState.nextId++}`;
189
+ nodeRefState.ids.set(value, nodeId);
190
+
191
+ if (nodePrefix === 'tree') {
192
+ return {
193
+ __type__: 'TreeNode',
194
+ __id__: nodeId,
195
+ val: serializeValue(value.val ?? value.value ?? null, depth + 1, seen, nodeRefState),
196
+ left: serializeValue(value.left ?? null, depth + 1, seen, nodeRefState),
197
+ right: serializeValue(value.right ?? null, depth + 1, seen, nodeRefState),
198
+ };
199
+ }
200
+
201
+ return {
202
+ __type__: 'ListNode',
203
+ __id__: nodeId,
204
+ val: serializeValue(value.val ?? value.value ?? null, depth + 1, seen, nodeRefState),
205
+ next: serializeValue(value.next ?? null, depth + 1, seen, nodeRefState),
206
+ ...(Object.prototype.hasOwnProperty.call(value, 'prev')
207
+ ? { prev: serializeValue(value.prev ?? null, depth + 1, seen, nodeRefState) }
208
+ : {}),
209
+ };
210
+ }
211
+
212
+ if (seen.has(value)) return '<cycle>';
213
+ seen.add(value);
214
+ const out = {};
215
+ for (const [k, v] of Object.entries(value)) {
216
+ out[k] = serializeValue(v, depth + 1, seen, nodeRefState);
217
+ }
218
+ seen.delete(value);
219
+ return out;
220
+ }
221
+
222
+ return String(value);
223
+ }
224
+
225
+ function extractUserErrorLine(error) {
226
+ if (error && typeof error === 'object' && '__tracecodeLine' in error) {
227
+ const line = Number(error.__tracecodeLine);
228
+ if (Number.isFinite(line)) return line;
229
+ }
230
+
231
+ const stack = error?.stack;
232
+ if (!stack || typeof stack !== 'string') return undefined;
233
+ const match = stack.match(/<anonymous>:(\d+):\d+/);
234
+ if (!match) return undefined;
235
+ const line = Number.parseInt(match[1], 10);
236
+ return Number.isFinite(line) ? line : undefined;
237
+ }
238
+
239
+ function isPlainObjectRecord(value) {
240
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
241
+ return Object.prototype.toString.call(value) === '[object Object]';
242
+ }
243
+
244
+ function collectReferenceTargets(value, byId, seen) {
245
+ if (value === null || value === undefined) return;
246
+ if (typeof value !== 'object') return;
247
+ if (seen.has(value)) return;
248
+ seen.add(value);
249
+
250
+ if (Array.isArray(value)) {
251
+ for (const item of value) {
252
+ collectReferenceTargets(item, byId, seen);
253
+ }
254
+ return;
255
+ }
256
+
257
+ if (!isPlainObjectRecord(value)) return;
258
+ if (typeof value.__id__ === 'string' && value.__id__.length > 0 && !byId.has(value.__id__)) {
259
+ byId.set(value.__id__, value);
260
+ }
261
+
262
+ for (const nested of Object.values(value)) {
263
+ collectReferenceTargets(nested, byId, seen);
264
+ }
265
+ }
266
+
267
+ function resolveReferenceGraph(value, byId, resolved) {
268
+ if (value === null || value === undefined) return value;
269
+ if (typeof value !== 'object') return value;
270
+
271
+ if (resolved.has(value)) {
272
+ return resolved.get(value);
273
+ }
274
+
275
+ if (Array.isArray(value)) {
276
+ const out = [];
277
+ resolved.set(value, out);
278
+ for (const item of value) {
279
+ out.push(resolveReferenceGraph(item, byId, resolved));
280
+ }
281
+ return out;
282
+ }
283
+
284
+ if (!isPlainObjectRecord(value)) {
285
+ return value;
286
+ }
287
+
288
+ const keys = Object.keys(value);
289
+ if (keys.length === 1 && typeof value.__ref__ === 'string') {
290
+ const target = byId.get(value.__ref__);
291
+ if (!target) return null;
292
+ return resolveReferenceGraph(target, byId, resolved);
293
+ }
294
+
295
+ const out = {};
296
+ resolved.set(value, out);
297
+ for (const [key, nested] of Object.entries(value)) {
298
+ out[key] = resolveReferenceGraph(nested, byId, resolved);
299
+ }
300
+ return out;
301
+ }
302
+
303
+ function normalizeInputs(inputs) {
304
+ if (!inputs || typeof inputs !== 'object' || Array.isArray(inputs)) return {};
305
+ const byId = new Map();
306
+ collectReferenceTargets(inputs, byId, new WeakSet());
307
+ if (byId.size === 0) {
308
+ return inputs;
309
+ }
310
+ const hydrated = resolveReferenceGraph(inputs, byId, new WeakMap());
311
+ if (!hydrated || typeof hydrated !== 'object' || Array.isArray(hydrated)) {
312
+ return inputs;
313
+ }
314
+ return hydrated;
315
+ }
316
+
317
+ function collectSimpleParameterNames(ts, functionLikeNode) {
318
+ const names = [];
319
+
320
+ for (const parameter of functionLikeNode.parameters ?? []) {
321
+ if (!ts.isIdentifier(parameter.name)) {
322
+ return null;
323
+ }
324
+ if (parameter.name.text === 'this') {
325
+ continue;
326
+ }
327
+ names.push(parameter.name.text);
328
+ }
329
+
330
+ return names;
331
+ }
332
+
333
+ function getPropertyNameText(ts, name) {
334
+ if (!name) return null;
335
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
336
+ return name.text;
337
+ }
338
+ return null;
339
+ }
340
+
341
+ function findFunctionLikeNode(ts, sourceFile, functionName, executionStyle) {
342
+ let found = null;
343
+
344
+ const visit = (node) => {
345
+ if (found) return;
346
+
347
+ if (executionStyle === 'solution-method' && ts.isClassDeclaration(node) && node.name?.text === 'Solution') {
348
+ for (const member of node.members) {
349
+ if (found) break;
350
+
351
+ if (ts.isMethodDeclaration(member) && getPropertyNameText(ts, member.name) === functionName) {
352
+ found = member;
353
+ break;
354
+ }
355
+
356
+ if (
357
+ ts.isPropertyDeclaration(member) &&
358
+ getPropertyNameText(ts, member.name) === functionName &&
359
+ member.initializer &&
360
+ (ts.isArrowFunction(member.initializer) || ts.isFunctionExpression(member.initializer))
361
+ ) {
362
+ found = member.initializer;
363
+ break;
364
+ }
365
+ }
366
+ return;
367
+ }
368
+
369
+ if (executionStyle === 'function') {
370
+ if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
371
+ found = node;
372
+ return;
373
+ }
374
+
375
+ if (
376
+ ts.isVariableDeclaration(node) &&
377
+ ts.isIdentifier(node.name) &&
378
+ node.name.text === functionName &&
379
+ node.initializer &&
380
+ (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))
381
+ ) {
382
+ found = node.initializer;
383
+ return;
384
+ }
385
+ }
386
+
387
+ ts.forEachChild(node, visit);
388
+ };
389
+
390
+ visit(sourceFile);
391
+ return found;
392
+ }
393
+
394
+ async function resolveOrderedInputKeys(code, functionName, inputs, executionStyle) {
395
+ const fallbackKeys = Object.keys(inputs ?? {});
396
+ if (!functionName || executionStyle === 'ops-class' || fallbackKeys.length <= 1) {
397
+ return fallbackKeys;
398
+ }
399
+
400
+ try {
401
+ await ensureTypeScriptCompiler();
402
+ const ts = getTypeScriptCompiler();
403
+ if (!ts) {
404
+ return fallbackKeys;
405
+ }
406
+
407
+ const sourceFile = ts.createSourceFile('runtime-input.js', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.JS);
408
+ const target = findFunctionLikeNode(ts, sourceFile, functionName, executionStyle);
409
+ if (!target) {
410
+ return fallbackKeys;
411
+ }
412
+
413
+ const parameterNames = collectSimpleParameterNames(ts, target);
414
+ if (!parameterNames || parameterNames.length === 0) {
415
+ return fallbackKeys;
416
+ }
417
+
418
+ const matchedKeys = parameterNames.filter((name) => Object.prototype.hasOwnProperty.call(inputs, name));
419
+ if (matchedKeys.length === 0) {
420
+ return fallbackKeys;
421
+ }
422
+
423
+ const extras = fallbackKeys.filter((key) => !matchedKeys.includes(key));
424
+ return [...matchedKeys, ...extras];
425
+ } catch (_error) {
426
+ return fallbackKeys;
427
+ }
428
+ }
429
+
430
+ function stripEngineSuggestionHints(message) {
431
+ if (typeof message !== 'string' || message.length === 0) {
432
+ return String(message ?? '');
433
+ }
434
+
435
+ return message
436
+ .replace(/\s*\(?Did you mean[^?\n]*\??\)?/gi, '')
437
+ .replace(/\n+\s*Did you mean[^\n]*/gi, '')
438
+ .replace(/\s{2,}/g, ' ')
439
+ .trim();
440
+ }
441
+
442
+ function formatRuntimeErrorMessage(error) {
443
+ const rawMessage = error instanceof Error ? error.message : String(error ?? 'Execution failed');
444
+ if (!(error instanceof Error)) {
445
+ return rawMessage;
446
+ }
447
+
448
+ const errorName = String(error.name || '');
449
+ const shouldStripHints =
450
+ error instanceof ReferenceError ||
451
+ error instanceof TypeError ||
452
+ error instanceof SyntaxError ||
453
+ errorName === 'ReferenceError' ||
454
+ errorName === 'TypeError' ||
455
+ errorName === 'SyntaxError';
456
+
457
+ if (!shouldStripHints) {
458
+ return rawMessage;
459
+ }
460
+
461
+ const sanitized = stripEngineSuggestionHints(rawMessage);
462
+ return sanitized.length > 0 ? sanitized : rawMessage;
463
+ }
464
+
465
+ function getNumericOption(value, fallback) {
466
+ if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
467
+ return fallback;
468
+ }
469
+ return Math.floor(value);
470
+ }
471
+
472
+ function isTraceableIntegerIndex(value) {
473
+ return typeof value === 'number' && Number.isInteger(value);
474
+ }
475
+
476
+ function normalizeTraceIndices(indices, maxDepth = 2) {
477
+ if (!Array.isArray(indices) || indices.length === 0 || indices.length > maxDepth) {
478
+ return null;
479
+ }
480
+ if (!indices.every(isTraceableIntegerIndex)) {
481
+ return null;
482
+ }
483
+ return indices.map((index) => Math.trunc(index));
484
+ }
485
+
486
+ function isTraceableMutatingMethod(methodName) {
487
+ return ['push', 'pop', 'shift', 'unshift', 'splice'].includes(methodName);
488
+ }
489
+
490
+ function readValueAtIndices(container, indices) {
491
+ let current = container;
492
+ for (const index of indices) {
493
+ if (current === null || current === undefined) {
494
+ return undefined;
495
+ }
496
+ current = current[index];
497
+ }
498
+ return current;
499
+ }
500
+
501
+ function writeValueAtIndices(container, indices, value) {
502
+ if (!Array.isArray(indices) || indices.length === 0) {
503
+ return value;
504
+ }
505
+ if (indices.length === 1) {
506
+ container[indices[0]] = value;
507
+ return value;
508
+ }
509
+
510
+ let parent = container;
511
+ for (let i = 0; i < indices.length - 1; i += 1) {
512
+ parent = parent?.[indices[i]];
513
+ }
514
+ if (parent !== null && parent !== undefined) {
515
+ parent[indices[indices.length - 1]] = value;
516
+ }
517
+ return value;
518
+ }
519
+
520
+ function createTraceRecorder(options = {}) {
521
+ const trace = [];
522
+ const callStack = [];
523
+ const pendingAccessesByFrame = new Map();
524
+ const lineHitCount = new Map();
525
+ const stableNodeRefState = { ids: new Map(), nextId: 1 };
526
+ const maxTraceSteps = getNumericOption(options.maxTraceSteps, 4000);
527
+ const maxLineEvents = getNumericOption(options.maxLineEvents, 12000);
528
+ const maxSingleLineHits = getNumericOption(options.maxSingleLineHits, 1000);
529
+ const maxCallDepth = getNumericOption(options.maxCallDepth, 2000);
530
+
531
+ let lineEventCount = 0;
532
+ let traceLimitExceeded = false;
533
+ let timeoutReason;
534
+ let timeoutRecorded = false;
535
+ let nextFrameId = 1;
536
+
537
+ function normalizeLine(lineNumber, fallback = 1) {
538
+ const parsed = Number(lineNumber);
539
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
540
+ return Math.floor(parsed);
541
+ }
542
+
543
+ function snapshotCallStack() {
544
+ return callStack.map((frame) => ({
545
+ function: frame.function,
546
+ args: frame.args,
547
+ line: frame.line,
548
+ }));
549
+ }
550
+
551
+ function sanitizeVariables(value) {
552
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
553
+ const result = {};
554
+ for (const [key, variableValue] of Object.entries(value)) {
555
+ if (variableValue === undefined) continue;
556
+ try {
557
+ result[key] = serializeValue(variableValue, 0, new WeakSet(), stableNodeRefState);
558
+ } catch {
559
+ // Skip variables that throw during serialization (e.g. transient proxy/getter failures).
560
+ }
561
+ }
562
+ return result;
563
+ }
564
+
565
+ function isLikelyTreeObject(value) {
566
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
567
+ const hasValue = Object.prototype.hasOwnProperty.call(value, 'val') || Object.prototype.hasOwnProperty.call(value, 'value');
568
+ const hasTreeLinks = Object.prototype.hasOwnProperty.call(value, 'left') || Object.prototype.hasOwnProperty.call(value, 'right');
569
+ return hasValue && hasTreeLinks;
570
+ }
571
+
572
+ function isLikelyLinkedListObject(value) {
573
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
574
+ const hasValue = Object.prototype.hasOwnProperty.call(value, 'val') || Object.prototype.hasOwnProperty.call(value, 'value');
575
+ const hasTreeLinks = Object.prototype.hasOwnProperty.call(value, 'left') || Object.prototype.hasOwnProperty.call(value, 'right');
576
+ const hasListLinks = Object.prototype.hasOwnProperty.call(value, 'next') || Object.prototype.hasOwnProperty.call(value, 'prev');
577
+ return hasValue && hasListLinks && !hasTreeLinks;
578
+ }
579
+
580
+ function isLikelyAdjacencyListObject(value) {
581
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
582
+ const keys = Object.keys(value);
583
+ if (keys.length === 0) return false;
584
+ if (!keys.every((key) => Array.isArray(value[key]))) return false;
585
+
586
+ const keySet = new Set(keys.map((key) => String(key)));
587
+ for (const neighbors of Object.values(value)) {
588
+ for (const neighbor of neighbors) {
589
+ if (keySet.has(String(neighbor))) {
590
+ return true;
591
+ }
592
+ }
593
+ }
594
+ return false;
595
+ }
596
+
597
+ function isLikelyIndexedAdjacencyListArray(value) {
598
+ if (!Array.isArray(value) || value.length === 0) return false;
599
+ if (!value.every((row) => Array.isArray(row))) return false;
600
+
601
+ const nodeCount = value.length;
602
+ let edgeCount = 0;
603
+ for (const neighbors of value) {
604
+ for (const neighbor of neighbors) {
605
+ if (typeof neighbor !== 'number' || !Number.isInteger(neighbor)) return false;
606
+ if (neighbor < 0 || neighbor >= nodeCount) return false;
607
+ edgeCount += 1;
608
+ }
609
+ }
610
+
611
+ if (edgeCount === 0) return false;
612
+
613
+ const looksLikeAdjacencyMatrix = value.every(
614
+ (row) => row.length === nodeCount && row.every((cell) => cell === 0 || cell === 1)
615
+ );
616
+ if (looksLikeAdjacencyMatrix) return false;
617
+
618
+ return true;
619
+ }
620
+
621
+ function shouldVisualizeObjectAsHashMap(name, value) {
622
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
623
+ if (isLikelyTreeObject(value) || isLikelyLinkedListObject(value)) return false;
624
+ if (Object.keys(value).length === 1 && typeof value.__ref__ === 'string') return false;
625
+ if (isLikelyAdjacencyListObject(value)) return false;
626
+
627
+ const lowerName = String(name).toLowerCase();
628
+ if (lowerName.includes('map') || lowerName.includes('seen') || lowerName.includes('dict')) {
629
+ return true;
630
+ }
631
+ return Object.keys(value).length > 0;
632
+ }
633
+
634
+ function collectRuntimeVisualizations(value) {
635
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
636
+ return { hashMaps: [], objectKinds: {} };
637
+ }
638
+
639
+ const visualizations = [];
640
+ const objectKinds = {};
641
+ for (const [name, variableValue] of Object.entries(value)) {
642
+ if (variableValue === undefined) continue;
643
+
644
+ if (variableValue instanceof Map) {
645
+ objectKinds[name] = 'map';
646
+ visualizations.push({
647
+ name,
648
+ kind: 'map',
649
+ entries: [...variableValue.entries()].map(([key, mapValue]) => ({
650
+ key: serializeValue(key),
651
+ value: serializeValue(mapValue),
652
+ })),
653
+ });
654
+ continue;
655
+ }
656
+
657
+ if (variableValue instanceof Set) {
658
+ objectKinds[name] = 'set';
659
+ visualizations.push({
660
+ name,
661
+ kind: 'set',
662
+ entries: [...variableValue.values()].map((item) => ({
663
+ key: serializeValue(item),
664
+ value: true,
665
+ })),
666
+ });
667
+ continue;
668
+ }
669
+
670
+ if (isLikelyIndexedAdjacencyListArray(variableValue)) {
671
+ objectKinds[name] = 'graph-adjacency';
672
+ continue;
673
+ }
674
+
675
+ if (variableValue && typeof variableValue === 'object' && !Array.isArray(variableValue)) {
676
+ const serializedValue = variableValue;
677
+
678
+ if (serializedValue.__type__ === 'map' && Array.isArray(serializedValue.entries)) {
679
+ objectKinds[name] = 'map';
680
+ visualizations.push({
681
+ name,
682
+ kind: 'map',
683
+ entries: serializedValue.entries
684
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
685
+ .map((entry) => ({
686
+ key: entry[0],
687
+ value: entry[1],
688
+ })),
689
+ });
690
+ continue;
691
+ }
692
+
693
+ if (serializedValue.__type__ === 'set' && Array.isArray(serializedValue.values)) {
694
+ objectKinds[name] = 'set';
695
+ visualizations.push({
696
+ name,
697
+ kind: 'set',
698
+ entries: serializedValue.values.map((item) => ({
699
+ key: item,
700
+ value: true,
701
+ })),
702
+ });
703
+ continue;
704
+ }
705
+
706
+ if (isLikelyTreeObject(serializedValue)) {
707
+ objectKinds[name] = 'tree';
708
+ continue;
709
+ }
710
+
711
+ if (isLikelyLinkedListObject(serializedValue)) {
712
+ objectKinds[name] = 'linked-list';
713
+ continue;
714
+ }
715
+
716
+ if (isLikelyAdjacencyListObject(serializedValue)) {
717
+ objectKinds[name] = 'graph-adjacency';
718
+ continue;
719
+ }
720
+
721
+ if (shouldVisualizeObjectAsHashMap(name, serializedValue)) {
722
+ objectKinds[name] = 'hashmap';
723
+ visualizations.push({
724
+ name,
725
+ kind: 'hashmap',
726
+ entries: Object.entries(serializedValue).map(([key, entryValue]) => ({
727
+ key,
728
+ value: serializeValue(entryValue),
729
+ })),
730
+ });
731
+ }
732
+ }
733
+ }
734
+
735
+ return { hashMaps: visualizations, objectKinds };
736
+ }
737
+
738
+ function buildVisualizationPayload(value) {
739
+ const { hashMaps, objectKinds } = collectRuntimeVisualizations(value);
740
+ if (hashMaps.length === 0 && Object.keys(objectKinds).length === 0) return undefined;
741
+ return {
742
+ ...(hashMaps.length > 0 ? { hashMaps } : {}),
743
+ ...(Object.keys(objectKinds).length > 0 ? { objectKinds } : {}),
744
+ };
745
+ }
746
+
747
+ function createLimitError(reason, lineNumber, message) {
748
+ const error = new Error(message);
749
+ error.__traceLimitExceeded = true;
750
+ error.__timeoutReason = reason;
751
+ error.__traceLine = lineNumber;
752
+ return error;
753
+ }
754
+
755
+ function getCurrentFrameId() {
756
+ return callStack[callStack.length - 1]?.id;
757
+ }
758
+
759
+ function flushPendingAccesses(frameId) {
760
+ if (frameId === undefined || frameId === null) {
761
+ return undefined;
762
+ }
763
+ const pending = pendingAccessesByFrame.get(frameId);
764
+ if (!Array.isArray(pending) || pending.length === 0) {
765
+ return undefined;
766
+ }
767
+ pendingAccessesByFrame.delete(frameId);
768
+ return pending.map((access) => ({
769
+ variable: access.variable,
770
+ kind: access.kind,
771
+ ...(Array.isArray(access.indices) && access.indices.length > 0
772
+ ? { indices: access.indices }
773
+ : {}),
774
+ ...(access.method ? { method: access.method } : {}),
775
+ ...(access.pathDepth ? { pathDepth: access.pathDepth } : {}),
776
+ }));
777
+ }
778
+
779
+ function appendTrace(step, frameId = getCurrentFrameId()) {
780
+ if (trace.length >= maxTraceSteps) {
781
+ const lineNumber = normalizeLine(step?.line, 1);
782
+ if (!traceLimitExceeded) {
783
+ traceLimitExceeded = true;
784
+ timeoutReason = 'trace-limit';
785
+ }
786
+ if (!timeoutRecorded && trace.length < maxTraceSteps) {
787
+ trace.push({
788
+ line: lineNumber,
789
+ event: 'timeout',
790
+ variables: {},
791
+ function: step?.function ?? callStack[callStack.length - 1]?.function ?? '<module>',
792
+ callStack: snapshotCallStack(),
793
+ });
794
+ timeoutRecorded = true;
795
+ }
796
+ throw createLimitError('trace-limit', lineNumber, `Exceeded ${maxTraceSteps} trace steps`);
797
+ }
798
+ const accesses = flushPendingAccesses(frameId);
799
+ trace.push({
800
+ ...step,
801
+ ...(accesses ? { accesses } : {}),
802
+ });
803
+ }
804
+
805
+ function markTimeout(reason, lineNumber, message) {
806
+ const normalizedLine = normalizeLine(lineNumber, 1);
807
+ if (!traceLimitExceeded) {
808
+ traceLimitExceeded = true;
809
+ timeoutReason = reason;
810
+ }
811
+ if (!timeoutRecorded && trace.length < maxTraceSteps) {
812
+ appendTrace({
813
+ line: normalizedLine,
814
+ event: 'timeout',
815
+ variables: {},
816
+ function: callStack[callStack.length - 1]?.function ?? '<module>',
817
+ callStack: snapshotCallStack(),
818
+ });
819
+ timeoutRecorded = true;
820
+ }
821
+ throw createLimitError(reason, normalizedLine, message);
822
+ }
823
+
824
+ function alignCallStackForLine(functionName, lineNumber, functionStartLine, inferredArgs = {}) {
825
+ const normalizedFunctionName =
826
+ typeof functionName === 'string' && functionName.length > 0 ? functionName : '<module>';
827
+
828
+ if (normalizedFunctionName === '<module>') {
829
+ if (callStack.length === 0) {
830
+ const moduleFrame = {
831
+ id: nextFrameId++,
832
+ function: '<module>',
833
+ args: sanitizeVariables(inferredArgs),
834
+ line: lineNumber,
835
+ };
836
+ callStack.push(moduleFrame);
837
+ } else {
838
+ const topFrame = callStack[callStack.length - 1];
839
+ if (
840
+ topFrame?.function === '<module>' &&
841
+ Object.keys(topFrame.args ?? {}).length === 0 &&
842
+ inferredArgs &&
843
+ typeof inferredArgs === 'object'
844
+ ) {
845
+ topFrame.args = sanitizeVariables(inferredArgs);
846
+ }
847
+ }
848
+
849
+ while (callStack.length > 1) {
850
+ callStack.pop();
851
+ }
852
+ return '<module>';
853
+ }
854
+
855
+ const topFrame = callStack[callStack.length - 1];
856
+ if (!topFrame || topFrame.function !== normalizedFunctionName) {
857
+ const callLine = normalizeLine(functionStartLine, lineNumber);
858
+ const inferredFrame = {
859
+ id: nextFrameId++,
860
+ function: normalizedFunctionName,
861
+ args: sanitizeVariables(inferredArgs),
862
+ line: callLine,
863
+ };
864
+ callStack.push(inferredFrame);
865
+ appendTrace({
866
+ line: callLine,
867
+ event: 'call',
868
+ variables: inferredFrame.args,
869
+ function: normalizedFunctionName,
870
+ callStack: snapshotCallStack(),
871
+ visualization: buildVisualizationPayload(inferredArgs),
872
+ });
873
+ } else if (
874
+ topFrame.function === normalizedFunctionName &&
875
+ Object.keys(topFrame.args ?? {}).length === 0 &&
876
+ inferredArgs &&
877
+ typeof inferredArgs === 'object'
878
+ ) {
879
+ topFrame.args = sanitizeVariables(inferredArgs);
880
+ }
881
+
882
+ return normalizedFunctionName;
883
+ }
884
+
885
+ return {
886
+ serialize(value) {
887
+ return serializeValue(value, 0, new WeakSet(), stableNodeRefState);
888
+ },
889
+ read(getter) {
890
+ try {
891
+ return getter();
892
+ } catch {
893
+ return undefined;
894
+ }
895
+ },
896
+ pushCall(functionName, args, lineNumber) {
897
+ const normalizedLine = normalizeLine(lineNumber, 1);
898
+ const normalizedArgs = sanitizeVariables(args);
899
+ if (callStack.length + 1 > maxCallDepth) {
900
+ markTimeout(
901
+ 'recursion-limit',
902
+ normalizedLine,
903
+ `Exceeded max call depth (${maxCallDepth})`
904
+ );
905
+ }
906
+ const frame = {
907
+ id: nextFrameId++,
908
+ function: functionName || '<module>',
909
+ args: normalizedArgs,
910
+ line: normalizedLine,
911
+ };
912
+ callStack.push(frame);
913
+ appendTrace({
914
+ line: normalizedLine,
915
+ event: 'call',
916
+ variables: normalizedArgs,
917
+ function: frame.function,
918
+ callStack: snapshotCallStack(),
919
+ visualization: buildVisualizationPayload(args),
920
+ });
921
+ },
922
+ recordAccess(event) {
923
+ if (!event || typeof event !== 'object') {
924
+ return;
925
+ }
926
+ const variable =
927
+ typeof event.variable === 'string' && event.variable.length > 0 ? event.variable : null;
928
+ const kind = typeof event.kind === 'string' ? event.kind : null;
929
+ if (!variable || !kind) {
930
+ return;
931
+ }
932
+
933
+ const frameId = getCurrentFrameId();
934
+ if (frameId === undefined) {
935
+ return;
936
+ }
937
+
938
+ const normalized = {
939
+ variable,
940
+ kind,
941
+ ...(Array.isArray(event.indices) && event.indices.length > 0
942
+ ? { indices: event.indices.map((index) => Math.trunc(index)) }
943
+ : {}),
944
+ ...(typeof event.method === 'string' && event.method.length > 0
945
+ ? { method: event.method }
946
+ : {}),
947
+ ...(event.pathDepth === 1 || event.pathDepth === 2 ? { pathDepth: event.pathDepth } : {}),
948
+ };
949
+
950
+ const existing = pendingAccessesByFrame.get(frameId) ?? [];
951
+ existing.push(normalized);
952
+ pendingAccessesByFrame.set(frameId, existing);
953
+ },
954
+ line(lineNumber, snapshotFactory, functionNameOverride, functionStartLine) {
955
+ const normalizedLine = normalizeLine(lineNumber, callStack[callStack.length - 1]?.line ?? 1);
956
+
957
+ lineEventCount += 1;
958
+ if (lineEventCount > maxLineEvents) {
959
+ markTimeout('line-limit', normalizedLine, `Exceeded ${maxLineEvents} line events`);
960
+ }
961
+
962
+ const nextLineHits = (lineHitCount.get(normalizedLine) ?? 0) + 1;
963
+ lineHitCount.set(normalizedLine, nextLineHits);
964
+ if (nextLineHits > maxSingleLineHits) {
965
+ markTimeout(
966
+ 'single-line-limit',
967
+ normalizedLine,
968
+ `Line ${normalizedLine} exceeded ${maxSingleLineHits} hits`
969
+ );
970
+ }
971
+
972
+ let variables = {};
973
+ let visualization;
974
+ if (typeof snapshotFactory === 'function') {
975
+ try {
976
+ const snapshot = snapshotFactory();
977
+ variables = sanitizeVariables(snapshot);
978
+ visualization = buildVisualizationPayload(snapshot);
979
+ } catch {
980
+ variables = {};
981
+ visualization = undefined;
982
+ }
983
+ }
984
+
985
+ const traceFunctionName = alignCallStackForLine(
986
+ functionNameOverride,
987
+ normalizedLine,
988
+ functionStartLine,
989
+ variables
990
+ );
991
+
992
+ appendTrace({
993
+ line: normalizedLine,
994
+ event: 'line',
995
+ variables,
996
+ function: traceFunctionName,
997
+ callStack: snapshotCallStack(),
998
+ visualization,
999
+ });
1000
+ },
1001
+ recordReturn(lineNumber, returnValue, functionNameOverride) {
1002
+ const normalizedLine = normalizeLine(lineNumber, callStack[callStack.length - 1]?.line ?? 1);
1003
+ const functionName =
1004
+ typeof functionNameOverride === 'string' && functionNameOverride.length > 0
1005
+ ? functionNameOverride
1006
+ : callStack[callStack.length - 1]?.function ?? '<module>';
1007
+ const serializedReturnValue = serializeValue(returnValue);
1008
+ const variables = functionName === '<module>' ? { result: serializedReturnValue } : {};
1009
+ const visualization = functionName === '<module>'
1010
+ ? buildVisualizationPayload({ result: returnValue })
1011
+ : undefined;
1012
+
1013
+ appendTrace({
1014
+ line: normalizedLine,
1015
+ event: 'return',
1016
+ variables,
1017
+ function: functionName,
1018
+ callStack: snapshotCallStack(),
1019
+ returnValue: serializedReturnValue,
1020
+ visualization,
1021
+ });
1022
+ },
1023
+ recordException(lineNumber, error, functionNameOverride) {
1024
+ const normalizedLine = normalizeLine(lineNumber, callStack[callStack.length - 1]?.line ?? 1);
1025
+ appendTrace({
1026
+ line: normalizedLine,
1027
+ event: 'exception',
1028
+ variables: {},
1029
+ function:
1030
+ typeof functionNameOverride === 'string' && functionNameOverride.length > 0
1031
+ ? functionNameOverride
1032
+ : callStack[callStack.length - 1]?.function ?? '<module>',
1033
+ callStack: snapshotCallStack(),
1034
+ returnValue: error instanceof Error ? error.message : String(error),
1035
+ });
1036
+ },
1037
+ popCall() {
1038
+ if (callStack.length > 0) {
1039
+ const frame = callStack.pop();
1040
+ if (frame?.id !== undefined) {
1041
+ pendingAccessesByFrame.delete(frame.id);
1042
+ }
1043
+ }
1044
+ },
1045
+ popToFunction(functionName) {
1046
+ const target = typeof functionName === 'string' && functionName.length > 0 ? functionName : '<module>';
1047
+ while (callStack.length > 1 && callStack[callStack.length - 1]?.function !== target) {
1048
+ const frame = callStack.pop();
1049
+ if (frame?.id !== undefined) {
1050
+ pendingAccessesByFrame.delete(frame.id);
1051
+ }
1052
+ }
1053
+ },
1054
+ getTrace() {
1055
+ return trace;
1056
+ },
1057
+ getLineEventCount() {
1058
+ return lineEventCount;
1059
+ },
1060
+ getTraceStepCount() {
1061
+ return trace.length;
1062
+ },
1063
+ isTraceLimitExceeded() {
1064
+ return traceLimitExceeded;
1065
+ },
1066
+ getTimeoutReason() {
1067
+ return timeoutReason;
1068
+ },
1069
+ };
1070
+ }
1071
+
1072
+ function escapeRegExp(value) {
1073
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1074
+ }
1075
+
1076
+ function getExecutableLineNumbers(code) {
1077
+ if (typeof code !== 'string') return [];
1078
+ const lines = code.split('\n');
1079
+ const executable = [];
1080
+
1081
+ for (let index = 0; index < lines.length; index += 1) {
1082
+ const trimmed = lines[index].trim();
1083
+ if (trimmed.length === 0) continue;
1084
+ if (trimmed.startsWith('//')) continue;
1085
+ if (trimmed === '/*' || trimmed === '*/' || trimmed.startsWith('*')) continue;
1086
+ executable.push(index + 1);
1087
+ }
1088
+
1089
+ return executable;
1090
+ }
1091
+
1092
+ function findFunctionStartLine(code, functionName, executionStyle) {
1093
+ if (typeof code !== 'string' || typeof functionName !== 'string' || functionName.length === 0) {
1094
+ return null;
1095
+ }
1096
+
1097
+ const escapedName = escapeRegExp(functionName);
1098
+ const declarationPattern = new RegExp(`\\bfunction\\s+${escapedName}\\s*\\(`);
1099
+ const assignmentPattern = new RegExp(`\\b(?:const|let|var)\\s+${escapedName}\\s*=`);
1100
+ const classPattern = new RegExp(`\\bclass\\s+${escapedName}\\b`);
1101
+ const methodPattern = new RegExp(`\\b${escapedName}\\s*\\(`);
1102
+ const lines = code.split('\n');
1103
+
1104
+ for (let index = 0; index < lines.length; index += 1) {
1105
+ const line = lines[index];
1106
+ if (declarationPattern.test(line) || assignmentPattern.test(line)) {
1107
+ return index + 1;
1108
+ }
1109
+ if (executionStyle === 'ops-class' && classPattern.test(line)) {
1110
+ return index + 1;
1111
+ }
1112
+ if (executionStyle === 'solution-method' && methodPattern.test(line)) {
1113
+ return index + 1;
1114
+ }
1115
+ }
1116
+
1117
+ return null;
1118
+ }
1119
+
1120
+ function findFunctionEndLine(code, startLine) {
1121
+ if (typeof code !== 'string' || !Number.isFinite(startLine) || startLine <= 0) return null;
1122
+
1123
+ const lines = code.split('\n');
1124
+ let braceBalance = 0;
1125
+ let opened = false;
1126
+
1127
+ for (let lineIndex = startLine - 1; lineIndex < lines.length; lineIndex += 1) {
1128
+ const line = lines[lineIndex];
1129
+ for (let charIndex = 0; charIndex < line.length; charIndex += 1) {
1130
+ const char = line[charIndex];
1131
+ if (char === '{') {
1132
+ braceBalance += 1;
1133
+ opened = true;
1134
+ } else if (char === '}') {
1135
+ braceBalance -= 1;
1136
+ if (opened && braceBalance <= 0) {
1137
+ return lineIndex + 1;
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+
1143
+ return null;
1144
+ }
1145
+
1146
+ function determineTraceLineBounds(code, functionName, executionStyle) {
1147
+ const executableLines = getExecutableLineNumbers(code);
1148
+ if (executableLines.length === 0) {
1149
+ return { startLine: 1, endLine: 1 };
1150
+ }
1151
+
1152
+ const hasNamedFunction = typeof functionName === 'string' && functionName.length > 0;
1153
+ if (!hasNamedFunction) {
1154
+ const firstExecutable = executableLines[0];
1155
+ const lastExecutable = executableLines[executableLines.length - 1];
1156
+ return { startLine: firstExecutable, endLine: lastExecutable };
1157
+ }
1158
+
1159
+ const defaultStart = executableLines[0];
1160
+ const defaultEnd = executableLines[executableLines.length - 1];
1161
+ const startLine = findFunctionStartLine(code, functionName, executionStyle) ?? defaultStart;
1162
+ const endLine = findFunctionEndLine(code, startLine) ?? defaultEnd;
1163
+ return { startLine, endLine };
1164
+ }
1165
+
1166
+ function createSyntheticTrace(payload, codeResult) {
1167
+ const { code, functionName, inputs, executionStyle = 'function' } = payload ?? {};
1168
+ const { startLine, endLine } = determineTraceLineBounds(code, functionName, executionStyle);
1169
+ const traceFunctionName =
1170
+ typeof functionName === 'string' && functionName.length > 0 ? functionName : '<module>';
1171
+
1172
+ const normalizedInputs = normalizeInputs(inputs);
1173
+ const inputSnapshot = {};
1174
+ for (const [key, value] of Object.entries(normalizedInputs)) {
1175
+ inputSnapshot[key] = serializeValue(value);
1176
+ }
1177
+
1178
+ const callFrame = {
1179
+ function: traceFunctionName,
1180
+ args: inputSnapshot,
1181
+ line: startLine,
1182
+ };
1183
+
1184
+ const returnVariables = { ...inputSnapshot };
1185
+ if (traceFunctionName === '<module>') {
1186
+ returnVariables.result = codeResult.output;
1187
+ }
1188
+
1189
+ return [
1190
+ {
1191
+ line: startLine,
1192
+ event: 'call',
1193
+ variables: inputSnapshot,
1194
+ function: traceFunctionName,
1195
+ callStack: [callFrame],
1196
+ },
1197
+ {
1198
+ line: startLine,
1199
+ event: 'line',
1200
+ variables: inputSnapshot,
1201
+ function: traceFunctionName,
1202
+ callStack: [callFrame],
1203
+ },
1204
+ {
1205
+ line: endLine,
1206
+ event: 'return',
1207
+ variables: returnVariables,
1208
+ function: traceFunctionName,
1209
+ callStack: [callFrame],
1210
+ returnValue: codeResult.output,
1211
+ stdoutLineCount: Array.isArray(codeResult.consoleOutput) ? codeResult.consoleOutput.length : 0,
1212
+ },
1213
+ ];
1214
+ }
1215
+
1216
+ function getTypeScriptCompiler() {
1217
+ const ts = self?.ts;
1218
+ if (ts && typeof ts.transpileModule === 'function') {
1219
+ return ts;
1220
+ }
1221
+ return null;
1222
+ }
1223
+
1224
+ async function ensureTypeScriptCompiler() {
1225
+ if (getTypeScriptCompiler()) return;
1226
+ if (typeScriptLoadPromise) return typeScriptLoadPromise;
1227
+
1228
+ typeScriptLoadPromise = (async () => {
1229
+ if (typeof importScripts !== 'function') {
1230
+ throw new Error('TypeScript compiler is unavailable in this environment.');
1231
+ }
1232
+
1233
+ const errors = [];
1234
+ for (const compilerUrl of TYPESCRIPT_COMPILER_URLS) {
1235
+ try {
1236
+ importScripts(compilerUrl);
1237
+ if (getTypeScriptCompiler()) {
1238
+ return;
1239
+ }
1240
+ errors.push(`${compilerUrl} (loaded but compiler object was missing)`);
1241
+ } catch (error) {
1242
+ const message = error instanceof Error ? error.message : String(error);
1243
+ errors.push(`${compilerUrl} (${message})`);
1244
+ }
1245
+ }
1246
+
1247
+ throw new Error(`Unable to load TypeScript compiler. Tried: ${errors.join(' | ')}`);
1248
+ })();
1249
+
1250
+ try {
1251
+ await typeScriptLoadPromise;
1252
+ } catch (error) {
1253
+ typeScriptLoadPromise = null;
1254
+ throw error;
1255
+ }
1256
+ }
1257
+
1258
+ function transpileTypeScript(sourceCode) {
1259
+ const ts = getTypeScriptCompiler();
1260
+ if (!ts) {
1261
+ throw new Error('TypeScript compiler failed to initialize.');
1262
+ }
1263
+
1264
+ const transpileInput = `${sourceCode}\n\n${TYPESCRIPT_RUNTIME_DECLARATIONS}\n`;
1265
+ const transpiled = ts.transpileModule(transpileInput, {
1266
+ compilerOptions: {
1267
+ target: ts.ScriptTarget.ES2020,
1268
+ module: ts.ModuleKind.None,
1269
+ strict: false,
1270
+ esModuleInterop: true,
1271
+ },
1272
+ reportDiagnostics: true,
1273
+ fileName: 'solution.ts',
1274
+ });
1275
+
1276
+ const diagnostics = Array.isArray(transpiled.diagnostics) ? transpiled.diagnostics : [];
1277
+ const errors = diagnostics.filter((diag) => diag.category === ts.DiagnosticCategory.Error);
1278
+ if (errors.length > 0) {
1279
+ const first = errors[0];
1280
+ const messageText = ts.flattenDiagnosticMessageText(first.messageText, '\n');
1281
+ let lineNumber;
1282
+ if (first.file && typeof first.start === 'number') {
1283
+ const position = first.file.getLineAndCharacterOfPosition(first.start);
1284
+ lineNumber = position.line + 1;
1285
+ }
1286
+ const error = new Error(
1287
+ lineNumber ? `TypeScript transpilation failed (line ${lineNumber}): ${messageText}` : `TypeScript transpilation failed: ${messageText}`
1288
+ );
1289
+ if (lineNumber) {
1290
+ error.__tracecodeLine = lineNumber;
1291
+ }
1292
+ throw error;
1293
+ }
1294
+
1295
+ return transpiled.outputText;
1296
+ }
1297
+
1298
+ async function prepareExecutableCode(sourceCode, language) {
1299
+ if (language === 'typescript') {
1300
+ await ensureTypeScriptCompiler();
1301
+ return transpileTypeScript(sourceCode);
1302
+ }
1303
+
1304
+ return sourceCode;
1305
+ }
1306
+
1307
+ function addBindingNames(ts, nameNode, names) {
1308
+ if (!nameNode) return;
1309
+ if (ts.isIdentifier(nameNode)) {
1310
+ if (!nameNode.text.startsWith('__trace')) {
1311
+ names.add(nameNode.text);
1312
+ }
1313
+ return;
1314
+ }
1315
+ if (ts.isObjectBindingPattern(nameNode) || ts.isArrayBindingPattern(nameNode)) {
1316
+ for (const element of nameNode.elements) {
1317
+ if (ts.isBindingElement(element)) {
1318
+ addBindingNames(ts, element.name, names);
1319
+ }
1320
+ }
1321
+ }
1322
+ }
1323
+
1324
+ function collectTraceVariableNames(ts, sourceFile) {
1325
+ const names = new Set();
1326
+
1327
+ function visit(node) {
1328
+ if (ts.isVariableDeclaration(node)) {
1329
+ addBindingNames(ts, node.name, names);
1330
+ } else if (ts.isParameter(node)) {
1331
+ addBindingNames(ts, node.name, names);
1332
+ } else if (ts.isCatchClause(node) && node.variableDeclaration) {
1333
+ addBindingNames(ts, node.variableDeclaration.name, names);
1334
+ } else if (
1335
+ ts.isBinaryExpression(node) &&
1336
+ node.left &&
1337
+ ts.isIdentifier(node.left) &&
1338
+ node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
1339
+ node.operatorToken.kind <= ts.SyntaxKind.LastAssignment
1340
+ ) {
1341
+ names.add(node.left.text);
1342
+ }
1343
+ ts.forEachChild(node, visit);
1344
+ }
1345
+
1346
+ visit(sourceFile);
1347
+ return [...names];
1348
+ }
1349
+
1350
+ function shouldTraceStatement(ts, statement) {
1351
+ return !(
1352
+ ts.isFunctionDeclaration(statement) ||
1353
+ ts.isClassDeclaration(statement) ||
1354
+ ts.isEmptyStatement(statement) ||
1355
+ ts.isBlock(statement)
1356
+ );
1357
+ }
1358
+
1359
+ function getNodeNameText(ts, nameNode) {
1360
+ if (!nameNode) return null;
1361
+ if (ts.isIdentifier(nameNode) || ts.isPrivateIdentifier(nameNode)) {
1362
+ return nameNode.text;
1363
+ }
1364
+ if (ts.isStringLiteral(nameNode) || ts.isNumericLiteral(nameNode)) {
1365
+ return String(nameNode.text);
1366
+ }
1367
+ return null;
1368
+ }
1369
+
1370
+ function inferTraceFunctionName(ts, node, fallbackFunctionName) {
1371
+ if (
1372
+ (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) &&
1373
+ node.name &&
1374
+ ts.isIdentifier(node.name)
1375
+ ) {
1376
+ return node.name.text;
1377
+ }
1378
+
1379
+ if (ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node)) {
1380
+ return getNodeNameText(ts, node.name) || fallbackFunctionName;
1381
+ }
1382
+
1383
+ if (ts.isConstructorDeclaration(node)) {
1384
+ const className =
1385
+ node.parent && ts.isClassLike(node.parent) && node.parent.name && ts.isIdentifier(node.parent.name)
1386
+ ? node.parent.name.text
1387
+ : null;
1388
+ return className ? `${className}.constructor` : 'constructor';
1389
+ }
1390
+
1391
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
1392
+ const parent = node.parent;
1393
+ if (parent && ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
1394
+ return parent.name.text;
1395
+ }
1396
+ if (parent && ts.isPropertyAssignment(parent)) {
1397
+ return getNodeNameText(ts, parent.name) || fallbackFunctionName;
1398
+ }
1399
+ if (parent && ts.isBinaryExpression(parent) && ts.isIdentifier(parent.left)) {
1400
+ return parent.left.text;
1401
+ }
1402
+ }
1403
+
1404
+ return fallbackFunctionName;
1405
+ }
1406
+
1407
+ function buildLineFunctionMap(ts, sourceFile, defaultFunctionName) {
1408
+ const lineFunctionMap = new Map();
1409
+
1410
+ function mapStatementLine(statementNode, functionName, functionStartLine) {
1411
+ const lineNumber = sourceFile.getLineAndCharacterOfPosition(statementNode.getStart(sourceFile)).line + 1;
1412
+ if (!lineFunctionMap.has(lineNumber)) {
1413
+ lineFunctionMap.set(lineNumber, {
1414
+ functionName,
1415
+ functionStartLine,
1416
+ });
1417
+ }
1418
+ }
1419
+
1420
+ function visitNode(node, currentFunctionName, currentFunctionStartLine) {
1421
+ const nextFunctionName = ts.isFunctionLike(node)
1422
+ ? inferTraceFunctionName(ts, node, currentFunctionName)
1423
+ : currentFunctionName;
1424
+ const nextFunctionStartLine = ts.isFunctionLike(node)
1425
+ ? sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1
1426
+ : currentFunctionStartLine;
1427
+
1428
+ if (ts.isSourceFile(node) || ts.isBlock(node)) {
1429
+ for (const statement of node.statements) {
1430
+ visitNode(statement, currentFunctionName, currentFunctionStartLine);
1431
+ }
1432
+ return;
1433
+ }
1434
+
1435
+ if (ts.isCaseClause(node) || ts.isDefaultClause(node)) {
1436
+ for (const statement of node.statements) {
1437
+ visitNode(statement, currentFunctionName, currentFunctionStartLine);
1438
+ }
1439
+ return;
1440
+ }
1441
+
1442
+ if (ts.isFunctionLike(node)) {
1443
+ if (node.body) {
1444
+ visitNode(node.body, nextFunctionName, nextFunctionStartLine);
1445
+ }
1446
+ return;
1447
+ }
1448
+
1449
+ if (ts.isStatement(node) && shouldTraceStatement(ts, node)) {
1450
+ mapStatementLine(node, currentFunctionName, currentFunctionStartLine);
1451
+ }
1452
+
1453
+ ts.forEachChild(node, (child) => visitNode(child, currentFunctionName, currentFunctionStartLine));
1454
+ }
1455
+
1456
+ visitNode(sourceFile, defaultFunctionName, 1);
1457
+ return lineFunctionMap;
1458
+ }
1459
+
1460
+ function unwrapParenthesizedExpression(ts, node) {
1461
+ let current = node;
1462
+ while (current && ts.isParenthesizedExpression(current)) {
1463
+ current = current.expression;
1464
+ }
1465
+ return current;
1466
+ }
1467
+
1468
+ function isAssignmentOperatorToken(ts, tokenKind) {
1469
+ return tokenKind >= ts.SyntaxKind.FirstAssignment && tokenKind <= ts.SyntaxKind.LastAssignment;
1470
+ }
1471
+
1472
+ function isAssignmentLikeLeftOperand(ts, node) {
1473
+ const parent = node?.parent;
1474
+ return Boolean(
1475
+ parent &&
1476
+ ts.isBinaryExpression(parent) &&
1477
+ parent.left === node &&
1478
+ isAssignmentOperatorToken(ts, parent.operatorToken.kind)
1479
+ );
1480
+ }
1481
+
1482
+ function isUpdateExpressionOperand(ts, node) {
1483
+ const parent = node?.parent;
1484
+ if (!parent) return false;
1485
+
1486
+ if (ts.isPrefixUnaryExpression(parent)) {
1487
+ return (
1488
+ parent.operand === node &&
1489
+ (parent.operator === ts.SyntaxKind.PlusPlusToken ||
1490
+ parent.operator === ts.SyntaxKind.MinusMinusToken)
1491
+ );
1492
+ }
1493
+
1494
+ if (ts.isPostfixUnaryExpression(parent)) {
1495
+ return (
1496
+ parent.operand === node &&
1497
+ (parent.operator === ts.SyntaxKind.PlusPlusToken ||
1498
+ parent.operator === ts.SyntaxKind.MinusMinusToken)
1499
+ );
1500
+ }
1501
+
1502
+ return false;
1503
+ }
1504
+
1505
+ function isDestructuringAssignmentTarget(ts, node) {
1506
+ let current = node;
1507
+ let parent = node?.parent;
1508
+
1509
+ while (
1510
+ parent &&
1511
+ (ts.isArrayLiteralExpression(parent) ||
1512
+ ts.isObjectLiteralExpression(parent) ||
1513
+ ts.isPropertyAssignment(parent) ||
1514
+ ts.isShorthandPropertyAssignment(parent))
1515
+ ) {
1516
+ current = parent;
1517
+ parent = parent.parent;
1518
+ }
1519
+
1520
+ return Boolean(
1521
+ parent &&
1522
+ ts.isBinaryExpression(parent) &&
1523
+ parent.left === current &&
1524
+ isAssignmentOperatorToken(ts, parent.operatorToken.kind)
1525
+ );
1526
+ }
1527
+
1528
+ function isNestedElementAccessExpression(ts, node) {
1529
+ const parent = node?.parent;
1530
+ return Boolean(parent && ts.isElementAccessExpression(parent) && parent.expression === node);
1531
+ }
1532
+
1533
+ function extractTraceableElementAccess(ts, node) {
1534
+ const indices = [];
1535
+ let current = unwrapParenthesizedExpression(ts, node);
1536
+
1537
+ while (current && ts.isElementAccessExpression(current) && indices.length < 3) {
1538
+ indices.unshift(current.argumentExpression);
1539
+ current = unwrapParenthesizedExpression(ts, current.expression);
1540
+ }
1541
+
1542
+ if (!current || !ts.isIdentifier(current) || indices.length === 0 || indices.length > 2) {
1543
+ return null;
1544
+ }
1545
+
1546
+ return {
1547
+ variableName: current.text,
1548
+ indices,
1549
+ };
1550
+ }
1551
+
1552
+ function extractTraceableMutatingCall(ts, node) {
1553
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) {
1554
+ return null;
1555
+ }
1556
+
1557
+ const receiver = unwrapParenthesizedExpression(ts, node.expression.expression);
1558
+ const methodName = node.expression.name.text;
1559
+ if (!receiver || !ts.isIdentifier(receiver) || !isTraceableMutatingMethod(methodName)) {
1560
+ return null;
1561
+ }
1562
+
1563
+ return {
1564
+ variableName: receiver.text,
1565
+ methodName,
1566
+ };
1567
+ }
1568
+
1569
+ function getCompoundAssignmentOperatorName(ts, tokenKind) {
1570
+ switch (tokenKind) {
1571
+ case ts.SyntaxKind.PlusEqualsToken:
1572
+ return 'add';
1573
+ case ts.SyntaxKind.MinusEqualsToken:
1574
+ return 'sub';
1575
+ case ts.SyntaxKind.AsteriskEqualsToken:
1576
+ return 'mul';
1577
+ case ts.SyntaxKind.SlashEqualsToken:
1578
+ return 'div';
1579
+ case ts.SyntaxKind.PercentEqualsToken:
1580
+ return 'mod';
1581
+ case ts.SyntaxKind.AsteriskAsteriskEqualsToken:
1582
+ return 'pow';
1583
+ case ts.SyntaxKind.LessThanLessThanEqualsToken:
1584
+ return 'lshift';
1585
+ case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken:
1586
+ return 'rshift';
1587
+ case ts.SyntaxKind.AmpersandEqualsToken:
1588
+ return 'bitand';
1589
+ case ts.SyntaxKind.BarEqualsToken:
1590
+ return 'bitor';
1591
+ case ts.SyntaxKind.CaretEqualsToken:
1592
+ return 'bitxor';
1593
+ default:
1594
+ return null;
1595
+ }
1596
+ }
1597
+
1598
+ function createIndicesArrayExpression(ts, indices) {
1599
+ return ts.factory.createArrayLiteralExpression(indices, false);
1600
+ }
1601
+
1602
+ function createTraceReadIndexExpression(ts, variableName, indices) {
1603
+ return ts.factory.createCallExpression(ts.factory.createIdentifier('__traceReadIndex'), undefined, [
1604
+ ts.factory.createStringLiteral(variableName),
1605
+ ts.factory.createIdentifier(variableName),
1606
+ createIndicesArrayExpression(ts, indices),
1607
+ ]);
1608
+ }
1609
+
1610
+ function createTraceWriteIndexExpression(ts, variableName, indices, value) {
1611
+ return ts.factory.createCallExpression(ts.factory.createIdentifier('__traceWriteIndex'), undefined, [
1612
+ ts.factory.createStringLiteral(variableName),
1613
+ ts.factory.createIdentifier(variableName),
1614
+ createIndicesArrayExpression(ts, indices),
1615
+ value,
1616
+ ]);
1617
+ }
1618
+
1619
+ function createTraceAugAssignExpression(ts, variableName, indices, operatorName, rhs) {
1620
+ return ts.factory.createCallExpression(
1621
+ ts.factory.createIdentifier('__traceAugAssignIndex'),
1622
+ undefined,
1623
+ [
1624
+ ts.factory.createStringLiteral(variableName),
1625
+ ts.factory.createIdentifier(variableName),
1626
+ createIndicesArrayExpression(ts, indices),
1627
+ ts.factory.createStringLiteral(operatorName),
1628
+ rhs,
1629
+ ]
1630
+ );
1631
+ }
1632
+
1633
+ function createTraceUpdateExpression(ts, variableName, indices, operatorName, isPrefix) {
1634
+ return ts.factory.createCallExpression(
1635
+ ts.factory.createIdentifier('__traceUpdateIndex'),
1636
+ undefined,
1637
+ [
1638
+ ts.factory.createStringLiteral(variableName),
1639
+ ts.factory.createIdentifier(variableName),
1640
+ createIndicesArrayExpression(ts, indices),
1641
+ ts.factory.createStringLiteral(operatorName),
1642
+ isPrefix ? ts.factory.createTrue() : ts.factory.createFalse(),
1643
+ ]
1644
+ );
1645
+ }
1646
+
1647
+ function createTraceMutatingCallExpression(ts, variableName, methodName, args) {
1648
+ return ts.factory.createCallExpression(
1649
+ ts.factory.createIdentifier('__traceMutatingCall'),
1650
+ undefined,
1651
+ [
1652
+ ts.factory.createStringLiteral(variableName),
1653
+ ts.factory.createIdentifier(variableName),
1654
+ ts.factory.createStringLiteral(methodName),
1655
+ ...args,
1656
+ ]
1657
+ );
1658
+ }
1659
+
1660
+ function createSnapshotFactory(ts, variableNames) {
1661
+ const properties = variableNames.map((name) =>
1662
+ ts.factory.createPropertyAssignment(
1663
+ ts.factory.createIdentifier(name),
1664
+ ts.factory.createCallExpression(
1665
+ ts.factory.createPropertyAccessExpression(
1666
+ ts.factory.createIdentifier('__traceRecorder'),
1667
+ ts.factory.createIdentifier('read')
1668
+ ),
1669
+ undefined,
1670
+ [
1671
+ ts.factory.createArrowFunction(
1672
+ undefined,
1673
+ undefined,
1674
+ [],
1675
+ undefined,
1676
+ ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
1677
+ ts.factory.createIdentifier(name)
1678
+ ),
1679
+ ]
1680
+ )
1681
+ )
1682
+ );
1683
+
1684
+ return ts.factory.createArrowFunction(
1685
+ undefined,
1686
+ undefined,
1687
+ [],
1688
+ undefined,
1689
+ ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
1690
+ ts.factory.createParenthesizedExpression(
1691
+ ts.factory.createObjectLiteralExpression(properties, false)
1692
+ )
1693
+ );
1694
+ }
1695
+
1696
+ function createTraceLineStatement(ts, sourceFile, statement, variableNames, lineFunctionMap, defaultFunctionName) {
1697
+ const lineNumber = sourceFile.getLineAndCharacterOfPosition(statement.getStart(sourceFile)).line + 1;
1698
+ const functionContext = lineFunctionMap.get(lineNumber);
1699
+ const traceFunctionName = functionContext?.functionName ?? defaultFunctionName;
1700
+ const traceFunctionStartLine = functionContext?.functionStartLine ?? lineNumber;
1701
+ return ts.factory.createExpressionStatement(
1702
+ ts.factory.createCallExpression(
1703
+ ts.factory.createPropertyAccessExpression(
1704
+ ts.factory.createIdentifier('__traceRecorder'),
1705
+ ts.factory.createIdentifier('line')
1706
+ ),
1707
+ undefined,
1708
+ [
1709
+ ts.factory.createNumericLiteral(lineNumber),
1710
+ createSnapshotFactory(ts, variableNames),
1711
+ ts.factory.createStringLiteral(traceFunctionName),
1712
+ ts.factory.createNumericLiteral(traceFunctionStartLine),
1713
+ ]
1714
+ )
1715
+ );
1716
+ }
1717
+
1718
+ function instrumentStatementList(
1719
+ ts,
1720
+ sourceFile,
1721
+ originalStatements,
1722
+ visitedStatements,
1723
+ variableNames,
1724
+ lineFunctionMap,
1725
+ defaultFunctionName
1726
+ ) {
1727
+ const nextStatements = [];
1728
+ for (let index = 0; index < visitedStatements.length; index += 1) {
1729
+ const visitedStatement = visitedStatements[index];
1730
+ const originalStatement =
1731
+ originalStatements[index] ??
1732
+ ts.getOriginalNode(visitedStatement) ??
1733
+ visitedStatement;
1734
+ if (shouldTraceStatement(ts, visitedStatement)) {
1735
+ nextStatements.push(
1736
+ createTraceLineStatement(
1737
+ ts,
1738
+ sourceFile,
1739
+ originalStatement,
1740
+ variableNames,
1741
+ lineFunctionMap,
1742
+ defaultFunctionName
1743
+ )
1744
+ );
1745
+ }
1746
+ nextStatements.push(visitedStatement);
1747
+ }
1748
+ return ts.factory.createNodeArray(nextStatements);
1749
+ }
1750
+
1751
+ function collectFunctionParameterNames(ts, functionLikeNode) {
1752
+ const names = new Set();
1753
+ for (const parameter of functionLikeNode.parameters ?? []) {
1754
+ addBindingNames(ts, parameter.name, names);
1755
+ }
1756
+ return [...names];
1757
+ }
1758
+
1759
+ function createTraceRecorderStatement(ts, methodName, args) {
1760
+ return ts.factory.createExpressionStatement(
1761
+ ts.factory.createCallExpression(
1762
+ ts.factory.createPropertyAccessExpression(
1763
+ ts.factory.createIdentifier('__traceRecorder'),
1764
+ ts.factory.createIdentifier(methodName)
1765
+ ),
1766
+ undefined,
1767
+ args
1768
+ )
1769
+ );
1770
+ }
1771
+
1772
+ function createInstrumentedReturnBlock(ts, sourceFile, returnStatement, traceFunctionName) {
1773
+ const returnLine = sourceFile.getLineAndCharacterOfPosition(returnStatement.getStart(sourceFile)).line + 1;
1774
+ const capturedValueName = ts.factory.createUniqueName('__traceReturnValue');
1775
+ const returnValueInitializer = returnStatement.expression ?? ts.factory.createIdentifier('undefined');
1776
+
1777
+ return ts.factory.createBlock(
1778
+ [
1779
+ ts.factory.createVariableStatement(
1780
+ undefined,
1781
+ ts.factory.createVariableDeclarationList(
1782
+ [
1783
+ ts.factory.createVariableDeclaration(
1784
+ capturedValueName,
1785
+ undefined,
1786
+ undefined,
1787
+ returnValueInitializer
1788
+ ),
1789
+ ],
1790
+ ts.NodeFlags.Const
1791
+ )
1792
+ ),
1793
+ createTraceRecorderStatement(ts, 'recordReturn', [
1794
+ ts.factory.createNumericLiteral(returnLine),
1795
+ capturedValueName,
1796
+ ts.factory.createStringLiteral(traceFunctionName),
1797
+ ]),
1798
+ ts.factory.createReturnStatement(capturedValueName),
1799
+ ],
1800
+ true
1801
+ );
1802
+ }
1803
+
1804
+ function rewriteFunctionReturnStatements(ts, sourceFile, context, functionBody, traceFunctionName) {
1805
+ const rewrite = (node) => {
1806
+ // Nested functions should own their own return instrumentation.
1807
+ if (node !== functionBody && ts.isFunctionLike(node)) {
1808
+ return node;
1809
+ }
1810
+
1811
+ if (ts.isReturnStatement(node)) {
1812
+ return createInstrumentedReturnBlock(ts, sourceFile, node, traceFunctionName);
1813
+ }
1814
+
1815
+ return ts.visitEachChild(node, rewrite, context);
1816
+ };
1817
+
1818
+ return ts.visitEachChild(functionBody, rewrite, context);
1819
+ }
1820
+
1821
+ function updateFunctionLikeWithBody(ts, functionLikeNode, body) {
1822
+ if (ts.isFunctionDeclaration(functionLikeNode)) {
1823
+ return ts.factory.updateFunctionDeclaration(
1824
+ functionLikeNode,
1825
+ functionLikeNode.modifiers,
1826
+ functionLikeNode.asteriskToken,
1827
+ functionLikeNode.name,
1828
+ functionLikeNode.typeParameters,
1829
+ functionLikeNode.parameters,
1830
+ functionLikeNode.type,
1831
+ body
1832
+ );
1833
+ }
1834
+
1835
+ if (ts.isFunctionExpression(functionLikeNode)) {
1836
+ return ts.factory.updateFunctionExpression(
1837
+ functionLikeNode,
1838
+ functionLikeNode.modifiers,
1839
+ functionLikeNode.asteriskToken,
1840
+ functionLikeNode.name,
1841
+ functionLikeNode.typeParameters,
1842
+ functionLikeNode.parameters,
1843
+ functionLikeNode.type,
1844
+ body
1845
+ );
1846
+ }
1847
+
1848
+ if (ts.isArrowFunction(functionLikeNode)) {
1849
+ return ts.factory.updateArrowFunction(
1850
+ functionLikeNode,
1851
+ functionLikeNode.modifiers,
1852
+ functionLikeNode.typeParameters,
1853
+ functionLikeNode.parameters,
1854
+ functionLikeNode.type,
1855
+ functionLikeNode.equalsGreaterThanToken,
1856
+ body
1857
+ );
1858
+ }
1859
+
1860
+ if (ts.isMethodDeclaration(functionLikeNode)) {
1861
+ return ts.factory.updateMethodDeclaration(
1862
+ functionLikeNode,
1863
+ functionLikeNode.modifiers,
1864
+ functionLikeNode.asteriskToken,
1865
+ functionLikeNode.name,
1866
+ functionLikeNode.questionToken,
1867
+ functionLikeNode.typeParameters,
1868
+ functionLikeNode.parameters,
1869
+ functionLikeNode.type,
1870
+ body
1871
+ );
1872
+ }
1873
+
1874
+ if (ts.isConstructorDeclaration(functionLikeNode)) {
1875
+ return ts.factory.updateConstructorDeclaration(
1876
+ functionLikeNode,
1877
+ functionLikeNode.modifiers,
1878
+ functionLikeNode.parameters,
1879
+ body
1880
+ );
1881
+ }
1882
+
1883
+ if (ts.isGetAccessorDeclaration(functionLikeNode)) {
1884
+ return ts.factory.updateGetAccessorDeclaration(
1885
+ functionLikeNode,
1886
+ functionLikeNode.modifiers,
1887
+ functionLikeNode.name,
1888
+ functionLikeNode.parameters,
1889
+ functionLikeNode.type,
1890
+ body
1891
+ );
1892
+ }
1893
+
1894
+ if (ts.isSetAccessorDeclaration(functionLikeNode)) {
1895
+ return ts.factory.updateSetAccessorDeclaration(
1896
+ functionLikeNode,
1897
+ functionLikeNode.modifiers,
1898
+ functionLikeNode.name,
1899
+ functionLikeNode.parameters,
1900
+ body
1901
+ );
1902
+ }
1903
+
1904
+ return functionLikeNode;
1905
+ }
1906
+
1907
+ function wrapFunctionBodyForTracing(
1908
+ ts,
1909
+ sourceFile,
1910
+ context,
1911
+ functionLikeNode,
1912
+ functionBody,
1913
+ defaultFunctionName
1914
+ ) {
1915
+ const traceFunctionName = inferTraceFunctionName(ts, functionLikeNode, defaultFunctionName);
1916
+ const functionStartLine = sourceFile.getLineAndCharacterOfPosition(functionLikeNode.getStart(sourceFile)).line + 1;
1917
+ const functionEndPosition = Math.max(functionBody.getEnd() - 1, functionBody.getStart(sourceFile));
1918
+ const functionEndLine = sourceFile.getLineAndCharacterOfPosition(functionEndPosition).line + 1;
1919
+ const parameterNames = collectFunctionParameterNames(ts, functionLikeNode);
1920
+
1921
+ const rewrittenBody = rewriteFunctionReturnStatements(
1922
+ ts,
1923
+ sourceFile,
1924
+ context,
1925
+ functionBody,
1926
+ traceFunctionName
1927
+ );
1928
+
1929
+ const argsSnapshotExpression = ts.factory.createCallExpression(
1930
+ ts.factory.createParenthesizedExpression(createSnapshotFactory(ts, parameterNames)),
1931
+ undefined,
1932
+ []
1933
+ );
1934
+
1935
+ const wrappedBody = ts.factory.createBlock(
1936
+ [
1937
+ createTraceRecorderStatement(ts, 'pushCall', [
1938
+ ts.factory.createStringLiteral(traceFunctionName),
1939
+ argsSnapshotExpression,
1940
+ ts.factory.createNumericLiteral(functionStartLine),
1941
+ ]),
1942
+ ts.factory.createTryStatement(
1943
+ ts.factory.createBlock(
1944
+ [
1945
+ ...rewrittenBody.statements,
1946
+ createTraceRecorderStatement(ts, 'recordReturn', [
1947
+ ts.factory.createNumericLiteral(functionEndLine),
1948
+ ts.factory.createIdentifier('undefined'),
1949
+ ts.factory.createStringLiteral(traceFunctionName),
1950
+ ]),
1951
+ ],
1952
+ true
1953
+ ),
1954
+ undefined,
1955
+ ts.factory.createBlock([createTraceRecorderStatement(ts, 'popCall', [])], true)
1956
+ ),
1957
+ ],
1958
+ true
1959
+ );
1960
+
1961
+ return updateFunctionLikeWithBody(ts, functionLikeNode, wrappedBody);
1962
+ }
1963
+
1964
+ async function instrumentCodeForTracing(sourceCode, language, traceFunctionName) {
1965
+ await ensureTypeScriptCompiler();
1966
+ const ts = getTypeScriptCompiler();
1967
+ if (!ts || typeof sourceCode !== 'string') {
1968
+ return null;
1969
+ }
1970
+
1971
+ const scriptKind = language === 'typescript' ? ts.ScriptKind.TS : ts.ScriptKind.JS;
1972
+ const sourceFile = ts.createSourceFile(
1973
+ `trace-input.${language === 'typescript' ? 'ts' : 'js'}`,
1974
+ sourceCode,
1975
+ ts.ScriptTarget.ES2020,
1976
+ true,
1977
+ scriptKind
1978
+ );
1979
+
1980
+ const variableNames = collectTraceVariableNames(ts, sourceFile);
1981
+ const effectiveFunctionName =
1982
+ typeof traceFunctionName === 'string' && traceFunctionName.length > 0
1983
+ ? traceFunctionName
1984
+ : '<module>';
1985
+ const lineFunctionMap = buildLineFunctionMap(ts, sourceFile, effectiveFunctionName);
1986
+
1987
+ const transformer = (context) => {
1988
+ const visit = (node) => {
1989
+ if (ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) {
1990
+ const tracedOperand = extractTraceableElementAccess(ts, node.operand);
1991
+ const operatorName =
1992
+ node.operator === ts.SyntaxKind.PlusPlusToken
1993
+ ? 'inc'
1994
+ : node.operator === ts.SyntaxKind.MinusMinusToken
1995
+ ? 'dec'
1996
+ : null;
1997
+ if (tracedOperand && operatorName) {
1998
+ const visitedIndices = tracedOperand.indices.map((indexExpr) => ts.visitNode(indexExpr, visit));
1999
+ return createTraceUpdateExpression(
2000
+ ts,
2001
+ tracedOperand.variableName,
2002
+ visitedIndices,
2003
+ operatorName,
2004
+ ts.isPrefixUnaryExpression(node)
2005
+ );
2006
+ }
2007
+ }
2008
+
2009
+ if (ts.isBinaryExpression(node)) {
2010
+ const tracedLeft = extractTraceableElementAccess(ts, node.left);
2011
+ if (tracedLeft && isAssignmentOperatorToken(ts, node.operatorToken.kind)) {
2012
+ const visitedIndices = tracedLeft.indices.map((indexExpr) => ts.visitNode(indexExpr, visit));
2013
+ const visitedRight = ts.visitNode(node.right, visit);
2014
+ if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
2015
+ return createTraceWriteIndexExpression(
2016
+ ts,
2017
+ tracedLeft.variableName,
2018
+ visitedIndices,
2019
+ visitedRight
2020
+ );
2021
+ }
2022
+
2023
+ const operatorName = getCompoundAssignmentOperatorName(ts, node.operatorToken.kind);
2024
+ if (operatorName) {
2025
+ return createTraceAugAssignExpression(
2026
+ ts,
2027
+ tracedLeft.variableName,
2028
+ visitedIndices,
2029
+ operatorName,
2030
+ visitedRight
2031
+ );
2032
+ }
2033
+ }
2034
+ }
2035
+
2036
+ if (ts.isCallExpression(node)) {
2037
+ const tracedCall = extractTraceableMutatingCall(ts, node);
2038
+ if (tracedCall) {
2039
+ const visitedArgs = node.arguments.map((arg) => ts.visitNode(arg, visit));
2040
+ return createTraceMutatingCallExpression(
2041
+ ts,
2042
+ tracedCall.variableName,
2043
+ tracedCall.methodName,
2044
+ visitedArgs
2045
+ );
2046
+ }
2047
+ }
2048
+
2049
+ if (ts.isElementAccessExpression(node)) {
2050
+ if (
2051
+ isNestedElementAccessExpression(ts, node) ||
2052
+ isAssignmentLikeLeftOperand(ts, node) ||
2053
+ isUpdateExpressionOperand(ts, node) ||
2054
+ isDestructuringAssignmentTarget(ts, node)
2055
+ ) {
2056
+ return ts.visitEachChild(node, visit, context);
2057
+ }
2058
+
2059
+ const tracedAccess = extractTraceableElementAccess(ts, node);
2060
+ if (tracedAccess) {
2061
+ const visitedIndices = tracedAccess.indices.map((indexExpr) => ts.visitNode(indexExpr, visit));
2062
+ return createTraceReadIndexExpression(ts, tracedAccess.variableName, visitedIndices);
2063
+ }
2064
+ }
2065
+
2066
+ if (ts.isFunctionLike(node) && node.body && ts.isBlock(node.body)) {
2067
+ const visitedFunction = ts.visitEachChild(node, visit, context);
2068
+ if (!visitedFunction.body || !ts.isBlock(visitedFunction.body)) {
2069
+ return visitedFunction;
2070
+ }
2071
+ return wrapFunctionBodyForTracing(
2072
+ ts,
2073
+ sourceFile,
2074
+ context,
2075
+ visitedFunction,
2076
+ visitedFunction.body,
2077
+ effectiveFunctionName
2078
+ );
2079
+ }
2080
+
2081
+ if (ts.isSourceFile(node)) {
2082
+ const visited = ts.visitEachChild(node, visit, context);
2083
+ return ts.factory.updateSourceFile(
2084
+ visited,
2085
+ instrumentStatementList(
2086
+ ts,
2087
+ sourceFile,
2088
+ node.statements,
2089
+ visited.statements,
2090
+ variableNames,
2091
+ lineFunctionMap,
2092
+ effectiveFunctionName
2093
+ )
2094
+ );
2095
+ }
2096
+
2097
+ if (ts.isBlock(node)) {
2098
+ const visited = ts.visitEachChild(node, visit, context);
2099
+ return ts.factory.updateBlock(
2100
+ visited,
2101
+ instrumentStatementList(
2102
+ ts,
2103
+ sourceFile,
2104
+ node.statements,
2105
+ visited.statements,
2106
+ variableNames,
2107
+ lineFunctionMap,
2108
+ effectiveFunctionName
2109
+ )
2110
+ );
2111
+ }
2112
+
2113
+ if (ts.isCaseClause(node)) {
2114
+ const visited = ts.visitEachChild(node, visit, context);
2115
+ return ts.factory.updateCaseClause(
2116
+ visited,
2117
+ visited.expression,
2118
+ instrumentStatementList(
2119
+ ts,
2120
+ sourceFile,
2121
+ node.statements,
2122
+ visited.statements,
2123
+ variableNames,
2124
+ lineFunctionMap,
2125
+ effectiveFunctionName
2126
+ )
2127
+ );
2128
+ }
2129
+
2130
+ if (ts.isDefaultClause(node)) {
2131
+ const visited = ts.visitEachChild(node, visit, context);
2132
+ return ts.factory.updateDefaultClause(
2133
+ visited,
2134
+ instrumentStatementList(
2135
+ ts,
2136
+ sourceFile,
2137
+ node.statements,
2138
+ visited.statements,
2139
+ variableNames,
2140
+ lineFunctionMap,
2141
+ effectiveFunctionName
2142
+ )
2143
+ );
2144
+ }
2145
+
2146
+ return ts.visitEachChild(node, visit, context);
2147
+ };
2148
+
2149
+ return (node) => ts.visitNode(node, visit);
2150
+ };
2151
+
2152
+ const transformed = ts.transform(sourceFile, [transformer]);
2153
+ try {
2154
+ const outputFile = transformed.transformed[0];
2155
+ const printer = ts.createPrinter({
2156
+ newLine: ts.NewLineKind.LineFeed,
2157
+ removeComments: false,
2158
+ });
2159
+ return printer.printFile(outputFile);
2160
+ } finally {
2161
+ transformed.dispose();
2162
+ }
2163
+ }
2164
+
2165
+ function buildScriptExecutionRunner(code) {
2166
+ return new Function(
2167
+ 'console',
2168
+ `"use strict";
2169
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2170
+ let result;
2171
+ ${code}
2172
+ if (typeof result === 'undefined') {
2173
+ return null;
2174
+ }
2175
+ return result;`
2176
+ );
2177
+ }
2178
+
2179
+ const TRACING_RUNTIME_HELPERS_SOURCE = `
2180
+ function __traceNormalizeIndices(__indices, __maxDepth = 2) {
2181
+ if (!Array.isArray(__indices) || __indices.length === 0 || __indices.length > __maxDepth) return null;
2182
+ if (!__indices.every((__index) => typeof __index === 'number' && Number.isInteger(__index))) return null;
2183
+ return __indices.map((__index) => Math.trunc(__index));
2184
+ }
2185
+
2186
+ function __traceReadValueAtIndices(__container, __indices) {
2187
+ let __current = __container;
2188
+ for (const __index of __indices) {
2189
+ if (__current === null || __current === undefined) return undefined;
2190
+ __current = __current[__index];
2191
+ }
2192
+ return __current;
2193
+ }
2194
+
2195
+ function __traceWriteValueAtIndices(__container, __indices, __value) {
2196
+ if (__indices.length === 1) {
2197
+ __container[__indices[0]] = __value;
2198
+ return __value;
2199
+ }
2200
+ let __parent = __container;
2201
+ for (let __i = 0; __i < __indices.length - 1; __i++) {
2202
+ __parent = __parent?.[__indices[__i]];
2203
+ }
2204
+ if (__parent !== null && __parent !== undefined) {
2205
+ __parent[__indices[__indices.length - 1]] = __value;
2206
+ }
2207
+ return __value;
2208
+ }
2209
+
2210
+ function __traceReadIndex(__varName, __container, __indices) {
2211
+ const __normalized = __traceNormalizeIndices(__indices);
2212
+ if (__normalized) {
2213
+ __traceRecorder.recordAccess({
2214
+ variable: __varName,
2215
+ kind: __normalized.length === 2 ? 'cell-read' : 'indexed-read',
2216
+ indices: __normalized,
2217
+ pathDepth: __normalized.length,
2218
+ });
2219
+ }
2220
+ return __traceReadValueAtIndices(__container, Array.isArray(__indices) ? __indices : []);
2221
+ }
2222
+
2223
+ function __traceWriteIndex(__varName, __container, __indices, __value) {
2224
+ const __normalized = __traceNormalizeIndices(__indices);
2225
+ const __result = __traceWriteValueAtIndices(__container, Array.isArray(__indices) ? __indices : [], __value);
2226
+ if (__normalized) {
2227
+ __traceRecorder.recordAccess({
2228
+ variable: __varName,
2229
+ kind: __normalized.length === 2 ? 'cell-write' : 'indexed-write',
2230
+ indices: __normalized,
2231
+ pathDepth: __normalized.length,
2232
+ });
2233
+ }
2234
+ return __result;
2235
+ }
2236
+
2237
+ function __traceApplyAugmentedValue(__current, __op, __rhs) {
2238
+ switch (__op) {
2239
+ case 'add': return __current + __rhs;
2240
+ case 'sub': return __current - __rhs;
2241
+ case 'mul': return __current * __rhs;
2242
+ case 'div': return __current / __rhs;
2243
+ case 'mod': return __current % __rhs;
2244
+ case 'pow': return __current ** __rhs;
2245
+ case 'lshift': return __current << __rhs;
2246
+ case 'rshift': return __current >> __rhs;
2247
+ case 'bitand': return __current & __rhs;
2248
+ case 'bitor': return __current | __rhs;
2249
+ case 'bitxor': return __current ^ __rhs;
2250
+ default: return __rhs;
2251
+ }
2252
+ }
2253
+
2254
+ function __traceAugAssignIndex(__varName, __container, __indices, __op, __rhs) {
2255
+ const __normalized = __traceNormalizeIndices(__indices);
2256
+ const __effectiveIndices = Array.isArray(__indices) ? __indices : [];
2257
+ const __current = __traceReadValueAtIndices(__container, __effectiveIndices);
2258
+ if (__normalized) {
2259
+ __traceRecorder.recordAccess({
2260
+ variable: __varName,
2261
+ kind: __normalized.length === 2 ? 'cell-read' : 'indexed-read',
2262
+ indices: __normalized,
2263
+ pathDepth: __normalized.length,
2264
+ });
2265
+ }
2266
+ const __next = __traceApplyAugmentedValue(__current, __op, __rhs);
2267
+ __traceWriteValueAtIndices(__container, __effectiveIndices, __next);
2268
+ if (__normalized) {
2269
+ __traceRecorder.recordAccess({
2270
+ variable: __varName,
2271
+ kind: __normalized.length === 2 ? 'cell-write' : 'indexed-write',
2272
+ indices: __normalized,
2273
+ pathDepth: __normalized.length,
2274
+ });
2275
+ }
2276
+ return __next;
2277
+ }
2278
+
2279
+ function __traceUpdateIndex(__varName, __container, __indices, __op, __isPrefix) {
2280
+ const __normalized = __traceNormalizeIndices(__indices);
2281
+ const __effectiveIndices = Array.isArray(__indices) ? __indices : [];
2282
+ const __current = __traceReadValueAtIndices(__container, __effectiveIndices);
2283
+ if (__normalized) {
2284
+ __traceRecorder.recordAccess({
2285
+ variable: __varName,
2286
+ kind: __normalized.length === 2 ? 'cell-read' : 'indexed-read',
2287
+ indices: __normalized,
2288
+ pathDepth: __normalized.length,
2289
+ });
2290
+ }
2291
+ const __delta = __op === 'dec' ? -1 : 1;
2292
+ const __next = __current + __delta;
2293
+ __traceWriteValueAtIndices(__container, __effectiveIndices, __next);
2294
+ if (__normalized) {
2295
+ __traceRecorder.recordAccess({
2296
+ variable: __varName,
2297
+ kind: __normalized.length === 2 ? 'cell-write' : 'indexed-write',
2298
+ indices: __normalized,
2299
+ pathDepth: __normalized.length,
2300
+ });
2301
+ }
2302
+ return __isPrefix ? __next : __current;
2303
+ }
2304
+
2305
+ function __traceMutatingCall(__varName, __container, __method, ...__args) {
2306
+ const __result = __container[__method](...__args);
2307
+ if (['push', 'pop', 'shift', 'unshift', 'splice'].includes(__method)) {
2308
+ __traceRecorder.recordAccess({
2309
+ variable: __varName,
2310
+ kind: 'mutating-call',
2311
+ method: __method,
2312
+ pathDepth: 1,
2313
+ });
2314
+ }
2315
+ return __result;
2316
+ }
2317
+ `;
2318
+
2319
+ function buildScriptTracingRunner(code) {
2320
+ return new Function(
2321
+ 'console',
2322
+ '__traceRecorder',
2323
+ '__traceCtx',
2324
+ `"use strict";
2325
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2326
+ ${TRACING_RUNTIME_HELPERS_SOURCE}
2327
+ let result;
2328
+ ${code}
2329
+ if (typeof result === 'undefined') {
2330
+ return null;
2331
+ }
2332
+ return result;`
2333
+ );
2334
+ }
2335
+
2336
+ function buildFunctionExecutionRunner(code, executionStyle, argNames) {
2337
+ if (executionStyle === 'function') {
2338
+ return new Function(
2339
+ 'console',
2340
+ '__functionName',
2341
+ ...argNames,
2342
+ `"use strict";
2343
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2344
+ ${code}
2345
+ let __target;
2346
+ try {
2347
+ __target = eval(__functionName);
2348
+ } catch (_err) {
2349
+ __target = undefined;
2350
+ }
2351
+ if (typeof __target !== 'function') {
2352
+ throw new Error('Function "' + __functionName + '" not found');
2353
+ }
2354
+ return __target(${argNames.join(', ')});`
2355
+ );
2356
+ }
2357
+
2358
+ if (executionStyle === 'solution-method') {
2359
+ return new Function(
2360
+ 'console',
2361
+ '__functionName',
2362
+ ...argNames,
2363
+ `"use strict";
2364
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2365
+ ${code}
2366
+ if (typeof Solution !== 'function') {
2367
+ throw new Error('Class "Solution" not found');
2368
+ }
2369
+ const __solver = new Solution();
2370
+ const __method = __solver[__functionName];
2371
+ if (typeof __method !== 'function') {
2372
+ throw new Error('Method "Solution.' + __functionName + '" not found');
2373
+ }
2374
+ return __method.call(__solver, ${argNames.join(', ')});`
2375
+ );
2376
+ }
2377
+
2378
+ if (executionStyle === 'ops-class') {
2379
+ return new Function(
2380
+ 'console',
2381
+ '__className',
2382
+ '__operations',
2383
+ '__arguments',
2384
+ `"use strict";
2385
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2386
+ ${code}
2387
+ if (!Array.isArray(__operations) || !Array.isArray(__arguments)) {
2388
+ throw new Error('ops-class execution requires inputs.operations and inputs.arguments (or ops/args)');
2389
+ }
2390
+ if (__operations.length !== __arguments.length) {
2391
+ throw new Error('operations and arguments must have the same length');
2392
+ }
2393
+ let __targetClass;
2394
+ try {
2395
+ __targetClass = eval(__className);
2396
+ } catch (_err) {
2397
+ __targetClass = undefined;
2398
+ }
2399
+ if (typeof __targetClass !== 'function') {
2400
+ throw new Error('Class "' + __className + '" not found');
2401
+ }
2402
+ let __instance = null;
2403
+ const __out = [];
2404
+ for (let __i = 0; __i < __operations.length; __i++) {
2405
+ const __op = __operations[__i];
2406
+ let __callArgs = __arguments[__i];
2407
+ if (__callArgs === null || __callArgs === undefined) {
2408
+ __callArgs = [];
2409
+ }
2410
+ if (!Array.isArray(__callArgs)) {
2411
+ __callArgs = [__callArgs];
2412
+ }
2413
+ if (__i === 0) {
2414
+ __instance = new __targetClass(...__callArgs);
2415
+ __out.push(null);
2416
+ continue;
2417
+ }
2418
+ if (!__instance || typeof __instance[__op] !== 'function') {
2419
+ throw new Error('Required method "' + __op + '" is not implemented on ' + (__className || 'target class'));
2420
+ }
2421
+ __out.push(__instance[__op](...__callArgs));
2422
+ }
2423
+ return __out;`
2424
+ );
2425
+ }
2426
+
2427
+ throw new Error(`Execution style "${executionStyle}" is not supported for JavaScript runtime yet.`);
2428
+ }
2429
+
2430
+ function buildFunctionTracingRunner(code, executionStyle, argNames) {
2431
+ if (executionStyle === 'function') {
2432
+ return new Function(
2433
+ 'console',
2434
+ '__traceRecorder',
2435
+ '__traceCtx',
2436
+ '__functionName',
2437
+ ...argNames,
2438
+ `"use strict";
2439
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2440
+ ${TRACING_RUNTIME_HELPERS_SOURCE}
2441
+ ${code}
2442
+ let __target;
2443
+ try {
2444
+ __target = eval(__functionName);
2445
+ } catch (_err) {
2446
+ __target = undefined;
2447
+ }
2448
+ if (typeof __target !== 'function') {
2449
+ throw new Error('Function "' + __functionName + '" not found');
2450
+ }
2451
+ return __target(${argNames.join(', ')});`
2452
+ );
2453
+ }
2454
+
2455
+ if (executionStyle === 'solution-method') {
2456
+ return new Function(
2457
+ 'console',
2458
+ '__traceRecorder',
2459
+ '__traceCtx',
2460
+ '__functionName',
2461
+ ...argNames,
2462
+ `"use strict";
2463
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2464
+ ${TRACING_RUNTIME_HELPERS_SOURCE}
2465
+ ${code}
2466
+ if (typeof Solution !== 'function') {
2467
+ throw new Error('Class "Solution" not found');
2468
+ }
2469
+ const __solver = new Solution();
2470
+ const __method = __solver[__functionName];
2471
+ if (typeof __method !== 'function') {
2472
+ throw new Error('Method "Solution.' + __functionName + '" not found');
2473
+ }
2474
+ return __method.call(__solver, ${argNames.join(', ')});`
2475
+ );
2476
+ }
2477
+
2478
+ if (executionStyle === 'ops-class') {
2479
+ return new Function(
2480
+ 'console',
2481
+ '__traceRecorder',
2482
+ '__traceCtx',
2483
+ '__className',
2484
+ '__operations',
2485
+ '__arguments',
2486
+ `"use strict";
2487
+ ${JAVASCRIPT_RUNTIME_PRELUDE}
2488
+ ${TRACING_RUNTIME_HELPERS_SOURCE}
2489
+ ${code}
2490
+ if (!Array.isArray(__operations) || !Array.isArray(__arguments)) {
2491
+ throw new Error('ops-class execution requires inputs.operations and inputs.arguments (or ops/args)');
2492
+ }
2493
+ if (__operations.length !== __arguments.length) {
2494
+ throw new Error('operations and arguments must have the same length');
2495
+ }
2496
+ let __targetClass;
2497
+ try {
2498
+ __targetClass = eval(__className);
2499
+ } catch (_err) {
2500
+ __targetClass = undefined;
2501
+ }
2502
+ if (typeof __targetClass !== 'function') {
2503
+ throw new Error('Class "' + __className + '" not found');
2504
+ }
2505
+ let __instance = null;
2506
+ const __out = [];
2507
+ for (let __i = 0; __i < __operations.length; __i++) {
2508
+ const __op = __operations[__i];
2509
+ let __callArgs = __arguments[__i];
2510
+ if (__callArgs === null || __callArgs === undefined) {
2511
+ __callArgs = [];
2512
+ }
2513
+ if (!Array.isArray(__callArgs)) {
2514
+ __callArgs = [__callArgs];
2515
+ }
2516
+ if (__i === 0) {
2517
+ __instance = new __targetClass(...__callArgs);
2518
+ __out.push(null);
2519
+ continue;
2520
+ }
2521
+ if (!__instance || typeof __instance[__op] !== 'function') {
2522
+ throw new Error('Required method "' + __op + '" is not implemented on ' + (__className || 'target class'));
2523
+ }
2524
+ __out.push(__instance[__op](...__callArgs));
2525
+ }
2526
+ return __out;`
2527
+ );
2528
+ }
2529
+
2530
+ throw new Error(`Execution style "${executionStyle}" is not supported for JavaScript runtime yet.`);
2531
+ }
2532
+
2533
+ function getOpsClassInputs(inputs) {
2534
+ if (!inputs || typeof inputs !== 'object' || Array.isArray(inputs)) {
2535
+ return { operations: null, argumentsList: null };
2536
+ }
2537
+ const operations = Array.isArray(inputs.operations)
2538
+ ? inputs.operations
2539
+ : (Array.isArray(inputs.ops) ? inputs.ops : null);
2540
+ const argumentsList = Array.isArray(inputs.arguments)
2541
+ ? inputs.arguments
2542
+ : (Array.isArray(inputs.args) ? inputs.args : null);
2543
+ return { operations, argumentsList };
2544
+ }
2545
+
2546
+ async function executeCode(payload) {
2547
+ const {
2548
+ code,
2549
+ functionName,
2550
+ inputs,
2551
+ executionStyle = 'function',
2552
+ language = 'javascript',
2553
+ } = payload ?? {};
2554
+ const consoleOutput = [];
2555
+ const consoleProxy = createConsoleProxy(consoleOutput);
2556
+ const normalizedInputs = normalizeInputs(inputs);
2557
+
2558
+ try {
2559
+ if (typeof code !== 'string') {
2560
+ throw new Error('`code` must be a string');
2561
+ }
2562
+ if (language !== 'javascript' && language !== 'typescript') {
2563
+ throw new Error(`Unsupported language for JavaScript worker: ${String(language)}`);
2564
+ }
2565
+
2566
+ const executableCode = await prepareExecutableCode(code, language);
2567
+ const hasNamedFunction = typeof functionName === 'string' && functionName.length > 0;
2568
+ let output;
2569
+
2570
+ if (hasNamedFunction) {
2571
+ if (executionStyle === 'ops-class') {
2572
+ const { operations, argumentsList } = getOpsClassInputs(normalizedInputs);
2573
+ const runner = buildFunctionExecutionRunner(executableCode, executionStyle, []);
2574
+ output = await Promise.resolve(runner(consoleProxy, functionName, operations, argumentsList));
2575
+ } else {
2576
+ const inputKeys = await resolveOrderedInputKeys(executableCode, functionName, normalizedInputs, executionStyle);
2577
+ const argNames = inputKeys.map((_, index) => `__arg${index}`);
2578
+ const argValues = inputKeys.map((key) => normalizedInputs[key]);
2579
+ const runner = buildFunctionExecutionRunner(executableCode, executionStyle, argNames);
2580
+ output = await Promise.resolve(runner(consoleProxy, functionName, ...argValues));
2581
+ }
2582
+ } else {
2583
+ if (executionStyle !== 'function') {
2584
+ throw new Error('Script-mode execution only supports executionStyle="function".');
2585
+ }
2586
+ const runner = buildScriptExecutionRunner(executableCode);
2587
+ output = await Promise.resolve(runner(consoleProxy));
2588
+ }
2589
+
2590
+ return {
2591
+ success: true,
2592
+ output: serializeValue(output),
2593
+ consoleOutput,
2594
+ };
2595
+ } catch (error) {
2596
+ const message = formatRuntimeErrorMessage(error);
2597
+ return {
2598
+ success: false,
2599
+ output: null,
2600
+ error: message,
2601
+ errorLine: extractUserErrorLine(error),
2602
+ consoleOutput,
2603
+ };
2604
+ }
2605
+ }
2606
+
2607
+ async function executeWithTracing(payload) {
2608
+ const startedAt = performanceNow();
2609
+ const {
2610
+ code,
2611
+ functionName,
2612
+ inputs,
2613
+ options,
2614
+ executionStyle = 'function',
2615
+ language = 'javascript',
2616
+ } = payload ?? {};
2617
+ const consoleOutput = [];
2618
+ const consoleProxy = createConsoleProxy(consoleOutput);
2619
+ const normalizedInputs = normalizeInputs(inputs);
2620
+ const hasNamedFunction = typeof functionName === 'string' && functionName.length > 0;
2621
+ const traceFunctionName = hasNamedFunction ? functionName : '<module>';
2622
+ const traceRecorder = createTraceRecorder(options);
2623
+
2624
+ let traceLineBounds = { startLine: 1, endLine: 1 };
2625
+
2626
+ try {
2627
+ if (typeof code !== 'string') {
2628
+ throw new Error('`code` must be a string');
2629
+ }
2630
+ if (language !== 'javascript' && language !== 'typescript') {
2631
+ throw new Error(`Unsupported language for JavaScript worker: ${String(language)}`);
2632
+ }
2633
+
2634
+ const executableCode = await prepareExecutableCode(code, language);
2635
+ traceLineBounds = determineTraceLineBounds(code, functionName, executionStyle);
2636
+
2637
+ let instrumentedCode = null;
2638
+ try {
2639
+ if (language === 'typescript') {
2640
+ const instrumentedTypeScript = await instrumentCodeForTracing(code, language, traceFunctionName);
2641
+ instrumentedCode = instrumentedTypeScript ? transpileTypeScript(instrumentedTypeScript) : null;
2642
+ } else {
2643
+ instrumentedCode = await instrumentCodeForTracing(executableCode, language, traceFunctionName);
2644
+ }
2645
+ } catch (instrumentationError) {
2646
+ if (WORKER_DEBUG) {
2647
+ const message =
2648
+ instrumentationError instanceof Error ? instrumentationError.message : String(instrumentationError);
2649
+ console.warn('[JavaScriptWorker] trace instrumentation failed, using synthetic fallback:', message);
2650
+ }
2651
+ }
2652
+
2653
+ if (!instrumentedCode) {
2654
+ const fallbackResult = await executeCode(payload);
2655
+ const executionTimeMs = performanceNow() - startedAt;
2656
+
2657
+ if (!fallbackResult.success) {
2658
+ return {
2659
+ success: false,
2660
+ error: fallbackResult.error,
2661
+ errorLine: fallbackResult.errorLine,
2662
+ trace: [],
2663
+ executionTimeMs,
2664
+ consoleOutput: fallbackResult.consoleOutput ?? [],
2665
+ lineEventCount: 0,
2666
+ traceStepCount: 0,
2667
+ };
2668
+ }
2669
+
2670
+ const syntheticTrace = createSyntheticTrace(payload, fallbackResult);
2671
+ return {
2672
+ success: true,
2673
+ output: fallbackResult.output,
2674
+ trace: syntheticTrace,
2675
+ executionTimeMs,
2676
+ consoleOutput: fallbackResult.consoleOutput ?? [],
2677
+ lineEventCount: syntheticTrace.filter((step) => step.event === 'line').length,
2678
+ traceStepCount: syntheticTrace.length,
2679
+ };
2680
+ }
2681
+
2682
+ const serializedInputs = {};
2683
+ for (const [key, value] of Object.entries(normalizedInputs)) {
2684
+ serializedInputs[key] = serializeValue(value);
2685
+ }
2686
+
2687
+ let output;
2688
+ if (hasNamedFunction) {
2689
+ if (executionStyle === 'ops-class') {
2690
+ const { operations, argumentsList } = getOpsClassInputs(normalizedInputs);
2691
+ const runner = buildFunctionTracingRunner(instrumentedCode, executionStyle, []);
2692
+ output = await Promise.resolve(
2693
+ runner(
2694
+ consoleProxy,
2695
+ traceRecorder,
2696
+ { functionName: traceFunctionName },
2697
+ functionName,
2698
+ operations,
2699
+ argumentsList
2700
+ )
2701
+ );
2702
+ } else {
2703
+ const inputKeys = await resolveOrderedInputKeys(executableCode, functionName, normalizedInputs, executionStyle);
2704
+ const argNames = inputKeys.map((_, index) => `__arg${index}`);
2705
+ const argValues = inputKeys.map((key) => normalizedInputs[key]);
2706
+ const runner = buildFunctionTracingRunner(instrumentedCode, executionStyle, argNames);
2707
+ output = await Promise.resolve(
2708
+ runner(consoleProxy, traceRecorder, { functionName: traceFunctionName }, functionName, ...argValues)
2709
+ );
2710
+ }
2711
+ } else {
2712
+ if (executionStyle !== 'function') {
2713
+ throw new Error('Script-mode execution only supports executionStyle="function".');
2714
+ }
2715
+ const runner = buildScriptTracingRunner(instrumentedCode);
2716
+ output = await Promise.resolve(
2717
+ runner(consoleProxy, traceRecorder, { functionName: traceFunctionName })
2718
+ );
2719
+ }
2720
+
2721
+ const serializedOutput = serializeValue(output);
2722
+ if (!hasNamedFunction) {
2723
+ traceRecorder.popToFunction(traceFunctionName);
2724
+ traceRecorder.recordReturn(traceLineBounds.endLine, serializedOutput, traceFunctionName);
2725
+ }
2726
+
2727
+ const executionTimeMs = performanceNow() - startedAt;
2728
+ return {
2729
+ success: true,
2730
+ output: serializedOutput,
2731
+ trace: traceRecorder.getTrace(),
2732
+ executionTimeMs,
2733
+ consoleOutput,
2734
+ lineEventCount: traceRecorder.getLineEventCount(),
2735
+ traceStepCount: traceRecorder.getTraceStepCount(),
2736
+ traceLimitExceeded: traceRecorder.isTraceLimitExceeded(),
2737
+ timeoutReason: traceRecorder.getTimeoutReason(),
2738
+ };
2739
+ } catch (error) {
2740
+ const executionTimeMs = performanceNow() - startedAt;
2741
+ const message = formatRuntimeErrorMessage(error);
2742
+ const errorLine = extractUserErrorLine(error);
2743
+ const traceErrorLine =
2744
+ error && typeof error === 'object' && '__traceLine' in error
2745
+ ? Number(error.__traceLine)
2746
+ : errorLine ?? traceLineBounds.endLine;
2747
+ const traceLimitExceeded =
2748
+ (error && typeof error === 'object' && error.__traceLimitExceeded === true) ||
2749
+ traceRecorder.isTraceLimitExceeded();
2750
+ const timeoutReason =
2751
+ (error && typeof error === 'object' && typeof error.__timeoutReason === 'string'
2752
+ ? error.__timeoutReason
2753
+ : traceRecorder.getTimeoutReason()) ?? undefined;
2754
+
2755
+ if (!traceLimitExceeded) {
2756
+ traceRecorder.popToFunction(traceFunctionName);
2757
+ traceRecorder.recordException(traceErrorLine, message, traceFunctionName);
2758
+ }
2759
+
2760
+ return {
2761
+ success: false,
2762
+ output: null,
2763
+ error: message,
2764
+ errorLine,
2765
+ trace: traceRecorder.getTrace(),
2766
+ executionTimeMs,
2767
+ consoleOutput,
2768
+ lineEventCount: traceRecorder.getLineEventCount(),
2769
+ traceStepCount: traceRecorder.getTraceStepCount(),
2770
+ traceLimitExceeded,
2771
+ timeoutReason,
2772
+ };
2773
+ }
2774
+ }
2775
+
2776
+ async function executeCodeInterview(payload) {
2777
+ const guardedOptions = {
2778
+ ...INTERVIEW_GUARD_DEFAULTS,
2779
+ ...(payload?.options && typeof payload.options === 'object' ? payload.options : {}),
2780
+ };
2781
+
2782
+ const tracedResult = await executeWithTracing({
2783
+ ...payload,
2784
+ options: guardedOptions,
2785
+ });
2786
+
2787
+ if (!tracedResult.success) {
2788
+ const normalized = String(tracedResult.error ?? '').toLowerCase();
2789
+ const timeoutReason = tracedResult.timeoutReason ?? '';
2790
+ const timeoutIndicators = [
2791
+ 'trace-limit',
2792
+ 'line-limit',
2793
+ 'single-line-limit',
2794
+ 'recursion-limit',
2795
+ ];
2796
+ const isGuardTimeout =
2797
+ timeoutIndicators.includes(timeoutReason) ||
2798
+ normalized.includes('timed out') ||
2799
+ normalized.includes('infinite loop') ||
2800
+ normalized.includes('line events') ||
2801
+ normalized.includes('trace steps') ||
2802
+ normalized.includes('call depth');
2803
+
2804
+ if (isGuardTimeout) {
2805
+ return {
2806
+ success: false,
2807
+ output: null,
2808
+ error: 'Time Limit Exceeded',
2809
+ consoleOutput: tracedResult.consoleOutput ?? [],
2810
+ };
2811
+ }
2812
+
2813
+ return {
2814
+ success: false,
2815
+ output: null,
2816
+ error: tracedResult.error,
2817
+ errorLine: tracedResult.errorLine,
2818
+ consoleOutput: tracedResult.consoleOutput ?? [],
2819
+ };
2820
+ }
2821
+
2822
+ return {
2823
+ success: true,
2824
+ output: tracedResult.output,
2825
+ consoleOutput: tracedResult.consoleOutput ?? [],
2826
+ };
2827
+ }
2828
+
2829
+ async function initRuntime() {
2830
+ if (isInitialized) {
2831
+ return { success: true, loadTimeMs: 0 };
2832
+ }
2833
+
2834
+ isLoading = true;
2835
+ const startedAt = performanceNow();
2836
+ isInitialized = true;
2837
+ isLoading = false;
2838
+ return { success: true, loadTimeMs: performanceNow() - startedAt };
2839
+ }
2840
+
2841
+ async function processMessage(data) {
2842
+ const { id, type, payload } = data;
2843
+
2844
+ try {
2845
+ switch (type) {
2846
+ case 'init': {
2847
+ const result = await initRuntime();
2848
+ self.postMessage({ id, type: 'init-result', payload: result });
2849
+ break;
2850
+ }
2851
+
2852
+ case 'execute-with-tracing': {
2853
+ const result = await executeWithTracing(payload);
2854
+ self.postMessage({ id, type: 'execute-result', payload: result });
2855
+ break;
2856
+ }
2857
+
2858
+ case 'execute-code': {
2859
+ const result = await executeCode(payload);
2860
+ self.postMessage({ id, type: 'execute-result', payload: result });
2861
+ break;
2862
+ }
2863
+
2864
+ case 'execute-code-interview': {
2865
+ const result = await executeCodeInterview(payload);
2866
+ self.postMessage({ id, type: 'execute-result', payload: result });
2867
+ break;
2868
+ }
2869
+
2870
+ case 'status': {
2871
+ self.postMessage({
2872
+ id,
2873
+ type: 'status-result',
2874
+ payload: {
2875
+ isReady: isInitialized,
2876
+ isLoading,
2877
+ },
2878
+ });
2879
+ break;
2880
+ }
2881
+
2882
+ default: {
2883
+ self.postMessage({
2884
+ id,
2885
+ type: 'error',
2886
+ payload: { error: `Unknown message type: ${type}` },
2887
+ });
2888
+ }
2889
+ }
2890
+ } catch (error) {
2891
+ self.postMessage({
2892
+ id,
2893
+ type: 'error',
2894
+ payload: { error: error instanceof Error ? error.message : String(error) },
2895
+ });
2896
+ }
2897
+ }
2898
+
2899
+ let messageQueue = Promise.resolve();
2900
+
2901
+ self.onmessage = function(event) {
2902
+ const messageData = event.data;
2903
+ messageQueue = messageQueue
2904
+ .then(() => processMessage(messageData))
2905
+ .catch((error) => {
2906
+ const { id } = messageData;
2907
+ self.postMessage({
2908
+ id,
2909
+ type: 'error',
2910
+ payload: { error: error instanceof Error ? error.message : String(error) },
2911
+ });
2912
+ });
2913
+ };
2914
+
2915
+ if (WORKER_DEBUG) {
2916
+ console.log('[JavaScriptWorker] ready');
2917
+ }
2918
+ self.postMessage({ type: 'worker-ready' });