@markwharton/pwa-push 1.0.0 → 1.0.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/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 +4 -25
- package/dist/client/subscribe.d.ts +5 -0
- package/dist/client/subscribe.js +4 -17
- package/dist/client/utils.d.ts +14 -0
- package/dist/client/utils.js +38 -0
- 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
|
|
@@ -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
|
-
}
|
|
@@ -14,6 +14,11 @@ 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
|
* Returns subscription request ready to send to backend
|
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();
|
|
@@ -60,7 +63,7 @@ async function subscribeToPush(vapidPublicKey, basePath = '/') {
|
|
|
60
63
|
const registration = await navigator.serviceWorker.ready;
|
|
61
64
|
const deviceId = (0, deviceId_1.getDeviceId)();
|
|
62
65
|
// Convert VAPID key to Uint8Array
|
|
63
|
-
const applicationServerKey = urlBase64ToUint8Array(vapidPublicKey);
|
|
66
|
+
const applicationServerKey = (0, encoding_1.urlBase64ToUint8Array)(vapidPublicKey);
|
|
64
67
|
const browserSub = await registration.pushManager.subscribe({
|
|
65
68
|
userVisibleOnly: true,
|
|
66
69
|
applicationServerKey
|
|
@@ -110,19 +113,3 @@ async function getCurrentSubscription() {
|
|
|
110
113
|
return null;
|
|
111
114
|
}
|
|
112
115
|
}
|
|
113
|
-
/**
|
|
114
|
-
* Convert base64 URL-encoded string to Uint8Array
|
|
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);
|
|
126
|
-
}
|
|
127
|
-
return outputArray;
|
|
128
|
-
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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;
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
}
|