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.
- package/dist/browser/bundler.d.ts +68 -0
- package/dist/browser/bundler.d.ts.map +1 -0
- package/dist/browser/executor.d.ts +46 -0
- package/dist/browser/executor.d.ts.map +1 -0
- package/dist/browser/index.d.ts +9 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +2692 -0
- package/dist/browser/preset.d.ts +63 -0
- package/dist/browser/preset.d.ts.map +1 -0
- package/dist/commands/index.d.ts +20 -11
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/types.d.ts +31 -132
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/core/bundler-utils.d.ts +142 -0
- package/dist/core/bundler-utils.d.ts.map +1 -0
- package/dist/core/esm-types-resolver.d.ts +125 -0
- package/dist/core/esm-types-resolver.d.ts.map +1 -0
- package/dist/core/executor.d.ts +35 -0
- package/dist/core/executor.d.ts.map +1 -0
- package/dist/{fs.d.ts → core/fs.d.ts} +27 -29
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/sandbox.d.ts +30 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandlot.d.ts +30 -0
- package/dist/core/sandlot.d.ts.map +1 -0
- package/dist/core/shared-module-registry.d.ts +46 -0
- package/dist/core/shared-module-registry.d.ts.map +1 -0
- package/dist/core/typechecker.d.ts +60 -0
- package/dist/core/typechecker.d.ts.map +1 -0
- package/dist/index.d.ts +11 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1405 -2049
- package/dist/node/bundler.d.ts +48 -0
- package/dist/node/bundler.d.ts.map +1 -0
- package/dist/node/executor.d.ts +48 -0
- package/dist/node/executor.d.ts.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2646 -0
- package/dist/node/preset.d.ts +62 -0
- package/dist/node/preset.d.ts.map +1 -0
- package/dist/types.d.ts +525 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +27 -8
- package/src/browser/bundler.ts +294 -0
- package/src/browser/executor.ts +71 -0
- package/src/browser/index.ts +57 -0
- package/src/browser/preset.ts +179 -0
- package/src/commands/index.ts +526 -43
- package/src/commands/types.ts +82 -146
- package/src/core/bundler-utils.ts +630 -0
- package/src/core/esm-types-resolver.ts +432 -0
- package/src/core/executor.ts +161 -0
- package/src/{fs.ts → core/fs.ts} +59 -37
- package/src/core/sandbox.ts +621 -0
- package/src/core/sandlot.ts +77 -0
- package/src/core/shared-module-registry.ts +138 -0
- package/src/core/typechecker.ts +607 -0
- package/src/index.ts +104 -139
- package/src/node/bundler.ts +194 -0
- package/src/node/executor.ts +87 -0
- package/src/node/index.ts +39 -0
- package/src/node/preset.ts +178 -0
- package/src/types.ts +668 -0
- package/README.md +0 -243
- package/dist/build-emitter.d.ts +0 -47
- package/dist/build-emitter.d.ts.map +0 -1
- package/dist/builder.d.ts +0 -370
- package/dist/builder.d.ts.map +0 -1
- package/dist/bundler.d.ts +0 -148
- package/dist/bundler.d.ts.map +0 -1
- package/dist/commands/compile.d.ts +0 -13
- package/dist/commands/compile.d.ts.map +0 -1
- package/dist/commands/packages.d.ts +0 -17
- package/dist/commands/packages.d.ts.map +0 -1
- package/dist/commands/run.d.ts +0 -40
- package/dist/commands/run.d.ts.map +0 -1
- package/dist/commands.d.ts +0 -179
- package/dist/commands.d.ts.map +0 -1
- package/dist/fs.d.ts.map +0 -1
- package/dist/internal.d.ts +0 -79
- package/dist/internal.d.ts.map +0 -1
- package/dist/internal.js +0 -1976
- package/dist/loader.d.ts +0 -164
- package/dist/loader.d.ts.map +0 -1
- package/dist/packages.d.ts +0 -199
- package/dist/packages.d.ts.map +0 -1
- package/dist/runner.d.ts +0 -314
- package/dist/runner.d.ts.map +0 -1
- package/dist/sandbox-manager.d.ts +0 -261
- package/dist/sandbox-manager.d.ts.map +0 -1
- package/dist/sandbox.d.ts +0 -267
- package/dist/sandbox.d.ts.map +0 -1
- package/dist/shared-modules.d.ts +0 -148
- package/dist/shared-modules.d.ts.map +0 -1
- package/dist/shared-resources.d.ts +0 -102
- package/dist/shared-resources.d.ts.map +0 -1
- package/dist/ts-libs.d.ts +0 -98
- package/dist/ts-libs.d.ts.map +0 -1
- package/dist/typechecker.d.ts +0 -127
- package/dist/typechecker.d.ts.map +0 -1
- package/src/build-emitter.ts +0 -64
- package/src/builder.ts +0 -498
- package/src/bundler.ts +0 -542
- package/src/commands/compile.ts +0 -236
- package/src/commands/packages.ts +0 -154
- package/src/commands/run.ts +0 -245
- package/src/internal.ts +0 -119
- package/src/loader.ts +0 -229
- package/src/packages.ts +0 -936
- package/src/sandbox.ts +0 -396
- package/src/shared-modules.ts +0 -280
- package/src/shared-resources.ts +0 -166
- package/src/ts-libs.ts +0 -320
- package/src/typechecker.ts +0 -635
package/src/commands/compile.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/packages.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/run.ts
DELETED
|
@@ -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
|
-
}
|