## Container Class API

```typescript
import { Container } from '@cloudflare/containers'

export class MyContainer extends Container {
  defaultPort = 8080
  requiredPorts = [8080]
  sleepAfter = '30m'
  enableInternet = true
  pingEndpoint = '/health'
  envVars = {}
  entrypoint = []

  onStart() {
    /* container started */
  }
  onStop() {
    /* container stopping */
  }
  onError(error: Error) {
    /* container error */
  }
  onActivityExpired(): boolean {
    /* timeout, return true to stay alive */
  }
  async alarm() {
    /* scheduled task */
  }
}
```

## Routing

**getByName(id)** - Named instance for session affinity, per-user state
**getRandom()** - Random instance for load balancing stateless services

```typescript
const container = env.MY_CONTAINER.getByName('user-123')
const container = env.MY_CONTAINER.getRandom()
```

## Startup Methods

### start() - Basic start (8s timeout)

```typescript
await container.start()
await container.start({ envVars: { KEY: 'value' } })
```

Returns when **process starts**, NOT when ports ready. Use for fire-and-forget.

### startAndWaitForPorts() - Recommended (20s timeout)

```typescript
await container.startAndWaitForPorts() // Uses requiredPorts
await container.startAndWaitForPorts({ ports: [8080, 9090] })
await container.startAndWaitForPorts({
  ports: [8080],
  startOptions: { envVars: { KEY: 'value' } },
})
```

Returns when **ports listening**. Use before HTTP/TCP requests.

**Port resolution:** explicit ports → requiredPorts → defaultPort → port 33

### waitForPort() - Wait for specific port

```typescript
await container.waitForPort(8080)
await container.waitForPort(8080, { timeout: 30000 })
```

## Communication

### fetch() - HTTP with WebSocket support

```typescript
// ✅ Supports WebSocket upgrades
const response = await container.fetch(request)
const response = await container.fetch('http://container/api', {
  method: 'POST',
  body: JSON.stringify({ data: 'value' }),
})
```

**Use for:** All HTTP, especially WebSocket.

### containerFetch() - HTTP only (no WebSocket)

```typescript
// ❌ No WebSocket support
const response = await container.containerFetch(request)
```

**⚠️ Critical:** Use `fetch()` for WebSocket, not `containerFetch()`.

### TCP Connections

```typescript
const port = this.ctx.container.getTcpPort(8080)
const conn = port.connect()
await conn.opened

if (request.body) await request.body.pipeTo(conn.writable)
return new Response(conn.readable)
```

### switchPort() - Change default port

```typescript
this.switchPort(8081) // Subsequent fetch() uses this port
```

## Lifecycle Hooks

### onStart()

Called when container process starts (ports may not be ready). Runs in `blockConcurrencyWhile` - no concurrent requests.

```typescript
onStart() {
  console.log("Container starting");
}
```

### onStop()

Called when SIGTERM received. 15 minutes until SIGKILL. Use for graceful shutdown.

```typescript
onStop() {
  // Save state, close connections, flush logs
}
```

### onError()

Called when container crashes or fails to start.

```typescript
onError(error: Error) {
  console.error("Container error:", error);
}
```

### onActivityExpired()

Called when `sleepAfter` timeout reached. Return `true` to stay alive, `false` to stop.

```typescript
onActivityExpired(): boolean {
  if (this.hasActiveConnections()) return true;  // Keep alive
  return false;  // OK to stop
}
```

## Scheduling

```typescript
export class ScheduledContainer extends Container {
  async fetch(request: Request) {
    await this.schedule(Date.now() + 60000) // 1 minute
    await this.schedule('2026-01-28T00:00:00Z') // ISO string
    return new Response('Scheduled')
  }

  async alarm() {
    // Called when schedule fires (SQLite-backed, survives restarts)
  }
}
```

**⚠️ Don't override `alarm()` directly when using `schedule()` helper.**

## State Inspection

### External state check

```typescript
const state = await container.getState()
// state.status: "starting" | "running" | "stopping" | "stopped"
```

### Internal state check

```typescript
export class MyContainer extends Container {
  async fetch(request: Request) {
    if (this.ctx.container.running) { ... }
  }
}
```

**⚠️ Use `getState()` for external checks, `ctx.container.running` for internal.**
