UNPKG

mongochangestream

Version:

Sync MongoDB collections via change streams into any database.

127 lines (126 loc) 4.73 kB
import type { AggregationCursor, ChangeStream, ChangeStreamDocument, ChangeStreamInsertDocument, Document, MongoAPIError, MongoServerError } from 'mongodb'; import { type Options as RetryOptions } from 'p-retry'; import type { LastFlush, QueueStats } from 'prom-utils'; export type Cursor = ChangeStream | AggregationCursor; export type JSONSchema = Record<string, any>; type MaybePromise<T> = T | Promise<T>; /** * Extract the operationType field from a type. * Create a union of all possible operation types. */ type ExtractOperationType<T> = T extends { operationType: infer Type; } ? Type : never; /** * Mapping from operation type to document type. For example, if the operation * type is 'insert', the document type is ChangeStreamInsertDocument. */ type OperationTypeMap = { [K in ExtractOperationType<ChangeStreamDocument>]: Extract<ChangeStreamDocument, { operationType: K; }>; }; /** * Union of all possible operation types - 'insert' | 'update' | 'delete' etc. */ export type OperationType = keyof OperationTypeMap; /** * Extract the specific document types from an array of operation types. * For example, if the type parameter is ['insert', 'update'], the return type * is ChangeStreamInsertDocument | ChangeStreamUpdateDocument. */ export type DocumentsForOperationTypes<T extends OperationType[] | undefined> = T extends OperationType[] ? OperationTypeMap[T[number]] : ChangeStreamDocument; export type ProcessChangeStreamRecords<T extends OperationType[] | undefined = undefined> = (docs: DocumentsForOperationTypes<T>[]) => MaybePromise<void>; export type ProcessInitialScanRecords = (docs: ChangeStreamInsertDocument[]) => MaybePromise<void>; export interface SyncOptions { /** Field paths to omit. */ omit?: string[]; /** * Added to all Redis keys to allow the same collection * to be synced in parallel. Otherwise, the Redis keys * would be the same for a given collection and parallel * syncing jobs would overwrite each other. */ uniqueId?: string; /** * Maximum time to pause in ms after calling `pausable.pause()`. * By default, pausing is indefinite. */ maxPauseTime?: number; /** * Options for handling retry logic when calling `processRecords`. * Defaults to `{ minTimeout: 30s, maxTimeout: 1h, retries: 30 }`. * Options passed here will be applied over the default options. */ retry?: RetryOptions; } export interface SortField<T> { field: string; /** Function to serialize value to string. */ serialize: (x: T) => string; deserialize: (x: string) => T; /** Sort order: asc or desc. Defaults to asc */ order?: 'asc' | 'desc'; } export interface ScanOptions<T = any> { /** Defaults to _id */ sortField?: SortField<T>; /** Extend the pipeline. Be careful not to exclude the sort field or change the sort order. */ pipeline?: Document[]; } export interface ChangeStreamOptions<T extends OperationType[] | undefined = undefined> { pipeline?: Document[]; operationTypes?: T; } export interface ChangeOptions { /** How often to retrieve the schema and look for a change. */ interval?: number; /** * Remove fields that are not used when converting the schema * in a downstream library like mongo2elastic or mongo2crate. * Preserves bsonType, properties, additionalProperties, items, and enum. */ shouldRemoveUnusedFields?: boolean; } export type Events = 'cursorError' | 'resync' | 'schemaChange' | 'stateChange' | 'initialScanComplete' | 'stats' | 'processError'; export interface ResyncEvent { type: 'resync'; } export interface SchemaChangeEvent { type: 'schemaChange'; previousSchema?: JSONSchema; currentSchema: JSONSchema; } export interface StateChangeEvent { type: 'stateChange'; name: string; from: string; to: string; } export interface InitialScanCompleteEvent { type: 'initialScanComplete'; } export type CursorError = MongoServerError | MongoAPIError; export interface CursorErrorEvent { type: 'cursorError'; name: 'runInitialScan' | 'processChangeStream'; error: CursorError; } export interface ProcessErrorEvent { type: 'processError'; name: 'runInitialScan' | 'processChangeStream'; error: unknown; } /** * If `maxItemsPerSec` is not set, `stats.itemsPerSec` will be 0. * If `maxBytesPerSec` is not set, `stats.bytesPerSec` will be 0. */ export interface StatsEvent { type: 'stats'; name: 'runInitialScan' | 'processChangeStream'; stats: QueueStats; lastFlush?: LastFlush; } export type State = 'starting' | 'started' | 'stopping' | 'stopped'; export type SimpleState = 'started' | 'stopped'; export {};