UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

155 lines (154 loc) • 6.68 kB
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; }