@prmichaelsen/firebase-admin-sdk-v8 2.2.0 → 2.2.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Collection iteration functions: `listDocuments()`, `iterateCollection()`, and `countDocuments()`
12
+ - Automatic pagination support for large collections
13
+ - Support for iterating with filters and ordering
14
+
15
+ ### Changed
16
+ - **BREAKING**: `fromFirestoreValue()` now throws an error for unknown Firestore value types instead of returning `null`
17
+ - Removed debug console.log statements from production code in `auth.ts`
18
+
19
+ ### Fixed
20
+ - Silent failures in Firestore data conversion now throw descriptive errors
21
+
22
+ ## [2.2.1] - 2026-02-12
23
+
24
+ ### Added
25
+ - Modular Firestore architecture with separate modules for converters, operations, query-builder, transforms, and path-validation
26
+ - Comprehensive path validation for Firestore operations
27
+ - Support for subcollection queries
28
+
29
+ ### Changed
30
+ - Refactored Firestore implementation into modular structure
31
+ - Improved error messages for path validation
32
+
33
+ ### Fixed
34
+ - Subcollection query support using correct REST API endpoints
35
+ - Path validation for nested collections
36
+
37
+ ## [2.2.0] - Previous Release
38
+
39
+ ### Added
40
+ - Firebase Storage support with signed URLs
41
+ - Custom token creation and sign-in
42
+ - Support for Firebase v10 session tokens
43
+ - Field transforms (serverTimestamp, increment, arrayUnion, arrayRemove, delete)
44
+ - Batch write operations
45
+ - Advanced query support (where, orderBy, limit)
46
+
47
+ ### Changed
48
+ - Improved token verification to support both v9 and v10 token formats
49
+ - Enhanced error handling across all modules
50
+
51
+ ### Fixed
52
+ - Public key caching and rotation handling
53
+ - Token verification for multiple issuer formats
54
+
55
+ ## [2.1.0] - Initial Release
56
+
57
+ ### Added
58
+ - Core Firebase Admin SDK functionality for Cloudflare Workers
59
+ - Authentication with ID token verification
60
+ - Firestore CRUD operations via REST API
61
+ - Service account configuration
62
+ - X.509 certificate handling
63
+ - JWT token generation
package/README.md CHANGED
@@ -514,8 +514,9 @@ For better query performance:
514
514
 
515
515
  | Feature | Supported | Notes |
516
516
  |---------|-----------|-------|
517
- | ID Token Verification | ✅ | Via firebase-auth-cloudflare-workers |
518
- | Custom Token Creation | | Not yet implemented |
517
+ | ID Token Verification | ✅ | Supports v9 and v10 token formats |
518
+ | Custom Token Creation | | createCustomToken() |
519
+ | Custom Token Exchange | ✅ | signInWithCustomToken() |
519
520
  | User Management | ❌ | Not yet implemented |
520
521
  | Firestore CRUD | ✅ | Full support |
521
522
  | Firestore Queries | ✅ | where, orderBy, limit, cursors |
@@ -524,7 +525,7 @@ For better query performance:
524
525
  | **Realtime Listeners** | **❌** | **See explanation below** |
525
526
  | Field Values | ✅ | increment, arrayUnion, serverTimestamp, etc. |
526
527
  | Realtime Database | ❌ | Not planned |
527
- | Cloud Storage | | Not yet implemented |
528
+ | Cloud Storage | | Upload, download, delete, signed URLs |
528
529
  | Cloud Messaging | ❌ | Not yet implemented |
529
530
 
530
531
  ## ⚠️ Realtime Listeners Not Supported
