API reference > @kontsedal/olas-mutation-queue > mutationQueuePlugin
mutationQueuePlugin() function
Plugin that persists every run of a defineMutation({ meta: { persist: true } }) mutation to a StorageAdapter and replays pending entries at startup.
Lifecycle per run (the plugin's onMutation hook): 1. 'start' → write a QueueEntry to storage, before the first attempt. A run cancelled while that write is pending sends nothing. A serial run waiting behind another reports 'queued' first, and its entry is written then, so a reload does not lose the queue. 2. 'success' → delete the entry. The server accepted; no replay needed. 3. 'error' → delete the entry IF attempts >= maxAttempts or isRetryable says the failure is final, else leave it and let the next page load trigger another attempt. (Within a single page load, in-process retries are the definition's retry policy.) A queued run whose onMutate threw never reached mutate, so its entry goes. 4. 'cancel' → depends on the reason. A latest-wins supersede and reset() are the app dropping the run, so the entry goes: replaying it would send a write the app replaced or withdrew. The owning controller disposing only means the screen is gone, so the entry stays for a replay. A reload emits nothing at all: plugins close before the root disposes, and the entry stays on disk.
Three rules keep one logical operation to one durable entry: - A run that SUCCEEDS also drops the entries left by earlier runs of the same logical operation that settled in error — that run is the manual retry, and replaying what it superseded would write twice. Identity comes from dedupeBy, or from the variables when it isn't configured. With dedupeBy, entries kept after a dispose go the same way. - A dedupeBy entry holds the newest write. Once the run that wrote it has settled, it holds the variables of the newest run collapsed onto it, so a superseded run leaves its successor's variables for a replay. A success, a live run's or a replay's, drops the entry only when no newer run's variables ride on it. - A replay pass SKIPS entries whose run is executing right now, in this tab or in another, so an online event inside the enqueue→settle window can't fire a live request a second time. Another tab's run is seen through a Web Lock, or a localStorage lease without Web Locks.
At startup (plugin setup): - List all keys under keyPrefix, parse each as a QueueEntry. - Group by mutationId; within each group sort by seq. - For each entry, check a definition is registered. If absent (module not imported yet), call onReplayError(err, entry) and leave it in storage. If present, run it through host.mutations.run — the engine's runner, so the definition's retry applies and mutate gets the root's deps — serially per mutation id. An entry that stays on disk (a failure worth a retry, or a run executing it) ends its group's pass, and the group's later entries wait for the next pass.
**Idempotency** is the consumer's responsibility — include anidempotencyKey in your variables and have the server dedupe by it. The queue makes no attempt at exactly-once delivery; it gives at-least- once-until-success.
**Variables MUST be JSON-serializable.** The entry is stored as JSON. ABigInt or a cycle throws at enqueue; the throw is reported via onWarn, and the in-process run continues without a durable entry. JSON drops a function or a symbol silently, and a class instance loses its prototype, so a replay sees plain data.
Signature:
export declare function mutationQueuePlugin(options: MutationQueueOptions): OlasPlugin;Parameters
Parameter | Type | Description |
|---|---|---|
options |
Returns: