UNPKG

firestore-snapshot-utils

Version:

[![github license](https://img.shields.io/github/license/ericvera/firestore-snapshot-utils.svg?style=flat-square)](https://github.com/ericvera/firestore-snapshot-utils/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/firestore-snapshot-ut

94 lines (93 loc) 4.53 kB
import { ascCompare } from './internal/ascCompare.js'; import { AddedDocumentSnapshot, ModifiedDocumentSnapshot, RemovedDocumentSnapshot, UnmodifiedDocumentSnapshot, } from './internal/DocumentChangeSnapshot.js'; import { extractTimestamps, } from './internal/extractTimestamps.js'; import { maskProps } from './internal/maskProps.js'; import { normalizeData } from './internal/normalizeData.js'; export const getDBSnapshotChanges = (beforeDocs, afterDocs, // Object with key as a collection name and value as an array of keys to mask maskKeys = {}, debugOptions = {}) => { const result = { added: [], removed: [], modified: [], unmodified: [], }; // Step 1: Extract all unique timestamps from all documents let timestampValues = new Set(); // Collect timestamps from beforeDocs beforeDocs.forEach((doc) => { // Merge the extracted timestamps into our set const timestampDebugOptions = { logTimestamps: debugOptions.logTimestamps ?? false, docPath: doc.ref.path, }; const docTimestamps = extractTimestamps(doc.data(), timestampDebugOptions); timestampValues = new Set([...timestampValues, ...docTimestamps]); }); // Collect timestamps from afterDocs afterDocs.forEach((doc) => { // Merge the extracted timestamps into our set const timestampDebugOptions = { logTimestamps: debugOptions.logTimestamps ?? false, docPath: doc.ref.path, }; const docTimestamps = extractTimestamps(doc.data(), timestampDebugOptions); timestampValues = new Set([...timestampValues, ...docTimestamps]); }); // Step 2: Sort timestamps chronologically const sortedTimestamps = Array.from(timestampValues).sort(); // Log sorted timestamps if debug is enabled if (debugOptions.logTimestamps && sortedTimestamps.length > 0) { console.log('\nSorted timestamps (chronological order):'); sortedTimestamps.forEach((timestamp, index) => { // Convert timestamp string back to date for readable output console.log(` ${index.toString().padStart(3, '0')}: ${timestamp}`); }); console.log(''); } // Step 3: Normalize all documents by replacing timestamps const beforeDocsNormalized = beforeDocs .sort(ascCompare((a) => a.updateTime.valueOf())) .map((doc) => { const normalizedData = normalizeData(doc.data(), sortedTimestamps); return { doc, normalizedData }; }); const afterDocsNormalized = afterDocs .sort(ascCompare((a) => a.updateTime.valueOf())) .map((doc) => { const normalizedData = normalizeData(doc.data(), sortedTimestamps); return { doc, normalizedData }; }); // Process added and modified docs afterDocsNormalized.forEach(({ doc: afterDoc, normalizedData: afterNormalizedData }) => { const afterMaskedData = maskProps(afterNormalizedData, afterDoc.ref.parent.id, maskKeys); // Get added docs const isAdded = beforeDocs.every((beforeDoc) => beforeDoc.ref.path !== afterDoc.ref.path); if (isAdded) { result.added.push(new AddedDocumentSnapshot(afterDoc, afterMaskedData, afterDocs)); return; } // Get modified and unmodified docs const beforeDocData = beforeDocsNormalized.find(({ doc }) => doc.ref.path === afterDoc.ref.path); if (!beforeDocData) { return; } const { doc: beforeDoc, normalizedData: beforeNormalizedData } = beforeDocData; const beforeMaskedData = maskProps(beforeNormalizedData, beforeDoc.ref.parent.id, maskKeys); if (!beforeDoc.updateTime.isEqual(afterDoc.updateTime)) { result.modified.push(new ModifiedDocumentSnapshot(beforeDoc, afterDoc, beforeMaskedData, afterMaskedData, afterDocs)); } else { result.unmodified.push(new UnmodifiedDocumentSnapshot(beforeDoc, afterDocs)); } }); // Process removed docs beforeDocsNormalized.forEach(({ doc: beforeDoc, normalizedData: beforeNormalizedData }) => { const isRemoved = afterDocs.every((afterDoc) => afterDoc.ref.path !== beforeDoc.ref.path); if (isRemoved) { const beforeMaskedData = maskProps(beforeNormalizedData, beforeDoc.ref.parent.id, maskKeys); result.removed.push(new RemovedDocumentSnapshot(beforeDoc, beforeMaskedData, beforeDocs)); } }); return result; };