Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

apns2

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apns2 - npm Package Compare versions

Comparing version 8.5.0 to 9.0.0

.github/workflows/publish.yml

15

.eslintrc.json

@@ -7,16 +7,7 @@ {

},
"extends": [
"eslint:recommended",
"plugin:prettier/recommended"
],
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"parserOptions": {
"ecmaVersion": 2018,
"ecmaVersion": 2019,
"sourceType": "module"
},
"rules": {
"require-atomic-updates": [
0,
"error"
]
}
}
}

@@ -7,2 +7,9 @@ # Change Log

## [9.0.0](https://github.com/AndrewBarba/apns2/releases/tag/9.0.0)
1. Full code cleanup
2. Removes tarn
3. Requires Node v12 or newer
4. Rename `destroy()` to `close()`
## [8.5.0](https://github.com/AndrewBarba/apns2/releases/tag/8.5.0)

@@ -9,0 +16,0 @@

2

index.d.ts

@@ -7,3 +7,3 @@ import { EventEmitter } from "events"

sendMany(notifications: Notification[]): Promise<(Notification | ResponseError)[]>
destroy(): Promise<void>
close(): Promise<void>
}

@@ -10,0 +10,0 @@

const { EventEmitter } = require('events')
const { Pool } = require('tarn')
const jwt = require('jsonwebtoken')

@@ -18,8 +17,2 @@ const Http2Client = require('./http2-client')

* @const
* @desc Number of connections to open up with apns API
*/
const MAX_CONNECTIONS = 10
/**
* @const
* @desc Default host to send request

@@ -61,11 +54,3 @@ */

