@prmichaelsen/firebase-admin-sdk-v8 2.5.3 → 2.7.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.
@@ -7,7 +7,15 @@
7
7
  "Bash(git add:*)",
8
8
  "Bash(grep:*)",
9
9
  "Bash(npm test:*)",
10
- "Bash(npm run:*)"
10
+ "Bash(npm run:*)",
11
+ "Bash(bash:*)",
12
+ "Bash(npx jest:*)",
13
+ "WebFetch(domain:agentbase.me)",
14
+ "WebSearch",
15
+ "WebFetch(domain:firebase.google.com)",
16
+ "WebFetch(domain:developers.google.com)",
17
+ "Bash(npx tsc:*)",
18
+ "Bash(node --env-file=.env node_modules/.bin/jest --config jest.e2e.config.js --testPathPatterns='firestore/operations.e2e' 2>&1 | tail -20)"
11
19
  ]
12
20
  }
13
21
  }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ 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.7.0] - 2026-03-12
9
+
10
+ ### Added
11
+ - **Firestore Batch Get (`getAll`)**: Fetch multiple documents in a single REST API call
12
+ - `getAll(collectionPath, documentIds)` - Batch get up to 100 documents via `documents:batchGet` endpoint
13
+ - Returns results in the same order as input document IDs
14
+ - Missing documents return `null` (no throw)
15
+ - Enforces 100-document limit with clear error message
16
+ - Supports subcollection paths
17
+ - 8 unit tests + 5 e2e tests
18
+
19
+ ### Changed
20
+ - Total unit tests increased from 498 to 506 (+8 tests)
21
+
22
+ ## [2.6.0] - 2026-03-07
23
+
24
+ ### Added
25
+ - **Firebase Cloud Messaging (FCM)**: Server-side push notification support via FCM HTTP v1 API
26
+ - `sendMessage()` - Send notifications to devices, topics, or conditions
27
+ - `subscribeToTopic()` - Subscribe up to 1000 device tokens to a topic
28
+ - `unsubscribeFromTopic()` - Unsubscribe device tokens from a topic
29
+ - Full TypeScript types for Message, Notification, AndroidConfig, WebpushConfig, ApnsConfig
30
+ - Platform-specific configuration support (Android, Web, iOS)
31
+ - Data-only messages and notification+data payloads
32
+ - Topic name normalization (auto-prefixes `/topics/` when missing)
33
+ - 29 comprehensive unit tests
34
+ - New module: `src/messaging/` (client, types, index)
35
+ - New exported types: `Message`, `FcmNotification`, `AndroidConfig`, `AndroidNotification`, `WebpushConfig`, `ApnsConfig`, `FcmOptions`, `SendResponse`, `TopicManagementResponse`, `TopicManagementError`
36
+
37
+ ### Changed
38
+ - Total unit tests increased from 469 to 498 (+29 tests)
39
+ - Total test suites increased from 16 to 17
40
+
8
41
  ## [2.5.2] - 2026-03-03
9
42
 
10
43
  ### Fixed
package/dist/index.d.mts CHANGED
@@ -640,6 +640,22 @@ declare function queryDocuments(collectionPath: string, options?: QueryOptions):
640
640
  id: string;
641
641
  data: DataObject;
642
642
  }>>;
643
+ /**
644
+ * Batch get multiple documents from Firestore
645
+ * Uses the documents:batchGet REST API endpoint
646
+ *
647
+ * @param collectionPath - The collection containing the documents
648
+ * @param documentIds - Array of document IDs to fetch (max 100)
649
+ * @returns Array of results in the same order as documentIds. Missing documents return null.
650
+ * @throws {Error} If the operation fails or more than 100 documents requested
651
+ *
652
+ * @example
653
+ * ```typescript
654
+ * const users = await getAll('users', ['user1', 'user2', 'user3']);
655
+ * // users[0] = { name: 'Alice', ... } or null if not found
656
+ * ```
657
+ */
658
+ declare function getAll(collectionPath: string, documentIds: string[]): Promise<(DataObject | null)[]>;
643
659
  /**
644
660
  * Perform batch write operations (set, update, delete)
645
661
  *
@@ -1006,6 +1022,153 @@ interface ResumableUploadOptions extends UploadOptions {
1006
1022
  */
