@doccident/doccident 0.0.3 → 0.0.5

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/dist/doctest.js CHANGED
@@ -6,18 +6,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.printResults = void 0;
7
7
  exports.runTests = runTests;
8
8
  const fs_1 = require("fs");
9
- const vm_1 = require("vm");
10
- const esbuild_1 = require("esbuild");
11
9
  const chalk_1 = __importDefault(require("chalk"));
12
10
  const utils_1 = require("./utils");
13
11
  const parse_code_snippets_from_markdown_1 = __importDefault(require("./parse-code-snippets-from-markdown"));
14
12
  const reporter_1 = require("./reporter");
15
13
  Object.defineProperty(exports, "printResults", { enumerable: true, get: function () { return reporter_1.printResults; } });
14
+ const python_1 = require("./languages/python");
15
+ const shell_1 = require("./languages/shell");
16
+ const go_1 = require("./languages/go");
17
+ const rust_1 = require("./languages/rust");
18
+ const fortran_1 = require("./languages/fortran");
19
+ const cobol_1 = require("./languages/cobol");
20
+ const c_1 = require("./languages/c");
21
+ const basic_1 = require("./languages/basic");
22
+ const java_1 = require("./languages/java");
23
+ const csharp_1 = require("./languages/csharp");
24
+ const perl_1 = require("./languages/perl");
25
+ const r_1 = require("./languages/r");
26
+ const pascal_1 = require("./languages/pascal");
27
+ const javascript_1 = require("./languages/javascript");
16
28
  function runTests(files, config) {
17
29
  const results = files
18
30
  .map(read)
19
- .map(parse_code_snippets_from_markdown_1.default)
20
- .map(testFile(config));
31
+ .map(fileInfo => {
32
+ const parsed = (0, parse_code_snippets_from_markdown_1.default)(fileInfo);
33
+ // Attach original contents to parsed file if needed for updates?
34
+ // Actually testFile closure captures it if we pass it correctly?
35
+ // But map pipeline separates them.
36
+ // Let's modify testFile to accept FileInfo + ParsedFile or just re-read?
37
+ // Or better: `read` returns FileInfo. `parseCodeSnippets` returns ParsedFile (without contents).
38
+ // We can wrap this better.
39
+ return { parsed, fileInfo };
40
+ })
41
+ .map(({ parsed, fileInfo }) => testFile(config)(parsed, fileInfo));
21
42
  return (0, utils_1.flatten)(results);
22
43
  }
