@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.
- package/CHANGELOG.md +113 -0
- package/LICENSE +674 -0
- package/README.md +266 -0
- package/dist/browser.cjs +1352 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +49 -0
- package/dist/browser.d.ts +49 -0
- package/dist/browser.js +1317 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.cjs +70 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +70 -0
- package/dist/cli.js.map +1 -0
- package/dist/core.cjs +286 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +69 -0
- package/dist/core.d.ts +69 -0
- package/dist/core.js +254 -0
- package/dist/core.js.map +1 -0
- package/dist/index.cjs +2603 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +2538 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/browser.cjs +647 -0
- package/dist/internal/browser.cjs.map +1 -0
- package/dist/internal/browser.d.cts +143 -0
- package/dist/internal/browser.d.ts +143 -0
- package/dist/internal/browser.js +617 -0
- package/dist/internal/browser.js.map +1 -0
- package/dist/javascript.cjs +549 -0
- package/dist/javascript.cjs.map +1 -0
- package/dist/javascript.d.cts +11 -0
- package/dist/javascript.d.ts +11 -0
- package/dist/javascript.js +518 -0
- package/dist/javascript.js.map +1 -0
- package/dist/python.cjs +744 -0
- package/dist/python.cjs.map +1 -0
- package/dist/python.d.cts +97 -0
- package/dist/python.d.ts +97 -0
- package/dist/python.js +698 -0
- package/dist/python.js.map +1 -0
- package/dist/runtime-types-C7d1LFbx.d.ts +85 -0
- package/dist/runtime-types-Dvgn07z9.d.cts +85 -0
- package/dist/types-Bzr1Ohcf.d.cts +96 -0
- package/dist/types-Bzr1Ohcf.d.ts +96 -0
- package/package.json +89 -0
- package/workers/javascript/javascript-worker.js +2918 -0
- package/workers/python/generated-python-harness-snippets.js +20 -0
- package/workers/python/pyodide-worker.js +1197 -0
- package/workers/python/runtime-core.js +1529 -0
- package/workers/vendor/typescript.js +200276 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2538 @@
|
|
|
1
|
+
// packages/harness-core/src/trace-contract.ts
|
|
2
|
+
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-03-07";
|
|
3
|
+
var TRACE_EVENTS = /* @__PURE__ */ new Set([
|
|
4
|
+
"line",
|
|
5
|
+
"call",
|
|
6
|
+
"return",
|
|
7
|
+
"exception",
|
|
8
|
+
"timeout",
|
|
9
|
+
"stdout"
|
|
10
|
+
]);
|
|
11
|
+
var TRACE_ACCESS_KINDS = /* @__PURE__ */ new Set([
|
|
12
|
+
"indexed-read",
|
|
13
|
+
"indexed-write",
|
|
14
|
+
"cell-read",
|
|
15
|
+
"cell-write",
|
|
16
|
+
"mutating-call"
|
|
17
|
+
]);
|
|
18
|
+
function normalizeLineNumber(value, fallback = 1) {
|
|
19
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
20
|
+
const normalized = Math.floor(value);
|
|
21
|
+
return normalized > 0 ? normalized : fallback;
|
|
22
|
+
}
|
|
23
|
+
function normalizeOutputLineCount(value) {
|
|
24
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
25
|
+
const normalized = Math.floor(value);
|
|
26
|
+
return normalized >= 0 ? normalized : void 0;
|
|
27
|
+
}
|
|
28
|
+
function normalizeEvent(value) {
|
|
29
|
+
if (typeof value === "string" && TRACE_EVENTS.has(value)) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
return "line";
|
|
33
|
+
}
|
|
34
|
+
function normalizeAccessKind(value) {
|
|
35
|
+
if (typeof value === "string" && TRACE_ACCESS_KINDS.has(value)) {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function normalizeFunctionName(value) {
|
|
41
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
42
|
+
return "<module>";
|
|
43
|
+
}
|
|
44
|
+
function normalizeKind(value) {
|
|
45
|
+
if (value === "map" || value === "set" || value === "hashmap") {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
return "hashmap";
|
|
49
|
+
}
|
|
50
|
+
function normalizeObjectKind(value) {
|
|
51
|
+
if (value === "hashmap" || value === "map" || value === "set" || value === "tree" || value === "linked-list" || value === "graph-adjacency") {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
function normalizeScalar(value) {
|
|
57
|
+
if (value === null || value === void 0) return value;
|
|
58
|
+
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
59
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : String(value);
|
|
60
|
+
if (typeof value === "bigint") {
|
|
61
|
+
const asNumber = Number(value);
|
|
62
|
+
return Number.isSafeInteger(asNumber) ? asNumber : String(value);
|
|
63
|
+
}
|
|
64
|
+
if (typeof value === "function") return "<function>";
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function normalizeUnknown(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
68
|
+
if (depth > 48) return "<max depth>";
|
|
69
|
+
const scalar = normalizeScalar(value);
|
|
70
|
+
if (scalar !== null || value === null || value === void 0) {
|
|
71
|
+
return scalar;
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return value.map((item) => {
|
|
75
|
+
if (item === void 0) return null;
|
|
76
|
+
return normalizeUnknown(item, depth + 1, seen);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (typeof value === "object" && value) {
|
|
80
|
+
if (seen.has(value)) return "<cycle>";
|
|
81
|
+
seen.add(value);
|
|
82
|
+
const normalized = {};
|
|
83
|
+
for (const key of Object.keys(value).sort()) {
|
|
84
|
+
const child = value[key];
|
|
85
|
+
if (child === void 0) continue;
|
|
86
|
+
normalized[key] = normalizeUnknown(child, depth + 1, seen);
|
|
87
|
+
}
|
|
88
|
+
seen.delete(value);
|
|
89
|
+
return normalized;
|
|
90
|
+
}
|
|
91
|
+
return String(value);
|
|
92
|
+
}
|
|
93
|
+
function normalizeAccesses(accesses) {
|
|
94
|
+
if (!Array.isArray(accesses) || accesses.length === 0) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
const normalized = accesses.map((access) => {
|
|
98
|
+
const variable = typeof access?.variable === "string" && access.variable.length > 0 ? access.variable : "";
|
|
99
|
+
const kind = normalizeAccessKind(access?.kind);
|
|
100
|
+
if (!variable || !kind) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const indices = Array.isArray(access?.indices) ? access.indices.map(
|
|
104
|
+
(index) => typeof index === "number" && Number.isFinite(index) ? Math.floor(index) : null
|
|
105
|
+
).filter((index) => index !== null) : void 0;
|
|
106
|
+
const pathDepth = access?.pathDepth === 1 || access?.pathDepth === 2 ? access.pathDepth : void 0;
|
|
107
|
+
const method = typeof access?.method === "string" && access.method.length > 0 ? access.method : void 0;
|
|
108
|
+
const payload = {
|
|
109
|
+
variable,
|
|
110
|
+
kind
|
|
111
|
+
};
|
|
112
|
+
if (indices && indices.length > 0) {
|
|
113
|
+
payload.indices = indices;
|
|
114
|
+
}
|
|
115
|
+
if (pathDepth !== void 0) {
|
|
116
|
+
payload.pathDepth = pathDepth;
|
|
117
|
+
}
|
|
118
|
+
if (method) {
|
|
119
|
+
payload.method = method;
|
|
120
|
+
}
|
|
121
|
+
return payload;
|
|
122
|
+
}).filter((access) => access !== null);
|
|
123
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
124
|
+
}
|
|
125
|
+
function normalizeRecord(value) {
|
|
126
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
127
|
+
return normalizeUnknown(value);
|
|
128
|
+
}
|
|
129
|
+
function normalizeCallStackFrame(frame) {
|
|
130
|
+
return {
|
|
131
|
+
function: normalizeFunctionName(frame?.function),
|
|
132
|
+
line: normalizeLineNumber(frame?.line, 1),
|
|
133
|
+
args: normalizeRecord(frame?.args)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function normalizeVisualizationPayload(payload) {
|
|
137
|
+
const hashMaps = Array.isArray(payload?.hashMaps) ? payload.hashMaps.map((entry) => ({
|
|
138
|
+
name: typeof entry?.name === "string" ? entry.name : "",
|
|
139
|
+
kind: normalizeKind(entry?.kind),
|
|
140
|
+
entries: Array.isArray(entry?.entries) ? entry.entries.map((item) => ({
|
|
141
|
+
key: normalizeUnknown(item?.key),
|
|
142
|
+
value: normalizeUnknown(item?.value),
|
|
143
|
+
...item?.highlight ? { highlight: true } : {}
|
|
144
|
+
})) : [],
|
|
145
|
+
...entry?.highlightedKey !== void 0 ? { highlightedKey: normalizeUnknown(entry.highlightedKey) } : {},
|
|
146
|
+
...entry?.deletedKey !== void 0 ? { deletedKey: normalizeUnknown(entry.deletedKey) } : {}
|
|
147
|
+
})).sort((a, b) => `${a.name}:${a.kind}`.localeCompare(`${b.name}:${b.kind}`)) : [];
|
|
148
|
+
const objectKinds = payload?.objectKinds && typeof payload.objectKinds === "object" ? Object.fromEntries(
|
|
149
|
+
Object.entries(payload.objectKinds).filter(([name]) => typeof name === "string" && name.length > 0).map(([name, kind]) => [name, normalizeObjectKind(kind)]).filter((entry) => entry[1] !== null).sort((a, b) => a[0].localeCompare(b[0]))
|
|
150
|
+
) : void 0;
|
|
151
|
+
if (hashMaps.length === 0 && (!objectKinds || Object.keys(objectKinds).length === 0)) {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
...hashMaps.length > 0 ? { hashMaps } : {},
|
|
156
|
+
...objectKinds && Object.keys(objectKinds).length > 0 ? { objectKinds } : {}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function normalizeTraceStep(step) {
|
|
160
|
+
const normalizedStdoutCount = normalizeOutputLineCount(step?.stdoutLineCount);
|
|
161
|
+
const normalizedVisualization = normalizeVisualizationPayload(step?.visualization);
|
|
162
|
+
const normalizedAccesses = normalizeAccesses(step?.accesses);
|
|
163
|
+
return {
|
|
164
|
+
event: normalizeEvent(step?.event),
|
|
165
|
+
line: normalizeLineNumber(step?.line, 1),
|
|
166
|
+
function: normalizeFunctionName(step?.function),
|
|
167
|
+
variables: normalizeRecord(step?.variables),
|
|
168
|
+
...Array.isArray(step?.callStack) && step.callStack.length > 0 ? { callStack: step.callStack.map(normalizeCallStackFrame) } : {},
|
|
169
|
+
...normalizedAccesses ? { accesses: normalizedAccesses } : {},
|
|
170
|
+
...step?.returnValue !== void 0 ? { returnValue: normalizeUnknown(step.returnValue) } : {},
|
|
171
|
+
...normalizedStdoutCount !== void 0 ? { stdoutLineCount: normalizedStdoutCount } : {},
|
|
172
|
+
...normalizedVisualization ? { visualization: normalizedVisualization } : {}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function normalizeRuntimeTraceContract(language, result) {
|
|
176
|
+
const normalizedTrace = Array.isArray(result.trace) ? result.trace.map(normalizeTraceStep) : [];
|
|
177
|
+
const lineEventCount = typeof result.lineEventCount === "number" && Number.isFinite(result.lineEventCount) ? Math.floor(result.lineEventCount) : normalizedTrace.filter((step) => step.event === "line").length;
|
|
178
|
+
const normalizedConsole = Array.isArray(result.consoleOutput) ? result.consoleOutput.map((line) => String(line)) : [];
|
|
179
|
+
return {
|
|
180
|
+
schemaVersion: RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION,
|
|
181
|
+
language,
|
|
182
|
+
success: Boolean(result.success),
|
|
183
|
+
...Object.prototype.hasOwnProperty.call(result, "output") ? { output: normalizeUnknown(result.output) } : {},
|
|
184
|
+
...typeof result.error === "string" && result.error.length > 0 ? { error: result.error } : {},
|
|
185
|
+
...typeof result.errorLine === "number" && Number.isFinite(result.errorLine) ? { errorLine: Math.floor(result.errorLine) } : {},
|
|
186
|
+
consoleOutput: normalizedConsole,
|
|
187
|
+
trace: normalizedTrace,
|
|
188
|
+
...result.traceLimitExceeded !== void 0 ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) } : {},
|
|
189
|
+
...typeof result.timeoutReason === "string" && result.timeoutReason.length > 0 ? { timeoutReason: result.timeoutReason } : {},
|
|
190
|
+
lineEventCount: Math.max(0, lineEventCount),
|
|
191
|
+
traceStepCount: normalizedTrace.length
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function stableStringifyRuntimeTraceContract(value) {
|
|
195
|
+
return JSON.stringify(normalizeUnknown(value), null, 2);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// packages/harness-core/src/trace-adapters/shared.ts
|
|
199
|
+
function denormalizeCallStackFrame(frame) {
|
|
200
|
+
return {
|
|
201
|
+
function: frame.function,
|
|
202
|
+
line: frame.line,
|
|
203
|
+
args: frame.args
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function denormalizeTraceStep(step) {
|
|
207
|
+
return {
|
|
208
|
+
line: step.line,
|
|
209
|
+
event: step.event,
|
|
210
|
+
variables: step.variables,
|
|
211
|
+
function: step.function,
|
|
212
|
+
...step.callStack ? { callStack: step.callStack.map(denormalizeCallStackFrame) } : {},
|
|
213
|
+
...step.accesses ? { accesses: step.accesses } : {},
|
|
214
|
+
...step.returnValue !== void 0 ? { returnValue: step.returnValue } : {},
|
|
215
|
+
...step.stdoutLineCount !== void 0 ? { stdoutLineCount: step.stdoutLineCount } : {},
|
|
216
|
+
...step.visualization ? { visualization: step.visualization } : {}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function adaptTraceExecutionResult(language, result) {
|
|
220
|
+
const normalized = normalizeRuntimeTraceContract(language, result);
|
|
221
|
+
const adaptedTrace = normalized.trace.map(denormalizeTraceStep);
|
|
222
|
+
return {
|
|
223
|
+
success: normalized.success,
|
|
224
|
+
...Object.prototype.hasOwnProperty.call(normalized, "output") ? { output: normalized.output } : {},
|
|
225
|
+
...normalized.error ? { error: normalized.error } : {},
|
|
226
|
+
...normalized.errorLine !== void 0 ? { errorLine: normalized.errorLine } : {},
|
|
227
|
+
trace: adaptedTrace,
|
|
228
|
+
executionTimeMs: typeof result.executionTimeMs === "number" && Number.isFinite(result.executionTimeMs) ? result.executionTimeMs : 0,
|
|
229
|
+
consoleOutput: normalized.consoleOutput,
|
|
230
|
+
...normalized.traceLimitExceeded !== void 0 ? { traceLimitExceeded: normalized.traceLimitExceeded } : {},
|
|
231
|
+
...normalized.timeoutReason ? { timeoutReason: normalized.timeoutReason } : {},
|
|
232
|
+
lineEventCount: normalized.lineEventCount,
|
|
233
|
+
traceStepCount: adaptedTrace.length
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// packages/harness-core/src/trace-adapters/python.ts
|
|
238
|
+
function adaptPythonTraceExecutionResult(result) {
|
|
239
|
+
return adaptTraceExecutionResult("python", result);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// packages/harness-core/src/trace-adapters/javascript.ts
|
|
243
|
+
function adaptJavaScriptTraceExecutionResult(language, result) {
|
|
244
|
+
return adaptTraceExecutionResult(language, result);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// packages/harness-browser/src/javascript-worker-client.ts
|
|
248
|
+
var EXECUTION_TIMEOUT_MS = 7e3;
|
|
249
|
+
var INTERVIEW_MODE_TIMEOUT_MS = 5e3;
|
|
250
|
+
var TRACING_TIMEOUT_MS = 7e3;
|
|
251
|
+
var INIT_TIMEOUT_MS = 1e4;
|
|
252
|
+
var MESSAGE_TIMEOUT_MS = 12e3;
|
|
253
|
+
var WORKER_READY_TIMEOUT_MS = 1e4;
|
|
254
|
+
var JavaScriptWorkerClient = class {
|
|
255
|
+
constructor(options) {
|
|
256
|
+
this.options = options;
|
|
257
|
+
this.debug = options.debug ?? process.env.NODE_ENV === "development";
|
|
258
|
+
}
|
|
259
|
+
worker = null;
|
|
260
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
261
|
+
messageId = 0;
|
|
262
|
+
isInitializing = false;
|
|
263
|
+
initPromise = null;
|
|
264
|
+
workerReadyPromise = null;
|
|
265
|
+
workerReadyResolve = null;
|
|
266
|
+
workerReadyReject = null;
|
|
267
|
+
debug;
|
|
268
|
+
isSupported() {
|
|
269
|
+
return typeof Worker !== "undefined";
|
|
270
|
+
}
|
|
271
|
+
getWorker() {
|
|
272
|
+
if (this.worker) return this.worker;
|
|
273
|
+
if (!this.isSupported()) {
|
|
274
|
+
throw new Error("Web Workers are not supported in this environment");
|
|
275
|
+
}
|
|
276
|
+
this.workerReadyPromise = new Promise((resolve, reject) => {
|
|
277
|
+
this.workerReadyResolve = resolve;
|
|
278
|
+
this.workerReadyReject = (error) => reject(error);
|
|
279
|
+
});
|
|
280
|
+
const workerUrl = this.debug && !this.options.workerUrl.includes("?") ? `${this.options.workerUrl}?dev=${Date.now()}` : this.options.workerUrl;
|
|
281
|
+
this.worker = new Worker(workerUrl);
|
|
282
|
+
this.worker.onmessage = (event) => {
|
|
283
|
+
const { id, type, payload } = event.data;
|
|
284
|
+
if (type === "worker-ready") {
|
|
285
|
+
this.workerReadyResolve?.();
|
|
286
|
+
this.workerReadyResolve = null;
|
|
287
|
+
this.workerReadyReject = null;
|
|
288
|
+
if (this.debug) console.log("[JavaScriptWorkerClient] worker-ready");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (id) {
|
|
292
|
+
const pending = this.pendingMessages.get(id);
|
|
293
|
+
if (!pending) return;
|
|
294
|
+
this.pendingMessages.delete(id);
|
|
295
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
296
|
+
if (type === "error") {
|
|
297
|
+
pending.reject(new Error(payload.error));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
pending.resolve(payload);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
this.worker.onerror = (error) => {
|
|
304
|
+
console.error("[JavaScriptWorkerClient] Worker error:", error);
|
|
305
|
+
const workerError = new Error("Worker error");
|
|
306
|
+
this.workerReadyReject?.(workerError);
|
|
307
|
+
this.workerReadyResolve = null;
|
|
308
|
+
this.workerReadyReject = null;
|
|
309
|
+
for (const [id, pending] of this.pendingMessages) {
|
|
310
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
311
|
+
pending.reject(workerError);
|
|
312
|
+
this.pendingMessages.delete(id);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
return this.worker;
|
|
316
|
+
}
|
|
317
|
+
async waitForWorkerReady() {
|
|
318
|
+
const readyPromise = this.workerReadyPromise;
|
|
319
|
+
if (!readyPromise) return;
|
|
320
|
+
await new Promise((resolve, reject) => {
|
|
321
|
+
let settled = false;
|
|
322
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
323
|
+
if (settled) return;
|
|
324
|
+
settled = true;
|
|
325
|
+
const timeoutError = new Error(
|
|
326
|
+
`JavaScript worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS / 1e3)}s)`
|
|
327
|
+
);
|
|
328
|
+
this.terminateAndReset(timeoutError);
|
|
329
|
+
reject(timeoutError);
|
|
330
|
+
}, WORKER_READY_TIMEOUT_MS);
|
|
331
|
+
readyPromise.then(() => {
|
|
332
|
+
if (settled) return;
|
|
333
|
+
settled = true;
|
|
334
|
+
globalThis.clearTimeout(timeoutId);
|
|
335
|
+
resolve();
|
|
336
|
+
}).catch((error) => {
|
|
337
|
+
if (settled) return;
|
|
338
|
+
settled = true;
|
|
339
|
+
globalThis.clearTimeout(timeoutId);
|
|
340
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS) {
|
|
345
|
+
const worker = this.getWorker();
|
|
346
|
+
await this.waitForWorkerReady();
|
|
347
|
+
const id = String(++this.messageId);
|
|
348
|
+
return new Promise((resolve, reject) => {
|
|
349
|
+
this.pendingMessages.set(id, {
|
|
350
|
+
resolve,
|
|
351
|
+
reject
|
|
352
|
+
});
|
|
353
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
354
|
+
const pending2 = this.pendingMessages.get(id);
|
|
355
|
+
if (!pending2) return;
|
|
356
|
+
this.pendingMessages.delete(id);
|
|
357
|
+
pending2.reject(new Error(`Worker request timed out: ${type}`));
|
|
358
|
+
}, timeoutMs);
|
|
359
|
+
const pending = this.pendingMessages.get(id);
|
|
360
|
+
if (pending) pending.timeoutId = timeoutId;
|
|
361
|
+
worker.postMessage({ id, type, payload });
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
async executeWithTimeout(executor, timeoutMs) {
|
|
365
|
+
return new Promise((resolve, reject) => {
|
|
366
|
+
let settled = false;
|
|
367
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
368
|
+
if (settled) return;
|
|
369
|
+
settled = true;
|
|
370
|
+
this.terminateAndReset();
|
|
371
|
+
reject(
|
|
372
|
+
new Error(
|
|
373
|
+
`Execution timed out (possible infinite loop). Code execution was stopped after ${Math.round(timeoutMs / 1e3)} seconds.`
|
|
374
|
+
)
|
|
375
|
+
);
|
|
376
|
+
}, timeoutMs);
|
|
377
|
+
executor().then((result) => {
|
|
378
|
+
if (settled) return;
|
|
379
|
+
settled = true;
|
|
380
|
+
globalThis.clearTimeout(timeoutId);
|
|
381
|
+
resolve(result);
|
|
382
|
+
}).catch((error) => {
|
|
383
|
+
if (settled) return;
|
|
384
|
+
settled = true;
|
|
385
|
+
globalThis.clearTimeout(timeoutId);
|
|
386
|
+
reject(error);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
terminateAndReset(reason = new Error("Worker was terminated")) {
|
|
391
|
+
this.workerReadyReject?.(reason);
|
|
392
|
+
if (this.worker) {
|
|
393
|
+
this.worker.terminate();
|
|
394
|
+
this.worker = null;
|
|
395
|
+
}
|
|
396
|
+
this.initPromise = null;
|
|
397
|
+
this.isInitializing = false;
|
|
398
|
+
this.workerReadyPromise = null;
|
|
399
|
+
this.workerReadyResolve = null;
|
|
400
|
+
this.workerReadyReject = null;
|
|
401
|
+
for (const [, pending] of this.pendingMessages) {
|
|
402
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
403
|
+
pending.reject(reason);
|
|
404
|
+
}
|
|
405
|
+
this.pendingMessages.clear();
|
|
406
|
+
}
|
|
407
|
+
async init() {
|
|
408
|
+
if (this.initPromise) return this.initPromise;
|
|
409
|
+
if (this.isInitializing) {
|
|
410
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, 100));
|
|
411
|
+
return this.init();
|
|
412
|
+
}
|
|
413
|
+
this.isInitializing = true;
|
|
414
|
+
this.initPromise = this.sendMessage("init", void 0, INIT_TIMEOUT_MS);
|
|
415
|
+
try {
|
|
416
|
+
return await this.initPromise;
|
|
417
|
+
} catch (error) {
|
|
418
|
+
this.initPromise = null;
|
|
419
|
+
throw error;
|
|
420
|
+
} finally {
|
|
421
|
+
this.isInitializing = false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function", language = "javascript") {
|
|
425
|
+
await this.init();
|
|
426
|
+
return this.executeWithTimeout(
|
|
427
|
+
() => this.sendMessage(
|
|
428
|
+
"execute-with-tracing",
|
|
429
|
+
{
|
|
430
|
+
code,
|
|
431
|
+
functionName,
|
|
432
|
+
inputs,
|
|
433
|
+
options,
|
|
434
|
+
executionStyle,
|
|
435
|
+
language
|
|
436
|
+
},
|
|
437
|
+
TRACING_TIMEOUT_MS + 2e3
|
|
438
|
+
),
|
|
439
|
+
TRACING_TIMEOUT_MS
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
async executeCode(code, functionName, inputs, executionStyle = "function", language = "javascript") {
|
|
443
|
+
await this.init();
|
|
444
|
+
return this.executeWithTimeout(
|
|
445
|
+
() => this.sendMessage(
|
|
446
|
+
"execute-code",
|
|
447
|
+
{
|
|
448
|
+
code,
|
|
449
|
+
functionName,
|
|
450
|
+
inputs,
|
|
451
|
+
executionStyle,
|
|
452
|
+
language
|
|
453
|
+
},
|
|
454
|
+
EXECUTION_TIMEOUT_MS + 2e3
|
|
455
|
+
),
|
|
456
|
+
EXECUTION_TIMEOUT_MS
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function", language = "javascript") {
|
|
460
|
+
await this.init();
|
|
461
|
+
try {
|
|
462
|
+
const result = await this.executeWithTimeout(
|
|
463
|
+
() => this.sendMessage(
|
|
464
|
+
"execute-code-interview",
|
|
465
|
+
{
|
|
466
|
+
code,
|
|
467
|
+
functionName,
|
|
468
|
+
inputs,
|
|
469
|
+
executionStyle,
|
|
470
|
+
language
|
|
471
|
+
},
|
|
472
|
+
INTERVIEW_MODE_TIMEOUT_MS + 2e3
|
|
473
|
+
),
|
|
474
|
+
INTERVIEW_MODE_TIMEOUT_MS
|
|
475
|
+
);
|
|
476
|
+
if (!result.success && result.error) {
|
|
477
|
+
const normalized = result.error.toLowerCase();
|
|
478
|
+
const isTimeoutOrResourceLimit = normalized.includes("timed out") || normalized.includes("infinite loop") || normalized.includes("line-limit") || normalized.includes("single-line-limit") || normalized.includes("recursion-limit") || normalized.includes("trace-limit") || normalized.includes("line events") || normalized.includes("trace steps") || normalized.includes("call depth");
|
|
479
|
+
if (isTimeoutOrResourceLimit) {
|
|
480
|
+
return {
|
|
481
|
+
success: false,
|
|
482
|
+
output: null,
|
|
483
|
+
error: "Time Limit Exceeded",
|
|
484
|
+
consoleOutput: result.consoleOutput ?? []
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return result;
|
|
489
|
+
} catch {
|
|
490
|
+
return {
|
|
491
|
+
success: false,
|
|
492
|
+
output: null,
|
|
493
|
+
error: "Time Limit Exceeded",
|
|
494
|
+
consoleOutput: []
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
terminate() {
|
|
499
|
+
this.terminateAndReset();
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// packages/harness-browser/src/runtime-capability-guards.ts
|
|
504
|
+
function isScriptRequest(functionName) {
|
|
505
|
+
if (functionName == null) return true;
|
|
506
|
+
return functionName.trim().length === 0;
|
|
507
|
+
}
|
|
508
|
+
function executionStyleLabel(executionStyle) {
|
|
509
|
+
if (executionStyle === "solution-method") return "solution-method";
|
|
510
|
+
if (executionStyle === "ops-class") return "ops-class";
|
|
511
|
+
return "function";
|
|
512
|
+
}
|
|
513
|
+
function isExecutionStyleSupported(profile, executionStyle) {
|
|
514
|
+
const styles = profile.capabilities.execution.styles;
|
|
515
|
+
if (executionStyle === "solution-method") return styles.solutionMethod;
|
|
516
|
+
if (executionStyle === "ops-class") return styles.opsClass;
|
|
517
|
+
return styles.function;
|
|
518
|
+
}
|
|
519
|
+
function describeRequest(request) {
|
|
520
|
+
if (request === "trace") return "tracing";
|
|
521
|
+
if (request === "interview") return "interview execution";
|
|
522
|
+
return "execution";
|
|
523
|
+
}
|
|
524
|
+
function assertRuntimeRequestSupported(profile, options) {
|
|
525
|
+
if (options.request === "trace" && !profile.capabilities.tracing.supported) {
|
|
526
|
+
throw new Error(`Runtime "${profile.language}" does not support tracing.`);
|
|
527
|
+
}
|
|
528
|
+
if (options.request === "interview" && !profile.capabilities.execution.styles.interviewMode) {
|
|
529
|
+
throw new Error(`Runtime "${profile.language}" does not support interview execution.`);
|
|
530
|
+
}
|
|
531
|
+
if (!isExecutionStyleSupported(profile, options.executionStyle)) {
|
|
532
|
+
throw new Error(
|
|
533
|
+
`Runtime "${profile.language}" does not support execution style "${executionStyleLabel(options.executionStyle)}".`
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (isScriptRequest(options.functionName) && !profile.capabilities.execution.styles.script) {
|
|
537
|
+
throw new Error(`Runtime "${profile.language}" does not support script mode ${describeRequest(options.request)}.`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// packages/harness-browser/src/runtime-profiles.ts
|
|
542
|
+
var PYTHON_RUNTIME_PROFILE = {
|
|
543
|
+
language: "python",
|
|
544
|
+
maturity: "stable",
|
|
545
|
+
capabilities: {
|
|
546
|
+
execution: {
|
|
547
|
+
styles: {
|
|
548
|
+
function: true,
|
|
549
|
+
solutionMethod: true,
|
|
550
|
+
opsClass: true,
|
|
551
|
+
script: true,
|
|
552
|
+
interviewMode: true
|
|
553
|
+
},
|
|
554
|
+
timeouts: {
|
|
555
|
+
clientTimeouts: true,
|
|
556
|
+
runtimeTimeouts: true
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
tracing: {
|
|
560
|
+
supported: true,
|
|
561
|
+
events: {
|
|
562
|
+
line: true,
|
|
563
|
+
call: true,
|
|
564
|
+
return: true,
|
|
565
|
+
exception: true,
|
|
566
|
+
stdout: true,
|
|
567
|
+
timeout: true
|
|
568
|
+
},
|
|
569
|
+
controls: {
|
|
570
|
+
maxTraceSteps: true,
|
|
571
|
+
maxLineEvents: true,
|
|
572
|
+
maxSingleLineHits: true,
|
|
573
|
+
minimalTrace: true
|
|
574
|
+
},
|
|
575
|
+
fidelity: {
|
|
576
|
+
preciseLineMapping: true,
|
|
577
|
+
stableFunctionNames: true,
|
|
578
|
+
callStack: true
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
diagnostics: {
|
|
582
|
+
compileErrors: false,
|
|
583
|
+
runtimeErrors: true,
|
|
584
|
+
mappedErrorLines: true,
|
|
585
|
+
stackTraces: false
|
|
586
|
+
},
|
|
587
|
+
structures: {
|
|
588
|
+
treeNodeRefs: true,
|
|
589
|
+
listNodeRefs: true,
|
|
590
|
+
mapSerialization: true,
|
|
591
|
+
setSerialization: true,
|
|
592
|
+
graphSerialization: true,
|
|
593
|
+
cycleReferences: true
|
|
594
|
+
},
|
|
595
|
+
visualization: {
|
|
596
|
+
runtimePayloads: true,
|
|
597
|
+
objectKinds: true,
|
|
598
|
+
hashMaps: true,
|
|
599
|
+
stepVisualization: true
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
var JAVASCRIPT_RUNTIME_PROFILE = {
|
|
604
|
+
language: "javascript",
|
|
605
|
+
maturity: "stable",
|
|
606
|
+
capabilities: {
|
|
607
|
+
execution: {
|
|
608
|
+
styles: {
|
|
609
|
+
function: true,
|
|
610
|
+
solutionMethod: true,
|
|
611
|
+
opsClass: true,
|
|
612
|
+
script: true,
|
|
613
|
+
interviewMode: true
|
|
614
|
+
},
|
|
615
|
+
timeouts: {
|
|
616
|
+
clientTimeouts: true,
|
|
617
|
+
runtimeTimeouts: false
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
tracing: {
|
|
621
|
+
supported: true,
|
|
622
|
+
events: {
|
|
623
|
+
line: true,
|
|
624
|
+
call: true,
|
|
625
|
+
return: true,
|
|
626
|
+
exception: true,
|
|
627
|
+
stdout: false,
|
|
628
|
+
timeout: true
|
|
629
|
+
},
|
|
630
|
+
controls: {
|
|
631
|
+
maxTraceSteps: true,
|
|
632
|
+
maxLineEvents: true,
|
|
633
|
+
maxSingleLineHits: true,
|
|
634
|
+
minimalTrace: true
|
|
635
|
+
},
|
|
636
|
+
fidelity: {
|
|
637
|
+
preciseLineMapping: true,
|
|
638
|
+
stableFunctionNames: true,
|
|
639
|
+
callStack: true
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
diagnostics: {
|
|
643
|
+
compileErrors: false,
|
|
644
|
+
runtimeErrors: true,
|
|
645
|
+
mappedErrorLines: false,
|
|
646
|
+
stackTraces: false
|
|
647
|
+
},
|
|
648
|
+
structures: {
|
|
649
|
+
treeNodeRefs: true,
|
|
650
|
+
listNodeRefs: true,
|
|
651
|
+
mapSerialization: true,
|
|
652
|
+
setSerialization: true,
|
|
653
|
+
graphSerialization: true,
|
|
654
|
+
cycleReferences: true
|
|
655
|
+
},
|
|
656
|
+
visualization: {
|
|
657
|
+
runtimePayloads: true,
|
|
658
|
+
objectKinds: true,
|
|
659
|
+
hashMaps: true,
|
|
660
|
+
stepVisualization: true
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
665
|
+
language: "typescript",
|
|
666
|
+
maturity: "stable",
|
|
667
|
+
capabilities: {
|
|
668
|
+
execution: {
|
|
669
|
+
styles: {
|
|
670
|
+
function: true,
|
|
671
|
+
solutionMethod: true,
|
|
672
|
+
opsClass: true,
|
|
673
|
+
script: true,
|
|
674
|
+
interviewMode: true
|
|
675
|
+
},
|
|
676
|
+
timeouts: {
|
|
677
|
+
clientTimeouts: true,
|
|
678
|
+
runtimeTimeouts: false
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
tracing: {
|
|
682
|
+
supported: true,
|
|
683
|
+
events: {
|
|
684
|
+
line: true,
|
|
685
|
+
call: true,
|
|
686
|
+
return: true,
|
|
687
|
+
exception: true,
|
|
688
|
+
stdout: false,
|
|
689
|
+
timeout: true
|
|
690
|
+
},
|
|
691
|
+
controls: {
|
|
692
|
+
maxTraceSteps: true,
|
|
693
|
+
maxLineEvents: true,
|
|
694
|
+
maxSingleLineHits: true,
|
|
695
|
+
minimalTrace: true
|
|
696
|
+
},
|
|
697
|
+
fidelity: {
|
|
698
|
+
preciseLineMapping: true,
|
|
699
|
+
stableFunctionNames: true,
|
|
700
|
+
callStack: true
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
diagnostics: {
|
|
704
|
+
compileErrors: true,
|
|
705
|
+
runtimeErrors: true,
|
|
706
|
+
mappedErrorLines: true,
|
|
707
|
+
stackTraces: false
|
|
708
|
+
},
|
|
709
|
+
structures: {
|
|
710
|
+
treeNodeRefs: true,
|
|
711
|
+
listNodeRefs: true,
|
|
712
|
+
mapSerialization: true,
|
|
713
|
+
setSerialization: true,
|
|
714
|
+
graphSerialization: true,
|
|
715
|
+
cycleReferences: true
|
|
716
|
+
},
|
|
717
|
+
visualization: {
|
|
718
|
+
runtimePayloads: true,
|
|
719
|
+
objectKinds: true,
|
|
720
|
+
hashMaps: true,
|
|
721
|
+
stepVisualization: true
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
var LANGUAGE_RUNTIME_PROFILES = {
|
|
726
|
+
python: PYTHON_RUNTIME_PROFILE,
|
|
727
|
+
javascript: JAVASCRIPT_RUNTIME_PROFILE,
|
|
728
|
+
typescript: TYPESCRIPT_RUNTIME_PROFILE
|
|
729
|
+
};
|
|
730
|
+
var SUPPORTED_LANGUAGES = Object.freeze(
|
|
731
|
+
Object.keys(LANGUAGE_RUNTIME_PROFILES)
|
|
732
|
+
);
|
|
733
|
+
function getLanguageRuntimeProfile(language) {
|
|
734
|
+
const profile = LANGUAGE_RUNTIME_PROFILES[language];
|
|
735
|
+
if (!profile) {
|
|
736
|
+
throw new Error(`Runtime profile for language "${language}" is not implemented yet.`);
|
|
737
|
+
}
|
|
738
|
+
return profile;
|
|
739
|
+
}
|
|
740
|
+
function getSupportedLanguageProfiles() {
|
|
741
|
+
return SUPPORTED_LANGUAGES.map((language) => LANGUAGE_RUNTIME_PROFILES[language]);
|
|
742
|
+
}
|
|
743
|
+
function isLanguageSupported(language) {
|
|
744
|
+
return SUPPORTED_LANGUAGES.includes(language);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// packages/harness-browser/src/javascript-runtime-client.ts
|
|
748
|
+
var JavaScriptRuntimeClient = class {
|
|
749
|
+
constructor(runtimeLanguage, workerClient) {
|
|
750
|
+
this.runtimeLanguage = runtimeLanguage;
|
|
751
|
+
this.workerClient = workerClient;
|
|
752
|
+
}
|
|
753
|
+
async init() {
|
|
754
|
+
return this.workerClient.init();
|
|
755
|
+
}
|
|
756
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
|
|
757
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile(this.runtimeLanguage), {
|
|
758
|
+
request: "trace",
|
|
759
|
+
executionStyle,
|
|
760
|
+
functionName
|
|
761
|
+
});
|
|
762
|
+
const rawResult = await this.workerClient.executeWithTracing(
|
|
763
|
+
code,
|
|
764
|
+
functionName,
|
|
765
|
+
inputs,
|
|
766
|
+
options,
|
|
767
|
+
executionStyle,
|
|
768
|
+
this.runtimeLanguage
|
|
769
|
+
);
|
|
770
|
+
return adaptJavaScriptTraceExecutionResult(this.runtimeLanguage, rawResult);
|
|
771
|
+
}
|
|
772
|
+
async executeCode(code, functionName, inputs, executionStyle = "function") {
|
|
773
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile(this.runtimeLanguage), {
|
|
774
|
+
request: "execute",
|
|
775
|
+
executionStyle,
|
|
776
|
+
functionName
|
|
777
|
+
});
|
|
778
|
+
return this.workerClient.executeCode(
|
|
779
|
+
code,
|
|
780
|
+
functionName,
|
|
781
|
+
inputs,
|
|
782
|
+
executionStyle,
|
|
783
|
+
this.runtimeLanguage
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
|
|
787
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile(this.runtimeLanguage), {
|
|
788
|
+
request: "interview",
|
|
789
|
+
executionStyle,
|
|
790
|
+
functionName
|
|
791
|
+
});
|
|
792
|
+
return this.workerClient.executeCodeInterviewMode(
|
|
793
|
+
code,
|
|
794
|
+
functionName,
|
|
795
|
+
inputs,
|
|
796
|
+
executionStyle,
|
|
797
|
+
this.runtimeLanguage
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
function createJavaScriptRuntimeClient(runtimeLanguage, workerClient) {
|
|
802
|
+
return new JavaScriptRuntimeClient(runtimeLanguage, workerClient);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// packages/harness-browser/src/pyodide-worker-client.ts
|
|
806
|
+
var EXECUTION_TIMEOUT_MS2 = 1e4;
|
|
807
|
+
var INTERVIEW_MODE_TIMEOUT_MS2 = 5e3;
|
|
808
|
+
var TRACING_TIMEOUT_MS2 = 3e4;
|
|
809
|
+
var INIT_TIMEOUT_MS2 = 12e4;
|
|
810
|
+
var MESSAGE_TIMEOUT_MS2 = 2e4;
|
|
811
|
+
var WORKER_READY_TIMEOUT_MS2 = 1e4;
|
|
812
|
+
var PyodideWorkerClient = class {
|
|
813
|
+
constructor(options) {
|
|
814
|
+
this.options = options;
|
|
815
|
+
this.debug = options.debug ?? process.env.NODE_ENV === "development";
|
|
816
|
+
}
|
|
817
|
+
worker = null;
|
|
818
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
819
|
+
messageId = 0;
|
|
820
|
+
isInitializing = false;
|
|
821
|
+
initPromise = null;
|
|
822
|
+
workerReadyPromise = null;
|
|
823
|
+
workerReadyResolve = null;
|
|
824
|
+
workerReadyReject = null;
|
|
825
|
+
debug;
|
|
826
|
+
/**
|
|
827
|
+
* Check if Web Workers are supported
|
|
828
|
+
*/
|
|
829
|
+
isSupported() {
|
|
830
|
+
return typeof Worker !== "undefined";
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Get or create the worker instance
|
|
834
|
+
*/
|
|
835
|
+
getWorker() {
|
|
836
|
+
if (this.worker) return this.worker;
|
|
837
|
+
if (!this.isSupported()) {
|
|
838
|
+
throw new Error("Web Workers are not supported in this environment");
|
|
839
|
+
}
|
|
840
|
+
this.workerReadyPromise = new Promise((resolve, reject) => {
|
|
841
|
+
this.workerReadyResolve = resolve;
|
|
842
|
+
this.workerReadyReject = (error) => reject(error);
|
|
843
|
+
});
|
|
844
|
+
const workerUrl = this.debug && !this.options.workerUrl.includes("?") ? `${this.options.workerUrl}?dev=${Date.now()}` : this.options.workerUrl;
|
|
845
|
+
this.worker = new Worker(workerUrl);
|
|
846
|
+
this.worker.onmessage = (event) => {
|
|
847
|
+
const { id, type, payload } = event.data;
|
|
848
|
+
if (type === "worker-ready") {
|
|
849
|
+
this.workerReadyResolve?.();
|
|
850
|
+
this.workerReadyResolve = null;
|
|
851
|
+
this.workerReadyReject = null;
|
|
852
|
+
if (this.debug) console.log("[PyodideWorkerClient] worker-ready");
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (this.debug && !id) {
|
|
856
|
+
console.log("[PyodideWorkerClient] event", { type, payload });
|
|
857
|
+
}
|
|
858
|
+
if (id) {
|
|
859
|
+
const pending = this.pendingMessages.get(id);
|
|
860
|
+
if (pending) {
|
|
861
|
+
this.pendingMessages.delete(id);
|
|
862
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
863
|
+
if (type === "error") {
|
|
864
|
+
pending.reject(new Error(payload.error));
|
|
865
|
+
} else {
|
|
866
|
+
if (this.debug) console.log("[PyodideWorkerClient] recv", { id, type });
|
|
867
|
+
pending.resolve(payload);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
this.worker.onerror = (error) => {
|
|
873
|
+
console.error("[PyodideWorkerClient] Worker error:", error);
|
|
874
|
+
const workerError = new Error("Worker error");
|
|
875
|
+
this.workerReadyReject?.(workerError);
|
|
876
|
+
this.workerReadyResolve = null;
|
|
877
|
+
this.workerReadyReject = null;
|
|
878
|
+
for (const [id, pending] of this.pendingMessages) {
|
|
879
|
+
if (pending.timeoutId) {
|
|
880
|
+
globalThis.clearTimeout(pending.timeoutId);
|
|
881
|
+
}
|
|
882
|
+
pending.reject(workerError);
|
|
883
|
+
this.pendingMessages.delete(id);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
return this.worker;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Wait for worker bootstrap signal with timeout.
|
|
890
|
+
* Guards against deadlocks when the worker script fails before posting "worker-ready".
|
|
891
|
+
*/
|
|
892
|
+
async waitForWorkerReady() {
|
|
893
|
+
const readyPromise = this.workerReadyPromise;
|
|
894
|
+
if (!readyPromise) return;
|
|
895
|
+
await new Promise((resolve, reject) => {
|
|
896
|
+
let settled = false;
|
|
897
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
898
|
+
if (settled) return;
|
|
899
|
+
settled = true;
|
|
900
|
+
const timeoutError = new Error(
|
|
901
|
+
`Python worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS2 / 1e3)}s)`
|
|
902
|
+
);
|
|
903
|
+
if (this.debug) {
|
|
904
|
+
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs: WORKER_READY_TIMEOUT_MS2 });
|
|
905
|
+
}
|
|
906
|
+
this.terminateAndReset(timeoutError);
|
|
907
|
+
reject(timeoutError);
|
|
908
|
+
}, WORKER_READY_TIMEOUT_MS2);
|
|
909
|
+
readyPromise.then(() => {
|
|
910
|
+
if (settled) return;
|
|
911
|
+
settled = true;
|
|
912
|
+
globalThis.clearTimeout(timeoutId);
|
|
913
|
+
resolve();
|
|
914
|
+
}).catch((error) => {
|
|
915
|
+
if (settled) return;
|
|
916
|
+
settled = true;
|
|
917
|
+
globalThis.clearTimeout(timeoutId);
|
|
918
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
919
|
+
});
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Send a message to the worker and wait for a response
|
|
924
|
+
*/
|
|
925
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS2) {
|
|
926
|
+
const worker = this.getWorker();
|
|
927
|
+
await this.waitForWorkerReady();
|
|
928
|
+
const id = String(++this.messageId);
|
|
929
|
+
return new Promise((resolve, reject) => {
|
|
930
|
+
this.pendingMessages.set(id, {
|
|
931
|
+
resolve,
|
|
932
|
+
reject
|
|
933
|
+
});
|
|
934
|
+
if (this.debug) console.log("[PyodideWorkerClient] send", { id, type });
|
|
935
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
936
|
+
const pending2 = this.pendingMessages.get(id);
|
|
937
|
+
if (!pending2) return;
|
|
938
|
+
this.pendingMessages.delete(id);
|
|
939
|
+
if (this.debug) console.warn("[PyodideWorkerClient] timeout", { id, type });
|
|
940
|
+
pending2.reject(new Error(`Worker request timed out: ${type}`));
|
|
941
|
+
}, timeoutMs);
|
|
942
|
+
const pending = this.pendingMessages.get(id);
|
|
943
|
+
if (pending) pending.timeoutId = timeoutId;
|
|
944
|
+
worker.postMessage({ id, type, payload });
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Execute code with a timeout - terminates worker if execution takes too long
|
|
949
|
+
*/
|
|
950
|
+
async executeWithTimeout(executor, timeoutMs = EXECUTION_TIMEOUT_MS2) {
|
|
951
|
+
return new Promise((resolve, reject) => {
|
|
952
|
+
let settled = false;
|
|
953
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
954
|
+
if (settled) return;
|
|
955
|
+
settled = true;
|
|
956
|
+
if (this.debug) {
|
|
957
|
+
console.warn("[PyodideWorkerClient] Execution timeout - terminating worker");
|
|
958
|
+
}
|
|
959
|
+
this.terminateAndReset();
|
|
960
|
+
const seconds = Math.round(timeoutMs / 1e3);
|
|
961
|
+
reject(new Error(`Execution timed out (possible infinite loop). Code execution was stopped after ${seconds} seconds.`));
|
|
962
|
+
}, timeoutMs);
|
|
963
|
+
executor().then((result) => {
|
|
964
|
+
if (settled) return;
|
|
965
|
+
settled = true;
|
|
966
|
+
globalThis.clearTimeout(timeoutId);
|
|
967
|
+
resolve(result);
|
|
968
|
+
}).catch((error) => {
|
|
969
|
+
if (settled) return;
|
|
970
|
+
settled = true;
|
|
971
|
+
globalThis.clearTimeout(timeoutId);
|
|
972
|
+
reject(error);
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Terminate the worker and reset state for recreation
|
|
978
|
+
*/
|
|
979
|
+
terminateAndReset(reason = new Error("Worker was terminated")) {
|
|
980
|
+
this.workerReadyReject?.(reason);
|
|
981
|
+
if (this.worker) {
|
|
982
|
+
this.worker.terminate();
|
|
983
|
+
this.worker = null;
|
|
984
|
+
}
|
|
985
|
+
this.initPromise = null;
|
|
986
|
+
this.isInitializing = false;
|
|
987
|
+
this.workerReadyPromise = null;
|
|
988
|
+
this.workerReadyResolve = null;
|
|
989
|
+
for (const [, pending] of this.pendingMessages) {
|
|
990
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
991
|
+
pending.reject(reason);
|
|
992
|
+
}
|
|
993
|
+
this.pendingMessages.clear();
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Initialize Pyodide in the worker
|
|
997
|
+
*/
|
|
998
|
+
async init() {
|
|
999
|
+
if (this.initPromise) {
|
|
1000
|
+
return this.initPromise;
|
|
1001
|
+
}
|
|
1002
|
+
if (this.isInitializing) {
|
|
1003
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1004
|
+
return this.init();
|
|
1005
|
+
}
|
|
1006
|
+
this.isInitializing = true;
|
|
1007
|
+
this.initPromise = (async () => {
|
|
1008
|
+
try {
|
|
1009
|
+
return await this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1012
|
+
const shouldRetry = message.includes("Worker request timed out: init") || message.includes("Worker was terminated") || message.includes("Worker error") || message.includes("failed to initialize in time");
|
|
1013
|
+
if (!shouldRetry) {
|
|
1014
|
+
throw error;
|
|
1015
|
+
}
|
|
1016
|
+
if (this.debug) {
|
|
1017
|
+
console.warn("[PyodideWorkerClient] init failed, resetting worker and retrying once", { message });
|
|
1018
|
+
}
|
|
1019
|
+
this.terminateAndReset();
|
|
1020
|
+
return this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
|
|
1021
|
+
}
|
|
1022
|
+
})();
|
|
1023
|
+
try {
|
|
1024
|
+
const result = await this.initPromise;
|
|
1025
|
+
return result;
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
this.initPromise = null;
|
|
1028
|
+
throw error;
|
|
1029
|
+
} finally {
|
|
1030
|
+
this.isInitializing = false;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Execute Python code with tracing for step-by-step visualization
|
|
1035
|
+
* @param options.maxLineEvents - Max line events before abort (for complexity analysis, use higher values)
|
|
1036
|
+
*/
|
|
1037
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
|
|
1038
|
+
await this.init();
|
|
1039
|
+
try {
|
|
1040
|
+
return await this.executeWithTimeout(
|
|
1041
|
+
() => this.sendMessage("execute-with-tracing", {
|
|
1042
|
+
code,
|
|
1043
|
+
functionName,
|
|
1044
|
+
inputs,
|
|
1045
|
+
executionStyle,
|
|
1046
|
+
options
|
|
1047
|
+
}, TRACING_TIMEOUT_MS2 + 5e3),
|
|
1048
|
+
// Message timeout slightly longer than execution timeout
|
|
1049
|
+
TRACING_TIMEOUT_MS2
|
|
1050
|
+
);
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1053
|
+
const isClientTimeout = errorMessage.includes("Execution timed out") || errorMessage.includes("possible infinite loop");
|
|
1054
|
+
if (isClientTimeout) {
|
|
1055
|
+
return {
|
|
1056
|
+
success: false,
|
|
1057
|
+
error: errorMessage,
|
|
1058
|
+
trace: [],
|
|
1059
|
+
executionTimeMs: TRACING_TIMEOUT_MS2,
|
|
1060
|
+
consoleOutput: [],
|
|
1061
|
+
traceLimitExceeded: true,
|
|
1062
|
+
timeoutReason: "client-timeout",
|
|
1063
|
+
lineEventCount: 0,
|
|
1064
|
+
traceStepCount: 0
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
throw error;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Execute Python code without tracing (for running tests)
|
|
1072
|
+
*/
|
|
1073
|
+
async executeCode(code, functionName, inputs, executionStyle = "function") {
|
|
1074
|
+
await this.init();
|
|
1075
|
+
return this.executeWithTimeout(
|
|
1076
|
+
() => this.sendMessage("execute-code", {
|
|
1077
|
+
code,
|
|
1078
|
+
functionName,
|
|
1079
|
+
inputs,
|
|
1080
|
+
executionStyle
|
|
1081
|
+
}, EXECUTION_TIMEOUT_MS2 + 5e3),
|
|
1082
|
+
EXECUTION_TIMEOUT_MS2
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Execute Python code in interview mode - 5 second timeout, generic error messages
|
|
1087
|
+
* Does not reveal which line caused the timeout
|
|
1088
|
+
*/
|
|
1089
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
|
|
1090
|
+
await this.init();
|
|
1091
|
+
try {
|
|
1092
|
+
const result = await this.executeWithTimeout(
|
|
1093
|
+
() => this.sendMessage("execute-code-interview", {
|
|
1094
|
+
code,
|
|
1095
|
+
functionName,
|
|
1096
|
+
inputs,
|
|
1097
|
+
executionStyle
|
|
1098
|
+
}, INTERVIEW_MODE_TIMEOUT_MS2 + 2e3),
|
|
1099
|
+
INTERVIEW_MODE_TIMEOUT_MS2
|
|
1100
|
+
);
|
|
1101
|
+
if (!result.success && result.error) {
|
|
1102
|
+
const normalizedError = result.error.toLowerCase();
|
|
1103
|
+
const isTimeoutOrResourceLimit = normalizedError.includes("timed out") || normalizedError.includes("execution timeout") || normalizedError.includes("infinite loop") || normalizedError.includes("interview_guard_triggered") || normalizedError.includes("memory-limit") || normalizedError.includes("line-limit") || normalizedError.includes("single-line-limit") || normalizedError.includes("recursion-limit");
|
|
1104
|
+
if (isTimeoutOrResourceLimit) {
|
|
1105
|
+
return {
|
|
1106
|
+
success: false,
|
|
1107
|
+
output: null,
|
|
1108
|
+
error: "Time Limit Exceeded",
|
|
1109
|
+
consoleOutput: result.consoleOutput ?? []
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
return result;
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1116
|
+
if (errorMsg.includes("timed out") || errorMsg.includes("Execution timeout")) {
|
|
1117
|
+
return {
|
|
1118
|
+
success: false,
|
|
1119
|
+
output: null,
|
|
1120
|
+
error: "Time Limit Exceeded",
|
|
1121
|
+
consoleOutput: []
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
return {
|
|
1125
|
+
success: false,
|
|
1126
|
+
output: null,
|
|
1127
|
+
error: errorMsg,
|
|
1128
|
+
consoleOutput: []
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Check the status of the worker
|
|
1134
|
+
*/
|
|
1135
|
+
async getStatus() {
|
|
1136
|
+
return this.sendMessage("status");
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Analyze Python code using AST (off main thread)
|
|
1140
|
+
* Returns CodeFacts with semantic information about the code
|
|
1141
|
+
*/
|
|
1142
|
+
async analyzeCode(code) {
|
|
1143
|
+
await this.init();
|
|
1144
|
+
return this.sendMessage("analyze-code", { code }, 5e3);
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Terminate the worker and clean up resources
|
|
1148
|
+
*/
|
|
1149
|
+
terminate() {
|
|
1150
|
+
this.terminateAndReset();
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
// packages/harness-browser/src/python-runtime-client.ts
|
|
1155
|
+
var PythonRuntimeClient = class {
|
|
1156
|
+
constructor(workerClient) {
|
|
1157
|
+
this.workerClient = workerClient;
|
|
1158
|
+
}
|
|
1159
|
+
async init() {
|
|
1160
|
+
return this.workerClient.init();
|
|
1161
|
+
}
|
|
1162
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
|
|
1163
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("python"), {
|
|
1164
|
+
request: "trace",
|
|
1165
|
+
executionStyle,
|
|
1166
|
+
functionName
|
|
1167
|
+
});
|
|
1168
|
+
const rawResult = await this.workerClient.executeWithTracing(
|
|
1169
|
+
code,
|
|
1170
|
+
functionName,
|
|
1171
|
+
inputs,
|
|
1172
|
+
options,
|
|
1173
|
+
executionStyle
|
|
1174
|
+
);
|
|
1175
|
+
return adaptPythonTraceExecutionResult(rawResult);
|
|
1176
|
+
}
|
|
1177
|
+
async executeCode(code, functionName, inputs, executionStyle = "function") {
|
|
1178
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("python"), {
|
|
1179
|
+
request: "execute",
|
|
1180
|
+
executionStyle,
|
|
1181
|
+
functionName
|
|
1182
|
+
});
|
|
1183
|
+
return this.workerClient.executeCode(
|
|
1184
|
+
code,
|
|
1185
|
+
functionName,
|
|
1186
|
+
inputs,
|
|
1187
|
+
executionStyle
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
|
|
1191
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("python"), {
|
|
1192
|
+
request: "interview",
|
|
1193
|
+
executionStyle,
|
|
1194
|
+
functionName
|
|
1195
|
+
});
|
|
1196
|
+
return this.workerClient.executeCodeInterviewMode(
|
|
1197
|
+
code,
|
|
1198
|
+
functionName,
|
|
1199
|
+
inputs,
|
|
1200
|
+
executionStyle
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
function createPythonRuntimeClient(workerClient) {
|
|
1205
|
+
return new PythonRuntimeClient(workerClient);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// packages/harness-browser/src/runtime-assets.ts
|
|
1209
|
+
var DEFAULT_ASSET_BASE_URL = "/workers";
|
|
1210
|
+
var DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS = Object.freeze({
|
|
1211
|
+
pythonWorker: "pyodide-worker.js",
|
|
1212
|
+
pythonRuntimeCore: "pyodide/runtime-core.js",
|
|
1213
|
+
pythonSnippets: "generated-python-harness-snippets.js",
|
|
1214
|
+
javascriptWorker: "javascript-worker.js",
|
|
1215
|
+
typescriptCompiler: "vendor/typescript.js"
|
|
1216
|
+
});
|
|
1217
|
+
function isExplicitAssetPath(pathname) {
|
|
1218
|
+
return pathname.startsWith("/") || pathname.startsWith("./") || pathname.startsWith("../") || pathname.startsWith("http://") || pathname.startsWith("https://") || pathname.startsWith("data:") || pathname.startsWith("blob:");
|
|
1219
|
+
}
|
|
1220
|
+
function stripTrailingSlash(value) {
|
|
1221
|
+
return value.replace(/\/+$/, "");
|
|
1222
|
+
}
|
|
1223
|
+
function trimLeadingSlash(value) {
|
|
1224
|
+
return value.replace(/^\/+/, "");
|
|
1225
|
+
}
|
|
1226
|
+
function resolveAssetPath(baseUrl, pathname) {
|
|
1227
|
+
if (isExplicitAssetPath(pathname)) {
|
|
1228
|
+
return pathname;
|
|
1229
|
+
}
|
|
1230
|
+
const normalizedBase = stripTrailingSlash(baseUrl || DEFAULT_ASSET_BASE_URL);
|
|
1231
|
+
const normalizedPath = trimLeadingSlash(pathname);
|
|
1232
|
+
return `${normalizedBase}/${normalizedPath}`;
|
|
1233
|
+
}
|
|
1234
|
+
function resolveBrowserHarnessAssets(options = {}) {
|
|
1235
|
+
const assetBaseUrl = options.assetBaseUrl ?? DEFAULT_ASSET_BASE_URL;
|
|
1236
|
+
const assets = options.assets ?? {};
|
|
1237
|
+
return {
|
|
1238
|
+
pythonWorker: resolveAssetPath(assetBaseUrl, assets.pythonWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.pythonWorker),
|
|
1239
|
+
pythonRuntimeCore: resolveAssetPath(
|
|
1240
|
+
assetBaseUrl,
|
|
1241
|
+
assets.pythonRuntimeCore ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.pythonRuntimeCore
|
|
1242
|
+
),
|
|
1243
|
+
pythonSnippets: resolveAssetPath(assetBaseUrl, assets.pythonSnippets ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.pythonSnippets),
|
|
1244
|
+
javascriptWorker: resolveAssetPath(
|
|
1245
|
+
assetBaseUrl,
|
|
1246
|
+
assets.javascriptWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javascriptWorker
|
|
1247
|
+
),
|
|
1248
|
+
typescriptCompiler: resolveAssetPath(
|
|
1249
|
+
assetBaseUrl,
|
|
1250
|
+
assets.typescriptCompiler ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.typescriptCompiler
|
|
1251
|
+
)
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
// packages/harness-browser/src/browser-harness.ts
|
|
1256
|
+
var BrowserHarnessRuntime = class {
|
|
1257
|
+
assets;
|
|
1258
|
+
supportedLanguages = SUPPORTED_LANGUAGES;
|
|
1259
|
+
pythonWorkerClient;
|
|
1260
|
+
javaScriptWorkerClient;
|
|
1261
|
+
clients;
|
|
1262
|
+
constructor(options = {}) {
|
|
1263
|
+
this.assets = resolveBrowserHarnessAssets(options);
|
|
1264
|
+
this.pythonWorkerClient = new PyodideWorkerClient({
|
|
1265
|
+
workerUrl: this.assets.pythonWorker,
|
|
1266
|
+
debug: options.debug
|
|
1267
|
+
});
|
|
1268
|
+
this.javaScriptWorkerClient = new JavaScriptWorkerClient({
|
|
1269
|
+
workerUrl: this.assets.javascriptWorker,
|
|
1270
|
+
debug: options.debug
|
|
1271
|
+
});
|
|
1272
|
+
this.clients = {
|
|
1273
|
+
python: createPythonRuntimeClient(this.pythonWorkerClient),
|
|
1274
|
+
javascript: createJavaScriptRuntimeClient("javascript", this.javaScriptWorkerClient),
|
|
1275
|
+
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient)
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
getClient(language) {
|
|
1279
|
+
const client = this.clients[language];
|
|
1280
|
+
if (!client) {
|
|
1281
|
+
throw new Error(`Runtime for language "${language}" is not implemented yet.`);
|
|
1282
|
+
}
|
|
1283
|
+
return client;
|
|
1284
|
+
}
|
|
1285
|
+
getProfile(language) {
|
|
1286
|
+
return getLanguageRuntimeProfile(language);
|
|
1287
|
+
}
|
|
1288
|
+
getSupportedLanguageProfiles() {
|
|
1289
|
+
return getSupportedLanguageProfiles();
|
|
1290
|
+
}
|
|
1291
|
+
isLanguageSupported(language) {
|
|
1292
|
+
return isLanguageSupported(language);
|
|
1293
|
+
}
|
|
1294
|
+
disposeLanguage(language) {
|
|
1295
|
+
if (language === "python") {
|
|
1296
|
+
this.pythonWorkerClient.terminate();
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
this.javaScriptWorkerClient.terminate();
|
|
1300
|
+
}
|
|
1301
|
+
dispose() {
|
|
1302
|
+
this.pythonWorkerClient.terminate();
|
|
1303
|
+
this.javaScriptWorkerClient.terminate();
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
function createBrowserHarness(options = {}) {
|
|
1307
|
+
return new BrowserHarnessRuntime(options);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// packages/harness-python/src/generated/python-harness-snippets.ts
|
|
1311
|
+
function toPythonLiteral(value) {
|
|
1312
|
+
if (value === null || value === void 0) {
|
|
1313
|
+
return "None";
|
|
1314
|
+
}
|
|
1315
|
+
if (typeof value === "boolean") {
|
|
1316
|
+
return value ? "True" : "False";
|
|
1317
|
+
}
|
|
1318
|
+
if (typeof value === "number") {
|
|
1319
|
+
return String(value);
|
|
1320
|
+
}
|
|
1321
|
+
if (typeof value === "string") {
|
|
1322
|
+
return JSON.stringify(value);
|
|
1323
|
+
}
|
|
1324
|
+
if (Array.isArray(value)) {
|
|
1325
|
+
return "[" + value.map(toPythonLiteral).join(", ") + "]";
|
|
1326
|
+
}
|
|
1327
|
+
if (typeof value === "object") {
|
|
1328
|
+
const entries = Object.entries(value).map(([k, v]) => `${JSON.stringify(k)}: ${toPythonLiteral(v)}`).join(", ");
|
|
1329
|
+
return "{" + entries + "}";
|
|
1330
|
+
}
|
|
1331
|
+
return JSON.stringify(value);
|
|
1332
|
+
}
|
|
1333
|
+
var PYTHON_CLASS_DEFINITIONS = `
|
|
1334
|
+
class TreeNode:
|
|
1335
|
+
def __init__(self, val=0, left=None, right=None):
|
|
1336
|
+
self.val = val
|
|
1337
|
+
self.value = val
|
|
1338
|
+
self.left = left
|
|
1339
|
+
self.right = right
|
|
1340
|
+
def __getitem__(self, key):
|
|
1341
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', None))
|
|
1342
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', None))
|
|
1343
|
+
if key == 'left': return self.left
|
|
1344
|
+
if key == 'right': return self.right
|
|
1345
|
+
raise KeyError(key)
|
|
1346
|
+
def get(self, key, default=None):
|
|
1347
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', default))
|
|
1348
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', default))
|
|
1349
|
+
if key == 'left': return self.left
|
|
1350
|
+
if key == 'right': return self.right
|
|
1351
|
+
return default
|
|
1352
|
+
def __repr__(self):
|
|
1353
|
+
return f"TreeNode({getattr(self, 'val', getattr(self, 'value', None))})"
|
|
1354
|
+
|
|
1355
|
+
class ListNode:
|
|
1356
|
+
def __init__(self, val=0, next=None):
|
|
1357
|
+
self.val = val
|
|
1358
|
+
self.value = val
|
|
1359
|
+
self.next = next
|
|
1360
|
+
def __getitem__(self, key):
|
|
1361
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', None))
|
|
1362
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', None))
|
|
1363
|
+
if key == 'next': return self.next
|
|
1364
|
+
raise KeyError(key)
|
|
1365
|
+
def get(self, key, default=None):
|
|
1366
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', default))
|
|
1367
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', default))
|
|
1368
|
+
if key == 'next': return self.next
|
|
1369
|
+
return default
|
|
1370
|
+
def __repr__(self):
|
|
1371
|
+
return f"ListNode({getattr(self, 'val', getattr(self, 'value', None))})"
|
|
1372
|
+
`;
|
|
1373
|
+
var PYTHON_CONVERSION_HELPERS = "\ndef _ensure_node_value_aliases(node):\n if node is None:\n return node\n try:\n has_val = hasattr(node, 'val')\n has_value = hasattr(node, 'value')\n if has_value and not has_val:\n try:\n setattr(node, 'val', getattr(node, 'value'))\n except Exception:\n pass\n elif has_val and not has_value:\n try:\n setattr(node, 'value', getattr(node, 'val'))\n except Exception:\n pass\n except Exception:\n pass\n return node\n\ndef _dict_to_tree(d):\n if d is None:\n return None\n if not isinstance(d, dict):\n return d\n if 'val' not in d and 'value' not in d:\n return d\n node = TreeNode(d.get('val', d.get('value', 0)))\n _ensure_node_value_aliases(node)\n node.left = _dict_to_tree(d.get('left'))\n node.right = _dict_to_tree(d.get('right'))\n return node\n\ndef _dict_to_list(d, _refs=None):\n if _refs is None:\n _refs = {}\n if d is None:\n return None\n if not isinstance(d, dict):\n return d\n if '__ref__' in d:\n return _refs.get(d.get('__ref__'))\n if 'val' not in d and 'value' not in d:\n return d\n node = ListNode(d.get('val', d.get('value', 0)))\n _ensure_node_value_aliases(node)\n node_id = d.get('__id__')\n if isinstance(node_id, str) and node_id:\n _refs[node_id] = node\n node.next = _dict_to_list(d.get('next'), _refs)\n return node\n";
|
|
1374
|
+
var PYTHON_TRACE_SERIALIZE_FUNCTION = `
|
|
1375
|
+
# Sentinel to mark skipped values (functions, etc.) - distinct from None
|
|
1376
|
+
_SKIP_SENTINEL = "__TRACECODE_SKIP__"
|
|
1377
|
+
_MAX_SERIALIZE_DEPTH = 48
|
|
1378
|
+
|
|
1379
|
+
def _serialize(obj, depth=0, node_refs=None):
|
|
1380
|
+
if node_refs is None:
|
|
1381
|
+
node_refs = {}
|
|
1382
|
+
if isinstance(obj, (bool, int, str, type(None))):
|
|
1383
|
+
return obj
|
|
1384
|
+
elif isinstance(obj, float):
|
|
1385
|
+
if not math.isfinite(obj):
|
|
1386
|
+
if math.isnan(obj):
|
|
1387
|
+
return "NaN"
|
|
1388
|
+
return "Infinity" if obj > 0 else "-Infinity"
|
|
1389
|
+
return obj
|
|
1390
|
+
if depth > _MAX_SERIALIZE_DEPTH:
|
|
1391
|
+
return "<max depth>"
|
|
1392
|
+
elif isinstance(obj, (list, tuple)):
|
|
1393
|
+
return [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1394
|
+
elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':
|
|
1395
|
+
return [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1396
|
+
elif isinstance(obj, dict):
|
|
1397
|
+
return {str(k): _serialize(v, depth + 1, node_refs) for k, v in obj.items()}
|
|
1398
|
+
elif isinstance(obj, set):
|
|
1399
|
+
# Use try/except for sorting to handle heterogeneous sets
|
|
1400
|
+
try:
|
|
1401
|
+
sorted_vals = sorted([_serialize(x, depth + 1, node_refs) for x in obj])
|
|
1402
|
+
except TypeError:
|
|
1403
|
+
sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1404
|
+
return {"__type__": "set", "values": sorted_vals}
|
|
1405
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1406
|
+
obj_ref = id(obj)
|
|
1407
|
+
if obj_ref in node_refs:
|
|
1408
|
+
return {"__ref__": node_refs[obj_ref]}
|
|
1409
|
+
node_id = f"tree-{obj_ref}"
|
|
1410
|
+
node_refs[obj_ref] = node_id
|
|
1411
|
+
result = {
|
|
1412
|
+
"__type__": "TreeNode",
|
|
1413
|
+
"__id__": node_id,
|
|
1414
|
+
"val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, node_refs),
|
|
1415
|
+
}
|
|
1416
|
+
if hasattr(obj, 'left'):
|
|
1417
|
+
result["left"] = _serialize(obj.left, depth + 1, node_refs)
|
|
1418
|
+
if hasattr(obj, 'right'):
|
|
1419
|
+
result["right"] = _serialize(obj.right, depth + 1, node_refs)
|
|
1420
|
+
return result
|
|
1421
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
|
|
1422
|
+
obj_ref = id(obj)
|
|
1423
|
+
if obj_ref in node_refs:
|
|
1424
|
+
return {"__ref__": node_refs[obj_ref]}
|
|
1425
|
+
node_id = f"list-{obj_ref}"
|
|
1426
|
+
node_refs[obj_ref] = node_id
|
|
1427
|
+
result = {
|
|
1428
|
+
"__type__": "ListNode",
|
|
1429
|
+
"__id__": node_id,
|
|
1430
|
+
"val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, node_refs),
|
|
1431
|
+
}
|
|
1432
|
+
result["next"] = _serialize(obj.next, depth + 1, node_refs)
|
|
1433
|
+
return result
|
|
1434
|
+
elif callable(obj):
|
|
1435
|
+
# Skip functions entirely - return sentinel
|
|
1436
|
+
return _SKIP_SENTINEL
|
|
1437
|
+
else:
|
|
1438
|
+
repr_str = repr(obj)
|
|
1439
|
+
# Filter out function-like representations (e.g., <function foo at 0x...>)
|
|
1440
|
+
if repr_str.startswith('<') and repr_str.endswith('>'):
|
|
1441
|
+
return _SKIP_SENTINEL
|
|
1442
|
+
return repr_str
|
|
1443
|
+
`;
|
|
1444
|
+
var PYTHON_EXECUTE_SERIALIZE_FUNCTION = `
|
|
1445
|
+
_MAX_SERIALIZE_DEPTH = 48
|
|
1446
|
+
|
|
1447
|
+
def _serialize(obj, depth=0):
|
|
1448
|
+
if isinstance(obj, (bool, int, str, type(None))):
|
|
1449
|
+
return obj
|
|
1450
|
+
elif isinstance(obj, float):
|
|
1451
|
+
if not math.isfinite(obj):
|
|
1452
|
+
if math.isnan(obj):
|
|
1453
|
+
return "NaN"
|
|
1454
|
+
return "Infinity" if obj > 0 else "-Infinity"
|
|
1455
|
+
return obj
|
|
1456
|
+
if depth > _MAX_SERIALIZE_DEPTH:
|
|
1457
|
+
return "<max depth>"
|
|
1458
|
+
elif isinstance(obj, (list, tuple)):
|
|
1459
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1460
|
+
elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':
|
|
1461
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1462
|
+
elif isinstance(obj, dict):
|
|
1463
|
+
return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}
|
|
1464
|
+
elif isinstance(obj, set):
|
|
1465
|
+
try:
|
|
1466
|
+
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1467
|
+
except TypeError:
|
|
1468
|
+
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1469
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1470
|
+
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1471
|
+
if hasattr(obj, 'left'):
|
|
1472
|
+
result["left"] = _serialize(obj.left, depth + 1)
|
|
1473
|
+
if hasattr(obj, 'right'):
|
|
1474
|
+
result["right"] = _serialize(obj.right, depth + 1)
|
|
1475
|
+
return result
|
|
1476
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
|
|
1477
|
+
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1478
|
+
result["next"] = _serialize(obj.next, depth + 1)
|
|
1479
|
+
return result
|
|
1480
|
+
elif callable(obj):
|
|
1481
|
+
return None
|
|
1482
|
+
else:
|
|
1483
|
+
repr_str = repr(obj)
|
|
1484
|
+
if repr_str.startswith('<') and repr_str.endswith('>'):
|
|
1485
|
+
return None
|
|
1486
|
+
return repr_str
|
|
1487
|
+
`;
|
|
1488
|
+
var PYTHON_PRACTICE_MATERIALIZE_SERIALIZE_FUNCTION = `
|
|
1489
|
+
def _serialize(obj, depth=0, state=None):
|
|
1490
|
+
if state is None:
|
|
1491
|
+
state = {"nodes": 0, "seen": set()}
|
|
1492
|
+
if depth > 64:
|
|
1493
|
+
return "__MAX_DEPTH__"
|
|
1494
|
+
if isinstance(obj, (int, float, str, bool, type(None))):
|
|
1495
|
+
return obj
|
|
1496
|
+
|
|
1497
|
+
state["nodes"] += 1
|
|
1498
|
+
if state["nodes"] > 600:
|
|
1499
|
+
return "__MAX_NODES__"
|
|
1500
|
+
|
|
1501
|
+
if isinstance(obj, (list, tuple)):
|
|
1502
|
+
return [_serialize(x, depth + 1, state) for x in obj]
|
|
1503
|
+
elif isinstance(obj, dict):
|
|
1504
|
+
return {str(k): _serialize(v, depth + 1, state) for k, v in obj.items()}
|
|
1505
|
+
elif isinstance(obj, set):
|
|
1506
|
+
serialized = [_serialize(x, depth + 1, state) for x in obj]
|
|
1507
|
+
try:
|
|
1508
|
+
serialized = sorted(serialized)
|
|
1509
|
+
except TypeError:
|
|
1510
|
+
pass
|
|
1511
|
+
return {"__type__": "set", "values": serialized}
|
|
1512
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1513
|
+
obj_id = id(obj)
|
|
1514
|
+
if obj_id in state["seen"]:
|
|
1515
|
+
return "__CYCLE__"
|
|
1516
|
+
state["seen"].add(obj_id)
|
|
1517
|
+
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, state)}
|
|
1518
|
+
if hasattr(obj, 'left'):
|
|
1519
|
+
result["left"] = _serialize(obj.left, depth + 1, state)
|
|
1520
|
+
if hasattr(obj, 'right'):
|
|
1521
|
+
result["right"] = _serialize(obj.right, depth + 1, state)
|
|
1522
|
+
state["seen"].remove(obj_id)
|
|
1523
|
+
return result
|
|
1524
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
|
|
1525
|
+
obj_id = id(obj)
|
|
1526
|
+
if obj_id in state["seen"]:
|
|
1527
|
+
return "__CYCLE__"
|
|
1528
|
+
state["seen"].add(obj_id)
|
|
1529
|
+
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, state)}
|
|
1530
|
+
result["next"] = _serialize(obj.next, depth + 1, state)
|
|
1531
|
+
state["seen"].remove(obj_id)
|
|
1532
|
+
return result
|
|
1533
|
+
else:
|
|
1534
|
+
return repr(obj)
|
|
1535
|
+
`;
|
|
1536
|
+
var PYTHON_INTERVIEW_MATERIALIZE_SERIALIZE_FUNCTION = `
|
|
1537
|
+
def _serialize(obj, depth=0):
|
|
1538
|
+
if depth > 10:
|
|
1539
|
+
return "<max depth>"
|
|
1540
|
+
if isinstance(obj, (int, float, str, bool, type(None))):
|
|
1541
|
+
return obj
|
|
1542
|
+
elif isinstance(obj, (list, tuple)):
|
|
1543
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1544
|
+
elif isinstance(obj, dict):
|
|
1545
|
+
return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}
|
|
1546
|
+
elif isinstance(obj, set):
|
|
1547
|
+
try:
|
|
1548
|
+
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1549
|
+
except TypeError:
|
|
1550
|
+
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1551
|
+
elif hasattr(obj, 'val') and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1552
|
+
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
1553
|
+
if hasattr(obj, 'left'):
|
|
1554
|
+
result["left"] = _serialize(obj.left, depth + 1)
|
|
1555
|
+
if hasattr(obj, 'right'):
|
|
1556
|
+
result["right"] = _serialize(obj.right, depth + 1)
|
|
1557
|
+
return result
|
|
1558
|
+
elif hasattr(obj, 'val') and hasattr(obj, 'next'):
|
|
1559
|
+
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
1560
|
+
result["next"] = _serialize(obj.next, depth + 1)
|
|
1561
|
+
return result
|
|
1562
|
+
else:
|
|
1563
|
+
return repr(obj)
|
|
1564
|
+
`;
|
|
1565
|
+
var PYTHON_SERIALIZE_FUNCTION = `
|
|
1566
|
+
_MAX_SERIALIZE_DEPTH = 48
|
|
1567
|
+
|
|
1568
|
+
def _serialize(obj, depth=0):
|
|
1569
|
+
if isinstance(obj, (bool, int, str, type(None))):
|
|
1570
|
+
return obj
|
|
1571
|
+
elif isinstance(obj, float):
|
|
1572
|
+
if not math.isfinite(obj):
|
|
1573
|
+
if math.isnan(obj):
|
|
1574
|
+
return "NaN"
|
|
1575
|
+
return "Infinity" if obj > 0 else "-Infinity"
|
|
1576
|
+
return obj
|
|
1577
|
+
if depth > _MAX_SERIALIZE_DEPTH:
|
|
1578
|
+
return "<max depth>"
|
|
1579
|
+
elif isinstance(obj, (list, tuple)):
|
|
1580
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1581
|
+
elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':
|
|
1582
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1583
|
+
elif isinstance(obj, dict):
|
|
1584
|
+
return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}
|
|
1585
|
+
elif isinstance(obj, set):
|
|
1586
|
+
try:
|
|
1587
|
+
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1588
|
+
except TypeError:
|
|
1589
|
+
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1590
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1591
|
+
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1592
|
+
if hasattr(obj, 'left'):
|
|
1593
|
+
result["left"] = _serialize(obj.left, depth + 1)
|
|
1594
|
+
if hasattr(obj, 'right'):
|
|
1595
|
+
result["right"] = _serialize(obj.right, depth + 1)
|
|
1596
|
+
return result
|
|
1597
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
|
|
1598
|
+
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1599
|
+
result["next"] = _serialize(obj.next, depth + 1)
|
|
1600
|
+
return result
|
|
1601
|
+
elif callable(obj):
|
|
1602
|
+
return None
|
|
1603
|
+
else:
|
|
1604
|
+
repr_str = repr(obj)
|
|
1605
|
+
if repr_str.startswith('<') and repr_str.endswith('>'):
|
|
1606
|
+
return None
|
|
1607
|
+
return repr_str
|
|
1608
|
+
`;
|
|
1609
|
+
|
|
1610
|
+
// packages/harness-python/src/python-harness.ts
|
|
1611
|
+
function identifyConversions(inputs) {
|
|
1612
|
+
const treeKeys = [];
|
|
1613
|
+
const listKeys = [];
|
|
1614
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
1615
|
+
if (value && typeof value === "object" && !Array.isArray(value) && ("val" in value || "value" in value)) {
|
|
1616
|
+
const obj = value;
|
|
1617
|
+
const hasLeft = "left" in obj;
|
|
1618
|
+
const hasRight = "right" in obj;
|
|
1619
|
+
const hasNext = "next" in obj;
|
|
1620
|
+
if (hasLeft || hasRight) {
|
|
1621
|
+
treeKeys.push(key);
|
|
1622
|
+
} else if (hasNext) {
|
|
1623
|
+
listKeys.push(key);
|
|
1624
|
+
} else {
|
|
1625
|
+
treeKeys.push(key);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
return { treeKeys, listKeys };
|
|
1630
|
+
}
|
|
1631
|
+
function generateConversionCode(inputs) {
|
|
1632
|
+
const { treeKeys, listKeys } = identifyConversions(inputs);
|
|
1633
|
+
const lines = [];
|
|
1634
|
+
for (const key of treeKeys) {
|
|
1635
|
+
lines.push(`${key} = _dict_to_tree(${key})`);
|
|
1636
|
+
}
|
|
1637
|
+
for (const key of listKeys) {
|
|
1638
|
+
lines.push(`${key} = _dict_to_list(${key})`);
|
|
1639
|
+
}
|
|
1640
|
+
return lines.join("\n");
|
|
1641
|
+
}
|
|
1642
|
+
function generateInputSetup(inputs) {
|
|
1643
|
+
return Object.entries(inputs).map(([key, value]) => `${key} = ${toPythonLiteral(value)}`).join("\n");
|
|
1644
|
+
}
|
|
1645
|
+
function generateSolutionScript(solutionCode, functionName, inputs) {
|
|
1646
|
+
const inputSetup = generateInputSetup(inputs);
|
|
1647
|
+
const conversionCode = generateConversionCode(inputs);
|
|
1648
|
+
const paramList = Object.keys(inputs).map((key) => `${key}=${key}`).join(", ");
|
|
1649
|
+
return `
|
|
1650
|
+
import json
|
|
1651
|
+
import sys
|
|
1652
|
+
|
|
1653
|
+
${PYTHON_CLASS_DEFINITIONS}
|
|
1654
|
+
|
|
1655
|
+
${PYTHON_CONVERSION_HELPERS}
|
|
1656
|
+
|
|
1657
|
+
${PYTHON_SERIALIZE_FUNCTION}
|
|
1658
|
+
|
|
1659
|
+
# Solution code
|
|
1660
|
+
${solutionCode}
|
|
1661
|
+
|
|
1662
|
+
# Set up inputs
|
|
1663
|
+
${inputSetup}
|
|
1664
|
+
|
|
1665
|
+
# Convert tree/list inputs
|
|
1666
|
+
${conversionCode}
|
|
1667
|
+
|
|
1668
|
+
# Run the function
|
|
1669
|
+
try:
|
|
1670
|
+
_result = ${functionName}(${paramList})
|
|
1671
|
+
print(json.dumps({"success": True, "output": _serialize(_result)}))
|
|
1672
|
+
except Exception as e:
|
|
1673
|
+
print(json.dumps({"success": False, "error": f"{type(e).__name__}: {str(e)}"}))
|
|
1674
|
+
`;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// packages/harness-python/src/python-harness-template.ts
|
|
1678
|
+
function templateToPythonLiteral(value) {
|
|
1679
|
+
if (value === null || value === void 0) {
|
|
1680
|
+
return "None";
|
|
1681
|
+
}
|
|
1682
|
+
if (typeof value === "boolean") {
|
|
1683
|
+
return value ? "True" : "False";
|
|
1684
|
+
}
|
|
1685
|
+
if (typeof value === "number") {
|
|
1686
|
+
return String(value);
|
|
1687
|
+
}
|
|
1688
|
+
if (typeof value === "string") {
|
|
1689
|
+
return JSON.stringify(value);
|
|
1690
|
+
}
|
|
1691
|
+
if (Array.isArray(value)) {
|
|
1692
|
+
return "[" + value.map(templateToPythonLiteral).join(", ") + "]";
|
|
1693
|
+
}
|
|
1694
|
+
if (typeof value === "object") {
|
|
1695
|
+
const entries = Object.entries(value).map(([k, v]) => `${JSON.stringify(k)}: ${templateToPythonLiteral(v)}`).join(", ");
|
|
1696
|
+
return "{" + entries + "}";
|
|
1697
|
+
}
|
|
1698
|
+
return JSON.stringify(value);
|
|
1699
|
+
}
|
|
1700
|
+
var TEMPLATE_PYTHON_CLASS_DEFINITIONS = `
|
|
1701
|
+
class TreeNode:
|
|
1702
|
+
def __init__(self, val=0, left=None, right=None):
|
|
1703
|
+
self.val = val
|
|
1704
|
+
self.value = val
|
|
1705
|
+
self.left = left
|
|
1706
|
+
self.right = right
|
|
1707
|
+
def __getitem__(self, key):
|
|
1708
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', None))
|
|
1709
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', None))
|
|
1710
|
+
if key == 'left': return self.left
|
|
1711
|
+
if key == 'right': return self.right
|
|
1712
|
+
raise KeyError(key)
|
|
1713
|
+
def get(self, key, default=None):
|
|
1714
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', default))
|
|
1715
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', default))
|
|
1716
|
+
if key == 'left': return self.left
|
|
1717
|
+
if key == 'right': return self.right
|
|
1718
|
+
return default
|
|
1719
|
+
def __repr__(self):
|
|
1720
|
+
return f"TreeNode({getattr(self, 'val', getattr(self, 'value', None))})"
|
|
1721
|
+
|
|
1722
|
+
class ListNode:
|
|
1723
|
+
def __init__(self, val=0, next=None):
|
|
1724
|
+
self.val = val
|
|
1725
|
+
self.value = val
|
|
1726
|
+
self.next = next
|
|
1727
|
+
def __getitem__(self, key):
|
|
1728
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', None))
|
|
1729
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', None))
|
|
1730
|
+
if key == 'next': return self.next
|
|
1731
|
+
raise KeyError(key)
|
|
1732
|
+
def get(self, key, default=None):
|
|
1733
|
+
if key == 'val': return getattr(self, 'val', getattr(self, 'value', default))
|
|
1734
|
+
if key == 'value': return getattr(self, 'value', getattr(self, 'val', default))
|
|
1735
|
+
if key == 'next': return self.next
|
|
1736
|
+
return default
|
|
1737
|
+
def __repr__(self):
|
|
1738
|
+
return f"ListNode({getattr(self, 'val', getattr(self, 'value', None))})"
|
|
1739
|
+
`;
|
|
1740
|
+
var TEMPLATE_PYTHON_CONVERSION_HELPERS = `
|
|
1741
|
+
def _ensure_node_value_aliases(node):
|
|
1742
|
+
if node is None:
|
|
1743
|
+
return node
|
|
1744
|
+
try:
|
|
1745
|
+
has_val = hasattr(node, 'val')
|
|
1746
|
+
has_value = hasattr(node, 'value')
|
|
1747
|
+
if has_value and not has_val:
|
|
1748
|
+
try:
|
|
1749
|
+
setattr(node, 'val', getattr(node, 'value'))
|
|
1750
|
+
except Exception:
|
|
1751
|
+
pass
|
|
1752
|
+
elif has_val and not has_value:
|
|
1753
|
+
try:
|
|
1754
|
+
setattr(node, 'value', getattr(node, 'val'))
|
|
1755
|
+
except Exception:
|
|
1756
|
+
pass
|
|
1757
|
+
except Exception:
|
|
1758
|
+
pass
|
|
1759
|
+
return node
|
|
1760
|
+
|
|
1761
|
+
def _dict_to_tree(d):
|
|
1762
|
+
if d is None:
|
|
1763
|
+
return None
|
|
1764
|
+
if not isinstance(d, dict):
|
|
1765
|
+
return d
|
|
1766
|
+
if 'val' not in d and 'value' not in d:
|
|
1767
|
+
return d
|
|
1768
|
+
node = TreeNode(d.get('val', d.get('value', 0)))
|
|
1769
|
+
_ensure_node_value_aliases(node)
|
|
1770
|
+
node.left = _dict_to_tree(d.get('left'))
|
|
1771
|
+
node.right = _dict_to_tree(d.get('right'))
|
|
1772
|
+
return node
|
|
1773
|
+
|
|
1774
|
+
def _dict_to_list(d, _refs=None):
|
|
1775
|
+
if _refs is None:
|
|
1776
|
+
_refs = {}
|
|
1777
|
+
if d is None:
|
|
1778
|
+
return None
|
|
1779
|
+
if not isinstance(d, dict):
|
|
1780
|
+
return d
|
|
1781
|
+
if '__ref__' in d:
|
|
1782
|
+
return _refs.get(d.get('__ref__'))
|
|
1783
|
+
if 'val' not in d and 'value' not in d:
|
|
1784
|
+
return d
|
|
1785
|
+
node = ListNode(d.get('val', d.get('value', 0)))
|
|
1786
|
+
_ensure_node_value_aliases(node)
|
|
1787
|
+
node_id = d.get('__id__')
|
|
1788
|
+
if isinstance(node_id, str) and node_id:
|
|
1789
|
+
_refs[node_id] = node
|
|
1790
|
+
node.next = _dict_to_list(d.get('next'), _refs)
|
|
1791
|
+
return node
|
|
1792
|
+
`;
|
|
1793
|
+
var TEMPLATE_PYTHON_TRACE_SERIALIZE_FUNCTION = `
|
|
1794
|
+
# Sentinel to mark skipped values (functions, etc.) - distinct from None
|
|
1795
|
+
_SKIP_SENTINEL = "__TRACECODE_SKIP__"
|
|
1796
|
+
_MAX_SERIALIZE_DEPTH = 48
|
|
1797
|
+
|
|
1798
|
+
def _serialize(obj, depth=0, node_refs=None):
|
|
1799
|
+
if node_refs is None:
|
|
1800
|
+
node_refs = {}
|
|
1801
|
+
if isinstance(obj, (bool, int, str, type(None))):
|
|
1802
|
+
return obj
|
|
1803
|
+
elif isinstance(obj, float):
|
|
1804
|
+
if not math.isfinite(obj):
|
|
1805
|
+
if math.isnan(obj):
|
|
1806
|
+
return "NaN"
|
|
1807
|
+
return "Infinity" if obj > 0 else "-Infinity"
|
|
1808
|
+
return obj
|
|
1809
|
+
if depth > _MAX_SERIALIZE_DEPTH:
|
|
1810
|
+
return "<max depth>"
|
|
1811
|
+
elif isinstance(obj, (list, tuple)):
|
|
1812
|
+
return [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1813
|
+
elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':
|
|
1814
|
+
return [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1815
|
+
elif isinstance(obj, dict):
|
|
1816
|
+
return {str(k): _serialize(v, depth + 1, node_refs) for k, v in obj.items()}
|
|
1817
|
+
elif isinstance(obj, set):
|
|
1818
|
+
# Use try/except for sorting to handle heterogeneous sets
|
|
1819
|
+
try:
|
|
1820
|
+
sorted_vals = sorted([_serialize(x, depth + 1, node_refs) for x in obj])
|
|
1821
|
+
except TypeError:
|
|
1822
|
+
sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1823
|
+
return {"__type__": "set", "values": sorted_vals}
|
|
1824
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1825
|
+
obj_ref = id(obj)
|
|
1826
|
+
if obj_ref in node_refs:
|
|
1827
|
+
return {"__ref__": node_refs[obj_ref]}
|
|
1828
|
+
node_id = f"tree-{obj_ref}"
|
|
1829
|
+
node_refs[obj_ref] = node_id
|
|
1830
|
+
result = {
|
|
1831
|
+
"__type__": "TreeNode",
|
|
1832
|
+
"__id__": node_id,
|
|
1833
|
+
"val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, node_refs),
|
|
1834
|
+
}
|
|
1835
|
+
if hasattr(obj, 'left'):
|
|
1836
|
+
result["left"] = _serialize(obj.left, depth + 1, node_refs)
|
|
1837
|
+
if hasattr(obj, 'right'):
|
|
1838
|
+
result["right"] = _serialize(obj.right, depth + 1, node_refs)
|
|
1839
|
+
return result
|
|
1840
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
|
|
1841
|
+
obj_ref = id(obj)
|
|
1842
|
+
if obj_ref in node_refs:
|
|
1843
|
+
return {"__ref__": node_refs[obj_ref]}
|
|
1844
|
+
node_id = f"list-{obj_ref}"
|
|
1845
|
+
node_refs[obj_ref] = node_id
|
|
1846
|
+
result = {
|
|
1847
|
+
"__type__": "ListNode",
|
|
1848
|
+
"__id__": node_id,
|
|
1849
|
+
"val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, node_refs),
|
|
1850
|
+
}
|
|
1851
|
+
result["next"] = _serialize(obj.next, depth + 1, node_refs)
|
|
1852
|
+
return result
|
|
1853
|
+
elif callable(obj):
|
|
1854
|
+
# Skip functions entirely - return sentinel
|
|
1855
|
+
return _SKIP_SENTINEL
|
|
1856
|
+
else:
|
|
1857
|
+
repr_str = repr(obj)
|
|
1858
|
+
# Filter out function-like representations (e.g., <function foo at 0x...>)
|
|
1859
|
+
if repr_str.startswith('<') and repr_str.endswith('>'):
|
|
1860
|
+
return _SKIP_SENTINEL
|
|
1861
|
+
return repr_str
|
|
1862
|
+
`;
|
|
1863
|
+
var TEMPLATE_PYTHON_EXECUTE_SERIALIZE_FUNCTION = `
|
|
1864
|
+
_MAX_SERIALIZE_DEPTH = 48
|
|
1865
|
+
|
|
1866
|
+
def _serialize(obj, depth=0):
|
|
1867
|
+
if isinstance(obj, (bool, int, str, type(None))):
|
|
1868
|
+
return obj
|
|
1869
|
+
elif isinstance(obj, float):
|
|
1870
|
+
if not math.isfinite(obj):
|
|
1871
|
+
if math.isnan(obj):
|
|
1872
|
+
return "NaN"
|
|
1873
|
+
return "Infinity" if obj > 0 else "-Infinity"
|
|
1874
|
+
return obj
|
|
1875
|
+
if depth > _MAX_SERIALIZE_DEPTH:
|
|
1876
|
+
return "<max depth>"
|
|
1877
|
+
elif isinstance(obj, (list, tuple)):
|
|
1878
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1879
|
+
elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':
|
|
1880
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1881
|
+
elif isinstance(obj, dict):
|
|
1882
|
+
return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}
|
|
1883
|
+
elif isinstance(obj, set):
|
|
1884
|
+
try:
|
|
1885
|
+
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1886
|
+
except TypeError:
|
|
1887
|
+
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1888
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1889
|
+
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1890
|
+
if hasattr(obj, 'left'):
|
|
1891
|
+
result["left"] = _serialize(obj.left, depth + 1)
|
|
1892
|
+
if hasattr(obj, 'right'):
|
|
1893
|
+
result["right"] = _serialize(obj.right, depth + 1)
|
|
1894
|
+
return result
|
|
1895
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
|
|
1896
|
+
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1897
|
+
result["next"] = _serialize(obj.next, depth + 1)
|
|
1898
|
+
return result
|
|
1899
|
+
elif callable(obj):
|
|
1900
|
+
return None
|
|
1901
|
+
else:
|
|
1902
|
+
repr_str = repr(obj)
|
|
1903
|
+
if repr_str.startswith('<') and repr_str.endswith('>'):
|
|
1904
|
+
return None
|
|
1905
|
+
return repr_str
|
|
1906
|
+
`;
|
|
1907
|
+
var TEMPLATE_PYTHON_PRACTICE_MATERIALIZE_SERIALIZE_FUNCTION = `
|
|
1908
|
+
def _serialize(obj, depth=0, state=None):
|
|
1909
|
+
if state is None:
|
|
1910
|
+
state = {"nodes": 0, "seen": set()}
|
|
1911
|
+
if depth > 64:
|
|
1912
|
+
return "__MAX_DEPTH__"
|
|
1913
|
+
if isinstance(obj, (int, float, str, bool, type(None))):
|
|
1914
|
+
return obj
|
|
1915
|
+
|
|
1916
|
+
state["nodes"] += 1
|
|
1917
|
+
if state["nodes"] > 600:
|
|
1918
|
+
return "__MAX_NODES__"
|
|
1919
|
+
|
|
1920
|
+
if isinstance(obj, (list, tuple)):
|
|
1921
|
+
return [_serialize(x, depth + 1, state) for x in obj]
|
|
1922
|
+
elif isinstance(obj, dict):
|
|
1923
|
+
return {str(k): _serialize(v, depth + 1, state) for k, v in obj.items()}
|
|
1924
|
+
elif isinstance(obj, set):
|
|
1925
|
+
serialized = [_serialize(x, depth + 1, state) for x in obj]
|
|
1926
|
+
try:
|
|
1927
|
+
serialized = sorted(serialized)
|
|
1928
|
+
except TypeError:
|
|
1929
|
+
pass
|
|
1930
|
+
return {"__type__": "set", "values": serialized}
|
|
1931
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1932
|
+
obj_id = id(obj)
|
|
1933
|
+
if obj_id in state["seen"]:
|
|
1934
|
+
return "__CYCLE__"
|
|
1935
|
+
state["seen"].add(obj_id)
|
|
1936
|
+
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, state)}
|
|
1937
|
+
if hasattr(obj, 'left'):
|
|
1938
|
+
result["left"] = _serialize(obj.left, depth + 1, state)
|
|
1939
|
+
if hasattr(obj, 'right'):
|
|
1940
|
+
result["right"] = _serialize(obj.right, depth + 1, state)
|
|
1941
|
+
state["seen"].remove(obj_id)
|
|
1942
|
+
return result
|
|
1943
|
+
elif (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):
|
|
1944
|
+
obj_id = id(obj)
|
|
1945
|
+
if obj_id in state["seen"]:
|
|
1946
|
+
return "__CYCLE__"
|
|
1947
|
+
state["seen"].add(obj_id)
|
|
1948
|
+
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, state)}
|
|
1949
|
+
result["next"] = _serialize(obj.next, depth + 1, state)
|
|
1950
|
+
state["seen"].remove(obj_id)
|
|
1951
|
+
return result
|
|
1952
|
+
else:
|
|
1953
|
+
return repr(obj)
|
|
1954
|
+
`;
|
|
1955
|
+
var TEMPLATE_PYTHON_INTERVIEW_MATERIALIZE_SERIALIZE_FUNCTION = `
|
|
1956
|
+
def _serialize(obj, depth=0):
|
|
1957
|
+
if depth > 10:
|
|
1958
|
+
return "<max depth>"
|
|
1959
|
+
if isinstance(obj, (int, float, str, bool, type(None))):
|
|
1960
|
+
return obj
|
|
1961
|
+
elif isinstance(obj, (list, tuple)):
|
|
1962
|
+
return [_serialize(x, depth + 1) for x in obj]
|
|
1963
|
+
elif isinstance(obj, dict):
|
|
1964
|
+
return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}
|
|
1965
|
+
elif isinstance(obj, set):
|
|
1966
|
+
try:
|
|
1967
|
+
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1968
|
+
except TypeError:
|
|
1969
|
+
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1970
|
+
elif hasattr(obj, 'val') and (hasattr(obj, 'left') or hasattr(obj, 'right')):
|
|
1971
|
+
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
1972
|
+
if hasattr(obj, 'left'):
|
|
1973
|
+
result["left"] = _serialize(obj.left, depth + 1)
|
|
1974
|
+
if hasattr(obj, 'right'):
|
|
1975
|
+
result["right"] = _serialize(obj.right, depth + 1)
|
|
1976
|
+
return result
|
|
1977
|
+
elif hasattr(obj, 'val') and hasattr(obj, 'next'):
|
|
1978
|
+
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
1979
|
+
result["next"] = _serialize(obj.next, depth + 1)
|
|
1980
|
+
return result
|
|
1981
|
+
else:
|
|
1982
|
+
return repr(obj)
|
|
1983
|
+
`;
|
|
1984
|
+
var TEMPLATE_PYTHON_SERIALIZE_FUNCTION = TEMPLATE_PYTHON_EXECUTE_SERIALIZE_FUNCTION;
|
|
1985
|
+
|
|
1986
|
+
// packages/harness-javascript/src/typescript-runtime-declarations.ts
|
|
1987
|
+
var TYPESCRIPT_RUNTIME_DECLARATIONS = `
|
|
1988
|
+
declare class ListNode {
|
|
1989
|
+
val: any;
|
|
1990
|
+
next: ListNode | SerializedListNode | SerializedRef | null;
|
|
1991
|
+
prev?: ListNode | SerializedListNode | SerializedRef | null;
|
|
1992
|
+
constructor(val?: any, next?: ListNode | null);
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
declare class TreeNode {
|
|
1996
|
+
val: any;
|
|
1997
|
+
left: TreeNode | SerializedTreeNode | SerializedRef | null;
|
|
1998
|
+
right: TreeNode | SerializedTreeNode | SerializedRef | null;
|
|
1999
|
+
constructor(val?: any, left?: TreeNode | null, right?: TreeNode | null);
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
type SerializedRef = { __ref__: string };
|
|
2003
|
+
|
|
2004
|
+
type SerializedListNode = {
|
|
2005
|
+
__id__?: string;
|
|
2006
|
+
__type__?: 'ListNode';
|
|
2007
|
+
val?: any;
|
|
2008
|
+
next?: SerializedListNode | SerializedRef | ListNode | null;
|
|
2009
|
+
prev?: SerializedListNode | SerializedRef | ListNode | null;
|
|
2010
|
+
};
|
|
2011
|
+
|
|
2012
|
+
type SerializedTreeNode = {
|
|
2013
|
+
__id__?: string;
|
|
2014
|
+
__type__?: 'TreeNode';
|
|
2015
|
+
val?: any;
|
|
2016
|
+
left?: SerializedTreeNode | SerializedRef | TreeNode | null;
|
|
2017
|
+
right?: SerializedTreeNode | SerializedRef | TreeNode | null;
|
|
2018
|
+
};
|
|
2019
|
+
`;
|
|
2020
|
+
function withTypeScriptRuntimeDeclarations(sourceCode) {
|
|
2021
|
+
return `${sourceCode}
|
|
2022
|
+
|
|
2023
|
+
${TYPESCRIPT_RUNTIME_DECLARATIONS}
|
|
2024
|
+
`;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// packages/harness-javascript/src/javascript-executor.ts
|
|
2028
|
+
var typeScriptModulePromise = null;
|
|
2029
|
+
async function getTypeScriptModule() {
|
|
2030
|
+
if (!typeScriptModulePromise) {
|
|
2031
|
+
const specifier = "typescript";
|
|
2032
|
+
typeScriptModulePromise = import(
|
|
2033
|
+
/* webpackIgnore: true */
|
|
2034
|
+
specifier
|
|
2035
|
+
);
|
|
2036
|
+
}
|
|
2037
|
+
return typeScriptModulePromise;
|
|
2038
|
+
}
|
|
2039
|
+
function performanceNow() {
|
|
2040
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
2041
|
+
return performance.now();
|
|
2042
|
+
}
|
|
2043
|
+
return Date.now();
|
|
2044
|
+
}
|
|
2045
|
+
function formatConsoleArg(value) {
|
|
2046
|
+
if (typeof value === "string") return value;
|
|
2047
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null || value === void 0) {
|
|
2048
|
+
return String(value);
|
|
2049
|
+
}
|
|
2050
|
+
try {
|
|
2051
|
+
return JSON.stringify(value);
|
|
2052
|
+
} catch {
|
|
2053
|
+
return String(value);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
function createConsoleProxy(output) {
|
|
2057
|
+
const capture = (...args) => {
|
|
2058
|
+
output.push(args.map(formatConsoleArg).join(" "));
|
|
2059
|
+
};
|
|
2060
|
+
return {
|
|
2061
|
+
...console,
|
|
2062
|
+
log: capture,
|
|
2063
|
+
info: capture,
|
|
2064
|
+
warn: capture,
|
|
2065
|
+
error: capture,
|
|
2066
|
+
debug: capture
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function isLikelyTreeNodeValue(value) {
|
|
2070
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2071
|
+
const hasValue = "val" in value || "value" in value;
|
|
2072
|
+
const hasTreeLinks = "left" in value || "right" in value;
|
|
2073
|
+
return hasValue && hasTreeLinks;
|
|
2074
|
+
}
|
|
2075
|
+
function isLikelyListNodeValue(value) {
|
|
2076
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2077
|
+
const hasValue = "val" in value || "value" in value;
|
|
2078
|
+
const hasTreeLinks = "left" in value || "right" in value;
|
|
2079
|
+
const hasListLinks = "next" in value || "prev" in value;
|
|
2080
|
+
return hasValue && hasListLinks && !hasTreeLinks;
|
|
2081
|
+
}
|
|
2082
|
+
function serializeValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet(), nodeRefState = { ids: /* @__PURE__ */ new Map(), nextId: 1 }) {
|
|
2083
|
+
if (depth > 48) return "<max depth>";
|
|
2084
|
+
if (value === null || value === void 0) return value;
|
|
2085
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
2086
|
+
return value;
|
|
2087
|
+
}
|
|
2088
|
+
if (typeof value === "bigint") {
|
|
2089
|
+
return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
|
|
2090
|
+
}
|
|
2091
|
+
if (typeof value === "function") {
|
|
2092
|
+
return "<function>";
|
|
2093
|
+
}
|
|
2094
|
+
if (Array.isArray(value)) {
|
|
2095
|
+
return value.map((item) => serializeValue(item, depth + 1, seen));
|
|
2096
|
+
}
|
|
2097
|
+
if (value instanceof Set) {
|
|
2098
|
+
return {
|
|
2099
|
+
__type__: "set",
|
|
2100
|
+
values: [...value].map((item) => serializeValue(item, depth + 1, seen, nodeRefState))
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
if (value instanceof Map) {
|
|
2104
|
+
return {
|
|
2105
|
+
__type__: "map",
|
|
2106
|
+
entries: [...value.entries()].map(([k, v]) => [
|
|
2107
|
+
serializeValue(k, depth + 1, seen, nodeRefState),
|
|
2108
|
+
serializeValue(v, depth + 1, seen, nodeRefState)
|
|
2109
|
+
])
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
if (typeof value === "object") {
|
|
2113
|
+
if (isLikelyTreeNodeValue(value) || isLikelyListNodeValue(value)) {
|
|
2114
|
+
const objectValue = value;
|
|
2115
|
+
const nodeValue = value;
|
|
2116
|
+
const existingId = nodeRefState.ids.get(objectValue);
|
|
2117
|
+
if (existingId) {
|
|
2118
|
+
return { __ref__: existingId };
|
|
2119
|
+
}
|
|
2120
|
+
const isTree = isLikelyTreeNodeValue(value);
|
|
2121
|
+
const nodePrefix = isTree ? "tree" : "list";
|
|
2122
|
+
const nodeId = `${nodePrefix}-${nodeRefState.nextId++}`;
|
|
2123
|
+
nodeRefState.ids.set(objectValue, nodeId);
|
|
2124
|
+
if (isTree) {
|
|
2125
|
+
return {
|
|
2126
|
+
__type__: "TreeNode",
|
|
2127
|
+
__id__: nodeId,
|
|
2128
|
+
val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, depth + 1, seen, nodeRefState),
|
|
2129
|
+
left: serializeValue(nodeValue.left ?? null, depth + 1, seen, nodeRefState),
|
|
2130
|
+
right: serializeValue(nodeValue.right ?? null, depth + 1, seen, nodeRefState)
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
return {
|
|
2134
|
+
__type__: "ListNode",
|
|
2135
|
+
__id__: nodeId,
|
|
2136
|
+
val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, depth + 1, seen, nodeRefState),
|
|
2137
|
+
next: serializeValue(nodeValue.next ?? null, depth + 1, seen, nodeRefState),
|
|
2138
|
+
..."prev" in nodeValue ? { prev: serializeValue(nodeValue.prev ?? null, depth + 1, seen, nodeRefState) } : {}
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
2141
|
+
if (seen.has(value)) return "<cycle>";
|
|
2142
|
+
seen.add(value);
|
|
2143
|
+
const out = {};
|
|
2144
|
+
for (const [k, v] of Object.entries(value)) {
|
|
2145
|
+
out[k] = serializeValue(v, depth + 1, seen, nodeRefState);
|
|
2146
|
+
}
|
|
2147
|
+
seen.delete(value);
|
|
2148
|
+
return out;
|
|
2149
|
+
}
|
|
2150
|
+
return String(value);
|
|
2151
|
+
}
|
|
2152
|
+
function extractUserErrorLine(error) {
|
|
2153
|
+
if (typeof error === "object" && error && "__tracecodeLine" in error) {
|
|
2154
|
+
const line2 = Number(error.__tracecodeLine);
|
|
2155
|
+
if (Number.isFinite(line2)) return line2;
|
|
2156
|
+
}
|
|
2157
|
+
const stack = typeof error === "object" && error && "stack" in error ? String(error.stack ?? "") : "";
|
|
2158
|
+
if (!stack) return void 0;
|
|
2159
|
+
const match = stack.match(/<anonymous>:(\d+):\d+/);
|
|
2160
|
+
if (!match) return void 0;
|
|
2161
|
+
const line = Number.parseInt(match[1], 10);
|
|
2162
|
+
return Number.isFinite(line) ? line : void 0;
|
|
2163
|
+
}
|
|
2164
|
+
function isPlainObjectRecord(value) {
|
|
2165
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2166
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
2167
|
+
}
|
|
2168
|
+
function collectReferenceTargets(value, byId, seen) {
|
|
2169
|
+
if (value === null || value === void 0) return;
|
|
2170
|
+
if (typeof value !== "object") return;
|
|
2171
|
+
if (seen.has(value)) return;
|
|
2172
|
+
seen.add(value);
|
|
2173
|
+
if (Array.isArray(value)) {
|
|
2174
|
+
for (const item of value) {
|
|
2175
|
+
collectReferenceTargets(item, byId, seen);
|
|
2176
|
+
}
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
if (!isPlainObjectRecord(value)) return;
|
|
2180
|
+
if (typeof value.__id__ === "string" && value.__id__.length > 0 && !byId.has(value.__id__)) {
|
|
2181
|
+
byId.set(value.__id__, value);
|
|
2182
|
+
}
|
|
2183
|
+
for (const nested of Object.values(value)) {
|
|
2184
|
+
collectReferenceTargets(nested, byId, seen);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
function resolveReferenceGraph(value, byId, resolved) {
|
|
2188
|
+
if (value === null || value === void 0) return value;
|
|
2189
|
+
if (typeof value !== "object") return value;
|
|
2190
|
+
if (resolved.has(value)) {
|
|
2191
|
+
return resolved.get(value);
|
|
2192
|
+
}
|
|
2193
|
+
if (Array.isArray(value)) {
|
|
2194
|
+
const out2 = [];
|
|
2195
|
+
resolved.set(value, out2);
|
|
2196
|
+
for (const item of value) {
|
|
2197
|
+
out2.push(resolveReferenceGraph(item, byId, resolved));
|
|
2198
|
+
}
|
|
2199
|
+
return out2;
|
|
2200
|
+
}
|
|
2201
|
+
if (!isPlainObjectRecord(value)) {
|
|
2202
|
+
return value;
|
|
2203
|
+
}
|
|
2204
|
+
const keys = Object.keys(value);
|
|
2205
|
+
if (keys.length === 1 && typeof value.__ref__ === "string") {
|
|
2206
|
+
const target = byId.get(value.__ref__);
|
|
2207
|
+
if (!target) return null;
|
|
2208
|
+
return resolveReferenceGraph(target, byId, resolved);
|
|
2209
|
+
}
|
|
2210
|
+
const out = {};
|
|
2211
|
+
resolved.set(value, out);
|
|
2212
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
2213
|
+
out[key] = resolveReferenceGraph(nested, byId, resolved);
|
|
2214
|
+
}
|
|
2215
|
+
return out;
|
|
2216
|
+
}
|
|
2217
|
+
function normalizeInputs(inputs) {
|
|
2218
|
+
if (!inputs || typeof inputs !== "object" || Array.isArray(inputs)) return {};
|
|
2219
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2220
|
+
collectReferenceTargets(inputs, byId, /* @__PURE__ */ new WeakSet());
|
|
2221
|
+
if (byId.size === 0) {
|
|
2222
|
+
return inputs;
|
|
2223
|
+
}
|
|
2224
|
+
const hydrated = resolveReferenceGraph(inputs, byId, /* @__PURE__ */ new WeakMap());
|
|
2225
|
+
if (!hydrated || typeof hydrated !== "object" || Array.isArray(hydrated)) {
|
|
2226
|
+
return inputs;
|
|
2227
|
+
}
|
|
2228
|
+
return hydrated;
|
|
2229
|
+
}
|
|
2230
|
+
function collectSimpleParameterNames(ts, functionLikeNode) {
|
|
2231
|
+
const names = [];
|
|
2232
|
+
for (const parameter of functionLikeNode.parameters ?? []) {
|
|
2233
|
+
if (!ts.isIdentifier(parameter.name)) {
|
|
2234
|
+
return null;
|
|
2235
|
+
}
|
|
2236
|
+
if (parameter.name.text === "this") {
|
|
2237
|
+
continue;
|
|
2238
|
+
}
|
|
2239
|
+
names.push(parameter.name.text);
|
|
2240
|
+
}
|
|
2241
|
+
return names;
|
|
2242
|
+
}
|
|
2243
|
+
function getPropertyNameText(ts, name) {
|
|
2244
|
+
if (!name) return null;
|
|
2245
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
|
|
2246
|
+
return name.text;
|
|
2247
|
+
}
|
|
2248
|
+
return null;
|
|
2249
|
+
}
|
|
2250
|
+
function findFunctionLikeNode(ts, sourceFile, functionName, executionStyle) {
|
|
2251
|
+
let found = null;
|
|
2252
|
+
const visit = (node) => {
|
|
2253
|
+
if (found) return;
|
|
2254
|
+
if (executionStyle === "solution-method" && ts.isClassDeclaration(node) && node.name?.text === "Solution") {
|
|
2255
|
+
for (const member of node.members) {
|
|
2256
|
+
if (found) break;
|
|
2257
|
+
if (ts.isMethodDeclaration(member) && getPropertyNameText(ts, member.name) === functionName) {
|
|
2258
|
+
found = member;
|
|
2259
|
+
break;
|
|
2260
|
+
}
|
|
2261
|
+
if (ts.isPropertyDeclaration(member) && getPropertyNameText(ts, member.name) === functionName && member.initializer && (ts.isArrowFunction(member.initializer) || ts.isFunctionExpression(member.initializer))) {
|
|
2262
|
+
found = member.initializer;
|
|
2263
|
+
break;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
if (executionStyle === "function") {
|
|
2269
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
|
|
2270
|
+
found = node;
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2273
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === functionName && node.initializer && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) {
|
|
2274
|
+
found = node.initializer;
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
ts.forEachChild(node, visit);
|
|
2279
|
+
};
|
|
2280
|
+
visit(sourceFile);
|
|
2281
|
+
return found;
|
|
2282
|
+
}
|
|
2283
|
+
async function resolveOrderedInputKeys(code, functionName, inputs, executionStyle) {
|
|
2284
|
+
const fallbackKeys = Object.keys(inputs);
|
|
2285
|
+
if (!functionName || executionStyle === "ops-class" || fallbackKeys.length <= 1) {
|
|
2286
|
+
return fallbackKeys;
|
|
2287
|
+
}
|
|
2288
|
+
try {
|
|
2289
|
+
const ts = await getTypeScriptModule();
|
|
2290
|
+
const sourceFile = ts.createSourceFile("runtime-input.js", code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.JS);
|
|
2291
|
+
const target = findFunctionLikeNode(ts, sourceFile, functionName, executionStyle);
|
|
2292
|
+
if (!target) {
|
|
2293
|
+
return fallbackKeys;
|
|
2294
|
+
}
|
|
2295
|
+
const parameterNames = collectSimpleParameterNames(ts, target);
|
|
2296
|
+
if (!parameterNames || parameterNames.length === 0) {
|
|
2297
|
+
return fallbackKeys;
|
|
2298
|
+
}
|
|
2299
|
+
const matchedKeys = parameterNames.filter((name) => Object.prototype.hasOwnProperty.call(inputs, name));
|
|
2300
|
+
if (matchedKeys.length === 0) {
|
|
2301
|
+
return fallbackKeys;
|
|
2302
|
+
}
|
|
2303
|
+
const extras = fallbackKeys.filter((key) => !matchedKeys.includes(key));
|
|
2304
|
+
return [...matchedKeys, ...extras];
|
|
2305
|
+
} catch {
|
|
2306
|
+
return fallbackKeys;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function buildRunner(code, executionStyle, argNames) {
|
|
2310
|
+
if (executionStyle === "function") {
|
|
2311
|
+
return new Function(
|
|
2312
|
+
"console",
|
|
2313
|
+
"__functionName",
|
|
2314
|
+
...argNames,
|
|
2315
|
+
`"use strict";
|
|
2316
|
+
${code}
|
|
2317
|
+
let __target;
|
|
2318
|
+
try {
|
|
2319
|
+
__target = eval(__functionName);
|
|
2320
|
+
} catch (_err) {
|
|
2321
|
+
__target = undefined;
|
|
2322
|
+
}
|
|
2323
|
+
if (typeof __target !== 'function') {
|
|
2324
|
+
throw new Error('Function "' + __functionName + '" not found');
|
|
2325
|
+
}
|
|
2326
|
+
return __target(${argNames.join(", ")});`
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2329
|
+
if (executionStyle === "solution-method") {
|
|
2330
|
+
return new Function(
|
|
2331
|
+
"console",
|
|
2332
|
+
"__functionName",
|
|
2333
|
+
...argNames,
|
|
2334
|
+
`"use strict";
|
|
2335
|
+
${code}
|
|
2336
|
+
if (typeof Solution !== 'function') {
|
|
2337
|
+
throw new Error('Class "Solution" not found');
|
|
2338
|
+
}
|
|
2339
|
+
const __solver = new Solution();
|
|
2340
|
+
const __method = __solver[__functionName];
|
|
2341
|
+
if (typeof __method !== 'function') {
|
|
2342
|
+
throw new Error('Method "Solution.' + __functionName + '" not found');
|
|
2343
|
+
}
|
|
2344
|
+
return __method.call(__solver, ${argNames.join(", ")});`
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
if (executionStyle === "ops-class") {
|
|
2348
|
+
return new Function(
|
|
2349
|
+
"console",
|
|
2350
|
+
"__className",
|
|
2351
|
+
"__operations",
|
|
2352
|
+
"__arguments",
|
|
2353
|
+
`"use strict";
|
|
2354
|
+
${code}
|
|
2355
|
+
if (!Array.isArray(__operations) || !Array.isArray(__arguments)) {
|
|
2356
|
+
throw new Error('ops-class execution requires inputs.operations and inputs.arguments (or ops/args)');
|
|
2357
|
+
}
|
|
2358
|
+
if (__operations.length !== __arguments.length) {
|
|
2359
|
+
throw new Error('operations and arguments must have the same length');
|
|
2360
|
+
}
|
|
2361
|
+
let __targetClass;
|
|
2362
|
+
try {
|
|
2363
|
+
__targetClass = eval(__className);
|
|
2364
|
+
} catch (_err) {
|
|
2365
|
+
__targetClass = undefined;
|
|
2366
|
+
}
|
|
2367
|
+
if (typeof __targetClass !== 'function') {
|
|
2368
|
+
throw new Error('Class "' + __className + '" not found');
|
|
2369
|
+
}
|
|
2370
|
+
let __instance = null;
|
|
2371
|
+
const __out = [];
|
|
2372
|
+
for (let __i = 0; __i < __operations.length; __i++) {
|
|
2373
|
+
const __op = __operations[__i];
|
|
2374
|
+
let __callArgs = __arguments[__i];
|
|
2375
|
+
if (__callArgs === null || __callArgs === undefined) {
|
|
2376
|
+
__callArgs = [];
|
|
2377
|
+
}
|
|
2378
|
+
if (!Array.isArray(__callArgs)) {
|
|
2379
|
+
__callArgs = [__callArgs];
|
|
2380
|
+
}
|
|
2381
|
+
if (__i === 0) {
|
|
2382
|
+
__instance = new __targetClass(...__callArgs);
|
|
2383
|
+
__out.push(null);
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
if (!__instance || typeof __instance[__op] !== 'function') {
|
|
2387
|
+
throw new Error('Required method "' + __op + '" is not implemented on ' + (__className || 'target class'));
|
|
2388
|
+
}
|
|
2389
|
+
__out.push(__instance[__op](...__callArgs));
|
|
2390
|
+
}
|
|
2391
|
+
return __out;`
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
throw new Error(`Execution style "${executionStyle}" is not supported for JavaScript runtime yet.`);
|
|
2395
|
+
}
|
|
2396
|
+
function getOpsClassInputs(inputs) {
|
|
2397
|
+
const operations = Array.isArray(inputs.operations) ? inputs.operations : Array.isArray(inputs.ops) ? inputs.ops : null;
|
|
2398
|
+
const argumentsList = Array.isArray(inputs.arguments) ? inputs.arguments : Array.isArray(inputs.args) ? inputs.args : null;
|
|
2399
|
+
return { operations, argumentsList };
|
|
2400
|
+
}
|
|
2401
|
+
async function transpileTypeScript(code) {
|
|
2402
|
+
const ts = await getTypeScriptModule();
|
|
2403
|
+
const transpileInput = withTypeScriptRuntimeDeclarations(code);
|
|
2404
|
+
const transpiled = ts.transpileModule(transpileInput, {
|
|
2405
|
+
compilerOptions: {
|
|
2406
|
+
target: ts.ScriptTarget.ES2020,
|
|
2407
|
+
module: ts.ModuleKind.None,
|
|
2408
|
+
strict: false,
|
|
2409
|
+
esModuleInterop: true
|
|
2410
|
+
},
|
|
2411
|
+
reportDiagnostics: true,
|
|
2412
|
+
fileName: "solution.ts"
|
|
2413
|
+
});
|
|
2414
|
+
const diagnostics = Array.isArray(transpiled.diagnostics) ? transpiled.diagnostics : [];
|
|
2415
|
+
const errors = diagnostics.filter((diag) => diag.category === ts.DiagnosticCategory.Error);
|
|
2416
|
+
if (errors.length > 0) {
|
|
2417
|
+
const first = errors[0];
|
|
2418
|
+
const messageText = ts.flattenDiagnosticMessageText(first.messageText, "\n");
|
|
2419
|
+
let lineNumber;
|
|
2420
|
+
if (first.file && typeof first.start === "number") {
|
|
2421
|
+
const position = first.file.getLineAndCharacterOfPosition(first.start);
|
|
2422
|
+
lineNumber = position.line + 1;
|
|
2423
|
+
}
|
|
2424
|
+
const error = new Error(
|
|
2425
|
+
lineNumber ? `TypeScript transpilation failed (line ${lineNumber}): ${messageText}` : `TypeScript transpilation failed: ${messageText}`
|
|
2426
|
+
);
|
|
2427
|
+
if (lineNumber) {
|
|
2428
|
+
error.__tracecodeLine = lineNumber;
|
|
2429
|
+
}
|
|
2430
|
+
throw error;
|
|
2431
|
+
}
|
|
2432
|
+
return transpiled.outputText;
|
|
2433
|
+
}
|
|
2434
|
+
async function executeJavaScriptCode(code, functionName, inputs, executionStyle = "function") {
|
|
2435
|
+
const consoleOutput = [];
|
|
2436
|
+
const consoleProxy = createConsoleProxy(consoleOutput);
|
|
2437
|
+
const normalizedInputs = normalizeInputs(inputs);
|
|
2438
|
+
try {
|
|
2439
|
+
let output;
|
|
2440
|
+
if (executionStyle === "ops-class") {
|
|
2441
|
+
const { operations, argumentsList } = getOpsClassInputs(normalizedInputs);
|
|
2442
|
+
const runner = buildRunner(code, executionStyle, []);
|
|
2443
|
+
output = await Promise.resolve(runner(consoleProxy, functionName, operations, argumentsList));
|
|
2444
|
+
} else {
|
|
2445
|
+
const inputKeys = await resolveOrderedInputKeys(code, functionName, normalizedInputs, executionStyle);
|
|
2446
|
+
const argNames = inputKeys.map((_, index) => `__arg${index}`);
|
|
2447
|
+
const argValues = inputKeys.map((key) => normalizedInputs[key]);
|
|
2448
|
+
const runner = buildRunner(code, executionStyle, argNames);
|
|
2449
|
+
output = await Promise.resolve(runner(consoleProxy, functionName, ...argValues));
|
|
2450
|
+
}
|
|
2451
|
+
return {
|
|
2452
|
+
success: true,
|
|
2453
|
+
output: serializeValue(output),
|
|
2454
|
+
consoleOutput
|
|
2455
|
+
};
|
|
2456
|
+
} catch (error) {
|
|
2457
|
+
return {
|
|
2458
|
+
success: false,
|
|
2459
|
+
output: null,
|
|
2460
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2461
|
+
errorLine: extractUserErrorLine(error),
|
|
2462
|
+
consoleOutput
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
async function executeJavaScriptWithTracing(code, functionName, inputs, executionStyle = "function") {
|
|
2467
|
+
const startedAt = performanceNow();
|
|
2468
|
+
const codeResult = await executeJavaScriptCode(code, functionName ?? "", inputs, executionStyle);
|
|
2469
|
+
const executionTimeMs = performanceNow() - startedAt;
|
|
2470
|
+
if (!codeResult.success) {
|
|
2471
|
+
return {
|
|
2472
|
+
success: false,
|
|
2473
|
+
error: codeResult.error,
|
|
2474
|
+
errorLine: codeResult.errorLine,
|
|
2475
|
+
trace: [],
|
|
2476
|
+
executionTimeMs,
|
|
2477
|
+
consoleOutput: codeResult.consoleOutput ?? [],
|
|
2478
|
+
lineEventCount: 0,
|
|
2479
|
+
traceStepCount: 0
|
|
2480
|
+
};
|
|
2481
|
+
}
|
|
2482
|
+
return {
|
|
2483
|
+
success: true,
|
|
2484
|
+
output: codeResult.output,
|
|
2485
|
+
trace: [],
|
|
2486
|
+
executionTimeMs,
|
|
2487
|
+
consoleOutput: codeResult.consoleOutput ?? [],
|
|
2488
|
+
lineEventCount: 0,
|
|
2489
|
+
traceStepCount: 0
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
async function executeTypeScriptCode(code, functionName, inputs, executionStyle = "function") {
|
|
2493
|
+
const transpiledCode = await transpileTypeScript(code);
|
|
2494
|
+
return executeJavaScriptCode(transpiledCode, functionName, inputs, executionStyle);
|
|
2495
|
+
}
|
|
2496
|
+
export {
|
|
2497
|
+
DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS,
|
|
2498
|
+
LANGUAGE_RUNTIME_PROFILES,
|
|
2499
|
+
PYTHON_CLASS_DEFINITIONS,
|
|
2500
|
+
PYTHON_CONVERSION_HELPERS,
|
|
2501
|
+
PYTHON_EXECUTE_SERIALIZE_FUNCTION,
|
|
2502
|
+
PYTHON_INTERVIEW_MATERIALIZE_SERIALIZE_FUNCTION,
|
|
2503
|
+
PYTHON_PRACTICE_MATERIALIZE_SERIALIZE_FUNCTION,
|
|
2504
|
+
PYTHON_SERIALIZE_FUNCTION,
|
|
2505
|
+
PYTHON_TRACE_SERIALIZE_FUNCTION,
|
|
2506
|
+
RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION,
|
|
2507
|
+
SUPPORTED_LANGUAGES,
|
|
2508
|
+
TEMPLATE_PYTHON_CLASS_DEFINITIONS,
|
|
2509
|
+
TEMPLATE_PYTHON_CONVERSION_HELPERS,
|
|
2510
|
+
TEMPLATE_PYTHON_EXECUTE_SERIALIZE_FUNCTION,
|
|
2511
|
+
TEMPLATE_PYTHON_INTERVIEW_MATERIALIZE_SERIALIZE_FUNCTION,
|
|
2512
|
+
TEMPLATE_PYTHON_PRACTICE_MATERIALIZE_SERIALIZE_FUNCTION,
|
|
2513
|
+
TEMPLATE_PYTHON_SERIALIZE_FUNCTION,
|
|
2514
|
+
TEMPLATE_PYTHON_TRACE_SERIALIZE_FUNCTION,
|
|
2515
|
+
TYPESCRIPT_RUNTIME_DECLARATIONS,
|
|
2516
|
+
adaptJavaScriptTraceExecutionResult,
|
|
2517
|
+
adaptPythonTraceExecutionResult,
|
|
2518
|
+
adaptTraceExecutionResult,
|
|
2519
|
+
assertRuntimeRequestSupported,
|
|
2520
|
+
createBrowserHarness,
|
|
2521
|
+
executeJavaScriptCode,
|
|
2522
|
+
executeJavaScriptWithTracing,
|
|
2523
|
+
executeTypeScriptCode,
|
|
2524
|
+
generateConversionCode,
|
|
2525
|
+
generateInputSetup,
|
|
2526
|
+
generateSolutionScript,
|
|
2527
|
+
getLanguageRuntimeProfile,
|
|
2528
|
+
getSupportedLanguageProfiles,
|
|
2529
|
+
identifyConversions,
|
|
2530
|
+
isLanguageSupported,
|
|
2531
|
+
normalizeRuntimeTraceContract,
|
|
2532
|
+
resolveBrowserHarnessAssets,
|
|
2533
|
+
stableStringifyRuntimeTraceContract,
|
|
2534
|
+
templateToPythonLiteral,
|
|
2535
|
+
toPythonLiteral,
|
|
2536
|
+
withTypeScriptRuntimeDeclarations
|
|
2537
|
+
};
|
|
2538
|
+
//# sourceMappingURL=index.js.map
|