Add types for web-push (#28910)

This commit is contained in:
Paul Lessing
2018-09-17 01:06:02 -07:00
committed by Ryan Cavanaugh
parent 3fdd79cae9
commit 5a12a3f10f
4 changed files with 554 additions and 0 deletions
+227
View File
@@ -0,0 +1,227 @@
// Type definitions for web-push 3.3
// Project: https://github.com/web-push-libs/web-push
// Definitions by: Paul Lessing <https://github.com/paullessing>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.6
/// <reference types="node" />
/**
* To send a push notification call this method with a subscription, optional payload and any options.
*
* Note: you don't need to define a payload, and this method will work without a GCM API Key and / or VAPID keys if the push service supports it.
*
* @param subscription The PushSubscription you wish to send the notification to.
* @param payload The payload you wish to send to the the user.
* @param options Options for the GCM API key and vapid keys can be passed in if they are unique for each notification you wish to send.
*
* @throws WebPushError if the upstream push endpoint returns a non-200 status code.
* @returns This method returns a Promise which resolves if the sending of the notification was successful, otherwise it rejects.
*
* @see SendResult
* @see WebPushError
* @see https://github.com/web-push-libs/web-push#sendnotificationpushsubscription-payload-options
*/
export function sendNotification(subscription: PushSubscription, payload?: string | Buffer | null, options?: RequestOptions): Promise<SendResult>;
/**
* Generate VAPID keys.
*
* @returns Returns an object with publicKey and privateKey values which are URL Safe Base64 encoded strings.
*
* Note: You should create these keys once, store them and use them for all future messages you send.
*/
export function generateVAPIDKeys(): VapidKeys;
/**
* This method expects the GCM API key that is linked to the gcm_sender_id in your web app manifest.
* You can use a GCM API Key from the Google Developer Console or the Cloud Messaging tab under a Firebase Project.
*
* @param apiKey An API key, or null.
*/
export function setGCMAPIKey(apiKey: string | null): void;
/**
* Encrypts the payload according to the Message Encryption for Web Push standard.
*
* (sendNotification will automatically encrypt the payload for you, so if you use sendNotification() you don't need to worry about it).
*
* @param userPublicKey The public key of the receiver (from the browser).
* @param userAuth The auth secret of the receiver (from the browser).
* @param payload The message to attach to the notification.
* @param contentEncoding The type of content encoding to use (e.g. aesgcm or aes128gcm).
*
* @returns This method returns an object with the following fields:
* localPublicKey: The public key matched the private key used during encryption.
* salt: A string representing the salt used to encrypt the payload.
* cipherText: The encrypted payload as a Buffer.
*
* @see ContentEncoding
* @see https://github.com/web-push-libs/web-push#encryptuserpublickey-userauth-payload-contentencoding
*/
export function encrypt(userPublicKey: string, userAuth: string, payload: string | Buffer, contentEncoding: ContentEncoding): EncryptionResult;
/**
* This method takes the required VAPID parameters and returns the required
* header to be added to a Web Push Protocol Request.
* @param audience This must be the origin of the push service.
* @param subject This should be a URL or a 'mailto:' email address.
* @param publicKey The VAPID public key.
* @param privateKey The VAPID private key.
* @param contentEncoding The contentEncoding type.
* @param [expiration] The expiration of the VAPID JWT.
* @returns Returns an Object with the Authorization and 'Crypto-Key' values to be used as headers.
*/
export function getVapidHeaders(
audience: string, subject: string, publicKey: string, privateKey: string, contentEncoding: 'aes128gcm', expiration?: number
): {
Authorization: string;
};
export function getVapidHeaders(
audience: string, subject: string, publicKey: string, privateKey: string, contentEncoding: 'aesgcm', expiration?: number
): {
Authorization: string;
'Crypto-Key': string;
};
export function getVapidHeaders(
audience: string, subject: string, publicKey: string, privateKey: string, contentEncoding: ContentEncoding, expiration?: number
): {
Authorization: string;
'Crypto-Key'?: string;
};
/**
* Note: When calling generateRequestDetails() the payload argument does not need to be defined, passing in null
* will return no body and exclude any unnecessary headers.
* Headers related to the GCM API Key and / or VAPID keys will be included if supplied and required.
*
* @param subscription must be an object containing the details for a push subscription.
* The expected format is the same output as JSON.stringify'ing a PushSubscription in the browser.
* @param payload The payload is optional, but if set, will be encrypted and a Buffer will be returned via the payload parameter.
* This argument must be either a string or a node Buffer.
* Note: In order to encrypt the payload, the pushSubscription must have a keys object with p256dh and auth values.
* @param options Optional argument, none of its parameters are required.
*
* @returns An object containing all the details needed to make a network request.
*
* @see PushSubscription
* @see RequestDetails
* @see https://github.com/web-push-libs/web-push#generaterequestdetailspushsubscription-payload-options
*/
export function generateRequestDetails(subscription: PushSubscription, payload?: null, options?: RequestOptions): RequestDetails & { body: null };
export function generateRequestDetails(subscription: PushSubscription, payload?: string | Buffer, options?: RequestOptions): RequestDetails & { body: Buffer };
export function generateRequestDetails(subscription: PushSubscription, payload?: string | Buffer, options?: RequestOptions): RequestDetails;
/**
* Valid content encodings used by encrypt(), getVapidHeaders(), generateRequestDetails() and sendNotification().
*/
export type ContentEncoding = 'aesgcm' | 'aes128gcm';
/**
* Map of valid content encodings.
*/
export const supportedContentEncodings: {
readonly AES_GCM: 'aesgcm' & ContentEncoding;
readonly AES_128_GCM: 'aws128gcm' & ContentEncoding;
};
/**
* Returned by encrypt() on success.
*/
export interface EncryptionResult {
localPublicKey: string;
salt: string;
cipherText: Buffer;
}
/**
* A private/public key pair for use with VAPID methods.
*/
export interface VapidKeys {
publicKey: string;
privateKey: string;
}
/**
* When making requests where you want to define VAPID details, call this method before sendNotification()
* or pass in the details and options to sendNotification.
*
* @param subject This must be either a URL or a 'mailto:' address.
* For example: 'https://my-site.com/contact' or 'mailto: contact@my-site.com'
* @param publicKey The public VAPID key.
* @param privateKey The private VAPID key.
*/
export function setVapidDetails(subject: string, publicKey: string, privateKey: string): void;
/**
* Configuration for a Push Subscription. This can be obtained on the frontend by calling
* serviceWorkerRegistration.pushManager.subscribe().
* The expected format is the same output as JSON.stringify'ing a PushSubscription in the browser.
*/
export interface PushSubscription {
endpoint: string;
keys: {
p256dh: string;
auth: string;
};
}
export interface Headers {
[header: string]: string;
}
/**
* Options for configuring the outgoing request in generateRequestDetails() or sendNotification().
*/
export interface RequestOptions {
headers?: Headers;
gcmAPIKey?: string; // can be a GCM API key to be used for this request and this request only. This overrides any API key set via setGCMAPIKey().
vapidDetails?: { // should be an object with subject, publicKey and privateKey values defined. These values should follow the VAPID Spec. (https://tools.ietf.org/html/draft-thomson-webpush-vapid)
subject: string;
publicKey: string;
privateKey: string;
};
TTL?: number; // a value in seconds that describes how long a push message is retained by the push service (by default, four weeks).
contentEncoding?: ContentEncoding; // the type of push encoding to use (e.g. 'aesgcm', by default, or 'aes128gcm').
proxy?: string; // proxy hostname/ip and a port to tunnel your requests through (eg. http://< hostname >:< port >).
}
/**
* An object containing all the details needed to make a network request.
* Buffer is null unless a payload was passed into generateRequestDetails().
*/
export interface RequestDetails {
method: 'POST';
headers: Headers;
body: Buffer | null;
endpoint: string;
proxy?: string;
}
/**
* If calling sendNotification() was successful, this result contains the response from the push endpoint.
*/
export interface SendResult {
statusCode: number;
body: string;
headers: Headers;
}
/**
* Error thrown when sendNotification() receives a non-200 status code from the push endpoint.
*/
export class WebPushError extends Error {
readonly message: string;
readonly statusCode: number;
readonly headers: Headers;
readonly body: string;
readonly endpoint: string;
constructor(
message: string,
statusCode: number,
headers: Headers,
body: string,
endpoint: string
);
}
+23
View File
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"strictFunctionTypes": true,
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"web-push-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }
+303
View File
@@ -0,0 +1,303 @@
import {
WebPushError,
ContentEncoding,
supportedContentEncodings,
encrypt,
EncryptionResult,
getVapidHeaders,
Headers,
generateVAPIDKeys,
VapidKeys,
setGCMAPIKey,
setVapidDetails,
PushSubscription,
RequestOptions,
RequestDetails,
generateRequestDetails,
SendResult,
sendNotification
} from 'web-push';
declare const anything: any;
// ==============
// WebPushError
// ==============
// $ExpectType WebPushError
const error = new WebPushError('message', 400, {}, 'body', 'endpoint');
// WebPushError extends Error
// $ExpectType string | undefined
error.stack;
// $ExpectType string
error.message;
// $ExpectType string
error.name;
// =================
// ContentEncoding
// =================
// ContentEncoding should only allow named types
// $ExpectError
const notAValidEncoding: ContentEncoding = 'hello world';
// The exported values should match the "enum" type
const aesGcm: ContentEncoding = supportedContentEncodings.AES_GCM;
const aes128Gcm: ContentEncoding = supportedContentEncodings.AES_128_GCM;
// ===========
// encrypt()
// ===========
// $ExpectType EncryptionResult
const encryptionResult = encrypt('publicKey', 'userAuth', 'myPayload', supportedContentEncodings.AES_GCM);
const buffer: Buffer = anything;
// $ExpectType EncryptionResult
encrypt('publicKey', 'userAuth', buffer, supportedContentEncodings.AES_128_GCM);
// Only valid encoding should be allowed
// $ExpectError
encrypt('publicKey', 'userAuth', 'myPayload', 'unknownEncoding');
// $ExpectType string
encryptionResult.localPublicKey;
// $ExpectType string
encryptionResult.salt;
// $ExpectType Buffer
encryptionResult.cipherText;
// ===================
// getVapidHeaders()
// ===================
// $ExpectType { Authorization: string; 'Crypto-Key': string; }
getVapidHeaders('audience', 'subject', 'publicKey', 'privateKey', supportedContentEncodings.AES_GCM, 150);
// $ExpectType { Authorization: string; }
getVapidHeaders('audience', 'subject', 'publicKey', 'privateKey', supportedContentEncodings.AES_128_GCM, 150);
// expiration is optional
// $ExpectType { Authorization: string; 'Crypto-Key': string; }
getVapidHeaders('audience', 'subject', 'publicKey', 'privateKey', supportedContentEncodings.AES_GCM);
// $ExpectType { Authorization: string; }
getVapidHeaders('audience', 'subject', 'publicKey', 'privateKey', supportedContentEncodings.AES_128_GCM);
// Buffers are not supported here
// $ExpectError
getVapidHeaders('audience', buffer, 'publicKey', 'privateKey', supportedContentEncodings.AES_128_GCM);
// $ExpectError
getVapidHeaders('audience', 'subject', buffer, 'privateKey', supportedContentEncodings.AES_128_GCM);
// $ExpectError
getVapidHeaders('audience', 'subject', 'publicKey', buffer, supportedContentEncodings.AES_128_GCM);
// =====================
// generateVAPIDKeys()
// =====================
// $ExpectType VapidKeys
const vapidKeys = generateVAPIDKeys();
// $ExpectType string
vapidKeys.publicKey;
// $ExpectType string
vapidKeys.privateKey;
// ================
// setGCMAPIKey()
// ================
// $ExpectType void
setGCMAPIKey('apiKey');
// $ExpectType void
setGCMAPIKey(null);
// ===================
// setVapidDetails()
// ===================
// $ExpectType void
setVapidDetails('subject', 'privateKey', 'publicKey');
// Buffers are not supported here
// $ExpectError
setVapidDetails(buffer, 'publicKey', 'privateKey');
// $ExpectError
setVapidDetails('subject', buffer, 'privateKey');
// $ExpectError
setVapidDetails('subject', 'publicKey', buffer);
// ==================
// PushSubscription
// ==================
const pushSubscription: PushSubscription = {
endpoint: 'endpointString',
keys: {
p256dh: 'p256dhString',
auth: 'authString',
}
};
// ================
// RequestOptions
// ================
let requestOptions: RequestOptions;
// Can be entirely empty
requestOptions = {};
requestOptions = {
headers: {
someHeader: 'value'
}
};
requestOptions = {
gcmAPIKey: 'key'
};
requestOptions = {
vapidDetails: {
privateKey: 'private',
publicKey: 'public',
subject: 'subject'
}
};
requestOptions = {
TTL: 100
};
requestOptions = {
contentEncoding: supportedContentEncodings.AES_128_GCM
};
requestOptions = {
proxy: 'http://proxy'
};
requestOptions = {
headers: {
someHeader: 'value'
},
gcmAPIKey: 'key',
vapidDetails: {
privateKey: 'private',
publicKey: 'public',
subject: 'subject'
},
TTL: 100,
contentEncoding: supportedContentEncodings.AES_GCM,
proxy: 'http://proxy'
};
// ==========================
// generateRequestDetails()
// ==========================
// $ExpectType RequestDetails & { body: Buffer; }
generateRequestDetails(pushSubscription, 'payload', requestOptions);
// $ExpectType Buffer
generateRequestDetails(pushSubscription, 'payload', requestOptions).body;
// Payload can be a Buffer
// $ExpectType Buffer
generateRequestDetails(pushSubscription, buffer).body;
// $ExpectType Buffer
generateRequestDetails(pushSubscription, buffer, requestOptions).body;
// Payload is optional, then body will be null
// $ExpectType RequestDetails & { body: null; }
generateRequestDetails(pushSubscription);
// $ExpectType null
generateRequestDetails(pushSubscription).body;
// $ExpectType null
generateRequestDetails(pushSubscription, null).body;
// $ExpectType null
generateRequestDetails(pushSubscription, null, requestOptions).body;
// PushSubscription must have all its values
// $ExpectError
generateRequestDetails({});
// $ExpectError
generateRequestDetails({ endpoint: 'endpoint' });
// $ExpectError
generateRequestDetails({ keys: { p256dh: 'p256dh', auth: 'auth' } });
// $ExpectError
generateRequestDetails({ endpoint: null, keys: { p256dh: 'p256dh', auth: 'auth' } });
// $ExpectError
generateRequestDetails({ endpoint: 'endpoint', keys: null });
generateRequestDetails({ endpoint: 'endpoint', keys: { p256dh: 'p256dh', auth: 'auth' } });
// RequestOptions is optional
generateRequestDetails(pushSubscription, 'payload');
generateRequestDetails(pushSubscription, 'payload', {});
// ================
// RequestDetails
// ================
const requestDetails = generateRequestDetails(pushSubscription, 'payload');
// $ExpectType "POST"
requestDetails.method;
// $ExpectType Headers
requestDetails.headers;
// $ExpectType Buffer
requestDetails.body;
// $ExpectType string
requestDetails.endpoint;
// $ExpectType string | undefined
requestDetails.proxy;
// ====================
// sendNotification()
// ====================
// $ExpectType Promise<SendResult>
sendNotification(pushSubscription, 'payload', requestOptions);
// Buffers are also supported
// $ExpectType Promise<SendResult>
sendNotification(pushSubscription, buffer);
// $ExpectType Promise<SendResult>
sendNotification(pushSubscription, buffer, requestOptions);
// Payload can be absent or null
// $ExpectType Promise<SendResult>
sendNotification(pushSubscription);
// $ExpectType Promise<SendResult>
sendNotification(pushSubscription, null, requestOptions);
// PushSubscription must have all its values
// $ExpectError
sendNotification({}, 'payload');
// $ExpectError
sendNotification({ endpoint: 'endpoint' }, 'payload');
// $ExpectError
sendNotification({ keys: { p256dh: 'p256dh', auth: 'auth' } }, 'payload');
// $ExpectError
sendNotification({ endpoint: null, keys: { p256dh: 'p256dh', auth: 'auth' } }, 'payload');
// $ExpectError
sendNotification({ endpoint: 'endpoint', keys: null }, 'payload');
sendNotification({ endpoint: 'endpoint', keys: { p256dh: 'p256dh', auth: 'auth' } }, 'payload');
// =============
// SendResult
// =============
const sendResult = sendNotification(pushSubscription, 'payload');
sendResult.then((result) => {
// ExpectType number
result.statusCode;
// $ExpectType string
result.body;
// $ExpectType Headers
result.headers;
});
sendResult.catch((error: WebPushError) => {
});