firebase-admin 9.12.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.
Files changed (84) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +90 -0
  3. package/lib/app-check/app-check-api-client-internal.js +197 -0
  4. package/lib/app-check/app-check.js +79 -0
  5. package/lib/app-check/index.d.ts +160 -0
  6. package/lib/app-check/index.js +19 -0
  7. package/lib/app-check/token-generator.js +161 -0
  8. package/lib/app-check/token-verifier.js +152 -0
  9. package/lib/auth/action-code-settings-builder.js +118 -0
  10. package/lib/auth/auth-api-request.js +1856 -0
  11. package/lib/auth/auth-config.js +636 -0
  12. package/lib/auth/auth.js +836 -0
  13. package/lib/auth/identifier.js +40 -0
  14. package/lib/auth/index.d.ts +1927 -0
  15. package/lib/auth/index.js +18 -0
  16. package/lib/auth/tenant-manager.js +140 -0
  17. package/lib/auth/tenant.js +171 -0
  18. package/lib/auth/token-generator.js +200 -0
  19. package/lib/auth/token-verifier.js +259 -0
  20. package/lib/auth/user-import-builder.js +387 -0
  21. package/lib/auth/user-record.js +346 -0
  22. package/lib/credential/credential-internal.js +391 -0
  23. package/lib/credential/credential.js +44 -0
  24. package/lib/credential/index.d.ts +169 -0
  25. package/lib/credential/index.js +23 -0
  26. package/lib/database/database-internal.js +266 -0
  27. package/lib/database/index.d.ts +89 -0
  28. package/lib/database/index.js +31 -0
  29. package/lib/default-namespace.js +31 -0
  30. package/lib/firebase-app.js +349 -0
  31. package/lib/firebase-namespace-api.d.ts +243 -0
  32. package/lib/firebase-namespace-api.js +18 -0
  33. package/lib/firebase-namespace.d.ts +31 -0
  34. package/lib/firebase-namespace.js +417 -0
  35. package/lib/firestore/firestore-internal.js +105 -0
  36. package/lib/firestore/index.d.ts +50 -0
  37. package/lib/firestore/index.js +47 -0
  38. package/lib/index.d.ts +24 -0
  39. package/lib/index.js +27 -0
  40. package/lib/installations/index.d.ts +81 -0
  41. package/lib/installations/index.js +18 -0
  42. package/lib/installations/installations-request-handler.js +117 -0
  43. package/lib/installations/installations.js +62 -0
  44. package/lib/instance-id/index.d.ts +83 -0
  45. package/lib/instance-id/index.js +18 -0
  46. package/lib/instance-id/instance-id.js +87 -0
  47. package/lib/machine-learning/index.d.ts +249 -0
  48. package/lib/machine-learning/index.js +18 -0
  49. package/lib/machine-learning/machine-learning-api-client.js +304 -0
  50. package/lib/machine-learning/machine-learning-utils.js +62 -0
  51. package/lib/machine-learning/machine-learning.js +364 -0
  52. package/lib/messaging/batch-request-internal.js +129 -0
  53. package/lib/messaging/index.d.ts +1174 -0
  54. package/lib/messaging/index.js +18 -0
  55. package/lib/messaging/messaging-api-request-internal.js +128 -0
  56. package/lib/messaging/messaging-errors-internal.js +106 -0
  57. package/lib/messaging/messaging-internal.js +484 -0
  58. package/lib/messaging/messaging.js +846 -0
  59. package/lib/project-management/android-app.js +176 -0
  60. package/lib/project-management/index.d.ts +363 -0
  61. package/lib/project-management/index.js +41 -0
  62. package/lib/project-management/ios-app.js +88 -0
  63. package/lib/project-management/project-management-api-request-internal.js +273 -0
  64. package/lib/project-management/project-management.js +254 -0
  65. package/lib/remote-config/index.d.ts +369 -0
  66. package/lib/remote-config/index.js +18 -0
  67. package/lib/remote-config/remote-config-api-client-internal.js +407 -0
  68. package/lib/remote-config/remote-config.js +304 -0
  69. package/lib/security-rules/index.d.ts +216 -0
  70. package/lib/security-rules/index.js +18 -0
  71. package/lib/security-rules/security-rules-api-client-internal.js +237 -0
  72. package/lib/security-rules/security-rules-internal.js +41 -0
  73. package/lib/security-rules/security-rules.js +310 -0
  74. package/lib/storage/index.d.ts +60 -0
  75. package/lib/storage/index.js +18 -0
  76. package/lib/storage/storage.js +123 -0
  77. package/lib/utils/api-request.js +845 -0
  78. package/lib/utils/crypto-signer.js +237 -0
  79. package/lib/utils/deep-copy.js +78 -0
  80. package/lib/utils/error.js +1063 -0
  81. package/lib/utils/index.js +217 -0
  82. package/lib/utils/jwt.js +355 -0
  83. package/lib/utils/validator.js +271 -0
  84. package/package.json +122 -0
