@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 +63 -0
- package/README.md +8 -5
- package/dist/index.d.mts +109 -1
- package/dist/index.d.ts +109 -1
- package/dist/index.js +47 -4
- package/dist/index.mjs +44 -4
- package/package.json +1 -1
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 | ✅ |
|
|
518
|
-
| Custom Token Creation |
|
|
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 |
|
|
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
|
-
- [
|
|
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
|
-
|
|
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
|
-
|
|
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