@markwharton/pwa-push 1.5.0 → 1.5.2

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.
@@ -81,13 +81,13 @@ const mockRegistration = {
81
81
  (0, vitest_1.describe)('Push subscription renewal', () => {
82
82
  (0, vitest_1.describe)('handleSubscriptionChange', () => {
83
83
  const mockEvent = {};
84
- (0, vitest_1.it)('returns false when no stored data', async () => {
84
+ (0, vitest_1.it)('returns error when no stored data', async () => {
85
85
  const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
86
86
  vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue(null);
87
87
  const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
88
88
  const result = await handleSubscriptionChange(mockEvent, '/api/renew');
89
- (0, vitest_1.expect)(result).toBe(false);
90
- (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('No subscription data found for renewal');
89
+ (0, vitest_1.expect)(result.ok).toBe(false);
90
+ (0, vitest_1.expect)(result.error).toBe('No subscription data found for renewal');
91
91
  });
92
92
  (0, vitest_1.it)('renews subscription successfully', async () => {
93
93
  const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
@@ -98,15 +98,14 @@ const mockRegistration = {
98
98
  });
99
99
  const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
100
100
  const result = await handleSubscriptionChange(mockEvent, '/api/renew');
101
- (0, vitest_1.expect)(result).toBe(true);
101
+ (0, vitest_1.expect)(result.ok).toBe(true);
102
102
  (0, vitest_1.expect)(fetch).toHaveBeenCalledWith('/api/renew', {
103
103
  method: 'POST',
104
104
  headers: { 'Content-Type': 'application/json' },
105
105
  body: vitest_1.expect.stringContaining('test-device-id')
106
106
  });
107
- (0, vitest_1.expect)(console.log).toHaveBeenCalledWith('Push subscription renewed successfully');
108
107
  });
109
- (0, vitest_1.it)('returns false when resubscribe fails', async () => {
108
+ (0, vitest_1.it)('returns error when resubscribe fails', async () => {
110
109
  const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
111
110
  vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
112
111
  publicKey: 'test-key',
@@ -116,10 +115,10 @@ const mockRegistration = {
116
115
  mockPushManager.subscribe.mockRejectedValueOnce(new Error('Failed'));
117
116
  const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
118
117
  const result = await handleSubscriptionChange(mockEvent, '/api/renew');
119
- (0, vitest_1.expect)(result).toBe(false);
120
- (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('Failed to create new subscription');
118
+ (0, vitest_1.expect)(result.ok).toBe(false);
119
+ (0, vitest_1.expect)(result.error).toBe('Failed to create new subscription');
121
120
  });
122
- (0, vitest_1.it)('returns false when backend request fails', async () => {
121
+ (0, vitest_1.it)('returns error when backend request fails', async () => {
123
122
  const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
124
123
  vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
125
124
  publicKey: 'test-key',
@@ -132,8 +131,8 @@ const mockRegistration = {
132
131
  });
133
132
  const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
134
133
  const result = await handleSubscriptionChange(mockEvent, '/api/renew');
135
- (0, vitest_1.expect)(result).toBe(false);
136
- (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('Renewal request failed:', 500);
134
+ (0, vitest_1.expect)(result.ok).toBe(false);
135
+ (0, vitest_1.expect)(result.error).toBe('Renewal request failed: 500');
137
136
  });
138
137
  (0, vitest_1.it)('sends correct renewal request body', async () => {
139
138
  const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
@@ -164,8 +163,8 @@ const mockRegistration = {
164
163
  global.fetch.mockRejectedValueOnce(new Error('Network error'));
165
164
  const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
166
165
  const result = await handleSubscriptionChange(mockEvent, '/api/renew');
167
- (0, vitest_1.expect)(result).toBe(false);
168
- (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('Subscription renewal failed:', vitest_1.expect.any(Error));
166
+ (0, vitest_1.expect)(result.ok).toBe(false);
167
+ (0, vitest_1.expect)(result.error).toBe('Network error');
169
168
  });
170
169
  });
171
170
  });
@@ -40,7 +40,8 @@ function mockClientDependencies() {
40
40
  getDeviceId: vitest_1.vi.fn(() => 'mock-device-id')
41
41
  }));
42
42
  vitest_1.vi.mock('../../client/indexedDb', () => ({
43
- saveSubscriptionData: vitest_1.vi.fn(() => Promise.resolve())
43
+ saveSubscriptionData: vitest_1.vi.fn(() => Promise.resolve()),
44
+ clearSubscriptionData: vitest_1.vi.fn(() => Promise.resolve())
44
45
  }));
45
46
  vitest_1.vi.mock('../../client/encoding', () => ({
46
47
  urlBase64ToUint8Array: vitest_1.vi.fn(() => new Uint8Array([1, 2, 3]))
@@ -237,11 +238,19 @@ function setupEmptyNavigator() {
237
238
  (0, vitest_1.expect)(result.ok).toBe(true);
238
239
  (0, vitest_1.expect)(mockSubscription.unsubscribe).toHaveBeenCalled();
239
240
  });
241
+ (0, vitest_1.it)('clears subscription data from IndexedDB', async () => {
242
+ const { clearSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
243
+ const { unsubscribeFromPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
244
+ await unsubscribeFromPush();
245
+ (0, vitest_1.expect)(clearSubscriptionData).toHaveBeenCalled();
246
+ });
240
247
  (0, vitest_1.it)('succeeds when no subscription exists', async () => {
241
248
  mockPushManager.getSubscription.mockResolvedValueOnce(null);
249
+ const { clearSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
242
250
  const { unsubscribeFromPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
243
251
  const result = await unsubscribeFromPush();
244
252
  (0, vitest_1.expect)(result.ok).toBe(true);
253
+ (0, vitest_1.expect)(clearSubscriptionData).toHaveBeenCalled();
245
254
  });
246
255
  (0, vitest_1.it)('handles unsubscribe error', async () => {
247
256
  mockPushManager.getSubscription.mockRejectedValueOnce(new Error('Failed'));
@@ -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 { Result } from '@markwharton/pwa-core/types';
5
6
  import { PushSubscription } from '../shared';
6
7
  /**
7
8
  * Handles the pushsubscriptionchange event in a service worker.
@@ -9,14 +10,14 @@ import { PushSubscription } from '../shared';
9
10
  * rotates push subscriptions (typically for security reasons).
10
11
  * @param event - The pushsubscriptionchange event from the service worker
11
12
  * @param renewEndpoint - The backend endpoint to send the renewal request to
12
- * @returns True if renewal succeeded, false otherwise
13
+ * @returns Result with ok=true on success, or error message on failure
13
14
  * @example
14
15
  * // In your service worker (sw.js):
15
16
  * self.addEventListener('pushsubscriptionchange', (event) => {
16
17
  * event.waitUntil(handleSubscriptionChange(event, '/api/push/renew'));
17
18
  * });
18
19
  */
19
- export declare function handleSubscriptionChange(event: PushSubscriptionChangeEvent, renewEndpoint: string): Promise<boolean>;
20
+ export declare function handleSubscriptionChange(event: PushSubscriptionChangeEvent, renewEndpoint: string): Promise<Result<void>>;
20
21
  /**
21
22
  * Type definition for the pushsubscriptionchange event.
22
23
  * Fired when the browser rotates push subscriptions.
@@ -5,6 +5,7 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.handleSubscriptionChange = handleSubscriptionChange;
8
+ const types_1 = require("@markwharton/pwa-core/types");
8
9
  const encoding_1 = require("./encoding");
9
10
  const indexedDb_1 = require("./indexedDb");
10
11
  const subscribe_1 = require("./subscribe");
@@ -14,7 +15,7 @@ const subscribe_1 = require("./subscribe");
14
15
  * rotates push subscriptions (typically for security reasons).
15
16
  * @param event - The pushsubscriptionchange event from the service worker
16
17
  * @param renewEndpoint - The backend endpoint to send the renewal request to
17
- * @returns True if renewal succeeded, false otherwise
18
+ * @returns Result with ok=true on success, or error message on failure
18
19
  * @example
19
20
  * // In your service worker (sw.js):
20
21
  * self.addEventListener('pushsubscriptionchange', (event) => {
@@ -26,14 +27,12 @@ async function handleSubscriptionChange(event, renewEndpoint) {
26
27
  // Get stored data from IndexedDB
27
28
  const data = await (0, indexedDb_1.getSubscriptionData)();
28
29
  if (!data) {
29
- console.error('No subscription data found for renewal');
30
- return false;
30
+ return (0, types_1.err)('No subscription data found for renewal');
31
31
  }
32
32
  // Resubscribe with same VAPID key
33
33
  const newSubscription = await resubscribe(data);
34
34
  if (!newSubscription) {
35
- console.error('Failed to create new subscription');
36
- return false;
35
+ return (0, types_1.err)('Failed to create new subscription');
37
36
  }
38
37
  // Build renewal request (uses SubscriptionRequest type)
39
38
  const renewalRequest = {
@@ -48,15 +47,13 @@ async function handleSubscriptionChange(event, renewEndpoint) {
48
47
  body: JSON.stringify(renewalRequest)
49
48
  });
50
49
  if (!response.ok) {
51
- console.error('Renewal request failed:', response.status);
52
- return false;
50
+ return (0, types_1.err)(`Renewal request failed: ${response.status}`);
53
51
  }
54
- console.log('Push subscription renewed successfully');
55
- return true;
52
+ return (0, types_1.okVoid)();
56
53
  }
57
54
  catch (error) {
58
- console.error('Subscription renewal failed:', error);
59
- return false;
55
+ const message = error instanceof Error ? error.message : 'Subscription renewal failed';
56
+ return (0, types_1.err)(message);
60
57
  }
61
58
  }
62
59
  /**
@@ -64,7 +64,7 @@ export declare function toPushSubscription(browserSub: globalThis.PushSubscripti
64
64
  export declare function subscribeToPush(vapidPublicKey: string, basePath?: string): Promise<Result<SubscriptionRequest>>;
65
65
  /**
66
66
  * Unsubscribes from push notifications.
67
- * Removes the browser push subscription if one exists.
67
+ * Removes the browser push subscription and clears stored subscription data.
68
68
  * @returns Result with ok=true on success, or error message on failure
69
69
  * @example
70
70
  * const result = await unsubscribeFromPush();
@@ -124,7 +124,7 @@ async function subscribeToPush(vapidPublicKey, basePath = '/') {
124
124
  }
125
125
  /**
126
126
  * Unsubscribes from push notifications.
127
- * Removes the browser push subscription if one exists.
127
+ * Removes the browser push subscription and clears stored subscription data.
128
128
  * @returns Result with ok=true on success, or error message on failure
129
129
  * @example
130
130
  * const result = await unsubscribeFromPush();
@@ -139,6 +139,8 @@ async function unsubscribeFromPush() {
139
139
  if (subscription) {
140
140
  await subscription.unsubscribe();
141
141
  }
142
+ // Clear stored subscription data from IndexedDB
143
+ await (0, indexedDb_1.clearSubscriptionData)();
142
144
  return (0, types_1.okVoid)();
143
145
  }
144
146
  catch (error) {
@@ -37,15 +37,15 @@ var PwaPush = (() => {
37
37
  "use strict";
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.ok = ok2;
40
- exports.okVoid = okVoid2;
41
- exports.err = err2;
40
+ exports.okVoid = okVoid3;
41
+ exports.err = err3;
42
42
  function ok2(data) {
43
43
  return { ok: true, data };
44
44
  }
45
- function okVoid2() {
45
+ function okVoid3() {
46
46
  return { ok: true };
47
47
  }
48
- function err2(error, statusCode) {
48
+ function err3(error, statusCode) {
49
49
  return statusCode !== void 0 ? { ok: false, error, statusCode } : { ok: false, error };
50
50
  }
51
51
  }
@@ -57,6 +57,9 @@ var PwaPush = (() => {
57
57
  handleSubscriptionChange: () => handleSubscriptionChange
58
58
  });
59
59
 
60
+ // src/client/renewal.ts
61
+ var import_types2 = __toESM(require_types());
62
+
60
63
  // src/client/encoding.ts
61
64
  function urlBase64ToUint8Array(base64String) {
62
65
  const padding = "=".repeat((4 - base64String.length % 4) % 4);
@@ -124,13 +127,11 @@ var PwaPush = (() => {
124
127
  try {
125
128
  const data = await getSubscriptionData();
126
129
  if (!data) {
127
- console.error("No subscription data found for renewal");
128
- return false;
130
+ return (0, import_types2.err)("No subscription data found for renewal");
129
131
  }
130
132
  const newSubscription = await resubscribe(data);
131
133
  if (!newSubscription) {
132
- console.error("Failed to create new subscription");
133
- return false;
134
+ return (0, import_types2.err)("Failed to create new subscription");
134
135
  }
135
136
  const renewalRequest = {
136
137
  subscription: newSubscription,
@@ -143,14 +144,12 @@ var PwaPush = (() => {
143
144
  body: JSON.stringify(renewalRequest)
144
145
  });
145
146
  if (!response.ok) {
146
- console.error("Renewal request failed:", response.status);
147
- return false;
147
+ return (0, import_types2.err)(`Renewal request failed: ${response.status}`);
148
148
  }
149
- console.log("Push subscription renewed successfully");
150
- return true;
149
+ return (0, import_types2.okVoid)();
151
150
  } catch (error) {
152
- console.error("Subscription renewal failed:", error);
153
- return false;
151
+ const message = error instanceof Error ? error.message : "Subscription renewal failed";
152
+ return (0, import_types2.err)(message);
154
153
  }
155
154
  }
156
155
  async function resubscribe(data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markwharton/pwa-push",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Web push notifications for Azure PWA projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,11 +30,11 @@
30
30
  "clean": "rm -rf dist"
31
31
  },
32
32
  "peerDependencies": {
33
- "@markwharton/pwa-core": "^1.1.0",
33
+ "@markwharton/pwa-core": "^1.7.0",
34
34
  "web-push": "^3.6.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@markwharton/pwa-core": "^1.1.0",
37
+ "@markwharton/pwa-core": "^1.7.0",
38
38
  "@types/node": "^20.10.0",
39
39
  "@types/web-push": "^3.6.4",
40
40
  "esbuild": "^0.27.2",