1# Chapter 19: jev-worker — the client on a Cloudflare Worker 2 3`jev-worker` fills in `jev-client`'s two ports for a Cloudflare Worker, 4Rust compiled to WebAssembly (`wasm32-unknown-unknown`) through workers-rs: 5 6- `FetchTransport` posts each request with the Worker's `fetch`. 7- `WorkerRuntime` uses the Worker's clock, its `Delay` timers and 8 `Math.random`. 9 10That is the whole crate, about a hundred lines. The retry policy, the 11response verification and the price are the same code every other host 12runs, in `jev-client` and `jev-protocol`. 13 14## Why a Worker needs its own pair 15 16A Worker is a different sort of place from a server process. There is no 17socket to hold open, so `jev-http`'s kept HTTP/2 connection cannot exist 18here: the runtime's `fetch` opens and pools connections where the Worker 19cannot see them. There is no disk either, so `jev-http`'s on-disk spend 20ledger cannot exist here. What a Worker has is `fetch`, timers, and Durable 21Objects. 22 23So this crate is the transport half only. Two consequences follow from 24`fetch` hiding its connections: 25 26- There is no connection to distrust, so `distrust_connection` does nothing. 27- A send never reports `NotSent`. A failure before any response is 28 `Connect`, which the client retries with backoff, as it would any broken 29 connection. 30 31> **Aside.** A Worker's clock is unusual: `Date.now()` and 32> `performance.now()` only move forward across I/O, never during CPU work. 33> That is still right for the client, because every interval it measures 34> spans a `fetch` or a timer. 35 36```mermaid 37sequenceDiagram 38 participant W as Your Worker 39 participant C as jev_client::Client 40 participant F as FetchTransport 41 participant J as api.typesafe.ai 42 W->>C: ask(state, questions) 43 C->>F: send(HttpRequest) 44 F->>J: fetch POST /v1/systemone 45 J-->>F: status, headers, body 46 F-->>C: HttpResponse (body capped at 8 MiB) 47 C-->>W: Answered, or ClientError after retries 48``` 49 50## Try it 51 52``` 53cargo build -p jev-worker --target wasm32-unknown-unknown 54``` 55 56That needs the `wasm32-unknown-unknown` target installed for your Rust 57toolchain. The crate also compiles in the native workspace build, so 58`cargo test --workspace` checks it, but nothing there can call it. 59 60Asking from a Worker: 61 62```rust 63use jev_worker::{FetchTransport, WorkerRuntime}; 64 65let client = jev_client::Client::new(FetchTransport::api(), WorkerRuntime, model, &key)?; 66let answered = client.ask(&state, &questions).await?; 67``` 68 69`FetchTransport::api()` posts to `jev_protocol::ENDPOINT`; 70`FetchTransport::new(url)` posts somewhere else, such as a relay or a test 71server. lmjtfy (`~/lmjtfy`) is the Worker that uses it, with its spend 72guard in a Durable Object. 73 74## For the people who maintain it 75 76### In this folder 77 78| Path | What | 79| --- | --- | 80| [src/](src/) | The code: Chapter 19½. | 81| [Cargo.toml](Cargo.toml) | Dependencies: `bytes`, `futures-util`, `http`, `jev-client`, `jev-protocol`, `js-sys`, `worker` (0.8). | 82| [CLAUDE.md](CLAUDE.md) | Invariants for agents. | 83 84The workspace pins `worker` only to its API (`0.8`). The consumer pins 85`wasm-bindgen` to the exact version of the CLI its build uses. 86 87← Previous: [Chapter 18¾: jev-client/src/client](../jev-client/src/client/) · Up: [jevcrates](../) · Next: [Chapter 19½: inside jev-worker/src](src/) →