Saving Server Data
Use createCommandExecution for writes whose server outcome must survive a lost
response. It is exported from @revolist/revogrid-pro; no separate engine package
installation is needed. Loading and Caching Server Data covers
the read configuration for pagination, infinite loading and grouping.
The executor captures an operation ID, resource ID, expected revision and command
payload once. Repeated execute() calls share a pending attempt or return its
stored receipt. There are no automatic write retries. Read retry/polling
settings and QueryClient mutation defaults do not cause command submission.
Receipts and backend requirements
Section titled “Receipts and backend requirements”The backend must authenticate the current tenant and permission scope, validate
the expected revision, and return a receipt correlated by operationId:
| Status | Required fields beyond operationId | Meaning |
|---|---|---|
accepted | changes, revision | The server committed canonical changes |
rejected | message | The server definitively declined this command |
conflict | message, authoritative, revision | The expected revision was superseded |
unknown | Optional message | The outcome still needs recovery |
A thrown transport error, aborted request, malformed receipt or wrong operation
ID produces unknown, never a fabricated rejection or optimistic rollback.
Use validateReceipt to check product-specific revision/completeness rules in
addition to the built-in shape and correlation checks.
For recovery and retry, the backend must store receipts durably by operation ID within the authenticated resource scope. An idempotency guarantee requires the write and its receipt to be committed atomically, and reuse of an ID with a different payload to be rejected. An HTTP request ID alone is insufficient. Client handles do not deduplicate independent application instances or browser tabs.
Submit and recover
Section titled “Submit and recover”import { createCommandExecution, type CommandReceipt,} from '@revolist/revogrid-pro';
type UpdateOrder = { id: string; title: string };type CanonicalOrder = { id: string; title: string };
const execution = createCommandExecution<UpdateOrder, CanonicalOrder>({ operationId: crypto.randomUUID(), resourceId: 'orders', expectedRevision: 'revision-17', command: { id: 'order-1', title: 'Updated order' },}, { submit: async (envelope, { signal }) => { const response = await fetch('/api/orders/commands', { method: 'POST', signal, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(envelope), }); if (!response.ok) throw new Error('Write response unavailable'); return response.json() as Promise<CommandReceipt<CanonicalOrder>>; }, recover: async (envelope, { signal }) => { const response = await fetch( `/api/orders/receipts/${encodeURIComponent(envelope.operationId)}`, { signal }, ); if (!response.ok) throw new Error('Receipt unavailable'); return response.json() as Promise<CommandReceipt<CanonicalOrder>>; }, applyAccepted: canonical => { // Atomically update your domain store, preserve/rebase newer proposals, // and record accepted history here. console.log(canonical); }, onStatus: state => console.log(state.status, state.error),});
const receipt = await execution.execute();if (receipt.status === 'unknown') { await execution.recover(); // Receipt lookup; no additional write.}Use the same handle for duplicate UI actions. The engine owns a detached snapshot:
later caller edits, provider mutations and changes to execution.envelope do not
change the captured ID, revision or payload used by recovery/retry. Transport data
must be structured-cloneable and cannot contain shared writable buffers.
The domain retains ownership of pending edits, conflict resolution and history. After a conflict, use the authoritative data to rebase the proposal and create a new execution with a new operation ID and expected revision. A definitive conflict/rejection is never retried under its old operation ID.
Configuration types
Section titled “Configuration types”Public types are CommandEnvelope<Command, Revision>,
CommandReceipt<Changes, Revision, Conflict>,
CommandProvider<Command, Changes, Revision, Conflict>,
CommandOptions<Command, Changes, Revision, Conflict>,
CommandState<Changes, Revision, Conflict> and
CommandExecution<Command, Changes, Revision, Conflict>. Revision defaults to
string; authoritative conflict data defaults to the canonical changes type.
Opaque revisions are validated and interpreted by the provider, not numerically ordered.
CommandOptions field | Behavior / default |
|---|---|
submit | Required provider receiving the captured envelope and attempt-specific signal |
recover | Optional receipt lookup provider; missing recovery rejects explicitly |
idempotent | false; only explicit true enables retry() of unknown writes |
resume | false; restore an already-submitted envelope as unknown without submitting |
validateReceipt | Optional product validation before accepting any receipt |
applyAccepted | Apply canonical changes atomically; may return a promise |
source | Optional shared query source; accepted() runs after canonical application |
onStatus | Optional lifecycle callback; not called after disposal |
| Handle member | Behavior |
|---|---|
envelope | Detached captured envelope; persist it if application-owned recovery must survive reload |
getState() | Detached receipt plus status, local submission attempt count and optional error |
execute() | Submit once; share a pending attempt or return the stored receipt |
recover() | Look up an existing submitted command receipt; never resubmit |
retry() | Explicit retry of an unknown write with the identical envelope; requires idempotency |
reconcile() | Retry accepted canonical application/read synchronization without another write |
cancel() | Abort the current transport; an unconfirmed submission remains unknown |
dispose() | Abort transport, settle logical callers and stop late domain/status delivery |
Status is idle, submitting, recovering, a receipt status, or disposed.
Application/refetch errors remain attached to an accepted receipt. They do not
turn it into a failed write or permit resubmission. Call reconcile() to retry
local synchronization; successful domain application is not repeated if only
query synchronization failed. Domain application must be atomic if it can throw.
Cancellation cannot undo a server write. An abort-ignoring physical submission must settle before another retry/recovery attempt starts. A receipt confirmed before cancellation/disposal retains its definitive outcome. Asynchronous domain callbacks already entered by the application must guard their own lifetime.
Shared read invalidation
Section titled “Shared read invalidation”Pass the same source used by the grid’s query.source. The executor applies
canonical changes first, then calls source.accepted() to advance the acceptance
fence, cancel obsolete reads and refetch active views. A pre-acceptance response
cannot hydrate either shared grid. The source fence does not replace backend
revision checks or replica read-after-write guarantees.
Default standalone loader contexts are private; use an explicit source to connect
external writes to shared reads.
Snapshot queries still require a new provider snapshot after membership changes.
With query: false, invalidate the application-owned cache and use the loader’s
normal refresh path; a shared Query source cannot refetch bypassed loaders.
Recovery after reload
Section titled “Recovery after reload”Persist the original envelope in your domain’s recovery store before submission.
Recreate an execution with that envelope and resume: true, then call recover().
In resume mode execute() returns unknown and does not write. Apply the recovered
receipt to the appropriate domain/source scope. The executor provides no durable
browser storage, cross-tab coordination, automatic retry or generic proposal ledger.
Scheduler integration
Section titled “Scheduler integration”See Scheduler integration for product configuration, acceptance and recovery.
Kanban integration
Section titled “Kanban integration”See Kanban integration for product configuration, acceptance and recovery.
See Gantt for product configuration, acceptance and recovery.