Comparing version 8.4.0 to 8.5.0
@@ -1,3 +0,2 @@ | ||
Change Log | ||
========== | ||
# Change Log | ||
@@ -8,2 +7,8 @@ `apns2` follows [Semantic Versioning](http://semver.org/) | ||
## [8.5.0](https://github.com/AndrewBarba/apns2/releases/tag/8.5.0) | ||
1. Fix TypeScript typings | ||
2. New push types and error constants | ||
3. Add prettier | ||
## [8.4.0](https://github.com/AndrewBarba/apns2/releases/tag/8.4.0) | ||
@@ -10,0 +15,0 @@ |
@@ -5,5 +5,5 @@ import { EventEmitter } from "events" | ||
constructor(options: APNSOptions) | ||
send(notification: Notification): Promise<Notification> | ||
sendMany(notifications: Notification[]): Promise<Notification[]> | ||
destroy(): Promise<Void> | ||
send(notification: Notification): Promise<Notification | ResponseError> | ||
sendMany(notifications: Notification[]): Promise<(Notification | ResponseError)[]> | ||
destroy(): Promise<void> | ||
} | ||
@@ -31,2 +31,10 @@ | ||
type ResponseError = { | ||
error: { | ||
reason: string; | ||
statusCode: number; | ||
notification: Notification; | ||
}; | ||
} | ||
export const Errors: { | ||
@@ -50,5 +58,7 @@ badCertificate: string | ||
invalidProviderToken: string | ||
invalidPushType: string | ||
invalidSigningKey: string | ||
methodNotAllowed: string | ||
missingDeviceToken: string | ||
missingProviderToken: string | ||
missingTopic: string | ||
@@ -59,2 +69,3 @@ payloadEmpty: string | ||
shutdown: string | ||
tooManyProviderTokenUpdates: string | ||
tooManyRequests: string | ||
@@ -77,3 +88,15 @@ topicDisallowed: string | ||
declare interface NotificationOptions { | ||
alert?: string | any; | ||
alert?: string | { | ||
title?: string; | ||
subtitle?: string; | ||
body: string; | ||
'title-loc-key'?: string; | ||
'title-loc-args'?: string[]; | ||
'subtitle-loc-key'?: string; | ||
'subtitle-loc-args'?: string[]; | ||
'loc-key'?: string; | ||
'loc-args'?: string[]; | ||
'action-loc-key'?: string; | ||
'launch-image'?: string; | ||
}; | ||
aps?: any; | ||
@@ -84,6 +107,6 @@ badge?: number; | ||
contentAvailable?: boolean; | ||
data?: any; | ||
data?: { [key: string]: any; }; | ||
expiration?: number; | ||
priority?: string; | ||
pushType?: string; | ||
priority?: number; | ||
pushType?: keyof PushType; | ||
sound?: string; | ||
@@ -103,2 +126,5 @@ threadId?: string; | ||
voip: string | ||
complication: string | ||
fileprovider: string | ||
mdm: string | ||
} |
@@ -50,3 +50,2 @@ const { EventEmitter } = require('events') | ||
class APNS extends EventEmitter { | ||
/** | ||
@@ -62,3 +61,11 @@ * @constructor | ||
*/ | ||
constructor({ team, keyId, signingKey, defaultTopic=null, host=HOST, port=PORT, connections=MAX_CONNECTIONS }) { | ||
constructor({ | ||
team, | ||
keyId, | ||
signingKey, | ||
defaultTopic = null, | ||
host = HOST, | ||
port = PORT, | ||
connections = MAX_CONNECTIONS | ||
}) { | ||
if (!team) throw new Error(`team is required`) | ||
@@ -97,3 +104,3 @@ if (!keyId) throw new Error(`keyId is required`) | ||
async sendMany(notifications) { | ||
let promises = notifications.map(async notification => { | ||
let promises = notifications.map(async (notification) => { | ||
try { | ||
@@ -126,3 +133,3 @@ return await this._sendOne(notification) | ||
headers: { | ||
'authorization': `bearer ${this._getSigningToken()}`, | ||
authorization: `bearer ${this._getSigningToken()}`, | ||
'apns-push-type': notification.pushType, | ||
@@ -135,6 +142,5 @@ 'apns-priority': notification.priority, | ||
if (notification.expiration) { | ||
options.headers['apns-expiration'] = | ||
notification.expiration.getTime | ||
? parseInt(notification.expiration.getTime() / 1000) | ||
: parseInt(notification.expiration) | ||
options.headers['apns-expiration'] = notification.expiration.getTime | ||
? parseInt(notification.expiration.getTime() / 1000) | ||
: parseInt(notification.expiration) | ||
} | ||
@@ -164,4 +170,4 @@ | ||
create: () => new Http2Client(host, port).connect(), | ||
validate: client => client.ready, | ||
destroy: client => client.destroy(), | ||
validate: (client) => client.ready, | ||
destroy: (client) => client.destroy(), | ||
min: 0, | ||
@@ -205,3 +211,3 @@ max: connections | ||
json = JSON.parse(res.body) | ||
} catch(err) { | ||
} catch (err) { | ||
json = { reason: Errors.unknownError } | ||
@@ -248,3 +254,3 @@ } | ||
token = jwt.sign(claims, key, options) | ||
} catch(err) { | ||
} catch (err) { | ||
token = null | ||
@@ -251,0 +257,0 @@ this.emit(Errors.invalidSigningKey) |
module.exports = { | ||
badCertificate: "BadCertificate", | ||
badCertificateEnvironment: "BadCertificateEnvironment", | ||
badCollapseId: "BadCollapseId", | ||
badDeviceToken: "BadDeviceToken", | ||
badExpirationDate: "BadExpirationDate", | ||
badMessageId: "BadMessageId", | ||
badPath: "BadPath", | ||
badPriority: "BadPriority", | ||
badTopic: "BadTopic", | ||
deviceTokenNotForTopic: "DeviceTokenNotForTopic", | ||
duplicateHeaders: "DuplicateHeaders", | ||
error: "Error", | ||
expiredProviderToken: "ExpiredProviderToken", | ||
forbidden: "Forbidden", | ||
idleTimeout: "IdleTimeout", | ||
internalServerError: "InternalServerError", | ||
invalidProviderToken: "InvalidProviderToken", | ||
invalidSigningKey: "InvalidSigningKey", | ||
methodNotAllowed: "MethodNotAllowed", | ||
missingDeviceToken: "MissingDeviceToken", | ||
missingTopic: "MissingTopic", | ||
payloadEmpty: "PayloadEmpty", | ||
payloadTooLarge: "PayloadTooLarge", | ||
serviceUnavailable: "ServiceUnavailable", | ||
shutdown: "Shutdown", | ||
tooManyRequests: "TooManyRequests", | ||
topicDisallowed: "TopicDisallowed", | ||
unknownError: "UnknownError", | ||
unregistered: "Unregistered" | ||
badCertificate: 'BadCertificate', | ||
badCertificateEnvironment: 'BadCertificateEnvironment', | ||
badCollapseId: 'BadCollapseId', | ||
badDeviceToken: 'BadDeviceToken', | ||
badExpirationDate: 'BadExpirationDate', | ||
badMessageId: 'BadMessageId', | ||
badPath: 'BadPath', | ||
badPriority: 'BadPriority', | ||
badTopic: 'BadTopic', | ||
deviceTokenNotForTopic: 'DeviceTokenNotForTopic', | ||
duplicateHeaders: 'DuplicateHeaders', | ||
error: 'Error', | ||
expiredProviderToken: 'ExpiredProviderToken', | ||
forbidden: 'Forbidden', | ||
idleTimeout: 'IdleTimeout', | ||
internalServerError: 'InternalServerError', | ||
invalidProviderToken: 'InvalidProviderToken', | ||
invalidPushType: 'InvalidPushType', | ||
invalidSigningKey: 'InvalidSigningKey', | ||
methodNotAllowed: 'MethodNotAllowed', | ||
missingDeviceToken: 'MissingDeviceToken', | ||
missingProviderToken: 'MissingProviderToken', | ||
missingTopic: 'MissingTopic', | ||
payloadEmpty: 'PayloadEmpty', | ||
payloadTooLarge: 'PayloadTooLarge', | ||
serviceUnavailable: 'ServiceUnavailable', | ||
shutdown: 'Shutdown', | ||
tooManyProviderTokenUpdates: 'TooManyProviderTokenUpdates', | ||
tooManyRequests: 'TooManyRequests', | ||
topicDisallowed: 'TopicDisallowed', | ||
unknownError: 'UnknownError', | ||
unregistered: 'Unregistered' | ||
} |
@@ -20,7 +20,6 @@ const { URL } = require('url') | ||
class HTTP2Client { | ||
/** | ||
* @constructor | ||
*/ | ||
constructor(host, port=443, { timeout=5000 }={}) { | ||
constructor(host, port = 443, { timeout = 5000 } = {}) { | ||
if (!host) throw new Error('host is required') | ||
@@ -114,3 +113,3 @@ this._url = new URL(`https://${host}:${port}`) | ||
*/ | ||
async request({ method, path, headers={} }, body=null) { | ||
async request({ method, path, headers = {} }, body = null) { | ||
if (!method) throw new Error('method is required') | ||
@@ -130,6 +129,6 @@ if (!path) throw new Error('path is required') | ||
// Response handling | ||
req.on('response', headers => { | ||
req.on('response', (headers) => { | ||
let body = '' | ||
req.on('data', chunk => body += chunk) | ||
req.on('data', (chunk) => (body += chunk)) | ||
@@ -136,0 +135,0 @@ req.on('end', () => { |
@@ -7,3 +7,2 @@ const Notification = require('./notification') | ||
class BasicNotification extends Notification { | ||
/** | ||
@@ -10,0 +9,0 @@ * @constructor |
module.exports = Object.freeze({ | ||
alert: 'alert', | ||
background: 'background', | ||
voip: 'voip' | ||
voip: 'voip', | ||
complication: 'complication', | ||
fileprovider: 'fileprovider', | ||
mdm: 'mdm' | ||
}) |
@@ -8,3 +8,2 @@ const pushType = require('./constants/push-type') | ||
class Notification { | ||
/** | ||
@@ -98,3 +97,2 @@ * @static | ||
APNSOptions() { | ||
let result = { | ||
@@ -101,0 +99,0 @@ aps: this._options.aps || {} |
@@ -9,3 +9,2 @@ const Notification = require('./notification') | ||
class SilentNotification extends Notification { | ||
/** | ||
@@ -12,0 +11,0 @@ * @constructor |
{ | ||
"name": "apns2", | ||
"version": "8.4.0", | ||
"version": "8.5.0", | ||
"description": "Node client for connecting to Apple's Push Notification Service using the new HTTP/2 protocol with JSON web tokens.", | ||
@@ -20,10 +20,19 @@ "author": "Andrew Barba <abarba.77@gmail.com>", | ||
"devDependencies": { | ||
"eslint": "^6.8.0", | ||
"mocha": "^7.0.1", | ||
"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", | ||
"should": "^13.2.3" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "eslint . --fix" | ||
} | ||
}, | ||
"scripts": { | ||
"lint": "npx eslint ./", | ||
"test": "npx mocha --exit --bail --slow 1000 --timeout 5000 ./test/test.js" | ||
"lint": "eslint .", | ||
"test": "mocha --exit --bail --slow 1000 --timeout 5000 ./test/test.js" | ||
} | ||
} |
@@ -1,5 +0,4 @@ | ||
APNS2 | ||
===== | ||
# APNS2 | ||
[![wercker status](https://app.wercker.com/status/0e705662e5c35d51a971764fe3e27814/s/master "wercker status")](https://app.wercker.com/project/byKey/0e705662e5c35d51a971764fe3e27814) | ||
[![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) | ||
@@ -42,3 +41,3 @@ [![Twitter](https://img.shields.io/badge/twitter-@andrew_barba-blue.svg?style=flat)](http://twitter.com/andrew_barba) | ||
await client.send(bn) | ||
} catch(err) { | ||
} catch (err) { | ||
console.error(err.reason) | ||
@@ -62,3 +61,3 @@ } | ||
await client.send(bn) | ||
} catch(err) { | ||
} catch (err) { | ||
console.error(err.reason) | ||
@@ -79,3 +78,3 @@ } | ||
await client.send(sn) | ||
} catch(err) { | ||
} catch (err) { | ||
console.error(err.reason) | ||
@@ -85,3 +84,3 @@ } | ||
Note: [Apple recommends](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW8) that no options other than the `content-available` flag be sent in order for a notification to truly be silent and wake up your app in the background. Therefore this class does not accept any additional options in the constructor. | ||
Note: [Apple recommends](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app#2980040) that no options other than the `content-available` flag be sent in order for a notification to truly be silent and wake up your app in the background. Therefore this class does not accept any additional options in the constructor. | ||
@@ -102,3 +101,3 @@ #### Many | ||
await client.sendMany(notifications) | ||
} catch(err) { | ||
} catch (err) { | ||
console.error(err.reason) | ||
@@ -126,7 +125,7 @@ } | ||
Available options can be found at [APNS Payload Options](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1) | ||
Available options can be found at [APNS Payload Options](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification#2943363) | ||
## Error Handling | ||
All errors are defined in `./lib/errors.js` and come directly from [APNS Table 8-6](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW17) | ||
All errors are defined in `./lib/errors.js` and come directly from [APNS Table 4](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/handling_notification_responses_from_apns#3394535) | ||
@@ -139,3 +138,3 @@ You can easily listen for these errors by attaching an error handler to the APNS client: | ||
// Listen for a specific error | ||
client.on(Errors.badDeviceToken, err => { | ||
client.on(Errors.badDeviceToken, (err) => { | ||
// Handle accordingly... | ||
@@ -147,3 +146,3 @@ // Perhaps delete token from your database | ||
// Listen for any error | ||
client.on(Errors.error, err => { | ||
client.on(Errors.error, (err) => { | ||
console.error(err.reason, err.statusCode, err.notification.deviceToken) | ||
@@ -153,2 +152,12 @@ }) | ||
## Destroy | ||
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() | ||
``` | ||
Once a client is destroyed you will not be able to use it again. Instead you should instantiate a new client with `new APNS()`. | ||
## Environments | ||
@@ -155,0 +164,0 @@ |
@@ -6,12 +6,5 @@ const fs = require('fs') | ||
const HTTP2Client = require('../lib/http2-client') | ||
const { | ||
APNS, | ||
Notification, | ||
BasicNotification, | ||
SilentNotification, | ||
Errors | ||
} = require('../') | ||
const { APNS, Notification, BasicNotification, SilentNotification, Errors } = require('../') | ||
describe('http2', () => { | ||
describe('success', () => { | ||
@@ -47,3 +40,3 @@ let client | ||
throw new Error('Should not have worked') | ||
} catch(err) { | ||
} catch (err) { | ||
should.exist(err) | ||
@@ -56,7 +49,5 @@ } | ||
describe('apns', () => { | ||
let deviceToken = `5ab4be4b2e511acfc64405be02a9544295f29b6157b75e3fbc5b2f5300eeda45` | ||
describe('signing token', () => { | ||
let apns | ||
@@ -68,6 +59,5 @@ | ||
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 | ||
? process.env.APNS_SIGNING_KEY.replace(/\\n/gi, '\n') | ||
: fs.readFileSync(`${__dirname}/certs/token.p8`, 'utf8'), | ||
defaultTopic: `com.tablelist.Tablelist` | ||
@@ -150,3 +140,3 @@ }) | ||
throw new Error('Should not have sent notification') | ||
} catch(err) { | ||
} catch (err) { | ||
should.exist(err) | ||
@@ -157,4 +147,4 @@ err.reason.should.equal(Errors.badDeviceToken) | ||
it('should fail to send a notification and emit an error', done => { | ||
apns.once(Errors.error, err => { | ||
it('should fail to send a notification and emit an error', (done) => { | ||
apns.once(Errors.error, (err) => { | ||
should.exist(err) | ||
@@ -169,4 +159,4 @@ err.reason.should.equal(Errors.badDeviceToken) | ||
it('should fail to send a notification and emit an error', done => { | ||
apns.once(Errors.badDeviceToken, err => { | ||
it('should fail to send a notification and emit an error', (done) => { | ||
apns.once(Errors.badDeviceToken, (err) => { | ||
should.exist(err) | ||
@@ -173,0 +163,0 @@ err.reason.should.equal(Errors.badDeviceToken) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33378
17
876
178
7