UNPKG

@mbc-cqrs-serverless/core

Version:
109 lines (108 loc) 5.1 kB
import { SessionService } from '../data-store/session.service'; import { CommandModel, CommandModuleOptions, DataListEntity, DataModel, DetailKey, ICommandOptions } from '../interfaces'; import { CommandService } from './command.service'; import { DataService } from './data.service'; export interface IMergeOptions<TItem extends { id: string; }> { /** * Set true to merge pending async commands into the result. * If false/undefined -> acts as a simple passthrough. */ latestFlg?: boolean; /** * Optional: Extract version from the query result item (e.g. RDS row). * If the provided version is >= session.version, the DynamoDB Data Table * check is skipped to reduce latency. */ getVersion?: (item: TItem) => number | undefined; /** * Transform a CommandModel into the same shape as a TItem returned by the query. * The `existing` parameter is provided for update cases to merge unchanged fields. */ transformCommand: (cmd: CommandModel, existing?: TItem) => TItem; /** * Optional filter applied AFTER transformCommand for "create-new" rows. * Prevents newly created items from appearing in lists where they don't match the current search criteria. */ matchesFilter?: (item: TItem) => boolean; } export declare class Repository { private readonly options; private readonly dataService; private readonly commandService; private readonly sessionService; private readonly logger; private readonly moduleTableName; constructor(options: CommandModuleOptions, dataService: DataService, commandService: CommandService, sessionService: SessionService); /** * Get a single data item, merging a pending async command when a session exists. */ getItem(key: DetailKey, options: ICommandOptions): Promise<DataModel>; /** * List items from the DynamoDB data table with an optional merge of pending async commands. * * When `mergeOptions.latestFlg` is true, the result is augmented with any * pending writes the current user has not yet seen reflected in the data table: * * - **update** — the existing item is replaced in-place, preserving its * position in the list. * - **delete** — the item is removed from the result. * - **create-new** — the item is prepended to the top of the list, sorted by * `updatedAt` descending when there are multiple pending creates. * * > **Sort-order note**: Prepended create-new items are not integrated into * > the caller's sort order (e.g. by `name` or `code`). They will appear * > at the top of the result until the async DynamoDB Stream sync completes and * > the next read returns fully sorted data. This is intentional — the * > guarantee is visibility, not sort position. */ listItemsByPk(pk: string, opts?: { sk?: { skExpression: string; skAttributeValues: Record<string, string>; skAttributeNames?: Record<string, string>; }; startFromSk?: string; limit?: number; order?: 'asc' | 'desc'; }, mergeOptions?: { latestFlg?: boolean; }, options?: ICommandOptions): Promise<DataListEntity>; /** * List items from an external source (e.g., RDS/Elasticsearch) with an * optional merge of pending async commands. * * When `mergeOptions.latestFlg` is true, the result is augmented with any * pending writes the current user has not yet seen reflected in the query * source: * * - **update** — the existing item is replaced in-place via `transformCommand`, * preserving its position. `existing` is passed to the transformer so join * data (e.g. related RDS rows) can be carried over. * - **delete** — the item is removed and `total` is decremented. * - **create-new** — the item is transformed, optionally filtered by * `matchesFilter`, and prepended to the top of the result sorted by * `updatedAt` descending. `total` is incremented. * * > **Sort-order note**: Prepended create-new items are not integrated into * > the caller's sort order (e.g. by `name` or `code`). They will appear * > at the top of the result until the async Stream → RDS sync completes and * > the next read returns fully sorted data. This is intentional — the * > guarantee is visibility, not sort position. * * > **Pagination note**: `total` reflects the adjusted count after merge. * > However, prepended items sit outside the RDS `LIMIT`/`OFFSET` window, so * > on page 2+ the caller may see a one-item overlap or gap until sync * > completes. For most UX patterns (redirect-after-write, optimistic UI) * > this is not noticeable. */ listItems<TItem extends { id: string; }>(query: () => Promise<{ total: number; items: TItem[]; }>, mergeOptions?: IMergeOptions<TItem>, options?: ICommandOptions): Promise<{ total: number; items: TItem[]; }>; }