firebase-functions 3.15.7 → 3.16.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ // The MIT License (MIT)
3
+ //
4
+ // Copyright (c) 2021 Firebase
5
+ //
6
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ // of this software and associated documentation files (the "Software"), to deal
8
+ // in the Software without restriction, including without limitation the rights
9
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ // copies of the Software, and to permit persons to whom the Software is
11
+ // furnished to do so, subject to the following conditions:
12
+ //
13
+ // The above copyright notice and this permission notice shall be included in all
14
+ // copies or substantial portions of the Software.
15
+ //
16
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ // SOFTWARE.
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.isDebugFeatureEnabled = exports.debugFeatureValue = void 0;
25
+ // Do NOT turn on a debug feature in production.
26
+ const debugMode = process.env.FIREBASE_DEBUG_MODE === 'true';
27
+ function loadDebugFeatures() {
28
+ if (!debugMode) {
29
+ return {};
30
+ }
31
+ try {
32
+ const obj = JSON.parse(process.env.FIREBASE_DEBUG_FEATURES);
33
+ if (typeof obj !== 'object') {
34
+ return {};
35
+ }
36
+ return obj;
37
+ }
38
+ catch (e) {
39
+ return {};
40
+ }
41
+ }
42
+ /* @internal */
43
+ function debugFeatureValue(feat) {
44
+ if (!debugMode) {
45
+ return;
46
+ }
47
+ return loadDebugFeatures()[feat];
48
+ }
49
+ exports.debugFeatureValue = debugFeatureValue;
50
+ /* @internal */
51
+ function isDebugFeatureEnabled(feat) {
52
+ return debugMode && !!debugFeatureValue(feat);
53
+ }
54
+ exports.isDebugFeatureEnabled = isDebugFeatureEnabled;
@@ -6,53 +6,54 @@ import * as firebase from 'firebase-admin';
6
6
  export interface Request extends express.Request {
7
7
  rawBody: Buffer;
8
8
  }
9
+ interface DecodedAppCheckToken {
10
+ /**
11
+ * The issuer identifier for the issuer of the response.
12
+ *
13
+ * This value is a URL with the format
14
+ * `https://firebaseappcheck.googleapis.com/<PROJECT_NUMBER>`, where `<PROJECT_NUMBER>` is the
15
+ * same project number specified in the [`aud`](#aud) property.
16
+ */
17
+ iss: string;
18
+ /**
19
+ * The Firebase App ID corresponding to the app the token belonged to.
20
+ *
21
+ * As a convenience, this value is copied over to the [`app_id`](#app_id) property.
22
+ */
23
+ sub: string;
24
+ /**
25
+ * The audience for which this token is intended.
26
+ *
27
+ * This value is a JSON array of two strings, the first is the project number of your
28
+ * Firebase project, and the second is the project ID of the same project.
29
+ */
30
+ aud: string[];
31
+ /**
32
+ * The App Check token's expiration time, in seconds since the Unix epoch. That is, the
33
+ * time at which this App Check token expires and should no longer be considered valid.
34
+ */
35
+ exp: number;
36
+ /**
37
+ * The App Check token's issued-at time, in seconds since the Unix epoch. That is, the
38
+ * time at which this App Check token was issued and should start to be considered
39
+ * valid.;
40
+ */
41
+ iat: number;
42
+ /**
43
+ * The App ID corresponding to the App the App Check token belonged to.
44
+ *
45
+ * This value is not actually one of the JWT token claims. It is added as a
46
+ * convenience, and is set as the value of the [`sub`](#sub) property.
47
+ */
48
+ app_id: string;
49
+ [key: string]: any;
50
+ }
9
51
  /**
10
52
  * The interface for AppCheck tokens verified in Callable functions
11
53
  */
