@illuma-ai/code-sandbox 1.0.0 → 1.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/components/CodeEditor.d.ts +18 -5
- package/dist/components/FileTree.d.ts +2 -1
- package/dist/components/Preview.d.ts +15 -3
- package/dist/components/Workbench.d.ts +3 -2
- package/dist/hooks/useRuntime.d.ts +35 -2
- package/dist/index.cjs +348 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8566 -7799
- package/dist/index.js.map +1 -1
- package/dist/services/runtime.d.ts +58 -1
- package/dist/types.d.ts +197 -9
- package/package.json +1 -1
- package/src/components/CodeEditor.tsx +141 -11
- package/src/components/FileTree.tsx +66 -4
- package/src/components/Preview.tsx +188 -5
- package/src/components/Workbench.tsx +140 -84
- package/src/hooks/useRuntime.ts +426 -89
- package/src/index.ts +4 -0
- package/src/services/runtime.ts +240 -1
- package/src/styles.css +96 -0
- package/src/templates/fullstack-starter.ts +211 -1
- package/src/types.ts +227 -10
package/src/types.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @illuma-ai/code-sandbox — Type definitions
|
|
3
3
|
*
|
|
4
4
|
* Core types for the browser-native code sandbox.
|
|
5
|
-
* Covers file maps, runtime states,
|
|
5
|
+
* Covers file maps, runtime states, imperative handle, and component props.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
@@ -20,6 +20,78 @@ export interface FileNode {
|
|
|
20
20
|
children?: FileNode[];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Change status for a file relative to the baseline (original) snapshot.
|
|
25
|
+
*
|
|
26
|
+
* - `new`: File exists in current but not in original
|
|
27
|
+
* - `modified`: File exists in both but content differs
|
|
28
|
+
* - `deleted`: File exists in original but not in current
|
|
29
|
+
* - `unchanged`: File content is identical
|
|
30
|
+
*/
|
|
31
|
+
export type FileChangeStatus = "new" | "modified" | "deleted" | "unchanged";
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Errors (structured error types for AI agent consumption)
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Categories of errors that can occur in the sandbox.
|
|
39
|
+
*
|
|
40
|
+
* - `process-stderr`: Output written to stderr by the Node.js process
|
|
41
|
+
* - `process-exit`: Process exited with a non-zero code
|
|
42
|
+
* - `runtime-exception`: Uncaught exception in the Node.js runtime (Nodepod)
|
|
43
|
+
* - `browser-error`: JavaScript error in the preview iframe (window.onerror)
|
|
44
|
+
* - `browser-unhandled-rejection`: Unhandled promise rejection in the iframe
|
|
45
|
+
* - `browser-console-error`: console.error() calls in the iframe
|
|
46
|
+
* - `compilation`: Syntax/import errors detected at module load time
|
|
47
|
+
* - `network`: Failed HTTP requests from the preview (fetch/XHR errors)
|
|
48
|
+
* - `boot`: Error during Nodepod boot, file writing, or dependency install
|
|
49
|
+
*/
|
|
50
|
+
export type SandboxErrorCategory =
|
|
51
|
+
| "process-stderr"
|
|
52
|
+
| "process-exit"
|
|
53
|
+
| "runtime-exception"
|
|
54
|
+
| "browser-error"
|
|
55
|
+
| "browser-unhandled-rejection"
|
|
56
|
+
| "browser-console-error"
|
|
57
|
+
| "compilation"
|
|
58
|
+
| "network"
|
|
59
|
+
| "boot";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A structured error emitted by the sandbox.
|
|
63
|
+
*
|
|
64
|
+
* Designed for AI agent consumption — includes enough context for the
|
|
65
|
+
* agent to construct a targeted fix prompt (file path, line number,
|
|
66
|
+
* surrounding code, stack trace).
|
|
67
|
+
*
|
|
68
|
+
* @see Ranger's auto-fix: client/src/components/Artifacts/Artifacts.tsx
|
|
69
|
+
*/
|
|
70
|
+
export interface SandboxError {
|
|
71
|
+
/** Unique ID for deduplication */
|
|
72
|
+
id: string;
|
|
73
|
+
/** Error category — determines how the agent should interpret it */
|
|
74
|
+
category: SandboxErrorCategory;
|
|
75
|
+
/** Human-readable error message (the "what") */
|
|
76
|
+
message: string;
|
|
77
|
+
/** Full stack trace if available */
|
|
78
|
+
stack?: string;
|
|
79
|
+
/** File path where the error occurred (relative to workdir) */
|
|
80
|
+
filePath?: string;
|
|
81
|
+
/** Line number in the file (1-indexed) */
|
|
82
|
+
line?: number;
|
|
83
|
+
/** Column number in the file (1-indexed) */
|
|
84
|
+
column?: number;
|
|
85
|
+
/** ISO 8601 timestamp */
|
|
86
|
+
timestamp: string;
|
|
87
|
+
/**
|
|
88
|
+
* Surrounding source code lines for context.
|
|
89
|
+
* Format: "lineNum: content" per line (e.g., "42: const x = foo();")
|
|
90
|
+
* Typically ~10 lines centered on the error line.
|
|
91
|
+
*/
|
|
92
|
+
sourceContext?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
23
95
|
// ---------------------------------------------------------------------------
|
|
24
96
|
// Runtime (Nodepod lifecycle)
|
|
25
97
|
// ---------------------------------------------------------------------------
|
|
@@ -64,12 +136,32 @@ export interface RuntimeState {
|
|
|
64
136
|
terminalOutput: string[];
|
|
65
137
|
/** Current files in the virtual FS (may differ from original after edits) */
|
|
66
138
|
files: FileMap;
|
|
139
|
+
/**
|
|
140
|
+
* Baseline file snapshot — the files as they were before the latest
|
|
141
|
+
* update (via updateFiles). Used by the diff viewer to show what changed.
|
|
142
|
+
* Empty on first load (everything is "new"), populated after the first
|
|
143
|
+
* file update.
|
|
144
|
+
*/
|
|
145
|
+
originalFiles: FileMap;
|
|
146
|
+
/**
|
|
147
|
+
* Per-file change status relative to originalFiles.
|
|
148
|
+
*
|
|
149
|
+
* Only includes files that have a non-"unchanged" status.
|
|
150
|
+
* Missing keys should be treated as "unchanged".
|
|
151
|
+
*/
|
|
152
|
+
fileChanges: Record<string, FileChangeStatus>;
|
|
67
153
|
/** Error message if status is 'error' */
|
|
68
154
|
error: string | null;
|
|
155
|
+
/**
|
|
156
|
+
* All structured errors collected during this session.
|
|
157
|
+
* New errors are appended — the array grows monotonically.
|
|
158
|
+
* Use `errors[errors.length - 1]` to get the latest.
|
|
159
|
+
*/
|
|
160
|
+
errors: SandboxError[];
|
|
69
161
|
}
|
|
70
162
|
|
|
71
163
|
// ---------------------------------------------------------------------------
|
|
72
|
-
// Git (GitHub API integration)
|
|
164
|
+
// Git (GitHub API integration — utility, NOT used internally by sandbox)
|
|
73
165
|
// ---------------------------------------------------------------------------
|
|
74
166
|
|
|
75
167
|
export interface GitHubRepo {
|
|
@@ -105,17 +197,118 @@ export interface GitPRResult {
|
|
|
105
197
|
branch: string;
|
|
106
198
|
}
|
|
107
199
|
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Imperative Handle
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Imperative handle exposed by CodeSandbox via React.forwardRef.
|
|
206
|
+
*
|
|
207
|
+
* This is the primary API for host applications (like Ranger) to
|
|
208
|
+
* control the sandbox programmatically. The sandbox is a "dumb renderer"
|
|
209
|
+
* — the host pushes files in and reads state out.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```tsx
|
|
213
|
+
* const ref = useRef<CodeSandboxHandle>(null);
|
|
214
|
+
*
|
|
215
|
+
* // Push files from any source (GitHub, DB, S3, agent output)
|
|
216
|
+
* ref.current?.updateFiles(fileMap);
|
|
217
|
+
*
|
|
218
|
+
* // Push a single file (e.g., agent modified one file)
|
|
219
|
+
* ref.current?.updateFile('server.js', newContent);
|
|
220
|
+
*
|
|
221
|
+
* // Read current state
|
|
222
|
+
* const files = ref.current?.getFiles();
|
|
223
|
+
* const changes = ref.current?.getChangedFiles();
|
|
224
|
+
* const errors = ref.current?.getErrors();
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
export interface CodeSandboxHandle {
|
|
228
|
+
/**
|
|
229
|
+
* Replace the entire file set. Diffs against current files, writes only
|
|
230
|
+
* changed files to Nodepod FS, and restarts the server if anything changed.
|
|
231
|
+
*
|
|
232
|
+
* The previous file set becomes `originalFiles` for diff tracking.
|
|
233
|
+
*
|
|
234
|
+
* @param files - Complete new file set
|
|
235
|
+
* @param options - Optional: restartServer (default true)
|
|
236
|
+
*/
|
|
237
|
+
updateFiles: (
|
|
238
|
+
files: FileMap,
|
|
239
|
+
options?: { restartServer?: boolean },
|
|
240
|
+
) => Promise<void>;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Update a single file. Writes to Nodepod FS and updates state.
|
|
244
|
+
* Does NOT restart the server — call `restart()` manually if needed,
|
|
245
|
+
* or use `updateFiles()` for bulk updates with auto-restart.
|
|
246
|
+
*
|
|
247
|
+
* @param path - File path (e.g., "server.js", "public/index.html")
|
|
248
|
+
* @param content - New file content
|
|
249
|
+
*/
|
|
250
|
+
updateFile: (path: string, content: string) => Promise<void>;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Force restart the server process. Kills the current process
|
|
254
|
+
* and re-runs the entry command.
|
|
255
|
+
*/
|
|
256
|
+
restart: () => Promise<void>;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the current file map (all files including edits).
|
|
260
|
+
*/
|
|
261
|
+
getFiles: () => FileMap;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Get only files that have been modified relative to originalFiles.
|
|
265
|
+
* Returns a FileMap containing only the changed files.
|
|
266
|
+
*/
|
|
267
|
+
getChangedFiles: () => FileMap;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get the per-file change status map (new/modified/deleted).
|
|
271
|
+
* Missing keys should be treated as "unchanged".
|
|
272
|
+
*/
|
|
273
|
+
getFileChanges: () => Record<string, FileChangeStatus>;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get all structured errors collected during this session.
|
|
277
|
+
*/
|
|
278
|
+
getErrors: () => SandboxError[];
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Get the current runtime state snapshot.
|
|
282
|
+
*/
|
|
283
|
+
getState: () => RuntimeState;
|
|
284
|
+
}
|
|
285
|
+
|
|
108
286
|
// ---------------------------------------------------------------------------
|
|
109
287
|
// Component Props
|
|
110
288
|
// ---------------------------------------------------------------------------
|
|
111
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Props for the CodeSandbox component.
|
|
292
|
+
*
|
|
293
|
+
* The sandbox is a pure renderer — it receives files via props and
|
|
294
|
+
* exposes an imperative handle for programmatic updates. It does NOT
|
|
295
|
+
* interact with any storage backend (GitHub, S3, DB) directly.
|
|
296
|
+
*
|
|
297
|
+
* File sources (for initial load, pick ONE):
|
|
298
|
+
* - `files` — pass a FileMap directly
|
|
299
|
+
* - `template` — use a built-in template name (e.g., "fullstack-starter")
|
|
300
|
+
*
|
|
301
|
+
* For live updates after mount, use the imperative handle:
|
|
302
|
+
* ```tsx
|
|
303
|
+
* const ref = useRef<CodeSandboxHandle>(null);
|
|
304
|
+
* <CodeSandbox ref={ref} files={initialFiles} />
|
|
305
|
+
* // Later:
|
|
306
|
+
* ref.current.updateFiles(newFiles);
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
112
309
|
export interface CodeSandboxProps {
|
|
113
|
-
/** Pass files directly */
|
|
310
|
+
/** Pass files directly (from any source — GitHub, DB, S3, agent, etc.) */
|
|
114
311
|
files?: FileMap;
|
|
115
|
-
/** OR load from GitHub */
|
|
116
|
-
github?: GitHubRepo;
|
|
117
|
-
/** GitHub personal access token (for private repos and write-back) */
|
|
118
|
-
gitToken?: string;
|
|
119
312
|
/** OR use a built-in template name */
|
|
120
313
|
template?: string;
|
|
121
314
|
/** Command to start the dev server (default inferred from package.json or template) */
|
|
@@ -126,14 +319,30 @@ export interface CodeSandboxProps {
|
|
|
126
319
|
env?: Record<string, string>;
|
|
127
320
|
|
|
128
321
|
// Callbacks
|
|
129
|
-
/** Fires when a file is modified in the editor */
|
|
322
|
+
/** Fires when a file is modified in the editor by the user */
|
|
130
323
|
onFileChange?: (path: string, content: string) => void;
|
|
131
|
-
/** Fires when the dev server is ready */
|
|
324
|
+
/** Fires when the dev server is ready (initial boot or after restart) */
|
|
132
325
|
onServerReady?: (port: number, url: string) => void;
|
|
133
326
|
/** Fires on boot progress changes */
|
|
134
327
|
onProgress?: (progress: BootProgress) => void;
|
|
135
|
-
/** Fires on errors */
|
|
328
|
+
/** Fires on errors (legacy — simple string message) */
|
|
136
329
|
onError?: (error: string) => void;
|
|
330
|
+
/**
|
|
331
|
+
* Fires when a structured error is detected.
|
|
332
|
+
*
|
|
333
|
+
* This is the primary error channel for AI agent integration.
|
|
334
|
+
* Errors include file path, line number, source context, and category
|
|
335
|
+
* so the agent can construct a targeted fix prompt.
|
|
336
|
+
*
|
|
337
|
+
* Max 2 auto-fix attempts per error is recommended (see Ranger pattern).
|
|
338
|
+
*/
|
|
339
|
+
onSandboxError?: (error: SandboxError) => void;
|
|
340
|
+
/**
|
|
341
|
+
* Fires after updateFiles() completes (files written + server restarted).
|
|
342
|
+
* Useful for the host to know when the sandbox has finished processing
|
|
343
|
+
* a file push.
|
|
344
|
+
*/
|
|
345
|
+
onFilesUpdated?: (fileChanges: Record<string, FileChangeStatus>) => void;
|
|
137
346
|
|
|
138
347
|
// Layout
|
|
139
348
|
/** CSS class name for the root element */
|
|
@@ -146,6 +355,8 @@ export interface FileTreeProps {
|
|
|
146
355
|
files: FileNode[];
|
|
147
356
|
selectedFile: string | null;
|
|
148
357
|
onSelectFile: (path: string) => void;
|
|
358
|
+
/** Per-file change status for visual indicators (colored dots) */
|
|
359
|
+
fileChanges?: Record<string, FileChangeStatus>;
|
|
149
360
|
onCreateFile?: (path: string) => void;
|
|
150
361
|
onCreateFolder?: (path: string) => void;
|
|
151
362
|
onDeleteFile?: (path: string) => void;
|
|
@@ -154,6 +365,10 @@ export interface FileTreeProps {
|
|
|
154
365
|
|
|
155
366
|
export interface CodeEditorProps {
|
|
156
367
|
files: FileMap;
|
|
368
|
+
/** Baseline files for diff comparison (empty = no diff available) */
|
|
369
|
+
originalFiles: FileMap;
|
|
370
|
+
/** Per-file change status for tab indicators */
|
|
371
|
+
fileChanges: Record<string, FileChangeStatus>;
|
|
157
372
|
activeFile: string | null;
|
|
158
373
|
openFiles: string[];
|
|
159
374
|
onSelectFile: (path: string) => void;
|
|
@@ -171,6 +386,8 @@ export interface PreviewProps {
|
|
|
171
386
|
url: string | null;
|
|
172
387
|
className?: string;
|
|
173
388
|
onRefresh?: () => void;
|
|
389
|
+
/** Called when a JavaScript error occurs inside the preview iframe */
|
|
390
|
+
onBrowserError?: (error: SandboxError) => void;
|
|
174
391
|
}
|
|
175
392
|
|
|
176
393
|
export interface BootOverlayProps {
|