1007
1023
  declare function uploadFileResumable(path: string, data: ArrayBuffer | Uint8Array | Blob | ReadableStream<Uint8Array>, contentType: string, options?: ResumableUploadOptions): Promise<FileMetadata>;
1008
1024
 
1025
+ /**
1026
+ * Firebase Cloud Messaging (FCM) Types
1027
+ * Based on FCM HTTP v1 API specification
1028
+ */
1029
+ interface Message {
1030
+ /** Device registration token (mutually exclusive with topic/condition) */
1031
+ token?: string;
1032
+ /** Topic name (mutually exclusive with token/condition) */
1033
+ topic?: string;
1034
+ /** Condition expression for topic combinations (mutually exclusive with token/topic) */
1035
+ condition?: string;
1036
+ /** Basic notification payload displayed on all platforms */
1037
+ notification?: Notification;
1038
+ /** Custom key-value data payload */
1039
+ data?: Record<string, string>;
1040
+ /** Android-specific configuration */
1041
+ android?: AndroidConfig;
1042
+ /** Web push protocol configuration */
1043
+ webpush?: WebpushConfig;
1044
+ /** Apple Push Notification Service configuration */
1045
+ apns?: ApnsConfig;
1046
+ /** FCM-specific options */
1047
+ fcm_options?: FcmOptions;
1048
+ }
1049
+ interface Notification {
1050
+ title?: string;
1051
+ body?: string;
1052
+ image?: string;
1053
+ }
1054
+ interface AndroidConfig {
1055
+ collapse_key?: string;
1056
+ priority?: 'high' | 'normal';
1057
+ ttl?: string;
1058
+ restricted_package_name?: string;
1059
+ data?: Record<string, string>;
1060
+ notification?: AndroidNotification;
1061
+ fcm_options?: AndroidFcmOptions;
1062
+ direct_boot_ok?: boolean;
1063
+ }
1064
+ interface AndroidNotification {
1065
+ title?: string;
1066
+ body?: string;
1067
+ icon?: string;
1068
+ color?: string;
1069
+ sound?: string;
1070
+ tag?: string;
1071
+ click_action?: string;
1072
+ body_loc_key?: string;
1073
+ body_loc_args?: string[];
1074
+ title_loc_key?: string;
1075
+ title_loc_args?: string[];
1076
+ channel_id?: string;
1077
+ ticker?: string;
1078
+ sticky?: boolean;
1079
+ event_time?: string;
1080
+ local_only?: boolean;
1081
+ notification_priority?: AndroidNotificationPriority;
1082
+ default_sound?: boolean;
1083
+ default_vibrate_timings?: boolean;
1084
+ default_light_settings?: boolean;
1085
+ vibrate_timings?: string[];
1086
+ visibility?: 'VISIBILITY_UNSPECIFIED' | 'PRIVATE' | 'PUBLIC' | 'SECRET';
1087
+ notification_count?: number;
1088
+ light_settings?: LightSettings;
1089
+ image?: string;
1090
+ }
1091
+ type AndroidNotificationPriority = 'PRIORITY_UNSPECIFIED' | 'PRIORITY_MIN' | 'PRIORITY_LOW' | 'PRIORITY_DEFAULT' | 'PRIORITY_HIGH' | 'PRIORITY_MAX';
1092
+ interface LightSettings {
1093
+ color: {
1094
+ red: number;
1095
+ green: number;
1096
+ blue: number;
1097
+ alpha: number;
1098
+ };
1099
+ light_on_duration: string;
1100
+ light_off_duration: string;
1101
+ }
1102
+ interface AndroidFcmOptions {
1103
+ analytics_label?: string;
1104
+ }
1105
+ interface WebpushConfig {
1106
+ headers?: Record<string, string>;
1107
+ data?: Record<string, string>;
1108
+ notification?: Record<string, unknown>;
1109
+ fcm_options?: WebpushFcmOptions;
1110
+ }
1111
+ interface WebpushFcmOptions {
1112
+ link?: string;
1113
+ analytics_label?: string;
1114
+ }
1115
+ interface ApnsConfig {
1116
+ headers?: Record<string, string>;
1117
+ payload?: Record<string, unknown>;
1118
+ fcm_options?: ApnsFcmOptions;
1119
+ }
1120
+ interface ApnsFcmOptions {
1121
+ analytics_label?: string;
1122
+ image?: string;
1123
+ }
1124
+ interface FcmOptions {
1125
+ analytics_label?: string;
1126
+ }
1127
+ interface SendResponse {
1128
+ /** Full resource name: "projects/{id}/messages/{id}" */
1129
+ name: string;
1130
+ }
1131
+ interface TopicManagementResponse {
1132
+ successCount: number;
1133
+ failureCount: number;
1134
+ errors: TopicManagementError[];
1135
+ }
1136
+ interface TopicManagementError {
1137
+ index: number;
1138
+ error: string;
1139
+ }
1140
+
1141
+ /**
1142
+ * Firebase Cloud Messaging (FCM) Client
1143
+ * Implements FCM HTTP v1 API for sending messages and managing topic subscriptions.
1144
+ */
1145
+
1146
+ /**
1147
+ * Send a message via FCM HTTP v1 API.
1148
+ *
1149
+ * Exactly one of `message.token`, `message.topic`, or `message.condition` must be set.
1150
+ *
1151
+ * @param message - The message to send
1152
+ * @returns The message resource name (e.g. "projects/my-project/messages/123")
1153
+ */
1154
+ declare function sendMessage(message: Message): Promise<string>;
1155
+ /**
1156
+ * Subscribe device tokens to a topic.
1157
+ *
1158
+ * @param tokens - Array of device registration tokens (max 1000)
1159
+ * @param topic - Topic name (with or without "/topics/" prefix)
1160
+ * @returns Results with success/failure counts and per-token errors
1161
+ */
1162
+ declare function subscribeToTopic(tokens: string[], topic: string): Promise<TopicManagementResponse>;
1163
+ /**
1164
+ * Unsubscribe device tokens from a topic.
1165
+ *
1166
+ * @param tokens - Array of device registration tokens (max 1000)
1167
+ * @param topic - Topic name (with or without "/topics/" prefix)
1168
+ * @returns Results with success/failure counts and per-token errors
1169
+ */
1170
+ declare function unsubscribeFromTopic(tokens: string[], topic: string): Promise<TopicManagementResponse>;
1171
+
1009
1172
  /**
1010
1173
  * Firebase Admin SDK v8 - Field Value Helpers
1011
1174
  * Special field values for Firestore operations
@@ -1103,4 +1266,4 @@ declare function getAdminAccessToken(): Promise<string>;
1103
1266
  */
