import { FirestoreField } from "./types"; interface ProcessOptions { id: string; fieldDependencyArray?: string[]; shouldBackfill?: (data: Record) => boolean; errorFn?: (e: unknown) => string | void | Promise; batchSize?: number; batchFn?: ( data: Record[], ) => Promise[]>; shouldProcess?: ( oldData: Record, newData: Record, ) => boolean; } type ProcessFunction = ( data: Record, ) => Record | Promise>; type ShouldBackfillFunction = (data: Record) => boolean; type ErrorFunction = (e: unknown) => string | void | Promise; type BatchFunction = ( data: Record[], ) => Promise[]>; type ShouldOnWriteProcessFunction = ( oldData: Record, newData: Record, ) => boolean; export class Process { public readonly processFn: ProcessFunction; public readonly id: string; public readonly fieldDependencyArray: string[]; public readonly batchSize?: number; public readonly shouldBackfill?: ShouldBackfillFunction; public readonly errorFn: ErrorFunction; public readonly batchFn?: BatchFunction; public readonly shouldProcessFn?: ShouldOnWriteProcessFunction; constructor(processFn: ProcessFunction, options: ProcessOptions) { const { id, fieldDependencyArray, shouldBackfill, errorFn, batchFn, batchSize, shouldProcess, } = options; this.id = id; this.fieldDependencyArray = fieldDependencyArray; this.shouldBackfill = shouldBackfill; if (errorFn) { this.errorFn = errorFn; } else { this.errorFn = console.error; } this.batchFn = batchFn ? async (data) => batchFn(data) : undefined; if (batchSize) { this.batchSize = batchSize; } this.shouldProcessFn = shouldProcess; this.processFn = async (data) => processFn(data); } async batchProcess( data: Record[], ): Promise[]> { if (this.batchFn) { return this.batchFn(data); } const allSettled = Promise.allSettled(data.map(this.processFn)); const results = await allSettled; const output: Record[] = []; for (const result of results) { if (result.status === "fulfilled") { output.push(result.value); } else { const error = result.reason; if (this.errorFn) { await this.errorFn(error); } } } return output; } shouldProcess( oldData: Record, newData: Record, ): boolean { if (this.shouldProcessFn && !this.shouldProcessFn(oldData, newData)) { return false; } for (const field of this.fieldDependencyArray) { const newValue = newData && newData[field]; const oldValue = oldData && oldData[field]; if (newValue !== oldValue) { return true; // Process if any field value has changed. } } return false; } }