*/
constructor({
team,
keyId,
signingKey,
defaultTopic = null,
host = HOST,
port = PORT,
connections = MAX_CONNECTIONS
}) {
constructor({ team, keyId, signingKey, defaultTopic = null, host = HOST, port = PORT }) {
if (!team) throw new Error(`team is required`)

@@ -79,3 +64,3 @@ if (!keyId) throw new Error(`keyId is required`)

this._defaultTopic = defaultTopic
this._clients = this._createClientPool({ host, port, connections })
this._client = new Http2Client(host, port)
this._interval = setInterval(() => this._resetSigningToken(), RESET_TOKEN_INTERVAL).unref()

@@ -90,9 +75,4 @@ this.on(Errors.expiredProviderToken, () => this._resetSigningToken())

*/
async send(notifications) {
if (Array.isArray(notifications)) {
console.warn('#send(Array<Notification>) is deprecated. Please use #sendMany()') // eslint-disable-line no-console
return this.sendMany(notifications)
} else {
return this._sendOne(notifications)
}
send(notification) {
return this._sendOne(notification)
}

@@ -105,9 +85,5 @@

*/
async sendMany(notifications) {
let promises = notifications.map(async (notification) => {
try {
return await this._sendOne(notification)
} catch (error) {
return { error }
}
sendMany(notifications) {
const promises = notifications.map((notification) => {
return this._sendOne(notification).catch((error) => ({ error }))
})

@@ -118,7 +94,7 @@ return Promise.all(promises)

/**
* @method destroy
* @method close
* @return {Promise}
*/
async destroy() {
return this._clients.destroy()
close() {
return this._client.close()
}

@@ -132,4 +108,5 @@

*/
async _sendOne(notification) {
let options = {
_sendOne(notification) {
const options = {
method: 'POST',
path: `/${API_VERSION}/device/${encodeURIComponent(notification.deviceToken)}`,

@@ -154,8 +131,8 @@ headers: {

let client = await this._acquireClient()
this._releaseClient(client)
let body = JSON.stringify(notification.APNSOptions())
let res = await client.post(options, body)
return this._handleServerResponse(res, notification)
return this._client
.request({
...options,
body: JSON.stringify(notification.APNSOptions())
})
.then((res) => this._handleServerResponse(res, notification))
}

@@ -165,37 +142,2 @@

* @private
* @method _createClientPool
* @param {String} host
* @param {Number} port
* @return {Pool}
*/
_createClientPool({ host, port, connections }) {
return new Pool({
create: () => new Http2Client(host, port).connect(),
validate: (client) => client.ready,
destroy: (client) => client.destroy(),
min: 0,
max: connections
})
}
/**
* @private
* @method _acquireClient
* @return {Promise}
*/
async _acquireClient() {
return this._clients.acquire().promise
}
/**
* @private
* @method _acquireClient
* @return {Promise}
*/
_releaseClient(client) {
return this._clients.release(client)
}
/**
* @private
* @method _handleServerResponse

@@ -205,3 +147,3 @@ * @param {ServerResponse} res

*/
async _handleServerResponse(res, notification) {
_handleServerResponse(res, notification) {
if (res.statusCode === 200) {

@@ -238,3 +180,3 @@ return notification

let claims = {
const claims = {
iss: this._team,

@@ -244,5 +186,5 @@ iat: parseInt(Date.now() / 1000)

let key = this._signingKey
const key = this._signingKey
let options = {
const options = {
algorithm: SIGNING_ALGORITHM,

@@ -249,0 +191,0 @@ header: {

@@ -1,9 +0,2 @@

const { URL } = require('url')
const http2 = require('http2')
// Check to make sure this is the native http2
if (!http2.constants || !http2.constants.NGHTTP2_SESSION_CLIENT) {
throw new Error('Invalid http2 library, must be running Node v10.16 or later')
}
const {

@@ -25,82 +18,28 @@ HTTP2_HEADER_METHOD,

if (!host) throw new Error('host is required')
this._url = new URL(`https://${host}:${port}`)
this._timeout = timeout
this._ready = false
this._session = null
this._client = http2.connect(`https://${host}:${port}`)
}
/**
* @param {Boolean} ready
* @param {ClientHttp2Session}
*/
get ready() {
return this._ready && this._session && !this._session.destroyed
get client() {
return this._client
}
/**
* @param {Http2Session}
* @param {Number}
*/
get session() {
return this._session
get timeout() {
return this._timeout
}
/**
* @method connect
* @return {Promise}
* @method close
*/
async connect() {
return new Promise((resolve, reject) => {
let session = http2.connect(this._url)
session.once('error', reject)
session.once('socketError', reject)
session.once('connect', () => {
this._connected(session)
resolve(this)
})
})
close() {
this.client.close()
}
/**
* @method destroy
*/
destroy() {
if (this._session && !this._session.destroyed) {
this._session.destroy()
}
this._ready = false
this._session = null
}
/**
* @method get
*/
async get(options) {
options.method = `GET`
return this.request(options)
}
/**
* @method post
*/
async post(options, body) {
options.method = `POST`
return this.request(options, body)
}
/**
* @method put
*/
async put(options, body) {
options.method = `PUT`
return this.request(options, body)
}
/**
* @method delete
*/
async delete(options) {
options.method = `DELETE`
return this.request(options)
}
/**
* @method request

@@ -110,9 +49,9 @@ * @param {Object} options

* @param {String} options.host
* @param {String|Buffer} [body]
* @param {Object} [options.headers]
* @param {String|Buffer} [options.body]
* @return {Promise<ServerResponse>}
*/
async request({ method, path, headers = {} }, body = null) {
request({ method, path, headers = {}, body = null }) {
if (!method) throw new Error('method is required')
if (!path) throw new Error('path is required')
if (!this._session) throw new Error('Must call connect() before making a request')

@@ -123,18 +62,19 @@ return new Promise((resolve, reject) => {

let req = this._session.request(headers)
const req = this.client.request(headers)
// Cancel request after timeout
req.setTimeout(this._timeout, () => req.close(NGHTTP2_CANCEL))
req.setTimeout(this.timeout, () => {
req.close(NGHTTP2_CANCEL)
reject(new Error(`http2: timeout ${method} ${path}`))
})
// Response handling
req.on('response', (headers) => {
let body = ''
req.on('data', (chunk) => (body += chunk))
let data = ''
req.on('data', (chunk) => (data += chunk))
req.on('end', () => {
resolve({
statusCode: headers[HTTP2_HEADER_STATUS],
headers,
body
headers: headers,
body: data
})

@@ -146,3 +86,2 @@ })

req.on('error', reject)
req.on('timeout', () => reject(new Error(`http2: timeout connecting to ${this._url}`)))

@@ -157,19 +96,4 @@ // Post body

}
/**
* @private
* @method _connected
* @param {Http2Session} session
*/
_connected(session) {
session.on('close', () => this.destroy())
session.on('frameError', () => this.destroy())
session.on('goaway', () => this.destroy())
session.on('socketError', () => this.destroy())
session.on('timeout', () => this.destroy())
this._session = session
this._ready = true
}
}
module.exports = HTTP2Client
{
"name": "apns2",
"version": "8.5.0",
"version": "9.0.0",
"description": "Node client for connecting to Apple's Push Notification Service using the new HTTP/2 protocol with JSON web tokens.",
"author": "Andrew Barba <abarba.77@gmail.com>",
"author": "Andrew Barba <barba@hey.com>",
"main": "lib/apns.js",

@@ -10,3 +10,3 @@ "types": "index.d.ts",

"engines": {
"node": ">=10.16.0"
"node": ">=12.14.0"
},

@@ -17,12 +17,12 @@ "repository": {

"dependencies": {
"jsonwebtoken": "^8.5.1",
"tarn": "^3.0.0"
"jsonwebtoken": "^8.5.1"
},
"devDependencies": {
"eslint": "^7.1.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"husky": "^4.0.4",
"mocha": "^7.2.0",
"prettier": "^2.0.5",
"dotenv": "^8.2.0",
"eslint": "^7.12.1",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.3.0",
"mocha": "^8.2.0",
"prettier": "^2.1.2",
"should": "^13.2.3"

@@ -29,0 +29,0 @@ },

# APNS2
[![wercker status](https://app.wercker.com/status/0e705662e5c35d51a971764fe3e27814/s/master 'wercker status')](https://app.wercker.com/project/byKey/0e705662e5c35d51a971764fe3e27814)
[![npm version](https://badge.fury.io/js/apns2.svg)](https://badge.fury.io/js/apns2)

@@ -9,4 +8,2 @@ [![Twitter](https://img.shields.io/badge/twitter-@andrew_barba-blue.svg?style=flat)](http://twitter.com/andrew_barba)

> Now uses the native `http2` module in Node.js v10.16 or later
---

@@ -21,3 +18,3 @@

let client = new APNS({
const client = new APNS({
team: `TFLP87PW54`,

@@ -39,3 +36,3 @@ keyId: `123ABC456`,

let bn = new BasicNotification(deviceToken, 'Hello, World')
const bn = new BasicNotification(deviceToken, 'Hello, World')

@@ -54,3 +51,3 @@ try {

let bn = new BasicNotification(deviceToken, 'Hello, World', {
const bn = new BasicNotification(deviceToken, 'Hello, World', {
badge: 4,

@@ -76,3 +73,3 @@ data: {

let sn = new SilentNotification(deviceToken)
const sn = new SilentNotification(deviceToken)

@@ -95,3 +92,3 @@ try {

let notifications = [
const notifications = [
new BasicNotification(deviceToken1, 'Hello, World'),

@@ -115,3 +112,3 @@ new BasicNotification(deviceToken2, 'Hello, World')

let notification = new Notification(deviceToken, {
const notification = new Notification(deviceToken, {
aps: { ... }

@@ -151,3 +148,3 @@ })

## Destroy
## Close Connections

@@ -157,6 +154,6 @@ If you need to close connections to Apple's APNS servers in order to allow the Node process to exit, you can tear down the APNS client:

```javascript
await client.destroy()
await client.close()
```
Once a client is destroyed you will not be able to use it again. Instead you should instantiate a new client with `new APNS()`.
Once a client is closed you will not be able to use it again. Instead you should instantiate a new client with `new APNS()`.

@@ -168,3 +165,3 @@ ## Environments

```javascript
let client = new APNS({
const client = new APNS({
host: 'api.push.apple.com',

@@ -179,3 +176,3 @@ port: 443,

```javascript
let client = new APNS({
const client = new APNS({
host: 'api.development.push.apple.com'

@@ -188,2 +185,2 @@ ...

`apns2` requires Node.js v10.16 or later
`apns2` requires Node.js v12.14 or later

@@ -1,5 +0,2 @@

const fs = require('fs')
const should = require('should')
// Package
const HTTP2Client = require('../lib/http2-client')

@@ -9,12 +6,11 @@ const { APNS, Notification, BasicNotification, SilentNotification, Errors } = require('../')

describe('http2', () => {
describe('success', () => {
describe('client', () => {
let client
before(async () => {
before(() => {
client = new HTTP2Client('www.google.com', 443)
return client.connect()
})
it('should make a get request', async () => {
let res = await client.get({ path: '/' })
const res = await client.request({ method: 'GET', path: '/' })
res.statusCode.should.equal(200)

@@ -24,27 +20,10 @@ })

it('should make a post request', async () => {
let res = await client.post({ path: '/' })
const res = await client.request({ method: 'POST', path: '/' })
res.statusCode.should.equal(405)
})
})
describe('error', () => {
let client
before(() => {
client = new HTTP2Client('bogus.google.com', 443, { timeout: 500 })
})
it('should not connect', async () => {
try {
await client.connect()
throw new Error('Should not have worked')
} catch (err) {
should.exist(err)
}
})
})
})
describe('apns', () => {
let deviceToken = `5ab4be4b2e511acfc64405be02a9544295f29b6157b75e3fbc5b2f5300eeda45`
const deviceToken = process.env.APNS_PUSH_TOKEN

@@ -58,5 +37,3 @@ describe('signing token', () => {

keyId: `7U6GT5Q49J`,
signingKey: process.env.APNS_SIGNING_KEY
? process.env.APNS_SIGNING_KEY.replace(/\\n/gi, '\n')
: fs.readFileSync(`${__dirname}/certs/token.p8`, 'utf8'),
signingKey: process.env.APNS_SIGNING_KEY,
defaultTopic: `com.tablelist.Tablelist`

@@ -67,3 +44,3 @@ })

it('should send a basic notification', async () => {
let basicNotification = new BasicNotification(deviceToken, `Hello, Basic`)
const basicNotification = new BasicNotification(deviceToken, `Hello, Basic`)
return apns.send(basicNotification)

@@ -73,3 +50,3 @@ })

it('should send a basic notification with options', async () => {
let basicNotification = new BasicNotification(deviceToken, `Hello, 1`, {
const basicNotification = new BasicNotification(deviceToken, `Hello, 1`, {
badge: 1

@@ -81,3 +58,3 @@ })

it('should send a basic notification with additional data', async () => {
let basicNotification = new BasicNotification(deviceToken, `Hello, ICON`, {
const basicNotification = new BasicNotification(deviceToken, `Hello, ICON`, {
badge: 0,

@@ -92,3 +69,3 @@ data: {

it('should send a silent notification', async () => {
let silentNotification = new SilentNotification(deviceToken)
const silentNotification = new SilentNotification(deviceToken)
return apns.send(silentNotification)

@@ -98,3 +75,3 @@ })

it('should send a notification', async () => {
let notification = new Notification(deviceToken, {
const notification = new Notification(deviceToken, {
aps: {

@@ -110,3 +87,3 @@ alert: {

it('should send a notification with a thread-id', async () => {
let notification = new Notification(deviceToken, {
const notification = new Notification(deviceToken, {
aps: {

@@ -123,5 +100,5 @@ alert: {

it('should send both notifications', async () => {
let basicNotification = new BasicNotification(deviceToken, `Hello, Multiple`)
let silentNotification = new SilentNotification(deviceToken)
let results = await apns.sendMany([basicNotification, silentNotification])
const basicNotification = new BasicNotification(deviceToken, `Hello, Multiple`)
const silentNotification = new SilentNotification(deviceToken)
const results = await apns.sendMany([basicNotification, silentNotification])
should.exist(results)

@@ -132,7 +109,7 @@ results.length.should.equal(2)

it('should send a lot of notifications', async () => {
let notifications = []
const notifications = []
for (let i = 0; i < 500; i++) {
notifications.push(new BasicNotification(deviceToken, `Hello #${i}`))
}
let results = await apns.sendMany(notifications)
const results = await apns.sendMany(notifications)
should.exist(results)

@@ -143,3 +120,3 @@ results.length.should.equal(notifications.length)

it('should fail to send a notification', async () => {
let noti = new BasicNotification(`fakedevicetoken`, `Hello, bad token`)
const noti = new BasicNotification(`fakedevicetoken`, `Hello, bad token`)
try {

@@ -161,3 +138,3 @@ await apns.send(noti)

let noti = new BasicNotification(`fakedevicetoken`, `Hello, bad token`)
const noti = new BasicNotification(`fakedevicetoken`, `Hello, bad token`)
apns.send(noti).catch(should.exist)

@@ -173,3 +150,3 @@ })

let noti = new BasicNotification(`fakedevicetoken`, `Hello, bad token`)
const noti = new BasicNotification(`fakedevicetoken`, `Hello, bad token`)
apns.send(noti).catch(should.exist)

@@ -176,0 +153,0 @@ })

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc