sandlot 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/browser/bundler.d.ts +68 -0
  2. package/dist/browser/bundler.d.ts.map +1 -0
  3. package/dist/browser/executor.d.ts +46 -0
  4. package/dist/browser/executor.d.ts.map +1 -0
  5. package/dist/browser/index.d.ts +9 -0
  6. package/dist/browser/index.d.ts.map +1 -0
  7. package/dist/browser/index.js +2692 -0
  8. package/dist/browser/preset.d.ts +63 -0
  9. package/dist/browser/preset.d.ts.map +1 -0
  10. package/dist/commands/index.d.ts +20 -11
  11. package/dist/commands/index.d.ts.map +1 -1
  12. package/dist/commands/types.d.ts +31 -132
  13. package/dist/commands/types.d.ts.map +1 -1
  14. package/dist/core/bundler-utils.d.ts +142 -0
  15. package/dist/core/bundler-utils.d.ts.map +1 -0
  16. package/dist/core/esm-types-resolver.d.ts +125 -0
  17. package/dist/core/esm-types-resolver.d.ts.map +1 -0
  18. package/dist/core/executor.d.ts +35 -0
  19. package/dist/core/executor.d.ts.map +1 -0
  20. package/dist/{fs.d.ts → core/fs.d.ts} +27 -29
  21. package/dist/core/fs.d.ts.map +1 -0
  22. package/dist/core/sandbox.d.ts +30 -0
  23. package/dist/core/sandbox.d.ts.map +1 -0
  24. package/dist/core/sandlot.d.ts +30 -0
  25. package/dist/core/sandlot.d.ts.map +1 -0
  26. package/dist/core/shared-module-registry.d.ts +46 -0
  27. package/dist/core/shared-module-registry.d.ts.map +1 -0
  28. package/dist/core/typechecker.d.ts +60 -0
  29. package/dist/core/typechecker.d.ts.map +1 -0
  30. package/dist/index.d.ts +11 -16
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1405 -2049
  33. package/dist/node/bundler.d.ts +48 -0
  34. package/dist/node/bundler.d.ts.map +1 -0
  35. package/dist/node/executor.d.ts +48 -0
  36. package/dist/node/executor.d.ts.map +1 -0
  37. package/dist/node/index.d.ts +9 -0
  38. package/dist/node/index.d.ts.map +1 -0
  39. package/dist/node/index.js +2646 -0
  40. package/dist/node/preset.d.ts +62 -0
  41. package/dist/node/preset.d.ts.map +1 -0
  42. package/dist/types.d.ts +525 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/package.json +27 -8
  45. package/src/browser/bundler.ts +294 -0
  46. package/src/browser/executor.ts +71 -0
  47. package/src/browser/index.ts +57 -0
  48. package/src/browser/preset.ts +179 -0
  49. package/src/commands/index.ts +526 -43
  50. package/src/commands/types.ts +82 -146
  51. package/src/core/bundler-utils.ts +630 -0
  52. package/src/core/esm-types-resolver.ts +432 -0
  53. package/src/core/executor.ts +161 -0
  54. package/src/{fs.ts → core/fs.ts} +59 -37
  55. package/src/core/sandbox.ts +621 -0
  56. package/src/core/sandlot.ts +77 -0
  57. package/src/core/shared-module-registry.ts +138 -0
  58. package/src/core/typechecker.ts +607 -0
  59. package/src/index.ts +104 -139
  60. package/src/node/bundler.ts +194 -0
  61. package/src/node/executor.ts +87 -0
  62. package/src/node/index.ts +39 -0
  63. package/src/node/preset.ts +178 -0
  64. package/src/types.ts +668 -0
  65. package/README.md +0 -243
  66. package/dist/build-emitter.d.ts +0 -47
  67. package/dist/build-emitter.d.ts.map +0 -1
  68. package/dist/builder.d.ts +0 -370
  69. package/dist/builder.d.ts.map +0 -1
  70. package/dist/bundler.d.ts +0 -148
  71. package/dist/bundler.d.ts.map +0 -1
  72. package/dist/commands/compile.d.ts +0 -13
  73. package/dist/commands/compile.d.ts.map +0 -1
  74. package/dist/commands/packages.d.ts +0 -17
  75. package/dist/commands/packages.d.ts.map +0 -1
  76. package/dist/commands/run.d.ts +0 -40
  77. package/dist/commands/run.d.ts.map +0 -1
  78. package/dist/commands.d.ts +0 -179
  79. package/dist/commands.d.ts.map +0 -1
  80. package/dist/fs.d.ts.map +0 -1
  81. package/dist/internal.d.ts +0 -79
  82. package/dist/internal.d.ts.map +0 -1
  83. package/dist/internal.js +0 -1976
  84. package/dist/loader.d.ts +0 -164
  85. package/dist/loader.d.ts.map +0 -1
  86. package/dist/packages.d.ts +0 -199
  87. package/dist/packages.d.ts.map +0 -1
  88. package/dist/runner.d.ts +0 -314
  89. package/dist/runner.d.ts.map +0 -1
  90. package/dist/sandbox-manager.d.ts +0 -261
  91. package/dist/sandbox-manager.d.ts.map +0 -1
  92. package/dist/sandbox.d.ts +0 -267
  93. package/dist/sandbox.d.ts.map +0 -1
  94. package/dist/shared-modules.d.ts +0 -148
  95. package/dist/shared-modules.d.ts.map +0 -1
  96. package/dist/shared-resources.d.ts +0 -102
  97. package/dist/shared-resources.d.ts.map +0 -1
  98. package/dist/ts-libs.d.ts +0 -98
  99. package/dist/ts-libs.d.ts.map +0 -1
  100. package/dist/typechecker.d.ts +0 -127
  101. package/dist/typechecker.d.ts.map +0 -1
  102. package/src/build-emitter.ts +0 -64
  103. package/src/builder.ts +0 -498
  104. package/src/bundler.ts +0 -542
  105. package/src/commands/compile.ts +0 -236
  106. package/src/commands/packages.ts +0 -154
  107. package/src/commands/run.ts +0 -245
  108. package/src/internal.ts +0 -119
  109. package/src/loader.ts +0 -229
  110. package/src/packages.ts +0 -936
  111. package/src/sandbox.ts +0 -396
  112. package/src/shared-modules.ts +0 -280
  113. package/src/shared-resources.ts +0 -166
  114. package/src/ts-libs.ts +0 -320
  115. package/src/typechecker.ts +0 -635
