@cloudflare/containers
Version:
Helper class for container-enabled Durable Objects
212 lines (211 loc) • 9.07 kB
TypeScript
import type { ContainerOptions, ContainerStartOptions, ContainerStartConfigOptions, Schedule, StopParams, State } from '../types';
import { DurableObject } from 'cloudflare:workers';
export type Signal = 'SIGKILL' | 'SIGINT' | 'SIGTERM';
export type SignalInteger = number;
export declare class Container<Env = unknown> extends DurableObject<Env> {
defaultPort?: number;
requiredPorts?: number[];
sleepAfter: string | number;
envVars: ContainerStartOptions['env'];
entrypoint: ContainerStartOptions['entrypoint'];
enableInternet: ContainerStartOptions['enableInternet'];
constructor(ctx: DurableObject['ctx'], env: Env, options?: ContainerOptions);
/**
* Gets the current state of the container
* @returns Promise<State>
*/
getState(): Promise<State>;
/**
* Start the container if it's not running and set up monitoring
*
* This method handles the core container startup process without waiting for ports to be ready.
* It will automatically retry if the container fails to start, up to maxTries attempts.
*
* It's useful when you need to:
* - Start a container without blocking until a port is available
* - Initialize a container that doesn't expose ports
* - Perform custom port availability checks separately
*
* The method applies the container configuration from your instance properties by default, but allows
* overriding these values for this specific startup:
* - Environment variables (defaults to this.envVars)
* - Custom entrypoint commands (defaults to this.entrypoint)
* - Internet access settings (defaults to this.enableInternet)
*
* It also sets up monitoring to track container lifecycle events and automatically
* calls the onStop handler when the container terminates.
*
* @example
* // Basic usage in a custom Container implementation
* async customInitialize() {
* // Start the container without waiting for a port
* await this.start();
*
* // Perform additional initialization steps
* // that don't require port access
* }
*
* @example
* // Start with custom configuration
* await this.start({
* envVars: { DEBUG: 'true', NODE_ENV: 'development' },
* entrypoint: ['npm', 'run', 'dev'],
* enableInternet: false
* });
*
* @param options - Optional configuration to override instance defaults
* @param waitOptions - Optional wait configuration with abort signal for cancellation
* @returns A promise that resolves when the container start command has been issued
* @throws Error if no container context is available or if all start attempts fail
*/
start(options?: ContainerStartConfigOptions, waitOptions?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Start the container and wait for ports to be available
* Based on containers-starter-go implementation
*
* This method builds on start() by adding port availability verification:
* 1. Calls start() to ensure the container is running
* 2. If no ports are specified and requiredPorts is not set, it uses defaultPort (if set)
* 3. If no ports can be determined, it calls onStart and renewActivityTimeout immediately
* 4. For each specified port, it polls until the port is available or maxTries is reached
* 5. When all ports are available, it triggers onStart and renewActivityTimeout
*
* The method prioritizes port sources in this order:
* 1. Ports specified directly in the method call
* 2. requiredPorts class property (if set)
* 3. defaultPort (if neither of the above is specified)
*
* @param ports - The ports to wait for (if undefined, uses requiredPorts or defaultPort)
* @param maxTries - Maximum number of attempts to connect to each port before failing
* @throws Error if port checks fail after maxTries attempts
*/
startAndWaitForPorts(ports?: number | number[], cancellationOptions?: {
abort?: AbortSignal;
instanceGetTimeoutMS?: number;
portReadyTimeoutMS?: number;
waitInterval?: number;
}): Promise<void>;
/**
* Shuts down the container.
* @param signal - The signal to send to the container (default: 15 for SIGTERM)
*/
stop(signal?: Signal | SignalInteger): Promise<void>;
/**
* Destroys the container. It will trigger onError instead of onStop.
*/
destroy(): Promise<void>;
/**
* Lifecycle method called when container starts successfully
* Override this method in subclasses to handle container start events
*/
onStart(): void | Promise<void>;
/**
* Lifecycle method called when container shuts down
* Override this method in subclasses to handle Container stopped events
* @param params - Object containing exitCode and reason for the stop
*/
onStop(_: StopParams): void | Promise<void>;
/**
* Lifecycle method called when the container is running, and the activity timeout
* expiration has been reached.
*
* If you want to shutdown the container, you should call this.destroy() here
*
* By default, this method calls `this.destroy()`
*/
onActivityExpired(): Promise<void>;
/**
* Error handler for container errors
* Override this method in subclasses to handle container errors
* @param error - The error that occurred
* @returns Can return any value or throw the error
*/
onError(error: unknown): any;
/**
* Renew the container's activity timeout
*
* Call this method whenever there is activity on the container
*/
renewActivityTimeout(): void;
/**
* Schedule a task to be executed in the future
* @template T Type of the payload data
* @param when When to execute the task (Date object or number of seconds delay)
* @param callback Name of the method to call
* @param payload Data to pass to the callback
* @returns Schedule object representing the scheduled task
*/
schedule<T = string>(when: Date | number, callback: string, payload?: T): Promise<Schedule<T>>;
/**
* Send a request to the container (HTTP or WebSocket) using standard fetch API signature
* Based on containers-starter-go implementation
*
* This method handles HTTP requests to the container. WebSocket requests done outside the DO*
* won't work until https://github.com/cloudflare/workerd/issues/2319 is addressed. Until then, please use `switchPort` + `fetch()`.
*
* Method supports multiple signatures to match standard fetch API:
* - containerFetch(request: Request, port?: number)
* - containerFetch(url: string | URL, init?: RequestInit, port?: number)
*
* @param requestOrUrl The request object or URL string/object to send to the container
* @param portOrInit Port number or fetch RequestInit options
* @param portParam Optional port number when using URL+init signature
* @returns A Response from the container, or WebSocket connection
*/
containerFetch(requestOrUrl: Request | string | URL, portOrInit?: number | RequestInit, portParam?: number): Promise<Response>;
/**
* Handle fetch requests to the Container
* Default implementation forwards all HTTP and WebSocket requests to the container
* Override this in your subclass to specify a port or implement custom request handling
*
* @param request The request to handle
*/
fetch(request: Request): Promise<Response>;
private container;
private onStopCalled;
private state;
private monitor;
private monitorSetup;
private sleepAfterMs;
private blockConcurrencyThrowable;
/**
* Execute SQL queries against the Container's database
*/
private sql;
private requestAndPortFromContainerFetchArgs;
private startContainerIfNotRunning;
private setupMonitorCallbacks;
deleteSchedules(name: string): void;
/**
* Method called when an alarm fires
* Executes any scheduled tasks that are due
*/
alarm(alarmProps: {
isRetry: boolean;
retryCount: number;
}): Promise<void>;
timeout?: ReturnType<typeof setTimeout>;
resolve?: () => void;
private syncPendingStoppedEvents;
private callOnStop;
/**
* Schedule the next alarm based on upcoming tasks
*/
scheduleNextAlarm(ms?: number): Promise<void>;
listSchedules<T = string>(name: string): Promise<Schedule<T>[]>;
private toSchedule;
/**
* Get a scheduled task by ID
* @template T Type of the payload data
* @param id ID of the scheduled task
* @returns The Schedule object or undefined if not found
*/
getSchedule<T = string>(id: string): Promise<Schedule<T> | undefined>;
private isActivityExpired;
/**
* Method called by scheduled task to stop the container due to inactivity
*/
private stopDueToInactivity;
}