12
54
  export interface AppCheckData {
13
55
  appId: string;
14
- token: {
15
- /**
16
- * The issuer identifier for the issuer of the response.
17
- *
18
- * This value is a URL with the format
19
- * `https://firebaseappcheck.googleapis.com/<PROJECT_NUMBER>`, where `<PROJECT_NUMBER>` is the
20
- * same project number specified in the [`aud`](#aud) property.
21
- */
22
- iss: string;
23
- /**
24
- * The Firebase App ID corresponding to the app the token belonged to.
25
- *
26
- * As a convenience, this value is copied over to the [`app_id`](#app_id) property.
27
- */
28
- sub: string;
29
- /**
30
- * The audience for which this token is intended.
31
- *
32
- * This value is a JSON array of two strings, the first is the project number of your
33
- * Firebase project, and the second is the project ID of the same project.
34
- */
35
- aud: string[];
36
- /**
37
- * The App Check token's expiration time, in seconds since the Unix epoch. That is, the
38
- * time at which this App Check token expires and should no longer be considered valid.
39
- */
40
- exp: number;
41
- /**
42
- * The App Check token's issued-at time, in seconds since the Unix epoch. That is, the
43
- * time at which this App Check token was issued and should start to be considered
44
- * valid.
45
- */
46
- iat: number;
47
- /**
48
- * The App ID corresponding to the App the App Check token belonged to.
49
- *
50
- * This value is not actually one of the JWT token claims. It is added as a
51
- * convenience, and is set as the value of the [`sub`](#sub) property.
52
- */
53
- app_id: string;
54
- [key: string]: any;
55
- };
56
+ token: DecodedAppCheckToken;
56
57
  }
57
58
  /**
58
59
  * The interface for Auth tokens verified in Callable functions
@@ -197,6 +198,11 @@ export declare function encode(data: any): any;
197
198
  export declare function decode(data: any): any;
198
199
  declare type v1Handler = (data: any, context: CallableContext) => any | Promise<any>;
199
200
  declare type v2Handler<Req, Res> = (request: CallableRequest<Req>) => Res;
201
+ /** @hidden **/
202
+ export interface CallableOptions {
203
+ cors: cors.CorsOptions;
204
+ allowInvalidAppCheckToken?: boolean;
205
+ }
200
206
  /** @hidden */
201
- export declare function onCallHandler<Req = any, Res = any>(options: cors.CorsOptions, handler: v1Handler | v2Handler<Req, Res>): (req: Request, res: express.Response) => Promise<void>;
207
+ export declare function onCallHandler<Req = any, Res = any>(options: CallableOptions, handler: v1Handler | v2Handler<Req, Res>): (req: Request, res: express.Response) => Promise<void>;
202
208
  export {};
@@ -21,12 +21,14 @@
21
21
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  // SOFTWARE.
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.onCallHandler = exports.decode = exports.encode = exports.HttpsError = void 0;
24
+ exports.onCallHandler = exports.unsafeDecodeAppCheckToken = exports.unsafeDecodeIdToken = exports.decode = exports.encode = exports.HttpsError = void 0;
25
25
  const cors = require("cors");
26
26
  const logger = require("../../logger");
27
27
  // TODO(inlined): Decide whether we want to un-version apps or whether we want a
28
28
  // different strategy
29
29
  const apps_1 = require("../../apps");
