Skip to content
53 changes: 52 additions & 1 deletion packages/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,61 @@ export default {
return Response.json({ content: file.content });
}

return new Response('Try /run or /file');
// Start a server and wait for it to be ready
if (url.pathname === '/server') {
await sandbox.writeFile(
'/workspace/server.js',
`
const server = Bun.serve({
port: 8080,
fetch() { return new Response("Hello from sandbox!"); }
});
console.log("Server ready on port 8080");
`
);

const proc = await sandbox.startProcess('bun run /workspace/server.js');
await proc.waitForPort(8080);
const { url: previewUrl } = await sandbox.exposePort(8080, {
hostname: url.hostname
});

return Response.json({ previewUrl, processId: proc.id });
}

return new Response('Try /run, /file, or /server');
}
};
```

## Process Readiness

Wait for processes to be ready before proceeding:

```typescript
const proc = await sandbox.startProcess('npm start');

// Wait for a log message
await proc.waitForLog('Database connected');

// Wait for a regex pattern (returns match info)
const result = await proc.waitForLog(/listening on port (\d+)/);
console.log(result.match[1]); // Access captured groups

// Wait for a port to be available
await proc.waitForPort(3000);

// Chain multiple conditions
await proc.waitForLog('DB ready');
await setupConnectionPool();
await proc.waitForPort(3000);
```

Methods:

- **`waitForLog(pattern, timeout?)`** - Waits for string or RegExp in stdout/stderr
- **`waitForPort(port, timeout?)`** - Waits for port to accept connections

## Documentation

**📖 [Full Documentation](https://developers.cloudflare.com/sandbox/)**
Expand All @@ -113,6 +163,7 @@ export default {
- **Code Interpreter** - Execute Python and JavaScript with rich outputs
- **File System Access** - Read, write, and manage files
- **Command Execution** - Run any command with streaming support
- **Process Readiness** - Wait for processes to be ready before proceeding
- **Preview URLs** - Expose services with public URLs
- **Git Integration** - Clone repositories directly

Expand Down
66 changes: 66 additions & 0 deletions packages/sandbox/src/errors/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import type {
PortErrorContext,
PortNotExposedContext,
ProcessErrorContext,
ProcessExitedBeforeReadyContext,
ProcessNotFoundContext,
ProcessReadyTimeoutContext,
ValidationFailedContext
} from '@repo/shared/errors';

Expand Down Expand Up @@ -592,3 +594,67 @@ export class ValidationFailedError extends SandboxError<ValidationFailedContext>
return this.context.validationErrors;
}
}

// ============================================================================
// Process Readiness Errors
// ============================================================================

/**
* Error thrown when a process does not become ready within the timeout period
*/
export class ProcessReadyTimeoutError extends SandboxError<ProcessReadyTimeoutContext> {
constructor(errorResponse: ErrorResponse<ProcessReadyTimeoutContext>) {
super(errorResponse);
this.name = 'ProcessReadyTimeoutError';
}

// Type-safe accessors
get processId() {
return this.context.processId;
}
get command() {
return this.context.command;
}
get condition() {
return this.context.condition;
}
get timeout() {
return this.context.timeout;
}
get stdout() {
return this.context.stdout;
}
get stderr() {
return this.context.stderr;
}
}

/**
* Error thrown when a process exits before becoming ready
*/
export class ProcessExitedBeforeReadyError extends SandboxError<ProcessExitedBeforeReadyContext> {
constructor(errorResponse: ErrorResponse<ProcessExitedBeforeReadyContext>) {
super(errorResponse);
this.name = 'ProcessExitedBeforeReadyError';
}

// Type-safe accessors
get processId() {
return this.context.processId;
}
get command() {
return this.context.command;
}
get condition() {
return this.context.condition;
}
get exitCode() {
return this.context.exitCode;
}
get stdout() {
return this.context.stdout;
}
get stderr() {
return this.context.stderr;
}
}
5 changes: 5 additions & 0 deletions packages/sandbox/src/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ export type {
PortErrorContext,
PortNotExposedContext,
ProcessErrorContext,
ProcessExitedBeforeReadyContext,
ProcessNotFoundContext,
ProcessReadyTimeoutContext,
ValidationFailedContext
} from '@repo/shared/errors';
// Re-export shared types and constants
Expand Down Expand Up @@ -100,8 +102,11 @@ export {
PortInUseError,
PortNotExposedError,
ProcessError,
// Process Readiness Errors
ProcessExitedBeforeReadyError,
// Process Errors
ProcessNotFoundError,
ProcessReadyTimeoutError,
SandboxError,
ServiceNotRespondingError,
// Validation Errors
Expand Down
13 changes: 12 additions & 1 deletion packages/sandbox/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export type {
RunCodeOptions,
SandboxOptions,
SessionOptions,
StreamOptions
StreamOptions,
// Process readiness types
WaitForLogResult
} from '@repo/shared';
// Export type guards for runtime validation
export { isExecResult, isProcess, isProcessStatus } from '@repo/shared';
Expand Down Expand Up @@ -96,6 +98,15 @@ export type {
ExecutionCallbacks,
InterpreterClient
} from './clients/interpreter-client.js';
export type {
ProcessExitedBeforeReadyContext,
ProcessReadyTimeoutContext
} from './errors';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do end users need these?

// Export process readiness errors
export {
ProcessExitedBeforeReadyError,
ProcessReadyTimeoutError
} from './errors';
// Export file streaming utilities for binary file support
export { collectFile, streamFile } from './file-stream';
// Export interpreter functionality
Expand Down
Loading
Loading