@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.
@@ -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
- * Returns subscription request ready to send to backend
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 | null>;
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<boolean>;
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 | null>;
39
+ export declare function getCurrentSubscription(): Promise<ClientResult<PushSubscription>>;
@@ -48,40 +48,50 @@ function toPushSubscription(browserSub) {
48
48
  }
49
49
  /**
50
50
  * Subscribe to push notifications
51
- * Returns subscription request ready to send to backend
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
- console.error('Push notifications not supported');
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
- console.warn('Push permission denied');
61
- return null;
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
- console.error('Unsubscribe failed:', error);
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 null;
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
- return null;
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
  }
@@ -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<boolean>;
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
  /**
@@ -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
- const result = await sendPushWithDetails(subscription, payload);
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 { success: false, error: 'Push not initialized' };
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(payload));
76
- return { success: true };
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
- success: false,
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
- success: boolean;
33
+ ok: boolean;
26
34
  error?: string;
27
35
  statusCode?: number;
28
36
  }
29
- export interface SubscriptionRequest {
30
- subscription: PushSubscription;
31
- deviceId: string;
32
- basePath?: string;
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 RenewalRequest {
46
+ export interface SubscriptionRequest {
35
47
  subscription: PushSubscription;
36
48
  deviceId: string;
37
49
  basePath?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markwharton/pwa-push",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Web push notifications for Azure PWA projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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;
@@ -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
- }