@dereekb/firebase 13.0.5 → 13.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -662,31 +662,33 @@ function extendFirestoreCollectionWithSingleDocumentAccessor(x, singleItemIdenti
662
662
  }
663
663
 
664
664
  /**
665
- * Creates an array of new FirestoreDocument instances without creating them in Firestore.
665
+ * Creates an array of new {@link FirestoreDocument} instances without persisting them to Firestore.
666
666
  *
667
- * @template T - The document data type
668
- * @template D - The FirestoreDocument implementation type
669
- * @param documentAccessor - The document accessor to use for creating document instances
670
- * @param count - The number of document instances to create
671
- * @returns An array of new document instances
667
+ * Each document is allocated a unique auto-generated ID via {@link FirestoreDocumentAccessor.newDocument},
668
+ * but no data is written to the database. Useful for preparing document references in bulk before
669
+ * deciding what data to write.
670
+ *
671
+ * @param documentAccessor - Accessor that provides the `newDocument()` factory method
672
+ * @param count - Number of document instances to create
673
+ * @returns Array of `count` new document instances, each with a unique auto-generated ID
672
674
  */
673
675
  function newDocuments(documentAccessor, count) {
674
676
  return util.makeWithFactory(() => documentAccessor.newDocument(), count);
675
677
  }
676
678
  /**
677
- * Creates multiple documents in Firestore and returns the document instances.
679
+ * Creates and optionally persists multiple Firestore documents in sequence.
678
680
  *
679
- * For each document, this function:
680
- * 1. Creates a new document instance using the specified or default factory
681
- * 2. Calls the init function with the document instance
682
- * 3. If init returns data, creates the document in Firestore
683
- * 4. Returns all document instances, whether they were created in Firestore or not
681
+ * Uses {@link performMakeLoop} to iterate `count` times. For each iteration:
682
+ * 1. A new document instance is created (via `make.newDocument` or the default factory)
683
+ * 2. `make.init(i, document)` is awaited to produce initial data
684
+ * 3. If init returns non-nullish data, `document.accessor.create(data)` persists it
685
+ * 4. The document instance is collected regardless of whether it was persisted
684
686
  *
685
- * @template T - The document data type
686
- * @template D - The FirestoreDocument implementation type
687
- * @param documentAccessor - The document accessor to use for creating document instances
688
- * @param make - Parameters for document creation and initialization
689
- * @returns A promise that resolves to an array of document instances
687
+ * Documents are created sequentially (not in parallel) to allow index-dependent logic.
688
+ *
689
+ * @param documentAccessor - Accessor providing the document factory and collection context
690
+ * @param make - Configuration controlling count, factory, and initialization
691
+ * @returns Promise resolving to all created document instances (length === `make.count`)
690
692
  */
691
693
  function makeDocuments(documentAccessor, make) {
692
694
  const newDocumentFn = make.newDocument ?? (() => documentAccessor.newDocument());
@@ -703,65 +705,52 @@ function makeDocuments(documentAccessor, make) {
703
705
  });
704
706
  }
705
707
  /**
706
- * Retrieves DocumentSnapshots for an array of documents in parallel.
708
+ * Fetches {@link DocumentSnapshot}s for multiple documents in parallel using {@link runAsyncTasksForValues}.
707
709
  *
708
- * This is useful for fetching the current state of multiple documents at once.
710
+ * Each document's `accessor.get()` is called concurrently. The returned array preserves
711
+ * the same ordering as the input `documents` array.
709
712
  *
710
- * @template D - The FirestoreDocument implementation type
711
- * @param documents - Array of document instances to get snapshots for
712
- * @returns Promise that resolves to an array of DocumentSnapshots in the same order as the input documents
713
+ * @param documents - Documents to fetch snapshots for
714
+ * @returns Snapshots in the same order as the input array
713
715
  */
714
716
  function getDocumentSnapshots(documents) {
715
717
  return util.runAsyncTasksForValues(documents, (x) => x.accessor.get());
716
718
  }
717
719
  /**
718
- * Creates a document-snapshot pair from a document instance.
719
- *
720
- * Fetches the current snapshot of the document and returns both the original
721
- * document instance and the snapshot together.
720
+ * Fetches the current snapshot of a single document and pairs it with the document instance.
722
721
  *
723
- * @template D - The FirestoreDocument implementation type
724
- * @param document - The document instance to get a snapshot for
725
- * @returns Promise that resolves to a document-snapshot pair
722
+ * @param document - The document to fetch
723
+ * @returns A pair containing the document and its snapshot
726
724
  */
727
725
  function getDocumentSnapshotPair(document) {
728
726
  return document.accessor.get().then((snapshot) => ({ document, snapshot }));
729
727
  }
730
728
  /**
731
- * Creates document-snapshot pairs for an array of documents in parallel.
729
+ * Fetches snapshots for multiple documents in parallel and pairs each with its document instance.
732
730
  *
733
- * Fetches the current snapshot of each document and returns pairs containing both
734
- * the original document instances and their snapshots.
735
- *
736
- * @template D - The FirestoreDocument implementation type
737
- * @param documents - Array of document instances to get snapshots for
738
- * @returns Promise that resolves to an array of document-snapshot pairs in the same order as the input documents
731
+ * @param documents - Documents to fetch
732
+ * @returns Pairs in the same order as the input array
739
733
  */
740
734
  function getDocumentSnapshotPairs(documents) {
741
735
  return util.runAsyncTasksForValues(documents, getDocumentSnapshotPair);
742
736
  }
743
737
  /**
744
- * Creates a document-snapshot-data triplet from a document instance.
745
- *
746
- * Fetches the current snapshot of the document, extracts its data with ID and key fields,
747
- * and returns all three together in a single object.
738
+ * Fetches a document's snapshot, extracts its data with `id`/`key` fields, and returns all three as a triplet.
748
739
  *
749
- * @template D - The FirestoreDocument implementation type
750
- * @param document - The document instance to get data for
751
- * @returns Promise that resolves to a document-snapshot-data triplet
740
+ * @param document - The document to fetch
741
+ * @returns A triplet of document, snapshot, and data (data may be `undefined` if the document doesn't exist)
752
742
  */
753
743
  function getDocumentSnapshotDataPair(document) {
754
744
  return document.accessor.get().then((snapshot) => ({ document, snapshot, data: documentDataWithIdAndKey(snapshot) }));
755
745
  }
756
746
  /**
757
- * Creates document-snapshot-data triplets for an array of documents in parallel.
747
+ * Fetches snapshot-data triplets for multiple documents in parallel.
758
748
  *
759
- * Fetches the current snapshot of each document, extracts its data with ID and key fields,
760
- * and returns triplets containing all three components for each document.
749
+ * Processes up to 200 documents concurrently via {@link runAsyncTasksForValues}.
750
+ * The returned array preserves the same ordering as the input.
761
751
  *
762
- * @template D - The FirestoreDocument implementation type
763
- * @param documents - Array of document instances to get data for
764
- * @returns Promise that resolves to an array of document-snapshot-data triplets in the same order as the input documents
752
+ * @param documents - Documents to fetch
753
+ * @returns Triplets in the same order as the input array
765
754
  */