23
44
  function read(fileName) {
@@ -46,28 +67,168 @@ function makeTestSandbox(config) {
46
67
  return sandbox;
47
68
  }
48
69
  function testFile(config) {
49
- return function testFileWithConfig(args) {
70
+ return function testFileWithConfig(args, fileInfo) {
50
71
  const codeSnippets = args.codeSnippets;
51
72
  const fileName = args.fileName;
52
73
  const shareCodeInFile = args.shareCodeInFile;
53
74
  let results;
75
+ // Map to store outputs by ID
76
+ const outputs = {};
77
+ // Store edits if updating output
78
+ const edits = [];
54
79
  if (shareCodeInFile) {
55
80
  const sandbox = makeTestSandbox(config);
56
- results = codeSnippets.map(test(config, fileName, sandbox));
81
+ results = codeSnippets.map(test(config, fileName, sandbox, outputs, edits));
57
82
  }
58
83
  else {
59
- results = codeSnippets.map(test(config, fileName));
84
+ results = codeSnippets.map(test(config, fileName, undefined, outputs, edits));
85
+ }
86
+ // Apply edits if updateOutput is true and we have file info
87
+ if (config.updateOutput && edits.length > 0 && fileInfo) {
88
+ applyEdits(fileInfo, edits);
60
89
  }
61
90
  return results;
62
91
  };
63
92
  }
64
- function test(config, _filename, sandbox) {
93
+ function applyEdits(fileInfo, edits) {
94
+ // Sort edits by startLine descending to safely modify file
95
+ // Note: We need endLine for snippets. Currently Snippet only has lineNumber (start).
96
+ // The parser needs to provide endLine or we need to calculate it.
97
+ // If we assume standard fences ```...```, we can try to guess or update parser.
98
+ // Parser update is safer. But let's check snippet first.
99
+ // For now, let's assume we can't do it safely without endLine.
100
+ // Snippet interface needs endLine.
101
+ // Sort descending
102
+ edits.sort((a, b) => b.startLine - a.startLine);
103
+ const lines = fileInfo.contents.split('\n');
104
+ for (const edit of edits) {
105
+ if (edit.endLine === undefined)
106
+ continue;
107
+ // Snippet startLine is 1-based index of the first line of content (inside fences) usually?
108
+ // Let's check parser.
109
+ // In parser: `lineNumber` is `index + 1`. `index` is passed from split('\n').map.
110
+ // `isStartOfSnippet` detects the opening fence.
111
+ // `startNewSnippet` called with that line number.
112
+ // So `lineNumber` is the line of the OPENING FENCE ```.
113
+ // Code content starts at lineNumber + 1?
114
+ // Wait, parser:
115
+ // `parseLine` calls `startNewSnippet` on `isStartOfSnippet`.
116
+ // So snippet.lineNumber is the line index (1-based) of the ```lang line.
117
+ // `endSnippet` is called on `isEndOfSnippet` (closing ```).
118
+ // We need to capture that line number too.
119
+ // We replace from `startLine` (exclusive? no, replace the CONTENT)
120
+ // Actually, we want to replace the content BETWEEN the fences.
121
+ // So from `startLine` (0-based index) + 1 to `endLine` (0-based index) - 1.
122
+ // Let's rely on parser providing `endLine`.
123
+ const startIdx = edit.startLine; // 1-based index of opening fence
124
+ const endIdx = edit.endLine; // 1-based index of closing fence
125
+ // Content lines are startIdx...endIdx-2 (0-based: startIdx is fence line)
126
+ // Example:
127
+ // 1: ```text <- startLine
128
+ // 2: Old <- content
129
+ // 3: ``` <- endLine
130
+ // We want to replace lines between startLine and endLine with new content.
131
+ // 0-based indexes:
132
+ // startLineIndex = startLine - 1;
133
+ // endLineIndex = endLine - 1;
134
+ // We want to replace lines from (startLineIndex + 1) to (endLineIndex - 1).
135
+ // splice(start, deleteCount, items...)
136
+ const startReplace = startIdx; // index after opening fence
137
+ const deleteCount = (endIdx - 1) - startReplace;
138
+ // If content is empty/new, we just splice.
139
+ lines.splice(startReplace, deleteCount, edit.content);
140
+ }
141
+ (0, fs_1.writeFileSync)(fileInfo.fileName, lines.join('\n'));
142
+ }
143
+ function test(config, _filename, sandbox, outputs, edits) {
65
144
  return (codeSnippet) => {
145
+ const startTime = performance.now();
66
146
  if (codeSnippet.skip) {
67
147
  return { status: "skip", codeSnippet, stack: "" };
68
148
  }
149
+ // Output verification / update logic
150
+ if (codeSnippet.outputOf) {
151
+ const expectedOutput = codeSnippet.code.trim(); // Trim for comparison
152
+ // For update, we want the raw output (maybe trimmed of trailing newline but preserving structure)
153
+ const actualOutput = outputs && outputs[codeSnippet.outputOf] ? outputs[codeSnippet.outputOf] : "";
154
+ const actualOutputTrimmed = actualOutput.trim();
155
+ // console.log(`[DEBUG] Verifying '${codeSnippet.outputOf}' Mode=${codeSnippet.outputMode}`);
156
+ // console.log(`[DEBUG] Expected: "${expectedOutput}"`);
157
+ // console.log(`[DEBUG] Actual: "${actualOutputTrimmed}"`);
158
+ if (!outputs || outputs[codeSnippet.outputOf] === undefined) {
159
+ return { status: "fail", codeSnippet, stack: `Snippet with id '${codeSnippet.outputOf}' not executed or not found.` };
160
+ }
161
+ if (config.updateOutput) {
162
+ // Schedule update
163
+ if (edits) {
164
+ // Normalize actual output: usually we want to preserve it exactly,
165
+ // but standard console.log adds newline.
166
+ // Markdown blocks usually end with newline before ```.
167
+ // Let's strip the very last newline if present to avoid growing gaps?
168
+ // Or just use trimEnd()?
169
+ // Generally, code blocks look like:
170
+ // ```text
171
+ // Output
172
+ // ```
173
+ // If actualOutput is "Output\n", we put "Output" inside?
174
+ // Or "Output\n"?
175
+ // If we put "Output\n", it becomes:
176
+ // ```text
177
+ // Output
178
+ //
179
+ // ```
180
+ // Let's trim trailing whitespace from actualOutput for the replacement content.
181
+ edits.push({
182
+ startLine: codeSnippet.lineNumber,
183
+ endLine: codeSnippet.endLine, // We need this!
184
+ content: actualOutput.trimEnd()
185
+ });
186
+ }
187
+ process.stdout.write(chalk_1.default.yellow("u"));
188
+ return { status: "pass", codeSnippet, stack: "" };
189
+ }
190
+ if (actualOutputTrimmed !== expectedOutput) {
191
+ if (codeSnippet.outputMode === 'ignore-whitespace') {
192
+ // Normalize both strings: replace all whitespace sequences with single space and trim
193
+ const normalize = (s) => s.replace(/\s+/g, ' ').trim();
194
+ if (normalize(actualOutput) === normalize(expectedOutput)) {
195
+ process.stdout.write(chalk_1.default.green("."));
196
+ return { status: "pass", codeSnippet, stack: "" };
197
+ }
198
+ }
199
+ else if (codeSnippet.outputMode === 'regex') {
200
+ // Treat expectedOutput as regex pattern
201
+ try {
202
+ const pattern = expectedOutput.trim();
203
+ // For regex, we match against trimmed actual output
204
+ const re = new RegExp(pattern);
205
+ if (re.test(actualOutput.trim())) {
206
+ process.stdout.write(chalk_1.default.green("."));
207
+ return { status: "pass", codeSnippet, stack: "" };
208
+ }
209
+ }
210
+ catch (e) {
211
+ process.stdout.write(chalk_1.default.red("x"));
212
+ return {
213
+ status: "fail",
214
+ codeSnippet,
215
+ stack: `Invalid Regex Pattern: ${e}\nPattern:\n${expectedOutput}`
216
+ };
217
+ }
218
+ }
219
+ process.stdout.write(chalk_1.default.red("x"));
220
+ return {
221
+ status: "fail",
222
+ codeSnippet,
223
+ stack: `Output verification failed (${codeSnippet.outputMode || 'exact'}).\nExpected:\n${expectedOutput}\n\nActual:\n${actualOutputTrimmed}`
224
+ };
225
+ }
226
+ process.stdout.write(chalk_1.default.green("."));
227
+ return { status: "pass", codeSnippet, stack: "" };
228
+ }
69
229
  let success = false;
70
230
  let stack = "";
231
+ let output = "";
71
232
  let code = codeSnippet.code;
72
233
  if (config.transformCode) {
73
234
  try {
@@ -78,28 +239,76 @@ function test(config, _filename, sandbox) {
78
239
  }
79
240
  }
80
241
  let perSnippetSandbox;
242
+ let activeSandbox;
243
+ let isSharedSandbox = false;
81
244
  if (sandbox === undefined) {
82
245
  perSnippetSandbox = makeTestSandbox(config);
246
+ activeSandbox = perSnippetSandbox;
247
+ }
248
+ else {
249
+ activeSandbox = sandbox;
250
+ isSharedSandbox = true;
83
251
  }
84
252
  if (config.beforeEach) {
85
253
  config.beforeEach();
86
254
  }
87
- try {
88
- const result = (0, esbuild_1.transformSync)(code, {
89
- loader: 'ts',
90
- format: 'cjs',
91
- target: 'node12'
92
- });
93
- code = result.code || "";
94
- (0, vm_1.runInNewContext)(code, perSnippetSandbox || sandbox);
95
- success = true;
255
+ let result;
256
+ if (codeSnippet.language === 'python') {
257
+ result = (0, python_1.pythonHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
258
+ }
259
+ else if (['bash', 'sh', 'zsh'].includes(codeSnippet.language || '')) {
260
+ result = (0, shell_1.shellHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
261
+ }
262
+ else if (codeSnippet.language === 'go') {
263
+ result = (0, go_1.goHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
264
+ }
265
+ else if (codeSnippet.language === 'rust') {
266
+ result = (0, rust_1.rustHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
267
+ }
268
+ else if (codeSnippet.language === 'fortran') {
269
+ result = (0, fortran_1.fortranHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
270
+ }
271
+ else if (codeSnippet.language === 'cobol') {
272
+ result = (0, cobol_1.cobolHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
273
+ }
274
+ else if (codeSnippet.language === 'c') {
275
+ result = (0, c_1.cHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
276
+ }
277
+ else if (codeSnippet.language === 'basic') {
278
+ result = (0, basic_1.basicHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
279
+ }
280
+ else if (codeSnippet.language === 'java') {
281
+ result = (0, java_1.javaHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
282
+ }
283
+ else if (codeSnippet.language === 'perl') {
284
+ result = (0, perl_1.perlHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
285
+ }
286
+ else if (codeSnippet.language === 'r') {
287
+ result = (0, r_1.rHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
288
+ }
289
+ else if (codeSnippet.language === 'pascal') {
290
+ result = (0, pascal_1.pascalHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
291
+ }
292
+ else if (codeSnippet.language === 'csharp') {
293
+ result = (0, csharp_1.csharpHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
294
+ }
295
+ else if (codeSnippet.language === 'text') {
296
+ result = { success: true, stack: "" };
297
+ }
298
+ else {
299
+ result = (0, javascript_1.javascriptHandler)(code, codeSnippet, config, activeSandbox, isSharedSandbox);
96
300
  }
97
- catch (e) {
98
- stack = e.stack || "";
301
+ success = result.success;
302
+ stack = result.stack;
303
+ output = result.output || "";
304
+ // Store output if ID is present
305
+ if (codeSnippet.id && outputs) {
306
+ outputs[codeSnippet.id] = output;
99
307
  }
100
308
  const status = success ? "pass" : "fail";
309
+ const executionTime = performance.now() - startTime;
101
310
  process.stdout.write(success ? chalk_1.default.green(".") : chalk_1.default.red("x"));
102
- return { status, codeSnippet, stack };
311
+ return { status, codeSnippet, stack, executionTime };
103
312
  };
104
313
  }
105
314
  function moduleNotFoundError(moduleName) {
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.basicHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ // Check if BASIC code is a complete program (has END statement)
6
+ function isCompleteBASIC(code) {
7
+ // Look for END as a statement (possibly with line number prefix)
8
+ // Matches: "END", "10 END", "END\n", etc.
9
+ return /^\d*\s*END\s*$/im.test(code);
10
+ }
11
+ const basicHandler = (code, _snippet, config, sandbox, isSharedSandbox) => {
12
+ let success = false;
13
+ let stack = "";
14
+ const context = sandbox;
15
+ // If sharing code, we need to accumulate previous basic snippets
16
+ if (isSharedSandbox) {
17
+ if (!context._basicContext) {
18
+ context._basicContext = "";
19
+ }
20
+ // Trim trailing newline from code before adding to avoid blank lines
21
+ // (code already has trailing newline from parser)
22
+ const trimmedCode = code.replace(/\n+$/, '');
23
+ context._basicContext += trimmedCode + "\n";
24
+ code = context._basicContext;
25
+ // In shared mode, only run when program is complete (has END)
26
+ // Incomplete snippets are setup code - mark as pass without running
27
+ if (!isCompleteBASIC(code)) {
28
+ return { success: true, stack: "", output: "" };
29
+ }
30
+ }
31
+ try {
32
+ // cbmbasic accepts input from stdin
33
+ const timeout = config.timeout || 30000;
34
+ const result = (0, child_process_1.spawnSync)('cbmbasic', [], { input: code, encoding: 'utf-8', timeout });
35
+ if (result.error && result.error.code === 'ETIMEDOUT') {
36
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
37
+ }
38
+ if (result.status === 0) {
39
+ success = true;
40
+ }
41
+ else {
42
+ stack = result.stderr || "BASIC execution failed with non-zero exit code";
43
+ }
44
+ return { success, stack, output: result.stdout };
45
+ }
46
+ catch (e) {
47
+ stack = e.message || "Failed to spawn cbmbasic";
48
+ return { success, stack };
49
+ }
50
+ };
51
+ exports.basicHandler = basicHandler;
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ // Helper to parse C code
9
+ function parseCCode(fullCode) {
10
+ const lines = fullCode.split('\n');
11
+ const includes = [];
12
+ let topLevel = "";
13
+ let mainBody = "";
14
+ let state = 'NORMAL';
15
+ let braceCount = 0;
16
+ for (const line of lines) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed) {
19
+ // Empty lines preserve structure slightly better if added to last active section
20
+ // or just ignore. Let's add to mainBody if normal, or topLevel if in block.
21
+ if (state === 'IN_TOP_LEVEL_BLOCK')
22
+ topLevel += "\n";
23
+ else
24
+ mainBody += "\n";
25
+ continue;
26
+ }
27
+ if (state === 'NORMAL') {
28
+ if (trimmed.startsWith('#')) {
29
+ includes.push(line);
30
+ }
31
+ else if (trimmed.startsWith('struct ') ||
32
+ trimmed.startsWith('union ') ||
33
+ trimmed.startsWith('enum ') ||
34
+ trimmed.startsWith('typedef ') ||
35
+ // Function definition heuristic: ReturnType Name(Args) {
36
+ // Must end with {
37
+ (trimmed.endsWith('{') && !trimmed.startsWith('if') && !trimmed.startsWith('for') && !trimmed.startsWith('while') && !trimmed.startsWith('switch') && !trimmed.startsWith('do'))) {
38
+ topLevel += line + "\n";
39
+ // Count braces
40
+ const open = (line.match(/\{/g) || []).length;
41
+ const close = (line.match(/\}/g) || []).length;
42
+ braceCount = open - close;
43
+ if (braceCount > 0) {
44
+ state = 'IN_TOP_LEVEL_BLOCK';
45
+ }
46
+ }
47
+ else {
48
+ // Statements, variable declarations (int x;), etc. go to main
49
+ mainBody += line + "\n";
50
+ }
51
+ }
52
+ else if (state === 'IN_TOP_LEVEL_BLOCK') {
53
+ topLevel += line + "\n";
54
+ const open = (line.match(/\{/g) || []).length;
55
+ const close = (line.match(/\}/g) || []).length;
56
+ braceCount += open - close;
57
+ if (braceCount <= 0) {
58
+ state = 'NORMAL';
59
+ braceCount = 0;
60
+ }
61
+ }
62
+ }
63
+ return { includes, topLevel, mainBody };
64
+ }
65
+ const cHandler = (code, snippet, config, sandbox, isSharedSandbox) => {
66
+ let success = false;
67
+ let stack = "";
68
+ const context = sandbox;
69
+ let cCode = code;
70
+ // Handle shared state
71
+ if (isSharedSandbox) {
72
+ if (!context._cCode) {
73
+ context._cCode = "";
74
+ }
75
+ context._cCode += code + "\n";
76
+ cCode = context._cCode;
77
+ }
78
+ let finalSource = "";
79
+ // If main is explicitly provided, use it (disable parsing/wrapping)
80
+ // But in shared mode, we usually want to compose.
81
+ // If user provides "int main() { ... }" in a snippet, we assume they take full control?
82
+ // Or do we try to merge?
83
+ // Merging multiple mains is impossible.
84
+ // So if any snippet contains "int main", we might just assume it's the full program so far?
85
+ // But earlier snippets might be structs/includes.
86
+ // Let's assume if "int main" or "void main" is present, we respect it,
87
+ // but we still might want to prepend includes from previous snippets if they were separate?
88
+ // For simplicity: If "main(" found, treat as full program.
89
+ // But parsing is safer for "includes" + "functions" + "statements".
90
+ if (cCode.includes('main(')) {
91
+ // If shared sandbox, we might have accumulated a main from previous snippets?
92
+ // No, if we are parsing, we construct main.
93
+ // If the USER typed main, we trust them.
94
+ finalSource = cCode;
95
+ // Auto-add stdio.h if missing and needed?
96
+ if (!finalSource.includes('<stdio.h>') && finalSource.includes('printf')) {
97
+ finalSource = `#include <stdio.h>\n${finalSource}`;
98
+ }
99
+ }
100
+ else {
101
+ const { includes, topLevel, mainBody } = parseCCode(cCode);
102
+ // Auto-add stdio.h if needed
103
+ let autoIncludes = "";
104
+ const includesStr = includes.join('\n');
105
+ if (!includesStr.includes('<stdio.h>') && (topLevel.includes('printf') || mainBody.includes('printf'))) {
106
+ autoIncludes = `#include <stdio.h>\n`;
107
+ }
108
+ finalSource = `${autoIncludes}${includesStr}
109
+
110
+ ${topLevel}
111
+
112
+ int main() {
113
+ ${mainBody}
114
+ return 0;
115
+ }`;
116
+ }
117
+ const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
118
+ const tempSourceFile = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_c_${uniqueId}.c`);
119
+ const tempExeFile = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_c_${uniqueId}`);
120
+ try {
121
+ const args = snippet.args || [];
122
+ const env = snippet.env ? { ...process.env, ...snippet.env } : undefined;
123
+ const timeout = config.timeout || 30000;
124
+ (0, fs_1.writeFileSync)(tempSourceFile, finalSource);
125
+ // Compile with gcc
126
+ // args passed to GCC (compiler flags)
127
+ const compileResult = (0, child_process_1.spawnSync)('gcc', [tempSourceFile, '-o', tempExeFile, ...args], { encoding: 'utf-8', timeout });
128
+ if (compileResult.status !== 0) {
129
+ stack = compileResult.stderr || "C compilation failed";
130
+ if (isSharedSandbox) {
131
+ stack += `\n\nGenerated Source:\n${finalSource}`;
132
+ }
133
+ }
134
+ else {
135
+ // Run
136
+ // What if we want runtime args?
137
+ // Currently args are compiler flags.
138
+ // Maybe we need separate config for runtime args?
139
+ // For now, adhering to "arguments to compilers/interpreters".
140
+ const runResult = (0, child_process_1.spawnSync)(tempExeFile, [], { encoding: 'utf-8', env, timeout });
141
+ if (runResult.error && runResult.error.code === 'ETIMEDOUT') {
142
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
143
+ }
144
+ if (runResult.status === 0) {
145
+ success = true;
146
+ }
147
+ else {
148
+ stack = runResult.stderr || "C execution failed with non-zero exit code";
149
+ }
150
+ return { success, stack, output: runResult.stdout };
151
+ }
152
+ }
153
+ catch (e) {
154
+ stack = e.message || "Failed to execute gcc or run binary";
155
+ }
156
+ finally {
157
+ try {
158
+ if ((0, fs_1.existsSync)(tempSourceFile))
159
+ (0, fs_1.unlinkSync)(tempSourceFile);
160
+ if ((0, fs_1.existsSync)(tempExeFile))
161
+ (0, fs_1.unlinkSync)(tempExeFile);
162
+ }
163
+ catch {
164
+ // Ignore cleanup error
165
+ }
166
+ }
167
+ return { success, stack };
168
+ };
169
+ exports.cHandler = cHandler;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cobolHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ const cobolHandler = (code, _snippet, config, _sandbox, _isSharedSandbox) => {
9
+ let success = false;
10
+ let stack = "";
11
+ // COBOL execution logic
12
+ // Use shorter filename as cobc has strict length limits
13
+ const uniqueId = Math.random().toString(36).substring(2, 10);
14
+ const tempSourceFile = (0, path_1.join)((0, os_1.tmpdir)(), `cob_${uniqueId}.cob`);
15
+ const tempExeFile = (0, path_1.join)((0, os_1.tmpdir)(), `cob_${uniqueId}`);
16
+ const timeout = config.timeout || 30000;
17
+ try {
18
+ (0, fs_1.writeFileSync)(tempSourceFile, code);
19
+ // Compile with cobc -x (executable) -free (free format)
20
+ const compileResult = (0, child_process_1.spawnSync)('cobc', ['-x', '-free', '-o', tempExeFile, tempSourceFile], { encoding: 'utf-8', timeout });
21
+ if (compileResult.status !== 0) {
22
+ stack = compileResult.stderr || "COBOL compilation failed";
23
+ }
24
+ else {
25
+ // Run
26
+ const runResult = (0, child_process_1.spawnSync)(tempExeFile, [], { encoding: 'utf-8', timeout });
27
+ if (runResult.error && runResult.error.code === 'ETIMEDOUT') {
28
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
29
+ }
30
+ if (runResult.status === 0) {
31
+ success = true;
32
+ }
33
+ else {
34
+ stack = runResult.stderr || "COBOL execution failed with non-zero exit code";
35
+ }
36
+ return { success, stack, output: runResult.stdout };
37
+ }
38
+ }
39
+ catch (e) {
40
+ stack = e.message || "Failed to execute cobc or run binary";
41
+ }
42
+ finally {
43
+ try {
44
+ if ((0, fs_1.existsSync)(tempSourceFile))
45
+ (0, fs_1.unlinkSync)(tempSourceFile);
46
+ if ((0, fs_1.existsSync)(tempExeFile))
47
+ (0, fs_1.unlinkSync)(tempExeFile);
48
+ }
49
+ catch {
50
+ // Ignore cleanup error
51
+ }
52
+ }
53
+ return { success, stack };
54
+ };
55
+ exports.cobolHandler = cobolHandler;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.csharpHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ const csharpHandler = (code, _snippet, config, _sandbox, _isSharedSandbox) => {
9
+ let success = false;
10
+ let stack = "";
11
+ // C# execution logic
12
+ // Auto-wrap in class Program and Main method if not present
13
+ let csCode = code;
14
+ const className = "Program";
15
+ // Simple heuristic to detect if class is provided
16
+ if (!csCode.includes("class ")) {
17
+ csCode = `using System;
18
+ public class ${className} {
19
+ public static void Main(string[] args) {
20
+ ${csCode}
21
+ }
22
+ }`;
23
+ }
24
+ const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
25
+ const tempDir = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_csharp_${uniqueId}`);
26
+ const timeout = config.timeout || 30000;
27
+ try {
28
+ if (!(0, fs_1.existsSync)(tempDir)) {
29
+ (0, fs_1.mkdirSync)(tempDir);
30
+ }
31
+ const tempSourceFile = (0, path_1.join)(tempDir, `${className}.cs`);
32
+ const tempExeFile = (0, path_1.join)(tempDir, `${className}.exe`);
33
+ (0, fs_1.writeFileSync)(tempSourceFile, csCode);
34
+ // Compile with mcs (Mono Compiler)
35
+ const compileResult = (0, child_process_1.spawnSync)('mcs', ['-out:' + tempExeFile, tempSourceFile], { encoding: 'utf-8', cwd: tempDir, timeout });
36
+ if (compileResult.status !== 0) {
37
+ stack = compileResult.stderr || "C# compilation failed (mcs)";
38
+ }
39
+ else {
40
+ // Run with mono
41
+ const runResult = (0, child_process_1.spawnSync)('mono', [tempExeFile], { encoding: 'utf-8', cwd: tempDir, timeout });
42
+ if (runResult.error && runResult.error.code === 'ETIMEDOUT') {
43
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
44
+ }
45
+ if (runResult.status === 0) {
46
+ success = true;
47
+ }
48
+ else {
49
+ stack = runResult.stderr || "C# execution failed with non-zero exit code";
50
+ }
51
+ return { success, stack, output: runResult.stdout };
52
+ }
53
+ }
54
+ catch (e) {
55
+ stack = e.message || "Failed to execute mcs or mono";
56
+ }
57
+ finally {
58
+ try {
59
+ if (fs_1.rmSync) {
60
+ (0, fs_1.rmSync)(tempDir, { recursive: true, force: true });
61
+ }
62
+ else {
63
+ if ((0, fs_1.existsSync)((0, path_1.join)(tempDir, `${className}.cs`)))
64
+ (0, fs_1.unlinkSync)((0, path_1.join)(tempDir, `${className}.cs`));
65
+ if ((0, fs_1.existsSync)((0, path_1.join)(tempDir, `${className}.exe`)))
66
+ (0, fs_1.unlinkSync)((0, path_1.join)(tempDir, `${className}.exe`));
67
+ if ((0, fs_1.existsSync)(tempDir))
68
+ (0, fs_1.rmdirSync)(tempDir);
69
+ }
70
+ }
71
+ catch {
72
+ // Ignore cleanup error
73
+ }
74
+ }
75
+ return { success, stack };
76
+ };
77
+ exports.csharpHandler = csharpHandler;