NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
Pure Dart command queue primitives for retryable API work, terminal failures, and offline-first orchestration.
Last release 20 days ago
17 Sep 2026
Release timing varies
gaps range from 2 weeks to 3 months
Nearly every release is documented
notes for 5 of 5 stable releases
Nothing withdrawn
no release was ever pulled
5 months old
5 releases · first in 2026
One column per month.
A queue with nothing pending no longer announces a flush it never ran.
A queue with nothing pending no longer announces a flush it never ran.
flush() set its status to in progress, logged a start and an end line, and
emitted idle again, all without sending anything. An orchestrator walking a
dozen queues on every connectivity change, app resume and periodic sync
turned that into a page of logs and a state emission per queue - and since
the orchestrator re-counts every queue it owns on each emission, an empty
walk cost more than the work it was checking for.
Dead-lettered commands do not count towards this: a flush only ever draws from pending.
Added ApiCommandOrchestrator.hasQueuedWork, for a consumer deciding whether
a flush is worth triggering or worth reporting to the user. Broader than
nextDueAt, which only answers for work that can be sent right now.
The orchestrator logs its active-command counts only when they change, and
prefixes the line [Orchestrator] like its neighbours. The flush walk now
names the queues in each batch, since a queue with nothing to do no longer
logs a flush of its own to say it was reached.
Added ApiCommandOrchestrator.autoFlushWhenDue, off by default.
Added ApiCommandOrchestrator.autoFlushWhenDue, off by default.
0.3.0 stopped flush() sleeping a failed command's backoff, which means a
retry now waits for something to trigger the next flush. A consumer driving
flushes from connectivity changes and user activity will not retry at all on
a device sitting idle - the ladder simply stops.
With this on, the orchestrator keeps a single timer set to the earliest
nextDueAt across its queues and flushes when it fires, so retries progress
on their own again. The timer is cancelled while processing is disabled and
on close, and has a one second floor so a due command a flush cannot clear
becomes a slow poll rather than a spin.
Consumers that already drive flushing on their own schedule do not need it.
nextDueAt no longer counts a command that is currently being sent. It is
not waiting for a flush, and reporting it as due made a caller scheduling
against it fire repeatedly for as long as the request took.
flushAll joins a flush already in progress rather than starting a second
walk beside it, and asks the running one to make another pass before it
finishes so work queued behind the point it had reached is still sent.
Previously two callers - a startup flush and a sync, say - walked every
queue independently.
A joined flush only makes a second pass if something is actually due behind the one that just finished, rather than re-walking every queue to find out.
A lull between commands no longer reports the queues as idle while a flush is still walking them.
The aggregate flush status is only emitted when it actually changes. Queues emit on every command they touch and most leave it where it was, so every listener was being woken dozens of times per flush for no change.
In-flight and active-queue counts are taken over distinct queues, so one registered under a create and a patch no longer reports double what it is doing.
flushAll visits each queue once rather than once per command type it is
registered under. The repeats were no-ops, but they made the walk several
times longer than it needed to be and the ordering log unreadable.
Retry backoff is now scheduled rather than slept. flush() processes the commands that are due and returns; a command that fails waits for its backoff
Retry backoff is now scheduled rather than slept. flush() processes the
commands that are due and returns; a command that fails waits for its backoff
between flushes instead of inside one.
Previously the delay was awaited inside _processCommand while the flush loop
kept re-selecting the same command, so a single failing command held its queue
for its entire retry ladder — minutes under the default policy — and, for
consumers that flush queues in sequence, held every queue behind it too.
Retry pacing is unchanged: the same policy produces the same intervals. What
changes is that the caller is no longer blocked across them, so flushes need
to be driven often enough to pick commands up as they come due. Connectivity
changes, app resume, and user-triggered syncs are usually enough; nextDueAt
is there for consumers that would rather schedule a timer.
Added ApiCommandQueue.nextAttemptAt and nextDueAt, so a consumer can tell
when a command is next eligible and when to flush again.
Time is now read through package:clock, so retry scheduling can be driven by
fake_async in tests. No behavioural change outside tests.
SyncState.fromJson no longer throws on state it can only partly read.
An unrecognised or wrongly typed flushStatus now reads as
QueueFlushStatus.idle, and a command bucket that is not a map is skipped
with the other bucket left intact. Both are reported through
apiCommandQueueLogger.
Previously either would throw, and a consumer persisting the state had no say
in what that cost: hydrated_bloc catches the error, falls back to an empty
queue and, by default, writes that back over the stored copy. flushStatus in
particular records whether a flush happened to be running when the state was
written — worth nothing on restore, but able to destroy every queued command
alongside it.
Command buckets are also read as Map rather than cast to
Map<String, dynamic>, so a storage backend handing them back loosely typed
no longer fails the restore.
Added terminal failure predicates for queue-wide and command-specific non-retryable API failures.
ApiCommandTerminalFailureRule for reusable status, data, error, and
full-response matching.First public release of the pure Dart api_command_queue core package.
api_command_queue core package.Your coding agent can read these notes before it upgrades. Set up the MCP server →