worldant-client
v0.5.0
Published
Standalone, browser-safe transport for worldant remote functions — createClient(call/stream).
Readme
worldant-client
The standalone, browser-safe transport for calling a deployed worldant world's workflows over HTTP. No napi binary, no @workflow, no Node Buffer — it runs in the browser and in Node.
Install
npm install worldant-clientUsually you don't install it directly — the per-world package generated by a world (npm install <world-id> --registry <world>/__worldant/v1/npm) depends on it and re-exports typed wrappers.
Use the generated schema package (typical)
import { checkout, chat, defaultConfiguration } from '<world-id>';
await checkout(order); // value -> value, typed
for await (const token of chat(prompt)) { } // value -> stream, typedThe generated package exports worldName, workflows, and defaultConfiguration (a non-secret { worldId, baseUrl, prefix, pollMs } the world derived from the request you installed from). To override the connection (e.g. add a token, point at a proxy):
import { configure } from '<world-id>';
configure({ baseUrl: 'https://proxy.internal', token });Use the client directly
import { createClient } from 'worldant-client';
import type { checkout } from './workflows/checkout'; // type-only, erased
const client = createClient({ baseUrl: 'https://orders.acme.com' });
const result = await client.call<typeof checkout>('checkout', [order]); // typed resultcreateClient(config?) returns { call, stream, configure }:
call(key, args)— POSTs/__worldant/v1/runs, polls, resolves the decoded return value.stream(key, args)— opens the SSE stream, yields decoded items, ends on the terminal event.configure(partial)— merge connection config at runtime.
config: { worldId?, baseUrl?, prefix?, token?, pollMs?, store?, retryBaseMs?, retryMaxMs? }. baseUrl defaults to '' (same-origin), prefix to /__worldant/v1, pollMs to 250, retryBaseMs to 250, retryMaxMs to 5000.
Idempotent, crash-resilient runs
Every call is idempotent by default. call() auto-generates a conformant wrun_<ULID> per call and uses it for the whole in-flight request. So if the connection drops or worldant restarts mid-call, the client keeps retrying/polling the same run and resumes to the same result — never a duplicate. The id is scoped to that one call (in-memory); nothing is persisted.
const receipt = await client.call('placeOrder', [order]); // auto-idempotent, zero-downtime, no setupThe id is a conformant wrun_<ULID>, valid for any world (sqlite / postgres / vercel). Pass your own to control it (e.g. derived from a business key) — same behavior, your id:
import { createClient, generateRunId } from 'worldant-client';
const client = createClient();
const receipt = await client.call('placeOrder', [order], { runId: generateRunId() });To also survive a full page reload (opt-in), give a scopeKey — the client mints + persists the runId, reuses it on retry, and clears it once the run is terminal:
const receipt = await client.call('placeOrder', [order], { scopeKey: `order:${cartId}` });Persistence is a pluggable RunStore (get/set/delete). Default: localStorage in the browser, in-memory in Node/SSR.
import { createClient, memoryStore, localStorageStore } from 'worldant-client';
createClient({ store: localStorageStore() }); // explicit; default already picks this in a browserThe poll loop is resilient: it retries on network failure with backoff (never rejects on a dropped connection), resumes a known run, and treats a 404 (pruned run) as "start fresh" by clearing the stored id. Distinct scopeKeys (or distinct runIds) stay isolated as separate runs; the same one converges. No user/session identity is involved — the id is the unit.
Security
defaultConfiguration is publicly downloadable inside the generated package — it never contains a token. Supply auth at runtime via createClient({ token }) / configure({ token }).