1104
1267
  declare function clearTokenCache(): void;
1105
1268
 
1106
- export { type BatchWrite, type BatchWriteResult, type CreateUserRequest, 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 ListUsersResult, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, setCustomUserClaims, setDocument, signInWithCustomToken, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
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 };
package/dist/index.d.ts CHANGED
@@ -640,6 +640,22 @@ declare function queryDocuments(collectionPath: string, options?: QueryOptions):
640
640
  id: string;
641
641
  data: DataObject;
642
642
  }>>;
643
+ /**
644
+ * Batch get multiple documents from Firestore
645
+ * Uses the documents:batchGet REST API endpoint
646
+ *
647
+ * @param collectionPath - The collection containing the documents
648
+ * @param documentIds - Array of document IDs to fetch (max 100)
649
+ * @returns Array of results in the same order as documentIds. Missing documents return null.
650
+ * @throws {Error} If the operation fails or more than 100 documents requested
651
+ *
652
+ * @example
653
+ * ```typescript
654
+ * const users = await getAll('users', ['user1', 'user2', 'user3']);
655
+ * // users[0] = { name: 'Alice', ... } or null if not found
656
+ * ```
657
+ */
658
+ declare function getAll(collectionPath: string, documentIds: string[]): Promise<(DataObject | null)[]>;
643
659
  /**
644
660
  * Perform batch write operations (set, update, delete)
645
661
  *
@@ -1006,6 +1022,153 @@ interface ResumableUploadOptions extends UploadOptions {
1006
1022
  */
1007
1023
  declare function uploadFileResumable(path: string, data: ArrayBuffer | Uint8Array | Blob | ReadableStream<Uint8Array>, contentType: string, options?: ResumableUploadOptions): Promise<FileMetadata>;
1008
1024
 
