@markwharton/pwa-push 1.0.0 → 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/deviceId.js +6 -6
- package/dist/client/encoding.d.ts +9 -0
- package/dist/client/encoding.js +24 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/indexedDb.js +15 -24
- package/dist/client/renewal.d.ts +1 -0
- package/dist/client/renewal.js +5 -26
- package/dist/client/subscribe.d.ts +15 -5
- package/dist/client/subscribe.js +47 -47
- 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/deviceId.js
CHANGED
|
@@ -8,14 +8,14 @@ exports.getDeviceId = getDeviceId;
|
|
|
8
8
|
exports.clearDeviceId = clearDeviceId;
|
|
9
9
|
const DEVICE_ID_KEY = 'push_device_id';
|
|
10
10
|
/**
|
|
11
|
-
* Generate a UUID v4
|
|
11
|
+
* Generate a cryptographically secure UUID v4
|
|
12
12
|
*/
|
|
13
13
|
function generateUUID() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
})
|
|
14
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
15
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
|
|
16
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1
|
|
17
|
+
const hex = [...bytes].map(b => b.toString(16).padStart(2, '0')).join('');
|
|
18
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Get or create a persistent device ID
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encoding utilities for push notification client
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert base64 URL-encoded string to Uint8Array
|
|
6
|
+
* Used to convert VAPID public keys for the PushManager API
|
|
7
|
+
* Works in both main thread and service worker contexts
|
|
8
|
+
*/
|
|
9
|
+
export declare function urlBase64ToUint8Array(base64String: string): Uint8Array<ArrayBuffer>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Encoding utilities for push notification client
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.urlBase64ToUint8Array = urlBase64ToUint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Convert base64 URL-encoded string to Uint8Array
|
|
9
|
+
* Used to convert VAPID public keys for the PushManager API
|
|
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
|
+
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { getDeviceId, clearDeviceId } from './deviceId';
|
|
2
2
|
export { saveSubscriptionData, getSubscriptionData, clearSubscriptionData } from './indexedDb';
|
|
3
|
-
export { getPermissionState, requestPermission, isPushSupported, subscribeToPush, unsubscribeFromPush, getCurrentSubscription } from './subscribe';
|
|
3
|
+
export { getPermissionState, requestPermission, isPushSupported, toPushSubscription, subscribeToPush, unsubscribeFromPush, getCurrentSubscription } from './subscribe';
|
|
4
4
|
export { handleSubscriptionChange } from './renewal';
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.handleSubscriptionChange = exports.getCurrentSubscription = exports.unsubscribeFromPush = exports.subscribeToPush = exports.isPushSupported = exports.requestPermission = exports.getPermissionState = exports.clearSubscriptionData = exports.getSubscriptionData = exports.saveSubscriptionData = exports.clearDeviceId = exports.getDeviceId = void 0;
|
|
3
|
+
exports.handleSubscriptionChange = exports.getCurrentSubscription = exports.unsubscribeFromPush = exports.subscribeToPush = exports.toPushSubscription = exports.isPushSupported = exports.requestPermission = exports.getPermissionState = exports.clearSubscriptionData = exports.getSubscriptionData = exports.saveSubscriptionData = exports.clearDeviceId = exports.getDeviceId = void 0;
|
|
4
4
|
var deviceId_1 = require("./deviceId");
|
|
5
5
|
Object.defineProperty(exports, "getDeviceId", { enumerable: true, get: function () { return deviceId_1.getDeviceId; } });
|
|
6
6
|
Object.defineProperty(exports, "clearDeviceId", { enumerable: true, get: function () { return deviceId_1.clearDeviceId; } });
|
|
@@ -12,6 +12,7 @@ var subscribe_1 = require("./subscribe");
|
|
|
12
12
|
Object.defineProperty(exports, "getPermissionState", { enumerable: true, get: function () { return subscribe_1.getPermissionState; } });
|
|
13
13
|
Object.defineProperty(exports, "requestPermission", { enumerable: true, get: function () { return subscribe_1.requestPermission; } });
|
|
14
14
|
Object.defineProperty(exports, "isPushSupported", { enumerable: true, get: function () { return subscribe_1.isPushSupported; } });
|
|
15
|
+
Object.defineProperty(exports, "toPushSubscription", { enumerable: true, get: function () { return subscribe_1.toPushSubscription; } });
|
|
15
16
|
Object.defineProperty(exports, "subscribeToPush", { enumerable: true, get: function () { return subscribe_1.subscribeToPush; } });
|
|
16
17
|
Object.defineProperty(exports, "unsubscribeFromPush", { enumerable: true, get: function () { return subscribe_1.unsubscribeFromPush; } });
|
|
17
18
|
Object.defineProperty(exports, "getCurrentSubscription", { enumerable: true, get: function () { return subscribe_1.getCurrentSubscription; } });
|
package/dist/client/indexedDb.js
CHANGED
|
@@ -27,44 +27,35 @@ function openDatabase() {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Execute an IndexedDB operation within a transaction
|
|
31
31
|
*/
|
|
32
|
-
async function
|
|
32
|
+
async function withTransaction(mode, operation) {
|
|
33
33
|
const db = await openDatabase();
|
|
34
34
|
return new Promise((resolve, reject) => {
|
|
35
|
-
const transaction = db.transaction(STORE_NAME,
|
|
35
|
+
const transaction = db.transaction(STORE_NAME, mode);
|
|
36
36
|
const store = transaction.objectStore(STORE_NAME);
|
|
37
|
-
const request = store
|
|
37
|
+
const request = operation(store);
|
|
38
38
|
request.onerror = () => reject(request.error);
|
|
39
|
-
request.onsuccess = () => resolve();
|
|
39
|
+
request.onsuccess = () => resolve(request.result);
|
|
40
40
|
transaction.oncomplete = () => db.close();
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Save subscription data to IndexedDB (for service worker access)
|
|
45
|
+
*/
|
|
46
|
+
function saveSubscriptionData(data) {
|
|
47
|
+
return withTransaction('readwrite', (store) => store.put(data, DATA_KEY));
|
|
48
|
+
}
|
|
43
49
|
/**
|
|
44
50
|
* Get subscription data from IndexedDB
|
|
45
51
|
*/
|
|
46
52
|
async function getSubscriptionData() {
|
|
47
|
-
const
|
|
48
|
-
return
|
|
49
|
-
const transaction = db.transaction(STORE_NAME, 'readonly');
|
|
50
|
-
const store = transaction.objectStore(STORE_NAME);
|
|
51
|
-
const request = store.get(DATA_KEY);
|
|
52
|
-
request.onerror = () => reject(request.error);
|
|
53
|
-
request.onsuccess = () => resolve(request.result || null);
|
|
54
|
-
transaction.oncomplete = () => db.close();
|
|
55
|
-
});
|
|
53
|
+
const result = await withTransaction('readonly', (store) => store.get(DATA_KEY));
|
|
54
|
+
return result || null;
|
|
56
55
|
}
|
|
57
56
|
/**
|
|
58
57
|
* Clear subscription data from IndexedDB
|
|
59
58
|
*/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return new Promise((resolve, reject) => {
|
|
63
|
-
const transaction = db.transaction(STORE_NAME, 'readwrite');
|
|
64
|
-
const store = transaction.objectStore(STORE_NAME);
|
|
65
|
-
const request = store.delete(DATA_KEY);
|
|
66
|
-
request.onerror = () => reject(request.error);
|
|
67
|
-
request.onsuccess = () => resolve();
|
|
68
|
-
transaction.oncomplete = () => db.close();
|
|
69
|
-
});
|
|
59
|
+
function clearSubscriptionData() {
|
|
60
|
+
return withTransaction('readwrite', (store) => store.delete(DATA_KEY));
|
|
70
61
|
}
|
package/dist/client/renewal.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Push subscription renewal handler for service worker
|
|
3
3
|
* Handles the 'pushsubscriptionchange' event when browser rotates subscriptions
|
|
4
4
|
*/
|
|
5
|
+
import { PushSubscription } from '../types';
|
|
5
6
|
/**
|
|
6
7
|
* Handle pushsubscriptionchange event in service worker
|
|
7
8
|
* Call this from your service worker's event listener
|
package/dist/client/renewal.js
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.handleSubscriptionChange = handleSubscriptionChange;
|
|
8
|
+
const encoding_1 = require("./encoding");
|
|
8
9
|
const indexedDb_1 = require("./indexedDb");
|
|
10
|
+
const subscribe_1 = require("./subscribe");
|
|
9
11
|
/**
|
|
10
12
|
* Handle pushsubscriptionchange event in service worker
|
|
11
13
|
* Call this from your service worker's event listener
|
|
@@ -31,7 +33,7 @@ async function handleSubscriptionChange(event, renewEndpoint) {
|
|
|
31
33
|
console.error('Failed to create new subscription');
|
|
32
34
|
return false;
|
|
33
35
|
}
|
|
34
|
-
// Build renewal request
|
|
36
|
+
// Build renewal request (uses SubscriptionRequest type)
|
|
35
37
|
const renewalRequest = {
|
|
36
38
|
subscription: newSubscription,
|
|
37
39
|
deviceId: data.deviceId,
|
|
@@ -62,38 +64,15 @@ async function resubscribe(data) {
|
|
|
62
64
|
try {
|
|
63
65
|
const self = globalThis;
|
|
64
66
|
const registration = self.registration;
|
|
65
|
-
const applicationServerKey = urlBase64ToUint8Array(data.publicKey);
|
|
67
|
+
const applicationServerKey = (0, encoding_1.urlBase64ToUint8Array)(data.publicKey);
|
|
66
68
|
const subscription = await registration.pushManager.subscribe({
|
|
67
69
|
userVisibleOnly: true,
|
|
68
70
|
applicationServerKey
|
|
69
71
|
});
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
endpoint: subscription.endpoint,
|
|
73
|
-
keys: {
|
|
74
|
-
p256dh: json.keys?.p256dh || '',
|
|
75
|
-
auth: json.keys?.auth || ''
|
|
76
|
-
}
|
|
77
|
-
};
|
|
72
|
+
return (0, subscribe_1.toPushSubscription)(subscription);
|
|
78
73
|
}
|
|
79
74
|
catch (error) {
|
|
80
75
|
console.error('Resubscribe failed:', error);
|
|
81
76
|
return null;
|
|
82
77
|
}
|
|
83
78
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Convert base64 URL-encoded string to Uint8Array (service worker version)
|
|
86
|
-
*/
|
|
87
|
-
function urlBase64ToUint8Array(base64String) {
|
|
88
|
-
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
|
89
|
-
const base64 = (base64String + padding)
|
|
90
|
-
.replace(/-/g, '+')
|
|
91
|
-
.replace(/_/g, '/');
|
|
92
|
-
const rawData = atob(base64);
|
|
93
|
-
const buffer = new ArrayBuffer(rawData.length);
|
|
94
|
-
const outputArray = new Uint8Array(buffer);
|
|
95
|
-
for (let i = 0; i < rawData.length; ++i) {
|
|
96
|
-
outputArray[i] = rawData.charCodeAt(i);
|
|
97
|
-
}
|
|
98
|
-
return outputArray;
|
|
99
|
-
}
|
|
@@ -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
|
*/
|
|
@@ -14,16 +14,26 @@ export declare function requestPermission(): Promise<PushPermissionState>;
|
|
|
14
14
|
* Check if push notifications are supported
|
|
15
15
|
*/
|
|
16
16
|
export declare function isPushSupported(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Convert browser PushSubscription to our format
|
|
19
|
+
* Works in both main thread and service worker contexts
|
|
20
|
+
*/
|
|
21
|
+
export declare function toPushSubscription(browserSub: globalThis.PushSubscription): PushSubscription;
|
|
17
22
|
/**
|
|
18
23
|
* Subscribe to push notifications
|
|
19
|
-
*
|
|
24
|
+
*
|
|
25
|
+
* @returns Result with subscription request ready to send to backend
|
|
20
26
|
*/
|
|
21
|
-
export declare function subscribeToPush(vapidPublicKey: string, basePath?: string): Promise<SubscriptionRequest
|
|
27
|
+
export declare function subscribeToPush(vapidPublicKey: string, basePath?: string): Promise<ClientResult<SubscriptionRequest>>;
|
|
22
28
|
/**
|
|
23
29
|
* Unsubscribe from push notifications
|
|
30
|
+
*
|
|
31
|
+
* @returns Result indicating success or failure
|
|
24
32
|
*/
|
|
25
|
-
export declare function unsubscribeFromPush(): Promise<
|
|
33
|
+
export declare function unsubscribeFromPush(): Promise<ClientResult<void>>;
|
|
26
34
|
/**
|
|
27
35
|
* Get current push subscription if exists
|
|
36
|
+
*
|
|
37
|
+
* @returns Result with current subscription, or error if not subscribed
|
|
28
38
|
*/
|
|
29
|
-
export declare function getCurrentSubscription(): Promise<PushSubscription
|
|
39
|
+
export declare function getCurrentSubscription(): Promise<ClientResult<PushSubscription>>;
|
package/dist/client/subscribe.js
CHANGED
|
@@ -6,11 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.getPermissionState = getPermissionState;
|
|
7
7
|
exports.requestPermission = requestPermission;
|
|
8
8
|
exports.isPushSupported = isPushSupported;
|
|
9
|
+
exports.toPushSubscription = toPushSubscription;
|
|
9
10
|
exports.subscribeToPush = subscribeToPush;
|
|
10
11
|
exports.unsubscribeFromPush = unsubscribeFromPush;
|
|
11
12
|
exports.getCurrentSubscription = getCurrentSubscription;
|
|
12
13
|
const deviceId_1 = require("./deviceId");
|
|
13
14
|
const indexedDb_1 = require("./indexedDb");
|
|
15
|
+
const encoding_1 = require("./encoding");
|
|
14
16
|
/**
|
|
15
17
|
* Check current notification permission state
|
|
16
18
|
*/
|
|
@@ -32,6 +34,7 @@ function isPushSupported() {
|
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
36
|
* Convert browser PushSubscription to our format
|
|
37
|
+
* Works in both main thread and service worker contexts
|
|
35
38
|
*/
|
|
36
39
|
function toPushSubscription(browserSub) {
|
|
37
40
|
const json = browserSub.toJSON();
|
|
@@ -45,40 +48,50 @@ function toPushSubscription(browserSub) {
|
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* Subscribe to push notifications
|
|
48
|
-
*
|
|
51
|
+
*
|
|
52
|
+
* @returns Result with subscription request ready to send to backend
|
|
49
53
|
*/
|
|
50
54
|
async function subscribeToPush(vapidPublicKey, basePath = '/') {
|
|
51
55
|
if (!isPushSupported()) {
|
|
52
|
-
|
|
53
|
-
return null;
|
|
56
|
+
return { ok: false, error: 'Push notifications not supported' };
|
|
54
57
|
}
|
|
55
58
|
const permission = await requestPermission();
|
|
56
59
|
if (permission !== 'granted') {
|
|
57
|
-
|
|
58
|
-
|
|
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 };
|
|
59
89
|
}
|
|
60
|
-
const registration = await navigator.serviceWorker.ready;
|
|
61
|
-
const deviceId = (0, deviceId_1.getDeviceId)();
|
|
62
|
-
// Convert VAPID key to Uint8Array
|
|
63
|
-
const applicationServerKey = urlBase64ToUint8Array(vapidPublicKey);
|
|
64
|
-
const browserSub = await registration.pushManager.subscribe({
|
|
65
|
-
userVisibleOnly: true,
|
|
66
|
-
applicationServerKey
|
|
67
|
-
});
|
|
68
|
-
// Save data for service worker renewal
|
|
69
|
-
await (0, indexedDb_1.saveSubscriptionData)({
|
|
70
|
-
publicKey: vapidPublicKey,
|
|
71
|
-
deviceId,
|
|
72
|
-
basePath
|
|
73
|
-
});
|
|
74
|
-
return {
|
|
75
|
-
subscription: toPushSubscription(browserSub),
|
|
76
|
-
deviceId,
|
|
77
|
-
basePath
|
|
78
|
-
};
|
|
79
90
|
}
|
|
80
91
|
/**
|
|
81
92
|
* Unsubscribe from push notifications
|
|
93
|
+
*
|
|
94
|
+
* @returns Result indicating success or failure
|
|
82
95
|
*/
|
|
83
96
|
async function unsubscribeFromPush() {
|
|
84
97
|
try {
|
|
@@ -87,42 +100,29 @@ async function unsubscribeFromPush() {
|
|
|
87
100
|
if (subscription) {
|
|
88
101
|
await subscription.unsubscribe();
|
|
89
102
|
}
|
|
90
|
-
return true;
|
|
103
|
+
return { ok: true };
|
|
91
104
|
}
|
|
92
105
|
catch (error) {
|
|
93
|
-
|
|
94
|
-
return false;
|
|
106
|
+
const message = error instanceof Error ? error.message : 'Unsubscribe failed';
|
|
107
|
+
return { ok: false, error: message };
|
|
95
108
|
}
|
|
96
109
|
}
|
|
97
110
|
/**
|
|
98
111
|
* Get current push subscription if exists
|
|
112
|
+
*
|
|
113
|
+
* @returns Result with current subscription, or error if not subscribed
|
|
99
114
|
*/
|
|
100
115
|
async function getCurrentSubscription() {
|
|
101
116
|
try {
|
|
102
117
|
const registration = await navigator.serviceWorker.ready;
|
|
103
118
|
const subscription = await registration.pushManager.getSubscription();
|
|
104
119
|
if (!subscription) {
|
|
105
|
-
return
|
|
120
|
+
return { ok: false, error: 'No active subscription' };
|
|
106
121
|
}
|
|
107
|
-
return toPushSubscription(subscription);
|
|
108
|
-
}
|
|
109
|
-
catch {
|
|
110
|
-
return null;
|
|
122
|
+
return { ok: true, data: toPushSubscription(subscription) };
|
|
111
123
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
*/
|
|
116
|
-
function urlBase64ToUint8Array(base64String) {
|
|
117
|
-
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
|
118
|
-
const base64 = (base64String + padding)
|
|
119
|
-
.replace(/-/g, '+')
|
|
120
|
-
.replace(/_/g, '/');
|
|
121
|
-
const rawData = window.atob(base64);
|
|
122
|
-
const buffer = new ArrayBuffer(rawData.length);
|
|
123
|
-
const outputArray = new Uint8Array(buffer);
|
|
124
|
-
for (let i = 0; i < rawData.length; ++i) {
|
|
125
|
-
outputArray[i] = rawData.charCodeAt(i);
|
|
124
|
+
catch (error) {
|
|
125
|
+
const message = error instanceof Error ? error.message : 'Failed to get subscription';
|
|
126
|
+
return { ok: false, error: message };
|
|
126
127
|
}
|
|
127
|
-
return outputArray;
|
|
128
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;
|