30
+ const debug_1 = require("../../common/debug");
31
+ const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/;
30
32
  /**
31
33
  * Standard error codes and HTTP statuses for different ways a request can fail,
32
34
  * as defined by:
@@ -148,7 +150,7 @@ function encode(data) {
148
150
  if (Array.isArray(data)) {
149
151
  return data.map(encode);
150
152
  }
151
- if (typeof data === 'object') {
153
+ if (typeof data === 'object' || typeof data === 'function') {
152
154
  // Sadly we don't have Object.fromEntries in Node 10, so we can't use a single
153
155
  // list comprehension
154
156
  const obj = {};
@@ -206,6 +208,53 @@ function decode(data) {
206
208
  return data;
207
209
  }
208
210
  exports.decode = decode;
211
+ function unsafeDecodeToken(token) {
212
+ if (!JWT_REGEX.test(token)) {
213
+ return {};
214
+ }
215
+ const components = token
216
+ .split('.')
217
+ .map((s) => Buffer.from(s, 'base64').toString());
218
+ let payload = components[1];
219
+ if (typeof payload === 'string') {
220
+ try {
221
+ const obj = JSON.parse(payload);
222
+ if (typeof obj === 'object') {
223
+ payload = obj;
224
+ }
225
+ }
226
+ catch (e) { }
227
+ }
228
+ return payload;
229
+ }
230
+ /**
231
+ * Decode, but not verify, a Auth ID token.
232
+ *
233
+ * Do not use in production. Token should always be verified using the Admin SDK.
234
+ *
235
+ * This is exposed only for testing.
236
+ */
237
+ /** @internal */
238
+ function unsafeDecodeIdToken(token) {
239
+ const decoded = unsafeDecodeToken(token);
240
+ decoded.uid = decoded.sub;
241
+ return decoded;
242
+ }
243
+ exports.unsafeDecodeIdToken = unsafeDecodeIdToken;
244
+ /**
245
+ * Decode, but not verify, an App Check token.
246
+ *
247
+ * Do not use in production. Token should always be verified using the Admin SDK.
248
+ *
249
+ * This is exposed only for testing.
250
+ */
251
+ /** @internal */
252
+ function unsafeDecodeAppCheckToken(token) {
253
+ const decoded = unsafeDecodeToken(token);
254
+ decoded.app_id = decoded.sub;
255
+ return decoded;
256
+ }
257
+ exports.unsafeDecodeAppCheckToken = unsafeDecodeAppCheckToken;
209
258
  /**
210
259
  * Check and verify tokens included in the requests. Once verified, tokens
211
260
  * are injected into the callable context.
@@ -220,6 +269,7 @@ async function checkTokens(req, ctx) {
220
269
  app: 'MISSING',
221
270
  auth: 'MISSING',
222
271
  };
272
+ const skipTokenVerify = (0, debug_1.isDebugFeatureEnabled)('skipTokenVerification');
223
273
  const appCheck = req.header('X-Firebase-AppCheck');
224
274
  if (appCheck) {
225
275
  verifications.app = 'INVALID';
@@ -227,13 +277,17 @@ async function checkTokens(req, ctx) {
227
277
  if (!(0, apps_1.apps)().admin.appCheck) {
228
278
  throw new Error('Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0');
229
279
  }
230
- const appCheckToken = await (0, apps_1.apps)()
231
- .admin.appCheck()
232
- .verifyToken(appCheck);
233
- ctx.app = {
234
- appId: appCheckToken.appId,
235
- token: appCheckToken.token,
236
- };
280
+ let appCheckData;
281
+ if (skipTokenVerify) {
282
+ const decodedToken = unsafeDecodeAppCheckToken(appCheck);
283
+ appCheckData = { appId: decodedToken.app_id, token: decodedToken };
284
+ }
285
+ else {
286
+ appCheckData = await (0, apps_1.apps)()
287
+ .admin.appCheck()
288
+ .verifyToken(appCheck);
289
+ }
290
+ ctx.app = appCheckData;
237
291
  verifications.app = 'VALID';
238
292
  }
239
293
  catch (err) {
@@ -247,9 +301,15 @@ async function checkTokens(req, ctx) {
247
301
  if (match) {
248
302
  const idToken = match[1];
249
303
  try {
250
- const authToken = await (0, apps_1.apps)()
251
- .admin.auth()
252
- .verifyIdToken(idToken);
304
+ let authToken;
305
+ if (skipTokenVerify) {
306
+ authToken = unsafeDecodeIdToken(idToken);
307
+ }
308
+ else {
309
+ authToken = await (0, apps_1.apps)()
310
+ .admin.auth()
311
+ .verifyIdToken(idToken);
312
+ }
253
313
  verifications.auth = 'VALID';
254
314
  ctx.auth = {
255
315
  uid: authToken.uid,
@@ -284,11 +344,11 @@ async function checkTokens(req, ctx) {
284
344
  }
285
345
  /** @hidden */
