@dereekb/firebase 13.0.6 → 13.1.0

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.
@@ -1,10 +1,21 @@
1
1
  /**
2
2
  * @module Firestore Query Iteration
3
3
  *
4
- * This module provides a comprehensive system for iterating through Firestore query results
5
- * with support for pagination, batching, and parallelism. It enables efficient processing of
6
- * large result sets by using cursor-based pagination (via "checkpoints") and various iteration
7
- * strategies.
4
+ * Provides a layered system for iterating through Firestore query results using
5
+ * cursor-based pagination ("checkpoints"). Each layer adds convenience on top of the
6
+ * one below:
7
+ *
8
+ * 1. **Checkpoints** ({@link iterateFirestoreDocumentSnapshotCheckpoints}) — core pagination engine
9
+ * 2. **Batches** ({@link iterateFirestoreDocumentSnapshotBatches}) — subdivides checkpoints into fixed-size batches
10
+ * 3. **Snapshots** ({@link iterateFirestoreDocumentSnapshots}) — processes individual snapshots
11
+ * 4. **Pairs** ({@link iterateFirestoreDocumentSnapshotPairs}) — loads typed document wrappers per snapshot
12
+ *
13
+ * Batch variants with document pairs are also available:
14
+ * - {@link iterateFirestoreDocumentSnapshotPairBatches} — batch processing with typed document access
15
+ *
16
+ * All functions support configurable limits (`limitPerCheckpoint`, `totalSnapshotsLimit`),
17
+ * concurrency (`maxParallelCheckpoints`), rate limiting (`waitBetweenCheckpoints`),
18
+ * snapshot filtering, and repeat cursor detection.
8
19
  */
9
20
  import { type GetterOrValue, type PromiseOrValue, type IndexRef, type Maybe, type PerformAsyncTasksConfig, type IndexNumber, type PerformAsyncTasksResult, type FactoryWithRequiredInput, type Milliseconds, type AllowValueOnceFilter, type ReadKeyFunction } from '@dereekb/util';
10
21
  import { type FirestoreDocument, type LimitedFirestoreDocumentAccessor, type FirestoreDocumentSnapshotDataPairWithData } from '../accessor';
@@ -13,35 +24,53 @@ import { type FirestoreQueryConstraint } from './constraint';
13
24
  import { type FirestoreQueryFactory } from './query';
14
25
  import { type FirestoreModelKey } from '../collection/collection';
15
26
  /**
16
- * Configuration for iterating through Firestore document snapshots with their associated data as pairs.
27
+ * Configuration for {@link iterateFirestoreDocumentSnapshotPairs}.
17
28
  *
18
- * @template T - The document data type
19
- * @template R - The result type of processing each snapshot pair
20
- * @template D - The FirestoreDocument implementation type (defaults to FirestoreDocument<T>)
29
+ * Extends per-snapshot iteration by automatically loading each snapshot's
30
+ * {@link FirestoreDocumentSnapshotDataPairWithData} via a document accessor,
31
+ * providing the callback with both the snapshot and its typed document wrapper.
21
32
  */
22
33
  export interface IterateFirestoreDocumentSnapshotPairsConfig<T, R, D extends FirestoreDocument<T> = FirestoreDocument<T>> extends Omit<IterateFirestoreDocumentSnapshotsConfig<T, R>, 'iterateSnapshot'> {
23
34
  /**
24
- * Document accessor to retrieve the documents for the references.
35
+ * Accessor used to load {@link FirestoreDocument} instances for each snapshot reference.
36
+ * Each snapshot is resolved through this accessor to produce a document-snapshot pair.
25
37
  */
26
38
  readonly documentAccessor: LimitedFirestoreDocumentAccessor<T, D>;
27
39
  /**
28
- * The iterate function per each snapshot.
40
+ * Callback invoked for each document-snapshot pair.
41
+ *
42
+ * Receives a pair containing both the {@link FirestoreDocument} wrapper and its
43
+ * snapshot data, enabling operations that need typed document access (e.g., updates, deletes).
44
+ *
45
+ * @param snapshot - The document-snapshot pair with guaranteed data
29
46
  */
30
47
  iterateSnapshotPair(snapshot: FirestoreDocumentSnapshotDataPairWithData<D>): Promise<R>;
31
48
  }
