messaging-internal.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*! firebase-admin v12.1.1 */
  2. "use strict";
  3. /*!
  4. * Copyright 2020 Google Inc.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. Object.defineProperty(exports, "__esModule", { value: true });
  19. exports.validateMessage = exports.BLACKLISTED_OPTIONS_KEYS = exports.BLACKLISTED_DATA_PAYLOAD_KEYS = void 0;
  20. const index_1 = require("../utils/index");
  21. const error_1 = require("../utils/error");
  22. const validator = require("../utils/validator");
  23. // Keys which are not allowed in the messaging data payload object.
  24. exports.BLACKLISTED_DATA_PAYLOAD_KEYS = ['from'];
  25. // Keys which are not allowed in the messaging options object.
  26. exports.BLACKLISTED_OPTIONS_KEYS = [
  27. 'condition', 'data', 'notification', 'registrationIds', 'registration_ids', 'to',
  28. ];
  29. /**
  30. * Checks if the given Message object is valid. Recursively validates all the child objects
  31. * included in the message (android, apns, data etc.). If successful, transforms the message
  32. * in place by renaming the keys to what's expected by the remote FCM service.
  33. *
  34. * @param {Message} Message An object to be validated.
  35. */
  36. function validateMessage(message) {
  37. if (!validator.isNonNullObject(message)) {
  38. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'Message must be a non-null object');
  39. }
  40. const anyMessage = message;
  41. if (anyMessage.topic) {
  42. // If the topic name is prefixed, remove it.
  43. if (anyMessage.topic.startsWith('/topics/')) {
  44. anyMessage.topic = anyMessage.topic.replace(/^\/topics\//, '');
  45. }
  46. // Checks for illegal characters and empty string.
  47. if (!/^[a-zA-Z0-9-_.~%]+$/.test(anyMessage.topic)) {
  48. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'Malformed topic name');
  49. }
  50. }
  51. const targets = [anyMessage.token, anyMessage.topic, anyMessage.condition];
  52. if (targets.filter((v) => validator.isNonEmptyString(v)).length !== 1) {
  53. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'Exactly one of topic, token or condition is required');
  54. }
  55. validateStringMap(message.data, 'data');
  56. validateAndroidConfig(message.android);
  57. validateWebpushConfig(message.webpush);
  58. validateApnsConfig(message.apns);
  59. validateFcmOptions(message.fcmOptions);
  60. validateNotification(message.notification);
  61. }
  62. exports.validateMessage = validateMessage;
  63. /**
  64. * Checks if the given object only contains strings as child values.
  65. *
  66. * @param {object} map An object to be validated.
  67. * @param {string} label A label to be included in the errors thrown.
  68. */
  69. function validateStringMap(map, label) {
  70. if (typeof map === 'undefined') {
  71. return;
  72. }
  73. else if (!validator.isNonNullObject(map)) {
  74. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, `${label} must be a non-null object`);
  75. }
  76. Object.keys(map).forEach((key) => {
  77. if (!validator.isString(map[key])) {
  78. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, `${label} must only contain string values`);
  79. }
  80. });
  81. }
  82. /**
  83. * Checks if the given WebpushConfig object is valid. The object must have valid headers and data.
  84. *
  85. * @param {WebpushConfig} config An object to be validated.
  86. */
  87. function validateWebpushConfig(config) {
  88. if (typeof config === 'undefined') {
  89. return;
  90. }
  91. else if (!validator.isNonNullObject(config)) {
  92. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'webpush must be a non-null object');
  93. }
  94. validateStringMap(config.headers, 'webpush.headers');
  95. validateStringMap(config.data, 'webpush.data');
  96. }
  97. /**
  98. * Checks if the given ApnsConfig object is valid. The object must have valid headers and a
  99. * payload.
  100. *
  101. * @param {ApnsConfig} config An object to be validated.
  102. */
  103. function validateApnsConfig(config) {
  104. if (typeof config === 'undefined') {
  105. return;
  106. }
  107. else if (!validator.isNonNullObject(config)) {
  108. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns must be a non-null object');
  109. }
  110. validateStringMap(config.headers, 'apns.headers');
  111. validateApnsPayload(config.payload);
  112. validateApnsFcmOptions(config.fcmOptions);
  113. }
  114. /**
  115. * Checks if the given ApnsFcmOptions object is valid.
  116. *
  117. * @param {ApnsFcmOptions} fcmOptions An object to be validated.
  118. */
  119. function validateApnsFcmOptions(fcmOptions) {
  120. if (typeof fcmOptions === 'undefined') {
  121. return;
  122. }
  123. else if (!validator.isNonNullObject(fcmOptions)) {
  124. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object');
  125. }
  126. if (typeof fcmOptions.imageUrl !== 'undefined' &&
  127. !validator.isURL(fcmOptions.imageUrl)) {
  128. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'imageUrl must be a valid URL string');
  129. }
  130. if (typeof fcmOptions.analyticsLabel !== 'undefined' && !validator.isString(fcmOptions.analyticsLabel)) {
  131. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value');
  132. }
  133. const propertyMappings = {
  134. imageUrl: 'image',
  135. };
  136. Object.keys(propertyMappings).forEach((key) => {
  137. if (key in fcmOptions && propertyMappings[key] in fcmOptions) {
  138. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, `Multiple specifications for ${key} in ApnsFcmOptions`);
  139. }
  140. });
  141. (0, index_1.renameProperties)(fcmOptions, propertyMappings);
  142. }
  143. /**
  144. * Checks if the given FcmOptions object is valid.
  145. *
  146. * @param {FcmOptions} fcmOptions An object to be validated.
  147. */
  148. function validateFcmOptions(fcmOptions) {
  149. if (typeof fcmOptions === 'undefined') {
  150. return;
  151. }
  152. else if (!validator.isNonNullObject(fcmOptions)) {
  153. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object');
  154. }
  155. if (typeof fcmOptions.analyticsLabel !== 'undefined' && !validator.isString(fcmOptions.analyticsLabel)) {
  156. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value');
  157. }
  158. }
  159. /**
  160. * Checks if the given Notification object is valid.
  161. *
  162. * @param {Notification} notification An object to be validated.
  163. */
  164. function validateNotification(notification) {
  165. if (typeof notification === 'undefined') {
  166. return;
  167. }
  168. else if (!validator.isNonNullObject(notification)) {
  169. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'notification must be a non-null object');
  170. }
  171. if (typeof notification.imageUrl !== 'undefined' && !validator.isURL(notification.imageUrl)) {
  172. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'notification.imageUrl must be a valid URL string');
  173. }
  174. const propertyMappings = {
  175. imageUrl: 'image',
  176. };
  177. Object.keys(propertyMappings).forEach((key) => {
  178. if (key in notification && propertyMappings[key] in notification) {
  179. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, `Multiple specifications for ${key} in Notification`);
  180. }
  181. });
  182. (0, index_1.renameProperties)(notification, propertyMappings);
  183. }
  184. /**
  185. * Checks if the given ApnsPayload object is valid. The object must have a valid aps value.
  186. *
  187. * @param {ApnsPayload} payload An object to be validated.
  188. */
  189. function validateApnsPayload(payload) {
  190. if (typeof payload === 'undefined') {
  191. return;
  192. }
  193. else if (!validator.isNonNullObject(payload)) {
  194. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload must be a non-null object');
  195. }
  196. validateAps(payload.aps);
  197. }
  198. /**
  199. * Checks if the given Aps object is valid. The object must have a valid alert. If the validation
  200. * is successful, transforms the input object by renaming the keys to valid APNS payload keys.
  201. *
  202. * @param {Aps} aps An object to be validated.
  203. */
  204. function validateAps(aps) {
  205. if (typeof aps === 'undefined') {
  206. return;
  207. }
  208. else if (!validator.isNonNullObject(aps)) {
  209. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps must be a non-null object');
  210. }
  211. validateApsAlert(aps.alert);
  212. validateApsSound(aps.sound);
  213. const propertyMappings = {
  214. contentAvailable: 'content-available',
  215. mutableContent: 'mutable-content',
  216. threadId: 'thread-id',
  217. };
  218. Object.keys(propertyMappings).forEach((key) => {
  219. if (key in aps && propertyMappings[key] in aps) {
  220. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, `Multiple specifications for ${key} in Aps`);
  221. }
  222. });
  223. (0, index_1.renameProperties)(aps, propertyMappings);
  224. const contentAvailable = aps['content-available'];
  225. if (typeof contentAvailable !== 'undefined' && contentAvailable !== 1) {
  226. if (contentAvailable === true) {
  227. aps['content-available'] = 1;
  228. }
  229. else {
  230. delete aps['content-available'];
  231. }
  232. }
  233. const mutableContent = aps['mutable-content'];
  234. if (typeof mutableContent !== 'undefined' && mutableContent !== 1) {
  235. if (mutableContent === true) {
  236. aps['mutable-content'] = 1;
  237. }
  238. else {
  239. delete aps['mutable-content'];
  240. }
  241. }
  242. }
  243. function validateApsSound(sound) {
  244. if (typeof sound === 'undefined' || validator.isNonEmptyString(sound)) {
  245. return;
  246. }
  247. else if (!validator.isNonNullObject(sound)) {
  248. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound must be a non-empty string or a non-null object');
  249. }
  250. if (!validator.isNonEmptyString(sound.name)) {
  251. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound.name must be a non-empty string');
  252. }
  253. const volume = sound.volume;
  254. if (typeof volume !== 'undefined') {
  255. if (!validator.isNumber(volume)) {
  256. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound.volume must be a number');
  257. }
  258. if (volume < 0 || volume > 1) {
  259. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound.volume must be in the interval [0, 1]');
  260. }
  261. }
  262. const soundObject = sound;
  263. const key = 'critical';
  264. const critical = soundObject[key];
  265. if (typeof critical !== 'undefined' && critical !== 1) {
  266. if (critical === true) {
  267. soundObject[key] = 1;
  268. }
  269. else {
  270. delete soundObject[key];
  271. }
  272. }
  273. }
  274. /**
  275. * Checks if the given alert object is valid. Alert could be a string or a complex object.
  276. * If specified as an object, it must have valid localization parameters. If successful, transforms
  277. * the input object by renaming the keys to valid APNS payload keys.
  278. *
  279. * @param {string | ApsAlert} alert An alert string or an object to be validated.
  280. */
  281. function validateApsAlert(alert) {
  282. if (typeof alert === 'undefined' || validator.isString(alert)) {
  283. return;
  284. }
  285. else if (!validator.isNonNullObject(alert)) {
  286. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert must be a string or a non-null object');
  287. }
  288. const apsAlert = alert;
  289. if (validator.isNonEmptyArray(apsAlert.locArgs) &&
  290. !validator.isNonEmptyString(apsAlert.locKey)) {
  291. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert.locKey is required when specifying locArgs');
  292. }
  293. if (validator.isNonEmptyArray(apsAlert.titleLocArgs) &&
  294. !validator.isNonEmptyString(apsAlert.titleLocKey)) {
  295. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert.titleLocKey is required when specifying titleLocArgs');
  296. }
  297. if (validator.isNonEmptyArray(apsAlert.subtitleLocArgs) &&
  298. !validator.isNonEmptyString(apsAlert.subtitleLocKey)) {
  299. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert.subtitleLocKey is required when specifying subtitleLocArgs');
  300. }
  301. const propertyMappings = {
  302. locKey: 'loc-key',
  303. locArgs: 'loc-args',
  304. titleLocKey: 'title-loc-key',
  305. titleLocArgs: 'title-loc-args',
  306. subtitleLocKey: 'subtitle-loc-key',
  307. subtitleLocArgs: 'subtitle-loc-args',
  308. actionLocKey: 'action-loc-key',
  309. launchImage: 'launch-image',
  310. };
  311. (0, index_1.renameProperties)(apsAlert, propertyMappings);
  312. }
  313. /**
  314. * Checks if the given AndroidConfig object is valid. The object must have valid ttl, data,
  315. * and notification fields. If successful, transforms the input object by renaming keys to valid
  316. * Android keys. Also transforms the ttl value to the format expected by FCM service.
  317. *
  318. * @param config - An object to be validated.
  319. */
  320. function validateAndroidConfig(config) {
  321. if (typeof config === 'undefined') {
  322. return;
  323. }
  324. else if (!validator.isNonNullObject(config)) {
  325. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android must be a non-null object');
  326. }
  327. if (typeof config.ttl !== 'undefined') {
  328. if (!validator.isNumber(config.ttl) || config.ttl < 0) {
  329. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'TTL must be a non-negative duration in milliseconds');
  330. }
  331. const duration = (0, index_1.transformMillisecondsToSecondsString)(config.ttl);
  332. config.ttl = duration;
  333. }
  334. validateStringMap(config.data, 'android.data');
  335. validateAndroidNotification(config.notification);
  336. validateAndroidFcmOptions(config.fcmOptions);
  337. const propertyMappings = {
  338. collapseKey: 'collapse_key',
  339. restrictedPackageName: 'restricted_package_name',
  340. };
  341. (0, index_1.renameProperties)(config, propertyMappings);
  342. }
  343. /**
  344. * Checks if the given AndroidNotification object is valid. The object must have valid color and
  345. * localization parameters. If successful, transforms the input object by renaming keys to valid
  346. * Android keys.
  347. *
  348. * @param {AndroidNotification} notification An object to be validated.
  349. */
  350. function validateAndroidNotification(notification) {
  351. if (typeof notification === 'undefined') {
  352. return;
  353. }
  354. else if (!validator.isNonNullObject(notification)) {
  355. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification must be a non-null object');
  356. }
  357. if (typeof notification.color !== 'undefined' && !/^#[0-9a-fA-F]{6}$/.test(notification.color)) {
  358. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.color must be in the form #RRGGBB');
  359. }
  360. if (validator.isNonEmptyArray(notification.bodyLocArgs) &&
  361. !validator.isNonEmptyString(notification.bodyLocKey)) {
  362. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.bodyLocKey is required when specifying bodyLocArgs');
  363. }
  364. if (validator.isNonEmptyArray(notification.titleLocArgs) &&
  365. !validator.isNonEmptyString(notification.titleLocKey)) {
  366. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.titleLocKey is required when specifying titleLocArgs');
  367. }
  368. if (typeof notification.imageUrl !== 'undefined' &&
  369. !validator.isURL(notification.imageUrl)) {
  370. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.imageUrl must be a valid URL string');
  371. }
  372. if (typeof notification.eventTimestamp !== 'undefined') {
  373. if (!(notification.eventTimestamp instanceof Date)) {
  374. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.eventTimestamp must be a valid `Date` object');
  375. }
  376. // Convert timestamp to RFC3339 UTC "Zulu" format, example "2014-10-02T15:01:23.045123456Z"
  377. const zuluTimestamp = notification.eventTimestamp.toISOString();
  378. notification.eventTimestamp = zuluTimestamp;
  379. }
  380. if (typeof notification.vibrateTimingsMillis !== 'undefined') {
  381. if (!validator.isNonEmptyArray(notification.vibrateTimingsMillis)) {
  382. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.vibrateTimingsMillis must be a non-empty array of numbers');
  383. }
  384. const vibrateTimings = [];
  385. notification.vibrateTimingsMillis.forEach((value) => {
  386. if (!validator.isNumber(value) || value < 0) {
  387. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.vibrateTimingsMillis must be non-negative durations in milliseconds');
  388. }
  389. const duration = (0, index_1.transformMillisecondsToSecondsString)(value);
  390. vibrateTimings.push(duration);
  391. });
  392. notification.vibrateTimingsMillis = vibrateTimings;
  393. }
  394. if (typeof notification.priority !== 'undefined') {
  395. const priority = 'PRIORITY_' + notification.priority.toUpperCase();
  396. notification.priority = priority;
  397. }
  398. if (typeof notification.visibility !== 'undefined') {
  399. const visibility = notification.visibility.toUpperCase();
  400. notification.visibility = visibility;
  401. }
  402. validateLightSettings(notification.lightSettings);
  403. const propertyMappings = {
  404. clickAction: 'click_action',
  405. bodyLocKey: 'body_loc_key',
  406. bodyLocArgs: 'body_loc_args',
  407. titleLocKey: 'title_loc_key',
  408. titleLocArgs: 'title_loc_args',
  409. channelId: 'channel_id',
  410. imageUrl: 'image',
  411. eventTimestamp: 'event_time',
  412. localOnly: 'local_only',
  413. priority: 'notification_priority',
  414. vibrateTimingsMillis: 'vibrate_timings',
  415. defaultVibrateTimings: 'default_vibrate_timings',
  416. defaultSound: 'default_sound',
  417. lightSettings: 'light_settings',
  418. defaultLightSettings: 'default_light_settings',
  419. notificationCount: 'notification_count',
  420. };
  421. (0, index_1.renameProperties)(notification, propertyMappings);
  422. }
  423. /**
  424. * Checks if the given LightSettings object is valid. The object must have valid color and
  425. * light on/off duration parameters. If successful, transforms the input object by renaming
  426. * keys to valid Android keys.
  427. *
  428. * @param {LightSettings} lightSettings An object to be validated.
  429. */
  430. function validateLightSettings(lightSettings) {
  431. if (typeof lightSettings === 'undefined') {
  432. return;
  433. }
  434. else if (!validator.isNonNullObject(lightSettings)) {
  435. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings must be a non-null object');
  436. }
  437. if (!validator.isNumber(lightSettings.lightOnDurationMillis) || lightSettings.lightOnDurationMillis < 0) {
  438. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings.lightOnDurationMillis must be a non-negative duration in milliseconds');
  439. }
  440. const durationOn = (0, index_1.transformMillisecondsToSecondsString)(lightSettings.lightOnDurationMillis);
  441. lightSettings.lightOnDurationMillis = durationOn;
  442. if (!validator.isNumber(lightSettings.lightOffDurationMillis) || lightSettings.lightOffDurationMillis < 0) {
  443. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings.lightOffDurationMillis must be a non-negative duration in milliseconds');
  444. }
  445. const durationOff = (0, index_1.transformMillisecondsToSecondsString)(lightSettings.lightOffDurationMillis);
  446. lightSettings.lightOffDurationMillis = durationOff;
  447. if (!validator.isString(lightSettings.color) ||
  448. (!/^#[0-9a-fA-F]{6}$/.test(lightSettings.color) && !/^#[0-9a-fA-F]{8}$/.test(lightSettings.color))) {
  449. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings.color must be in the form #RRGGBB or #RRGGBBAA format');
  450. }
  451. const colorString = lightSettings.color.length === 7 ? lightSettings.color + 'FF' : lightSettings.color;
  452. const rgb = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/i.exec(colorString);
  453. if (!rgb || rgb.length < 4) {
  454. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INTERNAL_ERROR, 'regex to extract rgba values from ' + colorString + ' failed.');
  455. }
  456. const color = {
  457. red: parseInt(rgb[1], 16) / 255.0,
  458. green: parseInt(rgb[2], 16) / 255.0,
  459. blue: parseInt(rgb[3], 16) / 255.0,
  460. alpha: parseInt(rgb[4], 16) / 255.0,
  461. };
  462. lightSettings.color = color;
  463. const propertyMappings = {
  464. lightOnDurationMillis: 'light_on_duration',
  465. lightOffDurationMillis: 'light_off_duration',
  466. };
  467. (0, index_1.renameProperties)(lightSettings, propertyMappings);
  468. }
  469. /**
  470. * Checks if the given AndroidFcmOptions object is valid.
  471. *
  472. * @param {AndroidFcmOptions} fcmOptions An object to be validated.
  473. */
  474. function validateAndroidFcmOptions(fcmOptions) {
  475. if (typeof fcmOptions === 'undefined') {
  476. return;
  477. }
  478. else if (!validator.isNonNullObject(fcmOptions)) {
  479. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object');
  480. }
  481. if (typeof fcmOptions.analyticsLabel !== 'undefined' && !validator.isString(fcmOptions.analyticsLabel)) {
  482. throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value');
  483. }
  484. }