766
755
  function getDocumentSnapshotDataPairs(documents) {
767
756
  return util.runAsyncTasksForValues(documents, getDocumentSnapshotDataPair, {
@@ -769,18 +758,27 @@ function getDocumentSnapshotDataPairs(documents) {
769
758
  });
770
759
  }
771
760
  /**
772
- * Creates document-snapshot-data triplets for an array of documents and filters out those without data.
761
+ * Fetches snapshot-data triplets for multiple documents and filters out those that don't exist in Firestore.
773
762
  *
774
- * This is a convenience function that fetches data for all documents and then returns
775
- * only the triplets for documents that actually exist in Firestore.
763
+ * Convenience wrapper around {@link getDocumentSnapshotDataPairs} that removes entries where `data` is nullish,
764
+ * returning only {@link FirestoreDocumentSnapshotDataPairWithData} results.
776
765
  *
777
- * @template D - The FirestoreDocument implementation type
778
- * @param documents - Array of document instances to get data for
779
- * @returns Promise that resolves to an array of document-snapshot-data triplets for existing documents only
766
+ * @param documents - Documents to fetch
767
+ * @returns Triplets for documents that exist, in their original relative order
780
768
  */
781
769
  function getDocumentSnapshotDataPairsWithData(documents) {
782
770
  return getDocumentSnapshotDataPairs(documents).then((x) => x.filter((y) => !!y.data));
783
771
  }
772
+ /**
773
+ * Fetches raw snapshot data tuples for multiple documents in parallel.
774
+ *
775
+ * Unlike {@link getDocumentSnapshotDataPairs}, this returns a lightweight `[document, data]` tuple
776
+ * and does not inject `id`/`key` fields onto the data. The `data` value is the raw result of
777
+ * `snapshot.data()` and may be `undefined` for non-existent documents.
778
+ *
779
+ * @param documents - Documents to fetch
780
+ * @returns Tuples in the same order as the input array
781
+ */
784
782
  function getDocumentSnapshotDataTuples(documents) {
785
783
  return util.runAsyncTasksForValues(documents, (document) => document.accessor.get().then((snapshot) => [document, snapshot.data()]));
786
784
  }
@@ -794,33 +792,109 @@ function getDataFromDocumentSnapshots(snapshots, withId = true) {
794
792
  const mapFn = documentDataFunction(withId);
795
793
  return util.filterMaybeArrayValues(snapshots.map(mapFn));
796
794
  }
795
+ /**
796
+ * Creates {@link FirestoreDocument} instances for all documents in a {@link QuerySnapshot}.
797
+ *
798
+ * Maps each document in `snapshots.docs` to a loaded document via `accessor.loadDocument(ref)`.
799
+ * No additional data fetching occurs; the documents are loaded from their existing references.
800
+ *
801
+ * @param accessor - Accessor to load documents with
802
+ * @param snapshots - Query snapshot containing the document references
803
+ * @returns Document instances in the same order as the query results
804
+ */
797
805
  function loadDocumentsForSnapshots(accessor, snapshots) {
798
806
  return snapshots.docs.map((x) => accessor.loadDocument(x.ref));
799
807
  }
808
+ /**
809
+ * Extracts {@link DocumentReference}s from arbitrary values and loads them as {@link FirestoreDocument} instances.
810
+ *
811
+ * Convenience function that maps values through `getRef` then delegates to {@link loadDocumentsForDocumentReferences}.
812
+ *
813
+ * @param accessor - Accessor to load documents with
814
+ * @param values - Source values to extract references from
815
+ * @param getRef - Extracts a document reference from each value
816
+ * @returns Document instances in the same order as the input values
817
+ */
800
818
  function loadDocumentsForDocumentReferencesFromValues(accessor, values, getRef) {
801
819
  return loadDocumentsForDocumentReferences(accessor, values.map(getRef));
802
820
  }
821
+ /**
822
+ * Alias for {@link loadDocumentsForDocumentReferencesFromValues}.
823
+ */
803
824
  const loadDocumentsForValues = loadDocumentsForDocumentReferencesFromValues;
825
+ /**
826
+ * Loads {@link FirestoreDocument} instances from an array of {@link DocumentReference}s.
827
+ *
828
+ * Each reference is passed to `accessor.loadDocument()` to create a document wrapper.
829
+ * No network calls are made; this only creates in-memory document instances bound to the given references.
830
+ *
831
+ * @param accessor - Accessor to load documents with
832
+ * @param refs - Document references to load
833
+ * @returns Document instances in the same order as the input references
834
+ */
804
835
  function loadDocumentsForDocumentReferences(accessor, refs) {
805
836
  return refs.map((x) => accessor.loadDocument(x));
806
837
  }
838
+ /**
839
+ * Extracts {@link FirestoreModelKey}s from arbitrary values and loads them as {@link FirestoreDocument} instances.
840
+ *
841
+ * Convenience function that maps values through `getKey` then delegates to {@link loadDocumentsForKeys}.
842
+ *
843
+ * @param accessor - Accessor to load documents with
844
+ * @param values - Source values to extract keys from
845
+ * @param getKey - Extracts a model key (full Firestore path) from each value
846
+ * @returns Document instances in the same order as the input values
847
+ */
807
848
  function loadDocumentsForKeysFromValues(accessor, values, getKey) {
808
849
  return loadDocumentsForKeys(accessor, values.map(getKey));
809
850
  }
851
+ /**
852
+ * Loads {@link FirestoreDocument} instances from an array of full Firestore document paths (keys).
853
+ *
854
+ * Each key is passed to `accessor.loadDocumentForKey()`. No network calls are made.
855
+ *
856
+ * @param accessor - Accessor to load documents with
857
+ * @param keys - Full Firestore document paths (e.g. `'users/abc123'`)
858
+ * @returns Document instances in the same order as the input keys
859
+ */
810
860
  function loadDocumentsForKeys(accessor, keys) {
811
861
  return keys.map((x) => accessor.loadDocumentForKey(x));
812
862
  }
863
+ /**
864
+ * Extracts {@link FirestoreModelId}s from arbitrary values and loads them as {@link FirestoreDocument} instances.
865
+ *
866
+ * Convenience function that maps values through `getId` then delegates to {@link loadDocumentsForIds}.
867
+ * Requires a full {@link FirestoreDocumentAccessor} (not limited) because loading by ID requires collection context.
868
+ *
869
+ * @param accessor - Accessor to load documents with (must have collection context for ID resolution)
870
+ * @param values - Source values to extract IDs from
871
+ * @param getId - Extracts a model ID from each value
872
+ * @returns Document instances in the same order as the input values
873
+ */
813
874
  function loadDocumentsForIdsFromValues(accessor, values, getId) {
814
875
  return loadDocumentsForIds(accessor, values.map(getId));
815
876
  }
877
+ /**
878
+ * Loads {@link FirestoreDocument} instances from an array of document IDs relative to the accessor's collection.
879
+ *
880
+ * Each ID is passed to `accessor.loadDocumentForId()`. Requires a full {@link FirestoreDocumentAccessor}
881
+ * because ID-based loading needs the collection reference to resolve the full path. No network calls are made.
882
+ *
883
+ * @param accessor - Accessor to load documents with (must have collection context)
884
+ * @param ids - Document IDs within the accessor's collection
885
+ * @returns Document instances in the same order as the input IDs
886
+ */
816
887
  function loadDocumentsForIds(accessor, ids) {
817
888
  return ids.map((x) => accessor.loadDocumentForId(x));
818
889
  }
819
890
  /**
820
- * Used to make a FirestoreDocumentLoader.
891
+ * Creates a {@link FirestoreDocumentLoader} from a {@link LimitedFirestoreDocumentAccessorContextExtension}.
821
892
  *
822
- * @param accessorContext
823
- * @returns
893
+ * The returned loader resolves the appropriate accessor based on whether a transaction is provided,
894
+ * then delegates to {@link loadDocumentsForDocumentReferences}.
895
+ *
896
+ * @param accessorContext - Context that provides accessors for both default and transactional use
897
+ * @returns A loader function that converts document references to document instances
824
898
  */
825
899
  function firestoreDocumentLoader(accessorContext) {
826
900
  return (references, transaction) => {
@@ -829,10 +903,16 @@ function firestoreDocumentLoader(accessorContext) {
829
903
  };
830
904
  }
831
905
  /**
832
- * Used to make a FirestoreDocumentSnapshotPairsLoader.
906
+ * Creates a {@link FirestoreDocumentSnapshotPairsLoader} from a {@link LimitedFirestoreDocumentAccessorContextExtension}.
833
907
  *
834
- * @param accessorContext
835
- * @returns
908
+ * The returned loader resolves the appropriate accessor based on whether a transaction is provided,
909
+ * then maps each snapshot to a {@link FirestoreDocumentSnapshotDataPair} using {@link firestoreDocumentSnapshotPairsLoaderInstance}.
910
+ *
911
+ * Also satisfies the {@link FirestoreQueryDocumentSnapshotPairsLoader} type since the implementation
912
+ * handles both {@link DocumentSnapshot} and {@link QueryDocumentSnapshot} inputs.
913
+ *
914
+ * @param accessorContext - Context that provides accessors for both default and transactional use
915
+ * @returns A loader function that converts snapshots to document-snapshot-data pairs
836
916
  */
837
917
  function firestoreDocumentSnapshotPairsLoader(accessorContext) {
838
918
  return (snapshots, transaction) => {
@@ -842,10 +922,15 @@ function firestoreDocumentSnapshotPairsLoader(accessorContext) {
842
922
  };
843
923
  }
844
924
  /**
845
- * Used to make a FirestoreDocumentSnapshotPairsLoader.
925
+ * Creates a {@link FirestoreDocumentSnapshotPairsLoaderInstance} bound to a specific accessor.
846
926
  *
847
- * @param accessorContext
848
- * @returns
927
+ * The returned function converts a snapshot into a {@link FirestoreDocumentSnapshotDataPair} by:
928
+ * 1. Extracting data with `id`/`key` fields via {@link documentDataWithIdAndKey}
929
+ * 2. Loading the document from the snapshot's reference via `accessor.loadDocument()`
930
+ * 3. Combining all three into a single pair object
931
+ *
932
+ * @param accessor - The accessor to bind for document loading
933
+ * @returns A reusable function that converts snapshots to pairs using the bound accessor
849
934
  */
850
935
  function firestoreDocumentSnapshotPairsLoaderInstance(accessor) {
851
936
  const fn = ((snapshot) => {
@@ -862,10 +947,10 @@ function firestoreDocumentSnapshotPairsLoaderInstance(accessor) {
862
947
  return fn;
863
948
  }
864
949
  /**
865
- * Used to make a FirestoreQueryDocumentSnapshotPairsLoader.
950
+ * Alias for {@link firestoreDocumentSnapshotPairsLoader}, typed specifically as a {@link FirestoreQueryDocumentSnapshotPairsLoader}.
866
951
  *
867
- * @param accessorContext
868
- * @returns
952
+ * Use this when you know all input snapshots are from a query and want the stronger return type
953
+ * that guarantees `data` is non-nullish.
869
954
  */
870
955
  const firestoreQueryDocumentSnapshotPairsLoader = firestoreDocumentSnapshotPairsLoader;
871
956
  function documentData(snapshot, withId = false) {
@@ -887,11 +972,14 @@ function documentDataWithIdAndKey(snapshot) {
887
972
  return data;
888
973
  }
889
974
  /**
890
- * Sets the id and key values from the snapshot onto the input data.
975
+ * Mutates the input data object to include `id` and `key` fields from a {@link DocumentSnapshot}.
891
976
  *
892
- * @param data
893
- * @param snapshot
894
- * @returns
977
+ * Sets `data.id` to `snapshot.id` and `data.key` to `snapshot.ref.path`. The data object
978
+ * is modified in-place and also returned for chaining convenience.
979
+ *
980
+ * @param data - The data object to augment (mutated in-place)
981
+ * @param snapshot - Source of the `id` and `key` values
982
+ * @returns The same data object, now typed as {@link DocumentDataWithIdAndKey}
895
983
  */
896
984
  function setIdAndKeyFromSnapshotOnDocumentData(data, snapshot) {
897
985
  const target = data;
@@ -900,11 +988,15 @@ function setIdAndKeyFromSnapshotOnDocumentData(data, snapshot) {
900
988
  return target;
901
989
  }
902
990
  /**
903
- * Sets the id and key values from the snapshot onto the input data.
991
+ * Mutates the input data object to include `id` and `key` fields from a model reference.
904
992
  *
905
- * @param data
906
- * @param snapshot
907
- * @returns
993
+ * Similar to {@link setIdAndKeyFromSnapshotOnDocumentData}, but sources the values from a
994
+ * {@link FirestoreModelKeyRef} & {@link FirestoreModelIdRef} instead of a snapshot.
995
+ * The data object is modified in-place and also returned for chaining convenience.
996
+ *
997
+ * @param data - The data object to augment (mutated in-place)
998
+ * @param modelRef - Source of the `id` and `key` values
999
+ * @returns The same data object, now typed as {@link DocumentDataWithIdAndKey}
908
1000
  */
909
1001
  function setIdAndKeyFromKeyIdRefOnDocumentData(data, modelRef) {
910
1002
  const target = data;
@@ -913,47 +1005,156 @@ function setIdAndKeyFromKeyIdRefOnDocumentData(data, modelRef) {
913
1005
  return target;
914
1006
  }
915
1007
  /**
916
- * MappedUseAsyncFunction to load a snapshot from the input document and use it.
1008
+ * Fetches a document's snapshot and passes it to a `use` callback, following the {@link UseAsync} pattern.
917
1009
  *
918
- * @param document
919
- * @param use
920
- * @param defaultValue
921
- * @returns
1010
+ * If `document` is nullish, the `use` callback is not invoked and `defaultValue` is returned instead.
1011
+ * If `document` exists but the snapshot is nullish (shouldn't happen in practice), `defaultValue` is also used.
1012
+ *
1013
+ * @param document - The document to fetch, or nullish to skip
1014
+ * @param use - Callback that receives the fetched snapshot and returns a result
1015
+ * @param defaultValue - Fallback value when `document` is nullish or the snapshot is unavailable
1016
+ * @returns The result of `use`, or the default value
922
1017
  */
923
1018
  async function useDocumentSnapshot(document, use, defaultValue) {
924
1019
  const snapshot = await document?.accessor.get();
925
1020
  return util.useAsync(snapshot, use, defaultValue);
926
1021
  }
927
1022
  /**
928
- * MappedUseAsyncFunction to load snapshot data from the input document and use it.
1023
+ * Fetches a document's snapshot data (via `snapshot.data()`) and passes it to a `use` callback.
1024
+ *
1025
+ * Wraps {@link useDocumentSnapshot} with a mapping step that extracts raw data from the snapshot.
1026
+ * If the document doesn't exist (data is `undefined`), the `use` callback is not invoked
1027
+ * and `defaultValue` is returned instead.
1028
+ *
1029
+ * @param document - The document to fetch, or nullish to skip
1030
+ * @param use - Callback that receives the snapshot data and returns a result
1031
+ * @param defaultValue - Fallback value when the document is nullish or doesn't exist
1032
+ * @returns The result of `use`, or the default value
929
1033
  */
930
1034
  const useDocumentSnapshotData = util.wrapUseAsyncFunction(useDocumentSnapshot, (x) => x.data());
931
1035
  // MARK: Key Accessors
1036
+ /**
1037
+ * Extracts the document ID ({@link FirestoreModelId}) from a {@link FirestoreDocument}.
1038
+ *
1039
+ * Useful as a mapper function, e.g. `documents.map(firestoreModelIdFromDocument)`.
1040
+ *
1041
+ * @param document - The document to extract the ID from
1042
+ * @returns The document's ID (the last segment of its Firestore path)
1043
+ */
932
1044
  function firestoreModelIdFromDocument(document) {
933
1045
  return document.id;
934
1046
  }
1047
+ /**
1048
+ * Extracts document IDs from an array of {@link FirestoreDocument}s.
1049
+ *
1050
+ * @param documents - Documents to extract IDs from
1051
+ * @returns Array of document IDs in the same order as the input
1052
+ */
935
1053
  function firestoreModelIdsFromDocuments(documents) {
936
1054
  return documents.map(firestoreModelIdFromDocument);
937
1055
  }
1056
+ /**
1057
+ * Extracts the full Firestore path ({@link FirestoreModelKey}) from a {@link FirestoreDocument}.
1058
+ *
1059
+ * Useful as a mapper function, e.g. `documents.map(firestoreModelKeyFromDocument)`.
1060
+ *
1061
+ * @param document - The document to extract the key from
1062
+ * @returns The document's full Firestore path (e.g. `'users/abc123'`)
1063
+ */
938
1064
  function firestoreModelKeyFromDocument(document) {
939
1065
  return document.key;
940
1066
  }
1067
+ /**
1068
+ * Extracts full Firestore paths from an array of {@link FirestoreDocument}s.
1069
+ *
1070
+ * @param documents - Documents to extract keys from
1071
+ * @returns Array of full Firestore paths in the same order as the input
1072
+ */
941
1073
  function firestoreModelKeysFromDocuments(documents) {
942
1074
  return documents.map(firestoreModelKeyFromDocument);
943
1075
  }
1076
+ /**
1077
+ * Extracts the {@link DocumentReference} from a {@link FirestoreDocument}.
1078
+ *
1079
+ * Useful as a mapper function, e.g. `documents.map(documentReferenceFromDocument)`.
1080
+ *
1081
+ * @param document - The document to extract the reference from
1082
+ * @returns The underlying Firestore document reference
1083
+ */
944
1084
  function documentReferenceFromDocument(document) {
945
1085
  return document.documentRef;
946
1086
  }
1087
+ /**
1088
+ * Extracts {@link DocumentReference}s from an array of {@link FirestoreDocument}s.
1089
+ *
1090
+ * @param documents - Documents to extract references from
1091
+ * @returns Array of document references in the same order as the input
1092
+ */
947
1093
  function documentReferencesFromDocuments(documents) {
948
1094
  return documents.map(documentReferenceFromDocument);
949
1095
  }
1096
+ /**
1097
+ * Creates a {@link LimitedFirestoreDocumentAccessorSnapshotCache} that wraps the given accessor
1098
+ * with an in-memory {@link Map} cache so that repeated loads for the same key return the cached
1099
+ * promise instead of re-reading from Firestore.
1100
+ *
1101
+ * The cache stores the promise itself (not the resolved value), which means concurrent requests
1102
+ * for the same key that arrive before the first read completes will also be deduplicated.
1103
+ *
1104
+ * The cache lives for the lifetime of the returned object and is never invalidated, so this is
1105
+ * best suited for short-lived scopes (e.g. a single request or batch operation) where stale reads
1106
+ * are acceptable.
1107
+ *
1108
+ * @param accessor - The accessor to wrap with caching behavior
1109
+ * @returns A {@link LimitedFirestoreDocumentAccessorSnapshotCache} backed by the given accessor
1110
+ *
1111
+ * @example
1112
+ * ```typescript
1113
+ * const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
1114
+ *
1115
+ * // First call reads from Firestore; second call returns cached result
1116
+ * const pair = await cache.getDocumentSnapshotDataPairForKey('users/abc123');
1117
+ * const samePair = await cache.getDocumentSnapshotDataPairForKey('users/abc123');
1118
+ *
1119
+ * // Batch fetch with automatic deduplication
1120
+ * const pairs = await cache.getDocumentSnapshotDataPairsWithDataForKeys(['users/abc', 'users/def']);
1121
+ *
1122
+ * // Access the underlying accessor directly
1123
+ * const doc = cache.accessor.loadDocumentForKey('users/xyz');
1124
+ * ```
1125
+ */
1126
+ function limitedFirestoreDocumentAccessorSnapshotCache(accessor) {
1127
+ const cache = new Map();
1128
+ function getDocumentSnapshotDataPairForKey(key) {
1129
+ let cached = cache.get(key);
1130
+ if (!cached) {
1131
+ const document = accessor.loadDocumentForKey(key);
1132
+ cached = getDocumentSnapshotDataPair(document);
1133
+ cache.set(key, cached);
1134
+ }
1135
+ return cached;
1136
+ }
1137
+ async function getDocumentSnapshotDataPairsForKeys(keys) {
1138
+ return Promise.all(keys.map((key) => getDocumentSnapshotDataPairForKey(key)));
1139
+ }
1140
+ async function getDocumentSnapshotDataPairsWithDataForKeys(keys) {
1141
+ const pairs = await getDocumentSnapshotDataPairsForKeys(keys);
1142
+ return util.filterMaybeArrayValues(pairs.map((pair) => (pair.data != null ? pair : undefined)));
1143
+ }
1144
+ return {
1145
+ accessor,
1146
+ getDocumentSnapshotDataPairForKey,
1147
+ getDocumentSnapshotDataPairsForKeys,
1148
+ getDocumentSnapshotDataPairsWithDataForKeys
1149
+ };
1150
+ }
950
1151
 
951
1152
  /**
952
1153
  * Creates an Observable that emits arrays of document snapshots for multiple documents.
953
1154
  *
954
1155
  * This function streams the latest snapshots for each document in the provided array.
955
1156
  * Each time any document in the array changes, a new array containing the latest snapshots
956
- * of all documents is emitted. Results are shared with multiple subscribers through shareReplay.
1157
+ * of all documents is emitted.
957
1158
  *
958
1159
  * If the input array is empty, an Observable that emits an empty array is returned.
959
1160
  *
@@ -962,7 +1163,25 @@ function documentReferencesFromDocuments(documents) {
962
1163
  * @returns Observable that emits arrays of DocumentSnapshots whenever any document changes
963
1164
  */
964
1165
  function latestSnapshotsFromDocuments(documents) {
965
- return documents.length ? rxjs$1.combineLatest(documents.map((x) => x.accessor.stream())).pipe(rxjs$1.shareReplay(1)) : rxjs$1.of([]);
1166
+ return mapLatestSnapshotsFromDocuments(documents, rxjs$1.map(util.MAP_IDENTITY));
1167
+ }
1168
+ /**
1169
+ * Creates an Observable that streams and transforms snapshots for multiple documents using `combineLatest`.
1170
+ *
1171
+ * Pipes each document's `accessor.stream()` through the provided `operator` before combining.
1172
+ * This ensures the transformation runs per-document when only one document changes, rather than
1173
+ * re-mapping all snapshots on every emission.
1174
+ *
1175
+ * {@link latestSnapshotsFromDocuments} delegates to this function with an identity operator.
1176
+ *
1177
+ * Returns `of([])` for an empty input array.
1178
+ *
1179
+ * @param documents - Documents to stream from
1180
+ * @param operator - RxJS operator applied to each document's snapshot stream individually
1181
+ * @returns Observable emitting an array of transformed values whenever any document changes
1182
+ */
1183
+ function mapLatestSnapshotsFromDocuments(documents, operator) {
1184
+ return documents.length ? rxjs$1.combineLatest(documents.map((x) => x.accessor.stream().pipe(operator))) : rxjs$1.of([]);
966
1185
  }
967
1186
  /**
968
1187
  * Creates an Observable that emits arrays of document data for multiple documents.
@@ -970,7 +1189,7 @@ function latestSnapshotsFromDocuments(documents) {
970
1189
  * This function streams the latest data for each document in the provided array.
971
1190
  * Each time any document in the array changes, a new array containing the latest data
972
1191
  * of all documents is emitted. Document data includes both the document content and
973
- * metadata like document ID and key. Results are shared with multiple subscribers.
1192
+ * metadata like document ID and key.
974
1193
  *
975
1194
  * Non-existent documents are filtered out of the results automatically.
976
1195
  *
@@ -978,8 +1197,8 @@ function latestSnapshotsFromDocuments(documents) {
978
1197
  * @param documents - Array of document instances to stream data for
979
1198
  * @returns Observable that emits arrays of document data whenever any document changes
980
1199
  */
981
- function latestDataFromDocuments(documents) {
982
- return latestSnapshotsFromDocuments(documents).pipe(dataFromDocumentSnapshots(), rxjs$1.shareReplay(1));
1200
+ function streamDocumentSnapshotsData(documents) {
1201
+ return latestSnapshotsFromDocuments(documents).pipe(dataFromDocumentSnapshots());
983
1202
  }
984
1203
  /**
985
1204
  * Creates an RxJS operator that transforms arrays of DocumentSnapshots into arrays of document data.
@@ -994,6 +1213,53 @@ function latestDataFromDocuments(documents) {
994
1213
  function dataFromDocumentSnapshots() {
995
1214
  return rxjs$1.map((x) => getDataFromDocumentSnapshots(x));
996
1215
  }
1216
+ // MARK: Streaming Document Snapshot Pairs
1217
+ /**
1218
+ * Streams {@link FirestoreDocumentSnapshotDataPair}s for multiple documents using `combineLatest`.
1219
+ *
1220
+ * Each document's `accessor.stream()` is individually piped to produce a `{ document, snapshot, data }` triplet,
1221
+ * then all streams are combined via `combineLatest`. This ensures the mapping only runs for the document
1222
+ * that actually changed. The `data` field has `id` and `key` fields injected via {@link documentDataWithIdAndKey},
1223
+ * and will be `undefined` for documents that don't exist in Firestore.
1224
+ *
1225
+ * Returns `of([])` for an empty input array.
1226
+ *
1227
+ * This is the streaming equivalent of {@link import('./document.utility').getDocumentSnapshotDataPairs}.
1228
+ *
1229
+ * @param documents - Documents to stream snapshot-data pairs for
1230
+ * @returns Observable emitting snapshot-data pairs whenever any document changes
1231
+ */
1232
+ function streamDocumentSnapshotDataPairs(documents) {
1233
+ return documents.length
1234
+ ? rxjs$1.combineLatest(documents.map((document) => document.accessor.stream().pipe(rxjs$1.map((snapshot) => ({
1235
+ document,
1236
+ snapshot,
1237
+ data: documentDataWithIdAndKey(snapshot)
1238
+ })))))
1239
+ : rxjs$1.of([]);
1240
+ }
1241
+ /**
1242
+ * Streams {@link FirestoreDocumentSnapshotDataPairWithData}s for multiple documents, filtering out non-existent documents.
1243
+ *
1244
+ * Builds on {@link streamDocumentSnapshotDataPairs} and filters each emission to include only pairs where
1245
+ * `data` is non-nullish (i.e., the document exists in Firestore). The filtered array may be shorter than
1246
+ * the input `documents` array and may change length over time as documents are created or deleted.
1247
+ *
1248
+ * Returns `of([])` for an empty input array.
1249
+ *
1250
+ * This is the streaming equivalent of {@link import('./document.utility').getDocumentSnapshotDataPairsWithData}.
1251
+ *
1252
+ * @param documents - Documents to stream snapshot-data pairs for
1253
+ * @returns Observable emitting snapshot-data pairs for existing documents only, whenever any document changes
1254
+ */
1255
+ function streamDocumentSnapshotDataPairsWithData(documents) {
1256
+ return streamDocumentSnapshotDataPairs(documents).pipe(rxjs$1.map((pairs) => pairs.filter((pair) => pair.data != null)));
1257
+ }
1258
+ // MARK: Compat
1259
+ /**
1260
+ * @deprecated Use {@link streamDocumentSnapshotsData} instead.
1261
+ */
1262
+ const latestDataFromDocuments = streamDocumentSnapshotsData;
997
1263
 
998
1264
  // A set of copied types from @google-cloud/firestore and firebase/firestore to allow cross-compatability.
999
1265
  /* eslint-disable */
@@ -2149,20 +2415,14 @@ function firebaseQueryItemAccumulator(iteration, mapItem) {
2149
2415
  }
2150
2416
 
2151
2417
  /**
2152
- * Creates a Firestore query constraint.
2418
+ * Creates a {@link FirestoreQueryConstraint} with the given type identifier and data.
2153
2419
  *
2154
- * @template T - Type of data stored in the constraint
2155
- * @param type - The constraint type identifier
2156
- * @param data - The constraint data
2157
- * @returns A Firestore query constraint object
2158
- */
2159
- /**
2160
- * Creates a Firestore query constraint.
2420
+ * This is the low-level factory used by all constraint builder functions (e.g., {@link where},
2421
+ * {@link limit}, {@link orderBy}). Most callers should use those typed builders instead.
2161
2422
  *
2162
- * @template T - Type of data stored in the constraint
2163
- * @param type - The constraint type identifier
2164
- * @param data - The constraint data
2165
- * @returns A Firestore query constraint object
2423
+ * @param type - The constraint type identifier (e.g., 'where', 'limit')
2424
+ * @param data - The constraint-specific configuration data
2425
+ * @returns A typed constraint object
2166
2426
  */
2167
2427
  function firestoreQueryConstraint(type, data) {
2168
2428
  return {
@@ -2662,6 +2922,17 @@ function filterDisallowedFirestoreItemPageIteratorInputConstraints(constraints)
2662
2922
  * This value is used when no itemsPerPage is explicitly specified in the configuration.
2663
2923
  */
2664
2924
  const DEFAULT_FIRESTORE_ITEM_PAGE_ITERATOR_ITEMS_PER_PAGE = 50;
2925
+ /**
2926
+ * Creates a {@link FirestoreItemPageIteratorDelegate} that handles cursor-based Firestore pagination.
2927
+ *
2928
+ * The delegate implements `loadItemsForPage` by:
2929
+ * 1. Retrieving the cursor document from the previous page's results
2930
+ * 2. Building a query with the configured constraints, `startAfter` cursor, and `limit`
2931
+ * 3. Executing the query and wrapping the results in a {@link FirestoreItemPageQueryResult}
2932
+ * with `reload()` and `stream()` capabilities
2933
+ *
2934
+ * @returns A delegate instance for use with {@link ItemPageIterator}
2935
+ */
2665
2936
  function makeFirestoreItemPageIteratorDelegate() {
2666
2937
  return {
2667
2938
  loadItemsForPage: (request) => {
@@ -2819,7 +3090,14 @@ function _firestoreItemPageIterationWithSnapshotIteration(snapshotIteration) {
2819
3090
  return result;
2820
3091
  }
2821
3092
  /**
2822
- * Creates a FirestoreFixedItemPageIterationFactoryFunction.
3093
+ * Creates a factory function for generating fixed-set Firestore pagination instances.
3094
+ *
3095
+ * The returned factory takes an array of document references and an optional filter,
3096
+ * producing a pagination instance that iterates over those specific documents in pages.
3097
+ *
3098
+ * @param baseConfig - Base pagination configuration (query reference, driver, page size)
3099
+ * @param documentAccessor - Accessor used to load document snapshots from the references
3100
+ * @returns A factory function that creates fixed-set pagination instances
2823
3101
  */
2824
3102
  function firestoreFixedItemPageIterationFactory(baseConfig, documentAccessor) {
2825
3103
  return (items, filter) => {
@@ -2836,7 +3114,17 @@ function firestoreFixedItemPageIterationFactory(baseConfig, documentAccessor) {
2836
3114
  };
2837
3115
  }
2838
3116
  /**
2839
- * Creates a FirestoreItemPageIterationInstance that iterates over the fixed
3117
+ * Creates a pagination instance that iterates over a fixed set of document references.
3118
+ *
3119
+ * Unlike {@link firestoreItemPageIteration} which queries Firestore dynamically, this function
3120
+ * paginates through a pre-determined list of document references. Each page loads the next
3121
+ * slice of references via the document accessor and produces a synthetic {@link QuerySnapshot}.
3122
+ *
3123
+ * This is useful for paginating over known document sets (e.g., from a pre-computed list
3124
+ * of references) without executing Firestore queries.
3125
+ *
3126
+ * @param config - Configuration including the document references, accessor, and pagination settings
3127
+ * @returns A pagination instance that pages through the fixed reference set
2840
3128
  */
2841
3129
  function firestoreFixedItemPageIteration(config) {
2842
3130
  const { items, documentAccessor } = config;
@@ -3053,24 +3341,47 @@ function readFirestoreModelKeyFromDocumentSnapshot(snapshot) {
3053
3341
  /**
3054
3342
  * @module Firestore Query Iteration
3055
3343
  *
3056
- * This module provides a comprehensive system for iterating through Firestore query results
3057
- * with support for pagination, batching, and parallelism. It enables efficient processing of
3058
- * large result sets by using cursor-based pagination (via "checkpoints") and various iteration
3059
- * strategies.
3344
+ * Provides a layered system for iterating through Firestore query results using
3345
+ * cursor-based pagination ("checkpoints"). Each layer adds convenience on top of the
3346
+ * one below:
3347
+ *
3348
+ * 1. **Checkpoints** ({@link iterateFirestoreDocumentSnapshotCheckpoints}) — core pagination engine
3349
+ * 2. **Batches** ({@link iterateFirestoreDocumentSnapshotBatches}) — subdivides checkpoints into fixed-size batches
3350
+ * 3. **Snapshots** ({@link iterateFirestoreDocumentSnapshots}) — processes individual snapshots
3351
+ * 4. **Pairs** ({@link iterateFirestoreDocumentSnapshotPairs}) — loads typed document wrappers per snapshot
3352
+ *
3353
+ * Batch variants with document pairs are also available:
3354
+ * - {@link iterateFirestoreDocumentSnapshotPairBatches} — batch processing with typed document access
3355
+ *
3356
+ * All functions support configurable limits (`limitPerCheckpoint`, `totalSnapshotsLimit`),
3357
+ * concurrency (`maxParallelCheckpoints`), rate limiting (`waitBetweenCheckpoints`),
3358
+ * snapshot filtering, and repeat cursor detection.
3060
3359
  */
3061
3360
  /**
3062
- * Iterates through the results of a Firestore query by each FirestoreDocumentSnapshotDataPairWithData.
3361
+ * Iterates through Firestore query results, loading each snapshot as a
3362
+ * {@link FirestoreDocumentSnapshotDataPairWithData} before processing.
3063
3363
  *
3064
- * This function efficiently handles pagination through potentially large result sets by using
3065
- * the checkpoint system to load documents in batches. For each document snapshot, it loads the
3066
- * associated data using the provided document accessor, then passes the combined pair to the
3067
- * processing function.
3364
+ * Built on {@link iterateFirestoreDocumentSnapshots}, this adds an automatic document
3365
+ * loading step: each raw snapshot is resolved through the `documentAccessor` to produce
3366
+ * a pair containing both the typed {@link FirestoreDocument} and its snapshot data.
3367
+ * This is the highest-level iteration function — use it when your callback needs
3368
+ * document-level operations (updates, deletes) alongside the snapshot data.
3068
3369
  *
3069
- * @template T - The document data type
3070
- * @template R - The result type of processing each snapshot pair
3071
- * @template D - The FirestoreDocument implementation type (defaults to FirestoreDocument<T>)
3072
- * @param config - Configuration for the iteration, including the document accessor and processing function
3073
- * @returns A promise that resolves to the result of the iteration, including statistics about checkpoints and snapshots processed
3370
+ * @param config - Iteration config including the document accessor and per-pair callback
3371
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
3372
+ *
3373
+ * @example
3374
+ * ```typescript
3375
+ * const result = await iterateFirestoreDocumentSnapshotPairs({
3376
+ * queryFactory,
3377
+ * constraintsFactory: [where('status', '==', 'pending')],
3378
+ * limitPerCheckpoint: 100,
3379
+ * documentAccessor: collection.documentAccessor(),
3380
+ * iterateSnapshotPair: async (pair) => {
3381
+ * await pair.document.accessor.set({ ...pair.data, status: 'processed' });
3382
+ * }
3383
+ * });
3384
+ * ```
3074
3385
  */
3075
3386
  async function iterateFirestoreDocumentSnapshotPairs(config) {
3076
3387
  const { iterateSnapshotPair, documentAccessor } = config;
@@ -3084,16 +3395,32 @@ async function iterateFirestoreDocumentSnapshotPairs(config) {
3084
3395
  });
3085
3396
  }
3086
3397
  /**
3087
- * Iterates through the results of a Firestore query by each document snapshot by itself.
3398
+ * Iterates through Firestore query results, processing each document snapshot individually.
3088
3399
  *
3089
- * This function efficiently handles pagination through potentially large result sets by using
3090
- * the checkpoint system to load documents in batches. Each document snapshot is then processed
3091
- * individually using the provided processing function.
3400
+ * Built on {@link iterateFirestoreDocumentSnapshotBatches} with `maxParallelCheckpoints: 1`,
3401
+ * this unwraps each checkpoint's snapshots and processes them one-by-one via
3402
+ * {@link performAsyncTasks} (sequential by default). Use `snapshotsPerformTasksConfig`
3403
+ * to enable parallel snapshot processing within each checkpoint.
3092
3404
  *
3093
- * @template T - The document data type
3094
- * @template R - The result type of processing each snapshot
3095
- * @param config - Configuration for the iteration, including the snapshot processing function
3096
- * @returns A promise that resolves to the result of the iteration, including statistics about checkpoints and snapshots processed
3405
+ * For document-level operations (needing the typed {@link FirestoreDocument} wrapper),
3406
+ * use {@link iterateFirestoreDocumentSnapshotPairs} instead.
3407
+ *
3408
+ * @param config - Iteration config including the per-snapshot callback
3409
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
3410
+ *
3411
+ * @example
3412
+ * ```typescript
3413
+ * const result = await iterateFirestoreDocumentSnapshots({
3414
+ * queryFactory,
3415
+ * constraintsFactory: [where('active', '==', true)],
3416
+ * limitPerCheckpoint: 200,
3417
+ * totalSnapshotsLimit: 1000,
3418
+ * iterateSnapshot: async (snapshot) => {
3419
+ * const data = snapshot.data();
3420
+ * await externalApi.sync(data);
3421
+ * }
3422
+ * });
3423
+ * ```
3097
3424
  */
3098
3425
  async function iterateFirestoreDocumentSnapshots(config) {
3099
3426
  const { iterateSnapshot, performTasksConfig, snapshotsPerformTasksConfig } = config;
@@ -3110,10 +3437,32 @@ async function iterateFirestoreDocumentSnapshots(config) {
3110
3437
  });
3111
3438
  }
3112
3439
  /**
3113
- * Iterates through the results of a Firestore query by each FirestoreDocumentSnapshotDataPair.
3440
+ * Iterates through Firestore query results in batches, loading each batch as
3441
+ * {@link FirestoreDocumentSnapshotDataPairWithData} instances before processing.
3114
3442
  *
3115
- * @param config
3116
- * @returns
3443
+ * Built on {@link iterateFirestoreDocumentSnapshotBatches} with `maxParallelCheckpoints: 1`.
3444
+ * Each batch of raw snapshots is resolved through the `documentAccessor` to produce
3445
+ * typed document-snapshot pairs. Use this when you need batch-level operations with
3446
+ * typed document access (e.g., bulk updates, batch writes).
3447
+ *
3448
+ * @param config - Iteration config including the document accessor and per-batch callback
3449
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
3450
+ *
3451
+ * @example
3452
+ * ```typescript
3453
+ * const result = await iterateFirestoreDocumentSnapshotPairBatches({
3454
+ * queryFactory,
3455
+ * constraintsFactory: [where('needsMigration', '==', true)],
3456
+ * limitPerCheckpoint: 500,
3457
+ * batchSize: 50,
3458
+ * documentAccessor: collection.documentAccessor(),
3459
+ * iterateSnapshotPairsBatch: async (pairs, batchIndex) => {
3460
+ * const writeBatch = firestore.batch();
3461
+ * pairs.forEach((pair) => writeBatch.update(pair.document.documentRef, { migrated: true }));
3462
+ * await writeBatch.commit();
3463
+ * }
3464
+ * });
3465
+ * ```
3117
3466
  */
3118
3467
  async function iterateFirestoreDocumentSnapshotPairBatches(config) {
3119
3468
  const { iterateSnapshotPairsBatch, documentAccessor } = config;
@@ -3134,10 +3483,32 @@ async function iterateFirestoreDocumentSnapshotPairBatches(config) {
3134
3483
  */
3135
3484
  const DEFAULT_ITERATE_FIRESTORE_DOCUMENT_SNAPSHOT_BATCHES_BATCH_SIZE = 25;
3136
3485
  /**
3137
- * Iterates through the results of a Firestore query by each document snapshot.
3486
+ * Iterates through Firestore query results by subdividing each checkpoint into smaller batches.
3138
3487
  *
3139
- * @param config
3140
- * @returns
3488
+ * Built on {@link iterateFirestoreDocumentSnapshotCheckpoints}, this function takes each
3489
+ * checkpoint's snapshots and splits them into batches (default size: 25). Batches are
3490
+ * processed via {@link performAsyncTasks}, sequential by default. Use this when operations
3491
+ * have size limits (e.g., Firestore batch writes) or benefit from controlled chunk sizes.
3492
+ *
3493
+ * For per-snapshot processing, use {@link iterateFirestoreDocumentSnapshots}.
3494
+ * For batch processing with typed document pairs, use {@link iterateFirestoreDocumentSnapshotPairBatches}.
3495
+ *
3496
+ * @param config - Iteration config including batch size and per-batch callback
3497
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
3498
+ *
3499
+ * @example
3500
+ * ```typescript
3501
+ * const result = await iterateFirestoreDocumentSnapshotBatches({
3502
+ * queryFactory,
3503
+ * constraintsFactory: [where('type', '==', 'order')],
3504
+ * limitPerCheckpoint: 500,
3505
+ * batchSize: 100,
3506
+ * iterateSnapshotBatch: async (snapshots, batchIndex) => {
3507
+ * const data = snapshots.map((s) => s.data());
3508
+ * await analytics.trackBatch(data);
3509
+ * }
3510
+ * });
3511
+ * ```
3141
3512
  */
3142
3513
  async function iterateFirestoreDocumentSnapshotBatches(config) {
3143
3514
  const { iterateSnapshotBatch, batchSizeForSnapshots: inputBatchSizeForSnapshots, performTasksConfig, batchSize: inputBatchSize } = config;
@@ -3163,29 +3534,75 @@ async function iterateFirestoreDocumentSnapshotBatches(config) {
3163
3534
  });
3164
3535
  }
3165
3536
  /**
3166
- * Creates a IterateFirestoreDocumentSnapshotCheckpointsFilterCheckpointSnapshotsFunction that filters out any repeat documents.
3537
+ * Creates a checkpoint filter that deduplicates documents across checkpoints.
3167
3538
  *
3168
- * Repeat documents can occur in cases where the document is updated and the query matches it again for a different reason.
3169
- * This utility function creates a filter that prevents processing the same document multiple times.
3539
+ * Repeat documents can appear when a document is updated during iteration and
3540
+ * re-matches the query in a subsequent checkpoint. This factory returns a stateful
3541
+ * filter that tracks seen document keys and removes duplicates.
3170
3542
  *
3171
- * @param readKeyFunction - Function that extracts a unique key from a document snapshot, defaults to document ID
3172
- * @returns A filter function that prevents duplicate document processing
3543
+ * The filter maintains state across checkpoints use a single instance for the
3544
+ * entire iteration run. Pair with `handleRepeatCursor: false` to also terminate
3545
+ * iteration when cursor-level repeats are detected.
3546
+ *
3547
+ * @param readKeyFunction - Extracts a unique key from each snapshot; defaults to `snapshot.id`
3548
+ * @returns A stateful filter function suitable for `filterCheckpointSnapshots`
3549
+ *
3550
+ * @example
3551
+ * ```typescript
3552
+ * const result = await iterateFirestoreDocumentSnapshotCheckpoints({
3553
+ * queryFactory,
3554
+ * constraintsFactory: [orderBy('updatedAt')],
3555
+ * limitPerCheckpoint: 100,
3556
+ * filterCheckpointSnapshots: filterRepeatCheckpointSnapshots(),
3557
+ * handleRepeatCursor: false,
3558
+ * iterateCheckpoint: async (snapshots) => {
3559
+ * return snapshots.map((s) => s.data());
3560
+ * }
3561
+ * });
3562
+ * ```
3173
3563
  */
3174
3564
  function filterRepeatCheckpointSnapshots(readKeyFunction = (x) => x.id) {
3175
3565
  const allowOnceFilter = util.allowValueOnceFilter(readKeyFunction);
3176
3566
  return async (snapshots) => snapshots.filter(allowOnceFilter);
3177
3567
  }
3178
3568
  /**
3179
- * Iterates through the results of a Firestore query in several batches.
3569
+ * Core cursor-based pagination engine for iterating through Firestore query results.
3180
3570
  *
3181
- * This is the core pagination function that handles cursor-based iteration through
3182
- * potentially large Firestore query results. It manages cursor documents, checkpoint
3183
- * processing, parallel execution, and various limit controls.
3571
+ * This is the foundational function in the Firestore iteration hierarchy. It drives
3572
+ * sequential cursor-based pagination: each "checkpoint" executes a Firestore query,
3573
+ * uses the last document as a `startAfter` cursor for the next query, and passes
3574
+ * results to the `iterateCheckpoint` callback.
3184
3575
  *
3185
- * @template T - The document data type
3186
- * @template R - The result type of the iteration
3187
- * @param config - Complete configuration for the pagination and processing behavior
3188
- * @returns Promise resolving to statistics about the iteration
3576
+ * The iteration loop continues until one of these conditions is met:
3577
+ * - A query returns no results (no more matching documents)
3578
+ * - The `totalSnapshotsLimit` is reached
3579
+ * - A repeat cursor is detected and `handleRepeatCursor` returns `false`
3580
+ * - The effective `limitPerCheckpoint` calculates to 0 remaining
3581
+ *
3582
+ * Higher-level functions build on this:
3583
+ * - {@link iterateFirestoreDocumentSnapshotBatches} — subdivides checkpoints into smaller batches
3584
+ * - {@link iterateFirestoreDocumentSnapshots} — processes one snapshot at a time
3585
+ * - {@link iterateFirestoreDocumentSnapshotPairs} — loads typed document wrappers per snapshot
3586
+ *
3587
+ * @param config - Complete configuration for pagination, processing, and termination behavior
3588
+ * @returns Statistics including total checkpoints executed, snapshots visited, and whether the limit was hit
3589
+ *
3590
+ * @example
3591
+ * ```typescript
3592
+ * const result = await iterateFirestoreDocumentSnapshotCheckpoints({
3593
+ * queryFactory,
3594
+ * constraintsFactory: [where('createdAt', '<=', cutoffDate), orderBy('createdAt')],
3595
+ * limitPerCheckpoint: 200,
3596
+ * totalSnapshotsLimit: 5000,
3597
+ * handleRepeatCursor: false,
3598
+ * iterateCheckpoint: async (snapshots, querySnapshot) => {
3599
+ * return snapshots.map((s) => ({ id: s.id, data: s.data() }));
3600
+ * },
3601
+ * useCheckpointResult: (result) => {
3602
+ * console.log(`Checkpoint ${result.i}: processed ${result.docSnapshots.length} docs`);
3603
+ * }
3604
+ * });
3605
+ * ```
3189
3606
  */
3190
3607
  async function iterateFirestoreDocumentSnapshotCheckpoints(config) {
3191
3608
  const { iterateCheckpoint, filterCheckpointSnapshots: inputFilterCheckpointSnapshot, handleRepeatCursor: inputHandleRepeatCursor, waitBetweenCheckpoints, useCheckpointResult, constraintsFactory: inputConstraintsFactory, dynamicConstraints: inputDynamicConstraints, queryFactory, maxParallelCheckpoints = 1, limitPerCheckpoint: inputLimitPerCheckpoint, totalSnapshotsLimit: inputTotalSnapshotsLimit } = config;
@@ -3288,13 +3705,20 @@ async function iterateFirestoreDocumentSnapshotCheckpoints(config) {
3288
3705
  }
3289
3706
  // MARK: Utility
3290
3707
  /**
3291
- * Creates a filter that allows each document snapshot to be processed only once based on its path.
3708
+ * Creates a stateful filter that allows each document snapshot through only once,
3709
+ * keyed by its full Firestore model path.
3292
3710
  *
3293
- * This utility helps prevent duplicate processing of documents by tracking which ones have
3294
- * already been seen based on their path.
3711
+ * Unlike {@link filterRepeatCheckpointSnapshots} which uses document ID by default,
3712
+ * this filter uses the full document path ({@link readFirestoreModelKeyFromDocumentSnapshot}),
3713
+ * making it suitable for cross-collection iteration where document IDs alone may collide.
3295
3714
  *
3296
- * @template T - The document snapshot type
3297
- * @returns A filter function that only allows each unique document to pass once
3715
+ * @returns A reusable filter function that passes each unique document path exactly once
3716
+ *
3717
+ * @example
3718
+ * ```typescript
3719
+ * const onceFilter = allowDocumentSnapshotWithPathOnceFilter();
3720
+ * const uniqueSnapshots = allSnapshots.filter(onceFilter);
3721
+ * ```
3298
3722
  */
3299
3723
  function allowDocumentSnapshotWithPathOnceFilter() {
3300
3724
  return util.allowValueOnceFilter(readFirestoreModelKeyFromDocumentSnapshot);
@@ -9701,6 +10125,7 @@ exports.limit = limit;
9701
10125
  exports.limitToLast = limitToLast;
9702
10126
  exports.limitUploadFileTypeDeterminer = limitUploadFileTypeDeterminer;
9703
10127
  exports.limitedFirestoreDocumentAccessorFactory = limitedFirestoreDocumentAccessorFactory;
10128
+ exports.limitedFirestoreDocumentAccessorSnapshotCache = limitedFirestoreDocumentAccessorSnapshotCache;
9704
10129
  exports.loadAllFirestoreDocumentSnapshot = loadAllFirestoreDocumentSnapshot;
9705
10130
  exports.loadAllFirestoreDocumentSnapshotPairs = loadAllFirestoreDocumentSnapshotPairs;
9706
10131
  exports.loadDocumentsForDocumentReferences = loadDocumentsForDocumentReferences;
@@ -9723,6 +10148,7 @@ exports.makeRootSingleItemFirestoreCollection = makeRootSingleItemFirestoreColle
9723
10148
  exports.makeSingleItemFirestoreCollection = makeSingleItemFirestoreCollection;
9724
10149
  exports.mapDataFromSnapshot = mapDataFromSnapshot;
9725
10150
  exports.mapHttpsCallable = mapHttpsCallable;
10151
+ exports.mapLatestSnapshotsFromDocuments = mapLatestSnapshotsFromDocuments;
9726
10152
  exports.mergeNotificationBoxRecipientTemplateConfigRecords = mergeNotificationBoxRecipientTemplateConfigRecords;
9727
10153
  exports.mergeNotificationBoxRecipientTemplateConfigs = mergeNotificationBoxRecipientTemplateConfigs;
9728
10154
  exports.mergeNotificationBoxRecipients = mergeNotificationBoxRecipients;
@@ -9855,6 +10281,9 @@ exports.storageListFilesResultFactory = storageListFilesResultFactory;
9855
10281
  exports.storageListFilesResultHasNoNextError = storageListFilesResultHasNoNextError;
9856
10282
  exports.storagePathFactory = storagePathFactory;
9857
10283
  exports.storedFileReaderFactory = storedFileReaderFactory;
10284
+ exports.streamDocumentSnapshotDataPairs = streamDocumentSnapshotDataPairs;
10285
+ exports.streamDocumentSnapshotDataPairsWithData = streamDocumentSnapshotDataPairsWithData;
10286
+ exports.streamDocumentSnapshotsData = streamDocumentSnapshotsData;
9858
10287
  exports.streamFromOnSnapshot = streamFromOnSnapshot;
9859
10288
  exports.systemStateCollectionReference = systemStateCollectionReference;
9860
10289
  exports.systemStateConverter = systemStateConverter;