32
49
  /**
33
- * Iterates through the results of a Firestore query by each FirestoreDocumentSnapshotDataPairWithData.
50
+ * Iterates through Firestore query results, loading each snapshot as a
51
+ * {@link FirestoreDocumentSnapshotDataPairWithData} before processing.
34
52
  *
35
- * This function efficiently handles pagination through potentially large result sets by using
36
- * the checkpoint system to load documents in batches. For each document snapshot, it loads the
37
- * associated data using the provided document accessor, then passes the combined pair to the
38
- * processing function.
53
+ * Built on {@link iterateFirestoreDocumentSnapshots}, this adds an automatic document
54
+ * loading step: each raw snapshot is resolved through the `documentAccessor` to produce
55
+ * a pair containing both the typed {@link FirestoreDocument} and its snapshot data.
56
+ * This is the highest-level iteration function — use it when your callback needs
57
+ * document-level operations (updates, deletes) alongside the snapshot data.
39
58
  *
40
- * @template T - The document data type
41
- * @template R - The result type of processing each snapshot pair
42
- * @template D - The FirestoreDocument implementation type (defaults to FirestoreDocument<T>)
43
- * @param config - Configuration for the iteration, including the document accessor and processing function
44
- * @returns A promise that resolves to the result of the iteration, including statistics about checkpoints and snapshots processed
59
+ * @param config - Iteration config including the document accessor and per-pair callback
60
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const result = await iterateFirestoreDocumentSnapshotPairs({
65
+ * queryFactory,
66
+ * constraintsFactory: [where('status', '==', 'pending')],
67
+ * limitPerCheckpoint: 100,
68
+ * documentAccessor: collection.documentAccessor(),
69
+ * iterateSnapshotPair: async (pair) => {
70
+ * await pair.document.accessor.set({ ...pair.data, status: 'processed' });
71
+ * }
72
+ * });
73
+ * ```
45
74
  */
46
75
  export declare function iterateFirestoreDocumentSnapshotPairs<T, R, D extends FirestoreDocument<T> = FirestoreDocument<T>>(config: IterateFirestoreDocumentSnapshotPairsConfig<T, R, D>): Promise<IterateFirestoreDocumentSnapshotCheckpointsResult>;
47
76
  /**
@@ -82,44 +111,83 @@ export interface IterateFirestoreDocumentSnapshotsConfig<T, R> extends Omit<Iter
82
111
  */
83
112
  export type IterateFirestoreDocumentSnapshotsResult<T, R> = PerformAsyncTasksResult<QueryDocumentSnapshot<T>, R>;
84
113
  /**
85
- * Iterates through the results of a Firestore query by each document snapshot by itself.
114
+ * Iterates through Firestore query results, processing each document snapshot individually.
86
115
  *
87
- * This function efficiently handles pagination through potentially large result sets by using
88
- * the checkpoint system to load documents in batches. Each document snapshot is then processed
89
- * individually using the provided processing function.
116
+ * Built on {@link iterateFirestoreDocumentSnapshotBatches} with `maxParallelCheckpoints: 1`,
117
+ * this unwraps each checkpoint's snapshots and processes them one-by-one via
118
+ * {@link performAsyncTasks} (sequential by default). Use `snapshotsPerformTasksConfig`
119
+ * to enable parallel snapshot processing within each checkpoint.
90
120
  *
91
- * @template T - The document data type
92
- * @template R - The result type of processing each snapshot
93
- * @param config - Configuration for the iteration, including the snapshot processing function
94
- * @returns A promise that resolves to the result of the iteration, including statistics about checkpoints and snapshots processed
121
+ * For document-level operations (needing the typed {@link FirestoreDocument} wrapper),
122
+ * use {@link iterateFirestoreDocumentSnapshotPairs} instead.
123
+ *
124
+ * @param config - Iteration config including the per-snapshot callback
125
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * const result = await iterateFirestoreDocumentSnapshots({
130
+ * queryFactory,
131
+ * constraintsFactory: [where('active', '==', true)],
132
+ * limitPerCheckpoint: 200,
133
+ * totalSnapshotsLimit: 1000,
134
+ * iterateSnapshot: async (snapshot) => {
135
+ * const data = snapshot.data();
136
+ * await externalApi.sync(data);
137
+ * }
138
+ * });
139
+ * ```
95
140
  */
96
141
  export declare function iterateFirestoreDocumentSnapshots<T, R>(config: IterateFirestoreDocumentSnapshotsConfig<T, R>): Promise<IterateFirestoreDocumentSnapshotCheckpointsResult>;
97
142
  /**
98
- * Config for iterateFirestoreDocumentSnapshots().
99
- *
100
- * This interface defines the configuration for processing batches of document snapshots
101
- * with their associated data. It supports efficient batch processing operations on related
102
- * documents.
143
+ * Configuration for {@link iterateFirestoreDocumentSnapshotPairBatches}.
103
144
  *
104
- * @template T - The document data type
105
- * @template R - The result type of processing each batch
106
- * @template D - The FirestoreDocument implementation type
145
+ * Extends batch-level iteration by automatically resolving each snapshot batch into
146
+ * {@link FirestoreDocumentSnapshotDataPairWithData} instances via a document accessor.
147
+ * The callback receives fully typed document-snapshot pairs rather than raw snapshots,
148
+ * enabling batch operations that need document-level access.
107
149
  */
