@prmichaelsen/firebase-admin-sdk-v8 2.0.21 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -62,6 +62,20 @@ function getProjectId() {
62
62
  "Firebase project ID not configured. Either call initializeApp({ projectId: ... }) or set FIREBASE_PROJECT_ID environment variable."
63
63
  );
64
64
  }
65
+ function getFirebaseApiKey() {
66
+ if (globalConfig.apiKey) {
67
+ return globalConfig.apiKey;
68
+ }
69
+ if (typeof process !== "undefined" && process.env) {
70
+ const apiKey = process.env.FIREBASE_API_KEY || process.env.PUBLIC_FIREBASE_API_KEY;
71
+ if (apiKey) {
72
+ return apiKey;
73
+ }
74
+ }
75
+ throw new Error(
76
+ "Firebase API key not configured. Either call initializeApp({ apiKey: ... }) or set FIREBASE_API_KEY environment variable. Find your API key in Firebase Console > Project Settings > Web API Key."
77
+ );
78
+ }
65
79
 
66
80
  // src/x509.ts
67
81
  function decodeBase64(str) {
@@ -282,6 +296,105 @@ async function getUserFromToken(idToken) {
282
296
  photoURL: decodedToken.picture || null
283
297
  };
284
298
  }
299
+ function base64UrlEncode(str) {
300
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
301
+ }
302
+ async function signWithPrivateKey(data, privateKey) {
303
+ const pemHeader = "-----BEGIN PRIVATE KEY-----";
304
+ const pemFooter = "-----END PRIVATE KEY-----";
305
+ const pemContents = privateKey.replace(pemHeader, "").replace(pemFooter, "").replace(/\s/g, "");
306
+ const binaryString = atob(pemContents);
307
+ const bytes = new Uint8Array(binaryString.length);
308
+ for (let i = 0; i < binaryString.length; i++) {
309
+ bytes[i] = binaryString.charCodeAt(i);
310
+ }
311
+ const key = await crypto.subtle.importKey(
312
+ "pkcs8",
313
+ bytes,
314
+ {
315
+ name: "RSASSA-PKCS1-v1_5",
316
+ hash: "SHA-256"
317
+ },
318
+ false,
319
+ ["sign"]
320
+ );
321
+ const encoder = new TextEncoder();
322
+ const dataBytes = encoder.encode(data);
323
+ const signature = await crypto.subtle.sign(
324
+ "RSASSA-PKCS1-v1_5",
325
+ key,
326
+ dataBytes
327
+ );
328
+ return base64UrlEncode(String.fromCharCode(...new Uint8Array(signature)));
329
+ }
330
+ async function createCustomToken(uid, customClaims) {
331
+ const serviceAccount = getServiceAccount();
332
+ if (!uid || typeof uid !== "string") {
333
+ throw new Error("uid must be a non-empty string");
334
+ }
335
+ if (uid.length > 128) {
336
+ throw new Error("uid must be at most 128 characters");
337
+ }
338
+ const now = Math.floor(Date.now() / 1e3);
339
+ const payload = {
340
+ iss: serviceAccount.client_email,
341
+ sub: serviceAccount.client_email,
342
+ aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
343
+ iat: now,
344
+ exp: now + 3600,
345
+ // 1 hour
346
+ uid
347
+ };
348
+ if (customClaims) {
349
+ payload.claims = customClaims;
350
+ }
351
+ const header = {
352
+ alg: "RS256",
353
+ typ: "JWT"
354
+ };
355
+ const encodedHeader = base64UrlEncode(JSON.stringify(header));
356
+ const encodedPayload = base64UrlEncode(JSON.stringify(payload));
357
+ const unsignedToken = `${encodedHeader}.${encodedPayload}`;
358
+ const signature = await signWithPrivateKey(unsignedToken, serviceAccount.private_key);
359
+ return `${unsignedToken}.${signature}`;
360
+ }
361
+ async function signInWithCustomToken(customToken) {
362
+ if (!customToken || typeof customToken !== "string") {
363
+ throw new Error("customToken must be a non-empty string");
364
+ }
365
+ const apiKey = getFirebaseApiKey();
366
+ const url = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`;
367
+ const response = await fetch(url, {
368
+ method: "POST",
369
+ headers: {
370
+ "Content-Type": "application/json"
371
+ },
372
+ body: JSON.stringify({
373
+ token: customToken,
374
+ returnSecureToken: true
375
+ })
376
+ });
377
+ if (!response.ok) {
378
+ const errorText = await response.text();
379
+ let errorMessage = `Failed to sign in with custom token: ${response.status}`;
380
+ try {
381
+ const errorJson = JSON.parse(errorText);
382
+ if (errorJson.error && errorJson.error.message) {
383
+ errorMessage += ` - ${errorJson.error.message}`;
384
+ }
385
+ } catch {
386
+ errorMessage += ` - ${errorText}`;
387
+ }
388
+ throw new Error(errorMessage);
389
+ }
390
+ const result = await response.json();
391
+ return {
392
+ idToken: result.idToken,
393
+ refreshToken: result.refreshToken,
394
+ expiresIn: result.expiresIn,
395
+ localId: result.localId
396
+ };
397
+ }
285
398
  function getAuth() {
286
399
  return {
287
400
  verifyIdToken
@@ -565,7 +678,7 @@ function removeFieldTransforms(data) {
565
678
  }
566
679
 
567
680
  // src/token-generation.ts
568
- function base64UrlEncode(str) {
681
+ function base64UrlEncode2(str) {
569
682
  return btoa(str).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
570
683
  }
571
684
  function base64UrlEncodeBuffer(buffer) {
@@ -586,8 +699,8 @@ async function createJWT(serviceAccount) {
586
699
  exp: expiry,
587
700
  scope: "https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/datastore https://www.googleapis.com/auth/firebase"
588
701
  };
589
- const encodedHeader = base64UrlEncode(JSON.stringify(header));
590
- const encodedPayload = base64UrlEncode(JSON.stringify(payload));
702
+ const encodedHeader = base64UrlEncode2(JSON.stringify(header));
703
+ const encodedPayload = base64UrlEncode2(JSON.stringify(payload));
591
704
  const unsignedToken = `${encodedHeader}.${encodedPayload}`;
592
705
  const pemContents = serviceAccount.private_key.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace(/\s/g, "");
593
706
  const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
@@ -636,6 +749,26 @@ function clearTokenCache() {
636
749
  tokenExpiry = 0;
637
750
  }
638
751
 
752
+ // src/firestore/path-validation.ts
753
+ function validateCollectionPath(argumentName, collectionPath) {
754
+ const segments = collectionPath.split("/").filter((s) => s.length > 0);
755
+ if (segments.length % 2 === 0) {
756
+ throw new Error(
757
+ `Value for argument "${argumentName}" must point to a collection, but was "${collectionPath}". Your path does not contain an odd number of components.`
758
+ );
759
+ }
760
+ }
761
+ function validateDocumentPath(argumentName, collectionPath, documentId) {
762
+ const collectionSegments = collectionPath.split("/").filter((s) => s.length > 0);
763
+ const totalSegments = collectionSegments.length + 1;
764
+ if (totalSegments % 2 !== 0) {
765
+ const fullPath = `${collectionPath}/${documentId}`;
766
+ throw new Error(
767
+ `Value for argument "${argumentName}" must point to a document, but was "${fullPath}". Your path does not contain an even number of components.`
768
+ );
769
+ }
770
+ }
771
+
639
772
  // src/firestore/operations.ts
640
773
  var FIRESTORE_API = "https://firestore.googleapis.com/v1";
641
774
  async function commitWrites(writes) {
@@ -656,6 +789,7 @@ async function commitWrites(writes) {
656
789
  }
657
790
  }
658
791
  async function setDocument(collectionPath, documentId, data, options) {
792
+ validateDocumentPath("collectionPath", collectionPath, documentId);
659
793
  const projectId = getProjectId();
660
794
  const documentPath = `projects/${projectId}/databases/(default)/documents/${collectionPath}/${documentId}`;
661
795
  const cleanData = removeFieldTransforms(data);
@@ -705,6 +839,7 @@ async function setDocument(collectionPath, documentId, data, options) {
705
839
  }
706
840
  }
707
841
  async function addDocument(collectionPath, data, documentId) {
842
+ validateCollectionPath("collectionPath", collectionPath);
708
843
  const accessToken = await getAdminAccessToken();
709
844
  const projectId = getProjectId();
710
845
  const baseUrl = `${FIRESTORE_API}/projects/${projectId}/databases/(default)/documents/${collectionPath}`;
@@ -736,6 +871,7 @@ async function addDocument(collectionPath, data, documentId) {
736
871
  };
737
872
  }
738
873
  async function getDocument(collectionPath, documentId) {
874
+ validateDocumentPath("collectionPath", collectionPath, documentId);
739
875
  const accessToken = await getAdminAccessToken();
740
876
  const projectId = getProjectId();
741
877
  const url = `${FIRESTORE_API}/projects/${projectId}/databases/(default)/documents/${collectionPath}/${documentId}`;
@@ -755,6 +891,7 @@ async function getDocument(collectionPath, documentId) {
755
891
  return convertFromFirestoreFormat(result.fields);
756
892
  }
757
893
  async function updateDocument(collectionPath, documentId, data) {
894
+ validateDocumentPath("collectionPath", collectionPath, documentId);
758
895
  const projectId = getProjectId();
759
896
  const documentPath = `projects/${projectId}/databases/(default)/documents/${collectionPath}/${documentId}`;
760
897
  const cleanData = removeFieldTransforms(data);
@@ -807,6 +944,7 @@ async function updateDocument(collectionPath, documentId, data) {
807
944
  }
808
945
  }
809
946
  async function deleteDocument(collectionPath, documentId) {
947
+ validateDocumentPath("collectionPath", collectionPath, documentId);
810
948
  const accessToken = await getAdminAccessToken();
811
949
  const projectId = getProjectId();
812
950
  const url = `${FIRESTORE_API}/projects/${projectId}/databases/(default)/documents/${collectionPath}/${documentId}`;
@@ -822,6 +960,7 @@ async function deleteDocument(collectionPath, documentId) {
822
960
  }
823
961
  }
824
962
  async function queryDocuments(collectionPath, options) {
963
+ validateCollectionPath("collectionPath", collectionPath);
825
964
  const accessToken = await getAdminAccessToken();
826
965
  const projectId = getProjectId();
827
966
  if (!options || Object.keys(options).length === 0) {
@@ -938,23 +1077,341 @@ async function batchWrite(operations) {
938
1077
  }
939
1078
  return await response.json();
940
1079
  }
1080
+
1081
+ // src/storage/client.ts
1082
+ var STORAGE_API_BASE = "https://storage.googleapis.com/storage/v1";
1083
+ var UPLOAD_API_BASE = "https://storage.googleapis.com/upload/storage/v1";
1084
+ function getDefaultBucket() {
1085
+ const customBucket = process.env.FIREBASE_STORAGE_BUCKET;
1086
+ if (customBucket) {
1087
+ return customBucket;
1088
+ }
1089
+ const projectId = getProjectId();
1090
+ return `${projectId}.appspot.com`;
1091
+ }
1092
+ function detectContentType(filename) {
1093
+ const ext = filename.split(".").pop()?.toLowerCase();
1094
+ const mimeTypes = {
1095
+ "txt": "text/plain",
1096
+ "html": "text/html",
1097
+ "htm": "text/html",
1098
+ "css": "text/css",
1099
+ "js": "application/javascript",
1100
+ "json": "application/json",
1101
+ "xml": "application/xml",
1102
+ "jpg": "image/jpeg",
1103
+ "jpeg": "image/jpeg",
1104
+ "png": "image/png",
1105
+ "gif": "image/gif",
1106
+ "svg": "image/svg+xml",
1107
+ "webp": "image/webp",
1108
+ "pdf": "application/pdf",
1109
+ "zip": "application/zip",
1110
+ "mp4": "video/mp4",
1111
+ "mp3": "audio/mpeg",
1112
+ "wav": "audio/wav"
1113
+ };
1114
+ return mimeTypes[ext || ""] || "application/octet-stream";
1115
+ }
1116
+ async function uploadFile(path, data, options = {}) {
1117
+ const token = await getAdminAccessToken();
1118
+ const bucket = getDefaultBucket();
1119
+ const contentType = options.contentType || detectContentType(path);
1120
+ const url = `${UPLOAD_API_BASE}/b/${encodeURIComponent(bucket)}/o?uploadType=media&name=${encodeURIComponent(path)}`;
1121
+ let body;
1122
+ if (data instanceof Blob) {
1123
+ body = await data.arrayBuffer();
1124
+ } else if (data instanceof Uint8Array) {
1125
+ const buffer = new ArrayBuffer(data.byteLength);
1126
+ new Uint8Array(buffer).set(data);
1127
+ body = buffer;
1128
+ } else {
1129
+ body = data;
1130
+ }
1131
+ const response = await fetch(url, {
1132
+ method: "POST",
1133
+ headers: {
1134
+ "Authorization": `Bearer ${token}`,
1135
+ "Content-Type": contentType,
1136
+ "Content-Length": body.byteLength.toString()
1137
+ },
1138
+ body
1139
+ });
1140
+ if (!response.ok) {
1141
+ const errorText = await response.text();
1142
+ throw new Error(`Failed to upload file: ${response.status} ${errorText}`);
1143
+ }
1144
+ const result = await response.json();
1145
+ if (options.public) {
1146
+ await makeFilePublic(path);
1147
+ }
1148
+ if (options.metadata) {
1149
+ return await updateFileMetadata(path, options.metadata);
1150
+ }
1151
+ return result;
1152
+ }
1153
+ async function downloadFile(path, _options = {}) {
1154
+ const token = await getAdminAccessToken();
1155
+ const bucket = getDefaultBucket();
1156
+ const url = `${STORAGE_API_BASE}/b/${encodeURIComponent(bucket)}/o/${encodeURIComponent(path)}?alt=media`;
1157
+ const response = await fetch(url, {
1158
+ method: "GET",
1159
+ headers: {
1160
+ "Authorization": `Bearer ${token}`
1161
+ }
1162
+ });
1163
+ if (!response.ok) {
1164
+ const errorText = await response.text();
1165
+ throw new Error(`Failed to download file: ${response.status} ${errorText}`);
1166
+ }
1167
+ return await response.arrayBuffer();
1168
+ }
1169
+ async function deleteFile(path) {
1170
+ const token = await getAdminAccessToken();
1171
+ const bucket = getDefaultBucket();
1172
+ const url = `${STORAGE_API_BASE}/b/${encodeURIComponent(bucket)}/o/${encodeURIComponent(path)}`;
1173
+ const response = await fetch(url, {
1174
+ method: "DELETE",
1175
+ headers: {
1176
+ "Authorization": `Bearer ${token}`
1177
+ }
1178
+ });
1179
+ if (!response.ok) {
1180
+ const errorText = await response.text();
1181
+ throw new Error(`Failed to delete file: ${response.status} ${errorText}`);
1182
+ }
1183
+ }
1184
+ async function getFileMetadata(path) {
1185
+ const token = await getAdminAccessToken();
1186
+ const bucket = getDefaultBucket();
1187
+ const url = `${STORAGE_API_BASE}/b/${encodeURIComponent(bucket)}/o/${encodeURIComponent(path)}`;
1188
+ const response = await fetch(url, {
1189
+ method: "GET",
1190
+ headers: {
1191
+ "Authorization": `Bearer ${token}`
1192
+ }
1193
+ });
1194
+ if (!response.ok) {
1195
+ const errorText = await response.text();
1196
+ throw new Error(`Failed to get file metadata: ${response.status} ${errorText}`);
1197
+ }
1198
+ return await response.json();
1199
+ }
1200
+ async function updateFileMetadata(path, metadata) {
1201
+ const token = await getAdminAccessToken();
1202
+ const bucket = getDefaultBucket();
1203
+ const url = `${STORAGE_API_BASE}/b/${encodeURIComponent(bucket)}/o/${encodeURIComponent(path)}`;
1204
+ const response = await fetch(url, {
1205
+ method: "PATCH",
1206
+ headers: {
1207
+ "Authorization": `Bearer ${token}`,
1208
+ "Content-Type": "application/json"
1209
+ },
1210
+ body: JSON.stringify({ metadata })
1211
+ });
1212
+ if (!response.ok) {
1213
+ const errorText = await response.text();
1214
+ throw new Error(`Failed to update file metadata: ${response.status} ${errorText}`);
1215
+ }
1216
+ return await response.json();
1217
+ }
1218
+ async function makeFilePublic(path) {
1219
+ const token = await getAdminAccessToken();
1220
+ const bucket = getDefaultBucket();
1221
+ const url = `${STORAGE_API_BASE}/b/${encodeURIComponent(bucket)}/o/${encodeURIComponent(path)}/acl`;
1222
+ const response = await fetch(url, {
1223
+ method: "POST",
1224
+ headers: {
1225
+ "Authorization": `Bearer ${token}`,
1226
+ "Content-Type": "application/json"
1227
+ },
1228
+ body: JSON.stringify({
1229
+ entity: "allUsers",
1230
+ role: "READER"
1231
+ })
1232
+ });
1233
+ if (!response.ok) {
1234
+ const errorText = await response.text();
1235
+ throw new Error(`Failed to make file public: ${response.status} ${errorText}`);
1236
+ }
1237
+ }
1238
+ async function listFiles(options = {}) {
1239
+ const token = await getAdminAccessToken();
1240
+ const bucket = getDefaultBucket();
1241
+ const params = new URLSearchParams();
1242
+ if (options.prefix) params.append("prefix", options.prefix);
1243
+ if (options.delimiter) params.append("delimiter", options.delimiter);
1244
+ if (options.maxResults) params.append("maxResults", options.maxResults.toString());
1245
+ if (options.pageToken) params.append("pageToken", options.pageToken);
1246
+ const url = `${STORAGE_API_BASE}/b/${encodeURIComponent(bucket)}/o?${params.toString()}`;
1247
+ const response = await fetch(url, {
1248
+ method: "GET",
1249
+ headers: {
1250
+ "Authorization": `Bearer ${token}`
1251
+ }
1252
+ });
1253
+ if (!response.ok) {
1254
+ const errorText = await response.text();
1255
+ throw new Error(`Failed to list files: ${response.status} ${errorText}`);
1256
+ }
1257
+ const result = await response.json();
1258
+ return {
1259
+ files: result.items || [],
1260
+ nextPageToken: result.nextPageToken
1261
+ };
1262
+ }
1263
+ async function fileExists(path) {
1264
+ try {
1265
+ await getFileMetadata(path);
1266
+ return true;
1267
+ } catch (error) {
1268
+ if (error instanceof Error && error.message.includes("404")) {
1269
+ return false;
1270
+ }
1271
+ throw error;
1272
+ }
1273
+ }
1274
+
1275
+ // src/storage/signed-urls.ts
1276
+ function getStorageBucket() {
1277
+ const customBucket = process.env.FIREBASE_STORAGE_BUCKET;
1278
+ if (customBucket) {
1279
+ return customBucket;
1280
+ }
1281
+ const projectId = getProjectId();
1282
+ return `${projectId}.appspot.com`;
1283
+ }
1284
+ function getExpirationTimestamp(expires) {
1285
+ if (expires instanceof Date) {
1286
+ return Math.floor(expires.getTime() / 1e3);
1287
+ }
1288
+ return Math.floor(Date.now() / 1e3) + expires;
1289
+ }
1290
+ function actionToMethod(action) {
1291
+ switch (action) {
1292
+ case "read":
1293
+ return "GET";
1294
+ case "write":
1295
+ return "PUT";
1296
+ case "delete":
1297
+ return "DELETE";
1298
+ }
1299
+ }
1300
+ function stringToHex(str) {
1301
+ const encoder = new TextEncoder();
1302
+ const bytes = encoder.encode(str);
1303
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1304
+ }
1305
+ async function signData(data, privateKey) {
1306
+ const pemHeader = "-----BEGIN PRIVATE KEY-----";
1307
+ const pemFooter = "-----END PRIVATE KEY-----";
1308
+ const pemContents = privateKey.replace(pemHeader, "").replace(pemFooter, "").replace(/\s/g, "");
1309
+ const binaryString = atob(pemContents);
1310
+ const bytes = new Uint8Array(binaryString.length);
1311
+ for (let i = 0; i < binaryString.length; i++) {
1312
+ bytes[i] = binaryString.charCodeAt(i);
1313
+ }
1314
+ const key = await crypto.subtle.importKey(
1315
+ "pkcs8",
1316
+ bytes,
1317
+ {
1318
+ name: "RSASSA-PKCS1-v1_5",
1319
+ hash: "SHA-256"
1320
+ },
1321
+ false,
1322
+ ["sign"]
1323
+ );
1324
+ const encoder = new TextEncoder();
1325
+ const dataBytes = encoder.encode(data);
1326
+ const signature = await crypto.subtle.sign(
1327
+ "RSASSA-PKCS1-v1_5",
1328
+ key,
1329
+ dataBytes
1330
+ );
1331
+ const signatureArray = new Uint8Array(signature);
1332
+ return Array.from(signatureArray).map((b) => b.toString(16).padStart(2, "0")).join("");
1333
+ }
1334
+ async function generateSignedUrl(path, options) {
1335
+ const serviceAccount = getServiceAccount();
1336
+ const bucket = getStorageBucket();
1337
+ const method = actionToMethod(options.action);
1338
+ const expiration = getExpirationTimestamp(options.expires);
1339
+ const now = /* @__PURE__ */ new Date();
1340
+ const timestamp = Math.floor(now.getTime() / 1e3);
1341
+ const isoString = now.toISOString();
1342
+ const datestamp = isoString.split("T")[0].replace(/-/g, "");
1343
+ const timeString = isoString.split("T")[1].replace(/[:.]/g, "").substring(0, 6);
1344
+ const dateTimeStamp = `${datestamp}T${timeString}Z`;
1345
+ const credentialScope = `${datestamp}/auto/storage/goog4_request`;
1346
+ const credential = `${serviceAccount.client_email}/${credentialScope}`;
1347
+ const canonicalHeaders = `host:storage.googleapis.com
1348
+ `;
1349
+ const signedHeaders = "host";
1350
+ const queryParams = {
1351
+ "X-Goog-Algorithm": "GOOG4-RSA-SHA256",
1352
+ "X-Goog-Credential": credential,
1353
+ "X-Goog-Date": dateTimeStamp,
1354
+ "X-Goog-Expires": (expiration - timestamp).toString(),
1355
+ "X-Goog-SignedHeaders": signedHeaders
1356
+ };
1357
+ if (options.contentType) {
1358
+ queryParams["response-content-type"] = options.contentType;
1359
+ }
1360
+ if (options.responseDisposition) {
1361
+ queryParams["response-content-disposition"] = options.responseDisposition;
1362
+ }
1363
+ if (options.responseType) {
1364
+ queryParams["response-content-type"] = options.responseType;
1365
+ }
1366
+ const sortedParams = Object.keys(queryParams).sort();
1367
+ const canonicalQueryString = sortedParams.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`).join("&");
1368
+ const encodedPath = path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
1369
+ const canonicalUri = `/${bucket}/${encodedPath}`;
1370
+ const canonicalRequest = [
1371
+ method,
1372
+ canonicalUri,
1373
+ canonicalQueryString,
1374
+ canonicalHeaders,
1375
+ signedHeaders,
1376
+ "UNSIGNED-PAYLOAD"
1377
+ ].join("\n");
1378
+ const canonicalRequestHash = stringToHex(canonicalRequest);
1379
+ const stringToSign = [
1380
+ "GOOG4-RSA-SHA256",
1381
+ dateTimeStamp,
1382
+ credentialScope,
1383
+ canonicalRequestHash
1384
+ ].join("\n");
1385
+ const signature = await signData(stringToSign, serviceAccount.private_key);
1386
+ const signedUrl = `https://storage.googleapis.com${canonicalUri}?${canonicalQueryString}&X-Goog-Signature=${signature}`;
1387
+ return signedUrl;
1388
+ }
941
1389
  export {
942
1390
  FieldValue,
943
1391
  addDocument,
944
1392
  batchWrite,
945
1393
  clearConfig,
946
1394
  clearTokenCache,
1395
+ createCustomToken,
947
1396
  deleteDocument,
1397
+ deleteFile,
1398
+ downloadFile,
1399
+ fileExists,
1400
+ generateSignedUrl,
948
1401
  getAdminAccessToken,
949
1402
  getAuth,
950
1403
  getConfig,
951
1404
  getDocument,
1405
+ getFileMetadata,
952
1406
  getProjectId,
953
1407
  getServiceAccount,
954
1408
  getUserFromToken,
955
1409
  initializeApp,
1410
+ listFiles,
956
1411
  queryDocuments,
957
1412
  setDocument,
1413
+ signInWithCustomToken,
958
1414
  updateDocument,
1415
+ uploadFile,
959
1416
  verifyIdToken
960
1417
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/firebase-admin-sdk-v8",
3
- "version": "2.0.21",
3
+ "version": "2.1.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",
Binary file