Reference · Requests and pages

events

signature
events(fn, map | null=) -> response

What it does

A response of Server-Sent Events, sent as the function makes them. The function gets send; each value given to it goes out as JSON in an event's data, with an event name when send is given a second argument, and send(null) is a comment, which keeps a quiet connection open. The second argument may be a map, send(value, { event, id, retry }): the event's name, an id, which a browser sends back as Last-Event-ID when it connects again (listen does), and retry, how many milliseconds it waits before it does. send(null, { id }) sends the id alone. The content-type is text/event-stream with cache-control: no-cache. The options are status (200 unless given, 200 to 599, but not 204, 205 or 304, which have no body) and headers, taken as respond takes them. A page follows the events with listen or receive. See Streaming.

Examples

in a program
route GET /jobs/:id/progress = events((send) => {    for step in range(10) {        work(id, step)        send({ done: step + 1, of: 10 }, "progress")   // a value, and an event name    }})// Goes on from where a dropped connection left off.route GET /feed = events((send) => {    let since = int(request.headers["last-event-id"] ?? "0") ?? 0    for item in items_after(since) {        send(item, { event: "item", id: str(item.id), retry: 2000 })    }})component Progress(job: int) {    state done = 0    listen(`/jobs/${job}/progress`, (p) => { done = p.done })    <progress value={done} max="10" />}

The value can be made anywhere; its status and headers say what the head will be, and its body is null, since the function makes the events while they are sent:

let e = events((send) => send({ n: 1 }, "tick"))print(e)print(e.status, e.headers, e.body)
output
<response 200>200 { "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "no" } null

Edge cases

print(events((send) => send("x"), { type: "text/csv", status: 202 }).headers)print(events((send) => send("x"), { headers: { "x-accel-buffering": "yes" } }).headers)
output
{ "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "no" }{ "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "yes" }

Errors

A first argument that is not a function, options that are not a map, a type that is not text, and a status outside 200 to 599 or with no body are runtime errors, not error values.

this one fails
print(events({ done: 1 }))
output
line 1: `events` takes a function that sends, as in events((send) => send("...")), got map
this one fails
print(events((send) => send("x"), { status: "200" }))
output
line 1: `events`: status must be an int from 200 to 599, got "200"

Chaining

send is a function, so it takes the end of a pipeline, and http.stream can be handed it to pass another server's events straight on:

in a program
route GET /ticker = events((send) => { for q in quotes() { q | send } })route POST /ask = events((send) => http.stream("https://api.example.com/v1/chat", send, {    body: { prompt: request.json.prompt, stream: true },    headers: { authorization: `Bearer ${env("API_KEY")}` },}))

See also

stream, listen, receive, http.stream, live

Thread
8Useful? Like it, or add to its thread below.

Thread 0 posts

On events(): questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.

    Edit this page on GitHub · every example's output was made by running it (tools/refcheck.mjs) · questions on Discord or r/quillo