jevcrates.git / jev-worker

Chapter 19: jev-worker — the client on a Cloudflare Worker

jev-worker fills in jev-client's two ports for a Cloudflare Worker, Rust compiled to WebAssembly (wasm32-unknown-unknown) through workers-rs:

  • FetchTransport posts each request with the Worker's fetch.
  • WorkerRuntime uses the Worker's clock, its Delay timers and Math.random.

That is the whole crate, about a hundred lines. The retry policy, the response verification and the price are the same code every other host runs, in jev-client and jev-protocol.

Why a Worker needs its own pair

A Worker is a different sort of place from a server process. There is no socket to hold open, so jev-http's kept HTTP/2 connection cannot exist here: the runtime's fetch opens and pools connections where the Worker cannot see them. There is no disk either, so jev-http's on-disk spend ledger cannot exist here. What a Worker has is fetch, timers, and Durable Objects.

So this crate is the transport half only. Two consequences follow from fetch hiding its connections:

  • There is no connection to distrust, so distrust_connection does nothing.
  • A send never reports NotSent. A failure before any response is Connect, which the client retries with backoff, as it would any broken connection.

Aside. A Worker's clock is unusual: Date.now() and performance.now() only move forward across I/O, never during CPU work. That is still right for the client, because every interval it measures spans a fetch or a timer.

sequenceDiagram
  participant W as Your Worker
  participant C as jev_client::Client
  participant F as FetchTransport
  participant J as api.typesafe.ai
  W->>C: ask(state, questions)
  C->>F: send(HttpRequest)
  F->>J: fetch POST /v1/systemone
  J-->>F: status, headers, body
  F-->>C: HttpResponse (body capped at 8 MiB)
  C-->>W: Answered, or ClientError after retries

Try it

cargo build -p jev-worker --target wasm32-unknown-unknown

That needs the wasm32-unknown-unknown target installed for your Rust toolchain. The crate also compiles in the native workspace build, so cargo test --workspace checks it, but nothing there can call it.

Asking from a Worker:

use jev_worker::{FetchTransport, WorkerRuntime};

let client = jev_client::Client::new(FetchTransport::api(), WorkerRuntime, model, &key)?;
let answered = client.ask(&state, &questions).await?;

FetchTransport::api() posts to jev_protocol::ENDPOINT; FetchTransport::new(url) posts somewhere else, such as a relay or a test server. lmjtfy (~/lmjtfy) is the Worker that uses it, with its spend guard in a Durable Object.

For the people who maintain it

In this folder

PathWhat
src/The code: Chapter 19½.
Cargo.tomlDependencies: bytes, futures-util, http, jev-client, jev-protocol, js-sys, worker (0.8).
CLAUDE.mdInvariants for agents.

The workspace pins worker only to its API (0.8). The consumer pins wasm-bindgen to the exact version of the CLI its build uses.

← Previous: Chapter 18¾: jev-client/src/client · Up: jevcrates · Next: Chapter 19½: inside jev-worker/src →