const bus = createAsyncCommandBus();
const outbox = createOutbox({ actions: ['cart*', 'order*'] });
outbox.install(bus); // outermost (priority 200)
bus.use(idempotent({ actions: ['order*'] }), { priority: 100 });
bus.use(createHttpBridge({ endpoint: '/api/vc', csrf: true }));
await outbox.hydrate(); // restore a previous session's queue
const result = await bus.dispatch('cartAdd', { id: 1 }, { qty: 2 });
if (result.ok && result.value?.queued) {
toast(`Saved offline — ${outbox.pending.value} pending`);
}
// back online: the 'online' event flushes automatically (autoFlush: true)
createOutbox — offline outbox: queue commands durably while offline, replay them in order on reconnect, deduplicate server-side via Idempotency-Key.
While offline (or while queued records exist — later commands must not overtake earlier ones), matching dispatches are intercepted before the transport: the command is recorded, persisted, stamped with an idempotency key, and resolved as
{ ok: true, value: { queued: true, id } }. The'outboxQueued'bus event fires with the record. On reconnect (window'online'event, or a manualflush()), records replay sequentially through the full pipeline withmeta.origin = 'replay'and their original idempotency keys, so the backend can reject any duplicate it already applied.'outboxFlushed'fires with the{ replayed, failed }summary.Failure-safe: the first failed replay (an
ok: falseresult or a throw) stops the flush and keeps that record plus everything behind it queued — order is never reshuffled, and the next flush retries from the same spot.SSR-safe: no window/navigator access at module load; the
'online'listener is only attached when a window exists and is removed bydispose().