From 6ed118a2de5f6dc0c1e17b7b7547bb244a231c53 Mon Sep 17 00:00:00 2001 From: Alexander Friedl Date: Thu, 11 Dec 2025 21:33:57 +0100 Subject: [PATCH 1/5] Remove support for legacy GCM API --- .github/copilot-instructions.md | 22 +- README.md | 253 ++----- package-lock.json | 77 +-- package.json | 1 - src/constants.js | 2 - src/push-notifications.js | 18 +- src/sendGCM.js | 108 --- src/utils/tools.js | 23 +- test/push-notifications/basic.js | 23 +- test/push-notifications/regIds.js | 10 +- test/send/sendADM.js | 28 +- test/send/sendAPN.js | 28 +- test/send/sendFCM.js | 1 - test/send/sendGCM.js | 1035 ----------------------------- test/send/sendWNS-accessToken.js | 28 +- test/send/sendWNS.js | 28 +- 16 files changed, 101 insertions(+), 1584 deletions(-) delete mode 100644 src/sendGCM.js delete mode 100644 test/send/sendGCM.js diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5721943..4f5ff8b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,7 +2,9 @@ ## Project Overview -This repository implements a Node.js module for sending push notifications across multiple platforms: Apple (APN), Google (GCM/FCM), Windows (WNS), Amazon (ADM), and Web-Push. The core logic is in `lib/` and `src/`, with each platform handled by a dedicated file (e.g., `sendAPN.js`, `sendFCM.js`). +This repository implements a Node.js module for sending push notifications across multiple platforms: Apple (APN), Google (FCM), Windows (WNS), Amazon (ADM), and Web-Push. The core logic is in `lib/` and `src/`, with each platform handled by a dedicated file (e.g., `sendAPN.js`, `sendFCM.js`). + +**Note:** Legacy GCM (Google Cloud Messaging) support has been removed. All Android push notifications now route exclusively through Firebase Cloud Messaging (FCM). ## Architecture & Data Flow @@ -21,29 +23,31 @@ This repository implements a Node.js module for sending push notifications acros ## Conventions & Patterns -- **Platform-specific files:** Each push service has its own file for isolation and clarity. +- **Platform-specific files:** Each push service has its own file for isolation and clarity. Legacy GCM is no longer supported. - **Unified Data Model:** The `data` object for notifications is normalized across platforms. See `README.md` for all supported fields. - **Error Handling:** Errors are unified and returned in the result array from `push.send`. - **RegId Format:** Prefer object format for registration IDs (`{id, type}`), but string format is supported for legacy reasons. -- **Chunking:** Android tokens are chunked in batches of 1,000 automatically. -- **Constants:** Use constants from `constants.js` for platform types. +- **Android Routing:** All Android push notifications (both long regIds and ADM/AMZN tokens without explicit type) route through FCM. +- **Chunking:** Android tokens are chunked in batches of 1,000 automatically by FCM. +- **Constants:** Use constants from `constants.js` for platform types. Available constants: `FCM_METHOD`, `APN_METHOD`, `WNS_METHOD`, `ADM_METHOD`, `WEB_METHOD`, `UNKNOWN_METHOD`. ## Integration Points - **External Libraries:** - - APN: `node-apn` - - FCM: `firebase-admin` - - GCM: `node-gcm` + - APN: `@parse/node-apn` + - FCM: `firebase-admin` (all Android push notifications) - ADM: `node-adm` - WNS: `wns` - Web-Push: `web-push` + - Note: Legacy `node-gcm` library has been removed - **Credentials:** Place service account keys and certificates in appropriate locations (see `README.md` for examples). ## Key Files & Directories -- `lib/` and `src/`: Main implementation (mirrored structure) -- `test/`: Test cases and sample credentials +- `lib/` and `src/`: Main implementation (mirrored structure, both CommonJS) +- `test/`: Test cases (78 tests, all passing) and sample credentials - `README.md`: Usage, configuration, and data model reference +- `.github/copilot-instructions.md`: This file - AI agent guidance ## Example Usage diff --git a/README.md b/README.md index 073cb41..bfd1c0a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ A node.js module for interfacing with Apple Push Notification, Google Cloud Mess - [Requirements](#requirements) - [Features](#features) - [Usage](#usage) -- [GCM](#gcm) - [FCM](#fcm) - [APN](#apn) - [WNS](#wns) @@ -46,17 +45,12 @@ Node version >= 14.x.x ### 1. Import and setup push module -Include the settings for each device type. You should only include the settings for the devices that you expect to have. I.e. if your app is only available for android or for ios, you should only include `gcm` or `apn` respectively. +Include the settings for each device type. You should only include the settings for the devices that you expect to have. I.e. if your app is only available for Android or for iOS, you should only include `fcm` or `apn` respectively. ```js import PushNotifications from 'node-pushnotifications'; const settings = { - gcm: { - id: 'your-GCM-id', - phonegap: false, // phonegap compatibility mode, see below (defaults to false) - ... - }, fcm: { appName: 'localFcmAppName', serviceAccountKey: require('../firebase-project-service-account-key.json'), // firebase service-account-file.json, @@ -88,26 +82,23 @@ const settings = { publicKey: '< URL Safe Base64 Encoded Public Key >', privateKey: '< URL Safe Base64 Encoded Private Key >', }, - gcmAPIKey: 'gcmkey', TTL: 2419200, contentEncoding: 'aes128gcm', headers: {} }, - isAlwaysUseFCM: false, // true all messages will be sent through gcm/fcm api - isLegacyGCM: false // if true gcm messages will be sent through node-gcm (deprecated api), if false gcm messages will be sent through 'firebase-admin' lib + isAlwaysUseFCM: false, // true all messages will be sent through FCM API }; const push = new PushNotifications(settings); ``` -- GCM options: see [node-gcm](https://github.com/ToothlessGear/node-gcm#custom-gcm-request-options) -- FCM options: see [firebase-admin](https://firebase.google.com/docs/admin/setup) (read [FCM](#fcm) section below!) +- FCM options: see [firebase-admin](https://firebase.google.com/docs/admin/setup) (read [FCM](#fcm) section below!) - used for Android and fallback for other platforms - APN options: see [node-apn](https://github.com/node-apn/node-apn/blob/master/doc/provider.markdown) - ADM options: see [node-adm](https://github.com/umano/node-adm) - WNS options: see [wns](https://github.com/tjanczuk/wns) - Web-push options: see [web-push](https://github.com/web-push-libs/web-push) -* `isAlwaysUseFCM`: use node-gcm to send notifications to GCM (by default), iOS, ADM and WNS. +* `isAlwaysUseFCM`: when set to `true`, will send all notifications through FCM instead of platform-specific services _iOS:_ It is recommended to use [provider authentication tokens](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html). You need the .p8 certificate that you can obtain in your [account membership](https://cloud.githubusercontent.com/assets/8225312/20380437/599a767c-aca2-11e6-82bd-3cbfc2feee33.png). You should ask for an _Apple Push Notification Authentication Key (Sandbox & Production)_ or _Apple Push Notification service SSL (Sandbox & Production)_. However, you can also use certificates. See [node-apn](https://github.com/node-apn/node-apn/wiki/Preparing-Certificates) to see how to prepare cert.pem and key.pem. @@ -144,10 +135,10 @@ It can be of 2 types: } ``` -Where type can be one of: 'apn', 'gcm', 'adm', 'wns', 'webPush'. The types are available as constants: +Where type can be one of: 'apn', 'fcm', 'adm', 'wns', 'webPush'. The types are available as constants: ```js -import { WEB, WNS, ADM, GCM, APN } from "node-pushnotifications"; +import { WEB, WNS, ADM, FCM, APN } from "node-pushnotifications"; const regId = { id: "INSERT_YOUR_DEVICE_ID", @@ -171,18 +162,19 @@ In case of webPush, `id` needs to be as defined below for `Web subscription`. #### String regId (not recommended) -It is not recommended, as Apple stays that the [reg id is of variable length](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application), which makes difficult to identify if it is a APN regId or GCM regId. +It is not recommended, as the [reg id is of variable length](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application), which makes it difficult to identify if it is an APN regId or FCM regId. - `regId.substring(0, 4) === 'http'`: 'wns' - `/^(amzn[0-9]*.adm)/i.test(regId)`: 'adm' - `(regId.length === 64 || regId.length === 160) && /^[a-fA-F0-9]+$/.test(regId)`: 'apn' -- `regId.length > 64`: 'gcm' +- `regId.length > 64`: 'fcm' - otherwise: 'unknown' (the notification will not be sent) **Android:** -- If you provide more than 1.000 registration tokens, they will automatically be splitted in 1.000 chunks (see [this issue in gcm repo](https://github.com/ToothlessGear/node-gcm/issues/42)) -- You are able to send to device groups or other custom recipients instead of using a list of device tokens (see [node-gcm docs](https://github.com/ToothlessGear/node-gcm#recipients)). Documentation can be found in the GCM section.. +- All Android notifications are sent through Firebase Cloud Messaging (FCM) +- If you provide more than 1.000 registration tokens, they will automatically be split into 1.000 chunks +- You are able to send to custom topics or conditions through FCM (see [firebase-admin docs](https://firebase.google.com/docs/cloud-messaging)) Example: @@ -197,7 +189,7 @@ Create a JSON object with a title and message and send the notification. ```js const data = { title: 'New push notification', // REQUIRED for Android - topic: 'topic', // REQUIRED for iOS (apn and gcm) + topic: 'topic', // REQUIRED for iOS (apn and fcm) /* The topic of the notification. When using token-based authentication, specify the bundle ID of the app. * When using certificate-based authentication, the topic is usually your app's bundle ID. * More details can be found under https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns @@ -206,48 +198,48 @@ const data = { custom: { sender: 'AppFeel', }, - priority: 'high', // gcm, apn. Supported values are 'high' or 'normal' (gcm). Will be translated to 10 and 5 for apn. Defaults to 'high' - collapseKey: '', // gcm for android, used as collapseId in apn - contentAvailable: true, // gcm, apn. node-apn will translate true to 1 as required by apn. - delayWhileIdle: true, // gcm for android - restrictedPackageName: '', // gcm for android - dryRun: false, // gcm for android - icon: '', // gcm for android - image: '', // gcm for android - style: '', // gcm for android - picture: '', // gcm for android - tag: '', // gcm for android - color: '', // gcm for android - clickAction: '', // gcm for android. In ios, category will be used if not supplied - locKey: '', // gcm, apn - titleLocKey: '', // gcm, apn - locArgs: undefined, // gcm, apn. Expected format: Stringified Array - titleLocArgs: undefined, // gcm, apn. Expected format: Stringified Array - retries: 1, // gcm, apn + priority: 'high', // fcm, apn. Supported values are 'high' or 'normal' (fcm). Will be translated to 10 and 5 for apn. Defaults to 'high' + collapseKey: '', // fcm for android, used as collapseId in apn + contentAvailable: true, // fcm, apn. node-apn will translate true to 1 as required by apn. + delayWhileIdle: true, // fcm for android + restrictedPackageName: '', // fcm for android + dryRun: false, // fcm for android + icon: '', // fcm for android + image: '', // fcm for android + style: '', // fcm for android + picture: '', // fcm for android + tag: '', // fcm for android + color: '', // fcm for android + clickAction: '', // fcm for android. In ios, category will be used if not supplied + locKey: '', // fcm, apn + titleLocKey: '', // fcm, apn + locArgs: undefined, // fcm, apn. Expected format: Stringified Array + titleLocArgs: undefined, // fcm, apn. Expected format: Stringified Array + retries: 1, // fcm, apn encoding: '', // apn - badge: 2, // gcm for ios, apn - sound: 'ping.aiff', // gcm, apn - android_channel_id: '', // gcm - Android Channel ID + badge: 2, // fcm for ios, apn + sound: 'ping.aiff', // fcm, apn + android_channel_id: '', // fcm - Android Channel ID notificationCount: 0, // fcm for android. badge can be used for both fcm and apn alert: { // apn, will take precedence over title and body title: 'title', body: 'body' // details: https://github.com/node-apn/node-apn/blob/master/doc/notification.markdown#convenience-setters }, - silent: false, // gcm, apn, will override badge, sound, alert and priority if set to true on iOS, will omit `notification` property and send as data-only on Android/GCM + silent: false, // fcm, apn, will override badge, sound, alert and priority if set to true on iOS, will omit `notification` property and send as data-only on Android/FCM /* * A string is also accepted as a payload for alert * Your notification won't appear on ios if alert is empty object * If alert is an empty string the regular 'title' and 'body' will show in Notification */ // alert: '', - launchImage: '', // apn and gcm for ios - action: '', // apn and gcm for ios - category: '', // apn and gcm for ios - // mdm: '', // apn and gcm for ios. Use this to send Mobile Device Management commands. + launchImage: '', // apn and fcm for ios + action: '', // apn and fcm for ios + category: '', // apn and fcm for ios + // mdm: '', // apn and fcm for ios. Use this to send Mobile Device Management commands. // https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/MobileDeviceManagementProtocolRef/3-MDM_Protocol/MDM_Protocol.html - urlArgs: '', // apn and gcm for ios - truncateAtWordEnd: true, // apn and gcm for ios + urlArgs: '', // apn and fcm for ios + truncateAtWordEnd: true, // apn and fcm for ios mutableContent: 0, // apn threadId: '', // apn pushType: undefined, // apn. valid values are 'alert' and 'background' (https://github.com/parse-community/node-apn/blob/master/doc/notification.markdown#notificationpushtype) @@ -280,7 +272,7 @@ push.send(registrationIds, data) ```js [ { - method: 'gcm', // The method used send notifications and which this info is related to + method: 'fcm', // The method used send notifications and which this info is related to multicastId: [], // (only Android) Array with unique ID (number) identifying the multicast message, one identifier for each chunk of 1.000 notifications) success: 0, // Number of notifications that have been successfully sent. It does not mean that the notification has been deliveried. failure: 0, // Number of notifications that have been failed to be send. @@ -288,7 +280,7 @@ push.send(registrationIds, data) messageId: '', // (only for android) String specifying a unique ID for each successfully processed message or undefined if error regId: value, // The current registrationId (device token id). Beware: For Android this may change if Google invalidates the previous device token. Use "originalRegId" if you are interested in when this changed occurs. originalRegId: value, // (only for android) The registrationId that was sent to the push.send() method. Compare this with field "regId" in order to know when the original registrationId (device token id) gets changed. - error: new Error('unknown'), // If any, there will be an Error object here for depuration purposes (when possible it will come form source libraries aka apn, node-gcm) + error: new Error('unknown'), // If any, there will be an Error object here for debugging purposes errorMsg: 'some error', // If any, will include the error message from the respective provider module }], }, @@ -311,158 +303,7 @@ push.send(registrationIds, data) ] ``` -## GCM - -**NOTE:** If you provide more than 1.000 registration tokens, they will automatically be splitted in 1.000 chunks (see [this issue in gcm repo](https://github.com/ToothlessGear/node-gcm/issues/42)) - -The following parameters are used to create a GCM message. See https://developers.google.com/cloud-messaging/http-server-ref#table5 for more info: - -```js - // Set default custom data from data - let custom; - if (typeof data.custom === 'string') { - custom = { - message: data.custom, - }; - } else if (typeof data.custom === 'object') { - custom = Object.assign({}, data.custom); - } else { - custom = { - data: data.custom, - }; - } - - custom.title = custom.title || data.title; - custom.message = custom.message || data.body; - custom.sound = custom.sound || data.sound; - custom.icon = custom.icon || data.icon; - custom.msgcnt = custom.msgcnt || data.badge; - if (opts.phonegap === true && data.contentAvailable) { - custom['content-available'] = 1; - } - - const message = new gcm.Message({ // See https://developers.google.com/cloud-messaging/http-server-ref#table5 - collapseKey: data.collapseKey, - priority: data.priority === 'normal' ? data.priority : 'high', - contentAvailable: data.contentAvailable || false, - delayWhileIdle: data.delayWhileIdle || false, // Deprecated from Nov 15th 2016 (will be ignored) - timeToLive: data.expiry - Math.floor(Date.now() / 1000) || data.timeToLive || 28 * 86400, - restrictedPackageName: data.restrictedPackageName, - dryRun: data.dryRun || false, - data: data.custom, - notification: { - title: data.title, // Android, iOS (Watch) - body: data.body, // Android, iOS - icon: data.icon, // Android - image: data.image, // Android - style: data.style, // Android - picture: data.picture, // Android - sound: data.sound, // Android, iOS - badge: data.badge, // iOS - tag: data.tag, // Android - color: data.color, // Android - click_action: data.clickAction || data.category, // Android, iOS - body_loc_key: data.locKey, // Android, iOS - body_loc_args: data.locArgs, // Android, iOS - title_loc_key: data.titleLocKey, // Android, iOS - title_loc_args: data.titleLocArgs, // Android, iOS - android_channel_id: data.android_channel_id, // Android - }, - } -``` - -_data is the parameter in `push.send(registrationIds, data)`_ - -- [See node-gcm fields](https://github.com/ToothlessGear/node-gcm#usage) -**Note:** parameters are duplicated in data and in notification, so in fact they are being send as: - -```js - data: { - title: 'title', - message: 'body', - sound: 'mySound.aiff', - icon: undefined, - msgcnt: undefined - // Any custom data - sender: 'appfeel-test', - }, - notification: { - title: 'title', - body: 'body', - icon: undefined, - image: undefined, - style: undefined, - picture: undefined, - sound: 'mySound.aiff', - badge: undefined, - tag: undefined, - color: undefined, - click_action: undefined, - body_loc_key: undefined, - body_loc_args: undefined, - title_loc_key: undefined, - title_loc_args: undefined, - android_channel_id: undefined - } -``` - -In that way, they can be accessed in android in the following two ways: - -```java - String title = extras.getString("title"); - title = title != null ? title : extras.getString("gcm.notification.title"); -``` - -### Silent push notifications - -GCM supports silent push notifications which are not displayed to the user but only used to transmit data. - -```js -const silentPushData = { - topic: 'yourTopic', - silent: true, - custom: { - yourKey: 'yourValue', - ... - } -} -``` - -Internally, `silent: true` will tell `node-gcm` _not_ to send the `notification` property and only send the `custom` property. If you don't specify `silent: true` then the push notifications will still be visible on the device. Note that this is nearly the same behavior as `phoneGap: true` and will set `content-available` to `true`. - -### Send to custom recipients (device groups or topics) - -In order to override the default behaviour of sending the notifications to a list of device tokens, -you can pass a `recipients` field with your desired recipients. Supported fields are `to` and `condition` as documented in the [node-gcm docs](https://github.com/ToothlessGear/node-gcm#recipients). - -Example: - -```javascript -const dataWithRecipientTo = { ...yourData, recipients: { to: 'topicName' } }; -const dataWithRecipientCondition = { ...yourData, recipients: { condition: 'topicName' } }; - -push.send(registrationIds, dataWithRecipientTo) - .then((results) => { ... }) - .catch((err) => { ... }); -``` - -Be aware that the presence of a valid `data.recipient` field will take precendence over any Android device tokens passed with the `registrationIds`. - -### PhoneGap compatibility mode - -In case your app is written with Cordova / Ionic and you are using the [PhoneGap PushPlugin](https://github.com/phonegap/phonegap-plugin-push/), -you can use the `phonegap` setting in order to adapt to the recommended behaviour described in -[https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#android-behaviour](https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#android-behaviour). - -```js -const settings = { - gcm: { - id: "", - phonegap: true, - }, -}; -``` ## APN @@ -533,8 +374,8 @@ const silentPushData = { ## FCM -The following parameters are used to create an FCM message (Android/APN): -[node-gcm](https://github.com/ToothlessGear/node-gcm) lib for `GCM` method use old firebase api (will be [deprecated ](https://firebase.google.com/docs/cloud-messaging/migrate-v1?hl=en&authuser=0)) +The following parameters are used to create an FCM message for Android and iOS: + Settings: - `settings.fcm.appName` [firebase app name](https://firebase.google.com/docs/reference/admin/node/firebase-admin.app.app#appname) (required) @@ -579,11 +420,7 @@ pushNotifications.send(tokens, notifications, (error, result) => { }); ``` -`fcm_notification` - object that will be passed to - -```js - new gcm.Message({ ..., notification: data.fcm_notification }) -``` +`fcm_notification` - object that will be passed to FCM message notification field Fcm object that will be sent to provider ([Fcm message format](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#Message)) : diff --git a/package-lock.json b/package-lock.json index 78dbabc..0d47934 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@parse/node-apn": "7.0.1", "firebase-admin": "12.1.1", "node-adm": "0.9.1", - "node-gcm": "1.1.4", "web-push": "3.6.7", "wns": "0.5.4" }, @@ -3653,7 +3652,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "devOptional": true }, "node_modules/available-typed-arrays": { "version": "1.0.7", @@ -3686,29 +3686,6 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, - "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", @@ -4387,6 +4364,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "devOptional": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4810,6 +4788,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -5900,25 +5879,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -7931,7 +7891,8 @@ "node_modules/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 }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -8180,6 +8141,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "devOptional": true, "engines": { "node": ">= 0.6" } @@ -8188,6 +8150,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "devOptional": true, "dependencies": { "mime-db": "1.52.0" }, @@ -8568,27 +8531,6 @@ "node": ">= 6.13.0" } }, - "node_modules/node-gcm": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/node-gcm/-/node-gcm-1.1.4.tgz", - "integrity": "sha512-6Z3Ksmum3xsux/Ejwg2pn+yELvL13nIP5ZbdJDZupnipfP10xyPvJGt5jlB3pCrKkIzcTKL87OI0xsbbz8YkpA==", - "dependencies": { - "axios": "~1.6.8", - "debug": "^3.1.0", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/node-gcm/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -9710,7 +9652,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "node_modules/psl": { "version": "1.2.0", diff --git a/package.json b/package.json index fe60fc8..22bb507 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "@parse/node-apn": "7.0.1", "firebase-admin": "12.1.1", "node-adm": "0.9.1", - "node-gcm": "1.1.4", "web-push": "3.6.7", "wns": "0.5.4" }, diff --git a/src/constants.js b/src/constants.js index 73bfd48..fe7d8d6 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,7 +2,6 @@ module.exports = { DEFAULT_TTL: 28 * 86400, GCM_MAX_TTL: 2419200, // 4 weeks in seconds (https://firebase.google.com/docs/cloud-messaging/http-server-ref#downstream-http-messages-json) APN_METHOD: "apn", - GCM_METHOD: "gcm", FCM_METHOD: "fcm", ADM_METHOD: "adm", WNS_METHOD: "wns", @@ -71,6 +70,5 @@ module.exports = { // contentEncoding: '< Encoding type, e.g.: aesgcm or aes128gcm >' }, isAlwaysUseFCM: false, - isLegacyGCM: false, }, }; diff --git a/src/push-notifications.js b/src/push-notifications.js index 4dd4a10..40afae3 100644 --- a/src/push-notifications.js +++ b/src/push-notifications.js @@ -1,4 +1,3 @@ -const sendGCM = require("./sendGCM"); const sendFCM = require("./sendFCM"); const APN = require("./sendAPN"); const sendADM = require("./sendADM"); @@ -11,7 +10,6 @@ const { WEB_METHOD, WNS_METHOD, ADM_METHOD, - GCM_METHOD, FCM_METHOD, APN_METHOD, } = require("./constants"); @@ -27,7 +25,6 @@ class PN { this.apn.shutdown(); } this.apn = new APN(this.settings.apn); - this.useFcmOrGcmMethod = this.settings.isLegacyGCM ? GCM_METHOD : FCM_METHOD; } sendWith(method, regIds, data, cb) { @@ -50,14 +47,14 @@ class PN { if (typeof regId === "object" && regId.id && regId.type) { return { regId: regId.id, - pushMethod: this.settings.isAlwaysUseFCM ? this.useFcmOrGcmMethod : regId.type, + pushMethod: this.settings.isAlwaysUseFCM ? FCM_METHOD : regId.type, }; } // TODO: deprecated, remove of all cases below in v3.0 // and review test cases if (this.settings.isAlwaysUseFCM) { - return { regId, pushMethod: this.useFcmOrGcmMethod }; + return { regId, pushMethod: FCM_METHOD }; } if (regId.substring(0, 4) === "http") { @@ -73,7 +70,7 @@ class PN { } if (regId.length > 64) { - return { regId, pushMethod: this.useFcmOrGcmMethod }; + return { regId, pushMethod: FCM_METHOD }; } return { regId, pushMethod: UNKNOWN_METHOD }; @@ -81,7 +78,6 @@ class PN { send(_regIds, data, callback) { const promises = []; - const regIdsGCM = []; const regIdsFCM = []; const regIdsAPN = []; const regIdsWNS = []; @@ -96,8 +92,6 @@ class PN { if (pushMethod === WEB_METHOD) { regIdsWebPush.push(regId); - } else if (pushMethod === GCM_METHOD) { - regIdsGCM.push(regId); } else if (pushMethod === FCM_METHOD) { regIdsFCM.push(regId); } else if (pushMethod === WNS_METHOD) { @@ -112,11 +106,6 @@ class PN { }); try { - // Android GCM / FCM (Android/iOS) Legacy - if (regIdsGCM.length > 0) { - promises.push(this.sendWith(sendGCM, regIdsGCM, data)); - } - // FCM (Android/iOS) if (regIdsFCM.length > 0) { promises.push(this.sendWith(sendFCM, regIdsFCM, data)); @@ -192,5 +181,4 @@ module.exports = PN; module.exports.WEB = WEB_METHOD; module.exports.WNS = WNS_METHOD; module.exports.ADM = ADM_METHOD; -module.exports.GCM = GCM_METHOD; module.exports.APN = APN_METHOD; diff --git a/src/sendGCM.js b/src/sendGCM.js deleted file mode 100644 index 15147a4..0000000 --- a/src/sendGCM.js +++ /dev/null @@ -1,108 +0,0 @@ -const gcm = require("node-gcm"); -const { GCM_METHOD } = require("./constants"); -const { containsValidRecipients, buildGcmMessage } = require("./utils/tools"); - -const getRecipientList = (obj) => obj.registrationTokens ?? [obj.to, obj.condition].filter(Boolean); - -const sendChunk = (GCMSender, recipients, message, retries) => - new Promise((resolve) => { - const recipientList = getRecipientList(recipients); - - GCMSender.send(message, recipients, retries, (err, response) => { - // Response: see https://developers.google.com/cloud-messaging/http-server-ref#table5 - if (err) { - resolve({ - method: GCM_METHOD, - success: 0, - failure: recipientList.length, - message: recipientList.map((value) => ({ - originalRegId: value, - regId: value, - error: err, - errorMsg: err instanceof Error ? err.message : err, - })), - }); - } else if (response && response.results !== undefined) { - let regIndex = 0; - resolve({ - method: GCM_METHOD, - multicastId: response.multicast_id, - success: response.success, - failure: response.failure, - message: response.results.map((value) => { - const regToken = recipientList[regIndex]; - regIndex += 1; - const errorMsg = value.error ? value.error.message || value.error : null; - return { - messageId: value.message_id, - originalRegId: regToken, - regId: value.registration_id || regToken, - error: value.error ? new Error(value.error) : null, - errorMsg, - }; - }), - }); - } else { - resolve({ - method: GCM_METHOD, - multicastId: response.multicast_id, - success: response.success, - failure: response.failure, - message: recipientList.map((value) => ({ - originalRegId: value, - regId: value, - error: new Error("unknown"), - errorMsg: "unknown", - })), - }); - } - }); - }); - -const sendGCM = (regIds, data, settings) => { - const opts = { ...settings.gcm }; - const { id } = opts; - delete opts.id; - const GCMSender = new gcm.Sender(id, opts); - const promises = []; - - const message = buildGcmMessage(data, opts); - - let chunk = 0; - - /* allow to override device tokens with custom `to` or `condition` field: - * https://github.com/ToothlessGear/node-gcm#recipients */ - if (containsValidRecipients(data)) { - promises.push(sendChunk(GCMSender, data.recipients, message, data.retries || 0)); - } else { - // Split tokens in 1.000 chunks, see https://developers.google.com/cloud-messaging/http-server-ref#table1 - do { - const registrationTokens = regIds.slice(chunk * 1000, (chunk + 1) * 1000); - promises.push(sendChunk(GCMSender, { registrationTokens }, message, data.retries || 0)); - chunk += 1; - } while (1000 * chunk < regIds.length); - } - - return Promise.all(promises).then((results) => { - const resumed = { - method: GCM_METHOD, - multicastId: [], - success: 0, - failure: 0, - message: [], - }; - - results.forEach((result) => { - if (result.multicastId) { - resumed.multicastId.push(result.multicastId); - } - resumed.success += result.success; - resumed.failure += result.failure; - resumed.message.push(...result.message); - }); - - return resumed; - }); -}; - -module.exports = sendGCM; diff --git a/src/utils/tools.js b/src/utils/tools.js index 5bb7eda..e4bec3f 100644 --- a/src/utils/tools.js +++ b/src/utils/tools.js @@ -1,5 +1,4 @@ const { Notification: ApnsMessage } = require("@parse/node-apn"); -const { Message: GcmMessage } = require("node-gcm"); const { DEFAULT_TTL, GCM_MAX_TTL } = require("../constants"); @@ -134,19 +133,23 @@ const buildGcmMessage = (data, options) => { custom["content-available"] = 1; } - const message = new GcmMessage({ - collapseKey: data.collapseKey, + const messageData = { + collapse_key: data.collapseKey, priority: data.priority === "normal" ? "normal" : "high", - contentAvailable: data.silent ? true : data.contentAvailable || false, - delayWhileIdle: data.delayWhileIdle || false, - timeToLive: extractTimeToLive(data), - restrictedPackageName: data.restrictedPackageName, - dryRun: data.dryRun || false, + content_available: data.silent ? true : data.contentAvailable || false, + delay_while_idle: data.delayWhileIdle || false, + time_to_live: extractTimeToLive(data), + restricted_package_name: data.restrictedPackageName, + dry_run: data.dryRun || false, data: options.phonegap === true ? Object.assign(custom, notification) : custom, notification: options.phonegap === true || data.silent === true ? undefined : notification, - }); + }; - return message; + // Return a wrapper object that mimics GcmMessage.toJson() + return { + toJson: () => messageData, + params: messageData, + }; }; const buildApnsMessage = (data) => { diff --git a/test/push-notifications/basic.js b/test/push-notifications/basic.js index c92b17d..bb1c579 100644 --- a/test/push-notifications/basic.js +++ b/test/push-notifications/basic.js @@ -10,7 +10,6 @@ import { WEB_METHOD, WNS_METHOD, ADM_METHOD, - GCM_METHOD, FCM_METHOD, APN_METHOD, } from "../../src/constants"; @@ -36,9 +35,6 @@ describe("push-notifications: instantiation and class properties", () => { describe("override options with constructor", () => { let pn; const settings = { - gcm: { - id: "gcm id", - }, fcm: { name: "testAppName", // eslint-disable-next-line no-undef @@ -81,7 +77,6 @@ describe("push-notifications: instantiation and class properties", () => { it("should override the given options", () => { expect(pn.settings.apn).to.eql(settings.apn); - expect(pn.settings.gcm).to.eql(settings.gcm); expect(pn.settings.fcm).to.eql(settings.fcm); expect(pn.settings.adm).to.eql(settings.adm); expect(pn.settings.wns).to.eql(settings.wns); @@ -191,40 +186,39 @@ describe("push-notifications: instantiation and class properties", () => { unknownRegId: "abcdef", }; - it("Android / GCM", () => { - let pn = new PN({ isLegacyGCM: true }); + it("Android / FCM", () => { + let pn = new PN({}); expect(pn.getPushMethodByRegId(regIds.androidRegId).regId).to.equal(regIds.androidRegId); - expect(pn.getPushMethodByRegId(regIds.androidRegId).pushMethod).to.equal(GCM_METHOD); + expect(pn.getPushMethodByRegId(regIds.androidRegId).pushMethod).to.equal(FCM_METHOD); expect(pn.getPushMethodByRegId(regIds.androidWithAdmSubstringRegId).regId).to.equal( regIds.androidWithAdmSubstringRegId ); expect(pn.getPushMethodByRegId(regIds.androidWithAdmSubstringRegId).pushMethod).to.equal( - GCM_METHOD + FCM_METHOD ); expect(pn.getPushMethodByRegId(regIds.androidWithAmznSubscringRegId).regId).to.equal( regIds.androidWithAmznSubscringRegId ); expect(pn.getPushMethodByRegId(regIds.androidWithAmznSubscringRegId).pushMethod).to.equal( - GCM_METHOD + FCM_METHOD ); const settings = { isAlwaysUseFCM: true, - isLegacyGCM: true, }; pn = new PN(settings); expect(pn.getPushMethodByRegId(regIds.unknownRegId).regId).to.equal(regIds.unknownRegId); - expect(pn.getPushMethodByRegId(regIds.unknownRegId).pushMethod).to.equal(GCM_METHOD); + expect(pn.getPushMethodByRegId(regIds.unknownRegId).pushMethod).to.equal(FCM_METHOD); expect(pn.getPushMethodByRegId(regIds.androidObject).regId).to.equal(regIds.androidObject.id); - expect(pn.getPushMethodByRegId(regIds.androidObject).pushMethod).to.equal(GCM_METHOD); + expect(pn.getPushMethodByRegId(regIds.androidObject).pushMethod).to.equal(FCM_METHOD); expect(pn.getPushMethodByRegId(regIds.androidObjectWhatever).regId).to.equal( regIds.androidObjectWhatever.id ); - expect(pn.getPushMethodByRegId(regIds.androidObjectWhatever).pushMethod).to.equal(GCM_METHOD); + expect(pn.getPushMethodByRegId(regIds.androidObjectWhatever).pushMethod).to.equal(FCM_METHOD); }); it("Android / FCM", () => { @@ -234,7 +228,6 @@ describe("push-notifications: instantiation and class properties", () => { // eslint-disable-next-line no-undef serviceAccountKey: require(path.resolve("test/send/FCM-service-account-key.json")), }, - isLegacyGCM: false, }; const pn = new PN(settings); diff --git a/test/push-notifications/regIds.js b/test/push-notifications/regIds.js index c80c0b2..537c7ea 100644 --- a/test/push-notifications/regIds.js +++ b/test/push-notifications/regIds.js @@ -3,7 +3,7 @@ import { expect } from "chai"; import sinon from "sinon"; import PN from "../../src"; -import sendGCM from "../../src/sendGCM"; +import sendFCM from "../../src/sendFCM"; import sendADM from "../../src/sendADM"; import sendWNS from "../../src/sendWNS"; import sendWeb from "../../src/sendWeb"; @@ -35,7 +35,7 @@ describe("push-notifications: call with registration ids for android, ios, windo let sendWith; before(() => { - pn = new PN({ isLegacyGCM: true }); + pn = new PN({}); const sendApnFunctionName = pn.apn.sendAPN.bind(pn.apn).name; sendWith = sinon.stub(PN.prototype, "sendWith", (method, _regIds, _data, cb) => { @@ -49,7 +49,7 @@ describe("push-notifications: call with registration ids for android, ios, windo case 0: case 1: case 2: - expect(method).to.equal(sendGCM); + expect(method).to.equal(sendFCM); break; case 3: @@ -70,7 +70,7 @@ describe("push-notifications: call with registration ids for android, ios, windo break; default: - expect.fail(null, null, "Method should be sendGCM, sendAPN, sendWNS, sendADM or sendWeb"); + expect.fail(null, null, "Method should be sendFCM, sendAPN, sendWNS, sendADM or sendWeb"); break; } expect(data).to.equal(data); @@ -105,7 +105,7 @@ describe("push-notifications: call with registration ids for android, ios, windo const assertPushResultsForArrayInput = (result) => { let expectedNumRegIds = 1; - if (result.method === "sendGCM") { + if (result.method === "sendFCM") { expectedNumRegIds = 3; } else if (result.method === "bound sendAPN") { expectedNumRegIds = 2; diff --git a/test/send/sendADM.js b/test/send/sendADM.js index 4c3cd61..a96c83e 100644 --- a/test/send/sendADM.js +++ b/test/send/sendADM.js @@ -3,7 +3,7 @@ import { expect } from "chai"; import sinon from "sinon"; import adm from "node-adm"; import PN from "../../src"; -import { sendOkMethodGCM, testPushSuccess, testPushError, testPushException } from "../util"; +import { testPushSuccess, testPushError, testPushException } from "../util"; const method = "adm"; const regIds = [ @@ -29,7 +29,6 @@ const pn = new PN(admOpts); const fErr = new Error("Forced error"); const testSuccess = testPushSuccess(method, regIds); -const testSuccessGCM = testPushSuccess("gcm", regIds); const testError = testPushError(method, regIds, fErr.message); const testException = testPushException(fErr.message); @@ -151,29 +150,4 @@ describe("push-notifications-adm", () => { .catch((err) => testException(err, undefined, done)); }); }); - - describe("send push notifications using FCM", () => { - const pnGCM = new PN({ - isAlwaysUseFCM: true, - isLegacyGCM: true, - }); - before(() => { - sendMethod = sendOkMethodGCM(regIds, data); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pnGCM.send(regIds, data, (err, results) => testSuccessGCM(err, results, done)); - }); - - it("all responses should be successful (promise)", (done) => { - pnGCM - .send(regIds, data) - .then((results) => testSuccessGCM(null, results, done)) - .catch(done); - }); - }); }); diff --git a/test/send/sendAPN.js b/test/send/sendAPN.js index 6bc79ac..a3a98e0 100644 --- a/test/send/sendAPN.js +++ b/test/send/sendAPN.js @@ -8,7 +8,7 @@ import apn from "@parse/node-apn"; import PN from "../../src"; import APN from "../../src/sendAPN"; -import { sendOkMethodGCM, testPushSuccess, testPushError, testPushException } from "../util"; +import { testPushSuccess, testPushError, testPushException } from "../util"; // Mock apn certificate loading to prevent file access before(() => { @@ -56,7 +56,6 @@ const fErr = new Error("Forced error"); const errStatusCode = "410"; const testSuccess = testPushSuccess(method, regIds); -const testSuccessGCM = testPushSuccess("gcm", regIds); const testError = testPushError(method, regIds, fErr.message); const testErrorStatusCode = testPushError(method, regIds, errStatusCode); const testException = testPushException(fErr.message); @@ -676,29 +675,4 @@ describe("push-notifications-apn", () => { }); }); }); - - describe("send push notifications successfully using FCM", () => { - const pnGCM = new PN({ - isAlwaysUseFCM: true, - isLegacyGCM: true, - }); - before(() => { - sendMethod = sendOkMethodGCM(regIds, data); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pnGCM.send(regIds, data, (err, results) => testSuccessGCM(err, results, done)); - }); - - it("all responses should be successful (promise)", (done) => { - pnGCM - .send(regIds, data) - .then((results) => testSuccessGCM(null, results, done)) - .catch(done); - }); - }); }); diff --git a/test/send/sendFCM.js b/test/send/sendFCM.js index 91bb560..a7672ca 100644 --- a/test/send/sendFCM.js +++ b/test/send/sendFCM.js @@ -22,7 +22,6 @@ const fcmOpts = { name: "testAppName", credential: { getAccessToken: () => Promise.resolve({}) }, }, - isLegacyGCM: false, }; const pn = new PN(fcmOpts); diff --git a/test/send/sendGCM.js b/test/send/sendGCM.js deleted file mode 100644 index 6f344ea..0000000 --- a/test/send/sendGCM.js +++ /dev/null @@ -1,1035 +0,0 @@ -/* eslint-env mocha */ -import chai from "chai"; -import sinon from "sinon"; -import dirtyChai from "dirty-chai"; -import gcm from "node-gcm"; -import PN from "../../src"; -import { DEFAULT_TTL, GCM_MAX_TTL } from "../../src/constants"; -import { sendOkMethodGCM, testPushSuccess, testPushError, testPushException } from "../util"; - -const { expect } = chai; -chai.use(dirtyChai); - -const method = "gcm"; -const regIds = [ - "APA91bFQCD9Ndd8uVggMhj1usfeWsKIfGyBUWMprpZLGciWrMjS-77bIY24IMQNeEHzjidCcddnDxqYo-UEV03xw6ySmtIgQyzTqhSxhPGAi1maf6KDMAQGuUWc6L5Khze8YK9YrL9I_WD1gl49P3f_9hr08ZAS5Tw", - "APA91bFQCD9Ndd8uVggMhj1usfeWsKIfGyBUWMprpZLGciWrMjS-77bIY24IMQNeEHzjidCcddnDxqYo-UEV03xw6ySmtIgQyzTqhSxhPGAi1maf6KDMAQGuUWc6L5Khze8YK9YrL9I_WD1gl49P3f_9hr08ZAS5Tw", - "APA91bFQCD9Ndd8uVggMhj1usfeWsKIfGyBUWMprpZLGciWrMjS-77bIY24IMQNeEHzjidCcddnDxqYo-UEV03xw6ySmtIgQyzTqhSxhPGAi1maf6KDMAQGuUWc6L5Khze8YK9YrL9I_WD1gl49P3f_9hr08ZAS5Tw", -]; -const data = { - title: "title", - body: "body", - sound: "mySound.aiff", - contentAvailable: true, - custom: { - sender: "appfeel-test", - }, - badge: 42, -}; -const gcmOpts = { - gcm: { - id: "your id", - }, - isLegacyGCM: true, -}; -const notificationOptions = { - title: "Notification Title", - body: "Notification Body", - image: "https://s3.image.com", - color: "#000", -}; -const pn = new PN(gcmOpts); -const fErr = new Error("Forced error"); - -const testSuccess = testPushSuccess(method, regIds); -const testError = testPushError(method, regIds, fErr.message); -const testUnknownError = testPushError(method, regIds, "unknown"); -const testException = testPushException(fErr.message); - -let sendMethod; - -function sendFailureMethod1() { - // Don't use arrow function because we use this!! - return sinon.stub( - gcm.Sender.prototype, - "send", - function SenderSend(message, recipients, retries, cb) { - const { registrationTokens } = recipients; - expect(this.key).equal(gcmOpts.gcm.id); - cb(null, { - multicast_id: "abc", - success: 0, - failure: regIds.length, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: fErr.message, - })), - }); - } - ); -} - -function sendFailureMethod2() { - return sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - cb(null, { - multicast_id: "abc", - success: 0, - failure: regIds.length, - }); - }); -} - -function sendErrorMethod() { - return sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - cb(fErr); - }); -} - -function sendThrowExceptionMethod() { - return sinon.stub(gcm.Sender.prototype, "send", () => { - throw fErr; - }); -} - -describe("push-notifications-gcm", () => { - describe("send push notifications successfully", () => { - before(() => { - sendMethod = sendOkMethodGCM(regIds, data); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pn.send(regIds, data, (err, results) => testSuccess(err, results, done)); - }); - - it("all responses should be successful (promise)", (done) => { - pn.send(regIds, data) - .then((results) => testSuccess(null, results, done)) - .catch(done); - }); - }); - - describe("send push notifications successfully, data no title", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.be.undefined(); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.data.sender).to.eql(data.custom.sender); - // This params are duplicated in order to facilitate extraction - // So they are available as `gcm.notification.title` and as `title` - expect(message.params.data.title).to.be.undefined(); - expect(message.params.data.message).to.eql(data.body); - expect(message.params.data.sound).to.eql(data.sound); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, no title)", (done) => { - const newData = { ...data }; - delete newData.title; - const callback = (err, results) => testSuccess(err, results, done); - pn.send(regIds, newData, callback).catch(() => {}); - }); - }); - - describe("send push notifications successfully, (callback, no sound, icon, msgcnt, badge)", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.be.undefined(); - expect(message.params.notification.icon).to.equal("myicon.png"); - expect(message.params.data.sender).to.eql(data.custom.sender); - // This params are duplicated in order to facilitate extraction - // So they are available as `gcm.notification.title` and as `title` - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.eql(data.body); - expect(message.params.data.sound).to.be.undefined(); - expect(message.params.data.icon).to.equal("myicon.png"); - expect(message.params.data.msgcnt).to.equal(2); - expect(message.params.notification.notification_count).to.eql(42); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, no sound, icon, msgcnt)", (done) => { - const newData = { ...data }; - delete newData.sound; - newData.icon = "myicon.png"; - newData.custom.msgcnt = 2; - const callback = (err, results) => testSuccess(err, results, done); - pn.send(regIds, newData, callback).catch(() => {}); - }); - }); - - describe("send push notifications successfully, (callback, no contentAvailable, notificationCount)", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.data.sender).to.eql(data.custom.sender); - // This params are duplicated in order to facilitate extraction - // So they are available as `gcm.notification.title` and as `title` - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.eql(data.body); - expect(message.params.data.sound).to.eql(data.sound); - expect(message.params.notification.notification_count).to.eql(42); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, no contentAvailable)", (done) => { - const newData = { ...data }; - delete newData.contentAvailable; - delete newData.badge; - newData.notificationCount = 42; - const callback = (err, results) => testSuccess(err, results, done); - pn.send(regIds, newData, callback).catch(() => {}); - }); - }); - - describe("send push notifications successfully, data no body", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.be.undefined(); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.data.sender).to.eql(data.custom.sender); - // This params are duplicated in order to facilitate extraction - // So they are available as `gcm.notification.title` and as `title` - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.be.undefined(); - expect(message.params.data.sound).to.eql(data.sound); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, no body)", (done) => { - const newData = { ...data }; - delete newData.body; - const callback = (err, results) => testSuccess(err, results, done); - pn.send(regIds, newData, callback).catch(() => {}); - }); - }); - - describe("send push notifications successfully, custom data string", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.data.message).to.equal("this is a string"); - // This params are duplicated in order to facilitate extraction - // So they are available as `gcm.notification.title` and as `title` - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.equal("this is a string"); - expect(message.params.data.sound).to.eql(data.sound); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, custom data as string)", (done) => { - const newData = { ...data, custom: "this is a string" }; - pn.send(regIds, newData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications successfully, custom data undefined", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.an.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.notification.data).to.be.undefined(); - // This params are duplicated in order to facilitate extraction - // So they are available as `gcm.notification.title` and as `title` - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.eql(data.body); - expect(message.params.data.sound).to.eql(data.sound); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, custom data undefined)", (done) => { - const newData = { ...data }; - delete newData.custom; - pn.send(regIds, newData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications successfully, normal priority", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.priority).to.equal("normal"); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, custom data undefined)", (done) => { - const normalPrioData = { ...data }; - normalPrioData.priority = "normal"; - pn.send(regIds, normalPrioData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("timeToLive", () => { - describe("send push notifications with custom timeToLive", () => { - const timeToLive = 4004; - - before(() => { - sendMethod = sinon.stub( - gcm.Sender.prototype, - "send", - (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(message.params.timeToLive).to.equal(timeToLive); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - } - ); - }); - - after(() => { - sendMethod.restore(); - }); - - it("timeToLive set correctly", (done) => { - const expiryData = { ...data, timeToLive }; - pn.send(regIds, expiryData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications with tll 0", () => { - const ttl = 0; - - before(() => { - sendMethod = sinon.stub( - gcm.Sender.prototype, - "send", - (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(message.params.timeToLive).to.equal(ttl); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - } - ); - }); - - after(() => { - sendMethod.restore(); - }); - - it("timeToLive 0 should be accepted as a valid value", (done) => { - const ttlData = { ...data, timeToLive: ttl }; - pn.send(regIds, ttlData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications with timeToLive calculated from expiry", () => { - const now = 150000; - let clock; - - before(() => { - const expectedTtl = 159850; - - clock = sinon.useFakeTimers(now); - - sendMethod = sinon.stub( - gcm.Sender.prototype, - "send", - (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(message.params.timeToLive).to.equal(expectedTtl); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - } - ); - }); - - after(() => { - sendMethod.restore(); - clock.restore(); - }); - - it("timeToLive should be calculated correctly from expiry. expiry takes precedence", (done) => { - const expiryData = { ...data, expiry: 160000, timeToLive: 3000 }; - pn.send(regIds, expiryData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications with timeToLive calculated from expiry must not exceed max TTL", () => { - const now = 150000; - let clock; - - before(() => { - clock = sinon.useFakeTimers(now); - - sendMethod = sinon.stub( - gcm.Sender.prototype, - "send", - (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(message.params.timeToLive).to.equal(GCM_MAX_TTL); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - } - ); - }); - - after(() => { - sendMethod.restore(); - clock.restore(); - }); - - it("timeToLive from expiry must not exceed max TTL", (done) => { - const expiryData = { ...data, expiry: 16000000, timeToLive: 3000 }; - pn.send(regIds, expiryData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications with timeToLive calculated from expiry must not be negative", () => { - const expiredTimestampDate = Math.floor(Date.now() / 1000) - 1; - - before(() => { - sendMethod = sinon.stub( - gcm.Sender.prototype, - "send", - (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(message.params.timeToLive).to.equal(0); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - } - ); - }); - - after(() => { - sendMethod.restore(); - }); - - it("timeToLive from expiry must not exceed max TTL", (done) => { - const expiryData = { ...data, expiry: expiredTimestampDate }; - pn.send(regIds, expiryData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications with neither expiry nor timeToLive given", () => { - before(() => { - sendMethod = sinon.stub( - gcm.Sender.prototype, - "send", - (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(message.params.timeToLive).to.equal(DEFAULT_TTL); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - } - ); - }); - - after(() => { - sendMethod.restore(); - }); - - it("should set the default expiry", (done) => { - pn.send(regIds, data, (err, results) => testSuccess(err, results, done)); - }); - }); - }); - - describe("send silent push notifications", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification).to.be.undefined(); - expect(message.params.priority).to.equal("high"); - expect(message.params.contentAvailable).to.be.true(); - expect(message.params.data.testKey).to.eql("testValue"); - expect(message.params.data.title).to.be.undefined(); - expect(message.params.data.message).to.be.undefined(); - expect(message.params.data.body).to.be.undefined(); - expect(message.params.data.sound).to.be.undefined(); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, custom data undefined)", (done) => { - const silentPushData = { - priority: "high", - silent: true, - custom: { - testKey: "testValue", - }, - }; - pn.send(regIds, silentPushData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("include Android-specific fields, such as channel id, image, style etc.", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.android_channel_id).to.equal("channelId"); - expect(message.params.notification.image).to.equal("imageData"); - expect(message.params.notification.style).to.equal("some-style"); - expect(message.params.notification.picture).to.equal("http://example.com"); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, custom data undefined)", (done) => { - const androidData = { - android_channel_id: "channelId", - image: "imageData", - style: "some-style", - picture: "http://example.com", - }; - pn.send(regIds, androidData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("include fcm_notification option", () => { - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.equal(notificationOptions.title); - expect(message.params.notification.body).to.equal(notificationOptions.body); - expect(message.params.notification.color).to.equal(notificationOptions.color); - expect(message.params.notification.image).to.equal(notificationOptions.image); - - expect(message.params.data.user_id).to.equal(538); - - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback, fcm_notification)", (done) => { - const androidData = { - ...data, - fcm_notification: notificationOptions, - custom: { user_id: 538 }, - }; - pn.send(regIds, androidData, (err, results) => testSuccess(err, results, done)); - }); - }); - - describe("send push notifications in phonegap-push compatibility mode", () => { - const pushPhoneGap = new PN({ - gcm: { - phonegap: true, - }, - isLegacyGCM: true, - }); - - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.an.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification).to.be.undefined(); - expect(message.params.data.sender).to.eql(data.custom.sender); - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.body).to.eql(data.body); - expect(message.params.data.sound).to.eql(data.sound); - expect(message.params.data["content-available"]).to.equal(1); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pushPhoneGap.send(regIds, data, (err, results) => testSuccess(err, results, done)); - }); - - it("all responses should be successful (promise)", (done) => { - pushPhoneGap - .send(regIds, data) - .then((results) => testSuccess(null, results, done)) - .catch(done); - }); - }); - - describe("send push notifications with { recipients: {to}}", () => { - const recipient = "recipientTo"; - const dataRecipientsTo = { ...data, recipients: { to: recipient } }; - const testSuccessRecipientTo = testPushSuccess(method, [recipient]); - - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("to"); - expect(recipients).to.not.have.property("registrationTokens"); - const { to } = recipients; - expect(to).to.be.a("string"); - expect(to).to.equal(recipient); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.data.sender).to.eql(data.custom.sender); - expect(message.params.priority).to.equal("high"); - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.eql(data.body); - expect(message.params.data.sound).to.eql(data.sound); - cb(null, { - multicast_id: "abc", - success: 1, - failure: 0, - results: [ - { - message_id: "", - registration_id: to, - error: null, - }, - ], - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pn.send(regIds, dataRecipientsTo, (err, results) => - testSuccessRecipientTo(err, results, done) - ); - }); - - it("all responses should be successful (promise)", (done) => { - pn.send(regIds, dataRecipientsTo) - .then((results) => testSuccessRecipientTo(null, results, done)) - .catch(done); - }); - }); - - describe("send push notifications with { recipients: {condition}}", () => { - const recipient = "recipientCondition"; - const dataRecipientsCondition = { - ...data, - recipients: { condition: recipient }, - }; - const testSuccessRecipientCondition = testPushSuccess(method, [recipient]); - - before(() => { - sendMethod = sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("condition"); - expect(recipients).to.not.have.property("registrationTokens"); - const { condition } = recipients; - expect(condition).to.be.a("string"); - expect(condition).to.equal(recipient); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.data.sender).to.eql(data.custom.sender); - expect(message.params.priority).to.equal("high"); - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.eql(data.body); - expect(message.params.data.sound).to.eql(data.sound); - cb(null, { - multicast_id: "abc", - success: 1, - failure: 0, - results: [ - { - message_id: "", - registration_id: condition, - error: null, - }, - ], - }); - }); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pn.send(regIds, dataRecipientsCondition, (err, results) => - testSuccessRecipientCondition(err, results, done) - ); - }); - - it("all responses should be successful (promise)", (done) => { - pn.send(regIds, dataRecipientsCondition) - .then((results) => testSuccessRecipientCondition(null, results, done)) - .catch(done); - }); - }); - - describe("send push notifications failure (with response)", () => { - before(() => { - sendMethod = sendFailureMethod1(); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be failed (callback)", (done) => { - pn.send(regIds, data, (err, results) => testError(err, results, done)); - }); - - it("all responses should be failed (promise)", (done) => { - pn.send(regIds, data) - .then((results) => testError(null, results, done)) - .catch(done); - }); - }); - - describe("send push notifications error", () => { - before(() => { - sendMethod = sendErrorMethod(); - }); - - after(() => { - sendMethod.restore(); - }); - - it("the error should be reported (callback)", (done) => { - pn.send(regIds, data, (err, results) => testError(err, results, done)); - }); - - it("the error should be reported (promise)", (done) => { - pn.send(regIds, data) - .then((results) => testError(null, results, done)) - .catch((err) => testError(err, undefined, done)); - }); - }); - - describe("send push notifications failure (without response)", () => { - before(() => { - sendMethod = sendFailureMethod2(); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be failed (callback)", (done) => { - pn.send(regIds, data, (err, results) => testUnknownError(err, results, done)); - }); - - it("all responses should be failed (promise)", (done) => { - pn.send(regIds, data) - .then((results) => testUnknownError(null, results, done)) - .catch(done); - }); - }); - - describe("send push notifications throw exception", () => { - before(() => { - sendMethod = sendThrowExceptionMethod(); - }); - - after(() => { - sendMethod.restore(); - }); - - it("the exception should be catched (callback)", (done) => { - pn.send(regIds, data, (err, results) => testException(err, results, done)).catch(() => {}); // This is to avoid UnhandledPromiseRejectionWarning - }); - - it("the exception should be catched (promise)", (done) => { - pn.send(regIds, data) - .then((results) => testException(null, results, done)) - .catch((err) => testException(err, undefined, done)); - }); - }); -}); diff --git a/test/send/sendWNS-accessToken.js b/test/send/sendWNS-accessToken.js index 3e6006c..fa43718 100644 --- a/test/send/sendWNS-accessToken.js +++ b/test/send/sendWNS-accessToken.js @@ -3,7 +3,7 @@ import { expect } from "chai"; import sinon from "sinon"; import wns from "wns"; import PN from "../../src"; -import { sendOkMethodGCM, testPushSuccess, testPushError, testPushException } from "../util"; +import { testPushSuccess, testPushError, testPushException } from "../util"; const method = "wns"; const regIds = [ @@ -87,7 +87,6 @@ const sendWNS = { }; const testSuccess = testPushSuccess(method, regIds); -const testSuccessGCM = testPushSuccess("gcm", regIds); const testError = testPushError(method, regIds, fErr.message); const testException = testPushException(fErr.message); @@ -206,29 +205,4 @@ describe("push-notifications-wns-access-token", () => { .catch((err) => testException(err, undefined, done)); }); }); - - describe("send push notifications successfully using FCM", () => { - const pnGCM = new PN({ - isAlwaysUseFCM: true, - isLegacyGCM: true, - }); - before(() => { - sendMethod = sendOkMethodGCM(regIds, data); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pnGCM.send(regIds, data, (err, results) => testSuccessGCM(err, results, done)); - }); - - it("all responses should be successful (promise)", (done) => { - pnGCM - .send(regIds, data) - .then((results) => testSuccessGCM(null, results, done)) - .catch(done); - }); - }); }); diff --git a/test/send/sendWNS.js b/test/send/sendWNS.js index ccc37d4..954dcbe 100644 --- a/test/send/sendWNS.js +++ b/test/send/sendWNS.js @@ -3,7 +3,7 @@ import { expect } from "chai"; import sinon from "sinon"; import wns from "wns"; import PN from "../../src"; -import { sendOkMethodGCM, testPushSuccess, testPushError, testPushException } from "../util"; +import { testPushSuccess, testPushError, testPushException } from "../util"; const method = "wns"; const regIds = [ @@ -82,7 +82,6 @@ const sendWNS = { }; const testSuccess = testPushSuccess(method, regIds); -const testSuccessGCM = testPushSuccess("gcm", regIds); const testError = testPushError(method, regIds, fErr.message); const testException = testPushException(fErr.message); @@ -237,29 +236,4 @@ describe("push-notifications-wns", () => { .catch((err) => testException(err, undefined, done)); }); }); - - describe("send push notifications successfully using FCM", () => { - const pnGCM = new PN({ - isAlwaysUseFCM: true, - isLegacyGCM: true, - }); - before(() => { - sendMethod = sendOkMethodGCM(regIds, data); - }); - - after(() => { - sendMethod.restore(); - }); - - it("all responses should be successful (callback)", (done) => { - pnGCM.send(regIds, data, (err, results) => testSuccessGCM(err, results, done)); - }); - - it("all responses should be successful (promise)", (done) => { - pnGCM - .send(regIds, data) - .then((results) => testSuccessGCM(null, results, done)) - .catch(done); - }); - }); }); From f5823d860aab69acdb840006b27d69104d81e783 Mon Sep 17 00:00:00 2001 From: Alexander Friedl Date: Thu, 11 Dec 2025 21:38:32 +0100 Subject: [PATCH 2/5] Require node18 in package.json and as babel target version --- .babelrc | 2 +- .github/copilot-instructions.md | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.babelrc b/.babelrc index 7be671b..1703508 100644 --- a/.babelrc +++ b/.babelrc @@ -4,7 +4,7 @@ "@babel/preset-env", { "targets": { - "node": "14" + "node": "18" } } ] diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 4f5ff8b..b58d2a1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -16,7 +16,7 @@ This repository implements a Node.js module for sending push notifications acros ## Developer Workflows -- **Install:** `npm install` +- **Install:** `npm install` (requires Node.js 18+) - **Test:** Run all tests with `npm test`. Tests are in `test/` and cover basic flows and platform-specific cases. - **Debug:** Use the callback or Promise error/result from `push.send`. Each result object includes method, success/failure counts, and error details per device. - **Build:** No build step required for basic usage. ES6 is used, but compatible with ES5 via Babel if needed. diff --git a/package.json b/package.json index 22bb507..7f1aaad 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "sinon-chai": "3.5.0" }, "engines": { - "node": ">=14.x.x" + "node": ">=18.x.x" }, "eslintConfig": { "ecmaVersion": 6, From a18a74f99a6a940480e284abc8c6dbd9eab613c1 Mon Sep 17 00:00:00 2001 From: Alexander Friedl Date: Thu, 11 Dec 2025 21:40:19 +0100 Subject: [PATCH 3/5] Bump firebase-admin to v13.6.0 --- package-lock.json | 942 +++++++++++++++++++--------------------------- package.json | 2 +- 2 files changed, 379 insertions(+), 565 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d47934..d3d5aa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@parse/node-apn": "7.0.1", - "firebase-admin": "12.1.1", + "firebase-admin": "13.6.0", "node-adm": "0.9.1", "web-push": "3.6.7", "wns": "0.5.4" @@ -36,7 +36,7 @@ "sinon-chai": "3.5.0" }, "engines": { - "node": ">=14.x.x" + "node": ">=18.x.x" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1851,95 +1851,120 @@ } }, "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "engines": { - "node": ">=14" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "license": "MIT" }, "node_modules/@firebase/app-check-interop-types": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", - "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "license": "Apache-2.0" }, "node_modules/@firebase/app-types": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", - "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" }, "node_modules/@firebase/auth-interop-types": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", - "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "license": "Apache-2.0" }, "node_modules/@firebase/component": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.7.tgz", - "integrity": "sha512-baH1AA5zxfaz4O8w0vDwETByrKTQqB5CDjRls79Sa4eAGAoERw4Tnung7XbMl3jbJ4B/dmmtsMrdki0KikwDYA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.0.tgz", + "integrity": "sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==", + "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.9.6", + "@firebase/util": "1.13.0", "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@firebase/database": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.5.tgz", - "integrity": "sha512-cAfwBqMQuW6HbhwI3Cb/gDqZg7aR0OmaJ85WUxlnoYW2Tm4eR0hFl5FEijI3/gYPUiUcUPQvTkGV222VkT7KPw==", - "dependencies": { - "@firebase/app-check-interop-types": "0.3.2", - "@firebase/auth-interop-types": "0.2.3", - "@firebase/component": "0.6.7", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.9.6", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.0.tgz", + "integrity": "sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", "faye-websocket": "0.11.4", "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@firebase/database-compat": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.5.tgz", - "integrity": "sha512-NDSMaDjQ+TZEMDMmzJwlTL05kh1+0Y84C+kVMaOmNOzRGRM7VHi29I6YUhCetXH+/b1Wh4ZZRyp1CuWkd8s6hg==", - "dependencies": { - "@firebase/component": "0.6.7", - "@firebase/database": "1.0.5", - "@firebase/database-types": "1.0.3", - "@firebase/logger": "0.4.2", - "@firebase/util": "1.9.6", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.0.tgz", + "integrity": "sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.0", + "@firebase/database": "1.1.0", + "@firebase/database-types": "1.0.16", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@firebase/database-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.3.tgz", - "integrity": "sha512-39V/Riv2R3O/aUjYKh0xypj7NTNXNAK1bcgY5Kx+hdQPRS/aPTS8/5c0CGFYKgVuFbYlnlnhrCTYsh2uNhGwzA==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.16.tgz", + "integrity": "sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw==", + "license": "Apache-2.0", "dependencies": { - "@firebase/app-types": "0.9.2", - "@firebase/util": "1.9.6" + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.13.0" } }, "node_modules/@firebase/logger": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", - "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", + "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@firebase/util": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.6.tgz", - "integrity": "sha512-IBr1MZbp4d5MjBCXL3TW1dK/PDXX4yOGbiwRNh1oAbE/+ci5Uuvy9KIrsFYY80as1I0iOaD5oOMA9Q8j4TJWcw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.13.0.tgz", + "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", + "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@google-cloud/firestore": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.8.0.tgz", - "integrity": "sha512-m21BWVZLz7H7NF8HZ5hCGUSCEJKNwYB5yzQqDTuE9YUzNDRMDei3BwVDht5k4xF636sGlnobyBL+dcbthSGONg==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz", + "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==", + "license": "Apache-2.0", "optional": true, "dependencies": { + "@opentelemetry/api": "^1.3.0", "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", "google-gax": "^4.3.3", @@ -1953,6 +1978,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "arrify": "^2.0.0", @@ -1966,6 +1992,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "license": "Apache-2.0", "optional": true, "engines": { "node": ">=14.0.0" @@ -1975,24 +2002,26 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "license": "Apache-2.0", "optional": true, "engines": { "node": ">=14" } }, "node_modules/@google-cloud/storage": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.11.2.tgz", - "integrity": "sha512-jJOrKyOdujfrSF8EJODW9yY6hqO4jSTk6eVITEj2gsD43BSXuDlnMlLOaBUQhXL29VGnSkxDgYl5tlFhA6LKSA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.18.0.tgz", + "integrity": "sha512-r3ZwDMiz4nwW6R922Z1pwpePxyRwE5GdevYX63hRmAQUkUQJcBH/79EnQPDv5cOv1mFBgevdNWQfi3tie3dHrQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@google-cloud/paginator": "^5.0.0", "@google-cloud/projectify": "^4.0.0", - "@google-cloud/promisify": "^4.0.0", + "@google-cloud/promisify": "<4.1.0", "abort-controller": "^3.0.0", "async-retry": "^1.3.3", "duplexify": "^4.1.3", - "fast-xml-parser": "^4.3.0", + "fast-xml-parser": "^4.4.1", "gaxios": "^6.0.2", "google-auth-library": "^9.6.3", "html-entities": "^2.5.2", @@ -2010,6 +2039,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", "optional": true, "dependencies": { "yocto-queue": "^0.1.0" @@ -2025,40 +2055,50 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", "optional": true, "bin": { "uuid": "dist/bin/uuid" } }, - "node_modules/@google-cloud/storage/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "license": "Apache-2.0", "optional": true, - "engines": { - "node": ">=10" + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=12.10.0" } }, - "node_modules/@grpc/grpc-js": { - "version": "1.10.9", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.9.tgz", - "integrity": "sha512-5tcgUctCG0qoNyfChZifz2tJqbRbXVO9J7X6duFcOjY3HUNCxg5D0ZCK7EP9vIcZ0zRpLU9bWkyCqVCLZ46IbQ==", + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@grpc/proto-loader": "^0.7.13", - "@js-sdsl/ordered-map": "^4.4.2" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, "engines": { - "node": ">=12.10.0" + "node": ">=6" } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "lodash.camelcase": "^4.3.0", @@ -2725,6 +2765,7 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", "optional": true, "funding": { "type": "opencollective", @@ -2949,6 +2990,16 @@ "@octokit/openapi-types": "^27.0.0" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@parse/node-apn": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-7.0.1.tgz", @@ -3019,30 +3070,35 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", "optional": true, "dependencies": { "@protobufjs/aspromise": "^1.1.1", @@ -3053,30 +3109,35 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/@rtsao/scc": { @@ -3097,6 +3158,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "license": "MIT", "optional": true, "engines": { "node": ">= 10" @@ -3122,6 +3184,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "license": "MIT", "optional": true }, "node_modules/@types/color-name": { @@ -3197,6 +3260,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT", "optional": true }, "node_modules/@types/mime": { @@ -3205,11 +3269,12 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { - "version": "20.14.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.4.tgz", - "integrity": "sha512-1ChboN+57suCT2t/f8lwtPY/k3qTpuD/qnqQuYoBg6OQOcPyaw7PiZVdGpaZYAvhDDtqrt0oAaM8+oSu1xsUGw==", + "version": "22.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz", + "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/@types/parse-path": { @@ -3230,31 +3295,57 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/request": { - "version": "2.48.12", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", - "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "version": "2.48.13", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz", + "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==", + "license": "MIT", "optional": true, "dependencies": { "@types/caseless": "*", "@types/node": "*", "@types/tough-cookie": "*", - "form-data": "^2.5.0" + "form-data": "^2.5.5" } }, "node_modules/@types/request/node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", + "license": "MIT", "optional": true, "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" }, "engines": { "node": ">= 0.12" } }, + "node_modules/@types/request/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -3278,6 +3369,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "license": "MIT", "optional": true }, "node_modules/abort-controller": { @@ -3577,6 +3669,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -3801,10 +3894,10 @@ "license": "Apache-2.0" }, "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "optional": true, + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", "engines": { "node": "*" } @@ -3823,16 +3916,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", @@ -3903,29 +3986,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4071,7 +4131,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4222,11 +4282,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "node_modules/ci-info": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", @@ -4612,31 +4667,6 @@ "node": ">=0.10.0" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -4650,14 +4680,6 @@ "node": ">=6" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4800,14 +4822,6 @@ "dev": true, "license": "MIT" }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, "node_modules/diff": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", @@ -4841,7 +4855,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4856,6 +4870,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", "optional": true, "dependencies": { "end-of-stream": "^1.4.1", @@ -4903,9 +4918,11 @@ "devOptional": true }, "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "optional": true, "dependencies": { "once": "^1.4.0" } @@ -4983,7 +5000,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4993,7 +5010,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 0.4" } @@ -5002,7 +5019,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5015,7 +5032,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5556,14 +5573,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", @@ -5574,8 +5583,7 @@ "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "devOptional": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "node_modules/extsprintf": { "version": "1.4.0", @@ -5585,17 +5593,13 @@ "node >=0.6.0" ] }, - "node_modules/farmhash": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/farmhash/-/farmhash-3.3.1.tgz", - "integrity": "sha512-XUizHanzlr/v7suBr/o85HSakOoWh6HKXZjFYl5C2+Gj0f0rkw+XTUZzrd9odDsgI9G5tRUcF4wSbKaX04T0DQ==", - "hasInstallScript": true, - "dependencies": { - "node-addon-api": "^5.1.0", - "prebuild-install": "^7.1.2" - }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18.0.0" } }, "node_modules/fast-content-type-parse": { @@ -5618,8 +5622,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "devOptional": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.2.0", @@ -5640,22 +5643,19 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "optional": true, "dependencies": { - "strnum": "^1.0.5" + "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" @@ -5665,6 +5665,7 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -5801,52 +5802,43 @@ "node": ">=8" } }, - "node_modules/find-up/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/firebase-admin": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.1.1.tgz", - "integrity": "sha512-Nuoxk//gaYrspS7TvwBINdGvFhh2QeiaWpRW6+PJ+tWyn2/CugBc7jKa1NaBg0AvhGSOXFOCIsXhzCzHA47Rew==", - "dependencies": { - "@fastify/busboy": "^2.1.0", - "@firebase/database-compat": "^1.0.2", - "@firebase/database-types": "^1.0.0", - "@types/node": "^20.10.3", - "farmhash": "^3.3.1", + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.6.0.tgz", + "integrity": "sha512-GdPA/t0+Cq8p1JnjFRBmxRxAGvF/kl2yfdhALl38PrRp325YxyQ5aNaHui0XmaKcKiGRFIJ/EgBNWFoDP0onjw==", + "license": "Apache-2.0", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "fast-deep-equal": "^3.1.1", + "google-auth-library": "^9.14.2", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.1.0", - "long": "^5.2.3", "node-forge": "^1.3.1", - "uuid": "^9.0.0" + "uuid": "^11.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" }, "optionalDependencies": { - "@google-cloud/firestore": "^7.7.0", - "@google-cloud/storage": "^7.7.0" + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" } }, "node_modules/firebase-admin/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/flat": { @@ -5946,11 +5938,6 @@ "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", "dev": true }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, "node_modules/fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -5982,7 +5969,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, + "devOptional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6012,6 +5999,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "license": "MIT", "optional": true }, "node_modules/functions-have-names": { @@ -6025,10 +6013,10 @@ } }, "node_modules/gaxios": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.6.0.tgz", - "integrity": "sha512-bpOZVQV5gthH/jVCSuYuokRo2bTKOcuBiVWpjmTn6C5Agl5zclGfTljuGsQZxwwDBkli+YhZhP4TdlqTnhOezQ==", - "optional": true, + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", @@ -6040,26 +6028,6 @@ "node": ">=14" } }, - "node_modules/gaxios/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/gaxios/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -6068,18 +6036,19 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "optional": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/gcp-metadata": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", - "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", - "optional": true, + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", "dependencies": { - "gaxios": "^6.0.0", + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" }, "engines": { @@ -6141,7 +6110,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -6175,7 +6144,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -6291,11 +6260,6 @@ "git-up": "^8.1.0" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -6361,10 +6325,10 @@ } }, "node_modules/google-auth-library": { - "version": "9.11.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.11.0.tgz", - "integrity": "sha512-epX3ww/mNnhl6tL45EQ/oixsY8JLEgUFoT4A5E/5iAR4esld9Kqv6IJGk7EmGuOgDvaarwF95hU2+v7Irql9lw==", - "optional": true, + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -6381,7 +6345,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "optional": true, + "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -6392,28 +6356,29 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "optional": true, + "license": "MIT", "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "node_modules/google-gax": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.3.6.tgz", - "integrity": "sha512-z3MR+pE6WqU+tnKtkJl4c723EYY7Il4fcSNgEbehzUJpcNWkca9AyoC2pdBWmEa0cda21VRpUBb4s6VSATiUKg==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", + "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@grpc/grpc-js": "~1.10.3", + "@grpc/grpc-js": "^1.10.9", "@grpc/proto-loader": "^0.7.13", "@types/long": "^4.0.0", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", "google-auth-library": "^9.3.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.7.0", "object-hash": "^3.0.0", - "proto3-json-serializer": "^2.0.0", - "protobufjs": "7.3.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", "retry-request": "^7.0.0", "uuid": "^9.0.1" }, @@ -6421,50 +6386,6 @@ "node": ">=14" } }, - "node_modules/google-gax/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/google-gax/node_modules/protobufjs": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.0.tgz", - "integrity": "sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/google-gax/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -6473,16 +6394,26 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "optional": true, "bin": { "uuid": "dist/bin/uuid" } }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6501,7 +6432,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "optional": true, + "license": "MIT", "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" @@ -6514,7 +6445,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "optional": true, + "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -6525,7 +6456,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "optional": true, + "license": "MIT", "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" @@ -6598,7 +6529,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6611,7 +6542,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -6640,7 +6571,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, + "devOptional": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -6658,9 +6589,9 @@ } }, "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", "funding": [ { "type": "github", @@ -6671,6 +6602,7 @@ "url": "https://patreon.com/mdevils" } ], + "license": "MIT", "optional": true }, "node_modules/html-escaper": { @@ -6688,9 +6620,10 @@ } }, "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" }, "node_modules/http-proxy-agent": { "version": "7.0.2", @@ -6765,6 +6698,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -7270,7 +7204,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "devOptional": true, "engines": { "node": ">=8" } @@ -7701,7 +7634,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "optional": true, + "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" } @@ -7898,6 +7831,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT", "optional": true }, "node_modules/lodash.capitalize": { @@ -8035,9 +7969,11 @@ "dev": true }, "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0", + "optional": true }, "node_modules/loupe": { "version": "2.3.7", @@ -8099,7 +8035,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8129,6 +8065,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", "optional": true, "bin": { "mime": "cli.js" @@ -8216,11 +8153,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "node_modules/mocha": { "version": "11.7.5", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", @@ -8432,11 +8364,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -8480,33 +8407,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-abi": { - "version": "3.65.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz", - "integrity": "sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, "node_modules/node-adm": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/node-adm/-/node-adm-0.9.1.tgz", @@ -8515,6 +8415,26 @@ "node": ">= 0.6.0" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-fetch-native": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", @@ -8843,6 +8763,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", "optional": true, "engines": { "node": ">= 6" @@ -8953,6 +8874,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "devOptional": true, "dependencies": { "wrappy": "1" } @@ -9491,31 +9413,6 @@ "node": ">= 0.4" } }, - "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -9580,6 +9477,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "protobufjs": "^7.2.5" @@ -9589,10 +9487,11 @@ } }, "node_modules/protobufjs": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz", - "integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", "hasInstallScript": true, + "license": "BSD-3-Clause", "optional": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -9661,15 +9560,6 @@ "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -9698,33 +9588,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/rc9": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", @@ -9737,9 +9600,11 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10141,6 +10006,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "license": "MIT", "optional": true, "dependencies": { "@types/request": "^2.48.8", @@ -10458,49 +10324,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/sinon": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", @@ -10693,6 +10516,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "license": "MIT", "optional": true, "dependencies": { "stubs": "^3.0.0" @@ -10702,12 +10526,14 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT", "optional": true }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -10716,6 +10542,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true, "funding": [ { "type": "github", @@ -10877,9 +10704,16 @@ } }, "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", "optional": true }, "node_modules/strtok3": { @@ -10904,6 +10738,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT", "optional": true }, "node_modules/supports-color": { @@ -10955,36 +10790,11 @@ "url": "https://opencollective.com/synckit" } }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/teeny-request": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "license": "Apache-2.0", "optional": true, "dependencies": { "http-proxy-agent": "^5.0.0", @@ -11001,6 +10811,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", "optional": true, "dependencies": { "debug": "4" @@ -11013,6 +10824,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", "optional": true, "dependencies": { "@tootallnate/once": "2", @@ -11027,6 +10839,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", "optional": true, "dependencies": { "agent-base": "6", @@ -11036,26 +10849,6 @@ "node": ">= 6" } }, - "node_modules/teeny-request/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/teeny-request/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -11064,6 +10857,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "optional": true, "bin": { "uuid": "dist/bin/uuid" @@ -11246,7 +11040,7 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "optional": true + "license": "MIT" }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -11281,6 +11075,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -11443,9 +11238,10 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", @@ -11575,7 +11371,9 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT", + "optional": true }, "node_modules/util/node_modules/inherits": { "version": "2.0.1", @@ -11647,12 +11445,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "optional": true + "license": "BSD-2-Clause" }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -11666,6 +11465,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", "engines": { "node": ">=0.8.0" } @@ -11674,7 +11474,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "optional": true, + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -11865,7 +11665,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "devOptional": true }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -11987,6 +11788,19 @@ "node": ">=10" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yoctocolors": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", diff --git a/package.json b/package.json index 7f1aaad..c327373 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ }, "dependencies": { "@parse/node-apn": "7.0.1", - "firebase-admin": "12.1.1", + "firebase-admin": "13.6.0", "node-adm": "0.9.1", "web-push": "3.6.7", "wns": "0.5.4" From fe5e4c2892f70627853c653f2ac38626d9b176f4 Mon Sep 17 00:00:00 2001 From: Alexander Friedl Date: Thu, 11 Dec 2025 21:44:53 +0100 Subject: [PATCH 4/5] Update test imports --- test/push-notifications/basic.js | 2 +- test/push-notifications/regIds.js | 10 ++++----- test/send/sendADM.js | 4 ++-- test/send/sendAPN.js | 6 +++--- test/send/sendFCM.js | 4 ++-- test/send/sendWEB.js | 4 ++-- test/send/sendWNS-accessToken.js | 4 ++-- test/send/sendWNS.js | 4 ++-- test/util.js | 36 +------------------------------ 9 files changed, 20 insertions(+), 54 deletions(-) diff --git a/test/push-notifications/basic.js b/test/push-notifications/basic.js index bb1c579..a62a185 100644 --- a/test/push-notifications/basic.js +++ b/test/push-notifications/basic.js @@ -4,7 +4,7 @@ import chai from "chai"; import dirtyChai from "dirty-chai"; import sinonChai from "sinon-chai"; import { spy } from "sinon"; -import PN from "../../src"; +import PN from "../../src/index.js"; import { UNKNOWN_METHOD, WEB_METHOD, diff --git a/test/push-notifications/regIds.js b/test/push-notifications/regIds.js index 537c7ea..4f093be 100644 --- a/test/push-notifications/regIds.js +++ b/test/push-notifications/regIds.js @@ -2,11 +2,11 @@ import { expect } from "chai"; import sinon from "sinon"; -import PN from "../../src"; -import sendFCM from "../../src/sendFCM"; -import sendADM from "../../src/sendADM"; -import sendWNS from "../../src/sendWNS"; -import sendWeb from "../../src/sendWeb"; +import PN from "../../src/index.js"; +import sendFCM from "../../src/sendFCM.js"; +import sendADM from "../../src/sendADM.js"; +import sendWNS from "../../src/sendWNS.js"; +import sendWeb from "../../src/sendWeb.js"; const regIds = [ "APA91bFQCD9Ndd8uVggMhj1usfeWsKIfGyBUWMprpZLGciWrMjS-77bIY24IMQNeEHzjidCcddnDxqYo-UEV03xw6ySmtIgQyzTqhSxhPGAi1maf6KDMAQGuUWc6L5Khze8YK9YrL9I_WD1gl49P3f_9hr08ZAS5Tw", // android diff --git a/test/send/sendADM.js b/test/send/sendADM.js index a96c83e..b111adf 100644 --- a/test/send/sendADM.js +++ b/test/send/sendADM.js @@ -2,8 +2,8 @@ import { expect } from "chai"; import sinon from "sinon"; import adm from "node-adm"; -import PN from "../../src"; -import { testPushSuccess, testPushError, testPushException } from "../util"; +import PN from "../../src/index.js"; +import { testPushSuccess, testPushError, testPushException } from "../util.js"; const method = "adm"; const regIds = [ diff --git a/test/send/sendAPN.js b/test/send/sendAPN.js index a3a98e0..55ed899 100644 --- a/test/send/sendAPN.js +++ b/test/send/sendAPN.js @@ -5,10 +5,10 @@ import sinonChai from "sinon-chai"; import dirtyChai from "dirty-chai"; import apn from "@parse/node-apn"; -import PN from "../../src"; -import APN from "../../src/sendAPN"; +import PN from "../../src/index.js"; +import APN from "../../src/sendAPN.js"; -import { testPushSuccess, testPushError, testPushException } from "../util"; +import { testPushSuccess, testPushError, testPushException } from "../util.js"; // Mock apn certificate loading to prevent file access before(() => { diff --git a/test/send/sendFCM.js b/test/send/sendFCM.js index a7672ca..0cfdb03 100644 --- a/test/send/sendFCM.js +++ b/test/send/sendFCM.js @@ -2,8 +2,8 @@ import { expect } from "chai"; import sinon from "sinon"; import { Messaging as fbMessaging } from "firebase-admin/messaging"; -import PN from "../../src"; -import { testPushSuccess } from "../util"; +import PN from "../../src/index.js"; +import { testPushSuccess } from "../util.js"; const method = "fcm"; const regIds = [ diff --git a/test/send/sendWEB.js b/test/send/sendWEB.js index 5b7542b..752e1ec 100644 --- a/test/send/sendWEB.js +++ b/test/send/sendWEB.js @@ -2,8 +2,8 @@ import { expect } from "chai"; import sinon from "sinon"; import webpush from "web-push"; -import PN from "../../src"; -import { testPushSuccess, testPushError, testPushException } from "../util"; +import PN from "../../src/index.js"; +import { testPushSuccess, testPushError, testPushException } from "../util.js"; const method = "webPush"; const regIds = [ diff --git a/test/send/sendWNS-accessToken.js b/test/send/sendWNS-accessToken.js index fa43718..8ea9fd0 100644 --- a/test/send/sendWNS-accessToken.js +++ b/test/send/sendWNS-accessToken.js @@ -2,8 +2,8 @@ import { expect } from "chai"; import sinon from "sinon"; import wns from "wns"; -import PN from "../../src"; -import { testPushSuccess, testPushError, testPushException } from "../util"; +import PN from "../../src/index.js"; +import { testPushSuccess, testPushError, testPushException } from "../util.js"; const method = "wns"; const regIds = [ diff --git a/test/send/sendWNS.js b/test/send/sendWNS.js index 954dcbe..1c71789 100644 --- a/test/send/sendWNS.js +++ b/test/send/sendWNS.js @@ -2,8 +2,8 @@ import { expect } from "chai"; import sinon from "sinon"; import wns from "wns"; -import PN from "../../src"; -import { testPushSuccess, testPushError, testPushException } from "../util"; +import PN from "../../src/index.js"; +import { testPushSuccess, testPushError, testPushException } from "../util.js"; const method = "wns"; const regIds = [ diff --git a/test/util.js b/test/util.js index fba1927..7f411ae 100644 --- a/test/util.js +++ b/test/util.js @@ -1,5 +1,4 @@ import sinon from "sinon"; -import gcm from "node-gcm"; import { expect } from "chai"; export const testPushSuccess = (method, regIds) => (err, results, done) => { @@ -13,10 +12,6 @@ export const testPushSuccess = (method, regIds) => (err, results, done) => { result.message.forEach((message) => { expect(message).to.have.property("regId"); expect(regIds).to.include(message.regId); - if (method === "gcm") { - expect(message).to.have.property("originalRegId"); - expect(regIds).to.include(message.originalRegId); - } }); }); done(err); @@ -60,33 +55,4 @@ export const testPushException = (errMessage) => (err, results, done) => { } }; -export const sendOkMethodGCM = (regIds, data) => - sinon.stub(gcm.Sender.prototype, "send", (message, recipients, retries, cb) => { - expect(recipients).to.be.instanceOf(Object); - expect(recipients).to.have.property("registrationTokens"); - const { registrationTokens } = recipients; - expect(registrationTokens).to.be.instanceOf(Array); - registrationTokens.forEach((regId) => expect(regIds).to.include(regId)); - expect(retries).to.be.a("number"); - expect(message).to.be.instanceOf(gcm.Message); - expect(message.params.notification.title).to.eql(data.title); - expect(message.params.notification.body).to.eql(data.body); - expect(message.params.notification.sound).to.eql(data.sound); - expect(message.params.data.sender).to.eql(data.custom.sender); - expect(message.params.priority).to.equal("high"); - // This params are duplicated in order to facilitate extraction - // So they are available as `gcm.notification.title` and as `title` - expect(message.params.data.title).to.eql(data.title); - expect(message.params.data.message).to.eql(data.body); - expect(message.params.data.sound).to.eql(data.sound); - cb(null, { - multicast_id: "abc", - success: registrationTokens.length, - failure: 0, - results: registrationTokens.map((token) => ({ - message_id: "", - registration_id: token, - error: null, - })), - }); - }); + From 4282462ae6f8f45ba75135512d29b348d25c380f Mon Sep 17 00:00:00 2001 From: Alexander Friedl Date: Thu, 11 Dec 2025 21:48:30 +0100 Subject: [PATCH 5/5] Fix linter --- README.md | 2 -- test/util.js | 3 --- 2 files changed, 5 deletions(-) diff --git a/README.md b/README.md index bfd1c0a..9f57c59 100644 --- a/README.md +++ b/README.md @@ -303,8 +303,6 @@ push.send(registrationIds, data) ] ``` - - ## APN The following parameters are used to create an APN message: diff --git a/test/util.js b/test/util.js index 7f411ae..7bff1bf 100644 --- a/test/util.js +++ b/test/util.js @@ -1,4 +1,3 @@ -import sinon from "sinon"; import { expect } from "chai"; export const testPushSuccess = (method, regIds) => (err, results, done) => { @@ -54,5 +53,3 @@ export const testPushException = (errMessage) => (err, results, done) => { done(err || e); } }; - -