1025
+ /**
1026
+ * Firebase Cloud Messaging (FCM) Types
1027
+ * Based on FCM HTTP v1 API specification
1028
+ */
1029
+ interface Message {
1030
+ /** Device registration token (mutually exclusive with topic/condition) */
1031
+ token?: string;
1032
+ /** Topic name (mutually exclusive with token/condition) */
1033
+ topic?: string;
1034
+ /** Condition expression for topic combinations (mutually exclusive with token/topic) */
1035
+ condition?: string;
1036
+ /** Basic notification payload displayed on all platforms */
1037
+ notification?: Notification;
1038
+ /** Custom key-value data payload */
1039
+ data?: Record<string, string>;
1040
+ /** Android-specific configuration */
1041
+ android?: AndroidConfig;
1042
+ /** Web push protocol configuration */
1043
+ webpush?: WebpushConfig;
1044
+ /** Apple Push Notification Service configuration */
1045
+ apns?: ApnsConfig;
1046
+ /** FCM-specific options */
1047
+ fcm_options?: FcmOptions;
1048
+ }
1049
+ interface Notification {
1050
+ title?: string;
1051
+ body?: string;
1052
+ image?: string;
1053
+ }
1054
+ interface AndroidConfig {
1055
+ collapse_key?: string;
1056
+ priority?: 'high' | 'normal';
1057
+ ttl?: string;
1058
+ restricted_package_name?: string;
1059
+ data?: Record<string, string>;
1060
+ notification?: AndroidNotification;
1061
+ fcm_options?: AndroidFcmOptions;
1062
+ direct_boot_ok?: boolean;
1063
+ }
1064
+ interface AndroidNotification {
1065
+ title?: string;
1066
+ body?: string;
1067
+ icon?: string;
1068
+ color?: string;
1069
+ sound?: string;
1070
+ tag?: string;
1071
+ click_action?: string;
1072
+ body_loc_key?: string;
1073
+ body_loc_args?: string[];
1074
+ title_loc_key?: string;
1075
+ title_loc_args?: string[];
1076
+ channel_id?: string;
1077
+ ticker?: string;
1078
+ sticky?: boolean;
1079
+ event_time?: string;
1080
+ local_only?: boolean;
1081
+ notification_priority?: AndroidNotificationPriority;
1082
+ default_sound?: boolean;
1083
+ default_vibrate_timings?: boolean;
1084
+ default_light_settings?: boolean;
1085
+ vibrate_timings?: string[];
1086
+ visibility?: 'VISIBILITY_UNSPECIFIED' | 'PRIVATE' | 'PUBLIC' | 'SECRET';
1087
+ notification_count?: number;
1088
+ light_settings?: LightSettings;
1089
+ image?: string;
1090
+ }
1091
+ type AndroidNotificationPriority = 'PRIORITY_UNSPECIFIED' | 'PRIORITY_MIN' | 'PRIORITY_LOW' | 'PRIORITY_DEFAULT' | 'PRIORITY_HIGH' | 'PRIORITY_MAX';
1092
+ interface LightSettings {
1093
+ color: {
1094
+ red: number;
1095
+ green: number;
1096
+ blue: number;
1097
+ alpha: number;
1098
+ };
1099
+ light_on_duration: string;
1100
+ light_off_duration: string;
1101
+ }
1102
+ interface AndroidFcmOptions {
1103
+ analytics_label?: string;
1104
+ }
1105
+ interface WebpushConfig {
1106
+ headers?: Record<string, string>;
1107
+ data?: Record<string, string>;
1108
+ notification?: Record<string, unknown>;
1109
+ fcm_options?: WebpushFcmOptions;
1110
+ }
1111
+ interface WebpushFcmOptions {
1112
+ link?: string;
1113
+ analytics_label?: string;
1114
+ }
1115
+ interface ApnsConfig {
1116
+ headers?: Record<string, string>;
1117
+ payload?: Record<string, unknown>;
1118
+ fcm_options?: ApnsFcmOptions;
1119
+ }
1120
+ interface ApnsFcmOptions {
1121
+ analytics_label?: string;
1122
+ image?: string;
1123
+ }
1124
+ interface FcmOptions {
1125
+ analytics_label?: string;
1126
+ }
1127
+ interface SendResponse {
1128
+ /** Full resource name: "projects/{id}/messages/{id}" */
1129
+ name: string;
1130
+ }
1131
+ interface TopicManagementResponse {
1132
+ successCount: number;
1133
+ failureCount: number;
1134
+ errors: TopicManagementError[];
1135
+ }
1136
+ interface TopicManagementError {
1137
+ index: number;
1138
+ error: string;
1139
+ }
1140
+
1141
+ /**
1142
+ * Firebase Cloud Messaging (FCM) Client
1143
+ * Implements FCM HTTP v1 API for sending messages and managing topic subscriptions.
1144
+ */
1145
+
1146
+ /**
1147
+ * Send a message via FCM HTTP v1 API.
1148
+ *
1149
+ * Exactly one of `message.token`, `message.topic`, or `message.condition` must be set.
1150
+ *
1151
+ * @param message - The message to send
1152
+ * @returns The message resource name (e.g. "projects/my-project/messages/123")
1153
+ */
1154
+ declare function sendMessage(message: Message): Promise<string>;
1155
+ /**
1156
+ * Subscribe device tokens to a topic.
1157
+ *
1158
+ * @param tokens - Array of device registration tokens (max 1000)
1159
+ * @param topic - Topic name (with or without "/topics/" prefix)
1160
+ * @returns Results with success/failure counts and per-token errors
1161
+ */
1162
+ declare function subscribeToTopic(tokens: string[], topic: string): Promise<TopicManagementResponse>;
1163
+ /**
1164
+ * Unsubscribe device tokens from a topic.
1165
+ *
1166
+ * @param tokens - Array of device registration tokens (max 1000)
1167
+ * @param topic - Topic name (with or without "/topics/" prefix)
1168
+ * @returns Results with success/failure counts and per-token errors
1169
+ */
1170
+ declare function unsubscribeFromTopic(tokens: string[], topic: string): Promise<TopicManagementResponse>;
1171
+
1009
1172
  /**
1010
1173
  * Firebase Admin SDK v8 - Field Value Helpers
1011
1174
  * Special field values for Firestore operations
@@ -1103,4 +1266,4 @@ declare function getAdminAccessToken(): Promise<string>;
1103
1266
  */