108
150
  export interface IterateFirestoreDocumentSnapshotPairBatchesConfig<T, R, D extends FirestoreDocument<T> = FirestoreDocument<T>> extends Omit<IterateFirestoreDocumentSnapshotBatchesConfig<T, R>, 'iterateSnapshotBatch'> {
109
151
  /**
110
- * Document accessor to retrieve the documents for the references.
152
+ * Accessor used to load {@link FirestoreDocument} instances for each snapshot reference.
153
+ * All snapshots in a batch are resolved through this accessor before being passed to the callback.
111
154
  */
112
155
  readonly documentAccessor: LimitedFirestoreDocumentAccessor<T, D>;
113
156
  /**
114
- * The iterate function per each snapshot batch.
157
+ * Callback invoked for each batch of document-snapshot pairs.
158
+ *
159
+ * @param snapshotDataPairs - Array of pairs, each containing a typed document and its snapshot data
160
+ * @param batchIndex - Zero-based index of this batch within the current checkpoint
115
161
  */
116
162
  iterateSnapshotPairsBatch(snapshotDataPairs: FirestoreDocumentSnapshotDataPairWithData<D>[], batchIndex: number): Promise<R>;
117
163
  }
118
164
  /**
119
- * Iterates through the results of a Firestore query by each FirestoreDocumentSnapshotDataPair.
165
+ * Iterates through Firestore query results in batches, loading each batch as
166
+ * {@link FirestoreDocumentSnapshotDataPairWithData} instances before processing.
120
167
  *
121
- * @param config
122
- * @returns
168
+ * Built on {@link iterateFirestoreDocumentSnapshotBatches} with `maxParallelCheckpoints: 1`.
169
+ * Each batch of raw snapshots is resolved through the `documentAccessor` to produce
170
+ * typed document-snapshot pairs. Use this when you need batch-level operations with
171
+ * typed document access (e.g., bulk updates, batch writes).
172
+ *
173
+ * @param config - Iteration config including the document accessor and per-batch callback
174
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * const result = await iterateFirestoreDocumentSnapshotPairBatches({
179
+ * queryFactory,
180
+ * constraintsFactory: [where('needsMigration', '==', true)],
181
+ * limitPerCheckpoint: 500,
182
+ * batchSize: 50,
183
+ * documentAccessor: collection.documentAccessor(),
184
+ * iterateSnapshotPairsBatch: async (pairs, batchIndex) => {
185
+ * const writeBatch = firestore.batch();
186
+ * pairs.forEach((pair) => writeBatch.update(pair.document.documentRef, { migrated: true }));
187
+ * await writeBatch.commit();
188
+ * }
189
+ * });
190
+ * ```
123
191
  */
124
192
  export declare function iterateFirestoreDocumentSnapshotPairBatches<T, R, D extends FirestoreDocument<T> = FirestoreDocument<T>>(config: IterateFirestoreDocumentSnapshotPairBatchesConfig<T, R>): Promise<IterateFirestoreDocumentSnapshotCheckpointsResult>;
125
193
  /**
@@ -147,34 +215,46 @@ export interface IterateFirestoreDocumentSnapshotBatchesResult<T, R> extends Ind
147
215
  readonly result: R;
148
216
  }
149
217
  /**
150
- * Config for iterateFirestoreDocumentSnapshots().
218
+ * Configuration for {@link iterateFirestoreDocumentSnapshotBatches}.
219
+ *
220
+ * Extends checkpoint-level iteration by subdividing each checkpoint's snapshots into
221
+ * smaller batches before processing. This is useful for operations that have size
222
+ * constraints (e.g., Firestore batch writes limited to 500 operations) or that
223
+ * benefit from smaller working sets for memory efficiency.
151
224
  */
