@prmichaelsen/firebase-admin-sdk-v8 2.7.0 → 2.9.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.
- package/.claude/settings.local.json +7 -1
- package/.github/workflows/publish.yml +75 -0
- package/AGENT.md +663 -252
- package/CHANGELOG.md +22 -0
- package/dist/index.d.mts +71 -1
- package/dist/index.d.ts +71 -1
- package/dist/index.js +103 -0
- package/dist/index.mjs +101 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.9.0] - 2026-03-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Password Reset Link Generation (`generatePasswordResetLink`)**: Generate password reset links via Identity Toolkit API
|
|
12
|
+
- `generatePasswordResetLink(email, actionCodeSettings?)` - Generate out-of-band password reset link
|
|
13
|
+
- Supports `ActionCodeSettings` for custom continue URLs, mobile deep linking (iOS/Android), and custom hosting domains
|
|
14
|
+
- Returns the OOB link for sending via custom email delivery
|
|
15
|
+
- 6 unit tests + 3 e2e tests
|
|
16
|
+
|
|
17
|
+
## [2.8.0] - 2026-03-12
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **Firestore Batch Get by Paths (`getAllByPaths`)**: Fetch multiple documents from different collections in a single REST API call
|
|
21
|
+
- `getAllByPaths([{ collection, id }])` - Batch get up to 100 documents from arbitrary paths via `documents:batchGet` endpoint
|
|
22
|
+
- Returns results in the same order as input refs
|
|
23
|
+
- Missing documents return `null` (no throw)
|
|
24
|
+
- Supports subcollection paths (e.g., `users/uid1/profile`)
|
|
25
|
+
- 7 unit tests + 3 e2e tests
|
|
26
|
+
|
|
8
27
|
## [2.7.0] - 2026-03-12
|
|
9
28
|
|
|
10
29
|
### Added
|
|
@@ -16,6 +35,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
35
|
- Supports subcollection paths
|
|
17
36
|
- 8 unit tests + 5 e2e tests
|
|
18
37
|
|
|
38
|
+
### Fixed
|
|
39
|
+
- **FCM**: `sendMessage` now uses `getProjectId()` instead of `getConfig().projectId`, fixing silent failure when `initializeApp()` hasn't been called (falls back to `FIREBASE_PROJECT_ID` env var)
|
|
40
|
+
|
|
19
41
|
### Changed
|
|
20
42
|
- Total unit tests increased from 498 to 506 (+8 tests)
|
|
21
43
|
|
package/dist/index.d.mts
CHANGED
|
@@ -258,6 +258,36 @@ interface ListUsersResult {
|
|
|
258
258
|
/** Token for fetching the next page (if available) */
|
|
259
259
|
pageToken?: string;
|
|
260
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Settings for email action links (password reset, email verification, etc.)
|
|
263
|
+
*/
|
|
264
|
+
interface ActionCodeSettings {
|
|
265
|
+
/** The continue/state URL to redirect to after the action is completed */
|
|
266
|
+
url: string;
|
|
267
|
+
/** Whether the action link should be opened in a mobile app or web */
|
|
268
|
+
handleCodeInApp?: boolean;
|
|
269
|
+
/** iOS-specific settings */
|
|
270
|
+
iOS?: {
|
|
271
|
+
/** The iOS bundle ID of the app to open the link in */
|
|
272
|
+
bundleId: string;
|
|
273
|
+
};
|
|
274
|
+
/** Android-specific settings */
|
|
275
|
+
android?: {
|
|
276
|
+
/** The Android package name of the app to open the link in */
|
|
277
|
+
packageName: string;
|
|
278
|
+
/** Whether to install the app if not already installed */
|
|
279
|
+
installApp?: boolean;
|
|
280
|
+
/** Minimum Android app version required */
|
|
281
|
+
minimumVersion?: string;
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* The domain to use for Dynamic Links
|
|
285
|
+
* @deprecated Use linkDomain instead
|
|
286
|
+
*/
|
|
287
|
+
dynamicLinkDomain?: string;
|
|
288
|
+
/** Custom Firebase Hosting domain to use for the link */
|
|
289
|
+
linkDomain?: string;
|
|
290
|
+
}
|
|
261
291
|
|
|
262
292
|
/**
|
|
263
293
|
* Firebase Admin SDK v8 - Configuration
|
|
@@ -518,6 +548,26 @@ declare function listUsers(maxResults?: number, pageToken?: string): Promise<Lis
|
|
|
518
548
|
* @param customClaims - Custom claims object (max 1000 bytes when serialized)
|
|
519
549
|
*/
|
|
520
550
|
declare function setCustomUserClaims(uid: string, customClaims: Record<string, any> | null): Promise<void>;
|
|
551
|
+
/**
|
|
552
|
+
* Generate a password reset link for the given email address
|
|
553
|
+
*
|
|
554
|
+
* Returns an out-of-band (OOB) link that can be sent to the user
|
|
555
|
+
* via a custom email delivery mechanism.
|
|
556
|
+
*
|
|
557
|
+
* @param email - User's email address
|
|
558
|
+
* @param actionCodeSettings - Optional settings for the action code
|
|
559
|
+
* @returns Password reset link URL
|
|
560
|
+
*
|
|
561
|
+
* @example
|
|
562
|
+
* ```typescript
|
|
563
|
+
* const link = await generatePasswordResetLink('user@example.com', {
|
|
564
|
+
* url: 'https://example.com/login',
|
|
565
|
+
* });
|
|
566
|
+
* // Send the link via your own email service
|
|
567
|
+
* await sendEmail(email, `Reset your password: ${link}`);
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
declare function generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise<string>;
|
|
521
571
|
|
|
522
572
|
/**
|
|
523
573
|
* Firebase Admin SDK v8 - Firestore CRUD Operations
|
|
@@ -656,6 +706,26 @@ declare function queryDocuments(collectionPath: string, options?: QueryOptions):
|
|
|
656
706
|
* ```
|
|
657
707
|
*/
|
|
658
708
|
declare function getAll(collectionPath: string, documentIds: string[]): Promise<(DataObject | null)[]>;
|
|
709
|
+
/**
|
|
710
|
+
* Batch get multiple documents from different collections by their full paths
|
|
711
|
+
* Uses the documents:batchGet REST API endpoint
|
|
712
|
+
*
|
|
713
|
+
* @param documentRefs - Array of { collection, id } tuples pointing to documents (max 100)
|
|
714
|
+
* @returns Array of results in the same order as documentRefs. Missing documents return null.
|
|
715
|
+
* @throws {Error} If the operation fails or more than 100 documents requested
|
|
716
|
+
*
|
|
717
|
+
* @example
|
|
718
|
+
* ```typescript
|
|
719
|
+
* const docs = await getAllByPaths([
|
|
720
|
+
* { collection: 'users/uid1/profile', id: 'default' },
|
|
721
|
+
* { collection: 'users/uid2/profile', id: 'default' },
|
|
722
|
+
* ]);
|
|
723
|
+
* ```
|
|
724
|
+
*/
|
|
725
|
+
declare function getAllByPaths(documentRefs: Array<{
|
|
726
|
+
collection: string;
|
|
727
|
+
id: string;
|
|
728
|
+
}>): Promise<(DataObject | null)[]>;
|
|
659
729
|
/**
|
|
660
730
|
* Perform batch write operations (set, update, delete)
|
|
661
731
|
*
|
|
@@ -1266,4 +1336,4 @@ declare function getAdminAccessToken(): Promise<string>;
|
|
|
1266
1336
|
*/
|
|
1267
1337
|
declare function clearTokenCache(): void;
|
|
1268
1338
|
|
|
1269
|
-
export { type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAll, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
|
|
1339
|
+
export { type ActionCodeSettings, type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generatePasswordResetLink, generateSignedUrl, getAdminAccessToken, getAll, getAllByPaths, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
|
package/dist/index.d.ts
CHANGED
|
@@ -258,6 +258,36 @@ interface ListUsersResult {
|
|
|
258
258
|
/** Token for fetching the next page (if available) */
|
|
259
259
|
pageToken?: string;
|
|
260
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Settings for email action links (password reset, email verification, etc.)
|
|
263
|
+
*/
|
|
264
|
+
interface ActionCodeSettings {
|
|
265
|
+
/** The continue/state URL to redirect to after the action is completed */
|
|
266
|
+
url: string;
|
|
267
|
+
/** Whether the action link should be opened in a mobile app or web */
|
|
268
|
+
handleCodeInApp?: boolean;
|
|
269
|
+
/** iOS-specific settings */
|
|
270
|
+
iOS?: {
|
|
271
|
+
/** The iOS bundle ID of the app to open the link in */
|
|
272
|
+
bundleId: string;
|
|
273
|
+
};
|
|
274
|
+
/** Android-specific settings */
|
|
275
|
+
android?: {
|
|
276
|
+
/** The Android package name of the app to open the link in */
|
|
277
|
+
packageName: string;
|
|
278
|
+
/** Whether to install the app if not already installed */
|
|
279
|
+
installApp?: boolean;
|
|
280
|
+
/** Minimum Android app version required */
|
|
281
|
+
minimumVersion?: string;
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* The domain to use for Dynamic Links
|
|
285
|
+
* @deprecated Use linkDomain instead
|
|
286
|
+
*/
|
|
287
|
+
dynamicLinkDomain?: string;
|
|
288
|
+
/** Custom Firebase Hosting domain to use for the link */
|
|
289
|
+
linkDomain?: string;
|
|
290
|
+
}
|
|
261
291
|
|
|
262
292
|
/**
|
|
263
293
|
* Firebase Admin SDK v8 - Configuration
|
|
@@ -518,6 +548,26 @@ declare function listUsers(maxResults?: number, pageToken?: string): Promise<Lis
|
|
|
518
548
|
* @param customClaims - Custom claims object (max 1000 bytes when serialized)
|
|
519
549
|
*/
|
|
520
550
|
declare function setCustomUserClaims(uid: string, customClaims: Record<string, any> | null): Promise<void>;
|
|
551
|
+
/**
|
|
552
|
+
* Generate a password reset link for the given email address
|
|
553
|
+
*
|
|
554
|
+
* Returns an out-of-band (OOB) link that can be sent to the user
|
|
555
|
+
* via a custom email delivery mechanism.
|
|
556
|
+
*
|
|
557
|
+
* @param email - User's email address
|
|
558
|
+
* @param actionCodeSettings - Optional settings for the action code
|
|
559
|
+
* @returns Password reset link URL
|
|
560
|
+
*
|
|
561
|
+
* @example
|
|
562
|
+
* ```typescript
|
|
563
|
+
* const link = await generatePasswordResetLink('user@example.com', {
|
|
564
|
+
* url: 'https://example.com/login',
|
|
565
|
+
* });
|
|
566
|
+
* // Send the link via your own email service
|
|
567
|
+
* await sendEmail(email, `Reset your password: ${link}`);
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
declare function generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise<string>;
|
|
521
571
|
|
|
522
572
|
/**
|
|
523
573
|
* Firebase Admin SDK v8 - Firestore CRUD Operations
|
|
@@ -656,6 +706,26 @@ declare function queryDocuments(collectionPath: string, options?: QueryOptions):
|
|
|
656
706
|
* ```
|
|
657
707
|
*/
|
|
658
708
|
declare function getAll(collectionPath: string, documentIds: string[]): Promise<(DataObject | null)[]>;
|
|
709
|
+
/**
|
|
710
|
+
* Batch get multiple documents from different collections by their full paths
|
|
711
|
+
* Uses the documents:batchGet REST API endpoint
|
|
712
|
+
*
|
|
713
|
+
* @param documentRefs - Array of { collection, id } tuples pointing to documents (max 100)
|
|
714
|
+
* @returns Array of results in the same order as documentRefs. Missing documents return null.
|
|
715
|
+
* @throws {Error} If the operation fails or more than 100 documents requested
|
|
716
|
+
*
|
|
717
|
+
* @example
|
|
718
|
+
* ```typescript
|
|
719
|
+
* const docs = await getAllByPaths([
|
|
720
|
+
* { collection: 'users/uid1/profile', id: 'default' },
|
|
721
|
+
* { collection: 'users/uid2/profile', id: 'default' },
|
|
722
|
+
* ]);
|
|
723
|
+
* ```
|
|
724
|
+
*/
|
|
725
|
+
declare function getAllByPaths(documentRefs: Array<{
|
|
726
|
+
collection: string;
|
|
727
|
+
id: string;
|
|
728
|
+
}>): Promise<(DataObject | null)[]>;
|
|
659
729
|
/**
|
|
660
730
|
* Perform batch write operations (set, update, delete)
|
|
661
731
|
*
|
|
@@ -1266,4 +1336,4 @@ declare function getAdminAccessToken(): Promise<string>;
|
|
|
1266
1336
|
*/
|
|
1267
1337
|
declare function clearTokenCache(): void;
|
|
1268
1338
|
|
|
1269
|
-
export { type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAll, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
|
|
1339
|
+
export { type ActionCodeSettings, type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generatePasswordResetLink, generateSignedUrl, getAdminAccessToken, getAll, getAllByPaths, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
|
package/dist/index.js
CHANGED
|
@@ -214,9 +214,11 @@ __export(index_exports, {
|
|
|
214
214
|
deleteUser: () => deleteUser,
|
|
215
215
|
downloadFile: () => downloadFile,
|
|
216
216
|
fileExists: () => fileExists,
|
|
217
|
+
generatePasswordResetLink: () => generatePasswordResetLink,
|
|
217
218
|
generateSignedUrl: () => generateSignedUrl,
|
|
218
219
|
getAdminAccessToken: () => getAdminAccessToken,
|
|
219
220
|
getAll: () => getAll,
|
|
221
|
+
getAllByPaths: () => getAllByPaths,
|
|
220
222
|
getAuth: () => getAuth,
|
|
221
223
|
getConfig: () => getConfig,
|
|
222
224
|
getDocument: () => getDocument,
|
|
@@ -953,6 +955,67 @@ async function setCustomUserClaims(uid, customClaims) {
|
|
|
953
955
|
throw new Error(`Failed to set custom claims: ${response.status} ${errorText}`);
|
|
954
956
|
}
|
|
955
957
|
}
|
|
958
|
+
function buildActionCodeSettingsRequest(settings) {
|
|
959
|
+
const request = {};
|
|
960
|
+
request.continueUrl = settings.url;
|
|
961
|
+
if (typeof settings.handleCodeInApp === "boolean") {
|
|
962
|
+
request.canHandleCodeInApp = settings.handleCodeInApp;
|
|
963
|
+
}
|
|
964
|
+
if (settings.iOS?.bundleId) {
|
|
965
|
+
request.iOSBundleId = settings.iOS.bundleId;
|
|
966
|
+
}
|
|
967
|
+
if (settings.android) {
|
|
968
|
+
request.androidPackageName = settings.android.packageName;
|
|
969
|
+
if (typeof settings.android.installApp === "boolean") {
|
|
970
|
+
request.androidInstallApp = settings.android.installApp;
|
|
971
|
+
}
|
|
972
|
+
if (settings.android.minimumVersion) {
|
|
973
|
+
request.androidMinimumVersion = settings.android.minimumVersion;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (settings.dynamicLinkDomain) {
|
|
977
|
+
request.dynamicLinkDomain = settings.dynamicLinkDomain;
|
|
978
|
+
}
|
|
979
|
+
if (settings.linkDomain) {
|
|
980
|
+
request.linkDomain = settings.linkDomain;
|
|
981
|
+
}
|
|
982
|
+
return request;
|
|
983
|
+
}
|
|
984
|
+
async function generatePasswordResetLink(email, actionCodeSettings) {
|
|
985
|
+
if (!email || typeof email !== "string") {
|
|
986
|
+
throw new Error("email must be a non-empty string");
|
|
987
|
+
}
|
|
988
|
+
const projectId = getProjectId();
|
|
989
|
+
const accessToken = await getAdminAccessToken();
|
|
990
|
+
const requestBody = {
|
|
991
|
+
requestType: "PASSWORD_RESET",
|
|
992
|
+
email,
|
|
993
|
+
returnOobLink: true
|
|
994
|
+
};
|
|
995
|
+
if (actionCodeSettings) {
|
|
996
|
+
Object.assign(requestBody, buildActionCodeSettingsRequest(actionCodeSettings));
|
|
997
|
+
}
|
|
998
|
+
const response = await fetch(
|
|
999
|
+
`${IDENTITY_TOOLKIT_API}/projects/${projectId}/accounts:sendOobCode`,
|
|
1000
|
+
{
|
|
1001
|
+
method: "POST",
|
|
1002
|
+
headers: {
|
|
1003
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
1004
|
+
"Content-Type": "application/json"
|
|
1005
|
+
},
|
|
1006
|
+
body: JSON.stringify(requestBody)
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
if (!response.ok) {
|
|
1010
|
+
const errorText = await response.text();
|
|
1011
|
+
throw new Error(`Failed to generate password reset link: ${response.status} ${errorText}`);
|
|
1012
|
+
}
|
|
1013
|
+
const data = await response.json();
|
|
1014
|
+
if (!data.oobLink) {
|
|
1015
|
+
throw new Error("Password reset link not returned by server");
|
|
1016
|
+
}
|
|
1017
|
+
return data.oobLink;
|
|
1018
|
+
}
|
|
956
1019
|
|
|
957
1020
|
// src/field-value.ts
|
|
958
1021
|
function serverTimestamp() {
|
|
@@ -1553,6 +1616,44 @@ async function getAll(collectionPath, documentIds) {
|
|
|
1553
1616
|
}
|
|
1554
1617
|
return documents.map((docPath) => resultMap.get(docPath) ?? null);
|
|
1555
1618
|
}
|
|
1619
|
+
async function getAllByPaths(documentRefs) {
|
|
1620
|
+
if (documentRefs.length === 0) return [];
|
|
1621
|
+
if (documentRefs.length > 100) {
|
|
1622
|
+
throw new Error("getAllByPaths supports a maximum of 100 documents per request");
|
|
1623
|
+
}
|
|
1624
|
+
for (const ref of documentRefs) {
|
|
1625
|
+
validateDocumentPath("collection", ref.collection, ref.id);
|
|
1626
|
+
}
|
|
1627
|
+
const accessToken = await getAdminAccessToken();
|
|
1628
|
+
const projectId = getProjectId();
|
|
1629
|
+
const basePath = `projects/${projectId}/databases/(default)/documents`;
|
|
1630
|
+
const documents = documentRefs.map((ref) => `${basePath}/${ref.collection}/${ref.id}`);
|
|
1631
|
+
const url = `${FIRESTORE_API}/${basePath}:batchGet`;
|
|
1632
|
+
const response = await fetch(url, {
|
|
1633
|
+
method: "POST",
|
|
1634
|
+
headers: {
|
|
1635
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
1636
|
+
"Content-Type": "application/json"
|
|
1637
|
+
},
|
|
1638
|
+
body: JSON.stringify({ documents })
|
|
1639
|
+
});
|
|
1640
|
+
if (!response.ok) {
|
|
1641
|
+
const errorText = await response.text();
|
|
1642
|
+
throw new Error(`Failed to batch get documents: ${errorText}`);
|
|
1643
|
+
}
|
|
1644
|
+
const results = await response.json();
|
|
1645
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
1646
|
+
for (const result of results) {
|
|
1647
|
+
if (result.found) {
|
|
1648
|
+
const name = result.found.name;
|
|
1649
|
+
const data = convertFromFirestoreFormat(result.found.fields);
|
|
1650
|
+
resultMap.set(name, data);
|
|
1651
|
+
} else if (result.missing) {
|
|
1652
|
+
resultMap.set(result.missing, null);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
return documents.map((docPath) => resultMap.get(docPath) ?? null);
|
|
1656
|
+
}
|
|
1556
1657
|
async function batchWrite(operations) {
|
|
1557
1658
|
const accessToken = await getAdminAccessToken();
|
|
1558
1659
|
const projectId = getProjectId();
|
|
@@ -2293,9 +2394,11 @@ init_service_account();
|
|
|
2293
2394
|
deleteUser,
|
|
2294
2395
|
downloadFile,
|
|
2295
2396
|
fileExists,
|
|
2397
|
+
generatePasswordResetLink,
|
|
2296
2398
|
generateSignedUrl,
|
|
2297
2399
|
getAdminAccessToken,
|
|
2298
2400
|
getAll,
|
|
2401
|
+
getAllByPaths,
|
|
2299
2402
|
getAuth,
|
|
2300
2403
|
getConfig,
|
|
2301
2404
|
getDocument,
|
package/dist/index.mjs
CHANGED
|
@@ -708,6 +708,67 @@ async function setCustomUserClaims(uid, customClaims) {
|
|
|
708
708
|
throw new Error(`Failed to set custom claims: ${response.status} ${errorText}`);
|
|
709
709
|
}
|
|
710
710
|
}
|
|
711
|
+
function buildActionCodeSettingsRequest(settings) {
|
|
712
|
+
const request = {};
|
|
713
|
+
request.continueUrl = settings.url;
|
|
714
|
+
if (typeof settings.handleCodeInApp === "boolean") {
|
|
715
|
+
request.canHandleCodeInApp = settings.handleCodeInApp;
|
|
716
|
+
}
|
|
717
|
+
if (settings.iOS?.bundleId) {
|
|
718
|
+
request.iOSBundleId = settings.iOS.bundleId;
|
|
719
|
+
}
|
|
720
|
+
if (settings.android) {
|
|
721
|
+
request.androidPackageName = settings.android.packageName;
|
|
722
|
+
if (typeof settings.android.installApp === "boolean") {
|
|
723
|
+
request.androidInstallApp = settings.android.installApp;
|
|
724
|
+
}
|
|
725
|
+
if (settings.android.minimumVersion) {
|
|
726
|
+
request.androidMinimumVersion = settings.android.minimumVersion;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (settings.dynamicLinkDomain) {
|
|
730
|
+
request.dynamicLinkDomain = settings.dynamicLinkDomain;
|
|
731
|
+
}
|
|
732
|
+
if (settings.linkDomain) {
|
|
733
|
+
request.linkDomain = settings.linkDomain;
|
|
734
|
+
}
|
|
735
|
+
return request;
|
|
736
|
+
}
|
|
737
|
+
async function generatePasswordResetLink(email, actionCodeSettings) {
|
|
738
|
+
if (!email || typeof email !== "string") {
|
|
739
|
+
throw new Error("email must be a non-empty string");
|
|
740
|
+
}
|
|
741
|
+
const projectId = getProjectId();
|
|
742
|
+
const accessToken = await getAdminAccessToken();
|
|
743
|
+
const requestBody = {
|
|
744
|
+
requestType: "PASSWORD_RESET",
|
|
745
|
+
email,
|
|
746
|
+
returnOobLink: true
|
|
747
|
+
};
|
|
748
|
+
if (actionCodeSettings) {
|
|
749
|
+
Object.assign(requestBody, buildActionCodeSettingsRequest(actionCodeSettings));
|
|
750
|
+
}
|
|
751
|
+
const response = await fetch(
|
|
752
|
+
`${IDENTITY_TOOLKIT_API}/projects/${projectId}/accounts:sendOobCode`,
|
|
753
|
+
{
|
|
754
|
+
method: "POST",
|
|
755
|
+
headers: {
|
|
756
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
757
|
+
"Content-Type": "application/json"
|
|
758
|
+
},
|
|
759
|
+
body: JSON.stringify(requestBody)
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
if (!response.ok) {
|
|
763
|
+
const errorText = await response.text();
|
|
764
|
+
throw new Error(`Failed to generate password reset link: ${response.status} ${errorText}`);
|
|
765
|
+
}
|
|
766
|
+
const data = await response.json();
|
|
767
|
+
if (!data.oobLink) {
|
|
768
|
+
throw new Error("Password reset link not returned by server");
|
|
769
|
+
}
|
|
770
|
+
return data.oobLink;
|
|
771
|
+
}
|
|
711
772
|
|
|
712
773
|
// src/field-value.ts
|
|
713
774
|
function serverTimestamp() {
|
|
@@ -1304,6 +1365,44 @@ async function getAll(collectionPath, documentIds) {
|
|
|
1304
1365
|
}
|
|
1305
1366
|
return documents.map((docPath) => resultMap.get(docPath) ?? null);
|
|
1306
1367
|
}
|
|
1368
|
+
async function getAllByPaths(documentRefs) {
|
|
1369
|
+
if (documentRefs.length === 0) return [];
|
|
1370
|
+
if (documentRefs.length > 100) {
|
|
1371
|
+
throw new Error("getAllByPaths supports a maximum of 100 documents per request");
|
|
1372
|
+
}
|
|
1373
|
+
for (const ref of documentRefs) {
|
|
1374
|
+
validateDocumentPath("collection", ref.collection, ref.id);
|
|
1375
|
+
}
|
|
1376
|
+
const accessToken = await getAdminAccessToken();
|
|
1377
|
+
const projectId = getProjectId();
|
|
1378
|
+
const basePath = `projects/${projectId}/databases/(default)/documents`;
|
|
1379
|
+
const documents = documentRefs.map((ref) => `${basePath}/${ref.collection}/${ref.id}`);
|
|
1380
|
+
const url = `${FIRESTORE_API}/${basePath}:batchGet`;
|
|
1381
|
+
const response = await fetch(url, {
|
|
1382
|
+
method: "POST",
|
|
1383
|
+
headers: {
|
|
1384
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
1385
|
+
"Content-Type": "application/json"
|
|
1386
|
+
},
|
|
1387
|
+
body: JSON.stringify({ documents })
|
|
1388
|
+
});
|
|
1389
|
+
if (!response.ok) {
|
|
1390
|
+
const errorText = await response.text();
|
|
1391
|
+
throw new Error(`Failed to batch get documents: ${errorText}`);
|
|
1392
|
+
}
|
|
1393
|
+
const results = await response.json();
|
|
1394
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
1395
|
+
for (const result of results) {
|
|
1396
|
+
if (result.found) {
|
|
1397
|
+
const name = result.found.name;
|
|
1398
|
+
const data = convertFromFirestoreFormat(result.found.fields);
|
|
1399
|
+
resultMap.set(name, data);
|
|
1400
|
+
} else if (result.missing) {
|
|
1401
|
+
resultMap.set(result.missing, null);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
return documents.map((docPath) => resultMap.get(docPath) ?? null);
|
|
1405
|
+
}
|
|
1307
1406
|
async function batchWrite(operations) {
|
|
1308
1407
|
const accessToken = await getAdminAccessToken();
|
|
1309
1408
|
const projectId = getProjectId();
|
|
@@ -2032,9 +2131,11 @@ export {
|
|
|
2032
2131
|
deleteUser,
|
|
2033
2132
|
downloadFile,
|
|
2034
2133
|
fileExists,
|
|
2134
|
+
generatePasswordResetLink,
|
|
2035
2135
|
generateSignedUrl,
|
|
2036
2136
|
getAdminAccessToken,
|
|
2037
2137
|
getAll,
|
|
2138
|
+
getAllByPaths,
|
|
2038
2139
|
getAuth,
|
|
2039
2140
|
getConfig,
|
|
2040
2141
|
getDocument,
|
package/package.json
CHANGED