@@ -1,236 +0,0 @@
1
- /**
2
- * Compile-related commands: tsc and build
3
- */
4
-
5
- import { defineCommand, type CommandContext } from "just-bash/browser";
6
- import { typecheck, formatDiagnosticsForAgent, type TypecheckResult } from "../typechecker";
7
- import { bundle } from "../bundler";
8
- import { loadModule } from "../loader";
9
- import { type CommandDeps, formatEsbuildMessages } from "./types";
10
-
11
- /**
12
- * Create the `tsc` command for type checking
13
- */
14
- export function createTscCommand(deps: CommandDeps) {
15
- const { fs, libFiles, tsconfigPath } = deps;
16
-
17
- return defineCommand("tsc", async (args, _ctx: CommandContext) => {
18
- const entryPoint = args[0];
19
- if (!entryPoint) {
20
- return {
21
- stdout: "",
22
- stderr: `Usage: tsc <entry-point>\n\nExample: tsc /src/index.ts\n`,
23
- exitCode: 1,
24
- };
25
- }
26
-
27
- try {
28
- // Check if entry point exists
29
- if (!(await fs.exists(entryPoint))) {
30
- return {
31
- stdout: "",
32
- stderr: `Error: Entry point not found: ${entryPoint}\n`,
33
- exitCode: 1,
34
- };
35
- }
36
-
37
- const result = await typecheck({
38
- fs,
39
- entryPoint,
40
- tsconfigPath,
41
- libFiles,
42
- });
43
-
44
- if (result.hasErrors) {
45
- const formatted = formatDiagnosticsForAgent(result.diagnostics);
46
- return {
47
- stdout: "",
48
- stderr: formatted + "\n",
49
- exitCode: 1,
50
- };
51
- }
52
-
53
- const checkedCount = result.checkedFiles.length;
54
- const warningCount = result.diagnostics.filter((d) => d.category === "warning").length;
55
-
56
- let output = `Type check passed. Checked ${checkedCount} file(s).\n`;
57
- if (warningCount > 0) {
58
- output += `\nWarnings:\n${formatDiagnosticsForAgent(result.diagnostics.filter((d) => d.category === "warning"))}\n`;
59
- }
60
-
61
- return {
62
- stdout: output,
63
- stderr: "",
64
- exitCode: 0,
65
- };
66
- } catch (err) {
67
- return {
68
- stdout: "",
69
- stderr: `Type check failed: ${err instanceof Error ? err.message : String(err)}\n`,
70
- exitCode: 1,
71
- };
72
- }
73
- });
74
- }
75
-
76
- /**
77
- * Create the `build` command for bundling (with automatic type checking)
78
- */
79
- export function createBuildCommand(deps: CommandDeps) {
80
- const { fs, libFiles, tsconfigPath, onBuild, getValidation, sharedModules } = deps;
81
-
82
- return defineCommand("build", async (args, _ctx: CommandContext) => {
83
- // Parse arguments
84
- let entryPoint: string | null = null;
85
- let skipTypecheck = false;
86
- let minify = false;
87
- let format: "esm" | "iife" | "cjs" = "esm";
88
-
89
- for (let i = 0; i < args.length; i++) {
90
- const arg = args[i];
91
- if (arg === "--skip-typecheck" || arg === "-s") {
92
- skipTypecheck = true;
93
- } else if (arg === "--minify" || arg === "-m") {
94
- minify = true;
95
- } else if ((arg === "--format" || arg === "-f") && args[i + 1]) {
96
- const f = args[++i]!.toLowerCase();
97
- if (f === "esm" || f === "iife" || f === "cjs") {
98
- format = f;
99
- }
100
- } else if (!arg!.startsWith("-")) {
101
- entryPoint = arg!;
102
- }
103
- }
104
-
105
- // Entry point is required
106
- if (!entryPoint) {
107
- return {
108
- stdout: "",
109
- stderr: `Usage: build <entry-point> [options]\n\nOptions:\n --skip-typecheck, -s Skip type checking\n --minify, -m Minify output\n --format, -f <fmt> Output format (esm|iife|cjs)\n\nExample: build /src/index.ts\n`,
110
- exitCode: 1,
111
- };
112
- }
113
-
114
- try {
115
- // Check if entry point exists
116
- if (!(await fs.exists(entryPoint))) {
117
- return {
118
- stdout: "",
119
- stderr: `Error: Entry point not found: ${entryPoint}\n`,
120
- exitCode: 1,
121
- };
122
- }
123
-
124
- // Step 1: Type check (unless skipped)
125
- let typecheckResult: TypecheckResult | null = null;
126
- if (!skipTypecheck) {
127
- typecheckResult = await typecheck({
128
- fs,
129
- entryPoint,
130
- tsconfigPath,
131
- libFiles,
132
- });
133
-
134
- if (typecheckResult.hasErrors) {
135
- const formatted = formatDiagnosticsForAgent(typecheckResult.diagnostics);
136
- return {
137
- stdout: "",
138
- stderr: `Build failed: Type errors found.\n\n${formatted}\n`,
139
- exitCode: 1,
140
- };
141
- }
142
- }
143
-
144
- // Step 2: Bundle
145
- const bundleResult = await bundle({
146
- fs,
147
- entryPoint,
148
- format,
149
- minify,
150
- sharedModules,
151
- });
152
-
153
- // Step 3: Load module
154
- let loadedModule: Record<string, unknown>;
155
- try {
156
- loadedModule = await loadModule<Record<string, unknown>>(bundleResult);
157
- } catch (err) {
158
- const errorMessage = err instanceof Error ? err.message : String(err);
159
- return {
160
- stdout: "",
161
- stderr: `Build failed: Module failed to load.\n\n${errorMessage}\n`,
162
- exitCode: 1,
163
- };
164
- }
165
-
166
- // Step 4: Validate (if validation function is set)
167
- const validateFn = getValidation?.();
168
- let validatedModule = loadedModule;
169
-
170
- if (validateFn) {
171
- try {
172
- validatedModule = validateFn(loadedModule);
173
- } catch (err) {
174
- const errorMessage = err instanceof Error ? err.message : String(err);
175
- return {
176
- stdout: "",
177
- stderr: `Build failed: Validation error.\n\n${errorMessage}\n`,
178
- exitCode: 1,
179
- };
180
- }
181
- }
182
-
183
- // Invoke callback with build output (bundle + validated module)
184
- if (onBuild) {
185
- await onBuild({ bundle: bundleResult, module: validatedModule });
186
- }
187
-
188
- // Build success message
189
- let output = `Build successful!\n`;
190
- output += `Entry: ${entryPoint}\n`;
191
- output += `Format: ${format}\n`;
192
- output += `Size: ${(bundleResult.code.length / 1024).toFixed(2)} KB\n`;
193
-
194
- if (typecheckResult) {
195
- output += `Type checked: ${typecheckResult.checkedFiles.length} file(s)\n`;
196
- }
197
-
198
- output += `Bundled: ${bundleResult.includedFiles.length} file(s)\n`;
199
-
200
- // Show exports for visibility
201
- const exportNames = Object.keys(loadedModule).filter((k) => !k.startsWith("__"));
202
- if (exportNames.length > 0) {
203
- output += `Exports: ${exportNames.join(", ")}\n`;
204
- }
205
-
206
- if (validateFn) {
207
- output += `Validation: passed\n`;
208
- }
209
-
210
- // Include warnings if any
211
- if (bundleResult.warnings.length > 0) {
212
- output += `\nBuild warnings:\n${formatEsbuildMessages(bundleResult.warnings)}\n`;
213
- }
214
-
215
- if (typecheckResult) {
216
- const warnings = typecheckResult.diagnostics.filter((d) => d.category === "warning");
217
- if (warnings.length > 0) {
218
- output += `\nType warnings:\n${formatDiagnosticsForAgent(warnings)}\n`;
219
- }
220
- }
221
-
222
- return {
223
- stdout: output,
224
- stderr: "",
225
- exitCode: 0,
226
- };
227
- } catch (err) {
228
- const errorMessage = err instanceof Error ? err.message : String(err);
229
- return {
230
- stdout: "",
231
- stderr: `Build failed: ${errorMessage}\n`,
232
- exitCode: 1,
233
- };
234
- }
235
- });
236
- }
@@ -1,154 +0,0 @@
1
- /**
2
- * Package management commands: install, uninstall, list
3
- */
4
-
5
- import { defineCommand, type CommandContext } from "just-bash/browser";
6
- import { installPackage, uninstallPackage, listPackages } from "../packages";
7
- import type { CommandDeps } from "./types";
8
-
9
- /**
10
- * Create the `install` command for adding packages from npm
11
- */
12
- export function createInstallCommand(deps: CommandDeps) {
13
- const { fs, typesCache } = deps;
14
-
15
- return defineCommand("install", async (args, _ctx: CommandContext) => {
16
- if (args.length === 0) {
17
- return {
18
- stdout: "",
19
- stderr: "Usage: install <package>[@version] [...packages]\n\nExamples:\n install react\n install lodash@4.17.21\n install @tanstack/react-query@5\n",
20
- exitCode: 1,
21
- };
22
- }
23
-
24
- const results: string[] = [];
25
- let hasError = false;
26
-
27
- for (const packageSpec of args) {
28
- try {
29
- const result = await installPackage(fs, packageSpec!, { cache: typesCache });
30
-
31
- let status = `+ ${result.name}@${result.version}`;
32
- if (result.typesInstalled) {
33
- status += ` (${result.typeFilesCount} type file${result.typeFilesCount !== 1 ? "s" : ""})`;
34
- if (result.fromCache) {
35
- status += " [cached]";
36
- }
37
- } else if (result.typesError) {
38
- status += ` (no types: ${result.typesError})`;
39
- }
40
- results.push(status);
41
- } catch (err) {
42
- hasError = true;
43
- const message = err instanceof Error ? err.message : String(err);
44
- results.push(`x ${packageSpec}: ${message}`);
45
- }
46
- }
47
-
48
- const output = results.join("\n") + "\n";
49
-
50
- if (hasError) {
51
- return {
52
- stdout: "",
53
- stderr: output,
54
- exitCode: 1,
55
- };
56
- }
57
-
58
- return {
59
- stdout: output,
60
- stderr: "",
61
- exitCode: 0,
62
- };
63
- });
64
- }
65
-
66
- /**
67
- * Create the `uninstall` command for removing packages
68
- */
69
- export function createUninstallCommand(deps: CommandDeps) {
70
- const { fs } = deps;
71
-
72
- return defineCommand("uninstall", async (args, _ctx: CommandContext) => {
73
- if (args.length === 0) {
74
- return {
75
- stdout: "",
76
- stderr: "Usage: uninstall <package> [...packages]\n",
77
- exitCode: 1,
78
- };
79
- }
80
-
81
- const results: string[] = [];
82
- let hasError = false;
83
-
84
- for (const packageName of args) {
85
- try {
86
- const removed = await uninstallPackage(fs, packageName!);
87
- if (removed) {
88
- results.push(`- ${packageName}`);
89
- } else {
90
- results.push(`x ${packageName}: not installed`);
91
- hasError = true;
92
- }
93
- } catch (err) {
94
- hasError = true;
95
- const message = err instanceof Error ? err.message : String(err);
96
- results.push(`x ${packageName}: ${message}`);
97
- }
98
- }
99
-
100
- const output = results.join("\n") + "\n";
101
-
102
- if (hasError) {
103
- return {
104
- stdout: "",
105
- stderr: output,
106
- exitCode: 1,
107
- };
108
- }
109
-
110
- return {
111
- stdout: output,
112
- stderr: "",
113
- exitCode: 0,
114
- };
115
- });
116
- }
117
-
118
- /**
119
- * Create the `list` command (alias: `ls`) for showing installed packages
120
- */
121
- export function createListCommand(deps: CommandDeps) {
122
- const { fs } = deps;
123
-
124
- return defineCommand("list", async (_args, _ctx: CommandContext) => {
125
- try {
126
- const packages = await listPackages(fs);
127
-
128
- if (packages.length === 0) {
129
- return {
130
- stdout: "No packages installed.\n",
131
- stderr: "",
132
- exitCode: 0,
133
- };
134
- }
135
-
136
- const output = packages
137
- .map((pkg) => `${pkg.name}@${pkg.version}`)
138
- .join("\n") + "\n";
139
-
140
- return {
141
- stdout: output,
142
- stderr: "",
143
- exitCode: 0,
144
- };
145
- } catch (err) {
146
- const message = err instanceof Error ? err.message : String(err);
147
- return {
148
- stdout: "",
149
- stderr: `Failed to list packages: ${message}\n`,
150
- exitCode: 1,
151
- };
152
- }
153
- });
154
- }
@@ -1,245 +0,0 @@
1
- /**
2
- * Run command for executing code in the sandbox.
3
- */
4
-
5
- import { defineCommand, type CommandContext } from "just-bash/browser";
6
- import { typecheck, formatDiagnosticsForAgent } from "../typechecker";
7
- import { bundle } from "../bundler";
8
- import { loadModule } from "../loader";
9
- import type { CommandDeps, RunContext } from "./types";
10
-
11
- /**
12
- * Create the `run` command for executing code in the sandbox.
13
- *
14
- * The run command:
15
- * 1. Builds the entry point (with type checking by default)
16
- * 2. Dynamically imports the bundle
17
- * 3. If a `main` export exists, calls it with a RunContext
18
- * 4. Captures all console output (log, warn, error)
19
- * 5. Returns the captured output and any return value from main()
20
- *
21
- * Usage:
22
- * run [entry] [--skip-typecheck|-s] [--timeout|-t <ms>] [-- args...]
23
- *
24
- * Code can be written in two styles:
25
- *
26
- * 1. Script style (top-level code, runs on import):
27
- * ```ts
28
- * console.log("Hello from script!");
29
- * const result = 2 + 2;
30
- * console.log("Result:", result);
31
- * ```
32
- *
33
- * 2. Main function style (with context access):
34
- * ```ts
35
- * import type { RunContext } from "sandlot";
36
- *
37
- * export async function main(ctx: RunContext) {
38
- * ctx.log("Reading file...");
39
- * const content = await ctx.fs.readFile("/data/input.txt");
40
- * ctx.log("Content:", content);
41
- * return { success: true };
42
- * }
43
- * ```
44
- */
45
- export function createRunCommand(deps: CommandDeps) {
46
- const { fs, libFiles, tsconfigPath, runOptions = {}, sharedModules } = deps;
47
-
48
- return defineCommand("run", async (args, _ctx: CommandContext) => {
49
- // Parse arguments
50
- let entryPoint: string | null = null;
51
- let skipTypecheck = runOptions.skipTypecheck ?? false;
52
- let timeout = runOptions.timeout ?? 30000;
53
- const scriptArgs: string[] = [];
54
- let collectingArgs = false;
55
-
56
- for (let i = 0; i < args.length; i++) {
57
- const arg = args[i];
58
-
59
- if (collectingArgs) {
60
- scriptArgs.push(arg!);
61
- continue;
62
- }
63
-
64
- if (arg === "--") {
65
- collectingArgs = true;
66
- } else if (arg === "--skip-typecheck" || arg === "-s") {
67
- skipTypecheck = true;
68
- } else if ((arg === "--timeout" || arg === "-t") && args[i + 1]) {
69
- timeout = parseInt(args[++i]!, 10);
70
- if (isNaN(timeout)) timeout = 30000;
71
- } else if (!arg!.startsWith("-")) {
72
- entryPoint = arg!;
73
- }
74
- }
75
-
76
- // Entry point is required
77
- if (!entryPoint) {
78
- return {
79
- stdout: "",
80
- stderr: `Usage: run <entry-point> [options] [-- args...]\n\nOptions:\n --skip-typecheck, -s Skip type checking\n --timeout, -t <ms> Execution timeout (default: 30000)\n\nExample: run /src/index.ts\n`,
81
- exitCode: 1,
82
- };
83
- }
84
-
85
- // Capture console output
86
- const logs: string[] = [];
87
- const originalConsole = {
88
- log: console.log,
89
- warn: console.warn,
90
- error: console.error,
91
- info: console.info,
92
- debug: console.debug,
93
- };
94
-
95
- const formatArgs = (...a: unknown[]) =>
96
- a.map((v) => (typeof v === "object" ? JSON.stringify(v) : String(v))).join(" ");
97
-
98
- const captureLog = (...a: unknown[]) => {
99
- logs.push(formatArgs(...a));
100
- originalConsole.log.apply(console, a);
101
- };
102
- const captureWarn = (...a: unknown[]) => {
103
- logs.push(`[warn] ${formatArgs(...a)}`);
104
- originalConsole.warn.apply(console, a);
105
- };
106
- const captureError = (...a: unknown[]) => {
107
- logs.push(`[error] ${formatArgs(...a)}`);
108
- originalConsole.error.apply(console, a);
109
- };
110
- const captureInfo = (...a: unknown[]) => {
111
- logs.push(`[info] ${formatArgs(...a)}`);
112
- originalConsole.info.apply(console, a);
113
- };
114
- const captureDebug = (...a: unknown[]) => {
115
- logs.push(`[debug] ${formatArgs(...a)}`);
116
- originalConsole.debug.apply(console, a);
117
- };
118
-
119
- const restoreConsole = () => {
120
- console.log = originalConsole.log;
121
- console.warn = originalConsole.warn;
122
- console.error = originalConsole.error;
123
- console.info = originalConsole.info;
124
- console.debug = originalConsole.debug;
125
- };
126
-
127
- try {
128
- // Check if entry point exists
129
- if (!(await fs.exists(entryPoint))) {
130
- return {
131
- stdout: "",
132
- stderr: `Error: Entry point not found: ${entryPoint}\n`,
133
- exitCode: 1,
134
- };
135
- }
136
-
137
- // Type check (unless skipped)
138
- if (!skipTypecheck) {
139
- const typecheckResult = await typecheck({
140
- fs,
141
- entryPoint,
142
- tsconfigPath,
143
- libFiles,
144
- });
145
-
146
- if (typecheckResult.hasErrors) {
147
- const formatted = formatDiagnosticsForAgent(typecheckResult.diagnostics);
148
- return {
149
- stdout: "",
150
- stderr: `Type errors:\n${formatted}\n`,
151
- exitCode: 1,
152
- };
153
- }
154
- }
155
-
156
- // Bundle the code
157
- const bundleResult = await bundle({
158
- fs,
159
- entryPoint,
160
- format: "esm",
161
- sharedModules,
162
- });
163
-
164
- // Install console interceptors
165
- console.log = captureLog;
166
- console.warn = captureWarn;
167
- console.error = captureError;
168
- console.info = captureInfo;
169
- console.debug = captureDebug;
170
-
171
- // Create the run context
172
- const context: RunContext = {
173
- fs,
174
- env: { ...runOptions.env },
175
- args: scriptArgs,
176
- log: captureLog,
177
- error: captureError,
178
- };
179
-
180
- // Execute the code with optional timeout
181
- const startTime = performance.now();
182
- let returnValue: unknown;
183
-
184
- const executeCode = async () => {
185
- // Load the module (this executes top-level code)
186
- const module = await loadModule<{ main?: (ctx: RunContext) => unknown }>(bundleResult);
187
-
188
- // If there's a main export, call it with context
189
- if (typeof module.main === "function") {
190
- returnValue = await module.main(context);
191
- }
192
- };
193
-
194
- if (timeout > 0) {
195
- const timeoutPromise = new Promise<never>((_, reject) => {
196
- setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout);
197
- });
198
- await Promise.race([executeCode(), timeoutPromise]);
199
- } else {
200
- await executeCode();
201
- }
202
-
203
- const executionTimeMs = performance.now() - startTime;
204
-
205
- // Restore console before building output
206
- restoreConsole();
207
-
208
- // Build output
209
- let output = "";
210
- if (logs.length > 0) {
211
- output = logs.join("\n") + "\n";
212
- }
213
- if (returnValue !== undefined) {
214
- const returnStr =
215
- typeof returnValue === "object"
216
- ? JSON.stringify(returnValue, null, 2)
217
- : String(returnValue);
218
- output += `[return] ${returnStr}\n`;
219
- }
220
- output += `\nExecution completed in ${executionTimeMs.toFixed(2)}ms\n`;
221
-
222
- return {
223
- stdout: output,
224
- stderr: "",
225
- exitCode: 0,
226
- };
227
- } catch (err) {
228
- restoreConsole();
229
-
230
- const errorMessage = err instanceof Error ? err.message : String(err);
231
- const errorStack = err instanceof Error && err.stack ? `\n${err.stack}` : "";
232
-
233
- let output = "";
234
- if (logs.length > 0) {
235
- output = logs.join("\n") + "\n\n";
236
- }
237
-
238
- return {
239
- stdout: output,
240
- stderr: `Runtime error: ${errorMessage}${errorStack}\n`,
241
- exitCode: 1,
242
- };
243
- }
244
- });
245
- }