Sakura Lang

Subscribe to a WebSocket feed

Open a WebSocket, subscribe to a topic and collect its updates until the server closes.

Generated by cargo run -p agent_docs from compiler/examples/. Edit the example files, then regenerate.

Open a WebSocket, subscribe to a topic and collect its updates until the server closes.

WebSocket.connect opens the connection and runs the callback with the Socket; when the callback returns, fails or is cancelled the connection is closed (1001 if it is still open), and returning the socket from the callback is V6003. There are no event listeners: a message or the close is a value WebSocket.receive returns, and WebSocket.messages(sock) is the same as a source that ends at the close, so filter, map and to_list(max) read it with a stated bound. max_message caps each message at 64 KiB in both directions; a larger one from the server ends the connection with 1009.

The manifest grants feed.example.com:443, the port a wss URL uses by default; a main that declares WebSocket with an empty net list exits 75 at startup. The tests run against WebSocket.scripted: Reply answers the subscription, Push sends updates, Close ends the feed, and a send the script does not expect fails with SocketError(Failed(...)) instead of waiting. No test opens a socket.

Source project · harness mode: test.

From the repository root, with this checkout's sakura on PATH:

cd compiler/examples/websocket_client
sakura check --json
sakura test --json

Manifest and permissions

[package]
name = "websocket_client"
version = "0.1.0"
edition = "2026"

[permissions]
net = ["feed.example.com:443"]

Source and tests

src/main.sak includes all required imports and tests.

import std/stream.{Source, StreamError}
import std/websocket.{Binary, Close, Message, Options, Push, Reply, SocketError, Text, WebSocket}

/// Subscribes to `topic` on a feed server and returns up to `max` text updates, in order, until
/// the server closes the connection. A binary frame is skipped. Every message is at most 64 KiB.
pub fn updates(url: String, topic: String, max: Int) -> List<String>
  ! {WebSocket, Fail<SocketError | StreamError>}
  requires max >= 0
{
  let opts = Options { ..WebSocket.options(), subprotocols: ["feed.v1"], max_message: 65_536 }
  WebSocket.connect(url, opts, fn(sock) {
    WebSocket.send(sock, Text("subscribe {topic}"))
    WebSocket.messages(sock).filter(is_text).map(text_of).to_list(max)
  })
}

fn is_text(m: Message) -> Bool {
  match m {
    Text(_) => true,
    Binary(_) => false,
  }
}

fn text_of(m: Message) -> String {
  match m {
    Text(s) => s,
    Binary(_) => "",
  }
}

test "updates arrive in order until the server closes" {
  let script = [
    Reply(Text("subscribe prices"), Text("ok")),
    Push(Text("BTC 1")),
    Push(Binary(Bytes.from_list([0]))),
    Push(Text("BTC 2")),
    Close(1000, "end of feed"),
  ]
  with WebSocket.scripted(script) {
    expect_eq(
      try { updates("wss://feed.example.com/live", "prices", 10) },
      Ok(["ok", "BTC 1", "BTC 2"]),
    )
  }
}

test "more updates than asked for fail instead of growing" {
  let script = [Reply(Text("subscribe a"), Text("1")), Push(Text("2")), Push(Text("3"))]
  with WebSocket.scripted(script) {
    let got = try { updates("wss://feed.example.com/live", "a", 2) }
    expect(String.contains("{got}", "MaxItems"))
  }
}

test "a server that answers the wrong subscription is a typed failure" {
  with WebSocket.scripted([Reply(Text("subscribe b"), Text("ok"))]) {
    let got = try { updates("wss://feed.example.com/live", "a", 10) }
    expect(String.contains("{got}", "Failed"))
  }
}

Compiler provenance and regeneration

Finish only when no holes remain, independent tests pass, the requested target builds, and contracts/tests/permissions have not been weakened. Repeat the frozen baseline check after the final edits. Report missing prerequisites and failing checks explicitly; do not claim a skipped test passed.

These ten projects teach supported Sakura APIs on the JavaScript backend. Each project is exercised offline by cargo test -p sakura_cli --test examples; HTTP and LLM exchanges are synthetic committed cassettes, with no network or API keys. Use Node 26 for execution, including its built-in SQLite implementation.

The initial API baseline is compiler revision 5cae349516c21714d90d36e87f1cfd6debe980c6. CI reruns these projects against the compiler in the same checkout. Build that checkout's CLI and run sakura --version and sakura prompt before adapting a recipe. Sakura Lang is pre-1.0: read STABILITY.md before depending on an API. An installed skill's frontmatter records its compiler version and build revision. These teaching examples are separate from the agent evaluation tasks.

The source of each page is its example's recipe.md, sakura.toml, src/main.sak and fixture files. Run cargo run -p agent_docs from compiler/ to regenerate the cookbook and skill reference. Edit those sources, not the rendered pages.

Edit on GitHub

Last updated

On this page