import type { SyncConfig } from '@tanstack/react-db'; import type { DecodedEntity } from '../../../core/index.js'; import type { CollectionItem } from '../../domain/collection-item/index.js'; /** * The mount-time callbacks TanStack hands a collection's `sync` function: the * transaction primitives (`begin`/`write`/`commit`) plus readiness/truncate. This * is the parameter object of `SyncConfig['sync']`, not a separately exported name. */ type SyncCallbacks = Parameters< SyncConfig['sync'] >[0]; /** * The TanStack write side of a mounted collection. Constructed per-mount with * callbacks in hand; the instance's existence is the mounted state. */ type Projector = { projectEntities: (entities: DecodedEntity[]) => void; }; type ProjectorOptions = { deleteKeyOf?: (entity: DecodedEntity) => string | null; }; /** * Builds the per-mount projector that turns accepted deltas into TanStack * update/delete writes. Translation hoists an entity's `value` fields to the top * level and nests its meta under `_meta`. The collection derives live row keys via * its own `getKey`; tombstone deletes carry keys from `deleteKeyOf`. */ export const makeCollectionProjector = ( callbacks: SyncCallbacks>, options: ProjectorOptions = {}, ): Projector => { const toItem = (entity: DecodedEntity): CollectionItem => ({ ...(entity.value as object), _meta: entity.meta, }) as CollectionItem; const projectEntities = (entities: DecodedEntity[]): void => { if (entities.length === 0) return; callbacks.begin(); for (const entity of entities) { if (entity.meta._d) { const key = options.deleteKeyOf?.(entity) ?? null; if (key != null) callbacks.write({ type: 'delete', key }); } else { callbacks.write({ type: 'update', value: toItem(entity) }); } } callbacks.commit(); }; return { projectEntities, }; };