Skip to content

Commit b5e15ee

Browse files
committed
Add process readiness documentation for sandbox SDK
Syncs documentation for PR #273: process readiness feature Changes: - Add comprehensive process readiness section to background-processes guide - Document new serve() method for starting servers with readiness checks - Document Process.waitFor() method for waiting on conditions - Update startProcess() API with ready and readyTimeout options - Add ReadyCondition, WaitForResult, and ServeOptions type documentation - Document ProcessReadyTimeoutError and ProcessExitedBeforeReadyError - Update examples to use new readiness API instead of manual log streaming - Simplify multi-process startup examples with inline readiness checks Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 1f2b21b commit b5e15ee

File tree

2 files changed

+264
-21
lines changed

2 files changed

+264
-21
lines changed

src/content/docs/sandbox/api/commands.mdx

Lines changed: 168 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,18 @@ for await (const event of parseSSEStream<ExecEvent>(stream)) {
8787
Start a long-running background process.
8888

8989
```ts
90-
const process = await sandbox.startProcess(command: string, options?: ProcessOptions): Promise<ProcessInfo>
90+
const process = await sandbox.startProcess(command: string, options?: ProcessOptions): Promise<Process>
9191
```
9292

9393
**Parameters**:
9494
- `command` - The command to start as a background process
9595
- `options` (optional):
9696
- `cwd` - Working directory
9797
- `env` - Environment variables
98+
- `ready` - Condition to wait for before returning (string, RegExp, or port number)
99+
- `readyTimeout` - Timeout for readiness check in milliseconds (default: 30000)
98100

99-
**Returns**: `Promise<ProcessInfo>` with `id`, `pid`, `command`, `status`
101+
**Returns**: `Promise<Process>` with `id`, `pid`, `command`, `status`, and `waitFor()` method
100102

101103
<TypeScriptExample>
102104
```
@@ -108,6 +110,104 @@ const app = await sandbox.startProcess('node app.js', {
108110
cwd: '/workspace/my-app',
109111
env: { NODE_ENV: 'production', PORT: '3000' }
110112
});
113+
114+
// Wait for process to be ready
115+
const server = await sandbox.startProcess('npm start', {
116+
ready: 'Server listening on port',
117+
readyTimeout: 30000
118+
});
119+
120+
// Server is guaranteed ready when startProcess returns
121+
console.log('Server is ready!');
122+
```
123+
</TypeScriptExample>
124+
125+
### `serve()`
126+
127+
Start a server process, wait for it to be ready, and optionally expose it with a preview URL.
128+
129+
```ts
130+
const result = await sandbox.serve(command: string, portOrOptions: number | ServeOptions): Promise<string | { url: string; process: Process }>
131+
```
132+
133+
**Parameters**:
134+
- `command` - The command to start the server
135+
- `portOrOptions`:
136+
- If `number` - Port number to wait for
137+
- If `ServeOptions`:
138+
- `port` - Port number to wait for and expose
139+
- `hostname` (optional) - Hostname for preview URL generation
140+
- `ready` (optional) - Additional log pattern to wait for
141+
- `timeout` (optional) - Timeout in milliseconds (default: 60000)
142+
- `env` (optional) - Environment variables
143+
- `cwd` (optional) - Working directory
144+
145+
**Returns**:
146+
- If `hostname` provided: `Promise<{ url: string; process: Process }>` with preview URL and process
147+
- Otherwise: `Promise<string>` with local URL
148+
149+
<TypeScriptExample>
150+
```
151+
// Simple usage - wait for port only
152+
const url = await sandbox.serve('npm start', 3000);
153+
console.log('Server ready at', url); // http://localhost:3000
154+
155+
// With hostname - get preview URL
156+
const { url, process } = await sandbox.serve('npm start', {
157+
port: 3000,
158+
hostname: 'app.example.com'
159+
});
160+
console.log('Public URL:', url);
161+
162+
// Wait for both log pattern and port
163+
const { url, process } = await sandbox.serve('npm start', {
164+
port: 3000,
165+
hostname: 'app.example.com',
166+
ready: /Server ready/,
167+
timeout: 60000
168+
});
169+
```
170+
</TypeScriptExample>
171+
172+
### `Process.waitFor()`
173+
174+
Wait for a process to meet a readiness condition. This method is available on `Process` objects returned by `startProcess()`.
175+
176+
```ts
177+
const result = await process.waitFor(condition: ReadyCondition, timeout?: number): Promise<WaitForResult>
178+
```
179+
180+
**Parameters**:
181+
- `condition` - The condition to wait for:
182+
- `string` - Wait for this substring in stdout or stderr
183+
- `RegExp` - Wait for this pattern in logs (returns capture groups)
184+
- `number` - Wait for this port to accept connections
185+
- `timeout` (optional) - Timeout in milliseconds (default: 30000)
186+
187+
**Returns**: `Promise<WaitForResult>` with optional `line` (matched log line) and `match` (RegExp capture groups)
188+
189+
**Errors**:
190+
- `ProcessReadyTimeoutError` - Process did not become ready within timeout
191+
- `ProcessExitedBeforeReadyError` - Process exited before becoming ready
192+
193+
<TypeScriptExample>
194+
```
195+
const server = await sandbox.startProcess('npm start');
196+
197+
// Wait for log pattern
198+
await server.waitFor('Server listening');
199+
200+
// Wait for regex with capture groups
201+
const result = await server.waitFor(/port (\d+)/);
202+
console.log('Port:', result.match?.[1]);
203+
204+
// Wait for port to be available
205+
await server.waitFor(3000);
206+
207+
// Sequential waits for multiple conditions
208+
await server.waitFor('Database connected');
209+
await server.waitFor(3000);
210+
console.log('Fully ready!');
111211
```
112212
</TypeScriptExample>
113213

@@ -213,6 +313,72 @@ console.log('Server logs:', logs);
213313
```
214314
</TypeScriptExample>
215315

316+
## Types
317+
318+
### `ReadyCondition`
319+
320+
Type for process readiness conditions.
321+
322+
```ts
323+
type ReadyCondition = string | RegExp | number;
324+
```
325+
326+
- `string` - Wait for substring in stdout or stderr
327+
- `RegExp` - Wait for pattern match in logs
328+
- `number` - Wait for port to accept connections
329+
330+
### `WaitForResult`
331+
332+
Result returned by `waitFor()` method.
333+
334+
```ts
335+
interface WaitForResult {
336+
line?: string; // The log line that matched (for string/regex conditions)
337+
match?: RegExpMatchArray; // Regex capture groups (if condition was RegExp)
338+
}
339+
```
340+
341+
### `ServeOptions`
342+
343+
Options for the `serve()` method.
344+
345+
```ts
346+
interface ServeOptions {
347+
port: number; // Port to wait for and expose
348+
hostname?: string; // Hostname for preview URL (required for URL generation)
349+
ready?: string | RegExp; // Additional ready condition (log pattern)
350+
timeout?: number; // Timeout in milliseconds (default: 60000)
351+
env?: Record<string, string>; // Environment variables
352+
cwd?: string; // Working directory
353+
}
354+
```
355+
356+
## Errors
357+
358+
### `ProcessReadyTimeoutError`
359+
360+
Thrown when a process does not become ready within the timeout period.
361+
362+
**Properties**:
363+
- `processId` - The process ID
364+
- `command` - The command that was executed
365+
- `condition` - The condition that was not met
366+
- `timeout` - The timeout value in milliseconds
367+
- `stdout` - Last 2000 characters of stdout
368+
- `stderr` - Last 2000 characters of stderr
369+
370+
### `ProcessExitedBeforeReadyError`
371+
372+
Thrown when a process exits before becoming ready.
373+
374+
**Properties**:
375+
- `processId` - The process ID
376+
- `command` - The command that was executed
377+
- `condition` - The condition that was not met
378+
- `exitCode` - The process exit code
379+
- `stdout` - Last 2000 characters of stdout
380+
- `stderr` - Last 2000 characters of stderr
381+
216382
## Related resources
217383

218384
- [Background processes guide](/sandbox/guides/background-processes/) - Managing long-running processes

src/content/docs/sandbox/guides/background-processes.mdx

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,80 @@ const isRunning = processes.some(p => p.id === processId && p.status === 'runnin
8383
```
8484
</TypeScriptExample>
8585

86+
## Wait for process readiness
87+
88+
Use `waitFor()` to block until a process is ready before proceeding. This is more reliable than manual log streaming:
89+
90+
<TypeScriptExample>
91+
```
92+
// Start a server and wait for log pattern
93+
const server = await sandbox.startProcess('npm start');
94+
await server.waitFor('Server listening on port');
95+
console.log('Server is ready!');
96+
97+
// Wait for regex pattern (with capture groups)
98+
const result = await server.waitFor(/listening on port (\d+)/);
99+
console.log(`Server ready on port ${result.match?.[1]}`);
100+
101+
// Wait for port to accept connections
102+
await server.waitFor(3000);
103+
console.log('Port 3000 is accepting connections');
104+
```
105+
</TypeScriptExample>
106+
107+
Or use the `ready` option to wait inline:
108+
109+
<TypeScriptExample>
110+
```
111+
// Blocks until pattern appears in logs
112+
const server = await sandbox.startProcess('npm start', {
113+
ready: 'Server listening on port',
114+
readyTimeout: 30000 // 30 seconds (default)
115+
});
116+
117+
// Server is guaranteed ready when startProcess returns
118+
console.log('Server is ready!');
119+
```
120+
</TypeScriptExample>
121+
122+
### Ready conditions
123+
124+
The `waitFor()` method and `ready` option support three types of conditions:
125+
126+
- **String** - Waits for substring in stdout or stderr (example: `"Server ready"`)
127+
- **RegExp** - Waits for pattern match with capture groups (example: `/port (\d+)/`)
128+
- **Number** - Waits for port to accept connections (example: `3000`)
129+
130+
### Error handling
131+
132+
Process readiness can fail in two ways:
133+
134+
<TypeScriptExample>
135+
```
136+
import { ProcessReadyTimeoutError, ProcessExitedBeforeReadyError } from '@cloudflare/sandbox';
137+
138+
try {
139+
const server = await sandbox.startProcess('npm start', {
140+
ready: 'Server listening',
141+
readyTimeout: 10000
142+
});
143+
} catch (error) {
144+
if (error instanceof ProcessReadyTimeoutError) {
145+
// Process is still running but pattern not found
146+
console.error('Timeout waiting for:', error.condition);
147+
console.error('Last output:', error.stdout);
148+
} else if (error instanceof ProcessExitedBeforeReadyError) {
149+
// Process crashed before becoming ready
150+
console.error('Process exited with code:', error.exitCode);
151+
console.error('Error output:', error.stderr);
152+
}
153+
}
154+
```
155+
</TypeScriptExample>
156+
86157
## Monitor process logs
87158

88-
Stream logs in real-time to detect when a service is ready:
159+
For advanced monitoring, stream logs in real-time:
89160

90161
<TypeScriptExample>
91162
```
@@ -98,11 +169,6 @@ const logStream = await sandbox.streamProcessLogs(server.id);
98169
99170
for await (const log of parseSSEStream<LogEvent>(logStream)) {
100171
console.log(log.data);
101-
102-
if (log.data.includes('Server listening')) {
103-
console.log('Server is ready!');
104-
break;
105-
}
106172
}
107173
```
108174
</TypeScriptExample>
@@ -137,28 +203,39 @@ Start services in sequence, waiting for dependencies:
137203

138204
<TypeScriptExample>
139205
```
140-
import { parseSSEStream, type LogEvent } from '@cloudflare/sandbox';
141-
142206
// Start database first
143-
const db = await sandbox.startProcess('redis-server');
144-
145-
// Wait for database to be ready
146-
const dbLogs = await sandbox.streamProcessLogs(db.id);
147-
for await (const log of parseSSEStream<LogEvent>(dbLogs)) {
148-
if (log.data.includes('Ready to accept connections')) {
149-
break;
150-
}
151-
}
207+
const db = await sandbox.startProcess('redis-server', {
208+
ready: 'Ready to accept connections'
209+
});
152210
153211
// Now start API server (depends on database)
154212
const api = await sandbox.startProcess('node api-server.js', {
213+
ready: 'API server listening',
155214
env: { DATABASE_URL: 'redis://localhost:6379' }
156215
});
157216
158217
console.log('All services running');
159218
```
160219
</TypeScriptExample>
161220

221+
Or wait for port availability:
222+
223+
<TypeScriptExample>
224+
```
225+
// Start database and wait for port
226+
const db = await sandbox.startProcess('redis-server', {
227+
ready: 6379
228+
});
229+
230+
// Start API and wait for both log pattern and port
231+
const api = await sandbox.startProcess('npm start');
232+
await api.waitFor('Database connected');
233+
await api.waitFor(3000);
234+
235+
console.log('API is fully ready');
236+
```
237+
</TypeScriptExample>
238+
162239
## Keep containers alive for long-running processes
163240

164241
By default, containers automatically shut down after 10 minutes of inactivity. For long-running processes that may have idle periods (like CI/CD pipelines, batch jobs, or monitoring tasks), use the [`keepAlive` option](/sandbox/configuration/sandbox-options/#keepalive):
@@ -207,9 +284,9 @@ When using `keepAlive: true`, containers will not automatically timeout. You **m
207284

208285
## Best practices
209286

210-
- **Wait for readiness** - Stream logs to detect when services are ready
287+
- **Wait for readiness** - Use `waitFor()` or the `ready` option to ensure processes are fully started
211288
- **Clean up** - Always stop processes when done
212-
- **Handle failures** - Monitor logs for errors and restart if needed
289+
- **Handle failures** - Catch readiness errors and check logs for diagnostics
213290
- **Use try/finally** - Ensure cleanup happens even on errors
214291
- **Use `keepAlive` for long-running tasks** - Prevent container shutdown during processes with idle periods
215292

0 commit comments

Comments
 (0)