286
346
  function onCallHandler(options, handler) {
287
- const wrapped = wrapOnCallHandler(handler);
347
+ const wrapped = wrapOnCallHandler(options, handler);
288
348
  return (req, res) => {
289
349
  return new Promise((resolve) => {
290
350
  res.on('finish', resolve);
291
- cors(options)(req, res, () => {
351
+ cors(options.cors)(req, res, () => {
292
352
  resolve(wrapped(req, res));
293
353
  });
294
354
  });
@@ -296,7 +356,7 @@ function onCallHandler(options, handler) {
296
356
  }
297
357
  exports.onCallHandler = onCallHandler;
298
358
  /** @internal */
299
- function wrapOnCallHandler(handler) {
359
+ function wrapOnCallHandler(options, handler) {
300
360
  return async (req, res) => {
301
361
  try {
302
362
  if (!isValidRequest(req)) {
@@ -305,7 +365,10 @@ function wrapOnCallHandler(handler) {
305
365
  }
306
366
  const context = { rawRequest: req };
307
367
  const tokenStatus = await checkTokens(req, context);
308
- if (tokenStatus.app === 'INVALID' || tokenStatus.auth === 'INVALID') {
368
+ if (tokenStatus.auth === 'INVALID') {
369
+ throw new HttpsError('unauthenticated', 'Unauthenticated');
370
+ }
371
+ if (tokenStatus.app === 'INVALID' && !options.allowInvalidAppCheckToken) {
309
372
  throw new HttpsError('unauthenticated', 'Unauthenticated');
310
373
  }
311
374
  const instanceId = req.header('Firebase-Instance-ID-Token');
@@ -98,6 +98,7 @@ export interface RuntimeOptions {
98
98
  * Invoker to set access control on https functions.
99
99
  */
100
100
  invoker?: 'public' | 'private' | string | string[];
101
+ allowInvalidAppCheckToken?: boolean;
101
102
  }
102
103
  export interface DeploymentOptions extends RuntimeOptions {
103
104
  regions?: Array<typeof SUPPORTED_REGIONS[number] | string>;
@@ -64,7 +64,10 @@ function _onCallWithOptions(handler, options) {
64
64
  // and the user could have only tried to listen to data. Wrap their handler
65
65
  // in another handler to avoid accidentally triggering the v2 API
66
66
  const fixedLen = (data, context) => handler(data, context);
67
- const func = (0, https_1.onCallHandler)({ origin: true, methods: 'POST' }, fixedLen);
67
+ const func = (0, https_1.onCallHandler)({
68
+ allowInvalidAppCheckToken: options.allowInvalidAppCheckToken,
69
+ cors: { origin: true, methods: 'POST' },
70
+ }, fixedLen);
68
71
  func.__trigger = {
69
72
  labels: {},
70
73
  ...(0, cloud_functions_1.optionsToTrigger)(options),
package/lib/v2/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as logger from '../logger';
2
2
  import * as params from './params';
3
3
  import * as https from './providers/https';
4
4
  import * as pubsub from './providers/pubsub';
5
- export { https, pubsub, logger, params };
5
+ import * as storage from './providers/storage';
6
+ export { https, pubsub, storage, logger, params };
6
7
  export { setGlobalOptions, GlobalOptions } from './options';
7
8
  export { CloudFunction, CloudEvent } from './core';
package/lib/v2/index.js CHANGED
@@ -21,7 +21,7 @@
21
21
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  // SOFTWARE.
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.setGlobalOptions = exports.params = exports.logger = exports.pubsub = exports.https = void 0;
24
+ exports.setGlobalOptions = exports.params = exports.logger = exports.storage = exports.pubsub = exports.https = void 0;
25
25
  const logger = require("../logger");
26
26
  exports.logger = logger;
27
27
  const params = require("./params");
@@ -30,5 +30,7 @@ const https = require("./providers/https");
30
30
  exports.https = https;
31
31
  const pubsub = require("./providers/pubsub");
32
32
  exports.pubsub = pubsub;
33
+ const storage = require("./providers/storage");
34
+ exports.storage = storage;
33
35
  var options_1 = require("./options");
34
36
  Object.defineProperty(exports, "setGlobalOptions", { enumerable: true, get: function () { return options_1.setGlobalOptions; } });
@@ -88,7 +88,7 @@ function onCall(optsOrHandler, handler) {
88
88
  // onCallHandler sniffs the function length to determine which API to present.
89
89
  // fix the length to prevent api versions from being mismatched.
90
90
  const fixedLen = (req) => handler(req);
91
- const func = (0, https_1.onCallHandler)({ origin, methods: 'POST' }, fixedLen);
91
+ const func = (0, https_1.onCallHandler)({ cors: { origin, methods: 'POST' } }, fixedLen);
92
92
  Object.defineProperty(func, '__trigger', {
93
93
  get: () => {
94
94
  const baseOpts = options.optionsToTriggerAnnotations(options.getGlobalOptions());
@@ -0,0 +1,170 @@
1
+ import { CloudEvent, CloudFunction } from '../core';
2
+ import * as options from '../options';
3
+ /**
4
+ * An object within Google Cloud Storage.
5
+ * Ref: https://github.com/googleapis/google-cloudevents-nodejs/blob/main/cloud/storage/v1/StorageObjectData.ts
6
+ */
7
+ export interface StorageObjectData {
8
+ /**
9
+ * The name of the bucket containing this object.
10
+ */
11
+ bucket?: string;
12
+ /**
13
+ * Cache-Control directive for the object data, matching
14
+ * [https://tools.ietf.org/html/rfc7234#section-5.2"][RFC 7234 §5.2].
15
+ */
16
+ cacheControl?: string;
17
+ /**
18
+ * Number of underlying components that make up this object. Components are
19
+ * accumulated by compose operations.
20
+ * Attempting to set this field will result in an error.
21
+ */
22
+ componentCount?: number;
23
+ /**
24
+ * Content-Disposition of the object data, matching
25
+ * [https://tools.ietf.org/html/rfc6266][RFC 6266].
26
+ */
27
+ contentDisposition?: string;
28
+ /**
29
+ * Content-Encoding of the object data, matching
30
+ * [https://tools.ietf.org/html/rfc7231#section-3.1.2.2][RFC 7231 §3.1.2.2]
31
+ */
32
+ contentEncoding?: string;
33
+ /**
34
+ * Content-Language of the object data, matching
35
+ * [https://tools.ietf.org/html/rfc7231#section-3.1.3.2][RFC 7231 §3.1.3.2].
36
+ */
37
+ contentLanguage?: string;
38
+ /**
39
+ * Content-Type of the object data, matching
40
+ * [https://tools.ietf.org/html/rfc7231#section-3.1.1.5][RFC 7231 §3.1.1.5].
41
+ * If an object is stored without a Content-Type, it is served as
42
+ * `application/octet-stream`.
43
+ */
44
+ contentType?: string;
45
+ /**
46
+ * CRC32c checksum. For more information about using the CRC32c
47
+ * checksum, see
48
+ * [https://cloud.google.com/storage/docs/hashes-etags#_JSONAPI][Hashes and
49
+ * ETags: Best Practices].
50
+ */
51
+ crc32c?: string;
52
+ /**
53
+ * Metadata of customer-supplied encryption key, if the object is encrypted by
54
+ * such a key.
55
+ */
56
+ customerEncryption?: CustomerEncryption;
57
+ /**
58
+ * HTTP 1.1 Entity tag for the object. See
59
+ * [https://tools.ietf.org/html/rfc7232#section-2.3][RFC 7232 §2.3].
60
+ */
61
+ etag?: string;
62
+ /**
63
+ * The content generation of this object. Used for object versioning.
64
+ * Attempting to set this field will result in an error.
65
+ */
66
+ generation?: number;
67
+ /**
68
+ * The ID of the object, including the bucket name, object name, and
69
+ * generation number.
70
+ */
71
+ id?: string;
72
+ /**
73
+ * The kind of item this is. For objects, this is always "storage#object".
74
+ */
75
+ kind?: string;
76
+ /**
77
+ * MD5 hash of the data; encoded using base64 as per
78
+ * [https://tools.ietf.org/html/rfc4648#section-4][RFC 4648 §4]. For more
79
+ * information about using the MD5 hash, see
80
+ * [https://cloud.google.com/storage/docs/hashes-etags#_JSONAPI][Hashes and
81
+ * ETags: Best Practices].
82
+ */
83
+ md5Hash?: string;
84
+ /**
85
+ * Media download link.
86
+ */
87
+ mediaLink?: string;
88
+ /**
89
+ * User-provided metadata, in key/value pairs.
90
+ */
91
+ metadata?: {
92
+ [key: string]: string;
93
+ };
94
+ /**
95
+ * The version of the metadata for this object at this generation. Used for
96
+ * preconditions and for detecting changes in metadata. A metageneration
97
+ * number is only meaningful in the context of a particular generation of a
98
+ * particular object.
99
+ */
100
+ metageneration?: number;
101
+ /**
102
+ * The name of the object.
103
+ */
104
+ name?: string;
105
+ /**
106
+ * The link to this object.
107
+ */
108
+ selfLink?: string;
109
+ /**
110
+ * Content-Length of the object data in bytes, matching
111
+ * [https://tools.ietf.org/html/rfc7230#section-3.3.2][RFC 7230 §3.3.2].
112
+ */
113
+ size?: number;
114
+ /**
115
+ * Storage class of the object.
116
+ */
117
+ storageClass?: string;
118
+ /**
119
+ * The creation time of the object.
120
+ * Attempting to set this field will result in an error.
121
+ */
122
+ timeCreated?: Date | string;
123
+ /**
124
+ * The deletion time of the object. Will be returned if and only if this
125
+ * version of the object has been deleted.
126
+ */
127
+ timeDeleted?: Date | string;
128
+ /**
129
+ * The time at which the object's storage class was last changed.
130
+ */
131
+ timeStorageClassUpdated?: Date | string;
132
+ /**
133
+ * The modification time of the object metadata.
134
+ */
135
+ updated?: Date | string;
136
+ }
137
+ /**
138
+ * Metadata of customer-supplied encryption key, if the object is encrypted by
139
+ * such a key.
140
+ */
141
+ export interface CustomerEncryption {
142
+ /**
143
+ * The encryption algorithm.
144
+ */
145
+ encryptionAlgorithm?: string;
146
+ /**
147
+ * SHA256 hash value of the encryption key.
148
+ */
149
+ keySha256?: string;
150
+ }
151
+ /** StorageOptions extend EventHandlerOptions with a bucket name */
152
+ export interface StorageOptions extends options.EventHandlerOptions {
153
+ bucket?: string;
154
+ }
155
+ /** Handle a storage object archived */
156
+ export declare function onObjectArchived(handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
157
+ export declare function onObjectArchived(bucket: string, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
158
+ export declare function onObjectArchived(opts: StorageOptions, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
159
+ /** Handle a storage object finalized */
160
+ export declare function onObjectFinalized(handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
161
+ export declare function onObjectFinalized(bucket: string, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
162
+ export declare function onObjectFinalized(opts: StorageOptions, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
163
+ /** Handle a storage object deleted */
164
+ export declare function onObjectDeleted(handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
165
+ export declare function onObjectDeleted(bucket: string, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
166
+ export declare function onObjectDeleted(opts: StorageOptions, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
167
+ /** Handle a storage object metadata updated */
168
+ export declare function onObjectMetadataUpdated(handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
169
+ export declare function onObjectMetadataUpdated(bucket: string, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
170
+ export declare function onObjectMetadataUpdated(opts: StorageOptions, handler: (event: CloudEvent<StorageObjectData>) => any | Promise<any>): CloudFunction<StorageObjectData>;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ // The MIT License (MIT)
3
+ //
4
+ // Copyright (c) 2017 Firebase
5
+ //
6
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ // of this software and associated documentation files (the "Software"), to deal
8
+ // in the Software without restriction, including without limitation the rights
9
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ // copies of the Software, and to permit persons to whom the Software is
11
+ // furnished to do so, subject to the following conditions:
12
+ //
13
+ // The above copyright notice and this permission notice shall be included in all
14
+ // copies or substantial portions of the Software.
15
+ //
16
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ // SOFTWARE.
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.getOptsAndBucket = exports.onOperation = exports.onObjectMetadataUpdated = exports.onObjectDeleted = exports.onObjectFinalized = exports.onObjectArchived = exports.metadataUpdatedEvent = exports.deletedEvent = exports.finalizedEvent = exports.archivedEvent = void 0;
25
+ const config_1 = require("../../config");
26
+ const options = require("../options");
27
+ /** @internal */
28
+ exports.archivedEvent = 'google.cloud.storage.object.v1.archived';
29
+ /** @internal */
30
+ exports.finalizedEvent = 'google.cloud.storage.object.v1.finalized';
31
+ /** @internal */
32
+ exports.deletedEvent = 'google.cloud.storage.object.v1.deleted';
33
+ /** @internal */
34
+ exports.metadataUpdatedEvent = 'google.cloud.storage.object.v1.metadataUpdated';
35
+ function onObjectArchived(buketOrOptsOrHandler, handler) {
36
+ return onOperation(exports.archivedEvent, buketOrOptsOrHandler, handler);
37
+ }
38
+ exports.onObjectArchived = onObjectArchived;
39
+ function onObjectFinalized(buketOrOptsOrHandler, handler) {
40
+ return onOperation(exports.finalizedEvent, buketOrOptsOrHandler, handler);
41
+ }
42
+ exports.onObjectFinalized = onObjectFinalized;
43
+ function onObjectDeleted(buketOrOptsOrHandler, handler) {
44
+ return onOperation(exports.deletedEvent, buketOrOptsOrHandler, handler);
45
+ }
46
+ exports.onObjectDeleted = onObjectDeleted;
47
+ function onObjectMetadataUpdated(buketOrOptsOrHandler, handler) {
48
+ return onOperation(exports.metadataUpdatedEvent, buketOrOptsOrHandler, handler);
49
+ }
50
+ exports.onObjectMetadataUpdated = onObjectMetadataUpdated;
51
+ /** @internal */
52
+ function onOperation(eventType, bucketOrOptsOrHandler, handler) {
53
+ if (typeof bucketOrOptsOrHandler === 'function') {
54
+ handler = bucketOrOptsOrHandler;
55
+ bucketOrOptsOrHandler = {};
56
+ }
57
+ const [opts, bucket] = getOptsAndBucket(bucketOrOptsOrHandler);
58
+ const func = (raw) => {
59
+ return handler(raw);
60
+ };
61
+ func.run = handler;
62
+ // TypeScript doesn't recongize defineProperty as adding a property and complains
63
+ // that __trigger doesn't exist. We can either cast to any and lose all type safety
64
+ // or we can just assign a meaningless value before calling defineProperty.
65
+ func.__trigger = 'silence the transpiler';
66
+ Object.defineProperty(func, '__trigger', {
67
+ get: () => {
68
+ const baseOpts = options.optionsToTriggerAnnotations(options.getGlobalOptions());
69
+ const specificOpts = options.optionsToTriggerAnnotations(opts);
70
+ return {
71
+ platform: 'gcfv2',
72
+ ...baseOpts,
73
+ ...specificOpts,
74
+ labels: {
75
+ ...baseOpts === null || baseOpts === void 0 ? void 0 : baseOpts.labels,
76
+ ...specificOpts === null || specificOpts === void 0 ? void 0 : specificOpts.labels,
77
+ },
78
+ eventTrigger: {
79
+ eventType,
80
+ resource: bucket, // TODO(colerogers): replace with 'bucket,' eventually
81
+ },
82
+ };
83
+ },
84
+ });
85
+ return func;
86
+ }
87
+ exports.onOperation = onOperation;
88
+ /** @internal */
89
+ function getOptsAndBucket(bucketOrOpts) {
90
+ let bucket;
91
+ let opts;
92
+ if (typeof bucketOrOpts === 'string') {
93
+ bucket = bucketOrOpts;
94
+ opts = {};
95
+ }
96
+ else {
97
+ bucket = bucketOrOpts.bucket || (0, config_1.firebaseConfig)().storageBucket;
98
+ opts = { ...bucketOrOpts };
99
+ delete opts.bucket;
100
+ }
101
+ if (!bucket) {
102
+ throw new Error('Missing bucket name. If you are unit testing, please provide a bucket name' +
103
+ ' by providing bucket name directly in the event handler or by setting process.env.FIREBASE_CONFIG.');
104
+ }
105
+ if (!/^[a-z\d][a-z\d\\._-]{1,230}[a-z\d]$/.test(bucket)) {
106
+ throw new Error(`Invalid bucket name ${bucket}`);
107
+ }
108
+ return [opts, bucket];
109
+ }
110
+ exports.getOptsAndBucket = getOptsAndBucket;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-functions",
3
- "version": "3.15.7",
3
+ "version": "3.16.0",
4
4
  "description": "Firebase SDK for Cloud Functions",
5
5
  "keywords": [
6
6
  "firebase",
@@ -53,7 +53,8 @@
53
53
  "./v2/options": "./lib/v2/options.js",
54
54
  "./v2/https": "./lib/v2/providers/https.js",
55
55
  "./v2/params": "./lib/v2/params/index.js",
56
- "./v2/pubsub": "./lib/v2/providers/pubsub.js"
56
+ "./v2/pubsub": "./lib/v2/providers/pubsub.js",
57
+ "./v2/storage": "./lib/v2/providers/storage.js"
57
58
  },
58
59
  "typesVersions": {
59
60
  "*": {
@@ -110,6 +111,9 @@
110
111
  ],
111
112
  "v2/pubsub": [
112
113
  "lib/v2/providers/pubsub"
114
+ ],
115
+ "v2/storage": [
116
+ "lib/v2/providers/storage"
113
117
  ]
114
118
  }
115
119
  },
@@ -148,7 +152,7 @@
148
152
  "chai": "^4.2.0",
149
153
  "chai-as-promised": "^7.1.1",
150
154
  "child-process-promise": "^2.2.1",
151
- "firebase-admin": "^9.8.0",
155
+ "firebase-admin": "10.0.0",
152
156
  "js-yaml": "^3.13.1",
153
157
  "jsdom": "^16.2.1",
154
158
  "jsonwebtoken": "^8.5.1",
@@ -164,12 +168,12 @@
164
168
  "tslint-config-prettier": "^1.18.0",
165
169
  "tslint-no-unused-expression-chai": "^0.1.4",
166
170
  "tslint-plugin-prettier": "^2.0.1",
167
- "typedoc": "^0.21.5",
171
+ "typedoc": "0.21.2",
168
172
  "typescript": "^4.3.5",
169
173
  "yargs": "^15.3.1"
170
174
  },
171
175
  "peerDependencies": {
172
- "firebase-admin": "^8.0.0 || ^9.0.0"
176
+ "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0"
173
177
  },
174
178
  "engines": {
175
179
  "node": "^8.13.0 || >=10.10.0"