Networks drop. Laptops sleep. Phones switch towers. Statewire is built for it.
Every command goes into an outbox before it leaves the client. If the connection drops, the outbox keeps it and the client redials on its own. On reconnect it sends what's still pending, under the same numbers as before. The host remembers what it already admitted, so while it keeps the client's record, a resend never runs the handler twice. While the host still holds the answer, it sends it again; an older one comes back as expired.
Survive a reload
Give the client a place to keep its session, and pending sends outlive the tab. After a reload the client picks up its identity and its outbox, and carries on.
import { StatewireHttp, useStatewire } from "statewire";
import { KeyValueSessionStorage } from "statewire/storage";
const session = KeyValueSessionStorage({
namespace: "my-app:",
principal: userId,
key: "main",
backend: localStorage,
});
const counter = useStatewire({
transport: StatewireHttp({ url: "/counters/main" }),
storage: { session },
});principal is the signed-in account, so two accounts on one browser keep separate sessions. IndexedDB storage is built in too.
What the screen shows
- The last state it received, with your pending predictions on top.
connection.status:connected,retryingwith the next attempt time, orstoppedwith a reason.- Fresh state the moment it reconnects. Changes made by others while you were away arrive in that snapshot.
When a send fails
A command that the host rejects, or that can't be confirmed, leaves the outbox and rejects its promise. On a stored session the failure is kept until your code has seen it, even across a reload. Put it back with one call, held until the user says retry:
const counter = useStatewire({
transport: StatewireHttp({ url: "/counters/main" }),
storage: { session },
onCommandChange: (update) => {
if (update.status !== "settled" || update.failure === undefined) return;
counter.outbox.insert({
from: update.id,
protocol: update.protocol!,
command: { method: update.method, params: update.params },
held: true,
});
},
});If the host has lost the client's record, say after a long time away, pending commands settle as lost and the fresh snapshot shows what landed. A retry is a new command, so the host may run it again. For payments and other side effects, send an operation id and have the host check it first.