Ir al contenido
Español

SSE (Server-Sent Events)

Esta página aún no está disponible en tu idioma.

SseActor connects to and consumes a Server-Sent Events endpoint. It is a read-only client, not a server: it opens a long-lived HTTP GET, parses the text/event-stream wire format, and forwards each parsed event to a target actor. There are no commands and no outbound path — SSE is unidirectional, from server to client.

import { ActorSystem, Actor } from 'actor-ts';
import { SseActor, type SseEvent } from 'actor-ts';
import { SseOptions } from 'actor-ts';
// A collector receives each parsed event.
class Collector extends Actor<SseEvent> {
override onReceive(ev: SseEvent): void {
// ev.event — the `event:` field ('message' by default)
// ev.data — the `data:` payload (multiline joined with '\n')
// ev.id — the `id:` field, when the server sent one
console.log(ev.event, ev.data, ev.id);
}
}
const target = sys.spawnAnonymous(Collector);
const sseOptions = SseOptions.create()
.withUrl('https://example.com/events')
.withTarget(target)
.withHeaders({ authorization: 'Bearer …' });
sys.spawnAnonymous(() => new SseActor(
sseOptions,
));

The actor uses the global fetch (Bun, Node ≥ 18, and Deno all provide one), so there is no extra dependency to install.

interface SseOptionsType extends BrokerCommonOptionsType {
url?: string; // required — the SSE endpoint
headers?: Readonly<Record<string, string>>; // optional custom request headers
target?: ActorRef<SseEvent>; // required — subscriber for inbound events
}
SettingRequiredDescription
urlyesThe SSE endpoint the actor connects to (GET, Accept: text/event-stream).
targetyesThe actor that receives each parsed SseEvent.
headersnoExtra request headers — e.g. authorization for a protected feed.

requiredOptions is ['url', 'target']; both must be present or the actor fails fast at startup.

Each parsed event is delivered as an SseEvent:

type SseEvent = {
event: string; // the `event:` field, or 'message' by default
data: string; // the `data:` payload
id?: string; // the `id:` field, when present
};

The client parses the SSE wire format — events separated by a blank line (\n\n), fields as field: value:

data: hello
event: tick
data: {"n":1}
id: 100
data: line-1
data: line-2

Parsing rules that follow the SSE spec:

  • Default event name. A block without an event: field arrives with event: 'message'.
  • Multiline data. Multiple data: lines in one block are joined with '\n' — the last block above yields data: 'line-1\nline-2'.
  • Comments ignored. Lines beginning with : (keepalive comments) are skipped.
  • id. Delivered as ev.id when the server sends one; otherwise undefined.

So the stream above delivers three events: { event: 'message', data: 'hello' }, one tick event with id '100', and the multiline { event: 'message', data: 'line-1\nline-2' }.

SseActor extends BrokerActor, so reconnect-with-backoff and the circuit breaker are inherited — configure them on the builder:

SseOptions.create()
.withUrl('https://example.com/events')
.withTarget(target)
.withReconnect({ /* base delay, max delay, jitter … */ })
.withCircuitBreaker(5, 30_000); // failureThreshold, resetMs

When the stream ends or the connection drops, the base class’ reconnect machinery kicks in. Pass .withReconnect(false) to stop after the stream ends instead of retrying — handy for finite feeds or tests:

SseOptions.create()
.withUrl(url)
.withTarget(target)
.withReconnect(false); // stop when the stream closes

Settings resolve with the usual precedence — explicit options > HOCON > built-in defaults. The HOCON path is actor-ts.io.broker.sse:

actor-ts.io.broker.sse {
url = "https://example.com/events"
headers { authorization = "Bearer …" }
}

target is an actor reference and so can only be set in code, not HOCON.

SseActor fits consuming an external SSE feed — anywhere a server pushes a one-way stream over plain HTTP and you want each event routed into the actor system:

  1. LLM token streams — consume a streaming completion endpoint and forward tokens to a collector.
  2. Market data / price ticks — a live feed pushed as events.
  3. CI / build logs — a long-running log stream tailed as it is produced.

Because it is just HTTP, it works through proxies, load balancers, and CDNs that don’t understand WebSocket.