1104
1267
  declare function clearTokenCache(): void;
1105
1268
 
1106
- export { type BatchWrite, type BatchWriteResult, type CreateUserRequest, 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 ListUsersResult, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, setCustomUserClaims, setDocument, signInWithCustomToken, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
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 };
package/dist/index.js CHANGED
@@ -216,6 +216,7 @@ __export(index_exports, {
216
216
  fileExists: () => fileExists,
217
217
  generateSignedUrl: () => generateSignedUrl,
218
218
  getAdminAccessToken: () => getAdminAccessToken,
219
+ getAll: () => getAll,
219
220
  getAuth: () => getAuth,
220
221
  getConfig: () => getConfig,
221
222
  getDocument: () => getDocument,
@@ -231,9 +232,12 @@ __export(index_exports, {
231
232
  listFiles: () => listFiles,
232
233
  listUsers: () => listUsers,
233
234
  queryDocuments: () => queryDocuments,
235
+ sendMessage: () => sendMessage,
234
236
  setCustomUserClaims: () => setCustomUserClaims,
235
237
  setDocument: () => setDocument,
236
238
  signInWithCustomToken: () => signInWithCustomToken,
239
+ subscribeToTopic: () => subscribeToTopic,
240
+ unsubscribeFromTopic: () => unsubscribeFromTopic,
237
241
  updateDocument: () => updateDocument,
238
242
  updateUser: () => updateUser,
239
243
  uploadFile: () => uploadFile,
@@ -1511,6 +1515,44 @@ async function queryDocuments(collectionPath, options) {
1511
1515
  data: convertFromFirestoreFormat(result.document.fields)
1512
1516
  }));
1513
1517
  }
1518
+ async function getAll(collectionPath, documentIds) {
1519
+ if (documentIds.length === 0) return [];
1520
+ if (documentIds.length > 100) {
1521
+ throw new Error("getAll supports a maximum of 100 documents per request");
1522
+ }
1523
+ for (const id of documentIds) {
1524
+ validateDocumentPath("collectionPath", collectionPath, id);
1525
+ }
1526
+ const accessToken = await getAdminAccessToken();
1527
+ const projectId = getProjectId();
1528
+ const basePath = `projects/${projectId}/databases/(default)/documents`;
1529
+ const documents = documentIds.map((id) => `${basePath}/${collectionPath}/${id}`);
1530
+ const url = `${FIRESTORE_API}/${basePath}:batchGet`;
1531
+ const response = await fetch(url, {
1532
+ method: "POST",
1533
+ headers: {
1534
+ "Authorization": `Bearer ${accessToken}`,
1535
+ "Content-Type": "application/json"
1536
+ },
1537
+ body: JSON.stringify({ documents })
1538
+ });
1539
+ if (!response.ok) {
1540
+ const errorText = await response.text();
1541
+ throw new Error(`Failed to batch get documents: ${errorText}`);
1542
+ }
1543
+ const results = await response.json();
1544
+ const resultMap = /* @__PURE__ */ new Map();
1545
+ for (const result of results) {
1546
+ if (result.found) {
1547
+ const name = result.found.name;
1548
+ const data = convertFromFirestoreFormat(result.found.fields);
1549
+ resultMap.set(name, data);
1550
+ } else if (result.missing) {
1551
+ resultMap.set(result.missing, null);
1552
+ }
1553
+ }
1554
+ return documents.map((docPath) => resultMap.get(docPath) ?? null);
1555
+ }
1514
1556
  async function batchWrite(operations) {
1515
1557
  const accessToken = await getAdminAccessToken();
1516
1558
  const projectId = getProjectId();
@@ -2142,6 +2184,96 @@ async function uploadFromStream(bucket, path, stream, contentType, chunkSize, op
2142
2184
  }
2143
2185
  }
2144
2186
 
2187
+ // src/messaging/client.ts
2188
+ init_token_generation();
2189
+ init_config();
2190
+ var FCM_BASE_URL = "https://fcm.googleapis.com/v1";
2191
+ var IID_BASE_URL = "https://iid.googleapis.com/iid/v1";
2192
+ async function sendMessage(message) {
2193
+ const targets = [message.token, message.topic, message.condition].filter(Boolean);
2194
+ if (targets.length === 0) {
2195
+ throw new Error("One of token, topic, or condition must be specified");
2196
+ }
2197
+ if (targets.length > 1) {
2198
+ throw new Error("Only one of token, topic, or condition can be specified");
2199
+ }
2200
+ const accessToken = await getAdminAccessToken();
2201
+ const projectId = getProjectId();
2202
+ const url = `${FCM_BASE_URL}/projects/${projectId}/messages:send`;
2203
+ const response = await fetch(url, {
2204
+ method: "POST",
2205
+ headers: {
2206
+ "Authorization": `Bearer ${accessToken}`,
2207
+ "Content-Type": "application/json"
2208
+ },
2209
+ body: JSON.stringify({ message })
2210
+ });
2211
+ if (!response.ok) {
2212
+ let errorDetail;
2213
+ try {
2214
+ const errorBody = await response.json();
2215
+ errorDetail = JSON.stringify(errorBody);
2216
+ } catch {
2217
+ errorDetail = await response.text();
2218
+ }
2219
+ throw new Error(`FCM send failed (${response.status}): ${errorDetail}`);
2220
+ }
2221
+ const result = await response.json();
2222
+ return result.name;
2223
+ }
2224
+ async function subscribeToTopic(tokens, topic) {
2225
+ return manageTopicSubscription(tokens, topic, "batchAdd");
2226
+ }
2227
+ async function unsubscribeFromTopic(tokens, topic) {
2228
+ return manageTopicSubscription(tokens, topic, "batchRemove");
2229
+ }
2230
+ async function manageTopicSubscription(tokens, topic, action) {
2231
+ if (tokens.length === 0) {
2232
+ throw new Error("At least one token is required");
2233
+ }
2234
+ if (tokens.length > 1e3) {
2235
+ throw new Error("Maximum 1000 tokens per request");
2236
+ }
2237
+ const accessToken = await getAdminAccessToken();
2238
+ const url = `${IID_BASE_URL}:${action}`;
2239
+ const topicPath = topic.startsWith("/topics/") ? topic : `/topics/${topic}`;
2240
+ const response = await fetch(url, {
2241
+ method: "POST",
2242
+ headers: {
2243
+ "Authorization": `Bearer ${accessToken}`,
2244
+ "Content-Type": "application/json",
2245
+ "access_token_auth": "true"
2246
+ },
2247
+ body: JSON.stringify({
2248
+ to: topicPath,
2249
+ registration_tokens: tokens
2250
+ })
2251
+ });
2252
+ if (!response.ok) {
2253
+ let errorDetail;
2254
+ try {
2255
+ errorDetail = await response.text();
2256
+ } catch {
2257
+ errorDetail = `HTTP ${response.status}`;
2258
+ }
2259
+ throw new Error(`FCM topic ${action} failed (${response.status}): ${errorDetail}`);
2260
+ }
2261
+ const result = await response.json();
2262
+ let successCount = 0;
2263
+ let failureCount = 0;
2264
+ const errors = [];
2265
+ for (let i = 0; i < result.results.length; i++) {
2266
+ const entry = result.results[i];
2267
+ if (entry.error) {
2268
+ failureCount++;
2269
+ errors.push({ index: i, error: entry.error });
2270
+ } else {
2271
+ successCount++;
2272
+ }
2273
+ }
2274
+ return { successCount, failureCount, errors };
2275
+ }
2276
+
2145
2277
  // src/index.ts
2146
2278
  init_token_generation();
2147
2279
  init_service_account();
@@ -2163,6 +2295,7 @@ init_service_account();
2163
2295
  fileExists,
2164
2296
  generateSignedUrl,
2165
2297
  getAdminAccessToken,
2298
+ getAll,
2166
2299
  getAuth,
2167
2300
  getConfig,
2168
2301
  getDocument,
@@ -2178,9 +2311,12 @@ init_service_account();
2178
2311
  listFiles,
2179
2312
  listUsers,
2180
2313
  queryDocuments,
2314
+ sendMessage,
2181
2315
  setCustomUserClaims,
2182
2316
  setDocument,
2183
2317
  signInWithCustomToken,
2318
+ subscribeToTopic,
2319
+ unsubscribeFromTopic,
2184
2320
  updateDocument,
2185
2321
  updateUser,
2186
2322
  uploadFile,
package/dist/index.mjs CHANGED
@@ -1266,6 +1266,44 @@ async function queryDocuments(collectionPath, options) {
1266
1266
  data: convertFromFirestoreFormat(result.document.fields)
1267
1267
  }));