@@ -649,10 +650,12 @@ onSnapshot(doc(db, 'users', 'user123'), (snapshot) => {
649
650
 
650
651
  ## 🗺️ Roadmap
651
652
 
652
- - [ ] Custom token creation
653
+ - [x] Custom token creation ✅ (v2.2.0)
654
+ - [x] Custom token exchange ✅ (v2.2.0)
655
+ - [x] Cloud Storage operations ✅ (v2.2.0)
653
656
  - [ ] User management (create, update, delete users)
654
657
  - [ ] Firestore transactions
655
- - [ ] Cloud Storage operations
656
658
  - [ ] More comprehensive error handling
657
659
  - [ ] Rate limiting helpers
658
660
  - [ ] Retry logic for failed operations
661
+ - [ ] Storage unit tests (currently only e2e)
package/dist/index.d.mts CHANGED
@@ -480,6 +480,114 @@ declare function queryDocuments(collectionPath: string, options?: QueryOptions):
480
480
  */
481
481
  declare function batchWrite(operations: BatchWrite[]): Promise<BatchWriteResult>;
482
482
 
483
+ /**
484
+ * Firebase Admin SDK v8 - Firestore Collection Iteration
485
+ * Convenience functions for iterating through collections
486
+ */
487
+
488
+ /**
489
+ * List all documents in a collection
490
+ * This is a convenience wrapper around queryDocuments with no filters
491
+ *
492
+ * @param collectionPath - Collection path
493
+ * @param options - Query options (limit, orderBy, etc.)
494
+ * @returns Array of documents with id and data
495
+ * @throws {Error} If the operation fails
496
+ *
497
+ * @example
498
+ * ```typescript
499
+ * // List all users
500
+ * const users = await listDocuments('users');
501
+ *
502
+ * // List with pagination
503
+ * const firstPage = await listDocuments('users', { limit: 10 });
504
+ * const secondPage = await listDocuments('users', {
505
+ * limit: 10,
506
+ * startAfter: [firstPage[firstPage.length - 1].data.name]
507
+ * });
508
+ * ```
509
+ */
510
+ declare function listDocuments(collectionPath: string, options?: QueryOptions): Promise<Array<{
511
+ id: string;
512
+ data: DataObject;
513
+ }>>;
514
+ /**
515
+ * Iterate through all documents in a collection
516
+ * Handles pagination automatically by fetching documents in batches
517
+ *
518
+ * @param collectionPath - Collection path
519
+ * @param callback - Function called for each document
520
+ * @param options - Iteration options
521
+ * @throws {Error} If the operation fails
522
+ *
523
+ * @example
524
+ * ```typescript
525
+ * // Process all messages
526
+ * await iterateCollection(
527
+ * 'users/user123/conversations/main/messages',
528
+ * async (msg) => {
529
+ * console.log('Processing message:', msg.id);
530
+ * // Perform operations on each message
531
+ * },
532
+ * { batchSize: 100 }
533
+ * );
534
+ *
535
+ * // Iterate with ordering
536
+ * await iterateCollection(
537
+ * 'users',
538
+ * async (user) => {
539
+ * console.log('User:', user.data.name);
540
+ * },
541
+ * {
542
+ * batchSize: 50,
543
+ * orderBy: [{ field: 'createdAt', direction: 'ASCENDING' }]
544
+ * }
545
+ * );
546
+ * ```
547
+ */
548
+ declare function iterateCollection(collectionPath: string, callback: (doc: {
549
+ id: string;
550
+ data: DataObject;
551
+ }) => Promise<void>, options?: {
552
+ batchSize?: number;
553
+ orderBy?: Array<{
554
+ field: string;
555
+ direction: 'ASCENDING' | 'DESCENDING';
556
+ }>;
557
+ where?: Array<{
558
+ field: string;
559
+ op: any;
560
+ value: any;
561
+ }>;
562
+ }): Promise<void>;
563
+ /**
564
+ * Count documents in a collection
565
+ * Note: This fetches all documents to count them, which may be expensive for large collections
566
+ *
567
+ * @param collectionPath - Collection path
568
+ * @param options - Query options (where filters)
569
+ * @returns Number of documents
570
+ * @throws {Error} If the operation fails
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * // Count all users
575
+ * const totalUsers = await countDocuments('users');
576
+ *
577
+ * // Count active users
578
+ * const activeUsers = await countDocuments('users', {
579
+ * where: [{ field: 'active', op: '==', value: true }]
580
+ * });
581
+ * ```
582
+ */
583
+ declare function countDocuments(collectionPath: string, options?: {
584
+ where?: Array<{
585
+ field: string;
586
+ op: any;
587
+ value: any;
588
+ }>;
589
+ }): Promise<number>;
590
+
483
591
  /**
484
592
  * Firebase Storage client using Google Cloud Storage REST API
485
593
  * Compatible with edge runtimes (Cloudflare Workers, Vercel Edge, etc.)
@@ -760,4 +868,4 @@ declare function getAdminAccessToken(): Promise<string>;
760
868
  */
761
869
  declare function clearTokenCache(): void;
762
870
 
763
- export { type BatchWrite, type BatchWriteResult, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type QueryFilter, type QueryOptions, type QueryOrder, type ServiceAccount, type SetOptions, type SignedUrlOptions, type TokenResponse, type UpdateOptions, type UploadOptions, type UserInfo, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, createCustomToken, deleteDocument, deleteFile, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserFromToken, initializeApp, listFiles, queryDocuments, setDocument, signInWithCustomToken, updateDocument, uploadFile, verifyIdToken };
871
+ export { type BatchWrite, type BatchWriteResult, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type QueryFilter, type QueryOptions, type QueryOrder, type ServiceAccount, type SetOptions, type SignedUrlOptions, type TokenResponse, type UpdateOptions, type UploadOptions, type UserInfo, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, deleteDocument, deleteFile, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, queryDocuments, setDocument, signInWithCustomToken, updateDocument, uploadFile, verifyIdToken };
package/dist/index.d.ts CHANGED
@@ -480,6 +480,114 @@ declare function queryDocuments(collectionPath: string, options?: QueryOptions):
480
480
  */
481
481
  declare function batchWrite(operations: BatchWrite[]): Promise<BatchWriteResult>;
482
482
 
483
+ /**
484
+ * Firebase Admin SDK v8 - Firestore Collection Iteration
485
+ * Convenience functions for iterating through collections
486
+ */
487
+
488
+ /**
489
+ * List all documents in a collection
490
+ * This is a convenience wrapper around queryDocuments with no filters
491
+ *
492
+ * @param collectionPath - Collection path
493
+ * @param options - Query options (limit, orderBy, etc.)
494
+ * @returns Array of documents with id and data
495
+ * @throws {Error} If the operation fails
496
+ *
497
+ * @example
498
+ * ```typescript
499
+ * // List all users
500
+ * const users = await listDocuments('users');
501
+ *
502
+ * // List with pagination
503
+ * const firstPage = await listDocuments('users', { limit: 10 });
504
+ * const secondPage = await listDocuments('users', {
505
+ * limit: 10,
506
+ * startAfter: [firstPage[firstPage.length - 1].data.name]
507
+ * });
508
+ * ```
509
+ */
510
+ declare function listDocuments(collectionPath: string, options?: QueryOptions): Promise<Array<{
511
+ id: string;
512
+ data: DataObject;
513
+ }>>;
514
+ /**
515
+ * Iterate through all documents in a collection
516
+ * Handles pagination automatically by fetching documents in batches
517
+ *
518
+ * @param collectionPath - Collection path
519
+ * @param callback - Function called for each document
520
+ * @param options - Iteration options
521
+ * @throws {Error} If the operation fails
522
+ *
523
+ * @example
524
+ * ```typescript
525
+ * // Process all messages
526
+ * await iterateCollection(
527
+ * 'users/user123/conversations/main/messages',
528
+ * async (msg) => {
529
+ * console.log('Processing message:', msg.id);
530
+ * // Perform operations on each message
531
+ * },
532
+ * { batchSize: 100 }
533
+ * );
534
+ *
535
+ * // Iterate with ordering
536
+ * await iterateCollection(
537
+ * 'users',
538
+ * async (user) => {
539
+ * console.log('User:', user.data.name);
540
+ * },
541
+ * {
542
+ * batchSize: 50,
543
+ * orderBy: [{ field: 'createdAt', direction: 'ASCENDING' }]
544
+ * }
545
+ * );
546
+ * ```
547
+ */
548
+ declare function iterateCollection(collectionPath: string, callback: (doc: {
549
+ id: string;
550
+ data: DataObject;
551
+ }) => Promise<void>, options?: {
552
+ batchSize?: number;
553
+ orderBy?: Array<{
554
+ field: string;
555
+ direction: 'ASCENDING' | 'DESCENDING';
556
+ }>;
557
+ where?: Array<{
558
+ field: string;
559
+ op: any;
560
+ value: any;
561
+ }>;
562
+ }): Promise<void>;
563
+ /**
564
+ * Count documents in a collection
565
+ * Note: This fetches all documents to count them, which may be expensive for large collections
566
+ *
567
+ * @param collectionPath - Collection path
568
+ * @param options - Query options (where filters)
569
+ * @returns Number of documents
570
+ * @throws {Error} If the operation fails
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * // Count all users
575
+ * const totalUsers = await countDocuments('users');
576
+ *
577
+ * // Count active users
578
+ * const activeUsers = await countDocuments('users', {
579
+ * where: [{ field: 'active', op: '==', value: true }]
580
+ * });
581
+ * ```
582
+ */
583
+ declare function countDocuments(collectionPath: string, options?: {
584
+ where?: Array<{
585
+ field: string;
586
+ op: any;
587
+ value: any;
588
+ }>;
589
+ }): Promise<number>;
590
+
483
591
  /**
484
592
  * Firebase Storage client using Google Cloud Storage REST API
485
593
  * Compatible with edge runtimes (Cloudflare Workers, Vercel Edge, etc.)
@@ -760,4 +868,4 @@ declare function getAdminAccessToken(): Promise<string>;
760
868
  */
761
869
  declare function clearTokenCache(): void;
762
870
 
763
- export { type BatchWrite, type BatchWriteResult, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type QueryFilter, type QueryOptions, type QueryOrder, type ServiceAccount, type SetOptions, type SignedUrlOptions, type TokenResponse, type UpdateOptions, type UploadOptions, type UserInfo, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, createCustomToken, deleteDocument, deleteFile, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserFromToken, initializeApp, listFiles, queryDocuments, setDocument, signInWithCustomToken, updateDocument, uploadFile, verifyIdToken };
871
+ export { type BatchWrite, type BatchWriteResult, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type QueryFilter, type QueryOptions, type QueryOrder, type ServiceAccount, type SetOptions, type SignedUrlOptions, type TokenResponse, type UpdateOptions, type UploadOptions, type UserInfo, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, deleteDocument, deleteFile, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, queryDocuments, setDocument, signInWithCustomToken, updateDocument, uploadFile, verifyIdToken };
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  batchWrite: () => batchWrite,
26
26
  clearConfig: () => clearConfig,
27
27
  clearTokenCache: () => clearTokenCache,
28
+ countDocuments: () => countDocuments,
28
29
  createCustomToken: () => createCustomToken,
29
30
  deleteDocument: () => deleteDocument,
30
31
  deleteFile: () => deleteFile,
@@ -40,6 +41,8 @@ __export(index_exports, {
40
41
  getServiceAccount: () => getServiceAccount,
41
42
  getUserFromToken: () => getUserFromToken,
42
43
  initializeApp: () => initializeApp,
44
+ iterateCollection: () => iterateCollection,
45
+ listDocuments: () => listDocuments,
43
46
  listFiles: () => listFiles,
44
47
  queryDocuments: () => queryDocuments,
45
48
  setDocument: () => setDocument,
@@ -228,14 +231,12 @@ async function fetchPublicKeys(issuer) {
228
231
  if (publicKeysCache && Date.now() < publicKeysCacheExpiry) {
229
232
  return publicKeysCache;
230
233
  }
231
- console.log(`[fetchPublicKeys] Fetching from: ${endpoint}`);
232
234
  const response = await fetch(endpoint);
233
235
  if (!response.ok) {
234
236
  throw new Error(`Failed to fetch Firebase public keys from ${endpoint}`);
235
237
  }
236
238
  publicKeysCache = await response.json();
237
239
  publicKeysCacheExpiry = Date.now() + 36e5;
238
- console.log(`[fetchPublicKeys] Fetched ${Object.keys(publicKeysCache || {}).length} keys`);
239
240
  return publicKeysCache;
240
241
  }
241
242
  function base64UrlDecode(str) {
@@ -311,7 +312,6 @@ async function verifyIdToken(idToken) {
311
312
  let publicKeys = await fetchPublicKeys(payload.iss);
312
313
  let publicKeyPem = publicKeys[header.kid];
313
314
  if (!publicKeyPem) {
314
- console.log(`[verifyIdToken] Key ${header.kid} not found in cache, refreshing keys...`);
315
315
  publicKeysCache = null;
316
316
  publicKeysCacheExpiry = 0;
317
317
  publicKeys = await fetchPublicKeys(payload.iss);
@@ -578,7 +578,7 @@ function fromFirestoreValue(value) {
578
578
  if ("mapValue" in value) {
579
579
  return convertFromFirestoreFormat(value.mapValue.fields || {});
580
580
  }
581
- return null;
581
+ throw new Error(`Unknown Firestore value type: ${JSON.stringify(value)}`);
582
582
  }
583
583
  function convertFromFirestoreFormat(fields) {
584
584
  if (!fields) {
@@ -1130,6 +1130,46 @@ async function batchWrite(operations) {
1130
1130
  return await response.json();
1131
1131
  }
1132
1132
 
1133
+ // src/firestore/iteration.ts
1134
+ async function listDocuments(collectionPath, options) {
1135
+ return queryDocuments(collectionPath, options);
1136
+ }
1137
+ async function iterateCollection(collectionPath, callback, options) {
1138
+ const batchSize = options?.batchSize || 100;
1139
+ let lastDoc = null;
1140
+ let hasMore = true;
1141
+ while (hasMore) {
1142
+ const queryOptions = {
1143
+ limit: batchSize,
1144
+ orderBy: options?.orderBy,
1145
+ where: options?.where
1146
+ };
1147
+ if (lastDoc && options?.orderBy && options.orderBy.length > 0) {
1148
+ const cursorValues = options.orderBy.map((order) => lastDoc.data[order.field]);
1149
+ queryOptions.startAfter = cursorValues;
1150
+ }
1151
+ const docs = await queryDocuments(collectionPath, queryOptions);
1152
+ for (const doc of docs) {
1153
+ await callback(doc);
1154
+ }
1155
+ hasMore = docs.length === batchSize;
1156
+ if (hasMore && docs.length > 0) {
1157
+ lastDoc = docs[docs.length - 1];
1158
+ }
1159
+ }
1160
+ }
1161
+ async function countDocuments(collectionPath, options) {
1162
+ let count = 0;
1163
+ await iterateCollection(
1164
+ collectionPath,
1165
+ async () => {
1166
+ count++;
1167
+ },
1168
+ { batchSize: 1e3, where: options?.where }
1169
+ );
1170
+ return count;
1171
+ }
1172
+
1133
1173
  // src/storage/client.ts
1134
1174
  var STORAGE_API_BASE = "https://storage.googleapis.com/storage/v1";
1135
1175
  var UPLOAD_API_BASE = "https://storage.googleapis.com/upload/storage/v1";
@@ -1445,6 +1485,7 @@ async function generateSignedUrl(path, options) {
1445
1485
  batchWrite,
1446
1486
  clearConfig,
1447
1487
  clearTokenCache,
1488
+ countDocuments,
1448
1489
  createCustomToken,
1449
1490
  deleteDocument,
1450
1491
  deleteFile,
@@ -1460,6 +1501,8 @@ async function generateSignedUrl(path, options) {
1460
1501
  getServiceAccount,
1461
1502
  getUserFromToken,
1462
1503
  initializeApp,
1504
+ iterateCollection,
1505
+ listDocuments,
1463
1506
  listFiles,
1464
1507
  queryDocuments,
1465
1508
  setDocument,
package/dist/index.mjs CHANGED
@@ -176,14 +176,12 @@ async function fetchPublicKeys(issuer) {
176
176
  if (publicKeysCache && Date.now() < publicKeysCacheExpiry) {
177
177
  return publicKeysCache;
178
178
  }
179
- console.log(`[fetchPublicKeys] Fetching from: ${endpoint}`);
180
179
  const response = await fetch(endpoint);
181
180
  if (!response.ok) {
182
181
  throw new Error(`Failed to fetch Firebase public keys from ${endpoint}`);
183
182
  }
184
183
  publicKeysCache = await response.json();
185
184
  publicKeysCacheExpiry = Date.now() + 36e5;
186
- console.log(`[fetchPublicKeys] Fetched ${Object.keys(publicKeysCache || {}).length} keys`);
187
185
  return publicKeysCache;
188
186
  }
189
187
  function base64UrlDecode(str) {
@@ -259,7 +257,6 @@ async function verifyIdToken(idToken) {
259
257
  let publicKeys = await fetchPublicKeys(payload.iss);
260
258
  let publicKeyPem = publicKeys[header.kid];
261
259
  if (!publicKeyPem) {
262
- console.log(`[verifyIdToken] Key ${header.kid} not found in cache, refreshing keys...`);
263
260
  publicKeysCache = null;
264
261
  publicKeysCacheExpiry = 0;
265
262
  publicKeys = await fetchPublicKeys(payload.iss);
@@ -526,7 +523,7 @@ function fromFirestoreValue(value) {
526
523
  if ("mapValue" in value) {
527
524
  return convertFromFirestoreFormat(value.mapValue.fields || {});
528
525
  }
529
- return null;
526
+ throw new Error(`Unknown Firestore value type: ${JSON.stringify(value)}`);
530
527
  }
531
528
  function convertFromFirestoreFormat(fields) {
532
529
  if (!fields) {
@@ -1078,6 +1075,46 @@ async function batchWrite(operations) {
1078
1075
  return await response.json();
1079
1076
  }
1080
1077
 
1078
+ // src/firestore/iteration.ts
1079
+ async function listDocuments(collectionPath, options) {
1080
+ return queryDocuments(collectionPath, options);
1081
+ }
1082
+ async function iterateCollection(collectionPath, callback, options) {
1083
+ const batchSize = options?.batchSize || 100;
1084
+ let lastDoc = null;
1085
+ let hasMore = true;
1086
+ while (hasMore) {
1087
+ const queryOptions = {
1088
+ limit: batchSize,
1089
+ orderBy: options?.orderBy,
1090
+ where: options?.where
1091
+ };
1092
+ if (lastDoc && options?.orderBy && options.orderBy.length > 0) {
1093
+ const cursorValues = options.orderBy.map((order) => lastDoc.data[order.field]);
1094
+ queryOptions.startAfter = cursorValues;
1095
+ }
1096
+ const docs = await queryDocuments(collectionPath, queryOptions);
1097
+ for (const doc of docs) {
1098
+ await callback(doc);
1099
+ }
1100
+ hasMore = docs.length === batchSize;
1101
+ if (hasMore && docs.length > 0) {
1102
+ lastDoc = docs[docs.length - 1];
1103
+ }
1104
+ }
1105
+ }
1106
+ async function countDocuments(collectionPath, options) {
1107
+ let count = 0;
1108
+ await iterateCollection(
1109
+ collectionPath,
1110
+ async () => {
1111
+ count++;
1112
+ },
1113
+ { batchSize: 1e3, where: options?.where }
1114
+ );
1115
+ return count;
1116
+ }
1117
+
1081
1118
  // src/storage/client.ts
1082
1119
  var STORAGE_API_BASE = "https://storage.googleapis.com/storage/v1";
1083
1120
  var UPLOAD_API_BASE = "https://storage.googleapis.com/upload/storage/v1";
@@ -1392,6 +1429,7 @@ export {
1392
1429
  batchWrite,
1393
1430
  clearConfig,
1394
1431
  clearTokenCache,
1432
+ countDocuments,
1395
1433
  createCustomToken,
1396
1434
  deleteDocument,
1397
1435
  deleteFile,
@@ -1407,6 +1445,8 @@ export {
1407
1445
  getServiceAccount,
1408
1446
  getUserFromToken,
1409
1447
  initializeApp,
1448
+ iterateCollection,
1449
+ listDocuments,
1410
1450
  listFiles,
1411
1451
  queryDocuments,
1412
1452
  setDocument,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/firebase-admin-sdk-v8",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Firebase Admin SDK for Cloudflare Workers and edge runtimes using REST APIs",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",