diff --git a/dist/constants.d.ts b/dist/constants.d.ts new file mode 100644 index 00000000..11347d25 --- /dev/null +++ b/dist/constants.d.ts @@ -0,0 +1,35 @@ +declare const Constants: { + GCM_SEND_ENDPOINT: string; + GCM_SEND_ENDPATH: string; + GCM_SEND_URI: string; + BACKOFF_INITIAL_DELAY: number; + MAX_BACKOFF_DELAY: number; + SOCKET_TIMEOUT: number; + /** DEPRECATED **/ + /** @deprecated */ + TOKEN_MESSAGE_ID: string; + TOKEN_CANONICAL_REG_ID: string; + TOKEN_ERROR: string; + JSON_REGISTRATION_IDS: string; + JSON_PAYLOAD: string; + JSON_NOTIFICATION: string; + JSON_SUCCESS: string; + JSON_FAILURE: string; + JSON_CANONICAL_IDS: string; + JSON_MULTICAST_ID: string; + JSON_RESULTS: string; + JSON_ERROR: string; + JSON_MESSAGE_ID: string; + UTF8: string; + ERROR_QUOTA_EXCEEDED: string; + ERROR_DEVICE_QUOTA_EXCEEDED: string; + ERROR_MISSING_REGISTRATION: string; + ERROR_INVALID_REGISTRATION: string; + ERROR_MISMATCH_SENDER_ID: string; + ERROR_NOT_REGISTERED: string; + ERROR_MESSAGE_TOO_BIG: string; + ERROR_MISSING_COLLAPSE_KEY: string; + ERROR_UNAVAILABLE: string; + ERROR_INTERNAL_SERVER_ERROR: string; +}; +export = Constants; diff --git a/dist/constants.js b/dist/constants.js new file mode 100644 index 00000000..4bda4cb0 --- /dev/null +++ b/dist/constants.js @@ -0,0 +1,36 @@ +"use strict"; +var Constants = { + GCM_SEND_ENDPOINT: 'fcm.googleapis.com', + GCM_SEND_ENDPATH: '/fcm/send', + GCM_SEND_URI: 'https://fcm.googleapis.com/fcm/send', + BACKOFF_INITIAL_DELAY: 1000, + MAX_BACKOFF_DELAY: 1024000, + SOCKET_TIMEOUT: 180000, + /** DEPRECATED **/ + /** @deprecated */ + TOKEN_MESSAGE_ID: 'id', + TOKEN_CANONICAL_REG_ID: 'registration_id', + TOKEN_ERROR: 'Error', + JSON_REGISTRATION_IDS: 'registration_ids', + JSON_PAYLOAD: 'data', + JSON_NOTIFICATION: 'notification', + JSON_SUCCESS: 'success', + JSON_FAILURE: 'failure', + JSON_CANONICAL_IDS: 'canonical_ids', + JSON_MULTICAST_ID: 'multicast_id', + JSON_RESULTS: 'results', + JSON_ERROR: 'error', + JSON_MESSAGE_ID: 'message_id', + UTF8: 'UTF-8', + ERROR_QUOTA_EXCEEDED: 'QuotaExceeded', + ERROR_DEVICE_QUOTA_EXCEEDED: 'DeviceQuotaExceeded', + ERROR_MISSING_REGISTRATION: 'MissingRegistration', + ERROR_INVALID_REGISTRATION: 'InvalidRegistration', + ERROR_MISMATCH_SENDER_ID: 'MismatchSenderId', + ERROR_NOT_REGISTERED: 'NotRegistered', + ERROR_MESSAGE_TOO_BIG: 'MessageTooBig', + ERROR_MISSING_COLLAPSE_KEY: 'MissingCollapseKey', + ERROR_UNAVAILABLE: 'Unavailable', + ERROR_INTERNAL_SERVER_ERROR: 'InternalServerError' +}; +module.exports = Constants; diff --git a/dist/main.d.ts b/dist/main.d.ts new file mode 100644 index 00000000..e144c26b --- /dev/null +++ b/dist/main.d.ts @@ -0,0 +1,10 @@ +/*! + * node-gcm + * Copyright(c) 2013 Marcus Farkas + * MIT Licensed + */ +export { default as Constants } from './constants'; +export { default as Message } from './message'; +export { default as Result } from './result'; +export { default as MulticastResult } from './multicastresult'; +export { default as Sender } from './sender'; diff --git a/dist/main.js b/dist/main.js new file mode 100644 index 00000000..2e92717f --- /dev/null +++ b/dist/main.js @@ -0,0 +1,25 @@ +"use strict"; +/*! + * node-gcm + * Copyright(c) 2013 Marcus Farkas + * MIT Licensed + */ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +exports.__esModule = true; +exports.Sender = exports.MulticastResult = exports.Result = exports.Message = exports.Constants = void 0; +var constants_1 = require("./constants"); +__createBinding(exports, constants_1, "default", "Constants"); +var message_1 = require("./message"); +__createBinding(exports, message_1, "default", "Message"); +var result_1 = require("./result"); +__createBinding(exports, result_1, "default", "Result"); +var multicastresult_1 = require("./multicastresult"); +__createBinding(exports, multicastresult_1, "default", "MulticastResult"); +var sender_1 = require("./sender"); +__createBinding(exports, sender_1, "default", "Sender"); diff --git a/dist/message-options.d.ts b/dist/message-options.d.ts new file mode 100644 index 00000000..33744804 --- /dev/null +++ b/dist/message-options.d.ts @@ -0,0 +1,56 @@ +/** + * This module defines all the arguments that may be passed to a message. + * + * Each argument may contain a field `__argName`, if the name of the field + * should be different when sent to the server. + * + * The argument may also contain a field `__argType`, if the given + * argument must be of that type. The types are the strings resulting from + * calling `typeof ` where `` is the argument. + * + * Other than that, the arguments are expected to follow the indicated + * structure. + */ +declare const MessageOptions: { + collapseKey: { + __argName: string; + __argType: string; + }; + priority: { + __argType: string; + }; + contentAvailable: { + __argName: string; + __argType: string; + }; + mutableContent: { + __argName: string; + __argType: string; + }; + delayWhileIdle: { + __argName: string; + __argType: string; + }; + timeToLive: { + __argName: string; + __argType: string; + }; + restrictedPackageName: { + __argName: string; + __argType: string; + }; + dryRun: { + __argName: string; + __argType: string; + }; + data: { + __argType: string; + }; + notification: { + __argType: string; + }; + fcm_options: { + __argType: string; + }; +}; +export = MessageOptions; diff --git a/lib/message-options.js b/dist/message-options.js similarity index 51% rename from lib/message-options.js rename to dist/message-options.js index 7a939804..e800c8ca 100644 --- a/lib/message-options.js +++ b/dist/message-options.js @@ -1,3 +1,4 @@ +"use strict"; /** * This module defines all the arguments that may be passed to a message. * @@ -11,48 +12,46 @@ * Other than that, the arguments are expected to follow the indicated * structure. */ - -module.exports = { +var MessageOptions = { collapseKey: { - __argName: "collapse_key", - __argType: "string" + __argName: 'collapse_key', + __argType: 'string' }, priority: { - __argType: "string" + __argType: 'string' }, contentAvailable: { - __argName: "content_available", - __argType: "boolean" + __argName: 'content_available', + __argType: 'boolean' }, mutableContent: { - __argName: "mutable_content", - __argType: "boolean" + __argName: 'mutable_content', + __argType: 'boolean' }, - delayWhileIdle: { - __argName: "delay_while_idle", - __argType: "boolean" + delayWhileIdle: { + __argName: 'delay_while_idle', + __argType: 'boolean' }, timeToLive: { - __argName: "time_to_live", - __argType: "number" + __argName: 'time_to_live', + __argType: 'number' }, restrictedPackageName: { - __argName: "restricted_package_name", - __argType: "string" + __argName: 'restricted_package_name', + __argType: 'string' }, dryRun: { - __argName: "dry_run", - __argType: "boolean" + __argName: 'dry_run', + __argType: 'boolean' }, data: { - __argType: "object" + __argType: 'object' }, notification: { - __argType: "object" - //TODO: There are a lot of very specific arguments that could - // be indicated here. + __argType: 'object' }, fcm_options: { - __argType: "object" + __argType: 'object' } }; +module.exports = MessageOptions; diff --git a/dist/message.d.ts b/dist/message.d.ts new file mode 100644 index 00000000..370dfb3e --- /dev/null +++ b/dist/message.d.ts @@ -0,0 +1,160 @@ +declare enum AndroidMessagePriority { + Normal = "NORMAL", + High = "HIGH" +} +interface MessageOptions { + data?: { + [key: string]: string; + }; + /** + * https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Notification + */ + notification?: { + title?: string; + body?: string; + /** URL of an image */ + image?: string; + }; + android?: { + collapse_key?: string; + priority?: AndroidMessagePriority; + ttl?: string; + restricted_package_name?: string; + /** + * If present, it will override [MessageOptions['data']] + */ + data?: { + [key: string]: string; + }; + notification?: AndroidNotification; + fcm_options?: { + analytics_label?: string; + }; + direct_boot_ok?: boolean; + }; + webpush?: { + headers?: Record; + data?: Record; + notification?: NotificationOptions; + fcm_options?: { + link?: string; + analytics_label?: string; + }; + }; + apns?: { + headers?: Record; + payload?: Record & ApnsPayload; + fcm_options?: { + analytics_label?: string; + image?: string; + }; + }; + fcm_options?: { + analytics_label?: string; + }; + token?: string; + topic?: string; + condition?: string; +} +interface AndroidNotification { + title?: string; + body?: string; + icon?: string; + color?: string; + sound?: string; + tag?: string; + click_action?: string; + body_loc_key?: string; + body_loc_args?: string[]; + title_loc_key?: string; + title_loc_args?: string[]; + channel_id?: string; + ticker?: string; + sticky?: boolean; + event_time?: string; + local_only?: boolean; + notification_priority?: NotificationPriority; + default_sound?: boolean; + default_vibrate_timings?: boolean; + default_light_settings?: boolean; + vibrate_timings?: string[]; + visibility?: Visibility; + notification_count?: number; + light_settings?: LightSettings; + image?: string; +} +declare enum NotificationPriority { + Unspecified = "PRIORITY_UNSPECIFIED", + Min = "PRIORITY_MIN", + Low = "PRIORITY_LOW", + Default = "PRIORITY_DEFAULT", + High = "PRIORITY_HIGH", + Max = "PRIORITY_MAX" +} +declare enum Visibility { + Unspecified = "VISIBILITY_UNSPECIFIED", + Private = "PRIVATE", + Public = "PUBLIC", + Secret = "SECRET" +} +interface LightSettings { + color: Color; + light_on_duration: string; + light_off_duration: string; +} +interface Color { + red: number; + green: number; + blue: number; + alpha: number; +} +interface ApnsPayload { + alert?: { + title?: string; + subtitle?: string; + body?: string; + 'launch-image'?: string; + 'title-loc-key'?: string; + 'title-loc-args'?: string[]; + 'subtitle-loc-key'?: string; + 'subtitle-loc-args'?: string[]; + 'loc-key'?: string; + 'loc-args'?: string[]; + }; + badge?: number; + sound?: string | { + critical?: 1; + name?: 'default' | string; + /** Must be a number between 0 and 1 */ + volume?: number; + }; + 'thread-id'?: string; + category?: string; + 'content-available'?: 1; + 'mutable-content'?: 1; + 'target-content-id'?: string; + 'interruption-level'?: 'passive' | 'active' | 'time-sensitive' | 'critical'; + /** Must be a number between 0 and 1 */ + 'relevance-score'?: number; +} +declare class Message { + options: MessageOptions; + constructor(options?: MessageOptions); + addNotification(object: MessageOptions['notification']): void; + addNotification(key: K, value: MessageOptions['notification'][K]): void; + addData(object: MessageOptions['data']): void; + addData(key: K, value: MessageOptions['data'][K]): void; + toJson(): Record; + /** + * @deprecated Please use Message#addData instead. + */ + addDataWithKeyValue(key: K, value: V): void; + /** + * @deprecated Please use Message#addData instead. + */ + addDataWithObject(obj: MessageOptions['data']): void; + private handleParamSet; + private addOption; + private setOption; +} +export = Message; diff --git a/dist/message.js b/dist/message.js new file mode 100644 index 00000000..4ec1e03b --- /dev/null +++ b/dist/message.js @@ -0,0 +1,88 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var message_options_1 = __importDefault(require("./message-options")); +var AndroidMessagePriority; +(function (AndroidMessagePriority) { + AndroidMessagePriority["Normal"] = "NORMAL"; + AndroidMessagePriority["High"] = "HIGH"; +})(AndroidMessagePriority || (AndroidMessagePriority = {})); +var NotificationPriority; +(function (NotificationPriority) { + NotificationPriority["Unspecified"] = "PRIORITY_UNSPECIFIED"; + NotificationPriority["Min"] = "PRIORITY_MIN"; + NotificationPriority["Low"] = "PRIORITY_LOW"; + NotificationPriority["Default"] = "PRIORITY_DEFAULT"; + NotificationPriority["High"] = "PRIORITY_HIGH"; + NotificationPriority["Max"] = "PRIORITY_MAX"; +})(NotificationPriority || (NotificationPriority = {})); +var Visibility; +(function (Visibility) { + Visibility["Unspecified"] = "VISIBILITY_UNSPECIFIED"; + Visibility["Private"] = "PRIVATE"; + Visibility["Public"] = "PUBLIC"; + Visibility["Secret"] = "SECRET"; +})(Visibility || (Visibility = {})); +var Message = /** @class */ (function () { + function Message(options) { + if (!(this instanceof Message)) { + return new Message(); + } + this.options = options || {}; + } + Message.prototype.addNotification = function (objectOrKey, value) { + return this.handleParamSet(value !== undefined ? [objectOrKey, value] : [objectOrKey], 'notification'); + }; + Message.prototype.addData = function (objectOrKey, value) { + return this.handleParamSet(value !== undefined ? [objectOrKey, value] : [objectOrKey], 'data'); + }; + Message.prototype.toJson = function () { + var json = {}; + for (var _i = 0, _a = Object.keys(this.options); _i < _a.length; _i++) { + var key = _a[_i]; + var optionDescription = message_options_1["default"][key]; + if (!optionDescription) { + continue; + } + var jsonKey = optionDescription.__argName || key; + json[jsonKey] = this.options[key]; + } + return json; + }; + /** + * @deprecated Please use Message#addData instead. + */ + Message.prototype.addDataWithKeyValue = function (key, value) { + console.warn('Message#addDataWithKeyValue has been deprecated. Please use Message#addData instead.'); + this.addData(key, value); + }; + /** + * @deprecated Please use Message#addData instead. + */ + Message.prototype.addDataWithObject = function (obj) { + console.warn('Message#addDataWithObject has been deprecated. Please use Message#addData instead.'); + this.addData(obj); + }; + Message.prototype.handleParamSet = function (args, paramType) { + if (args.length == 1) { + return this.setOption(paramType, args[0]); + } + else if (args.length == 2) { + return this.addOption(paramType, args[0], args[1]); + } + }; + Message.prototype.addOption = function (paramType, key, value) { + if (!this.options[paramType]) { + this.options[paramType] = {}; + } + return (this.options[paramType][key] = value); + }; + Message.prototype.setOption = function (paramType, obj) { + if (typeof obj === 'object' && Object.keys(obj).length > 0) { + this.options[paramType] = obj; + } + }; + return Message; +}()); +module.exports = Message; diff --git a/dist/multicastresult.d.ts b/dist/multicastresult.d.ts new file mode 100644 index 00000000..00f5a3bd --- /dev/null +++ b/dist/multicastresult.d.ts @@ -0,0 +1,15 @@ +/** + * @deprecated You are using node-gcm MulticastResult, which is deprecated. + */ +declare class MulticastResult { + success: any; + failure: any; + canonicalIds: any; + multicastId: any; + results: any[]; + retryMulticastIds: any[]; + constructor(); + addResult(result: any): void; + getTotal(): any; +} +export = MulticastResult; diff --git a/dist/multicastresult.js b/dist/multicastresult.js new file mode 100644 index 00000000..2dd8f62a --- /dev/null +++ b/dist/multicastresult.js @@ -0,0 +1,23 @@ +"use strict"; +/** + * @deprecated You are using node-gcm MulticastResult, which is deprecated. + */ +var MulticastResult = /** @class */ (function () { + function MulticastResult() { + this.success = undefined; + this.failure = undefined; + this.canonicalIds = undefined; + this.multicastId = undefined; + this.results = []; + this.retryMulticastIds = []; + console.warn('You are using node-gcm MulticastResult, which is deprecated.'); + } + MulticastResult.prototype.addResult = function (result) { + this.results.push(result); + }; + MulticastResult.prototype.getTotal = function () { + return this.success + this.failure; + }; + return MulticastResult; +}()); +module.exports = MulticastResult; diff --git a/dist/result.d.ts b/dist/result.d.ts new file mode 100644 index 00000000..a043183e --- /dev/null +++ b/dist/result.d.ts @@ -0,0 +1,5 @@ +/** + * @deprecated You are using node-gcm Result which is deprecated + */ +declare function Result(): void; +export = Result; diff --git a/lib/result.js b/dist/result.js similarity index 50% rename from lib/result.js rename to dist/result.js index e76fedf2..5e954c48 100644 --- a/lib/result.js +++ b/dist/result.js @@ -1,13 +1,11 @@ -/** DEPRECATED **/ - +"use strict"; +/** + * @deprecated You are using node-gcm Result which is deprecated + */ function Result() { this.messageId = undefined; this.canonicalRegistrationId = undefined; this.errorCode = undefined; - - console.warn("You are using node-gcm Result which is deprecated"); + console.warn('You are using node-gcm Result which is deprecated'); } - module.exports = Result; - -/** END DEPRECATED **/ diff --git a/dist/sender.d.ts b/dist/sender.d.ts new file mode 100644 index 00000000..3a0fa837 --- /dev/null +++ b/dist/sender.d.ts @@ -0,0 +1,15 @@ +import Message from './message'; +interface SenderOptions { +} +interface SendOptions { +} +declare type SendCallback = (err: unknown, response?: Record) => void; +declare class Sender { + key: string; + options: SenderOptions; + constructor(key: string, options?: SenderOptions); + send(message: Message, recipient: any, callback: SendCallback): any; + send(message: Message, recipient: any, options?: SendOptions, callback?: SendCallback): any; + sendNoRetry(message: Message, recipient: string | string[], callback?: (err: unknown, response?: Record, attemptedRegTokens?: string[]) => void): void; +} +export = Sender; diff --git a/dist/sender.js b/dist/sender.js new file mode 100644 index 00000000..6d09df35 --- /dev/null +++ b/dist/sender.js @@ -0,0 +1,251 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var request_1 = __importDefault(require("request")); +var lodash_defaultsdeep_1 = __importDefault(require("lodash.defaultsdeep")); +var constants_1 = __importDefault(require("./constants")); +var debug = require('debug')('node-gcm'); +var Sender = /** @class */ (function () { + function Sender(key, options) { + if (!(this instanceof Sender)) { + return new Sender(key, options); + } + this.key = key; + this.options = options || {}; + } + Sender.prototype.send = function (message, recipient, optionsOrCallback, callback) { + var _a, _b; + if (typeof optionsOrCallback == 'function') { + callback = optionsOrCallback; + optionsOrCallback = null; + } + else if (!callback) { + callback = function () { }; + } + var options = cleanOptions(optionsOrCallback); + if ((_b = (_a = message.options) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.from) { + console.warn("Sending a notification with the 'from' data attribute may invoke a 400 Bad Request by FCM."); + } + if (options.retries == 0) { + return this.sendNoRetry(message, recipient, callback); + } + var self = this; + this.sendNoRetry(message, recipient, function (err, response, attemptedRegTokens) { + if (err) { + // Attempt to determine HTTP status code + var statusCode = typeof err === 'number' ? err : err.code || 0; + // 4xx error? + if (statusCode > 399 && statusCode < 500) { + debug('Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)'); + return callback(err); + } + return retry(self, message, recipient, options, callback); + } + if (!response.results) { + return callback(null, response); + } + checkForBadTokens(response.results, attemptedRegTokens, function (err, unsentRegTokens, regTokenPositionMap) { + if (err) { + return callback(err); + } + if (unsentRegTokens.length == 0) { + return callback(null, response); + } + debug('Retrying ' + unsentRegTokens.length + ' unsent registration tokens'); + retry(self, message, unsentRegTokens, options, function (err, retriedResponse) { + if (err) { + return callback(null, response); + } + response = updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens); + callback(null, response); + }); + }); + }); + }; + Sender.prototype.sendNoRetry = function (message, recipient, callback) { + var _this = this; + if (!callback) { + callback = function () { }; + } + getRequestBody(message, recipient, function (err, body) { + if (err) { + return callback(err); + } + //Build request options, allowing some to be overridden + var request_options = (0, lodash_defaultsdeep_1["default"])({ + method: 'POST', + headers: { + Authorization: 'key=' + _this.key + }, + json: body + }, _this.options, { + uri: constants_1["default"].GCM_SEND_URI, + timeout: constants_1["default"].SOCKET_TIMEOUT + }); + (0, request_1["default"])(request_options, function (err, res, resBodyJSON) { + if (err) { + return callback(err); + } + if (res.statusCode >= 500) { + debug('GCM service is unavailable (500)'); + return callback(res.statusCode); + } + if (res.statusCode === 401) { + debug('Unauthorized (401). Check that your API token is correct.'); + return callback(res.statusCode); + } + if (res.statusCode !== 200) { + debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON); + return callback(res.statusCode); + } + if (!resBodyJSON) { + debug('Empty response received (' + res.statusCode + ' ' + res.statusMessage + ')'); + // Spoof error code 400 to avoid retrying the request + return callback({ error: res.statusMessage, code: 400 }); + } + callback(null, resBodyJSON, body.registration_ids || [body.to]); + }); + }); + }; + return Sender; +}()); +function cleanOptions(options) { + if (!options || typeof options != 'object') { + var retries = 5; + if (typeof options == 'number') { + retries = options; + } + return { + retries: retries, + backoff: constants_1["default"].BACKOFF_INITIAL_DELAY + }; + } + if (typeof options.retries != 'number') { + options.retries = 5; + } + if (typeof options.backoff != 'number') { + options.backoff = constants_1["default"].BACKOFF_INITIAL_DELAY; + } + if (options.backoff > constants_1["default"].MAX_BACKOFF_DELAY) { + options.backoff = constants_1["default"].MAX_BACKOFF_DELAY; + } + return options; +} +function retry(self, message, recipient, options, callback) { + return setTimeout(function () { + self.send(message, recipient, { + retries: options.retries - 1, + backoff: options.backoff * 2 + }, callback); + }, options.backoff); +} +function checkForBadTokens(results, originalRecipients, callback) { + var unsentRegTokens = []; + var regTokenPositionMap = []; + for (var i = 0; i < results.length; i++) { + if (results[i].error === 'Unavailable' || results[i].error === 'InternalServerError') { + regTokenPositionMap.push(i); + unsentRegTokens.push(originalRecipients[i]); + } + } + nextTick(callback, null, unsentRegTokens, regTokenPositionMap); +} +function updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens) { + updateResults(response.results, retriedResponse.results, regTokenPositionMap); + updateResponseMetaData(response, retriedResponse, unsentRegTokens); + return response; +} +function updateResults(results, retriedResults, regTokenPositionMap) { + for (var i = 0; i < results.length; i++) { + results[regTokenPositionMap[i]] = retriedResults[i]; + } +} +function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { + response.success += retriedResponse.success; + response.canonical_ids += retriedResponse.canonical_ids; + response.failure -= unsentRegTokens.length - retriedResponse.failure; +} +function getRequestBody(message, recipientOrCallback, callback) { + var body = message.toJson(); + if (typeof recipientOrCallback == 'string') { + body.to = recipientOrCallback; + return nextTick(callback, null, body); + } + if (Array.isArray(recipientOrCallback)) { + if (!recipientOrCallback.length) { + return nextTick(callback, 'No recipient provided!'); + } + else if (recipientOrCallback.length == 1) { + body.to = recipientOrCallback[0]; + return nextTick(callback, null, body); + } + body.registration_ids = recipientOrCallback; + return nextTick(callback, null, body); + } + if (typeof recipientOrCallback == 'object') { + return extractRecipient(recipientOrCallback, function (err, recipient, param) { + if (err) { + return callback(err); + } + body[param] = recipient; + return callback(null, body); + }); + } + return nextTick(callback, 'Invalid recipient (' + recipientOrCallback + ', type ' + typeof recipientOrCallback + ') provided!'); +} +function nextTick(func) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + process.nextTick(function () { + func.apply(this, args); + }.bind(this)); +} +function extractRecipient(recipient, callback) { + var recipientKeys = Object.keys(recipient); + if (recipientKeys.length !== 1) { + return nextTick(callback, new Error('Please specify exactly one recipient key (you specified [' + recipientKeys + '])')); + } + var key = recipientKeys[0]; + var value = recipient[key]; + if (!value) { + return nextTick(callback, new Error("Falsy value for recipient key '" + key + "'.")); + } + var keyValidators = { + to: isString, + topic: isString, + condition: isString, + notificationKey: isString, + registrationIds: isArray, + registrationTokens: isArray + }; + var validator = keyValidators[key]; + if (!validator) { + return nextTick(callback, new Error("Key '" + key + "' is not a valid recipient key.")); + } + if (!validator(value)) { + return nextTick(callback, new Error("Recipient key '" + key + "' was provided as an incorrect type.")); + } + var param = getParamFromKey(key); + return nextTick(callback, null, value, param); +} +function getParamFromKey(key) { + if (key === 'condition') { + return 'condition'; + } + else if (['registrationIds', 'registrationTokens'].indexOf(key) !== -1) { + return 'registration_ids'; + } + else { + return 'to'; + } +} +function isString(x) { + return typeof x == 'string'; +} +function isArray(x) { + return Array.isArray(x); +} +module.exports = Sender; diff --git a/examples/notification.js b/examples/notification.js index 11ec000e..a5516d8e 100644 --- a/examples/notification.js +++ b/examples/notification.js @@ -1,4 +1,4 @@ -var gcm = require('../lib/node-gcm'); +var gcm = require('../dist/main.js'); var message = new gcm.Message(); diff --git a/index.js b/index.js deleted file mode 100644 index 4c426b21..00000000 --- a/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * node-gcm - * Copyright(c) 2013 Marcus Farkas - * MIT Licensed - */ - -module.exports = require('./lib/node-gcm'); diff --git a/lib/constants.js b/lib/constants.js deleted file mode 100644 index bef8d90a..00000000 --- a/lib/constants.js +++ /dev/null @@ -1,42 +0,0 @@ -var Constants = { - 'GCM_SEND_ENDPOINT' : 'fcm.googleapis.com', - 'GCM_SEND_ENDPATH' : '/fcm/send', - 'GCM_SEND_URI' : 'https://fcm.googleapis.com/fcm/send', - 'BACKOFF_INITIAL_DELAY' : 1000, - 'MAX_BACKOFF_DELAY' : 1024000 , - 'SOCKET_TIMEOUT' : 180000, //three minutes - - /** DEPRECATED **/ - - 'TOKEN_MESSAGE_ID' : 'id', - 'TOKEN_CANONICAL_REG_ID' : 'registration_id', - 'TOKEN_ERROR' : 'Error', - 'JSON_REGISTRATION_IDS' : 'registration_ids', - 'JSON_PAYLOAD' : 'data', - 'JSON_NOTIFICATION' : 'notification', - 'JSON_SUCCESS' : 'success', - 'JSON_FAILURE' : 'failure', - 'JSON_CANONICAL_IDS' : 'canonical_ids', - 'JSON_MULTICAST_ID' : 'multicast_id', - 'JSON_RESULTS' : 'results', - 'JSON_ERROR' : 'error', - 'JSON_MESSAGE_ID' : 'message_id', - 'UTF8' : 'UTF-8', - - //These errors could probably be structured more nicely, and could be used in the code. - // -- maybe just as an Error abstraction? - 'ERROR_QUOTA_EXCEEDED' : 'QuotaExceeded', - 'ERROR_DEVICE_QUOTA_EXCEEDED' : 'DeviceQuotaExceeded', - 'ERROR_MISSING_REGISTRATION' : 'MissingRegistration', - 'ERROR_INVALID_REGISTRATION' : 'InvalidRegistration', - 'ERROR_MISMATCH_SENDER_ID' : 'MismatchSenderId', - 'ERROR_NOT_REGISTERED' : 'NotRegistered', - 'ERROR_MESSAGE_TOO_BIG' : 'MessageTooBig', - 'ERROR_MISSING_COLLAPSE_KEY' : 'MissingCollapseKey', - 'ERROR_UNAVAILABLE' : 'Unavailable', - 'ERROR_INTERNAL_SERVER_ERROR' : 'InternalServerError' - - /** END DEPRECATED **/ -}; - -module.exports = Constants; diff --git a/lib/message.js b/lib/message.js deleted file mode 100644 index 2834f0cf..00000000 --- a/lib/message.js +++ /dev/null @@ -1,80 +0,0 @@ -var messageOptions = require("./message-options"); - -function Message(raw) { - if (!(this instanceof Message)) { - return new Message(raw); - } - this.params = cleanParams(raw || {}); -} - -function cleanParams(raw) { - var params = {}; - Object.keys(raw).forEach(function(param) { - if(messageOptions[param]) { - params[param] = raw[param]; - } - }); - return params; -} - -Message.prototype.addNotification = function() { - return handleParamSet.call(this, arguments, "notification"); -}; - -function handleParamSet(args, paramType) { - if(args.length == 1) { - return setParam.call(this, paramType, args[0]); - } - if(args.length == 2) { - return addParam.call(this, paramType, args[0], args[1]); - } - throw new Error("Invalid number of arguments given to for setting " + paramType + " (" + args.length + ")"); -} - -function setParam(paramType, obj) { - if (typeof obj === 'object' && Object.keys(obj).length > 0) { - this.params[paramType] = obj; - } -} - -function addParam(paramType, key, value) { - if(!this.params[paramType]) { - this.params[paramType] = {}; - } - return this.params[paramType][key] = value; -} - -Message.prototype.addData = function() { - return handleParamSet.call(this, arguments, "data"); -}; - -Message.prototype.toJson = function() { - var json = {}; - - Object.keys(this.params).forEach(function(param) { - var optionDescription = messageOptions[param]; - if(!optionDescription) { - return; - } - var key = optionDescription.__argName || param; - json[key] = this.params[param]; - }.bind(this)); - - return json; -}; - -/** DEPRECATED */ - -Message.prototype.addDataWithKeyValue = function (key, value) { - console.warn("Message#addDataWithKeyValue has been deprecated. Please use Message#addData instead."); - this.addData(key, value); -}; - -Message.prototype.addDataWithObject = function (obj) { - console.warn("Message#addDataWithObject has been deprecated. Please use Message#addData instead."); - this.addData(obj); -}; - -/** END DEPRECATED */ - -module.exports = Message; diff --git a/lib/multicastresult.js b/lib/multicastresult.js deleted file mode 100644 index 8390b1cb..00000000 --- a/lib/multicastresult.js +++ /dev/null @@ -1,28 +0,0 @@ -/** DEPRECATED **/ - -function MulitcastResult() { - if (!(this instanceof MulitcastResult)) { - return new MulitcastResult(); - } - - console.warn("You are using node-gcm MulticastResult, which is deprecated."); - - this.success = undefined; - this.failure = undefined; - this.canonicalIds = undefined; - this.multicastId = undefined; - this.results = []; - this.retryMulticastIds = []; -} - -MulitcastResult.prototype.addResult = function (result) { - this.results.push(result); -}; - -MulitcastResult.prototype.getTotal = function () { - return this.success + this.failure; -}; - -module.exports = MulitcastResult; - -/** END DEPRECATED **/ diff --git a/lib/node-gcm.js b/lib/node-gcm.js deleted file mode 100644 index 107f64ec..00000000 --- a/lib/node-gcm.js +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * node-gcm - * Copyright(c) 2013 Marcus Farkas - * MIT Licensed - */ - -exports.Constants = require('./constants'); -exports.Message = require('./message'); -exports.Result = require('./result'); -exports.MulitcastResult = require('./multicastresult'); -exports.Sender = require('./sender'); diff --git a/lib/sender.js b/lib/sender.js deleted file mode 100644 index 15a8447a..00000000 --- a/lib/sender.js +++ /dev/null @@ -1,276 +0,0 @@ -var Constants = require('./constants'); -var _ = require('lodash'); -var request = require('request'); -var debug = require('debug')('node-gcm'); - -function Sender(key, options) { - if (!(this instanceof Sender)) { - return new Sender(key, options); - } - - this.key = key; - this.options = options || {}; -} - -Sender.prototype.send = function(message, recipient, options, callback) { - if(typeof options == "function") { - callback = options; - options = null; - } - else if(!callback) { - callback = function() {}; - } - options = cleanOptions(options); - - if(message.params && message.params.data && message.params.data.from) { - console.warn("Sending a notification with the 'from' data attribute may invoke a 400 Bad Request by FCM."); - } - - if(options.retries == 0) { - return this.sendNoRetry(message, recipient, callback); - } - - var self = this; - - this.sendNoRetry(message, recipient, function(err, response, attemptedRegTokens) { - if (err) { - // Attempt to determine HTTP status code - var statusCode = typeof err === 'number' ? err : (err.code || 0); - - // 4xx error? - if (statusCode > 399 && statusCode < 500) { - debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); - return callback(err); - } - return retry(self, message, recipient, options, callback); - } - if(!response.results) { - return callback(null, response); - } - checkForBadTokens(response.results, attemptedRegTokens, function(err, unsentRegTokens, regTokenPositionMap) { - if(err) { - return callback(err); - } - if (unsentRegTokens.length == 0) { - return callback(null, response); - } - - debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); - - retry(self, message, unsentRegTokens, options, function(err, retriedResponse) { - if(err) { - return callback(null, response); - } - response = updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens); - callback(null, response); - }); - }); - }); -}; - -function cleanOptions(options) { - if(!options || typeof options != "object") { - var retries = 5; - if(typeof options == "number") { - retries = options; - } - return { - retries: retries, - backoff: Constants.BACKOFF_INITIAL_DELAY - }; - } - - if(typeof options.retries != "number") { - options.retries = 5; - } - if(typeof options.backoff != "number") { - options.backoff = Constants.BACKOFF_INITIAL_DELAY; - } - if (options.backoff > Constants.MAX_BACKOFF_DELAY) { - options.backoff = Constants.MAX_BACKOFF_DELAY; - } - - return options; -} - -function retry(self, message, recipient, options, callback) { - return setTimeout(function() { - self.send(message, recipient, { - retries: options.retries - 1, - backoff: options.backoff * 2 - }, callback); - }, options.backoff); -} - -function checkForBadTokens(results, originalRecipients, callback) { - var unsentRegTokens = []; - var regTokenPositionMap = []; - for (var i = 0; i < results.length; i++) { - if (results[i].error === 'Unavailable' || results[i].error === 'InternalServerError') { - regTokenPositionMap.push(i); - unsentRegTokens.push(originalRecipients[i]); - } - } - nextTick(callback, null, unsentRegTokens, regTokenPositionMap); -} - -function updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens) { - updateResults(response.results, retriedResponse.results, regTokenPositionMap); - updateResponseMetaData(response, retriedResponse, unsentRegTokens); - return response; -} - -function updateResults(results, retriedResults, regTokenPositionMap) { - for(var i = 0; i < results.length; i++) { - results[regTokenPositionMap[i]] = retriedResults[i]; - } -} - -function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { - response.success += retriedResponse.success; - response.canonical_ids += retriedResponse.canonical_ids; - response.failure -= unsentRegTokens.length - retriedResponse.failure; -} - -Sender.prototype.sendNoRetry = function(message, recipient, callback) { - if(!callback) { - callback = function() {}; - } - - getRequestBody(message, recipient, function(err, body) { - if(err) { - return callback(err); - } - - //Build request options, allowing some to be overridden - var request_options = _.defaultsDeep({ - method: 'POST', - headers: { - 'Authorization': 'key=' + this.key - }, - json: body - }, this.options, { - uri: Constants.GCM_SEND_URI, - timeout: Constants.SOCKET_TIMEOUT - }); - - request(request_options, function (err, res, resBodyJSON) { - if (err) { - return callback(err); - } - if (res.statusCode >= 500) { - debug('GCM service is unavailable (500)'); - return callback(res.statusCode); - } - if (res.statusCode === 401) { - debug('Unauthorized (401). Check that your API token is correct.'); - return callback(res.statusCode); - } - if (res.statusCode !== 200) { - debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON); - return callback(res.statusCode); - } - if (!resBodyJSON) { - debug('Empty response received (' + res.statusCode + ' ' + res.statusMessage + ')'); - // Spoof error code 400 to avoid retrying the request - return callback({error: res.statusMessage, code: 400}); - } - callback(null, resBodyJSON, body.registration_ids || [ body.to ]); - }); - }.bind(this)); -}; - -function getRequestBody(message, recipient, callback) { - var body = message.toJson(); - - if(typeof recipient == "string") { - body.to = recipient; - return nextTick(callback, null, body); - } - if(Array.isArray(recipient)) { - if(!recipient.length) { - return nextTick(callback, 'No recipient provided!'); - } - else if(recipient.length == 1) { - body.to = recipient[0]; - return nextTick(callback, null, body); - } - body.registration_ids = recipient; - return nextTick(callback, null, body); - } - if (typeof recipient == "object") { - return extractRecipient(recipient, function(err, recipient, param) { - if(err) { - return callback(err); - } - body[param] = recipient; - return callback(null, body); - }); - } - return nextTick(callback, 'Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided!'); -} - -function nextTick(func) { - var args = Array.prototype.slice.call(arguments, 1); - process.nextTick(function() { - func.apply(this, args); - }.bind(this)); -} - -function extractRecipient(recipient, callback) { - var recipientKeys = Object.keys(recipient); - - if(recipientKeys.length !== 1) { - return nextTick(callback, new Error("Please specify exactly one recipient key (you specified [" + recipientKeys + "])")); - } - - var key = recipientKeys[0]; - var value = recipient[key]; - - if(!value) { - return nextTick(callback, new Error("Falsy value for recipient key '" + key + "'.")); - } - - var keyValidators = { - to: isString, - topic: isString, - condition: isString, - notificationKey: isString, - registrationIds: isArray, - registrationTokens: isArray - }; - - var validator = keyValidators[key]; - if(!validator) { - return nextTick(callback, new Error("Key '" + key + "' is not a valid recipient key.")); - } - if(!validator(value)) { - return nextTick(callback, new Error("Recipient key '" + key + "' was provided as an incorrect type.")); - } - - var param = getParamFromKey(key); - - return nextTick(callback, null, value, param); -} - -function getParamFromKey(key) { - if (key === 'condition') { - return 'condition'; - } - else if (['registrationIds', 'registrationTokens'].indexOf(key) !== -1) { - return 'registration_ids'; - } - else { - return 'to'; - } -} - -function isString(x) { - return typeof x == "string"; -} - -function isArray(x) { - return Array.isArray(x); -} - -module.exports = Sender; diff --git a/package-lock.json b/package-lock.json index 0726d7c5..8f4eb482 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ }, "@sinonjs/formatio": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "dev": true, "requires": { @@ -42,9 +42,67 @@ } }, "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.184", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.184.tgz", + "integrity": "sha512-RoZphVtHbxPZizt4IcILciSWiC6dcn+eZ8oX9IWEYfDMcocdd42f7NPI6fQj+6zI8y4E0L7gu2pcZKLGTRaV9Q==", + "dev": true + }, + "@types/lodash.defaultsdeep": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.7.tgz", + "integrity": "sha512-D+AUxs64qehDMkbfFoskG0XsIOh2CHBGqYfcQcubLbZSFCGKJKS885su3a97huqBNHj+p9of9UZ/uUIP46wUGQ==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/node": { + "version": "18.7.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz", + "integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==", + "dev": true + }, + "@types/request": { + "version": "2.48.8", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", + "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, "@ungap/promise-all-settled": { @@ -71,9 +129,9 @@ "dev": true }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -104,13 +162,13 @@ "array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", "dev": true }, "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "requires": { "safer-buffer": "~2.1.0" } @@ -118,7 +176,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" }, "assertion-error": { "version": "1.0.0", @@ -134,7 +192,7 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" }, "aws4": { "version": "1.11.0", @@ -150,7 +208,7 @@ "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "requires": { "tweetnacl": "^0.14.3" } @@ -187,15 +245,15 @@ "dev": true }, "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, "chai": { "version": "2.3.0", @@ -229,9 +287,9 @@ } }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -253,40 +311,6 @@ "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } } }, "color-convert": { @@ -315,28 +339,28 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "requires": { "assert-plus": "^1.0.0" } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -360,15 +384,15 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -400,7 +424,7 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" }, "fast-deep-equal": { "version": "3.1.3", @@ -415,7 +439,7 @@ "fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", + "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==", "dev": true, "requires": { "is-object": "~1.0.1", @@ -450,7 +474,7 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" }, "form-data": { "version": "2.3.3", @@ -465,7 +489,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { @@ -475,6 +499,12 @@ "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -484,15 +514,15 @@ "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "requires": { "assert-plus": "^1.0.0" } }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -501,6 +531,17 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "glob-parent": { @@ -521,7 +562,7 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" }, "har-validator": { "version": "5.1.5", @@ -532,6 +573,15 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -547,7 +597,7 @@ "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -557,7 +607,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -579,22 +629,31 @@ "binary-extensions": "^2.0.0" } }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -607,9 +666,9 @@ "dev": true }, "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", "dev": true }, "is-plain-obj": { @@ -621,7 +680,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "is-unicode-supported": { "version": "0.1.0", @@ -632,19 +691,19 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "js-yaml": { "version": "4.1.0", @@ -658,12 +717,12 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "json-schema-traverse": { "version": "0.4.1", @@ -673,19 +732,25 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" } }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -698,12 +763,18 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==" }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "log-symbols": { @@ -717,15 +788,15 @@ } }, "lolex": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.4.2.tgz", - "integrity": "sha512-92IC6/K6abCQqmaBhFt4OYLSkpOkqUULeazKiEna96om4o87vLGmwTmrsZ5mK+4F6kZ8dS1w8VnFbdhFJF8sJw==", + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", "dev": true }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, "mime-db": { @@ -742,51 +813,50 @@ } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-Kjg/XxYOFFUi0h/FwMOeb6RoroiZ+P1yOfya6NK7h3dNhahrJx1r2XIT3ge4ZQvJM86mdjNA+W5phqRQh7DwCg==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.23", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -799,36 +869,24 @@ "dev": true } } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true } } }, "module-not-found-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", + "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==", "dev": true }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "nise": { @@ -854,12 +912,6 @@ "@sinonjs/samsam": "^3.1.0" } }, - "just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true - }, "lolex": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", @@ -885,7 +937,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -918,7 +970,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-parse": { @@ -928,9 +980,9 @@ "dev": true }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { "isarray": "0.0.1" @@ -939,29 +991,29 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "proxyquire": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.0.1.tgz", - "integrity": "sha512-fQr3VQrbdzHrdaDn3XuisVoJlJNDJizHAvUXw9IuXRR8BpV2x0N7LsCxrpJkeKfPbNjiNU/V5vc008cI0TmzzQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", + "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", "dev": true, "requires": { "fill-keys": "^1.0.2", - "module-not-found-error": "^1.0.0", - "resolve": "~1.5.0" + "module-not-found-error": "^1.0.1", + "resolve": "^1.11.1" } }, "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "punycode": { "version": "2.1.1", @@ -969,9 +1021,9 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" }, "randombytes": { "version": "2.1.0", @@ -992,9 +1044,9 @@ } }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -1003,7 +1055,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -1013,7 +1065,7 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" } @@ -1021,16 +1073,18 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "safe-buffer": { @@ -1059,30 +1113,36 @@ } }, "sinon": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-5.0.7.tgz", - "integrity": "sha512-GvNLrwpvLZ8jIMZBUhHGUZDq5wlUdceJWyHvZDmqBxnjazpxY1L0FNbGBX6VpcOEoQ8Q4XMWFzm2myJMvx+VjA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-5.1.1.tgz", + "integrity": "sha512-h/3uHscbt5pQNxkf7Y/Lb9/OM44YNCicHakcq73ncbrIS8lXg+ZGOZbtuU+/km4YnyiCYfQQEwANaReJz7KDfw==", "dev": true, "requires": { "@sinonjs/formatio": "^2.0.0", - "diff": "^3.1.0", + "diff": "^3.5.0", "lodash.get": "^4.4.2", - "lolex": "^2.2.0", - "nise": "^1.2.0", - "supports-color": "^5.1.0", - "type-detect": "^4.0.5" + "lolex": "^2.4.2", + "nise": "^1.3.3", + "supports-color": "^5.4.0", + "type-detect": "^4.0.8" }, "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -1097,9 +1157,9 @@ } }, "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -1113,22 +1173,23 @@ } }, "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" } }, "strip-json-comments": { @@ -1146,6 +1207,12 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1156,25 +1223,18 @@ } }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } + "psl": "^1.1.28", + "punycode": "^2.1.1" } }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "requires": { "safe-buffer": "^5.0.1" } @@ -1182,7 +1242,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "type-detect": { "version": "0.1.1", @@ -1190,6 +1250,12 @@ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", "dev": true }, + "typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1206,7 +1272,7 @@ "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -1222,19 +1288,10 @@ "isexe": "^2.0.0" } }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { @@ -1246,46 +1303,12 @@ "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "y18n": { @@ -1307,40 +1330,6 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } } }, "yargs-parser": { diff --git a/package.json b/package.json index 36fca072..ce7ddab1 100644 --- a/package.json +++ b/package.json @@ -60,26 +60,29 @@ "apn", "messaging" ], - "main": "index", + "main": "dist/main.js", + "types": "dist/main.d.ts", "files": [ - "index.js", - "lib/" + "dist" ], "scripts": { + "build": "tsc", "test": "mocha test/**/*Spec.js" }, "dependencies": { - "debug": "^3.1.0", - "lodash": "^4.17.21", - "request": "^2.88.0" + "debug": "^3.2.7", + "lodash.defaultsdeep": "^4.6.1", + "request": "^2.88.2" }, "devDependencies": { + "@types/lodash.defaultsdeep": "^4.6.6", + "@types/request": "^2.48.8", "chai": "^2.2.0", - "mocha": "^9.1.0", - "proxyquire": "^2.0.1", - "sinon": "^5.0.7" + "mocha": "^9.2.2", + "proxyquire": "^2.1.3", + "sinon": "^5.1.1", + "typescript": "^4.5.4" }, - "optionalDependencies": {}, "engines": { "node": ">= 4" }, diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 00000000..9c486723 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,43 @@ +const Constants = { + GCM_SEND_ENDPOINT: 'fcm.googleapis.com', + GCM_SEND_ENDPATH: '/fcm/send', + GCM_SEND_URI: 'https://fcm.googleapis.com/fcm/send', + BACKOFF_INITIAL_DELAY: 1000, + MAX_BACKOFF_DELAY: 1024000, + SOCKET_TIMEOUT: 180000, //three minutes + + /** DEPRECATED **/ + + /** @deprecated */ + TOKEN_MESSAGE_ID: 'id', + TOKEN_CANONICAL_REG_ID: 'registration_id', + TOKEN_ERROR: 'Error', + JSON_REGISTRATION_IDS: 'registration_ids', + JSON_PAYLOAD: 'data', + JSON_NOTIFICATION: 'notification', + JSON_SUCCESS: 'success', + JSON_FAILURE: 'failure', + JSON_CANONICAL_IDS: 'canonical_ids', + JSON_MULTICAST_ID: 'multicast_id', + JSON_RESULTS: 'results', + JSON_ERROR: 'error', + JSON_MESSAGE_ID: 'message_id', + UTF8: 'UTF-8', + + //These errors could probably be structured more nicely, and could be used in the code. + // -- maybe just as an Error abstraction? + ERROR_QUOTA_EXCEEDED: 'QuotaExceeded', + ERROR_DEVICE_QUOTA_EXCEEDED: 'DeviceQuotaExceeded', + ERROR_MISSING_REGISTRATION: 'MissingRegistration', + ERROR_INVALID_REGISTRATION: 'InvalidRegistration', + ERROR_MISMATCH_SENDER_ID: 'MismatchSenderId', + ERROR_NOT_REGISTERED: 'NotRegistered', + ERROR_MESSAGE_TOO_BIG: 'MessageTooBig', + ERROR_MISSING_COLLAPSE_KEY: 'MissingCollapseKey', + ERROR_UNAVAILABLE: 'Unavailable', + ERROR_INTERNAL_SERVER_ERROR: 'InternalServerError', + + /** END DEPRECATED **/ +} + +export = Constants diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 00000000..1ca5cd20 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,11 @@ +/*! + * node-gcm + * Copyright(c) 2013 Marcus Farkas + * MIT Licensed + */ + +export { default as Constants } from './constants' +export { default as Message } from './message' +export { default as Result } from './result' +export { default as MulticastResult } from './multicastresult' +export { default as Sender } from './sender' diff --git a/src/message-options.ts b/src/message-options.ts new file mode 100644 index 00000000..927c4ba5 --- /dev/null +++ b/src/message-options.ts @@ -0,0 +1,60 @@ +/** + * This module defines all the arguments that may be passed to a message. + * + * Each argument may contain a field `__argName`, if the name of the field + * should be different when sent to the server. + * + * The argument may also contain a field `__argType`, if the given + * argument must be of that type. The types are the strings resulting from + * calling `typeof ` where `` is the argument. + * + * Other than that, the arguments are expected to follow the indicated + * structure. + */ + +const MessageOptions = { + collapseKey: { + __argName: 'collapse_key', + __argType: 'string', + }, + priority: { + __argType: 'string', + }, + contentAvailable: { + __argName: 'content_available', + __argType: 'boolean', + }, + mutableContent: { + __argName: 'mutable_content', + __argType: 'boolean', + }, + delayWhileIdle: { + __argName: 'delay_while_idle', + __argType: 'boolean', + }, + timeToLive: { + __argName: 'time_to_live', + __argType: 'number', + }, + restrictedPackageName: { + __argName: 'restricted_package_name', + __argType: 'string', + }, + dryRun: { + __argName: 'dry_run', + __argType: 'boolean', + }, + data: { + __argType: 'object', + }, + notification: { + __argType: 'object', + //TODO: There are a lot of very specific arguments that could + // be indicated here. + }, + fcm_options: { + __argType: 'object', + }, +} + +export = MessageOptions diff --git a/src/message.ts b/src/message.ts new file mode 100644 index 00000000..f37c194f --- /dev/null +++ b/src/message.ts @@ -0,0 +1,250 @@ +import messageOptions from './message-options' + +enum AndroidMessagePriority { + Normal = 'NORMAL', + High = 'HIGH', +} + +interface MessageOptions { + data?: { [key: string]: string } + /** + * https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Notification + */ + notification?: { + title?: string + body?: string + /** URL of an image */ + image?: string + } + android?: { + collapse_key?: string + priority?: AndroidMessagePriority + ttl?: string + restricted_package_name?: string + /** + * If present, it will override [MessageOptions['data']] + */ + data?: { [key: string]: string } + notification?: AndroidNotification + fcm_options?: { + analytics_label?: string + } + direct_boot_ok?: boolean + } + webpush?: { + headers?: Record + data?: Record + notification?: NotificationOptions + fcm_options?: { + link?: string + analytics_label?: string + } + } + apns?: { + headers?: Record + payload?: Record & ApnsPayload + fcm_options?: { + analytics_label?: string + image?: string + } + } + fcm_options?: { + analytics_label?: string + } + token?: string + topic?: string + condition?: string +} + +interface AndroidNotification { + title?: string + body?: string + icon?: string + color?: string + sound?: string + tag?: string + click_action?: string + body_loc_key?: string + body_loc_args?: string[] + title_loc_key?: string + title_loc_args?: string[] + channel_id?: string + ticker?: string + sticky?: boolean + event_time?: string + local_only?: boolean + notification_priority?: NotificationPriority + default_sound?: boolean + default_vibrate_timings?: boolean + default_light_settings?: boolean + vibrate_timings?: string[] + visibility?: Visibility + notification_count?: number + light_settings?: LightSettings + image?: string +} + +enum NotificationPriority { + Unspecified = 'PRIORITY_UNSPECIFIED', + Min = 'PRIORITY_MIN', + Low = 'PRIORITY_LOW', + Default = 'PRIORITY_DEFAULT', + High = 'PRIORITY_HIGH', + Max = 'PRIORITY_MAX', +} + +enum Visibility { + Unspecified = 'VISIBILITY_UNSPECIFIED', + Private = 'PRIVATE', + Public = 'PUBLIC', + Secret = 'SECRET', +} + +interface LightSettings { + color: Color + light_on_duration: string + light_off_duration: string +} + +interface Color { + red: number + green: number + blue: number + alpha: number +} + +interface ApnsPayload { + alert?: { + title?: string + subtitle?: string + body?: string + 'launch-image'?: string + 'title-loc-key'?: string + 'title-loc-args'?: string[] + 'subtitle-loc-key'?: string + 'subtitle-loc-args'?: string[] + 'loc-key'?: string + 'loc-args'?: string[] + } + badge?: number + sound?: + | string + | { + critical?: 1 + name?: 'default' | string + /** Must be a number between 0 and 1 */ + volume?: number + } + 'thread-id'?: string + category?: string + 'content-available'?: 1 + 'mutable-content'?: 1 + 'target-content-id'?: string + 'interruption-level'?: 'passive' | 'active' | 'time-sensitive' | 'critical' + /** Must be a number between 0 and 1 */ + 'relevance-score'?: number +} + +type ObjectMessageOptions = Omit + +class Message { + options: MessageOptions + + constructor(options?: MessageOptions) { + if (!(this instanceof Message)) { + return new Message() + } + + this.options = options || {} + } + + addNotification(object: MessageOptions['notification']): void + addNotification( + key: K, + value: MessageOptions['notification'][K] + ): void + addNotification( + objectOrKey: MessageOptions['notification'] | K, + value?: MessageOptions['notification'][K] + ) { + return this.handleParamSet( + value !== undefined ? [objectOrKey as K, value] : [objectOrKey as MessageOptions['notification']], + 'notification' + ) + } + + addData(object: MessageOptions['data']): void + addData(key: K, value: MessageOptions['data'][K]): void + addData( + objectOrKey: MessageOptions['data'] | K, + value?: MessageOptions['data'][K] + ) { + return this.handleParamSet( + value !== undefined ? [objectOrKey as K, value] : [objectOrKey as MessageOptions['data']], + 'data' + ) + } + + toJson() { + const json: Record = {} + + for (const key of Object.keys(this.options)) { + const optionDescription = messageOptions[key] + + if (!optionDescription) { + continue + } + + const jsonKey = optionDescription.__argName || key + json[jsonKey] = this.options[key] + } + + return json + } + + /** + * @deprecated Please use Message#addData instead. + */ + addDataWithKeyValue(key: K, value: V) { + console.warn('Message#addDataWithKeyValue has been deprecated. Please use Message#addData instead.') + this.addData(key, value) + } + + /** + * @deprecated Please use Message#addData instead. + */ + addDataWithObject(obj: MessageOptions['data']) { + console.warn('Message#addDataWithObject has been deprecated. Please use Message#addData instead.') + this.addData(obj) + } + + private handleParamSet

( + args: [MessageOptions[P]] | [K, MessageOptions[P][K]], + paramType: P + ) { + if (args.length == 1) { + return this.setOption(paramType, args[0]) + } else if (args.length == 2) { + return this.addOption(paramType, args[0], args[1]) + } + } + + private addOption

( + paramType: P, + key: K, + value: MessageOptions[P][K] + ) { + if (!this.options[paramType]) { + this.options[paramType] = {} + } + return (this.options[paramType][key] = value) + } + + private setOption

(paramType: P, obj: MessageOptions[P]) { + if (typeof obj === 'object' && Object.keys(obj).length > 0) { + this.options[paramType] = obj + } + } +} + +export = Message diff --git a/src/multicastresult.ts b/src/multicastresult.ts new file mode 100644 index 00000000..5e0955ba --- /dev/null +++ b/src/multicastresult.ts @@ -0,0 +1,25 @@ +/** + * @deprecated You are using node-gcm MulticastResult, which is deprecated. + */ +class MulticastResult { + success = undefined + failure = undefined + canonicalIds = undefined + multicastId = undefined + results = [] + retryMulticastIds = [] + + constructor() { + console.warn('You are using node-gcm MulticastResult, which is deprecated.') + } + + addResult(result: any) { + this.results.push(result) + } + + getTotal() { + return this.success + this.failure + } +} + +export = MulticastResult diff --git a/src/result.ts b/src/result.ts new file mode 100644 index 00000000..a615feca --- /dev/null +++ b/src/result.ts @@ -0,0 +1,12 @@ +/** + * @deprecated You are using node-gcm Result which is deprecated + */ +function Result() { + this.messageId = undefined + this.canonicalRegistrationId = undefined + this.errorCode = undefined + + console.warn('You are using node-gcm Result which is deprecated') +} + +export = Result diff --git a/src/sender.ts b/src/sender.ts new file mode 100644 index 00000000..a8296fb3 --- /dev/null +++ b/src/sender.ts @@ -0,0 +1,330 @@ +import Message from './message' +import request from 'request' +import defaultsDeep from 'lodash.defaultsdeep' +import Constants from './constants' + +var debug = require('debug')('node-gcm') + +interface SenderOptions {} + +interface SendOptions {} + +type SendCallback = (err: unknown, response?: Record) => void + +class Sender { + key: string + options: SenderOptions + + constructor(key: string, options?: SenderOptions) { + if (!(this instanceof Sender)) { + return new Sender(key, options) + } + + this.key = key + this.options = options || {} + } + + send(message: Message, recipient, callback: SendCallback) + send(message: Message, recipient, options?: SendOptions, callback?: SendCallback) + send(message: Message, recipient, optionsOrCallback?: SendOptions | SendCallback, callback?: SendCallback) { + if (typeof optionsOrCallback == 'function') { + callback = optionsOrCallback as SendCallback + optionsOrCallback = null + } else if (!callback) { + callback = function () {} + } + const options = cleanOptions(optionsOrCallback) + + if (message.options?.data?.from) { + console.warn("Sending a notification with the 'from' data attribute may invoke a 400 Bad Request by FCM.") + } + + if (options.retries == 0) { + return this.sendNoRetry(message, recipient, callback) + } + + var self = this + + this.sendNoRetry(message, recipient, (err, response, attemptedRegTokens) => { + if (err) { + // Attempt to determine HTTP status code + const statusCode = typeof err === 'number' ? err : (err as any).code || 0 + + // 4xx error? + if (statusCode > 399 && statusCode < 500) { + debug('Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)') + return callback(err) + } + return retry(self, message, recipient, options, callback) + } + if (!response.results) { + return callback(null, response) + } + checkForBadTokens(response.results, attemptedRegTokens, function (err, unsentRegTokens, regTokenPositionMap) { + if (err) { + return callback(err) + } + if (unsentRegTokens.length == 0) { + return callback(null, response) + } + + debug('Retrying ' + unsentRegTokens.length + ' unsent registration tokens') + + retry(self, message, unsentRegTokens, options, function (err, retriedResponse) { + if (err) { + return callback(null, response) + } + response = updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens) + callback(null, response) + }) + }) + }) + } + + sendNoRetry( + message: Message, + recipient: string | string[], + callback?: (err: unknown, response?: Record, attemptedRegTokens?: string[]) => void + ) { + if (!callback) { + callback = function () {} + } + + getRequestBody(message, recipient, (err, body) => { + if (err) { + return callback(err) + } + + //Build request options, allowing some to be overridden + const request_options: request.RequiredUriUrl & request.CoreOptions = defaultsDeep( + { + method: 'POST', + headers: { + Authorization: 'key=' + this.key, + }, + json: body, + }, + this.options, + { + uri: Constants.GCM_SEND_URI, + timeout: Constants.SOCKET_TIMEOUT, + } + ) + + request(request_options, (err, res, resBodyJSON) => { + if (err) { + return callback(err) + } + if (res.statusCode >= 500) { + debug('GCM service is unavailable (500)') + return callback(res.statusCode) + } + if (res.statusCode === 401) { + debug('Unauthorized (401). Check that your API token is correct.') + return callback(res.statusCode) + } + if (res.statusCode !== 200) { + debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON) + return callback(res.statusCode) + } + if (!resBodyJSON) { + debug('Empty response received (' + res.statusCode + ' ' + res.statusMessage + ')') + // Spoof error code 400 to avoid retrying the request + return callback({ error: res.statusMessage, code: 400 }) + } + callback(null, resBodyJSON, (body.registration_ids as string[]) || [body.to as string]) + }) + }) + } +} + +function cleanOptions(options) { + if (!options || typeof options != 'object') { + var retries = 5 + if (typeof options == 'number') { + retries = options + } + return { + retries: retries, + backoff: Constants.BACKOFF_INITIAL_DELAY, + } + } + + if (typeof options.retries != 'number') { + options.retries = 5 + } + if (typeof options.backoff != 'number') { + options.backoff = Constants.BACKOFF_INITIAL_DELAY + } + if (options.backoff > Constants.MAX_BACKOFF_DELAY) { + options.backoff = Constants.MAX_BACKOFF_DELAY + } + + return options +} + +function retry(self, message, recipient, options, callback) { + return setTimeout(function () { + self.send( + message, + recipient, + { + retries: options.retries - 1, + backoff: options.backoff * 2, + }, + callback + ) + }, options.backoff) +} + +function checkForBadTokens(results, originalRecipients, callback) { + var unsentRegTokens = [] + var regTokenPositionMap = [] + for (var i = 0; i < results.length; i++) { + if (results[i].error === 'Unavailable' || results[i].error === 'InternalServerError') { + regTokenPositionMap.push(i) + unsentRegTokens.push(originalRecipients[i]) + } + } + nextTick(callback, null, unsentRegTokens, regTokenPositionMap) +} + +function updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens) { + updateResults(response.results, retriedResponse.results, regTokenPositionMap) + updateResponseMetaData(response, retriedResponse, unsentRegTokens) + return response +} + +function updateResults(results, retriedResults, regTokenPositionMap) { + for (var i = 0; i < results.length; i++) { + results[regTokenPositionMap[i]] = retriedResults[i] + } +} + +function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { + response.success += retriedResponse.success + response.canonical_ids += retriedResponse.canonical_ids + response.failure -= unsentRegTokens.length - retriedResponse.failure +} + +type RequestBodyCallback = (err: string | Error | null, body?: Record) => void + +function getRequestBody(message: Message, callback: RequestBodyCallback): void +function getRequestBody(message: Message, recipient: string | string[], callback: RequestBodyCallback): void +function getRequestBody( + message: Message, + recipientOrCallback: (string | string[]) | RequestBodyCallback, + callback?: RequestBodyCallback +) { + const body = message.toJson() + + if (typeof recipientOrCallback == 'string') { + body.to = recipientOrCallback + return nextTick(callback, null, body) + } + + if (Array.isArray(recipientOrCallback)) { + if (!recipientOrCallback.length) { + return nextTick(callback, 'No recipient provided!') + } else if (recipientOrCallback.length == 1) { + body.to = recipientOrCallback[0] + return nextTick(callback, null, body) + } + + body.registration_ids = recipientOrCallback + return nextTick(callback, null, body) + } + + if (typeof recipientOrCallback == 'object') { + return extractRecipient(recipientOrCallback, function (err, recipient, param) { + if (err) { + return callback(err) + } + body[param] = recipient + return callback(null, body) + }) + } + return nextTick( + callback, + 'Invalid recipient (' + recipientOrCallback + ', type ' + typeof recipientOrCallback + ') provided!' + ) +} + +function nextTick(func: (...args: A) => R, ...args: A) { + process.nextTick( + function () { + func.apply(this, args) + }.bind(this) + ) +} + +type RecipientObject = { + to?: string + topic?: string + condition?: string + notificationKey?: string + registrationIds?: string[] + registrationTokens?: string[] +} + +function extractRecipient( + recipient: RecipientObject, + callback: (error: Error | null, recipient?: string, param?: ReturnType) => void +) { + const recipientKeys = Object.keys(recipient) + + if (recipientKeys.length !== 1) { + return nextTick( + callback, + new Error('Please specify exactly one recipient key (you specified [' + recipientKeys + '])') + ) + } + + const key = recipientKeys[0] + const value = recipient[key] + + if (!value) { + return nextTick(callback, new Error("Falsy value for recipient key '" + key + "'.")) + } + + const keyValidators = { + to: isString, + topic: isString, + condition: isString, + notificationKey: isString, + registrationIds: isArray, + registrationTokens: isArray, + } + + const validator = keyValidators[key] + if (!validator) { + return nextTick(callback, new Error("Key '" + key + "' is not a valid recipient key.")) + } + if (!validator(value)) { + return nextTick(callback, new Error("Recipient key '" + key + "' was provided as an incorrect type.")) + } + + const param = getParamFromKey(key as keyof RecipientObject) + + return nextTick(callback, null, value, param) +} + +function getParamFromKey(key: keyof RecipientObject) { + if (key === 'condition') { + return 'condition' + } else if (['registrationIds', 'registrationTokens'].indexOf(key) !== -1) { + return 'registration_ids' + } else { + return 'to' + } +} + +function isString(x: unknown) { + return typeof x == 'string' +} + +function isArray(x: unknown) { + return Array.isArray(x) +} + +export = Sender diff --git a/test/unit/messageSpec.js b/test/unit/messageSpec.js index ed63f27e..6639e079 100644 --- a/test/unit/messageSpec.js +++ b/test/unit/messageSpec.js @@ -1,6 +1,6 @@ "use strict"; -var Message = require('../../lib/message'), +var Message = require('../../dist/message'), chai = require('chai'), expect = chai.expect; diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 027944b1..e182e215 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -4,9 +4,9 @@ var chai = require('chai'), expect = chai.expect, sinon = require('sinon'), proxyquire = require('proxyquire'), - senderPath = '../../lib/sender', - Constants = require('../../lib/constants'), - Message = require('../../lib/message'); + senderPath = '../../dist/sender', + Constants = require('../../dist/constants'), + Message = require('../../dist/message'); describe('UNIT Sender', function () { // Use object to set arguments passed into callback diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..3bc30c52 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "include": ["src/main.ts"], + "compilerOptions": { + "moduleResolution": "node", + "outDir": "dist", + "esModuleInterop": true, + "declaration": true, + "allowSyntheticDefaultImports": true + } +} \ No newline at end of file