@@ -0,0 +1,846 @@
1
+ /*! firebase-admin v9.12.0 */
2
+ "use strict";
3
+ /*!
4
+ * @license
5
+ * Copyright 2017 Google Inc.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ var __assign = (this && this.__assign) || function () {
20
+ __assign = Object.assign || function(t) {
21
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
22
+ s = arguments[i];
23
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
24
+ t[p] = s[p];
25
+ }
26
+ return t;
27
+ };
28
+ return __assign.apply(this, arguments);
29
+ };
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.Messaging = void 0;
32
+ var deep_copy_1 = require("../utils/deep-copy");
33
+ var messaging_internal_1 = require("./messaging-internal");
34
+ var messaging_api_request_internal_1 = require("./messaging-api-request-internal");
35
+ var error_1 = require("../utils/error");
36
+ var utils = require("../utils");
37
+ var validator = require("../utils/validator");
38
+ /* eslint-disable @typescript-eslint/camelcase */
39
+ // FCM endpoints
40
+ var FCM_SEND_HOST = 'fcm.googleapis.com';
41
+ var FCM_SEND_PATH = '/fcm/send';
42
+ var FCM_TOPIC_MANAGEMENT_HOST = 'iid.googleapis.com';
43
+ var FCM_TOPIC_MANAGEMENT_ADD_PATH = '/iid/v1:batchAdd';
44
+ var FCM_TOPIC_MANAGEMENT_REMOVE_PATH = '/iid/v1:batchRemove';
45
+ // Maximum messages that can be included in a batch request.
46
+ var FCM_MAX_BATCH_SIZE = 500;
47
+ // Key renames for the messaging notification payload object.
48
+ var CAMELCASED_NOTIFICATION_PAYLOAD_KEYS_MAP = {
49
+ bodyLocArgs: 'body_loc_args',
50
+ bodyLocKey: 'body_loc_key',
51
+ clickAction: 'click_action',
52
+ titleLocArgs: 'title_loc_args',
53
+ titleLocKey: 'title_loc_key',
54
+ };
55
+ // Key renames for the messaging options object.
56
+ var CAMELCASE_OPTIONS_KEYS_MAP = {
57
+ dryRun: 'dry_run',
58
+ timeToLive: 'time_to_live',
59
+ collapseKey: 'collapse_key',
60
+ mutableContent: 'mutable_content',
61
+ contentAvailable: 'content_available',
62
+ restrictedPackageName: 'restricted_package_name',
63
+ };
64
+ // Key renames for the MessagingDeviceResult object.
65
+ var MESSAGING_DEVICE_RESULT_KEYS_MAP = {
66
+ message_id: 'messageId',
67
+ registration_id: 'canonicalRegistrationToken',
68
+ };
69
+ // Key renames for the MessagingDevicesResponse object.
70
+ var MESSAGING_DEVICES_RESPONSE_KEYS_MAP = {
71
+ canonical_ids: 'canonicalRegistrationTokenCount',
72
+ failure: 'failureCount',
73
+ success: 'successCount',
74
+ multicast_id: 'multicastId',
75
+ };
76
+ // Key renames for the MessagingDeviceGroupResponse object.
77
+ var MESSAGING_DEVICE_GROUP_RESPONSE_KEYS_MAP = {
78
+ success: 'successCount',
79
+ failure: 'failureCount',
80
+ failed_registration_ids: 'failedRegistrationTokens',
81
+ };
82
+ // Key renames for the MessagingTopicResponse object.
83
+ var MESSAGING_TOPIC_RESPONSE_KEYS_MAP = {
84
+ message_id: 'messageId',
85
+ };
86
+ // Key renames for the MessagingConditionResponse object.
87
+ var MESSAGING_CONDITION_RESPONSE_KEYS_MAP = {
88
+ message_id: 'messageId',
89
+ };
90
+ /**
91
+ * Maps a raw FCM server response to a MessagingDevicesResponse object.
92
+ *
93
+ * @param {object} response The raw FCM server response to map.
94
+ *
95
+ * @return {MessagingDeviceGroupResponse} The mapped MessagingDevicesResponse object.
96
+ */
97
+ function mapRawResponseToDevicesResponse(response) {
98
+ // Rename properties on the server response
99
+ utils.renameProperties(response, MESSAGING_DEVICES_RESPONSE_KEYS_MAP);
100
+ if ('results' in response) {
101
+ response.results.forEach(function (messagingDeviceResult) {
102
+ utils.renameProperties(messagingDeviceResult, MESSAGING_DEVICE_RESULT_KEYS_MAP);
103
+ // Map the FCM server's error strings to actual error objects.
104
+ if ('error' in messagingDeviceResult) {
105
+ var newError = error_1.FirebaseMessagingError.fromServerError(messagingDeviceResult.error, /* message */ undefined, messagingDeviceResult.error);
106
+ messagingDeviceResult.error = newError;
107
+ }
108
+ });
109
+ }
110
+ return response;
111
+ }
112
+ /**
113
+ * Maps a raw FCM server response to a MessagingDeviceGroupResponse object.
114
+ *
115
+ * @param {object} response The raw FCM server response to map.
116
+ *
117
+ * @return {MessagingDeviceGroupResponse} The mapped MessagingDeviceGroupResponse object.
118
+ */
119
+ function mapRawResponseToDeviceGroupResponse(response) {
120
+ // Rename properties on the server response
121
+ utils.renameProperties(response, MESSAGING_DEVICE_GROUP_RESPONSE_KEYS_MAP);
122
+ // Add the 'failedRegistrationTokens' property if it does not exist on the response, which
123
+ // it won't when the 'failureCount' property has a value of 0)
124
+ response.failedRegistrationTokens = response.failedRegistrationTokens || [];
125
+ return response;
126
+ }
127
+ /**
128
+ * Maps a raw FCM server response to a MessagingTopicManagementResponse object.
129
+ *
130
+ * @param {object} response The raw FCM server response to map.
131
+ *
132
+ * @return {MessagingTopicManagementResponse} The mapped MessagingTopicManagementResponse object.
133
+ */
134
+ function mapRawResponseToTopicManagementResponse(response) {
135
+ // Add the success and failure counts.
136
+ var result = {
137
+ successCount: 0,
138
+ failureCount: 0,
139
+ errors: [],
140
+ };
141
+ if ('results' in response) {
142
+ response.results.forEach(function (tokenManagementResult, index) {
143
+ // Map the FCM server's error strings to actual error objects.
144
+ if ('error' in tokenManagementResult) {
145
+ result.failureCount += 1;
146
+ var newError = error_1.FirebaseMessagingError.fromTopicManagementServerError(tokenManagementResult.error, /* message */ undefined, tokenManagementResult.error);
147
+ result.errors.push({
148
+ index: index,
149
+ error: newError,
150
+ });
151
+ }
152
+ else {
153
+ result.successCount += 1;
154
+ }
155
+ });
156
+ }
157
+ return result;
158
+ }
159
+ /**
160
+ * Messaging service bound to the provided app.
161
+ */
162
+ var Messaging = /** @class */ (function () {
163
+ /**
164
+ * Gets the {@link messaging.Messaging `Messaging`} service for the
165
+ * current app.
166
+ *
167
+ * @example
168
+ * ```javascript
169
+ * var messaging = app.messaging();
170
+ * // The above is shorthand for:
171
+ * // var messaging = admin.messaging(app);
172
+ * ```
173
+ *
174
+ * @return The `Messaging` service for the current app.
175
+ */
176
+ function Messaging(app) {
177
+ if (!validator.isNonNullObject(app) || !('options' in app)) {
178
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, 'First argument passed to admin.messaging() must be a valid Firebase app instance.');
179
+ }
180
+ this.appInternal = app;
181
+ this.messagingRequestHandler = new messaging_api_request_internal_1.FirebaseMessagingRequestHandler(app);
182
+ }
183
+ Object.defineProperty(Messaging.prototype, "app", {
184
+ /**
185
+ * Returns the app associated with this Messaging instance.
186
+ *
187
+ * @return {FirebaseApp} The app associated with this Messaging instance.
188
+ */
189
+ get: function () {
190
+ return this.appInternal;
191
+ },
192
+ enumerable: false,
193
+ configurable: true
194
+ });
195
+ /**
196
+ * Sends the given message via FCM.
197
+ *
198
+ * @param message The message payload.
199
+ * @param dryRun Whether to send the message in the dry-run
200
+ * (validation only) mode.
201
+ * @return A promise fulfilled with a unique message ID
202
+ * string after the message has been successfully handed off to the FCM
203
+ * service for delivery.
204
+ */
205
+ Messaging.prototype.send = function (message, dryRun) {
206
+ var _this = this;
207
+ var copy = deep_copy_1.deepCopy(message);
208
+ messaging_internal_1.validateMessage(copy);
209
+ if (typeof dryRun !== 'undefined' && !validator.isBoolean(dryRun)) {
210
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, 'dryRun must be a boolean');
211
+ }
212
+ return this.getUrlPath()
213
+ .then(function (urlPath) {
214
+ var request = { message: copy };
215
+ if (dryRun) {
216
+ request.validate_only = true;
217
+ }
218
+ return _this.messagingRequestHandler.invokeRequestHandler(FCM_SEND_HOST, urlPath, request);
219
+ })
220
+ .then(function (response) {
221
+ return response.name;
222
+ });
223
+ };
224
+ /**
225
+ * Sends all the messages in the given array via Firebase Cloud Messaging.
226
+ * Employs batching to send the entire list as a single RPC call. Compared
227
+ * to the `send()` method, this method is a significantly more efficient way
228
+ * to send multiple messages.
229
+ *
230
+ * The responses list obtained from the return value
231
+ * corresponds to the order of tokens in the `MulticastMessage`. An error
232
+ * from this method indicates a total failure -- i.e. none of the messages in
233
+ * the list could be sent. Partial failures are indicated by a `BatchResponse`
234
+ * return value.
235
+ *
236
+ * @param messages A non-empty array
237
+ * containing up to 500 messages.
238
+ * @param dryRun Whether to send the messages in the dry-run
239
+ * (validation only) mode.
240
+ * @return A Promise fulfilled with an object representing the result of the
241
+ * send operation.
242
+ */
243
+ Messaging.prototype.sendAll = function (messages, dryRun) {
244
+ var _this = this;
245
+ if (validator.isArray(messages) && messages.constructor !== Array) {
246
+ // In more recent JS specs, an array-like object might have a constructor that is not of
247
+ // Array type. Our deepCopy() method doesn't handle them properly. Convert such objects to
248
+ // a regular array here before calling deepCopy(). See issue #566 for details.
249
+ messages = Array.from(messages);
250
+ }
251
+ var copy = deep_copy_1.deepCopy(messages);
252
+ if (!validator.isNonEmptyArray(copy)) {
253
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, 'messages must be a non-empty array');
254
+ }
255
+ if (copy.length > FCM_MAX_BATCH_SIZE) {
256
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, "messages list must not contain more than " + FCM_MAX_BATCH_SIZE + " items");
257
+ }
258
+ if (typeof dryRun !== 'undefined' && !validator.isBoolean(dryRun)) {
259
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, 'dryRun must be a boolean');
260
+ }
261
+ return this.getUrlPath()
262
+ .then(function (urlPath) {
263
+ var requests = copy.map(function (message) {
264
+ messaging_internal_1.validateMessage(message);
265
+ var request = { message: message };
266
+ if (dryRun) {
267
+ request.validate_only = true;
268
+ }
269
+ return {
270
+ url: "https://" + FCM_SEND_HOST + urlPath,
271
+ body: request,
272
+ };
273
+ });
274
+ return _this.messagingRequestHandler.sendBatchRequest(requests);
275
+ });
276
+ };
277
+ /**
278
+ * Sends the given multicast message to all the FCM registration tokens
279
+ * specified in it.
280
+ *
281
+ * This method uses the `sendAll()` API under the hood to send the given
282
+ * message to all the target recipients. The responses list obtained from the
283
+ * return value corresponds to the order of tokens in the `MulticastMessage`.
284
+ * An error from this method indicates a total failure -- i.e. the message was
285
+ * not sent to any of the tokens in the list. Partial failures are indicated by
286
+ * a `BatchResponse` return value.
287
+ *
288
+ * @param message A multicast message
289
+ * containing up to 500 tokens.
290
+ * @param dryRun Whether to send the message in the dry-run
291
+ * (validation only) mode.
292
+ * @return A Promise fulfilled with an object representing the result of the
293
+ * send operation.
294
+ */
295
+ Messaging.prototype.sendMulticast = function (message, dryRun) {
296
+ var copy = deep_copy_1.deepCopy(message);
297
+ if (!validator.isNonNullObject(copy)) {
298
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, 'MulticastMessage must be a non-null object');
299
+ }
300
+ if (!validator.isNonEmptyArray(copy.tokens)) {
301
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, 'tokens must be a non-empty array');
302
+ }
303
+ if (copy.tokens.length > FCM_MAX_BATCH_SIZE) {
304
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, "tokens list must not contain more than " + FCM_MAX_BATCH_SIZE + " items");
305
+ }
306
+ var messages = copy.tokens.map(function (token) {
307
+ return {
308
+ token: token,
309
+ android: copy.android,
310
+ apns: copy.apns,
311
+ data: copy.data,
312
+ notification: copy.notification,
313
+ webpush: copy.webpush,
314
+ fcmOptions: copy.fcmOptions,
315
+ };
316
+ });
317
+ return this.sendAll(messages, dryRun);
318
+ };
319
+ /**
320
+ * Sends an FCM message to a single device corresponding to the provided
321
+ * registration token.
322
+ *
323
+ * See
324
+ * [Send to individual devices](/docs/cloud-messaging/admin/legacy-fcm#send_to_individual_devices)
325
+ * for code samples and detailed documentation. Takes either a
326
+ * `registrationToken` to send to a single device or a
327
+ * `registrationTokens` parameter containing an array of tokens to send
328
+ * to multiple devices.
329
+ *
330
+ * @param registrationToken A device registration token or an array of
331
+ * device registration tokens to which the message should be sent.
332
+ * @param payload The message payload.
333
+ * @param options Optional options to
334
+ * alter the message.
335
+ *
336
+ * @return A promise fulfilled with the server's response after the message
337
+ * has been sent.
338
+ */
339
+ Messaging.prototype.sendToDevice = function (registrationTokenOrTokens, payload, options) {
340
+ var _this = this;
341
+ if (options === void 0) { options = {}; }
342
+ // Validate the input argument types. Since these are common developer errors when getting
343
+ // started, throw an error instead of returning a rejected promise.
344
+ this.validateRegistrationTokensType(registrationTokenOrTokens, 'sendToDevice', error_1.MessagingClientErrorCode.INVALID_RECIPIENT);
345
+ this.validateMessagingPayloadAndOptionsTypes(payload, options);
346
+ return Promise.resolve()
347
+ .then(function () {
348
+ // Validate the contents of the input arguments. Because we are now in a promise, any thrown
349
+ // error will cause this method to return a rejected promise.
350
+ _this.validateRegistrationTokens(registrationTokenOrTokens, 'sendToDevice', error_1.MessagingClientErrorCode.INVALID_RECIPIENT);
351
+ var payloadCopy = _this.validateMessagingPayload(payload);
352
+ var optionsCopy = _this.validateMessagingOptions(options);
353
+ var request = deep_copy_1.deepCopy(payloadCopy);
354
+ deep_copy_1.deepExtend(request, optionsCopy);
355
+ if (validator.isString(registrationTokenOrTokens)) {
356
+ request.to = registrationTokenOrTokens;
357
+ }
358
+ else {
359
+ request.registration_ids = registrationTokenOrTokens;
360
+ }
361
+ return _this.messagingRequestHandler.invokeRequestHandler(FCM_SEND_HOST, FCM_SEND_PATH, request);
362
+ })
363
+ .then(function (response) {
364
+ // The sendToDevice() and sendToDeviceGroup() methods both set the `to` query parameter in
365
+ // the underlying FCM request. If the provided registration token argument is actually a
366
+ // valid notification key, the response from the FCM server will be a device group response.
367
+ // If that is the case, we map the response to a MessagingDeviceGroupResponse.
368
+ // See b/35394951 for more context.
369
+ if ('multicast_id' in response) {
370
+ return mapRawResponseToDevicesResponse(response);
371
+ }
372
+ else {
373
+ var groupResponse = mapRawResponseToDeviceGroupResponse(response);
374
+ return __assign(__assign({}, groupResponse), { canonicalRegistrationTokenCount: -1, multicastId: -1, results: [] });
375
+ }
376
+ });
377
+ };
378
+ /**
379
+ * Sends an FCM message to a device group corresponding to the provided
380
+ * notification key.
381
+ *
382
+ * See
383
+ * [Send to a device group](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_device_group)
384
+ * for code samples and detailed documentation.
385
+ *
386
+ * @param notificationKey The notification key for the device group to
387
+ * which to send the message.
388
+ * @param payload The message payload.
389
+ * @param options Optional options to
390
+ * alter the message.
391
+ *
392
+ * @return A promise fulfilled with the server's response after the message
393
+ * has been sent.
394
+ */
395
+ Messaging.prototype.sendToDeviceGroup = function (notificationKey, payload, options) {
396
+ var _this = this;
397
+ if (options === void 0) { options = {}; }
398
+ if (!validator.isNonEmptyString(notificationKey)) {
399
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_RECIPIENT, 'Notification key provided to sendToDeviceGroup() must be a non-empty string.');
400
+ }
401
+ else if (notificationKey.indexOf(':') !== -1) {
402
+ // It is possible the developer provides a registration token instead of a notification key
403
+ // to this method. We can detect some of those cases by checking to see if the string contains
404
+ // a colon. Not all registration tokens will contain a colon (only newer ones will), but no
405
+ // notification keys will contain a colon, so we can use it as a rough heuristic.
406
+ // See b/35394951 for more context.
407
+ return Promise.reject(new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_RECIPIENT, 'Notification key provided to sendToDeviceGroup() has the format of a registration token. ' +
408
+ 'You should use sendToDevice() instead.'));
409
+ }
410
+ // Validate the types of the payload and options arguments. Since these are common developer
411
+ // errors, throw an error instead of returning a rejected promise.
412
+ this.validateMessagingPayloadAndOptionsTypes(payload, options);
413
+ return Promise.resolve()
414
+ .then(function () {
415
+ // Validate the contents of the payload and options objects. Because we are now in a
416
+ // promise, any thrown error will cause this method to return a rejected promise.
417
+ var payloadCopy = _this.validateMessagingPayload(payload);
418
+ var optionsCopy = _this.validateMessagingOptions(options);
419
+ var request = deep_copy_1.deepCopy(payloadCopy);
420
+ deep_copy_1.deepExtend(request, optionsCopy);
421
+ request.to = notificationKey;
422
+ return _this.messagingRequestHandler.invokeRequestHandler(FCM_SEND_HOST, FCM_SEND_PATH, request);
423
+ })
424
+ .then(function (response) {
425
+ // The sendToDevice() and sendToDeviceGroup() methods both set the `to` query parameter in
426
+ // the underlying FCM request. If the provided notification key argument has an invalid
427
+ // format (that is, it is either a registration token or some random string), the response
428
+ // from the FCM server will default to a devices response (which we detect by looking for
429
+ // the `multicast_id` property). If that is the case, we either throw an error saying the
430
+ // provided notification key is invalid (if the message failed to send) or map the response
431
+ // to a MessagingDevicesResponse (if the message succeeded).
432
+ // See b/35394951 for more context.
433
+ if ('multicast_id' in response) {
434
+ if (response.success === 0) {
435
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_RECIPIENT, 'Notification key provided to sendToDeviceGroup() is invalid.');
436
+ }
437
+ else {
438
+ var devicesResponse = mapRawResponseToDevicesResponse(response);
439
+ return __assign(__assign({}, devicesResponse), { failedRegistrationTokens: [] });
440
+ }
441
+ }
442
+ return mapRawResponseToDeviceGroupResponse(response);
443
+ });
444
+ };
445
+ /**
446
+ * Sends an FCM message to a topic.
447
+ *
448
+ * See
449
+ * [Send to a topic](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_topic)
450
+ * for code samples and detailed documentation.
451
+ *
452
+ * @param topic The topic to which to send the message.
453
+ * @param payload The message payload.
454
+ * @param options Optional options to
455
+ * alter the message.
456
+ *
457
+ * @return A promise fulfilled with the server's response after the message
458
+ * has been sent.
459
+ */
460
+ Messaging.prototype.sendToTopic = function (topic, payload, options) {
461
+ var _this = this;
462
+ if (options === void 0) { options = {}; }
463
+ // Validate the input argument types. Since these are common developer errors when getting
464
+ // started, throw an error instead of returning a rejected promise.
465
+ this.validateTopicType(topic, 'sendToTopic', error_1.MessagingClientErrorCode.INVALID_RECIPIENT);
466
+ this.validateMessagingPayloadAndOptionsTypes(payload, options);
467
+ // Prepend the topic with /topics/ if necessary.
468
+ topic = this.normalizeTopic(topic);
469
+ return Promise.resolve()
470
+ .then(function () {
471
+ // Validate the contents of the payload and options objects. Because we are now in a
472
+ // promise, any thrown error will cause this method to return a rejected promise.
473
+ var payloadCopy = _this.validateMessagingPayload(payload);
474
+ var optionsCopy = _this.validateMessagingOptions(options);
475
+ _this.validateTopic(topic, 'sendToTopic', error_1.MessagingClientErrorCode.INVALID_RECIPIENT);
476
+ var request = deep_copy_1.deepCopy(payloadCopy);
477
+ deep_copy_1.deepExtend(request, optionsCopy);
478
+ request.to = topic;
479
+ return _this.messagingRequestHandler.invokeRequestHandler(FCM_SEND_HOST, FCM_SEND_PATH, request);
480
+ })
481
+ .then(function (response) {
482
+ // Rename properties on the server response
483
+ utils.renameProperties(response, MESSAGING_TOPIC_RESPONSE_KEYS_MAP);
484
+ return response;
485
+ });
486
+ };
487
+ /**
488
+ * Sends an FCM message to a condition.
489
+ *
490
+ * See
491
+ * [Send to a condition](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_condition)
492
+ * for code samples and detailed documentation.
493
+ *
494
+ * @param condition The condition determining to which topics to send
495
+ * the message.
496
+ * @param payload The message payload.
497
+ * @param options Optional options to
498
+ * alter the message.
499
+ *
500
+ * @return A promise fulfilled with the server's response after the message
501
+ * has been sent.
502
+ */
503
+ Messaging.prototype.sendToCondition = function (condition, payload, options) {
504
+ var _this = this;
505
+ if (options === void 0) { options = {}; }
506
+ if (!validator.isNonEmptyString(condition)) {
507
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_RECIPIENT, 'Condition provided to sendToCondition() must be a non-empty string.');
508
+ }
509
+ // Validate the types of the payload and options arguments. Since these are common developer
510
+ // errors, throw an error instead of returning a rejected promise.
511
+ this.validateMessagingPayloadAndOptionsTypes(payload, options);
512
+ // The FCM server rejects conditions which are surrounded in single quotes. When the condition
513
+ // is stringified over the wire, double quotes in it get converted to \" which the FCM server
514
+ // does not properly handle. We can get around this by replacing internal double quotes with
515
+ // single quotes.
516
+ condition = condition.replace(/"/g, '\'');
517
+ return Promise.resolve()
518
+ .then(function () {
519
+ // Validate the contents of the payload and options objects. Because we are now in a
520
+ // promise, any thrown error will cause this method to return a rejected promise.
521
+ var payloadCopy = _this.validateMessagingPayload(payload);
522
+ var optionsCopy = _this.validateMessagingOptions(options);
523
+ var request = deep_copy_1.deepCopy(payloadCopy);
524
+ deep_copy_1.deepExtend(request, optionsCopy);
525
+ request.condition = condition;
526
+ return _this.messagingRequestHandler.invokeRequestHandler(FCM_SEND_HOST, FCM_SEND_PATH, request);
527
+ })
528
+ .then(function (response) {
529
+ // Rename properties on the server response
530
+ utils.renameProperties(response, MESSAGING_CONDITION_RESPONSE_KEYS_MAP);
531
+ return response;
532
+ });
533
+ };
534
+ /**
535
+ * Subscribes a device to an FCM topic.
536
+ *
537
+ * See [Subscribe to a
538
+ * topic](/docs/cloud-messaging/manage-topics#suscribe_and_unsubscribe_using_the)
539
+ * for code samples and detailed documentation. Optionally, you can provide an
540
+ * array of tokens to subscribe multiple devices.
541
+ *
542
+ * @param registrationTokens A token or array of registration tokens
543
+ * for the devices to subscribe to the topic.
544
+ * @param topic The topic to which to subscribe.
545
+ *
546
+ * @return A promise fulfilled with the server's response after the device has been
547
+ * subscribed to the topic.
548
+ */
549
+ Messaging.prototype.subscribeToTopic = function (registrationTokenOrTokens, topic) {
550
+ return this.sendTopicManagementRequest(registrationTokenOrTokens, topic, 'subscribeToTopic', FCM_TOPIC_MANAGEMENT_ADD_PATH);
551
+ };
552
+ /**
553
+ * Unsubscribes a device from an FCM topic.
554
+ *
555
+ * See [Unsubscribe from a
556
+ * topic](/docs/cloud-messaging/admin/manage-topic-subscriptions#unsubscribe_from_a_topic)
557
+ * for code samples and detailed documentation. Optionally, you can provide an
558
+ * array of tokens to unsubscribe multiple devices.
559
+ *
560
+ * @param registrationTokens A device registration token or an array of
561
+ * device registration tokens to unsubscribe from the topic.
562
+ * @param topic The topic from which to unsubscribe.
563
+ *
564
+ * @return A promise fulfilled with the server's response after the device has been
565
+ * unsubscribed from the topic.
566
+ */
567
+ Messaging.prototype.unsubscribeFromTopic = function (registrationTokenOrTokens, topic) {
568
+ return this.sendTopicManagementRequest(registrationTokenOrTokens, topic, 'unsubscribeFromTopic', FCM_TOPIC_MANAGEMENT_REMOVE_PATH);
569
+ };
570
+ Messaging.prototype.getUrlPath = function () {
571
+ var _this = this;
572
+ if (this.urlPath) {
573
+ return Promise.resolve(this.urlPath);
574
+ }
575
+ return utils.findProjectId(this.app)
576
+ .then(function (projectId) {
577
+ if (!validator.isNonEmptyString(projectId)) {
578
+ // Assert for an explicit project ID (either via AppOptions or the cert itself).
579
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_ARGUMENT, 'Failed to determine project ID for Messaging. Initialize the '
580
+ + 'SDK with service account credentials or set project ID as an app option. '
581
+ + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.');
582
+ }
583
+ _this.urlPath = "/v1/projects/" + projectId + "/messages:send";
584
+ return _this.urlPath;
585
+ });
586
+ };
587
+ /**
588
+ * Helper method which sends and handles topic subscription management requests.
589
+ *
590
+ * @param {string|string[]} registrationTokenOrTokens The registration token or an array of
591
+ * registration tokens to unsubscribe from the topic.
592
+ * @param {string} topic The topic to which to subscribe.
593
+ * @param {string} methodName The name of the original method called.
594
+ * @param {string} path The endpoint path to use for the request.
595
+ *
596
+ * @return {Promise<MessagingTopicManagementResponse>} A Promise fulfilled with the parsed server
597
+ * response.
598
+ */
599
+ Messaging.prototype.sendTopicManagementRequest = function (registrationTokenOrTokens, topic, methodName, path) {
600
+ var _this = this;
601
+ this.validateRegistrationTokensType(registrationTokenOrTokens, methodName);
602
+ this.validateTopicType(topic, methodName);
603
+ // Prepend the topic with /topics/ if necessary.
604
+ topic = this.normalizeTopic(topic);
605
+ return Promise.resolve()
606
+ .then(function () {
607
+ // Validate the contents of the input arguments. Because we are now in a promise, any thrown
608
+ // error will cause this method to return a rejected promise.
609
+ _this.validateRegistrationTokens(registrationTokenOrTokens, methodName);
610
+ _this.validateTopic(topic, methodName);
611
+ // Ensure the registration token(s) input argument is an array.
612
+ var registrationTokensArray = registrationTokenOrTokens;
613
+ if (validator.isString(registrationTokenOrTokens)) {
614
+ registrationTokensArray = [registrationTokenOrTokens];
615
+ }
616
+ var request = {
617
+ to: topic,
618
+ registration_tokens: registrationTokensArray,
619
+ };
620
+ return _this.messagingRequestHandler.invokeRequestHandler(FCM_TOPIC_MANAGEMENT_HOST, path, request);
621
+ })
622
+ .then(function (response) {
623
+ return mapRawResponseToTopicManagementResponse(response);
624
+ });
625
+ };
626
+ /**
627
+ * Validates the types of the messaging payload and options. If invalid, an error will be thrown.
628
+ *
629
+ * @param {MessagingPayload} payload The messaging payload to validate.
630
+ * @param {MessagingOptions} options The messaging options to validate.
631
+ */
632
+ Messaging.prototype.validateMessagingPayloadAndOptionsTypes = function (payload, options) {
633
+ // Validate the payload is an object
634
+ if (!validator.isNonNullObject(payload)) {
635
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'Messaging payload must be an object with at least one of the "data" or "notification" properties.');
636
+ }
637
+ // Validate the options argument is an object
638
+ if (!validator.isNonNullObject(options)) {
639
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, 'Messaging options must be an object.');
640
+ }
641
+ };
642
+ /**
643
+ * Validates the messaging payload. If invalid, an error will be thrown.
644
+ *
645
+ * @param {MessagingPayload} payload The messaging payload to validate.
646
+ *
647
+ * @return {MessagingPayload} A copy of the provided payload with whitelisted properties switched
648
+ * from camelCase to underscore_case.
649
+ */
650
+ Messaging.prototype.validateMessagingPayload = function (payload) {
651
+ var payloadCopy = deep_copy_1.deepCopy(payload);
652
+ var payloadKeys = Object.keys(payloadCopy);
653
+ var validPayloadKeys = ['data', 'notification'];
654
+ var containsDataOrNotificationKey = false;
655
+ payloadKeys.forEach(function (payloadKey) {
656
+ // Validate the payload does not contain any invalid keys
657
+ if (validPayloadKeys.indexOf(payloadKey) === -1) {
658
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, "Messaging payload contains an invalid \"" + payloadKey + "\" property. Valid properties are " +
659
+ '"data" and "notification".');
660
+ }
661
+ else {
662
+ containsDataOrNotificationKey = true;
663
+ }
664
+ });
665
+ // Validate the payload contains at least one of the "data" and "notification" keys
666
+ if (!containsDataOrNotificationKey) {
667
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'Messaging payload must contain at least one of the "data" or "notification" properties.');
668
+ }
669
+ var validatePayload = function (payloadKey, value) {
670
+ // Validate each top-level key in the payload is an object
671
+ if (!validator.isNonNullObject(value)) {
672
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, "Messaging payload contains an invalid value for the \"" + payloadKey + "\" property. " +
673
+ 'Value must be an object.');
674
+ }
675
+ Object.keys(value).forEach(function (subKey) {
676
+ if (!validator.isString(value[subKey])) {
677
+ // Validate all sub-keys have a string value
678
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, "Messaging payload contains an invalid value for the \"" + payloadKey + "." + subKey + "\" " +
679
+ 'property. Values must be strings.');
680
+ }
681
+ else if (payloadKey === 'data' && /^google\./.test(subKey)) {
682
+ // Validate the data payload does not contain keys which start with 'google.'.
683
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, "Messaging payload contains the blacklisted \"data." + subKey + "\" property.");
684
+ }
685
+ });
686
+ };
687
+ if (payloadCopy.data !== undefined) {
688
+ validatePayload('data', payloadCopy.data);
689
+ }
690
+ if (payloadCopy.notification !== undefined) {
691
+ validatePayload('notification', payloadCopy.notification);
692
+ }
693
+ // Validate the data payload object does not contain blacklisted properties
694
+ if ('data' in payloadCopy) {
695
+ messaging_internal_1.BLACKLISTED_DATA_PAYLOAD_KEYS.forEach(function (blacklistedKey) {
696
+ if (blacklistedKey in payloadCopy.data) {
697
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, "Messaging payload contains the blacklisted \"data." + blacklistedKey + "\" property.");
698
+ }
699
+ });
700
+ }
701
+ // Convert whitelisted camelCase keys to underscore_case
702
+ if (payloadCopy.notification) {
703
+ utils.renameProperties(payloadCopy.notification, CAMELCASED_NOTIFICATION_PAYLOAD_KEYS_MAP);
704
+ }
705
+ return payloadCopy;
706
+ };
707
+ /**
708
+ * Validates the messaging options. If invalid, an error will be thrown.
709
+ *
710
+ * @param {MessagingOptions} options The messaging options to validate.
711
+ *
712
+ * @return {MessagingOptions} A copy of the provided options with whitelisted properties switched
713
+ * from camelCase to underscore_case.
714
+ */
715
+ Messaging.prototype.validateMessagingOptions = function (options) {
716
+ var optionsCopy = deep_copy_1.deepCopy(options);
717
+ // Validate the options object does not contain blacklisted properties
718
+ messaging_internal_1.BLACKLISTED_OPTIONS_KEYS.forEach(function (blacklistedKey) {
719
+ if (blacklistedKey in optionsCopy) {
720
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, "Messaging options contains the blacklisted \"" + blacklistedKey + "\" property.");
721
+ }
722
+ });
723
+ // Convert whitelisted camelCase keys to underscore_case
724
+ utils.renameProperties(optionsCopy, CAMELCASE_OPTIONS_KEYS_MAP);
725
+ // Validate the options object contains valid values for whitelisted properties
726
+ if ('collapse_key' in optionsCopy && !validator.isNonEmptyString(optionsCopy.collapse_key)) {
727
+ var keyName = ('collapseKey' in options) ? 'collapseKey' : 'collapse_key';
728
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, "Messaging options contains an invalid value for the \"" + keyName + "\" property. Value must " +
729
+ 'be a non-empty string.');
730
+ }
731
+ else if ('dry_run' in optionsCopy && !validator.isBoolean(optionsCopy.dry_run)) {
732
+ var keyName = ('dryRun' in options) ? 'dryRun' : 'dry_run';
733
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, "Messaging options contains an invalid value for the \"" + keyName + "\" property. Value must " +
734
+ 'be a boolean.');
735
+ }
736
+ else if ('priority' in optionsCopy && !validator.isNonEmptyString(optionsCopy.priority)) {
737
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, 'Messaging options contains an invalid value for the "priority" property. Value must ' +
738
+ 'be a non-empty string.');
739
+ }
740
+ else if ('restricted_package_name' in optionsCopy &&
741
+ !validator.isNonEmptyString(optionsCopy.restricted_package_name)) {
742
+ var keyName = ('restrictedPackageName' in options) ? 'restrictedPackageName' : 'restricted_package_name';
743
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, "Messaging options contains an invalid value for the \"" + keyName + "\" property. Value must " +
744
+ 'be a non-empty string.');
745
+ }
746
+ else if ('time_to_live' in optionsCopy && !validator.isNumber(optionsCopy.time_to_live)) {
747
+ var keyName = ('timeToLive' in options) ? 'timeToLive' : 'time_to_live';
748
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, "Messaging options contains an invalid value for the \"" + keyName + "\" property. Value must " +
749
+ 'be a number.');
750
+ }
751
+ else if ('content_available' in optionsCopy && !validator.isBoolean(optionsCopy.content_available)) {
752
+ var keyName = ('contentAvailable' in options) ? 'contentAvailable' : 'content_available';
753
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, "Messaging options contains an invalid value for the \"" + keyName + "\" property. Value must " +
754
+ 'be a boolean.');
755
+ }
756
+ else if ('mutable_content' in optionsCopy && !validator.isBoolean(optionsCopy.mutable_content)) {
757
+ var keyName = ('mutableContent' in options) ? 'mutableContent' : 'mutable_content';
758
+ throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_OPTIONS, "Messaging options contains an invalid value for the \"" + keyName + "\" property. Value must " +
759
+ 'be a boolean.');
760
+ }
761
+ return optionsCopy;
762
+ };
763
+ /**
764
+ * Validates the type of the provided registration token(s). If invalid, an error will be thrown.
765
+ *
766
+ * @param {string|string[]} registrationTokenOrTokens The registration token(s) to validate.
767
+ * @param {string} method The method name to use in error messages.
768
+ * @param {ErrorInfo?} [errorInfo] The error info to use if the registration tokens are invalid.
769
+ */
770
+ Messaging.prototype.validateRegistrationTokensType = function (registrationTokenOrTokens, methodName, errorInfo) {
771
+ if (errorInfo === void 0) { errorInfo = error_1.MessagingClientErrorCode.INVALID_ARGUMENT; }
772
+ if (!validator.isNonEmptyArray(registrationTokenOrTokens) &&
773
+ !validator.isNonEmptyString(registrationTokenOrTokens)) {
774
+ throw new error_1.FirebaseMessagingError(errorInfo, "Registration token(s) provided to " + methodName + "() must be a non-empty string or a " +
775
+ 'non-empty array.');
776
+ }
777
+ };
778
+ /**
779
+ * Validates the provided registration tokens. If invalid, an error will be thrown.
780
+ *
781
+ * @param {string|string[]} registrationTokenOrTokens The registration token or an array of
782
+ * registration tokens to validate.
783
+ * @param {string} method The method name to use in error messages.
784
+ * @param {errorInfo?} [ErrorInfo] The error info to use if the registration tokens are invalid.
785
+ */
786
+ Messaging.prototype.validateRegistrationTokens = function (registrationTokenOrTokens, methodName, errorInfo) {
787
+ if (errorInfo === void 0) { errorInfo = error_1.MessagingClientErrorCode.INVALID_ARGUMENT; }
788
+ if (validator.isArray(registrationTokenOrTokens)) {
789
+ // Validate the array contains no more than 1,000 registration tokens.
790
+ if (registrationTokenOrTokens.length > 1000) {
791
+ throw new error_1.FirebaseMessagingError(errorInfo, "Too many registration tokens provided in a single request to " + methodName + "(). Batch " +
792
+ 'your requests to contain no more than 1,000 registration tokens per request.');
793
+ }
794
+ // Validate the array contains registration tokens which are non-empty strings.
795
+ registrationTokenOrTokens.forEach(function (registrationToken, index) {
796
+ if (!validator.isNonEmptyString(registrationToken)) {
797
+ throw new error_1.FirebaseMessagingError(errorInfo, "Registration token provided to " + methodName + "() at index " + index + " must be a " +
798
+ 'non-empty string.');
799
+ }
800
+ });
801
+ }
802
+ };
803
+ /**
804
+ * Validates the type of the provided topic. If invalid, an error will be thrown.
805
+ *
806
+ * @param {string} topic The topic to validate.
807
+ * @param {string} method The method name to use in error messages.
808
+ * @param {ErrorInfo?} [errorInfo] The error info to use if the topic is invalid.
809
+ */
810
+ Messaging.prototype.validateTopicType = function (topic, methodName, errorInfo) {
811
+ if (errorInfo === void 0) { errorInfo = error_1.MessagingClientErrorCode.INVALID_ARGUMENT; }
812
+ if (!validator.isNonEmptyString(topic)) {
813
+ throw new error_1.FirebaseMessagingError(errorInfo, "Topic provided to " + methodName + "() must be a string which matches the format " +
814
+ '"/topics/[a-zA-Z0-9-_.~%]+".');
815
+ }
816
+ };
817
+ /**
818
+ * Validates the provided topic. If invalid, an error will be thrown.
819
+ *
820
+ * @param {string} topic The topic to validate.
821
+ * @param {string} method The method name to use in error messages.
822
+ * @param {ErrorInfo?} [errorInfo] The error info to use if the topic is invalid.
823
+ */
824
+ Messaging.prototype.validateTopic = function (topic, methodName, errorInfo) {
825
+ if (errorInfo === void 0) { errorInfo = error_1.MessagingClientErrorCode.INVALID_ARGUMENT; }
826
+ if (!validator.isTopic(topic)) {
827
+ throw new error_1.FirebaseMessagingError(errorInfo, "Topic provided to " + methodName + "() must be a string which matches the format " +
828
+ '"/topics/[a-zA-Z0-9-_.~%]+".');
829
+ }
830
+ };
831
+ /**
832
+ * Normalizes the provided topic name by prepending it with '/topics/', if necessary.
833
+ *
834
+ * @param {string} topic The topic name to normalize.
835
+ *
836
+ * @return {string} The normalized topic name.
837
+ */
838
+ Messaging.prototype.normalizeTopic = function (topic) {
839
+ if (!/^\/topics\//.test(topic)) {
840
+ topic = "/topics/" + topic;
841
+ }
842
+ return topic;
843
+ };
844
+ return Messaging;
845
+ }());
846
+ exports.Messaging = Messaging;