@markwharton/pwa-push 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/client/deviceId.test.d.ts +1 -0
- package/dist/__tests__/client/deviceId.test.js +134 -0
- package/dist/__tests__/client/encoding.test.d.ts +1 -0
- package/dist/__tests__/client/encoding.test.js +89 -0
- package/dist/__tests__/client/indexedDb.test.d.ts +1 -0
- package/dist/__tests__/client/indexedDb.test.js +195 -0
- package/dist/__tests__/client/renewal.test.d.ts +1 -0
- package/dist/__tests__/client/renewal.test.js +171 -0
- package/dist/__tests__/client/subscribe.test.d.ts +1 -0
- package/dist/__tests__/client/subscribe.test.js +268 -0
- package/dist/__tests__/server/send.test.d.ts +1 -0
- package/dist/__tests__/server/send.test.js +217 -0
- package/dist/client/deviceId.d.ts +13 -3
- package/dist/client/deviceId.js +16 -4
- package/dist/client/encoding.d.ts +11 -3
- package/dist/client/encoding.js +11 -3
- package/dist/client/indexedDb.d.ts +24 -3
- package/dist/client/indexedDb.js +33 -5
- package/dist/client/renewal.d.ts +13 -8
- package/dist/client/renewal.js +13 -8
- package/dist/client/subscribe.d.ts +67 -18
- package/dist/client/subscribe.js +76 -30
- package/dist/pwa-push-sw.js +172 -0
- package/dist/server/send.d.ts +64 -16
- package/dist/server/send.js +63 -19
- package/dist/sw.d.ts +14 -0
- package/dist/sw.js +18 -0
- package/dist/types.d.ts +1 -18
- package/dist/types.js +6 -0
- package/package.json +9 -4
package/dist/server/send.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Result } from '@markwharton/pwa-core/types';
|
|
2
|
+
import { PushSubscription, NotificationPayload } from '../types';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
4
|
+
* Initializes VAPID configuration for Web Push. Call once at application startup.
|
|
5
|
+
* @param config - VAPID configuration
|
|
6
|
+
* @param config.publicKey - The VAPID public key
|
|
7
|
+
* @param config.privateKey - The VAPID private key
|
|
8
|
+
* @param config.subject - Contact email (mailto:) or URL
|
|
9
|
+
* @throws Error if any required configuration is missing
|
|
10
|
+
* @example
|
|
11
|
+
* initPush({
|
|
12
|
+
* publicKey: process.env.VAPID_PUBLIC_KEY,
|
|
13
|
+
* privateKey: process.env.VAPID_PRIVATE_KEY,
|
|
14
|
+
* subject: 'mailto:admin@example.com'
|
|
15
|
+
* });
|
|
5
16
|
*/
|
|
6
17
|
export declare function initPush(config: {
|
|
7
18
|
publicKey?: string;
|
|
@@ -9,30 +20,67 @@ export declare function initPush(config: {
|
|
|
9
20
|
subject?: string;
|
|
10
21
|
}): void;
|
|
11
22
|
/**
|
|
12
|
-
*
|
|
23
|
+
* Initializes VAPID from environment variables.
|
|
24
|
+
* Reads VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT.
|
|
25
|
+
* @throws Error if required environment variables are missing
|
|
26
|
+
* @example
|
|
27
|
+
* initPushFromEnv(); // Uses process.env automatically
|
|
13
28
|
*/
|
|
14
29
|
export declare function initPushFromEnv(): void;
|
|
15
30
|
/**
|
|
16
|
-
*
|
|
31
|
+
* Gets the VAPID public key for client-side subscription.
|
|
32
|
+
* @returns The VAPID public key string
|
|
33
|
+
* @throws Error if initPush() has not been called
|
|
34
|
+
* @example
|
|
35
|
+
* // Server endpoint
|
|
36
|
+
* app.get('/api/vapid-key', (req, res) => {
|
|
37
|
+
* res.json({ publicKey: getVapidPublicKey() });
|
|
38
|
+
* });
|
|
17
39
|
*/
|
|
18
40
|
export declare function getVapidPublicKey(): string;
|
|
19
41
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @
|
|
42
|
+
* Sends a push notification to a single subscription.
|
|
43
|
+
* @param subscription - The push subscription to send to
|
|
44
|
+
* @param payload - The notification payload (title, body, etc.)
|
|
45
|
+
* @returns Result with ok=true on success, or error message and statusCode on failure
|
|
46
|
+
* @example
|
|
47
|
+
* const result = await sendPushNotification(subscription, {
|
|
48
|
+
* title: 'New Message',
|
|
49
|
+
* body: 'You have a new message'
|
|
50
|
+
* });
|
|
23
51
|
*/
|
|
24
|
-
export declare function sendPushNotification(subscription: PushSubscription, payload: NotificationPayload): Promise<
|
|
52
|
+
export declare function sendPushNotification(subscription: PushSubscription, payload: NotificationPayload): Promise<Result<void>>;
|
|
25
53
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
54
|
+
* Sends a push notification to multiple subscriptions in parallel.
|
|
55
|
+
* @param subscriptions - Array of push subscriptions
|
|
56
|
+
* @param payload - The notification payload (title, body, etc.)
|
|
57
|
+
* @returns Array of Results, one per subscription (in same order)
|
|
58
|
+
* @example
|
|
59
|
+
* const results = await sendPushToAll(subscriptions, { title: 'Alert', body: 'System update' });
|
|
60
|
+
* const failed = results.filter(r => !r.ok);
|
|
28
61
|
*/
|
|
29
|
-
export declare function sendPushToAll(subscriptions: PushSubscription[], payload: NotificationPayload): Promise<
|
|
62
|
+
export declare function sendPushToAll(subscriptions: PushSubscription[], payload: NotificationPayload): Promise<Result<void>[]>;
|
|
30
63
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
64
|
+
* Sends a push notification with detailed error information.
|
|
65
|
+
* Automatically injects basePath from subscription if not set in payload.
|
|
66
|
+
* @param subscription - The push subscription to send to
|
|
67
|
+
* @param payload - The notification payload
|
|
68
|
+
* @returns Result with ok=true on success, or error with statusCode on failure
|
|
69
|
+
* @example
|
|
70
|
+
* const result = await sendPushWithDetails(subscription, { title: 'Alert' });
|
|
71
|
+
* if (!result.ok && isSubscriptionExpired(result.statusCode)) {
|
|
72
|
+
* await deleteSubscription(subscription);
|
|
73
|
+
* }
|
|
33
74
|
*/
|
|
34
|
-
export declare function sendPushWithDetails(subscription: PushSubscription, payload: NotificationPayload): Promise<
|
|
75
|
+
export declare function sendPushWithDetails(subscription: PushSubscription, payload: NotificationPayload): Promise<Result<void>>;
|
|
35
76
|
/**
|
|
36
|
-
*
|
|
77
|
+
* Checks if a status code indicates an expired subscription (410 Gone).
|
|
78
|
+
* Use this to clean up stale subscriptions from your database.
|
|
79
|
+
* @param statusCode - The HTTP status code from a failed push attempt
|
|
80
|
+
* @returns True if the subscription should be deleted
|
|
81
|
+
* @example
|
|
82
|
+
* if (!result.ok && isSubscriptionExpired(result.statusCode)) {
|
|
83
|
+
* await db.deleteSubscription(subscription.endpoint);
|
|
84
|
+
* }
|
|
37
85
|
*/
|
|
38
86
|
export declare function isSubscriptionExpired(statusCode: number | undefined): boolean;
|
package/dist/server/send.js
CHANGED
|
@@ -11,13 +11,24 @@ exports.sendPushToAll = sendPushToAll;
|
|
|
11
11
|
exports.sendPushWithDetails = sendPushWithDetails;
|
|
12
12
|
exports.isSubscriptionExpired = isSubscriptionExpired;
|
|
13
13
|
const web_push_1 = __importDefault(require("web-push"));
|
|
14
|
+
const types_1 = require("@markwharton/pwa-core/types");
|
|
14
15
|
/**
|
|
15
16
|
* Server-side Web Push notification utilities
|
|
16
17
|
*/
|
|
17
18
|
let vapidConfig = null;
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* Initializes VAPID configuration for Web Push. Call once at application startup.
|
|
21
|
+
* @param config - VAPID configuration
|
|
22
|
+
* @param config.publicKey - The VAPID public key
|
|
23
|
+
* @param config.privateKey - The VAPID private key
|
|
24
|
+
* @param config.subject - Contact email (mailto:) or URL
|
|
25
|
+
* @throws Error if any required configuration is missing
|
|
26
|
+
* @example
|
|
27
|
+
* initPush({
|
|
28
|
+
* publicKey: process.env.VAPID_PUBLIC_KEY,
|
|
29
|
+
* privateKey: process.env.VAPID_PRIVATE_KEY,
|
|
30
|
+
* subject: 'mailto:admin@example.com'
|
|
31
|
+
* });
|
|
21
32
|
*/
|
|
22
33
|
function initPush(config) {
|
|
23
34
|
const { publicKey, privateKey, subject } = config;
|
|
@@ -31,7 +42,11 @@ function initPush(config) {
|
|
|
31
42
|
web_push_1.default.setVapidDetails(subject, publicKey, privateKey);
|
|
32
43
|
}
|
|
33
44
|
/**
|
|
34
|
-
*
|
|
45
|
+
* Initializes VAPID from environment variables.
|
|
46
|
+
* Reads VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT.
|
|
47
|
+
* @throws Error if required environment variables are missing
|
|
48
|
+
* @example
|
|
49
|
+
* initPushFromEnv(); // Uses process.env automatically
|
|
35
50
|
*/
|
|
36
51
|
function initPushFromEnv() {
|
|
37
52
|
initPush({
|
|
@@ -41,7 +56,14 @@ function initPushFromEnv() {
|
|
|
41
56
|
});
|
|
42
57
|
}
|
|
43
58
|
/**
|
|
44
|
-
*
|
|
59
|
+
* Gets the VAPID public key for client-side subscription.
|
|
60
|
+
* @returns The VAPID public key string
|
|
61
|
+
* @throws Error if initPush() has not been called
|
|
62
|
+
* @example
|
|
63
|
+
* // Server endpoint
|
|
64
|
+
* app.get('/api/vapid-key', (req, res) => {
|
|
65
|
+
* res.json({ publicKey: getVapidPublicKey() });
|
|
66
|
+
* });
|
|
45
67
|
*/
|
|
46
68
|
function getVapidPublicKey() {
|
|
47
69
|
if (!vapidConfig) {
|
|
@@ -50,28 +72,47 @@ function getVapidPublicKey() {
|
|
|
50
72
|
return vapidConfig.publicKey;
|
|
51
73
|
}
|
|
52
74
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* @
|
|
75
|
+
* Sends a push notification to a single subscription.
|
|
76
|
+
* @param subscription - The push subscription to send to
|
|
77
|
+
* @param payload - The notification payload (title, body, etc.)
|
|
78
|
+
* @returns Result with ok=true on success, or error message and statusCode on failure
|
|
79
|
+
* @example
|
|
80
|
+
* const result = await sendPushNotification(subscription, {
|
|
81
|
+
* title: 'New Message',
|
|
82
|
+
* body: 'You have a new message'
|
|
83
|
+
* });
|
|
56
84
|
*/
|
|
57
85
|
async function sendPushNotification(subscription, payload) {
|
|
58
86
|
return sendPushWithDetails(subscription, payload);
|
|
59
87
|
}
|
|
60
88
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
89
|
+
* Sends a push notification to multiple subscriptions in parallel.
|
|
90
|
+
* @param subscriptions - Array of push subscriptions
|
|
91
|
+
* @param payload - The notification payload (title, body, etc.)
|
|
92
|
+
* @returns Array of Results, one per subscription (in same order)
|
|
93
|
+
* @example
|
|
94
|
+
* const results = await sendPushToAll(subscriptions, { title: 'Alert', body: 'System update' });
|
|
95
|
+
* const failed = results.filter(r => !r.ok);
|
|
63
96
|
*/
|
|
64
97
|
async function sendPushToAll(subscriptions, payload) {
|
|
65
98
|
const results = await Promise.all(subscriptions.map(sub => sendPushWithDetails(sub, payload)));
|
|
66
99
|
return results;
|
|
67
100
|
}
|
|
68
101
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
102
|
+
* Sends a push notification with detailed error information.
|
|
103
|
+
* Automatically injects basePath from subscription if not set in payload.
|
|
104
|
+
* @param subscription - The push subscription to send to
|
|
105
|
+
* @param payload - The notification payload
|
|
106
|
+
* @returns Result with ok=true on success, or error with statusCode on failure
|
|
107
|
+
* @example
|
|
108
|
+
* const result = await sendPushWithDetails(subscription, { title: 'Alert' });
|
|
109
|
+
* if (!result.ok && isSubscriptionExpired(result.statusCode)) {
|
|
110
|
+
* await deleteSubscription(subscription);
|
|
111
|
+
* }
|
|
71
112
|
*/
|
|
72
113
|
async function sendPushWithDetails(subscription, payload) {
|
|
73
114
|
if (!vapidConfig) {
|
|
74
|
-
return
|
|
115
|
+
return (0, types_1.err)('Push not initialized');
|
|
75
116
|
}
|
|
76
117
|
// Auto-inject basePath from subscription if not set in payload
|
|
77
118
|
const payloadWithBasePath = {
|
|
@@ -80,19 +121,22 @@ async function sendPushWithDetails(subscription, payload) {
|
|
|
80
121
|
};
|
|
81
122
|
try {
|
|
82
123
|
await web_push_1.default.sendNotification(subscription, JSON.stringify(payloadWithBasePath));
|
|
83
|
-
return
|
|
124
|
+
return (0, types_1.okVoid)();
|
|
84
125
|
}
|
|
85
126
|
catch (error) {
|
|
86
127
|
const webPushError = error;
|
|
87
|
-
return
|
|
88
|
-
ok: false,
|
|
89
|
-
error: webPushError.message || 'Unknown error',
|
|
90
|
-
statusCode: webPushError.statusCode
|
|
91
|
-
};
|
|
128
|
+
return (0, types_1.err)(webPushError.message || 'Unknown error', webPushError.statusCode);
|
|
92
129
|
}
|
|
93
130
|
}
|
|
94
131
|
/**
|
|
95
|
-
*
|
|
132
|
+
* Checks if a status code indicates an expired subscription (410 Gone).
|
|
133
|
+
* Use this to clean up stale subscriptions from your database.
|
|
134
|
+
* @param statusCode - The HTTP status code from a failed push attempt
|
|
135
|
+
* @returns True if the subscription should be deleted
|
|
136
|
+
* @example
|
|
137
|
+
* if (!result.ok && isSubscriptionExpired(result.statusCode)) {
|
|
138
|
+
* await db.deleteSubscription(subscription.endpoint);
|
|
139
|
+
* }
|
|
96
140
|
*/
|
|
97
141
|
function isSubscriptionExpired(statusCode) {
|
|
98
142
|
return statusCode === 410;
|
package/dist/sw.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Worker bundle entry point
|
|
3
|
+
* Exports functions for use via importScripts()
|
|
4
|
+
*
|
|
5
|
+
* Usage in sw.js:
|
|
6
|
+
* ```
|
|
7
|
+
* importScripts('/path/to/pwa-push-sw.js');
|
|
8
|
+
*
|
|
9
|
+
* self.addEventListener('pushsubscriptionchange', (event) => {
|
|
10
|
+
* event.waitUntil(PwaPush.handleSubscriptionChange('/api/push/renew'));
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export { handleSubscriptionChange } from './client/renewal';
|
package/dist/sw.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Service Worker bundle entry point
|
|
4
|
+
* Exports functions for use via importScripts()
|
|
5
|
+
*
|
|
6
|
+
* Usage in sw.js:
|
|
7
|
+
* ```
|
|
8
|
+
* importScripts('/path/to/pwa-push-sw.js');
|
|
9
|
+
*
|
|
10
|
+
* self.addEventListener('pushsubscriptionchange', (event) => {
|
|
11
|
+
* event.waitUntil(PwaPush.handleSubscriptionChange('/api/push/renew'));
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.handleSubscriptionChange = void 0;
|
|
17
|
+
var renewal_1 = require("./client/renewal");
|
|
18
|
+
Object.defineProperty(exports, "handleSubscriptionChange", { enumerable: true, get: function () { return renewal_1.handleSubscriptionChange; } });
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Push notification types - shared between server and client
|
|
3
3
|
*/
|
|
4
|
+
export { Result, ok, okVoid, err } from '@markwharton/pwa-core/types';
|
|
4
5
|
export interface PushSubscription {
|
|
5
6
|
endpoint: string;
|
|
6
7
|
keys: {
|
|
@@ -25,24 +26,6 @@ export interface VapidConfig {
|
|
|
25
26
|
privateKey: string;
|
|
26
27
|
subject: string;
|
|
27
28
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Result type for push operations.
|
|
30
|
-
* Aligned with Result<T> pattern from @markwharton/pwa-core
|
|
31
|
-
*/
|
|
32
|
-
export interface SendResult {
|
|
33
|
-
ok: boolean;
|
|
34
|
-
error?: string;
|
|
35
|
-
statusCode?: number;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Generic result type for client operations.
|
|
39
|
-
* Aligned with Result<T> pattern from @markwharton/pwa-core
|
|
40
|
-
*/
|
|
41
|
-
export interface ClientResult<T> {
|
|
42
|
-
ok: boolean;
|
|
43
|
-
data?: T;
|
|
44
|
-
error?: string;
|
|
45
|
-
}
|
|
46
29
|
export interface SubscriptionRequest {
|
|
47
30
|
subscription: PushSubscription;
|
|
48
31
|
deviceId: string;
|
package/dist/types.js
CHANGED
|
@@ -3,3 +3,9 @@
|
|
|
3
3
|
* Push notification types - shared between server and client
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.err = exports.okVoid = exports.ok = void 0;
|
|
7
|
+
// Re-export Result from pwa-core for convenience
|
|
8
|
+
var types_1 = require("@markwharton/pwa-core/types");
|
|
9
|
+
Object.defineProperty(exports, "ok", { enumerable: true, get: function () { return types_1.ok; } });
|
|
10
|
+
Object.defineProperty(exports, "okVoid", { enumerable: true, get: function () { return types_1.okVoid; } });
|
|
11
|
+
Object.defineProperty(exports, "err", { enumerable: true, get: function () { return types_1.err; } });
|
package/package.json
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markwharton/pwa-push",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Web push notifications for Azure PWA projects",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./dist/index.js",
|
|
9
9
|
"./server": "./dist/server/index.js",
|
|
10
|
-
"./client": "./dist/client/index.js"
|
|
10
|
+
"./client": "./dist/client/index.js",
|
|
11
|
+
"./sw": "./dist/pwa-push-sw.js"
|
|
11
12
|
},
|
|
12
13
|
"scripts": {
|
|
13
|
-
"build": "tsc",
|
|
14
|
+
"build": "tsc && npm run build:sw",
|
|
15
|
+
"build:sw": "esbuild src/sw.ts --bundle --outfile=dist/pwa-push-sw.js --format=iife --global-name=PwaPush",
|
|
14
16
|
"clean": "rm -rf dist"
|
|
15
17
|
},
|
|
16
18
|
"peerDependencies": {
|
|
19
|
+
"@markwharton/pwa-core": "^1.1.0",
|
|
17
20
|
"web-push": "^3.6.0"
|
|
18
21
|
},
|
|
19
22
|
"devDependencies": {
|
|
23
|
+
"@markwharton/pwa-core": "^1.1.0",
|
|
20
24
|
"@types/node": "^20.10.0",
|
|
21
25
|
"@types/web-push": "^3.6.4",
|
|
26
|
+
"esbuild": "^0.27.2",
|
|
22
27
|
"typescript": "^5.3.0",
|
|
23
28
|
"web-push": "^3.6.7"
|
|
24
29
|
},
|
|
@@ -27,7 +32,7 @@
|
|
|
27
32
|
],
|
|
28
33
|
"repository": {
|
|
29
34
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/MarkWharton/pwa-packages.git",
|
|
35
|
+
"url": "git+https://github.com/MarkWharton/pwa-packages.git",
|
|
31
36
|
"directory": "packages/push"
|
|
32
37
|
},
|
|
33
38
|
"author": "Mark Wharton",
|