152
225
  export interface IterateFirestoreDocumentSnapshotBatchesConfig<T, R> extends Omit<IterateFirestoreDocumentSnapshotCheckpointsConfig<T, IterateFirestoreDocumentSnapshotBatchesResult<T, R>>, 'iterateCheckpoint'> {
153
226
  /**
154
- * Max number of documents per batch. Defaults to 25.
227
+ * Maximum number of document snapshots per batch.
155
228
  *
156
- * If null is then all snapshots will be processed in a single batch.
229
+ * Defaults to {@link DEFAULT_ITERATE_FIRESTORE_DOCUMENT_SNAPSHOT_BATCHES_BATCH_SIZE} (25).
230
+ * Pass `null` to process all snapshots from a checkpoint in a single batch.
157
231
  */
158
232
  readonly batchSize?: Maybe<number>;
159
233
  /**
160
- * Determines the custom batch size for the input.
234
+ * Dynamic batch size factory that can vary batch size based on the checkpoint's snapshots.
161
235
  *
162
- * If the factory returns returned null then all snapshots will be processed in a single batch.
236
+ * Called once per checkpoint with all snapshots from that checkpoint. If it returns `null`,
237
+ * all snapshots are processed in a single batch. Takes precedence over `batchSize` when provided.
163
238
  */
164
239
  readonly batchSizeForSnapshots?: Maybe<FactoryWithRequiredInput<number | null, QueryDocumentSnapshot<T>[]>>;
165
240
  /**
166
- * The iterate function per each snapshot.
241
+ * Callback invoked for each batch of document snapshots within a checkpoint.
167
242
  *
168
- * The batch will have atleast one item in it.
243
+ * Each batch is guaranteed to contain at least one item. Batches within a checkpoint
244
+ * are processed via {@link performAsyncTasks} (sequential by default, configurable
245
+ * via `performTasksConfig`).
246
+ *
247
+ * @param snapshotBatch - Non-empty array of document snapshots in this batch
248
+ * @param batchIndex - Zero-based index of this batch within the current checkpoint
169
249
  */
170
250
  iterateSnapshotBatch(snapshotBatch: QueryDocumentSnapshot<T>[], batchIndex: number): Promise<R>;
171
251
  /**
172
- * (Optional) additional config for the PerformAsyncTasks call.
252
+ * Optional configuration for the {@link performAsyncTasks} call that processes batches
253
+ * within each checkpoint.
173
254
  *
174
- * By default:
175
- * - retriesAllowed = 0
176
- * - throwError = true
177
- * - sequential = true
255
+ * Defaults: `sequential: true`, `retriesAllowed: 0`, `throwError: true`.
256
+ * Override to enable parallel batch processing, add retries for transient failures,
257
+ * or customize error handling.
178
258
  */
179
259
  readonly performTasksConfig?: Partial<PerformAsyncTasksConfig<QueryDocumentSnapshot<T>[]>>;
180
260
  }
@@ -185,74 +265,145 @@ export interface IterateFirestoreDocumentSnapshotBatchesConfig<T, R> extends Omi
185
265
  */
186
266
  export declare const DEFAULT_ITERATE_FIRESTORE_DOCUMENT_SNAPSHOT_BATCHES_BATCH_SIZE = 25;
187
267
  /**
188
- * Iterates through the results of a Firestore query by each document snapshot.
268
+ * Iterates through Firestore query results by subdividing each checkpoint into smaller batches.
269
+ *
270
+ * Built on {@link iterateFirestoreDocumentSnapshotCheckpoints}, this function takes each
271
+ * checkpoint's snapshots and splits them into batches (default size: 25). Batches are
272
+ * processed via {@link performAsyncTasks}, sequential by default. Use this when operations
273
+ * have size limits (e.g., Firestore batch writes) or benefit from controlled chunk sizes.
189
274
  *
190
- * @param config
191
- * @returns
275
+ * For per-snapshot processing, use {@link iterateFirestoreDocumentSnapshots}.
276
+ * For batch processing with typed document pairs, use {@link iterateFirestoreDocumentSnapshotPairBatches}.
277
+ *
278
+ * @param config - Iteration config including batch size and per-batch callback
279
+ * @returns Checkpoint-level statistics (total checkpoints, snapshots visited, limit status)
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * const result = await iterateFirestoreDocumentSnapshotBatches({
284
+ * queryFactory,
285
+ * constraintsFactory: [where('type', '==', 'order')],
286
+ * limitPerCheckpoint: 500,
287
+ * batchSize: 100,
288
+ * iterateSnapshotBatch: async (snapshots, batchIndex) => {
289
+ * const data = snapshots.map((s) => s.data());
290
+ * await analytics.trackBatch(data);
291
+ * }
292
+ * });
293
+ * ```
192
294
  */
193
295
  export declare function iterateFirestoreDocumentSnapshotBatches<T, R>(config: IterateFirestoreDocumentSnapshotBatchesConfig<T, R>): Promise<IterateFirestoreDocumentSnapshotCheckpointsResult>;