1268
1268
  }
1269
+ async function getAll(collectionPath, documentIds) {
1270
+ if (documentIds.length === 0) return [];
1271
+ if (documentIds.length > 100) {
1272
+ throw new Error("getAll supports a maximum of 100 documents per request");
1273
+ }
1274
+ for (const id of documentIds) {
1275
+ validateDocumentPath("collectionPath", collectionPath, id);
1276
+ }
1277
+ const accessToken = await getAdminAccessToken();
1278
+ const projectId = getProjectId();
1279
+ const basePath = `projects/${projectId}/databases/(default)/documents`;
1280
+ const documents = documentIds.map((id) => `${basePath}/${collectionPath}/${id}`);
1281
+ const url = `${FIRESTORE_API}/${basePath}:batchGet`;
1282
+ const response = await fetch(url, {
1283
+ method: "POST",
1284
+ headers: {
1285
+ "Authorization": `Bearer ${accessToken}`,
1286
+ "Content-Type": "application/json"
1287
+ },
1288
+ body: JSON.stringify({ documents })
1289
+ });
1290
+ if (!response.ok) {
1291
+ const errorText = await response.text();
1292
+ throw new Error(`Failed to batch get documents: ${errorText}`);
1293
+ }
1294
+ const results = await response.json();
1295
+ const resultMap = /* @__PURE__ */ new Map();
1296
+ for (const result of results) {
1297
+ if (result.found) {
1298
+ const name = result.found.name;
1299
+ const data = convertFromFirestoreFormat(result.found.fields);
1300
+ resultMap.set(name, data);
1301
+ } else if (result.missing) {
1302
+ resultMap.set(result.missing, null);
1303
+ }
1304
+ }
1305
+ return documents.map((docPath) => resultMap.get(docPath) ?? null);
1306
+ }
1269
1307
  async function batchWrite(operations) {
1270
1308
  const accessToken = await getAdminAccessToken();
1271
1309
  const projectId = getProjectId();
@@ -1891,6 +1929,94 @@ async function uploadFromStream(bucket, path, stream, contentType, chunkSize, op
1891
1929
  reader.releaseLock();
1892
1930
  }
1893
1931
  }
