@markwharton/pwa-push 1.0.1 → 1.1.0
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/client/renewal.js +1 -1
- package/dist/client/subscribe.d.ts +10 -5
- package/dist/client/subscribe.js +44 -31
- package/dist/server/send.d.ts +5 -2
- package/dist/server/send.js +14 -7
- package/dist/types.d.ts +18 -6
- package/package.json +1 -1
- package/dist/client/utils.d.ts +0 -14
- package/dist/client/utils.js +0 -38
package/dist/client/renewal.js
CHANGED
|
@@ -33,7 +33,7 @@ async function handleSubscriptionChange(event, renewEndpoint) {
|
|
|
33
33
|
console.error('Failed to create new subscription');
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
|
-
// Build renewal request
|
|
36
|
+
// Build renewal request (uses SubscriptionRequest type)
|
|
37
37
|
const renewalRequest = {
|
|
38
38
|
subscription: newSubscription,
|
|
39
39
|
deviceId: data.deviceId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Push subscription helpers for main thread
|
|
3
3
|
*/
|
|
4
|
-
import { PushSubscription, SubscriptionRequest, PushPermissionState } from '../types';
|
|
4
|
+
import { PushSubscription, SubscriptionRequest, PushPermissionState, ClientResult } from '../types';
|
|
5
5
|
/**
|
|
6
6
|
* Check current notification permission state
|
|
7
7
|
*/
|
|
@@ -21,14 +21,19 @@ export declare function isPushSupported(): boolean;
|
|
|
21
21
|
export declare function toPushSubscription(browserSub: globalThis.PushSubscription): PushSubscription;
|
|
22
22
|
/**
|
|
23
23
|
* Subscribe to push notifications
|
|
24
|
-
*
|
|
24
|
+
*
|
|
25
|
+
* @returns Result with subscription request ready to send to backend
|
|
25
26
|
*/
|
|
26
|
-
export declare function subscribeToPush(vapidPublicKey: string, basePath?: string): Promise<SubscriptionRequest
|
|
27
|
+
export declare function subscribeToPush(vapidPublicKey: string, basePath?: string): Promise<ClientResult<SubscriptionRequest>>;
|
|
27
28
|
/**
|
|
28
29
|
* Unsubscribe from push notifications
|
|
30
|
+
*
|
|
31
|
+
* @returns Result indicating success or failure
|
|
29
32
|
*/
|
|
30
|
-
export declare function unsubscribeFromPush(): Promise<
|
|
33
|
+
export declare function unsubscribeFromPush(): Promise<ClientResult<void>>;
|
|
31
34
|
/**
|
|
32
35
|
* Get current push subscription if exists
|
|
36
|
+
*
|
|
37
|
+
* @returns Result with current subscription, or error if not subscribed
|
|
33
38
|
*/
|
|
34
|
-
export declare function getCurrentSubscription(): Promise<PushSubscription
|
|
39
|
+
export declare function getCurrentSubscription(): Promise<ClientResult<PushSubscription>>;
|
package/dist/client/subscribe.js
CHANGED
|
@@ -48,40 +48,50 @@ function toPushSubscription(browserSub) {
|
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Subscribe to push notifications
|
|
51
|
-
*
|
|
51
|
+
*
|
|
52
|
+
* @returns Result with subscription request ready to send to backend
|
|
52
53
|
*/
|
|
53
54
|
async function subscribeToPush(vapidPublicKey, basePath = '/') {
|
|
54
55
|
if (!isPushSupported()) {
|
|
55
|
-
|
|
56
|
-
return null;
|
|
56
|
+
return { ok: false, error: 'Push notifications not supported' };
|
|
57
57
|
}
|
|
58
58
|
const permission = await requestPermission();
|
|
59
59
|
if (permission !== 'granted') {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
return { ok: false, error: 'Push permission denied' };
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const registration = await navigator.serviceWorker.ready;
|
|
64
|
+
const deviceId = (0, deviceId_1.getDeviceId)();
|
|
65
|
+
// Convert VAPID key to Uint8Array
|
|
66
|
+
const applicationServerKey = (0, encoding_1.urlBase64ToUint8Array)(vapidPublicKey);
|
|
67
|
+
const browserSub = await registration.pushManager.subscribe({
|
|
68
|
+
userVisibleOnly: true,
|
|
69
|
+
applicationServerKey
|
|
70
|
+
});
|
|
71
|
+
// Save data for service worker renewal
|
|
72
|
+
await (0, indexedDb_1.saveSubscriptionData)({
|
|
73
|
+
publicKey: vapidPublicKey,
|
|
74
|
+
deviceId,
|
|
75
|
+
basePath
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
data: {
|
|
80
|
+
subscription: toPushSubscription(browserSub),
|
|
81
|
+
deviceId,
|
|
82
|
+
basePath
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
const message = error instanceof Error ? error.message : 'Subscription failed';
|
|
88
|
+
return { ok: false, error: message };
|
|
62
89
|
}
|
|
63
|
-
const registration = await navigator.serviceWorker.ready;
|
|
64
|
-
const deviceId = (0, deviceId_1.getDeviceId)();
|
|
65
|
-
// Convert VAPID key to Uint8Array
|
|
66
|
-
const applicationServerKey = (0, encoding_1.urlBase64ToUint8Array)(vapidPublicKey);
|
|
67
|
-
const browserSub = await registration.pushManager.subscribe({
|
|
68
|
-
userVisibleOnly: true,
|
|
69
|
-
applicationServerKey
|
|
70
|
-
});
|
|
71
|
-
// Save data for service worker renewal
|
|
72
|
-
await (0, indexedDb_1.saveSubscriptionData)({
|
|
73
|
-
publicKey: vapidPublicKey,
|
|
74
|
-
deviceId,
|
|
75
|
-
basePath
|
|
76
|
-
});
|
|
77
|
-
return {
|
|
78
|
-
subscription: toPushSubscription(browserSub),
|
|
79
|
-
deviceId,
|
|
80
|
-
basePath
|
|
81
|
-
};
|
|
82
90
|
}
|
|
83
91
|
/**
|
|
84
92
|
* Unsubscribe from push notifications
|
|
93
|
+
*
|
|
94
|
+
* @returns Result indicating success or failure
|
|
85
95
|
*/
|
|
86
96
|
async function unsubscribeFromPush() {
|
|
87
97
|
try {
|
|
@@ -90,26 +100,29 @@ async function unsubscribeFromPush() {
|
|
|
90
100
|
if (subscription) {
|
|
91
101
|
await subscription.unsubscribe();
|
|
92
102
|
}
|
|
93
|
-
return true;
|
|
103
|
+
return { ok: true };
|
|
94
104
|
}
|
|
95
105
|
catch (error) {
|
|
96
|
-
|
|
97
|
-
return false;
|
|
106
|
+
const message = error instanceof Error ? error.message : 'Unsubscribe failed';
|
|
107
|
+
return { ok: false, error: message };
|
|
98
108
|
}
|
|
99
109
|
}
|
|
100
110
|
/**
|
|
101
111
|
* Get current push subscription if exists
|
|
112
|
+
*
|
|
113
|
+
* @returns Result with current subscription, or error if not subscribed
|
|
102
114
|
*/
|
|
103
115
|
async function getCurrentSubscription() {
|
|
104
116
|
try {
|
|
105
117
|
const registration = await navigator.serviceWorker.ready;
|
|
106
118
|
const subscription = await registration.pushManager.getSubscription();
|
|
107
119
|
if (!subscription) {
|
|
108
|
-
return
|
|
120
|
+
return { ok: false, error: 'No active subscription' };
|
|
109
121
|
}
|
|
110
|
-
return toPushSubscription(subscription);
|
|
122
|
+
return { ok: true, data: toPushSubscription(subscription) };
|
|
111
123
|
}
|
|
112
|
-
catch {
|
|
113
|
-
|
|
124
|
+
catch (error) {
|
|
125
|
+
const message = error instanceof Error ? error.message : 'Failed to get subscription';
|
|
126
|
+
return { ok: false, error: message };
|
|
114
127
|
}
|
|
115
128
|
}
|
package/dist/server/send.d.ts
CHANGED
|
@@ -18,15 +18,18 @@ export declare function initPushFromEnv(): void;
|
|
|
18
18
|
export declare function getVapidPublicKey(): string;
|
|
19
19
|
/**
|
|
20
20
|
* Send a push notification to a single subscription
|
|
21
|
+
*
|
|
22
|
+
* @returns Result with ok status, or error details on failure
|
|
21
23
|
*/
|
|
22
|
-
export declare function sendPushNotification(subscription: PushSubscription, payload: NotificationPayload): Promise<
|
|
24
|
+
export declare function sendPushNotification(subscription: PushSubscription, payload: NotificationPayload): Promise<SendResult>;
|
|
23
25
|
/**
|
|
24
26
|
* Send a push notification to multiple subscriptions
|
|
25
|
-
* Returns array of results
|
|
27
|
+
* Returns array of results, one per subscription
|
|
26
28
|
*/
|
|
27
29
|
export declare function sendPushToAll(subscriptions: PushSubscription[], payload: NotificationPayload): Promise<SendResult[]>;
|
|
28
30
|
/**
|
|
29
31
|
* Send push notification with detailed error information
|
|
32
|
+
* Auto-injects basePath from subscription to payload if not already set
|
|
30
33
|
*/
|
|
31
34
|
export declare function sendPushWithDetails(subscription: PushSubscription, payload: NotificationPayload): Promise<SendResult>;
|
|
32
35
|
/**
|
package/dist/server/send.js
CHANGED
|
@@ -51,14 +51,15 @@ function getVapidPublicKey() {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Send a push notification to a single subscription
|
|
54
|
+
*
|
|
55
|
+
* @returns Result with ok status, or error details on failure
|
|
54
56
|
*/
|
|
55
57
|
async function sendPushNotification(subscription, payload) {
|
|
56
|
-
|
|
57
|
-
return result.success;
|
|
58
|
+
return sendPushWithDetails(subscription, payload);
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* Send a push notification to multiple subscriptions
|
|
61
|
-
* Returns array of results
|
|
62
|
+
* Returns array of results, one per subscription
|
|
62
63
|
*/
|
|
63
64
|
async function sendPushToAll(subscriptions, payload) {
|
|
64
65
|
const results = await Promise.all(subscriptions.map(sub => sendPushWithDetails(sub, payload)));
|
|
@@ -66,19 +67,25 @@ async function sendPushToAll(subscriptions, payload) {
|
|
|
66
67
|
}
|
|
67
68
|
/**
|
|
68
69
|
* Send push notification with detailed error information
|
|
70
|
+
* Auto-injects basePath from subscription to payload if not already set
|
|
69
71
|
*/
|
|
70
72
|
async function sendPushWithDetails(subscription, payload) {
|
|
71
73
|
if (!vapidConfig) {
|
|
72
|
-
return {
|
|
74
|
+
return { ok: false, error: 'Push not initialized' };
|
|
73
75
|
}
|
|
76
|
+
// Auto-inject basePath from subscription if not set in payload
|
|
77
|
+
const payloadWithBasePath = {
|
|
78
|
+
...payload,
|
|
79
|
+
basePath: payload.basePath ?? subscription.basePath ?? '/'
|
|
80
|
+
};
|
|
74
81
|
try {
|
|
75
|
-
await web_push_1.default.sendNotification(subscription, JSON.stringify(
|
|
76
|
-
return {
|
|
82
|
+
await web_push_1.default.sendNotification(subscription, JSON.stringify(payloadWithBasePath));
|
|
83
|
+
return { ok: true };
|
|
77
84
|
}
|
|
78
85
|
catch (error) {
|
|
79
86
|
const webPushError = error;
|
|
80
87
|
return {
|
|
81
|
-
|
|
88
|
+
ok: false,
|
|
82
89
|
error: webPushError.message || 'Unknown error',
|
|
83
90
|
statusCode: webPushError.statusCode
|
|
84
91
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface PushSubscription {
|
|
|
7
7
|
p256dh: string;
|
|
8
8
|
auth: string;
|
|
9
9
|
};
|
|
10
|
+
basePath?: string;
|
|
10
11
|
}
|
|
11
12
|
export interface NotificationPayload {
|
|
12
13
|
title: string;
|
|
@@ -15,23 +16,34 @@ export interface NotificationPayload {
|
|
|
15
16
|
url?: string;
|
|
16
17
|
tag?: string;
|
|
17
18
|
data?: Record<string, unknown>;
|
|
19
|
+
id?: string;
|
|
20
|
+
timestamp?: string;
|
|
21
|
+
basePath?: string;
|
|
18
22
|
}
|
|
19
23
|
export interface VapidConfig {
|
|
20
24
|
publicKey: string;
|
|
21
25
|
privateKey: string;
|
|
22
26
|
subject: string;
|
|
23
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Result type for push operations.
|
|
30
|
+
* Aligned with Result<T> pattern from @markwharton/pwa-core
|
|
31
|
+
*/
|
|
24
32
|
export interface SendResult {
|
|
25
|
-
|
|
33
|
+
ok: boolean;
|
|
26
34
|
error?: string;
|
|
27
35
|
statusCode?: number;
|
|
28
36
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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;
|
|
33
45
|
}
|
|
34
|
-
export interface
|
|
46
|
+
export interface SubscriptionRequest {
|
|
35
47
|
subscription: PushSubscription;
|
|
36
48
|
deviceId: string;
|
|
37
49
|
basePath?: string;
|
package/package.json
CHANGED
package/dist/client/utils.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared utilities for push notification client code
|
|
3
|
-
*/
|
|
4
|
-
import { PushSubscription } from '../types';
|
|
5
|
-
/**
|
|
6
|
-
* Convert base64 URL-encoded string to Uint8Array
|
|
7
|
-
* Works in both main thread and service worker contexts
|
|
8
|
-
*/
|
|
9
|
-
export declare function urlBase64ToUint8Array(base64String: string): Uint8Array<ArrayBuffer>;
|
|
10
|
-
/**
|
|
11
|
-
* Convert browser PushSubscription to our format
|
|
12
|
-
* Works in both main thread and service worker contexts
|
|
13
|
-
*/
|
|
14
|
-
export declare function toPushSubscription(browserSub: globalThis.PushSubscription): PushSubscription;
|
package/dist/client/utils.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Shared utilities for push notification client code
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.urlBase64ToUint8Array = urlBase64ToUint8Array;
|
|
7
|
-
exports.toPushSubscription = toPushSubscription;
|
|
8
|
-
/**
|
|
9
|
-
* Convert base64 URL-encoded string to Uint8Array
|
|
10
|
-
* Works in both main thread and service worker contexts
|
|
11
|
-
*/
|
|
12
|
-
function urlBase64ToUint8Array(base64String) {
|
|
13
|
-
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
|
14
|
-
const base64 = (base64String + padding)
|
|
15
|
-
.replace(/-/g, '+')
|
|
16
|
-
.replace(/_/g, '/');
|
|
17
|
-
const rawData = atob(base64);
|
|
18
|
-
const buffer = new ArrayBuffer(rawData.length);
|
|
19
|
-
const outputArray = new Uint8Array(buffer);
|
|
20
|
-
for (let i = 0; i < rawData.length; ++i) {
|
|
21
|
-
outputArray[i] = rawData.charCodeAt(i);
|
|
22
|
-
}
|
|
23
|
-
return outputArray;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Convert browser PushSubscription to our format
|
|
27
|
-
* Works in both main thread and service worker contexts
|
|
28
|
-
*/
|
|
29
|
-
function toPushSubscription(browserSub) {
|
|
30
|
-
const json = browserSub.toJSON();
|
|
31
|
-
return {
|
|
32
|
-
endpoint: browserSub.endpoint,
|
|
33
|
-
keys: {
|
|
34
|
-
p256dh: json.keys?.p256dh || '',
|
|
35
|
-
auth: json.keys?.auth || ''
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
}
|