194
296
  /**
195
- * Config for iterateFirestoreDocumentSnapshotCheckpoints().
297
+ * Configuration for {@link iterateFirestoreDocumentSnapshotCheckpoints}.
298
+ *
299
+ * This is the lowest-level configuration in the iteration hierarchy. It controls
300
+ * cursor-based pagination through Firestore query results, where each "checkpoint"
301
+ * represents one query execution that fetches a page of documents. Higher-level
302
+ * functions ({@link iterateFirestoreDocumentSnapshotBatches}, {@link iterateFirestoreDocumentSnapshots},
303
+ * {@link iterateFirestoreDocumentSnapshotPairs}) build on this.
196
304
  */
197
305
  export interface IterateFirestoreDocumentSnapshotCheckpointsConfig<T, R> {
306
+ /**
307
+ * Factory used to create Firestore queries. The query is rebuilt for each checkpoint
308
+ * with the current constraints (including the cursor `startAfter` and `limit`).
309
+ */
198
310
  readonly queryFactory: FirestoreQueryFactory<T>;
311
+ /**
312
+ * Base query constraints applied to every checkpoint query.
313
+ *
314
+ * When a function (getter), it is called for each checkpoint by default, enabling
315
+ * dynamic constraints that change between pages. When a static array, the same
316
+ * constraints are reused. Override this behavior with `dynamicConstraints`.
317
+ *
318
+ * The cursor (`startAfter`) and page limit are appended automatically — do not
319
+ * include them here.
320
+ */
199
321
  readonly constraintsFactory: GetterOrValue<FirestoreQueryConstraint[]>;
200
322
  /**
201
- * Whether or not to call the constraints factory each time.
323
+ * Controls whether `constraintsFactory` is re-evaluated for each checkpoint.
202
324
  *
203
- * If the constraintsFactory is a getter then this defaults to true. If constraintsFactory is a value then this is set to false.
325
+ * Defaults to `true` when `constraintsFactory` is a function, `false` when it is a static array.
326
+ * Set explicitly to override the default — e.g., `false` to cache a function's result,
327
+ * or `true` to force re-evaluation of a memoized getter.
204
328
  */
205
329
  readonly dynamicConstraints?: boolean;
206
330
  /**
207
- * Convenience paramenter to add a maximum limit constraint to the query.
331
+ * Maximum number of documents to fetch per checkpoint (Firestore query `limit`).
208
332
  *
209
- * The actual limit passed to the query will be calculated between this value, the totalSnapshotsLimit value, and the remaining number of snapshots to load.
333
+ * The effective limit is the minimum of this value and the remaining documents
334
+ * allowed by `totalSnapshotsLimit`. Defaults to `totalSnapshotsLimit` if not set.
210
335
  *
211
- * A limit of 0 is NOT considered as unlimited and will cause the function to end immediately.
336
+ * A value of `0` causes immediate termination with no documents loaded it is NOT
337
+ * treated as "unlimited".
212
338
  */
213
339
  readonly limitPerCheckpoint?: Maybe<number>;
214
340
  /**
215
- * The total number of snapshots allowed.
341
+ * Maximum total number of document snapshots to load across all checkpoints.
216
342
  *
217
- * Ends on the checkpoint that reaches this limit.
343
+ * Iteration stops after the checkpoint that causes this limit to be reached or exceeded.
344
+ * When omitted, there is no snapshot count limit (pagination continues until results
345
+ * are exhausted or `maxPage`/`endEarly` terminates it).
218
346
  */
219
347
  readonly totalSnapshotsLimit?: Maybe<number>;
220
348
  /**
221
- * The number of max parallel checkpoints to run.
349
+ * Maximum number of checkpoints to process concurrently.
222
350
  *
223
- * By default checkpoints are run serially (max of 1), but can be run in parallel.
351
+ * Defaults to 1 (serial execution). When set higher, checkpoint *processing*
352
+ * (via `iterateCheckpoint`) can overlap, though checkpoint *fetching* remains
353
+ * sequential since each query depends on the previous checkpoint's cursor.
224
354
  */
225
355
  readonly maxParallelCheckpoints?: number;
226
356
  /**
227
- * The amount of time to add as a delay between beginning a new checkpoint.
357
+ * Minimum delay in milliseconds between initiating consecutive checkpoint queries.
228
358
  *
229
- * If in parallel this is the minimum amount of time to wait before starting a new checkpoint.
359
+ * Useful for Firestore rate limiting or quota management. When running checkpoints
360
+ * in parallel, this ensures at least this much time passes between starting each
361
+ * new query.
230
362
  */
231
363
  readonly waitBetweenCheckpoints?: Milliseconds;
232
364
  /**
233
- * Configuration to use when a repeat cursor is visited (potentially indicative of an infinite query loop).
365
+ * Strategy for handling repeat cursor documents, which can indicate an infinite loop.
234
366
  *
235
- * Can be configured with false to exit the iteration immediately and return, or can use a function to decide if the iteration should continue.
367
+ * A repeat cursor occurs when the last document of a checkpoint has already been
368
+ * seen as a cursor in a previous checkpoint — often caused by document updates
369
+ * during iteration that re-match the query.
236
370
  *
237
- * If false is returned the cursor is discarded and the loop will end.
371
+ * - `false` or `undefined`: Immediately stop iteration when a repeat is detected
372
+ * - `async (cursor) => boolean`: Custom handler — return `true` to continue iteration
373
+ * despite the repeat, or `false` to stop
238
374
  */
239
375
  readonly handleRepeatCursor?: Maybe<false | ((cursor: QueryDocumentSnapshot<T>) => Promise<boolean>)>;
240
376
  /**
241
- * Filter function that can be used to filter out snapshots.
377
+ * Optional filter applied to each checkpoint's snapshots before they reach `iterateCheckpoint`.
242
378
  *
243
- * If all snapshots are filtered out then the iteration will continue with final item of the snapshot regardless of filtering. The filtering does not impact the continuation decision.
244
- * Use the handleRepeatCursor to properly exit the loop in unwanted repeat cursor cases.
379
+ * Filtering does not affect pagination decisions the cursor for the next checkpoint
380
+ * is always based on the last *unfiltered* document. If all documents in a checkpoint
381
+ * are filtered out, iteration continues to the next checkpoint.
245
382
  *
246
- * @param snapshot
247
- * @returns
383
+ * Use {@link filterRepeatCheckpointSnapshots} to deduplicate documents that appear
384
+ * in multiple checkpoints. Combine with `handleRepeatCursor` to properly terminate
385
+ * iteration when filtering causes repeated cursors.
248
386
  */
249
387
  readonly filterCheckpointSnapshots?: IterateFirestoreDocumentSnapshotCheckpointsFilterCheckpointSnapshotsFunction<T>;
250
388
  /**
251
- * The iterate function per each snapshot.
389
+ * Core callback invoked once per checkpoint with the (optionally filtered) document snapshots.
390
+ *
391
+ * Receives all snapshots for this checkpoint and the raw query snapshot for metadata access.
392
+ * Returns an array of results — one per logical unit processed (could be per-document,
393
+ * per-batch, or any grouping the implementation chooses).
394
+ *
395
+ * @param snapshots - Document snapshots for this checkpoint (post-filter)
396
+ * @param query - The raw Firestore query snapshot for this checkpoint
252
397
  */
253
398
  iterateCheckpoint(snapshots: QueryDocumentSnapshot<T>[], query: QuerySnapshot<T>): Promise<R[]>;
254
399
  /**
255
- * (Optional) Called at the end of each checkpoint.
400
+ * Optional side-effect callback invoked after each checkpoint is fully processed.
401
+ *
402
+ * Called after `iterateCheckpoint` completes, receiving the full iteration result
403
+ * including checkpoint index, cursor state, results, and raw snapshots. Useful for
404
+ * progress logging, metrics, or external state accumulation.
405
+ *
406
+ * @param checkpointResults - Complete iteration result for this checkpoint
256
407
  */
257
408
  useCheckpointResult?(checkpointResults: IterateFirestoreDocumentSnapshotCheckpointsIterationResult<T, R>): PromiseOrValue<void>;
258
409
  }