1932
+
1933
+ // src/messaging/client.ts
1934
+ var FCM_BASE_URL = "https://fcm.googleapis.com/v1";
1935
+ var IID_BASE_URL = "https://iid.googleapis.com/iid/v1";
1936
+ async function sendMessage(message) {
1937
+ const targets = [message.token, message.topic, message.condition].filter(Boolean);
1938
+ if (targets.length === 0) {
1939
+ throw new Error("One of token, topic, or condition must be specified");
1940
+ }
1941
+ if (targets.length > 1) {
1942
+ throw new Error("Only one of token, topic, or condition can be specified");
1943
+ }
1944
+ const accessToken = await getAdminAccessToken();
1945
+ const projectId = getProjectId();
1946
+ const url = `${FCM_BASE_URL}/projects/${projectId}/messages:send`;
1947
+ const response = await fetch(url, {
1948
+ method: "POST",
1949
+ headers: {
1950
+ "Authorization": `Bearer ${accessToken}`,
1951
+ "Content-Type": "application/json"
1952
+ },
1953
+ body: JSON.stringify({ message })
1954
+ });
1955
+ if (!response.ok) {
1956
+ let errorDetail;
1957
+ try {
1958
+ const errorBody = await response.json();
1959
+ errorDetail = JSON.stringify(errorBody);
1960
+ } catch {
1961
+ errorDetail = await response.text();
1962
+ }
1963
+ throw new Error(`FCM send failed (${response.status}): ${errorDetail}`);
1964
+ }
1965
+ const result = await response.json();
1966
+ return result.name;
1967
+ }
1968
+ async function subscribeToTopic(tokens, topic) {
1969
+ return manageTopicSubscription(tokens, topic, "batchAdd");
1970
+ }
1971
+ async function unsubscribeFromTopic(tokens, topic) {
1972
+ return manageTopicSubscription(tokens, topic, "batchRemove");
1973
+ }
1974
+ async function manageTopicSubscription(tokens, topic, action) {
1975
+ if (tokens.length === 0) {
1976
+ throw new Error("At least one token is required");
1977
+ }
1978
+ if (tokens.length > 1e3) {
1979
+ throw new Error("Maximum 1000 tokens per request");
1980
+ }
1981
+ const accessToken = await getAdminAccessToken();
1982
+ const url = `${IID_BASE_URL}:${action}`;
1983
+ const topicPath = topic.startsWith("/topics/") ? topic : `/topics/${topic}`;
1984
+ const response = await fetch(url, {
1985
+ method: "POST",
1986
+ headers: {
1987
+ "Authorization": `Bearer ${accessToken}`,
1988
+ "Content-Type": "application/json",
1989
+ "access_token_auth": "true"
1990
+ },
1991
+ body: JSON.stringify({
1992
+ to: topicPath,
1993
+ registration_tokens: tokens
1994
+ })
1995
+ });
1996
+ if (!response.ok) {
1997
+ let errorDetail;
1998
+ try {
1999
+ errorDetail = await response.text();
2000
+ } catch {
2001
+ errorDetail = `HTTP ${response.status}`;
2002
+ }
2003
+ throw new Error(`FCM topic ${action} failed (${response.status}): ${errorDetail}`);
2004
+ }
2005
+ const result = await response.json();
2006
+ let successCount = 0;
2007
+ let failureCount = 0;
2008
+ const errors = [];
2009
+ for (let i = 0; i < result.results.length; i++) {
2010
+ const entry = result.results[i];
2011
+ if (entry.error) {
2012
+ failureCount++;
2013
+ errors.push({ index: i, error: entry.error });
2014
+ } else {
2015
+ successCount++;
2016
+ }
2017
+ }
2018
+ return { successCount, failureCount, errors };
2019
+ }
1894
2020
  export {
1895
2021
  FieldValue,
1896
2022
  addDocument,
@@ -1908,6 +2034,7 @@ export {
1908
2034
  fileExists,
1909
2035
  generateSignedUrl,
1910
2036
  getAdminAccessToken,
2037
+ getAll,
1911
2038
  getAuth,
1912
2039
  getConfig,
1913
2040
  getDocument,
@@ -1923,9 +2050,12 @@ export {
1923
2050
  listFiles,
1924
2051
  listUsers,
1925
2052
  queryDocuments,
2053
+ sendMessage,
1926
2054
  setCustomUserClaims,
1927
2055
  setDocument,
1928
2056
  signInWithCustomToken,
2057
+ subscribeToTopic,
2058
+ unsubscribeFromTopic,
1929
2059
  updateDocument,
1930
2060
  updateUser,
1931
2061
  uploadFile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/firebase-admin-sdk-v8",
3
- "version": "2.5.3",
3
+ "version": "2.7.0",
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",