Sequence, persist, acknowledge, retry, and conservatively rebase Sheetwrite document operations.On this page
Sheetwrite provides transport-, database-, and authentication-neutral collaboration primitives. The host still owns server sequencing, durable document storage, user identity, authorization, and network lifecycle.
Load, mount, queue, and acknowledge
Load a validated snapshot before mounting, then let one SyncCoordinator
observe local grid transactions. Do not also save event.changes: that list is
cell-oriented rollback detail and omits document metadata operations.
voidsync.flush(); // sends in order; each response acknowledges only its mutation ID
});
// Teardown:
// unsubscribeRemote();
// sync.destroy();
// grid.destroy();
SyncCoordinator subscribes to Grid changes itself. Local rendering remains
optimistic; the host controls when to call sendNext, flush, or retry.
applied and duplicate responses acknowledge the matching operations and
remove that mutation. A transport error leaves the immutable record pending.
An HTTP adapter can stay transport-neutral at the core boundary:
The server must authenticate the request, authorize every operation, validate
the document schema/policy, and assign the version. Client protection metadata
is not authorization.
Database-neutral server model
A snapshot plus append-only operation log is the default persistence shape:
Illustrative excerpt
documents(
document_id primary key,
current_version integernot null,
snapshot_version integernot null,
snapshot_json jsonnot null
)
document_operations(
document_id not null,
versionintegernot null,
client_mutation_id textnot null,
operations_json jsonnot null,
created_at server_timestamp not null,
primary key(document_id, version),
unique(document_id, client_mutation_id)
)
Handle commit() in one database transaction:
Return duplicate with the existing version when
(document_id, client_mutation_id) already exists.
Lock/read the document version. If it differs from baseVersion, return
conflict with ordered operations since base or a current snapshot.
Validate and authorize the submitted DocumentOp[] on the server.
Append at current_version + 1, update the document version, and return
applied with the same mutation ID.
Periodically fold the log into snapshot_json; never rewrite operation
versions or accept client-authored server timestamps.
Handle commitBatch() the same way, with one difference: the request has
versionOperationCounts, and it fills several consecutive versions. In one
database transaction, check the duplicate and the base version once, validate
every version, then append all of them or none. Store each version with its
batch: { index, count } position, and acknowledge with the last version.
Normalized cell tables are an alternative for products that need SQL queries
over cell values. They do not replace the protocol: the host must still
reconstruct a complete schema-versioned WorkbookSnapshot, preserve formula
source/style/metadata, and serialize structural operations under one document
version. Mixing independent cell writes with the operation log breaks atomic
row/column/formula semantics.
Large undo and atomic batches
Undo of a large clear restores the old cells. When the ordinary setBlock
operation is too large for one version, the Grid sends one restoreBlock
operation instead: the same cells as compressed JSON (deflate-json-v1). If
even that does not fit one version, the Grid splits the restore into an atomic
batch of up to 16 versions and 64 MiB. Undo stays one history step and one
change event.
Before you accept 0.5.0 clients, make sure that your server:
accepts restoreBlock wherever it switches over the DocumentOp union, and
decodes it as untrusted input. @sheetwrite/core does not export a server
decoder, so your decoder must do these checks, in this order, before it
changes the document:
encoding is "deflate-json-v1", and decodedBytes is a positive
integer of at most 64 MiB;
data is canonical base64 (it decodes and re-encodes to the same text);
the raw DEFLATE stream inflates into a buffer of exactly decodedBytes
bytes. Never inflate without that output limit: a small stream can expand
to gigabytes. Reject a stream that ends early or has bytes left over;
the bytes are valid UTF-8 JSON of a PackedCellBlock whose row and
column counts match range, with at most 4,000,000 cells;
every value, formula, reference, and style in the block passes the same
validation as a setBlock operation;
implements commitBatch as above, or accepts that SyncCoordinator
rejects a local step that needs more than one version;
publishes batch members with their batch field, so that peers apply the
whole batch at once; and
folds restoreBlock into snapshots as ordinary cells. Snapshots never
store restore streams.
The document operations reference
has the exact field rules, limits, and error codes. When an undo is above the
transaction limits even after this, the Grid changes nothing and reports it;
see Interaction.
Durable pending commits
SyncCoordinator can receive a PendingCommitStorage adapter. With one configured, it:
persists each immutable commit before it can be sent;
restores pending commits on startup and reapplies them locally as remote-source operations, so restoration does not echo into the outgoing queue;
retries with the original clientMutationId after reconnect or reload;
removes durable work only after an applied or duplicate acknowledgement; and
retains conflicts and storage failures for explicit host recovery.
Await coordinator.ready() before declaring startup synchronized. Use initialConnection: "offline" for offline-first startup, setOnline(false) on disconnect, and setOnline(true) to reconnect and drain pending work in order. coordinator.state distinguishes connection state from hydration, persistence, sending, conflict, and error activity.
The browser IndexedDB implementation is intentionally isolated from Node and SSR entrypoints:
IndexedDB record migration is versioned. Unsupported future schemas, blocked upgrades, quota exhaustion, transaction failures, and aborts surface as typed IndexedDbPendingCommitStorageError codes. Core continues to work without IndexedDB when no durable adapter is supplied.
Remote operations and version gaps
SyncCoordinator.subscribe accepts either RemoteOperationSource or AsyncIterable<VersionedOperation>. Operations apply only at serverVersion + 1; own echoed mutation IDs and already acknowledged IDs are deduplicated. Remote application uses Grid.applyRemoteOperations, so it does not create outgoing dirty work or local undo entries. Remote row and column inserts and deletes move existing undo entries, so a later undo restores the cell the user edited.
A gap emits reload-required. Prefer fetching missing ordered operations through
recoverVersionGap; the coordinator applies them in sequence. A returned
snapshot is a host remount signal, not an automatic store replacement.
resumeAfterReload(snapshot) updates retained queue base versions only after the
host has installed that snapshot and reapplied local work; it does not hydrate a
grid or attach the coordinator to a newly created grid.
Conflict and snapshot reload
Never acknowledge or discard a conflicted mutation implicitly. First call
sync.recoverConflict(). It recovers on the live Grid, without a reload, when
the server returns operationsSinceBase, the pending work and the server
versions it did not see change different cells and sheets, and neither side
inserts, deletes, or moves rows or columns. It applies the missed versions,
moves the queue onto the server head, and emits recovered; call
sync.flush() to resend:
if (result.status==="recovered") returnsync.flush();
if (result.status==="reload-required") returnreloadSession(event.response, result.reason);
});
});
Otherwise the result is reload-required with a reason and nothing changes.
If the server returns both operationsSinceBase and a current snapshot, the
host can then gate every pending transaction through the conservative rebaser,
clear the old durable IDs, remount, and submit the safe results as new
mutations:
When the rebaser reports conflict—overlap, formulas across structural changes,
concurrent moves, or sheet lifecycle ambiguity—keep the original queue and show
product-specific conflict UI. If the server returns only a snapshot, the host
cannot infer a safe transform; offer explicit discard/export/manual re-entry
instead. Reloading a snapshot over pending edits without this decision loses
intent.
Two ordering rules keep recovery from losing work:
Freeze the Grid and await sync.ready() before reading pendingCommits().
An edit made while recovery awaits would be neither rebased nor removed, and
a queue write still in flight could land after its removal. Either comes back
on the remounted client with a stale base version.
Keep remote operations that arrive between loading latest and
sync.subscribe(...), and deliver them to the new coordinator. Drop versions
at or below latest.version, which the snapshot already contains. A
transport that only forwards to the current subscriber loses them, and the
client then waits on a version gap that never closes.
Presence
PresenceCoordinator publishes actor metadata, active sheet, and bounded selection ranges through a host PresenceTransport. Presence:
is never written to document operations, snapshots, dirty state, or history;
expires according to local heartbeat receipt time;
is bounded by actor and range limits;
clamps ranges to valid visible grid geometry and hides other-sheet or filtered selections;
reuses the grid overlay element pool rather than mutating canvas cells; and
supports privacy controls for display name, selections, inbound presence, and actor filtering.
Presence is advisory UI state. Do not use it for authorization, locking, or conflict prevention.
Revisions
RevisionCoordinator delegates revision listing, loading, and restore to a RevisionAdapter. Preview snapshots pass through the optional host schema migrator and mount with readOnly: true. Restore requests include targetVersion, current baseVersion, and a stable mutation ID. The adapter contract requires restore to create a newer auditable server version; it must never rewind database state in place.
Comments
Comments are document-adjacent server entities, not fields on every cell. CommentCoordinator stores stable thread/message IDs and range or cell anchors, while adapters supply authenticated author references and server timestamps. Create, reply, and resolve requests contain no client-authored identity or time fields. Authorization remains entirely host/server-owned.
Comment streams are versioned independently. Out-of-order events emit a gap rather than being applied speculatively.
Concurrent structural editing decision
Sheetwrite uses deterministic server ordering plus conservative rebase, not a CRDT or general OT layer.
rebaseDocumentOperations safely shifts non-overlapping literal edits and references across server-ordered row/column insertions and deletions. It reports explicit conflicts for:
edits or range pastes overlapping inserted/deleted boundaries;
targets or references deleted by a concurrent operation;
formula source crossing structural edits, because text-only rewriting cannot preserve sheet-aware reference intent;
concurrent row/column moves;
conflicting sheet lifecycle operations; and
metadata families that need domain-specific transforms.
This choice preserves stable sheet IDs and authoritative formula source without lossy adapters. A CRDT/OT dependency is not justified by current scenarios: short-lived offline and online collaboration converge under server sequencing, while long-lived offline concurrent structural edits require product-level conflict UX and spreadsheet-specific formula/range semantics that generic sequence CRDTs do not supply. Revisit only if requirements demand automatic preservation of those ambiguous edits and scenario tests define the intended result for every structural/formula case.
function createGridFromSnapshot(host: HTMLElement, snapshot: unknown, options?: SnapshotGridOptions): GridfunctioncreateGridFromSnapshot(host:HTMLElement,snapshot:unknown,options?:SnapshotGridOptions,):GridfunctioncreateGridFromSnapshot(host:HTMLElement,snapshot:unknown,options?:SnapshotGridOptions,):GridMount a grid over a validated, non-dirty snapshot. The grid owns and disposes the hydrated store just like one created through createGrid.API reference →class SyncCoordinatorclassSyncCoordinatorclassSyncCoordinatorDeterministic, transport-neutral optimistic sync. Local rendering is never blocked: changes queue immediately, while hosts explicitly call sendNext or retry to perform network work.@example import { SyncCoordinator, type Grid, type PersistenceAdapter } from "@sheetwrite/core"; async function connect(grid: Grid, adapter: PersistenceAdapter, documentId: string, serverVersion: number) { const sync = new SyncCoordinator(grid, adapter, { documentId, serverVersion }); await sync.ready(); await sync.flush(); return sync; // Call sync.destroy() before removing the Grid. }@see PersistenceAdapter *@see SyncCoordinator.recoverConflictAPI reference →interface PersistenceAdapterinterfacePersistenceAdapterinterfacePersistenceAdapterHost load and commit contract for versioned workbook persistence.@example import { MemoryPersistenceAdapter, type PersistenceAdapter, type WorkbookSnapshot } from "@sheetwrite/core"; async function loadLocal(snapshot: WorkbookSnapshot) { if (!snapshot.documentId) throw new Error("A document ID is required"); const adapter: PersistenceAdapter = new MemoryPersistenceAdapter(snapshot); return adapter.load(snapshot.documentId); }@see MemoryPersistenceAdapter *@see SyncCoordinatorAPI reference →interface RemoteOperationSourceinterfaceRemoteOperationSourceinterfaceRemoteOperationSourceHost subscription contract for ordered versioned operations. Sources that can pause intake should await the listener promise to preserve backpressure.API reference →const documentId: "workbook-42"constdocumentId:"workbook-42"constdocumentId:"workbook-42"const snapshot: WorkbookSnapshotconstsnapshot:WorkbookSnapshotconstsnapshot:WorkbookSnapshotAPI reference →const persistenceAdapter: PersistenceAdapterconstpersistenceAdapter:PersistenceAdapterconstpersistenceAdapter:PersistenceAdapterAPI reference →PersistenceAdapter.load(documentId: string, signal?: AbortSignal): Promise<WorkbookSnapshot>PersistenceAdapter.load(documentId: string, signal?: AbortSignal): Promise<WorkbookSnapshot>PersistenceAdapter.load(documentId: string, signal?: AbortSignal): Promise<WorkbookSnapshot>const documentId: stringconstdocumentId:stringconstdocumentId:stringconst grid: Gridconstgrid:Gridconstgrid:GridThe imperative core grid this controller owns.API reference →var document: Documentvar document:Documentvar document:Documentwindow.document returns a reference to the document contained in the window. MDN ReferenceParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement | null (+4 overloads)ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement |null (+4 overloads)ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement |null (+4 overloads)Returns the first element that is a descendant of node that matches selectors. MDN Referenceconst sync: SyncCoordinatorconstsync:SyncCoordinatorconstsync:SyncCoordinatorAPI reference →new SyncCoordinator(grid: Grid, adapter: PersistenceAdapter, options: SyncCoordinatorOptions): SyncCoordinatornewSyncCoordinator(grid: Grid, adapter: PersistenceAdapter, options: SyncCoordinatorOptions): SyncCoordinatornewSyncCoordinator(grid: Grid, adapter: PersistenceAdapter, options: SyncCoordinatorOptions): SyncCoordinatorDeterministic, transport-neutral optimistic sync. Local rendering is never blocked: changes queue immediately, while hosts explicitly call sendNext or retry to perform network work.@example import { SyncCoordinator, type Grid, type PersistenceAdapter } from "@sheetwrite/core"; async function connect(grid: Grid, adapter: PersistenceAdapter, documentId: string, serverVersion: number) { const sync = new SyncCoordinator(grid, adapter, { documentId, serverVersion }); await sync.ready(); await sync.flush(); return sync; // Call sync.destroy() before removing the Grid. }@see PersistenceAdapter *@see SyncCoordinator.recoverConflictAPI reference →SyncCoordinatorOptions.documentId: stringinterfaceSyncCoordinatorOptions {documentId:string;}interfaceSyncCoordinatorOptions {documentId:string;}API reference →SyncCoordinatorOptions.serverVersion: numberinterfaceSyncCoordinatorOptions {serverVersion:number;}interfaceSyncCoordinatorOptions {serverVersion:number;}Last accepted contiguous remote server version.API reference →WorkbookSnapshot.version?: number | undefinedinterfaceWorkbookSnapshot {version?:number|undefined;}interfaceWorkbookSnapshot {version?:number|undefined;}API reference →SyncCoordinator.on(listener: SyncListener): () => voidSyncCoordinator.on(listener: SyncListener): () =>voidSyncCoordinator.on(listener: SyncListener): () =>voidAPI reference →event: SyncCoordinatorEventlet event:SyncCoordinatorEventlet event:SyncCoordinatorEventAPI reference →type: "conflict" | "state" | "restored" | "persisting" | "pending" | "sending" | "acknowledged" | "remote-applied" | "reload-required" | "reloaded" | "recovered" | "storage-error" | "error"let type:|"conflict"|"state"|"restored"|"persisting"|"pending"|"sending"|"acknowledged"|"remote-applied"|"reload-required"|"reloaded"|"recovered"|"storage-error"|"error"let type:|"conflict"|"state"|"restored"|"persisting"|"pending"|"sending"|"acknowledged"|"remote-applied"|"reload-required"|"reloaded"|"recovered"|"storage-error"|"error"Controls cell input parsing and default value formatting for this column.API reference →var console: Consolevar console:Consolevar console:ConsoleConsole.log(...data: any[]): void (+1 overload)Console.log(...data: any[]): void (+1 overload)Console.log(...data: any[]): void (+1 overload)The console.log() static method outputs a message to the console. MDN Referenceevent: {
type: "acknowledged";
clientMutationId: string;
version: number;
duplicate: boolean;
}let event: {type:"acknowledged";clientMutationId:string;version:number;duplicate:boolean;}let event: {type:"acknowledged";clientMutationId:string;version:number;duplicate:boolean;}version: numberlet version:numberlet version:numberclientMutationId: stringlet clientMutationId:stringlet clientMutationId:stringevent: {
type: "state";
state: SyncStateSnapshot;
} | {
type: "restored";
pending: readonly SyncMutationRecord[];
} | {
type: "persisting";
mutation: SyncMutationRecord;
} | {
type: "pending";
mutation: SyncMutationRecord;
} | {
type: "sending";
mutation: SyncMutationRecord;
} | {
type: "conflict";
mutation: SyncMutationRecord;
response: Extract<PersistenceCommitResponse, {
status: "conflict";
}>;
} | ... 5 more ... | {
...;
}let event: {type:"state";state:SyncStateSnapshot;} | {type:"restored";pending:readonlySyncMutationRecord[];} | {type:"persisting";mutation:SyncMutationRecord;} | {type:"pending";mutation:SyncMutationRecord;} | {type:"sending";mutation:SyncMutationRecord;} | {type:"conflict";mutation:SyncMutationRecord;response:Extract<PersistenceCommitResponse, {status:"conflict";}>;} | ... 5more ... | {...;}let event: {type:"state";state:SyncStateSnapshot;} | {type:"restored";pending:readonlySyncMutationRecord[];} | {type:"persisting";mutation:SyncMutationRecord;} | {type:"pending";mutation:SyncMutationRecord;} | {type:"sending";mutation:SyncMutationRecord;} | {type:"conflict";mutation:SyncMutationRecord;response:Extract<PersistenceCommitResponse, {status:"conflict";}>;} | ... 5more ... | {...;}type: "conflict" | "state" | "restored" | "persisting" | "pending" | "sending" | "remote-applied" | "reload-required" | "reloaded" | "recovered" | "storage-error" | "error"let type:|"conflict"|"state"|"restored"|"persisting"|"pending"|"sending"|"remote-applied"|"reload-required"|"reloaded"|"recovered"|"storage-error"|"error"let type:|"conflict"|"state"|"restored"|"persisting"|"pending"|"sending"|"remote-applied"|"reload-required"|"reloaded"|"recovered"|"storage-error"|"error"Controls cell input parsing and default value formatting for this column.API reference →Console.error(...data: any[]): void (+1 overload)Console.error(...data: any[]): void (+1 overload)Console.error(...data: any[]): void (+1 overload)Log to stderr in your terminal Appears in red@param data something to displayevent: {
type: "conflict";
mutation: SyncMutationRecord;
response: Extract<PersistenceCommitResponse, {
status: "conflict";
}>;
}let event: {type:"conflict";mutation:SyncMutationRecord;response:Extract<PersistenceCommitResponse,{status:"conflict";}>;}let event: {type:"conflict";mutation:SyncMutationRecord;response:Extract<PersistenceCommitResponse,{status:"conflict";}>;}mutation: SyncMutationRecordlet mutation:SyncMutationRecordlet mutation:SyncMutationRecordAPI reference →SyncCoordinator.ready(): Promise<void>SyncCoordinator.ready(): Promise<void>SyncCoordinator.ready(): Promise<void>Resolves after durable work is restored and every startup commit is visible locally.API reference →const unsubscribeRemote: () => voidconstunsubscribeRemote: () =>voidconstunsubscribeRemote: () =>voidSyncCoordinator.subscribe(source: RemoteOperationSource | AsyncIterable<VersionedOperation>): () => voidSyncCoordinator.subscribe(source: RemoteOperationSource | AsyncIterable<VersionedOperation>): () =>voidSyncCoordinator.subscribe(source: RemoteOperationSource | AsyncIterable<VersionedOperation>): () =>voidconst remoteOperationSource: AsyncIterable<DocumentOp>constremoteOperationSource:AsyncIterable<DocumentOp>constremoteOperationSource:AsyncIterable<DocumentOp>API reference →const saveButton: HTMLButtonElementconstsaveButton:HTMLButtonElementconstsaveButton:HTMLButtonElementHTMLButtonElement.addEventListener<"click">(type: "click", listener: (this: HTMLButtonElement, ev: PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)HTMLButtonElement.addEventListener<"click">(type: "click", listener: (this:HTMLButtonElement, ev:PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)HTMLButtonElement.addEventListener<"click">(type: "click", listener: (this:HTMLButtonElement, ev:PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. MDN Reference The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. MDN ReferenceSyncCoordinator.flush(): Promise<readonly PersistenceCommitResponse[]>SyncCoordinator.flush(): Promise<readonly PersistenceCommitResponse[]>SyncCoordinator.flush(): Promise<readonly PersistenceCommitResponse[]>interface PersistenceBatchCommitRequestinterfacePersistenceBatchCommitRequestinterfacePersistenceBatchCommitRequestCancellable commit of one atomic batch that spans several server versions.API reference →interface PersistenceCommitRequestinterfacePersistenceCommitRequestinterfacePersistenceCommitRequestCancellable pending commit submitted to a persistence adapter.API reference →type PersistenceCommitResponse = {
status: "applied";
version: number;
clientMutationId: string;
} | {
status: "duplicate";
version: number;
clientMutationId: string;
} | {
status: "conflict";
currentVersion: number;
operationsSinceBase?: readonly VersionedOperation[];
snapshot?: WorkbookSnapshot;
}typePersistenceCommitResponse=| {status:"applied";version:number;clientMutationId:string;}| {status:"duplicate";version:number;clientMutationId:string;}| {status:"conflict";currentVersion:number;operationsSinceBase?:readonlyVersionedOperation[];snapshot?:WorkbookSnapshot;};typePersistenceCommitResponse=| {status:"applied";version:number;clientMutationId:string;}| {status:"duplicate";version:number;clientMutationId:string;}| {status:"conflict";currentVersion:number;operationsSinceBase?:readonlyVersionedOperation[];snapshot?:WorkbookSnapshot;};Applied, duplicate, or conflict acknowledgement from persistence. applied confirms the submitted operations unchanged; normalization must conflict.API reference →interface WorkbookSnapshotinterfaceWorkbookSnapshotinterfaceWorkbookSnapshotSchema-versioned serializable workbook document.API reference →(type parameter) T in responseJson<T>(response: Response): Promise<T>(type parameter) TinresponseJson<T>(response: Response): Promise<T>(type parameter) TinresponseJson<T>(response: Response): Promise<T>interface ResponseinterfaceResponseinterfaceResponseThe Response interface of the Fetch API represents the response to a request. MDN Referenceinterface Promise<T>interfacePromise<T>interfacePromise<T>Represents the completion of an asynchronous operationresponse: Responselet response:Responselet response:ResponseResponse.ok: booleaninterfaceResponse {ok:boolean;}interfaceResponse {ok:boolean;}The ok read-only property of the Response interface contains a Boolean stating whether the response was successful (status in the range 200-299) or not. MDN Referencevar Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+2 overloads)var Error:ErrorConstructornew (message?:string, options?:ErrorOptions) =>Error (+2 overloads)var Error:ErrorConstructornew (message?:string, options?:ErrorOptions) =>Error (+2 overloads)Response.status: numberinterfaceResponse {status:number;}interfaceResponse {status:number;}The status read-only property of the Response interface contains the HTTP status codes of the response. MDN ReferenceBody.json(): Promise<any>Body.json(): Promise<any>Body.json(): Promise<any>MDN ReferencedocumentId: stringlet documentId:stringlet documentId:stringsignal: AbortSignal | undefinedlet signal:AbortSignal|undefinedlet signal:AbortSignal|undefinedAbort before or between bounded codec operations.API reference →const response: Responseconstresponse:Responseconstresponse:Responsefunction fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+2 overloads)
namespace fetchfunctionfetch(input:string|URL|Request, init?:RequestInit):Promise<Response> (+2overloads)namespacefetchfunctionfetch(input:string|URL|Request, init?:RequestInit):Promise<Response> (+2overloads)namespacefetchMDN Referencefunction encodeURIComponent(uriComponent: string | number | boolean): stringfunctionencodeURIComponent(uriComponent:string|number|boolean,):stringfunctionencodeURIComponent(uriComponent:string|number|boolean,):stringEncodes a text string as a valid component of a Uniform Resource Identifier (URI).@param uriComponent A value representing an unencoded URI component.RequestInit.signal?: AbortSignal | null | undefinedinterfaceRequestInit {signal?:AbortSignal|null|undefined;}interfaceRequestInit {signal?:|AbortSignal|null|undefined;}An AbortSignal to set request's signal.API reference →function responseJson<WorkbookSnapshot>(response: Response): Promise<WorkbookSnapshot>functionresponseJson<WorkbookSnapshot>(response:Response,):Promise<WorkbookSnapshot>functionresponseJson<WorkbookSnapshot,>(response:Response,):Promise<WorkbookSnapshot>API reference →PersistenceAdapter.commit(request: PersistenceCommitRequest): Promise<PersistenceCommitResponse>PersistenceAdapter.commit(request: PersistenceCommitRequest): Promise<PersistenceCommitResponse>PersistenceAdapter.commit(request: PersistenceCommitRequest): Promise<PersistenceCommitResponse>const signal: AbortSignal | undefinedconstsignal:AbortSignal|undefinedconstsignal:AbortSignal|undefinedAbort before or between bounded codec operations.API reference →const body: {
documentId: string;
baseVersion: number;
clientMutationId: string;
operations: readonly DocumentOp[];
versionOperationCounts?: readonly number[];
}constbody: {documentId:string;baseVersion:number;clientMutationId:string;operations:readonlyDocumentOp[];versionOperationCounts?:readonlynumber[];}constbody: {documentId:string;baseVersion:number;clientMutationId:string;operations:readonlyDocumentOp[];versionOperationCounts?:readonlynumber[];}API reference →request: PersistenceCommitRequestlet request:PersistenceCommitRequestlet request:PersistenceCommitRequestAPI reference →PendingCommit.documentId: stringinterfacePendingCommit {documentId:string;}interfacePendingCommit {documentId:string;}API reference →RequestInit.method?: string | undefinedinterfaceRequestInit {method?:string|undefined;}interfaceRequestInit {method?:string|undefined;}A string to set request's method.RequestInit.headers?: HeadersInit | undefinedinterfaceRequestInit {headers?:HeadersInit|undefined;}interfaceRequestInit {headers?:HeadersInit|undefined;}A Headers object, an object literal, or an array of two-item arrays to set request's headers.RequestInit.body?: BodyInit | null | undefinedinterfaceRequestInit {body?:BodyInit|null|undefined;}interfaceRequestInit {body?:BodyInit|null|undefined;}A BodyInit object or null to set request's body.var JSON: JSONvarJSON:JSONvarJSON:JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)JSON.stringify(value: any, replacer?: (this:any, key:string, value:any) => any, space?: string | number): string (+1 overload)JSON.stringify(value: any, replacer?: (this:any, key:string, value:any) => any, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.@param value A JavaScript value, usually an object or array, to be converted.@param replacer A function that transforms the results.@param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.@throws {TypeError} If a circular reference or a BigInt value is found.function responseJson<PersistenceCommitResponse>(response: Response): Promise<PersistenceCommitResponse>functionresponseJson<PersistenceCommitResponse>(response:Response,):Promise<PersistenceCommitResponse>functionresponseJson<PersistenceCommitResponse,>(response:Response,):Promise<PersistenceCommitResponse>API reference →PersistenceAdapter.commitBatch?(request: PersistenceBatchCommitRequest): Promise<PersistenceCommitResponse>PersistenceAdapter.commitBatch?(request:PersistenceBatchCommitRequest):Promise<PersistenceCommitResponse>PersistenceAdapter.commitBatch?(request:PersistenceBatchCommitRequest):Promise<PersistenceCommitResponse>Commit an atomic batch at versions baseVersion + 1 through baseVersion + versionOperationCounts.length. Apply every member or none, publish every member with its batch position, and acknowledge with the last version. SyncCoordinator rejects a local transaction that needs more than one version when the adapter does not implement this method.API reference →const body: {
versionOperationCounts: readonly number[];
documentId: string;
baseVersion: number;
clientMutationId: string;
operations: readonly DocumentOp[];
}constbody: {versionOperationCounts:readonlynumber[];documentId:string;baseVersion:number;clientMutationId:string;operations:readonlyDocumentOp[];}constbody: {versionOperationCounts:readonlynumber[];documentId:string;baseVersion:number;clientMutationId:string;operations:readonlyDocumentOp[];}API reference →request: PersistenceBatchCommitRequestlet request:PersistenceBatchCommitRequestlet request:PersistenceBatchCommitRequestAPI reference →class IndexedDbPendingCommitStorageclassIndexedDbPendingCommitStorageclassIndexedDbPendingCommitStorageBrowser-only durable pending queue. Import it from @sheetwrite/core/browser; the package's root entrypoint never evaluates IndexedDB globals.API reference →const pendingStorage: IndexedDbPendingCommitStorageconstpendingStorage:IndexedDbPendingCommitStorageconstpendingStorage:IndexedDbPendingCommitStorageAPI reference →new IndexedDbPendingCommitStorage(options?: IndexedDbPendingCommitStorageOptions): IndexedDbPendingCommitStoragenewIndexedDbPendingCommitStorage(options?: IndexedDbPendingCommitStorageOptions): IndexedDbPendingCommitStoragenewIndexedDbPendingCommitStorage(options?: IndexedDbPendingCommitStorageOptions): IndexedDbPendingCommitStorageBrowser-only durable pending queue. Import it from @sheetwrite/core/browser; the package's root entrypoint never evaluates IndexedDB globals.API reference →IndexedDbPendingCommitStorageOptions.databaseName?: string | undefinedinterfaceIndexedDbPendingCommitStorageOptions {databaseName?:string|undefined;}interfaceIndexedDbPendingCommitStorageOptions {databaseName?:string|undefined;}API reference →SyncCoordinatorOptions.pendingStorage?: PendingCommitStorage | undefinedinterfaceSyncCoordinatorOptions {pendingStorage?:PendingCommitStorage|undefined;}interfaceSyncCoordinatorOptions {pendingStorage?:|PendingCommitStorage|undefined;}SyncCoordinatorOptions.initialConnection?: "online" | "offline" | undefinedinterfaceSyncCoordinatorOptions {initialConnection?:"online"|"offline"|undefined;}interfaceSyncCoordinatorOptions {initialConnection?:|"online"|"offline"|undefined;}API reference →var navigator: Navigatorvar navigator:Navigatorvar navigator:NavigatorThe Window.navigator read-only property returns a reference to the Navigator object, which has methods and properties about the application running the script. MDN ReferenceNavigatorOnLine.onLine: booleaninterfaceNavigatorOnLine {onLine:boolean;}interfaceNavigatorOnLine {onLine:boolean;}MDN Referencevar window: Window & typeof globalThisvar window:Window&typeof globalThisvar window:Window&typeof globalThisThe window property of a Window object points to the window object itself. MDN ReferenceaddEventListener<"online">(type: "online", listener: (this: Window, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+3 overloads)addEventListener<"online">(type: "online", listener: (this:Window, ev:Event) => any, options?: boolean | AddEventListenerOptions): void (+3 overloads)addEventListener<"online">(type: "online", listener: (this:Window, ev:Event) => any, options?: boolean | AddEventListenerOptions): void (+3 overloads)The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. MDN ReferenceSyncCoordinator.setOnline(online: boolean): voidSyncCoordinator.setOnline(online: boolean): voidSyncCoordinator.setOnline(online: boolean): voidUpdate connectivity. Reconnection drains durable work in order; going offline aborts in-flight requests so retries retain their mutation IDs.API reference →addEventListener<"offline">(type: "offline", listener: (this: Window, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+3 overloads)addEventListener<"offline">(type: "offline", listener: (this:Window, ev:Event) => any, options?: boolean | AddEventListenerOptions): void (+3 overloads)addEventListener<"offline">(type: "offline", listener: (this:Window, ev:Event) => any, options?: boolean | AddEventListenerOptions): void (+3 overloads)The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. MDN Referencetype SyncConflictReloadReason = "rejected" | "missing-operations" | "structural-change" | "overlapping-work" | "version-advanced" | "own-mutation"typeSyncConflictReloadReason=|"rejected"|"missing-operations"|"structural-change"|"overlapping-work"|"version-advanced"|"own-mutation";typeSyncConflictReloadReason=|"rejected"|"missing-operations"|"structural-change"|"overlapping-work"|"version-advanced"|"own-mutation";Why a conflict cannot be recovered in place; the host reloads the document instead.API reference →function reloadSession(response: Extract<PersistenceCommitResponse, {
status: "conflict";
}>, reason: SyncConflictReloadReason): Promise<void>functionreloadSession(response:Extract<PersistenceCommitResponse,{status:"conflict";}>,reason:SyncConflictReloadReason,):Promise<void>functionreloadSession(response:Extract<PersistenceCommitResponse,{status:"conflict";}>,reason:SyncConflictReloadReason,):Promise<void>Host fallback: the rebase-and-remount recipe below.response: {
status: "conflict";
currentVersion: number;
operationsSinceBase?: readonly VersionedOperation[];
snapshot?: WorkbookSnapshot;
}let response: {status:"conflict";currentVersion:number;operationsSinceBase?:readonlyVersionedOperation[];snapshot?:WorkbookSnapshot;}let response: {status:"conflict";currentVersion:number;operationsSinceBase?:readonlyVersionedOperation[];snapshot?:WorkbookSnapshot;}type Extract<T, U> = T extends U ? T : nevertypeExtract<T, U> =TextendsU?T:never;typeExtract<T, U> =TextendsU?T:never;Extract from T those types that are assignable to Ureason: SyncConflictReloadReasonlet reason:SyncConflictReloadReasonlet reason:SyncConflictReloadReasonWhether readiness followed first initialization, an input reset, or a renderer reset.API reference →SyncCoordinator.recoverConflict(): Promise<SyncConflictRecovery>SyncCoordinator.recoverConflict(): Promise<SyncConflictRecovery>SyncCoordinator.recoverConflict(): Promise<SyncConflictRecovery>Recover from the outstanding base-version conflict without reloading the document. This succeeds when the pending work and every server version it did not see change different cells, and neither inserts, deletes, or moves rows or columns: the local Grid then already shows the server result plus the pending work, so recovery applies the missed server versions and moves the pending queue onto the new server version. Call flush afterwards to send it. In every other case nothing changes and the result names the reason; the host then reloads and calls resumeAfterReload.@example import type { SyncCoordinator } from "@sheetwrite/core"; async function recover(sync: SyncCoordinator) { const result = await sync.recoverConflict(); if (result.status === "recovered") await sync.flush(); // A reload-required result leaves the document unchanged for host recovery. return result; }@see SyncCoordinator.resumeAfterReloadAPI reference →Promise<SyncConflictRecovery>.then<SyncConflictRecovery, never>(onfulfilled?: ((value: SyncConflictRecovery) => SyncConflictRecovery | PromiseLike<SyncConflictRecovery>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<SyncConflictRecovery>Promise<SyncConflictRecovery>.then<SyncConflictRecovery, never>(onfulfilled?: ((value:SyncConflictRecovery) => SyncConflictRecovery | PromiseLike<SyncConflictRecovery>) |null|undefined, onrejected?: ((reason:any) => PromiseLike<never>) |null|undefined): Promise<SyncConflictRecovery>Promise<SyncConflictRecovery>.then<SyncConflictRecovery, never>(onfulfilled?: ((value:SyncConflictRecovery) => SyncConflictRecovery | PromiseLike<SyncConflictRecovery>) |null|undefined, onrejected?: ((reason:any) => PromiseLike<never>) |null|undefined): Promise<SyncConflictRecovery>Attaches callbacks for the resolution and/or rejection of the Promise.@param onfulfilled The callback to execute when the Promise is resolved.@param onrejected The callback to execute when the Promise is rejected.@returns A Promise for the completion of which ever callback is executed.API reference →result: SyncConflictRecoverylet result:SyncConflictRecoverylet result:SyncConflictRecoverySearch state after the replacement (matches re-scanned against the new data).API reference →status: "reload-required" | "recovered" | "no-conflict"let status:"reload-required"|"recovered"|"no-conflict"let status:|"reload-required"|"recovered"|"no-conflict"result: {
status: "reload-required";
reason: SyncConflictReloadReason;
conflict?: RebaseConflict;
} | {
status: "no-conflict";
}let result:| {status:"reload-required";reason:SyncConflictReloadReason;conflict?:RebaseConflict;}| {status:"no-conflict";}let result:| {status:"reload-required";reason:SyncConflictReloadReason;conflict?:RebaseConflict;}| {status:"no-conflict";}Search state after the replacement (matches re-scanned against the new data).API reference →status: "reload-required" | "no-conflict"let status:"reload-required"|"no-conflict"let status:|"reload-required"|"no-conflict"result: {
status: "reload-required";
reason: SyncConflictReloadReason;
conflict?: RebaseConflict;
}let result: {status:"reload-required";reason:SyncConflictReloadReason;conflict?:RebaseConflict;}let result: {status:"reload-required";reason:SyncConflictReloadReason;conflict?:RebaseConflict;}Search state after the replacement (matches re-scanned against the new data).API reference →function rebaseDocumentOperations(localOperations: readonly DocumentOp[], remoteOperations: readonly DocumentOp[]): DocumentRebaseResultfunctionrebaseDocumentOperations(localOperations:readonlyDocumentOp[],remoteOperations:readonlyDocumentOp[],):DocumentRebaseResultfunctionrebaseDocumentOperations(localOperations:readonlyDocumentOp[],remoteOperations:readonlyDocumentOp[],):DocumentRebaseResultConservative server-ordered rebase for pending offline work. Non-overlapping literal edits are shifted across row/column insertion and deletion. Ambiguous formula, overlapping, sheet-lifecycle, and move cases become explicit conflicts instead of lossy guesses. This is the collaboration design gate; no CRDT dependency is required for the supported cases. Each local operation is written after the local operations before it, and each remote operation after the remote operations before it. A remote operation is therefore carried forward through the local sequence: before it is compared with local operation k, it is moved past local operations 0..k-1.API reference →interface GridinterfaceGridinterfaceGridImperative grid handle for document commands, events, rendering, and teardown.@example import type { Grid } from "@sheetwrite/core"; function updateTotal(grid: Grid) { const sheet = grid.getActiveSheet(); const result = grid.applyTransaction({ patches: [{ op: "set", addr: { sheet, row: 0, col: 0 }, value: { kind: "literal", value: 42 } }], }); if (result.status === "applied") grid.scrollToCell({ sheet, row: 0, col: 0 }); return grid.aggregate(0, "sum"); }@see createGrid *@see Grid.applyTransactionAPI reference →type ConflictResponse = {
status: "conflict";
currentVersion: number;
operationsSinceBase?: readonly VersionedOperation[];
snapshot?: WorkbookSnapshot;
}typeConflictResponse= {status:"conflict";currentVersion:number;operationsSinceBase?:readonlyVersionedOperation[];snapshot?:WorkbookSnapshot;};typeConflictResponse= {status:"conflict";currentVersion:number;operationsSinceBase?:readonlyVersionedOperation[];snapshot?:WorkbookSnapshot;};SyncSession.grid: GridinterfaceSyncSession {grid:Grid;}interfaceSyncSession {grid:Grid;}The imperative core grid this controller owns.API reference →SyncSession.sync: SyncCoordinatorinterfaceSyncSession {sync:SyncCoordinator;}interfaceSyncSession {sync:SyncCoordinator;}API reference →SyncSession.unsubscribeRemote: () => voidinterfaceSyncSession {unsubscribeRemote: () =>void;}interfaceSyncSession {unsubscribeRemote: () =>void;}function reloadAfterConflict(session: SyncSession, response: ConflictResponse): Promise<SyncSession>functionreloadAfterConflict(session:SyncSession,response:ConflictResponse,):Promise<SyncSession>functionreloadAfterConflict(session:SyncSession,response:ConflictResponse,):Promise<SyncSession>interface SyncSessioninterfaceSyncSessioninterfaceSyncSessionoperationsSinceBase?: readonly VersionedOperation[] | undefinedoperationsSinceBase?: readonly VersionedOperation[] |undefinedoperationsSinceBase?: readonly VersionedOperation[] |undefinedAPI reference →session: SyncSessionlet session:SyncSessionlet session:SyncSessionGrid.setReadOnly(readOnly: boolean): voidGrid.setReadOnly(readOnly: boolean): voidGrid.setReadOnly(readOnly: boolean): voidUpdate editability without replacing the Grid or clearing session state.API reference →const pending: readonly SyncMutationRecord[]constpending:readonlySyncMutationRecord[]constpending:readonlySyncMutationRecord[]API reference →SyncCoordinator.pendingCommits(): readonly SyncMutationRecord[]SyncCoordinator.pendingCommits(): readonly SyncMutationRecord[]SyncCoordinator.pendingCommits(): readonly SyncMutationRecord[]const remoteOperations: DocumentOp[]constremoteOperations:DocumentOp[]constremoteOperations:DocumentOp[]API reference →operationsSinceBase?: readonly VersionedOperation[]operationsSinceBase?: readonly VersionedOperation[]operationsSinceBase?: readonly VersionedOperation[]API reference →ReadonlyArray<VersionedOperation>.flatMap<DocumentOp, undefined>(callback: (this: undefined, value: VersionedOperation, index: number, array: VersionedOperation[]) => DocumentOp | readonly DocumentOp[], thisArg?: undefined): DocumentOp[]ReadonlyArray<VersionedOperation>.flatMap<DocumentOp, undefined>(callback: (this:undefined, value:VersionedOperation, index:number, array:VersionedOperation[]) => DocumentOp | readonly DocumentOp[], thisArg?:undefined): DocumentOp[]ReadonlyArray<VersionedOperation>.flatMap<DocumentOp, undefined>(callback: (this:undefined, value:VersionedOperation, index:number, array:VersionedOperation[]) => DocumentOp | readonly DocumentOp[], thisArg?:undefined): DocumentOp[]Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1.@param callback A function that accepts up to three arguments. The flatMap method calls the callback function one time for each element in the array.@param thisArg An object to which the this keyword can refer in the callback function. If thisArg is omitted, undefined is used as the this value.entry: VersionedOperationlet entry:VersionedOperationlet entry:VersionedOperationAPI reference →VersionedOperation.operations: readonly DocumentOp[]interfaceVersionedOperation {operations:readonlyDocumentOp[];}interfaceVersionedOperation {operations:readonlyDocumentOp[];}Canonical operations applied by the document engine.API reference →const safeBatches: ({
status: "rebased";
operations: readonly DocumentOp[];
} & {
status: "rebased";
})[]constsafeBatches: ({status:"rebased";operations:readonlyDocumentOp[];} & {status:"rebased";})[]constsafeBatches: ({status:"rebased";operations:readonlyDocumentOp[];} & {status:"rebased";})[]API reference →interface Array<T>interfaceArray<T>interfaceArray<T>type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : anytypeReturnType<Textends (...args:any) =>any> =Textends (...args:any) =>inferR?R:any;typeReturnType<Textends (...args:any) =>any,> =Textends (...args:any) =>inferR?R:any;Obtain the return type of a function typeconst record: SyncMutationRecordconstrecord:SyncMutationRecordconstrecord:SyncMutationRecordAPI reference →const result: DocumentRebaseResultconstresult:DocumentRebaseResultconstresult:DocumentRebaseResultSearch state after the replacement (matches re-scanned against the new data).API reference →PendingCommit.operations: readonly DocumentOp[]interfacePendingCommit {operations:readonlyDocumentOp[];}interfacePendingCommit {operations:readonlyDocumentOp[];}Canonical operations applied by the document engine.API reference →status: "conflict" | "rebased"let status:"conflict"|"rebased"let status:"conflict"|"rebased"const result: {
status: "conflict";
conflict: RebaseConflict;
}constresult: {status:"conflict";conflict:RebaseConflict;}constresult: {status:"conflict";conflict:RebaseConflict;}Search state after the replacement (matches re-scanned against the new data).API reference →conflict: RebaseConflictlet conflict:RebaseConflictlet conflict:RebaseConflictAPI reference →Array<{ status: "rebased"; operations: readonly DocumentOp[]; } & { status: "rebased"; }>.push(...items: ({
status: "rebased";
operations: readonly DocumentOp[];
} & {
status: "rebased";
})[]): numberArray<{ status: "rebased"; operations: readonly DocumentOp[]; } & { status: "rebased"; }>.push(...items: ({status: "rebased";operations: readonly DocumentOp[];} & {status: "rebased";})[]): numberArray<{ status: "rebased"; operations: readonly DocumentOp[]; } & { status: "rebased"; }>.push(...items: ({status: "rebased";operations: readonly DocumentOp[];} & {status: "rebased";})[]): numberAppends new elements to the end of an array, and returns the new length of the array.@param items New elements to add to the array.API reference →const result: {
status: "rebased";
operations: readonly DocumentOp[];
}constresult: {status:"rebased";operations:readonlyDocumentOp[];}constresult: {status:"rebased";operations:readonlyDocumentOp[];}Search state after the replacement (matches re-scanned against the new data).API reference →const latest: WorkbookSnapshotconstlatest:WorkbookSnapshotconstlatest:WorkbookSnapshotAPI reference →snapshot?: WorkbookSnapshot | undefinedsnapshot?: WorkbookSnapshot |undefinedsnapshot?: WorkbookSnapshot |undefinedAPI reference →const pendingStorage: {
remove(documentId: string): Promise<void>;
}constpendingStorage: {remove(documentId:string):Promise<void>;}constpendingStorage: {remove(documentId:string,):Promise<void>;}function remove(documentId: string): Promise<void>functionremove(documentId:string):Promise<void>functionremove(documentId:string,):Promise<void>PendingCommit.clientMutationId: stringinterfacePendingCommit {clientMutationId:string;}interfacePendingCommit {clientMutationId:string;}API reference →SyncCoordinator.destroy(): voidSyncCoordinator.destroy(): voidSyncCoordinator.destroy(): voidRuns immediately before a retained element is removed or replaced.API reference →Grid.destroy(): voidGrid.destroy(): voidGrid.destroy(): voidRuns immediately before a retained element is removed or replaced.API reference →const batch: {
status: "rebased";
operations: readonly DocumentOp[];
} & {
status: "rebased";
}constbatch: {status:"rebased";operations:readonlyDocumentOp[];} & {status:"rebased";}constbatch: {status:"rebased";operations:readonlyDocumentOp[];} & {status:"rebased";}Present when this version is one member of an atomic batch. Every member carries the same clientMutationId and count. Receivers apply the batch only after its last member arrives.API reference →const outcome: ApplyTransactionResultconstoutcome:ApplyTransactionResultconstoutcome:ApplyTransactionResultAPI reference →Grid.applyTransaction(transaction: GridTransaction): ApplyTransactionResultGrid.applyTransaction(transaction: GridTransaction): ApplyTransactionResultGrid.applyTransaction(transaction: GridTransaction): ApplyTransactionResultApply arbitrary patches as one undoable Grid commit. No-op when read-only. Use Store.applyTransaction only for low-level writes that intentionally bypass Grid history and policy.@example import type { Grid } from "@sheetwrite/core"; function setFirstCell(grid: Grid) { return grid.applyTransaction({ patches: [ { op: "set", addr: { sheet: grid.getActiveSheet(), row: 0, col: 0 }, value: { kind: "literal", value: 42 } }, ] }); }@see TransactionAPI reference →GridTransaction.patches: DocumentOp[]interfaceGridTransaction {patches:DocumentOp[];}interfaceGridTransaction {patches:DocumentOp[];}Ordered document operations submitted as one store commit.API reference →operations: readonly DocumentOp[]let operations:readonlyDocumentOp[]let operations:readonlyDocumentOp[]Canonical operations applied by the document engine.API reference →status: "applied" | "conflict" | "rejected" | "noop"let status:"applied"|"conflict"|"rejected"|"noop"let status:|"applied"|"conflict"|"rejected"|"noop"const outcome: {
status: "conflict";
expectedEpoch: number;
actualEpoch: number;
} | {
status: "rejected";
epoch: number;
issues: MutationIssue[];
} | {
status: "noop";
epoch: number;
reason: "empty" | "out-of-bounds" | "incomplete-data" | "read-only";
}constoutcome:| {status:"conflict";expectedEpoch:number;actualEpoch:number;}| {status:"rejected";epoch:number;issues:MutationIssue[];}| {status:"noop";epoch:number;reason:"empty"|"out-of-bounds"|"incomplete-data"|"read-only";}constoutcome:| {status:"conflict";expectedEpoch:number;actualEpoch:number;}| {status:"rejected";epoch:number;issues:MutationIssue[];}| {status:"noop";epoch:number;reason:|"empty"|"out-of-bounds"|"incomplete-data"|"read-only";}API reference →status: "conflict" | "rejected" | "noop"let status:"conflict"|"rejected"|"noop"let status:|"conflict"|"rejected"|"noop"