@@ -268,13 +419,32 @@ export interface IterateFirestoreDocumentSnapshotCheckpointsConfig<T, R> {
268
419
  */
269
420
  export type IterateFirestoreDocumentSnapshotCheckpointsFilterCheckpointSnapshotsFunction<T> = (snapshot: QueryDocumentSnapshot<T>[]) => PromiseOrValue<QueryDocumentSnapshot<T>[]>;
270
421
  /**
271
- * Creates a IterateFirestoreDocumentSnapshotCheckpointsFilterCheckpointSnapshotsFunction that filters out any repeat documents.
422
+ * Creates a checkpoint filter that deduplicates documents across checkpoints.
423
+ *
424
+ * Repeat documents can appear when a document is updated during iteration and
425
+ * re-matches the query in a subsequent checkpoint. This factory returns a stateful
426
+ * filter that tracks seen document keys and removes duplicates.
427
+ *
428
+ * The filter maintains state across checkpoints — use a single instance for the
429
+ * entire iteration run. Pair with `handleRepeatCursor: false` to also terminate
430
+ * iteration when cursor-level repeats are detected.
272
431
  *
273
- * Repeat documents can occur in cases where the document is updated and the query matches it again for a different reason.
274
- * This utility function creates a filter that prevents processing the same document multiple times.
432
+ * @param readKeyFunction - Extracts a unique key from each snapshot; defaults to `snapshot.id`
433
+ * @returns A stateful filter function suitable for `filterCheckpointSnapshots`
275
434
  *
276
- * @param readKeyFunction - Function that extracts a unique key from a document snapshot, defaults to document ID
277
- * @returns A filter function that prevents duplicate document processing
435
+ * @example
436
+ * ```typescript
437
+ * const result = await iterateFirestoreDocumentSnapshotCheckpoints({
438
+ * queryFactory,
439
+ * constraintsFactory: [orderBy('updatedAt')],
440
+ * limitPerCheckpoint: 100,
441
+ * filterCheckpointSnapshots: filterRepeatCheckpointSnapshots(),
442
+ * handleRepeatCursor: false,
443
+ * iterateCheckpoint: async (snapshots) => {
444
+ * return snapshots.map((s) => s.data());
445
+ * }
446
+ * });
447
+ * ```
278
448
  */
279
449
  export declare function filterRepeatCheckpointSnapshots<T>(readKeyFunction?: ReadKeyFunction<QueryDocumentSnapshot<T>>): IterateFirestoreDocumentSnapshotCheckpointsFilterCheckpointSnapshotsFunction<T>;
280
450
  /**
@@ -356,25 +526,59 @@ export interface IterateFirestoreDocumentSnapshotCheckpointsResult {
356
526
  readonly totalSnapshotsLimitReached: boolean;
357
527
  }
358
528
  /**
359
- * Iterates through the results of a Firestore query in several batches.
529
+ * Core cursor-based pagination engine for iterating through Firestore query results.
360
530
  *
361
- * This is the core pagination function that handles cursor-based iteration through
362
- * potentially large Firestore query results. It manages cursor documents, checkpoint
363
- * processing, parallel execution, and various limit controls.
531
+ * This is the foundational function in the Firestore iteration hierarchy. It drives
532
+ * sequential cursor-based pagination: each "checkpoint" executes a Firestore query,
533
+ * uses the last document as a `startAfter` cursor for the next query, and passes
534
+ * results to the `iterateCheckpoint` callback.
364
535
  *
365
- * @template T - The document data type
366
- * @template R - The result type of the iteration
367
- * @param config - Complete configuration for the pagination and processing behavior
368
- * @returns Promise resolving to statistics about the iteration
536
+ * The iteration loop continues until one of these conditions is met:
537
+ * - A query returns no results (no more matching documents)
538
+ * - The `totalSnapshotsLimit` is reached
539
+ * - A repeat cursor is detected and `handleRepeatCursor` returns `false`
540
+ * - The effective `limitPerCheckpoint` calculates to 0 remaining
541
+ *
542
+ * Higher-level functions build on this:
543
+ * - {@link iterateFirestoreDocumentSnapshotBatches} — subdivides checkpoints into smaller batches
544
+ * - {@link iterateFirestoreDocumentSnapshots} — processes one snapshot at a time
545
+ * - {@link iterateFirestoreDocumentSnapshotPairs} — loads typed document wrappers per snapshot
546
+ *
547
+ * @param config - Complete configuration for pagination, processing, and termination behavior
548
+ * @returns Statistics including total checkpoints executed, snapshots visited, and whether the limit was hit
549
+ *
550
+ * @example
551
+ * ```typescript
552
+ * const result = await iterateFirestoreDocumentSnapshotCheckpoints({
553
+ * queryFactory,
554
+ * constraintsFactory: [where('createdAt', '<=', cutoffDate), orderBy('createdAt')],
555
+ * limitPerCheckpoint: 200,
556
+ * totalSnapshotsLimit: 5000,
557
+ * handleRepeatCursor: false,
558
+ * iterateCheckpoint: async (snapshots, querySnapshot) => {
559
+ * return snapshots.map((s) => ({ id: s.id, data: s.data() }));
560
+ * },
561
+ * useCheckpointResult: (result) => {
562
+ * console.log(`Checkpoint ${result.i}: processed ${result.docSnapshots.length} docs`);
563
+ * }
564
+ * });
565
+ * ```
369
566
  */
370
567
  export declare function iterateFirestoreDocumentSnapshotCheckpoints<T, R>(config: IterateFirestoreDocumentSnapshotCheckpointsConfig<T, R>): Promise<IterateFirestoreDocumentSnapshotCheckpointsResult>;
371
568
  /**
372
- * Creates a filter that allows each document snapshot to be processed only once based on its path.
569
+ * Creates a stateful filter that allows each document snapshot through only once,
570
+ * keyed by its full Firestore model path.
571
+ *
572
+ * Unlike {@link filterRepeatCheckpointSnapshots} which uses document ID by default,
573
+ * this filter uses the full document path ({@link readFirestoreModelKeyFromDocumentSnapshot}),
574
+ * making it suitable for cross-collection iteration where document IDs alone may collide.
373
575
  *
374
- * This utility helps prevent duplicate processing of documents by tracking which ones have
375
- * already been seen based on their path.
576
+ * @returns A reusable filter function that passes each unique document path exactly once
376
577
  *
377
- * @template T - The document snapshot type
378
- * @returns A filter function that only allows each unique document to pass once
578
+ * @example
579
+ * ```typescript
580
+ * const onceFilter = allowDocumentSnapshotWithPathOnceFilter();
581
+ * const uniqueSnapshots = allSnapshots.filter(onceFilter);
582
+ * ```
379
583
  */
380
584
  export declare function allowDocumentSnapshotWithPathOnceFilter<T extends DocumentSnapshot>(): AllowValueOnceFilter<T, FirestoreModelKey>;
@@ -1,19 +1,24 @@
1
+ import { type Type } from 'arktype';
1
2
  import { type FirestoreModelKey, type FirestoreModelKeyRef } from '../../firestore/collection/collection';
2
3
  /**
3
- * Simple annotated params that implements FirestoreModelKeyRef.
4
+ * Simple params that implements FirestoreModelKeyRef.
4
5
  */
5
- export declare class TargetModelParams implements FirestoreModelKeyRef {
6
- key: FirestoreModelKey;
6
+ export interface TargetModelParams extends FirestoreModelKeyRef {
7
+ readonly key: FirestoreModelKey;
7
8
  }
8
- export declare class InferredTargetModelParams implements Partial<FirestoreModelKeyRef> {
9
- key?: FirestoreModelKey;
9
+ export declare const targetModelParamsType: Type<TargetModelParams>;
10
+ export interface InferredTargetModelParams extends Partial<FirestoreModelKeyRef> {
11
+ readonly key?: FirestoreModelKey;
10
12
  }
13
+ export declare const inferredTargetModelParamsType: Type<InferredTargetModelParams>;
11
14
  /**
12
- * Simple annotated params that implements FirestoreModelKeyRef but key is a FirestoreModelId.
15
+ * Simple params that implements FirestoreModelKeyRef but key is a FirestoreModelId.
13
16
  */
14
- export declare class TargetModelIdParams implements FirestoreModelKeyRef {
15
- key: FirestoreModelKey;
17
+ export interface TargetModelIdParams extends FirestoreModelKeyRef {
18
+ readonly key: FirestoreModelKey;
16
19
  }
17
- export declare class InferredTargetModelIdParams implements Partial<FirestoreModelKeyRef> {
18
- key?: FirestoreModelKey;
20
+ export declare const targetModelIdParamsType: Type<TargetModelIdParams>;
21
+ export interface InferredTargetModelIdParams extends Partial<FirestoreModelKeyRef> {
22
+ readonly key?: FirestoreModelKey;
19
23
  }
24
+ export declare const inferredTargetModelIdParamsType: Type<InferredTargetModelIdParams>;
@@ -1,14 +1,12 @@
1
- import { type ObjectWithConstructor } from '@dereekb/util';
2
- import { type ValidationOptions } from 'class-validator';
3
1
  /**
4
- * isFirestoreModelKey validator
2
+ * ArkType schema for a FirestoreModelKey (full path like "collection/12345").
5
3
  */
6
- export declare function IsFirestoreModelKey(validationOptions?: ValidationOptions): (object: ObjectWithConstructor, propertyName: string) => void;
4
+ export declare const firestoreModelKeyType: import("arktype/internal/variants/string.ts").StringType<string, {}>;
7
5
  /**
8
- * isFirestoreModelId validator
6
+ * ArkType schema for a FirestoreModelId (document ID like "12345").
9
7
  */
10
- export declare function IsFirestoreModelId(validationOptions?: ValidationOptions): (object: ObjectWithConstructor, propertyName: string) => void;
8
+ export declare const firestoreModelIdType: import("arktype/internal/variants/string.ts").StringType<string, {}>;
11
9
  /**
12
- * isFirestoreModelIdOrKey validator
10
+ * ArkType schema for a FirestoreModelId or FirestoreModelKey.
13
11
  */
14
- export declare function IsFirestoreModelIdOrKey(validationOptions?: ValidationOptions): (object: ObjectWithConstructor, propertyName: string) => void;
12
+ export declare const firestoreModelIdOrKeyType: import("arktype/internal/variants/string.ts").StringType<string, {}>;