@tanstack/db
Version:
A reactive client store for building super fast apps on sync
155 lines (154 loc) • 6.68 kB
TypeScript
import { StandardSchemaV1 } from '@standard-schema/spec';
import { CleanupFn, CollectionConfig, LoadSubsetFn, LoadSubsetOptions, LoadSubsetRequestResult } from '../types.js';
import { CollectionImpl } from './index.js';
import { CollectionStateManager } from './state.js';
import { CollectionLifecycleManager } from './lifecycle.js';
import { CollectionEventsManager } from './events.js';
/**
* Internal marker on a sync write: an update that names the whole row, as an
* insert does. The persisted wrapper hands an insert it already judged this
* way. It is not part of the public sync API.
*
* @internal
*/
export declare const syncWriteNamesWholeRow: unique symbol;
export declare class CollectionSyncManager<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
private collection;
private state;
private lifecycle;
private _events;
private config;
private id;
private syncMode;
private writtenRows;
preloadPromise: Promise<void> | null;
private rejectPreload?;
syncCleanupFn: CleanupFn | null;
syncLoadSubsetFn: LoadSubsetFn | null;
syncUnloadSubsetFn: ((options: LoadSubsetOptions) => void) | null;
private pendingLoadSubsetPromises;
private activeLoadSubsetOperation;
private loadSubsetOperations;
private syncStartDeferred;
private syncStartRequested;
private deferredLoadSubsets;
private syncCallbackEpoch;
private syncRunGeneration;
private syncEntryActive;
private loadSubsetDepth;
private pendingSyncEntryCleanup;
/**
* Creates a new CollectionSyncManager instance
*/
constructor(config: CollectionConfig<TOutput, TKey, TSchema, any>, id: string);
setDeps(deps: {
collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>;
state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>;
events: CollectionEventsManager;
}): void;
/**
* Core keeps the object a source writes as the stored row. A source that
* changes that object in place and writes it again has already
* overwritten the previous value core would publish, unless it passes
* `previousValue`. Rewriting an unchanged object stays valid.
*/
private checkReusedRow;
private createDuplicateKeyError;
/** Mark the active sync transaction as changing collection layout. */
markLayoutChange(): void;
/**
* Start the sync process for this collection
* This is called when the collection is first accessed or preloaded
*/
startSync(): void;
/**
* Start a new sync run while everything visible stays: the rows, the
* status, and the node's problems. The new run replaces the rows only with
* a snapshot or its own truncate, and every other write applies on the
* kept rows. The reload completes when a replacement applies, or at the
* run's `markReady()`. A reload in flight returns its promise; a crashed
* Collection cannot reload.
*/
reload(): Promise<void>;
/**
* Run the sync function. A sync function that throws crashes the run and
* rethrows; a ready callback that throws while it runs is returned.
*/
private enterSync;
deferStart(): boolean;
resumeStart(): void;
private getActivePendingSyncTransaction;
/**
* The open transaction that takes a row write or truncate. A replay can
* invalidate an open transaction between writes. Its commit receipt owns
* that failure; later writes, a truncate included, cannot revive it, so
* they are ignored. Its metadata writes need no check: no read includes an
* invalid transaction, and it never applies.
*/
private getWritableSyncTransaction;
/** Record an explicit row metadata write and its place among the operations. */
private writeExplicitRowMetadata;
private createSyncMetadataApi;
/** Whether a caller is still waiting for the initial sync to finish. */
get hasPendingPreload(): boolean;
/**
* Preload the collection data by starting sync if not already started
* Multiple concurrent calls will share the same promise
*/
preload(): Promise<void>;
private getPreloadError;
/**
* Gets whether the collection is currently loading more data
*/
get isLoadingSubset(): boolean;
/** @internal Observe subset requests caused by one imperative operation. */
beginLoadSubsetOperation(): {
wait: () => true | Promise<void>;
cancel: () => void;
};
private waitForLoadSubsetOperation;
private settleLoadSubsetOperation;
private completeLoadSubsetOperation;
private emitLoadingSubsetChange;
/** @internal Attach a relevant existing request to the active operation. */
trackLoadSubsetOperationPromise(promise: Promise<unknown>): void;
/**
* Tracks a load promise for isLoadingSubset state.
* @internal This is for internal coordination (e.g., live-query glue code), not for general use.
*/
trackLoadPromise(promise: Promise<unknown>): void;
/** @internal Generation fence for subscription-owned async work. */
getSyncRunGeneration(): number;
/**
* Requests the sync layer to load more data.
* @param options Options to control what data is being loaded
* @returns If data loading is asynchronous, this method returns a promise that resolves when the data is loaded.
* Returns true if no sync function is configured, if syncMode is 'eager', or if there is no work to do.
*/
loadSubset(options: LoadSubsetOptions): LoadSubsetRequestResult;
/**
* Run the adapter's loadSubset; a reload from inside it rejects. A
* synchronous throw is a rejection: the load failed.
*/
private callLoadSubset;
/**
* Notifies the sync layer that a subset is no longer needed.
* @param options Options that identify what data is being unloaded
*/
unloadSubset(options: LoadSubsetOptions): void;
private wrapCleanupError;
private invokeCleanup;
private registerPendingSyncEntryCleanup;
private completePendingSyncEntryCleanup;
/**
* Retire the current adapter run for a reload: its callbacks, loaders, and
* resources, but not the visible state, the status, or a waiting preload.
*/
private retireSyncRun;
private abortLoadSubsetWork;
cleanup(): true | Promise<void>;
cleanup(onSettled: (failure?: {
error: unknown;
}) => void): boolean;
}