@markwharton/pwa-push 1.1.0 → 1.2.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.
@@ -1,7 +1,18 @@
1
- import { PushSubscription, NotificationPayload, SendResult } from '../types';
1
+ import { Result } from '@markwharton/pwa-core/types';
2
+ import { PushSubscription, NotificationPayload } from '../types';
2
3
  /**
3
- * Initialize VAPID configuration - call once at startup
4
- * Fails fast if keys are missing
4
+ * Initializes VAPID configuration for Web Push. Call once at application startup.
5
+ * @param config - VAPID configuration
6
+ * @param config.publicKey - The VAPID public key
7
+ * @param config.privateKey - The VAPID private key
8
+ * @param config.subject - Contact email (mailto:) or URL
9
+ * @throws Error if any required configuration is missing
10
+ * @example
11
+ * initPush({
12
+ * publicKey: process.env.VAPID_PUBLIC_KEY,
13
+ * privateKey: process.env.VAPID_PRIVATE_KEY,
14
+ * subject: 'mailto:admin@example.com'
15
+ * });
5
16
  */
6
17
  export declare function initPush(config: {
7
18
  publicKey?: string;
@@ -9,30 +20,67 @@ export declare function initPush(config: {
9
20
  subject?: string;
10
21
  }): void;
11
22
  /**
12
- * Auto-initialize from environment variables
23
+ * Initializes VAPID from environment variables.
24
+ * Reads VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT.
25
+ * @throws Error if required environment variables are missing
26
+ * @example
27
+ * initPushFromEnv(); // Uses process.env automatically
13
28
  */
14
29
  export declare function initPushFromEnv(): void;
15
30
  /**
16
- * Get the VAPID public key (for client subscription)
31
+ * Gets the VAPID public key for client-side subscription.
32
+ * @returns The VAPID public key string
33
+ * @throws Error if initPush() has not been called
34
+ * @example
35
+ * // Server endpoint
36
+ * app.get('/api/vapid-key', (req, res) => {
37
+ * res.json({ publicKey: getVapidPublicKey() });
38
+ * });
17
39
  */
18
40
  export declare function getVapidPublicKey(): string;
19
41
  /**
20
- * Send a push notification to a single subscription
21
- *
22
- * @returns Result with ok status, or error details on failure
42
+ * Sends a push notification to a single subscription.
43
+ * @param subscription - The push subscription to send to
44
+ * @param payload - The notification payload (title, body, etc.)
45
+ * @returns Result with ok=true on success, or error message and statusCode on failure
46
+ * @example
47
+ * const result = await sendPushNotification(subscription, {
48
+ * title: 'New Message',
49
+ * body: 'You have a new message'
50
+ * });
23
51
  */
24
- export declare function sendPushNotification(subscription: PushSubscription, payload: NotificationPayload): Promise<SendResult>;
52
+ export declare function sendPushNotification(subscription: PushSubscription, payload: NotificationPayload): Promise<Result<void>>;
25
53
  /**
26
- * Send a push notification to multiple subscriptions
27
- * Returns array of results, one per subscription
54
+ * Sends a push notification to multiple subscriptions in parallel.
55
+ * @param subscriptions - Array of push subscriptions
56
+ * @param payload - The notification payload (title, body, etc.)
57
+ * @returns Array of Results, one per subscription (in same order)
58
+ * @example
59
+ * const results = await sendPushToAll(subscriptions, { title: 'Alert', body: 'System update' });
60
+ * const failed = results.filter(r => !r.ok);
28
61
  */
29
- export declare function sendPushToAll(subscriptions: PushSubscription[], payload: NotificationPayload): Promise<SendResult[]>;
62
+ export declare function sendPushToAll(subscriptions: PushSubscription[], payload: NotificationPayload): Promise<Result<void>[]>;
30
63
  /**
31
- * Send push notification with detailed error information
32
- * Auto-injects basePath from subscription to payload if not already set
64
+ * Sends a push notification with detailed error information.
65
+ * Automatically injects basePath from subscription if not set in payload.
66
+ * @param subscription - The push subscription to send to
67
+ * @param payload - The notification payload
68
+ * @returns Result with ok=true on success, or error with statusCode on failure
69
+ * @example
70
+ * const result = await sendPushWithDetails(subscription, { title: 'Alert' });
71
+ * if (!result.ok && isSubscriptionExpired(result.statusCode)) {
72
+ * await deleteSubscription(subscription);
73
+ * }
33
74
  */
34
- export declare function sendPushWithDetails(subscription: PushSubscription, payload: NotificationPayload): Promise<SendResult>;
75
+ export declare function sendPushWithDetails(subscription: PushSubscription, payload: NotificationPayload): Promise<Result<void>>;
35
76
  /**
36
- * Check if a subscription is expired (410 Gone)
77
+ * Checks if a status code indicates an expired subscription (410 Gone).
78
+ * Use this to clean up stale subscriptions from your database.
79
+ * @param statusCode - The HTTP status code from a failed push attempt
80
+ * @returns True if the subscription should be deleted
81
+ * @example
82
+ * if (!result.ok && isSubscriptionExpired(result.statusCode)) {
83
+ * await db.deleteSubscription(subscription.endpoint);
84
+ * }
37
85
  */
38
86
  export declare function isSubscriptionExpired(statusCode: number | undefined): boolean;
@@ -11,13 +11,24 @@ exports.sendPushToAll = sendPushToAll;
11
11
  exports.sendPushWithDetails = sendPushWithDetails;
12
12
  exports.isSubscriptionExpired = isSubscriptionExpired;
13
13
  const web_push_1 = __importDefault(require("web-push"));
14
+ const types_1 = require("@markwharton/pwa-core/types");
14
15
  /**
15
16
  * Server-side Web Push notification utilities
16
17
  */
17
18
  let vapidConfig = null;
18
19
  /**
19
- * Initialize VAPID configuration - call once at startup
20
- * Fails fast if keys are missing
20
+ * Initializes VAPID configuration for Web Push. Call once at application startup.
21
+ * @param config - VAPID configuration
22
+ * @param config.publicKey - The VAPID public key
23
+ * @param config.privateKey - The VAPID private key
24
+ * @param config.subject - Contact email (mailto:) or URL
25
+ * @throws Error if any required configuration is missing
26
+ * @example
27
+ * initPush({
28
+ * publicKey: process.env.VAPID_PUBLIC_KEY,
29
+ * privateKey: process.env.VAPID_PRIVATE_KEY,
30
+ * subject: 'mailto:admin@example.com'
31
+ * });
21
32
  */
22
33
  function initPush(config) {
23
34
  const { publicKey, privateKey, subject } = config;
@@ -31,7 +42,11 @@ function initPush(config) {
31
42
  web_push_1.default.setVapidDetails(subject, publicKey, privateKey);
32
43
  }
33
44
  /**
34
- * Auto-initialize from environment variables
45
+ * Initializes VAPID from environment variables.
46
+ * Reads VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT.
47
+ * @throws Error if required environment variables are missing
48
+ * @example
49
+ * initPushFromEnv(); // Uses process.env automatically
35
50
  */
36
51
  function initPushFromEnv() {
37
52
  initPush({
@@ -41,7 +56,14 @@ function initPushFromEnv() {
41
56
  });
42
57
  }
43
58
  /**
44
- * Get the VAPID public key (for client subscription)
59
+ * Gets the VAPID public key for client-side subscription.
60
+ * @returns The VAPID public key string
61
+ * @throws Error if initPush() has not been called
62
+ * @example
63
+ * // Server endpoint
64
+ * app.get('/api/vapid-key', (req, res) => {
65
+ * res.json({ publicKey: getVapidPublicKey() });
66
+ * });
45
67
  */
46
68
  function getVapidPublicKey() {
47
69
  if (!vapidConfig) {
@@ -50,28 +72,47 @@ function getVapidPublicKey() {
50
72
  return vapidConfig.publicKey;
51
73
  }
52
74
  /**
53
- * Send a push notification to a single subscription
54
- *
55
- * @returns Result with ok status, or error details on failure
75
+ * Sends a push notification to a single subscription.
76
+ * @param subscription - The push subscription to send to
77
+ * @param payload - The notification payload (title, body, etc.)
78
+ * @returns Result with ok=true on success, or error message and statusCode on failure
79
+ * @example
80
+ * const result = await sendPushNotification(subscription, {
81
+ * title: 'New Message',
82
+ * body: 'You have a new message'
83
+ * });
56
84
  */
57
85
  async function sendPushNotification(subscription, payload) {
58
86
  return sendPushWithDetails(subscription, payload);
59
87
  }
60
88
  /**
61
- * Send a push notification to multiple subscriptions
62
- * Returns array of results, one per subscription
89
+ * Sends a push notification to multiple subscriptions in parallel.
90
+ * @param subscriptions - Array of push subscriptions
91
+ * @param payload - The notification payload (title, body, etc.)
92
+ * @returns Array of Results, one per subscription (in same order)
93
+ * @example
94
+ * const results = await sendPushToAll(subscriptions, { title: 'Alert', body: 'System update' });
95
+ * const failed = results.filter(r => !r.ok);
63
96
  */
64
97
  async function sendPushToAll(subscriptions, payload) {
65
98
  const results = await Promise.all(subscriptions.map(sub => sendPushWithDetails(sub, payload)));
66
99
  return results;
67
100
  }
68
101
  /**
69
- * Send push notification with detailed error information
70
- * Auto-injects basePath from subscription to payload if not already set
102
+ * Sends a push notification with detailed error information.
103
+ * Automatically injects basePath from subscription if not set in payload.
104
+ * @param subscription - The push subscription to send to
105
+ * @param payload - The notification payload
106
+ * @returns Result with ok=true on success, or error with statusCode on failure
107
+ * @example
108
+ * const result = await sendPushWithDetails(subscription, { title: 'Alert' });
109
+ * if (!result.ok && isSubscriptionExpired(result.statusCode)) {
110
+ * await deleteSubscription(subscription);
111
+ * }
71
112
  */
72
113
  async function sendPushWithDetails(subscription, payload) {
73
114
  if (!vapidConfig) {
74
- return { ok: false, error: 'Push not initialized' };
115
+ return (0, types_1.err)('Push not initialized');
75
116
  }
76
117
  // Auto-inject basePath from subscription if not set in payload
77
118
  const payloadWithBasePath = {
@@ -80,19 +121,22 @@ async function sendPushWithDetails(subscription, payload) {
80
121
  };
81
122
  try {
82
123
  await web_push_1.default.sendNotification(subscription, JSON.stringify(payloadWithBasePath));
83
- return { ok: true };
124
+ return (0, types_1.okVoid)();
84
125
  }
85
126
  catch (error) {
86
127
  const webPushError = error;
87
- return {
88
- ok: false,
89
- error: webPushError.message || 'Unknown error',
90
- statusCode: webPushError.statusCode
91
- };
128
+ return (0, types_1.err)(webPushError.message || 'Unknown error', webPushError.statusCode);
92
129
  }
93
130
  }
94
131
  /**
95
- * Check if a subscription is expired (410 Gone)
132
+ * Checks if a status code indicates an expired subscription (410 Gone).
133
+ * Use this to clean up stale subscriptions from your database.
134
+ * @param statusCode - The HTTP status code from a failed push attempt
135
+ * @returns True if the subscription should be deleted
136
+ * @example
137
+ * if (!result.ok && isSubscriptionExpired(result.statusCode)) {
138
+ * await db.deleteSubscription(subscription.endpoint);
139
+ * }
96
140
  */
97
141
  function isSubscriptionExpired(statusCode) {
98
142
  return statusCode === 410;
package/dist/sw.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Service Worker bundle entry point
3
+ * Exports functions for use via importScripts()
4
+ *
5
+ * Usage in sw.js:
6
+ * ```
7
+ * importScripts('/path/to/pwa-push-sw.js');
8
+ *
9
+ * self.addEventListener('pushsubscriptionchange', (event) => {
10
+ * event.waitUntil(PwaPush.handleSubscriptionChange('/api/push/renew'));
11
+ * });
12
+ * ```
13
+ */
14
+ export { handleSubscriptionChange } from './client/renewal';
package/dist/sw.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Service Worker bundle entry point
4
+ * Exports functions for use via importScripts()
5
+ *
6
+ * Usage in sw.js:
7
+ * ```
8
+ * importScripts('/path/to/pwa-push-sw.js');
9
+ *
10
+ * self.addEventListener('pushsubscriptionchange', (event) => {
11
+ * event.waitUntil(PwaPush.handleSubscriptionChange('/api/push/renew'));
12
+ * });
13
+ * ```
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.handleSubscriptionChange = void 0;
17
+ var renewal_1 = require("./client/renewal");
18
+ Object.defineProperty(exports, "handleSubscriptionChange", { enumerable: true, get: function () { return renewal_1.handleSubscriptionChange; } });
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Push notification types - shared between server and client
3
3
  */
4
+ export { Result, ok, okVoid, err } from '@markwharton/pwa-core/types';
4
5
  export interface PushSubscription {
5
6
  endpoint: string;
6
7
  keys: {
@@ -25,24 +26,6 @@ export interface VapidConfig {
25
26
  privateKey: string;
26
27
  subject: string;
27
28
  }
28
- /**
29
- * Result type for push operations.
30
- * Aligned with Result<T> pattern from @markwharton/pwa-core
31
- */
32
- export interface SendResult {
33
- ok: boolean;
34
- error?: string;
35
- statusCode?: number;
36
- }
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;
45
- }
46
29
  export interface SubscriptionRequest {
47
30
  subscription: PushSubscription;
48
31
  deviceId: string;
package/dist/types.js CHANGED
@@ -3,3 +3,9 @@
3
3
  * Push notification types - shared between server and client
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.err = exports.okVoid = exports.ok = void 0;
7
+ // Re-export Result from pwa-core for convenience
8
+ var types_1 = require("@markwharton/pwa-core/types");
9
+ Object.defineProperty(exports, "ok", { enumerable: true, get: function () { return types_1.ok; } });
10
+ Object.defineProperty(exports, "okVoid", { enumerable: true, get: function () { return types_1.okVoid; } });
11
+ Object.defineProperty(exports, "err", { enumerable: true, get: function () { return types_1.err; } });
package/package.json CHANGED
@@ -1,24 +1,29 @@
1
1
  {
2
2
  "name": "@markwharton/pwa-push",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Web push notifications for Azure PWA projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": "./dist/index.js",
9
9
  "./server": "./dist/server/index.js",
10
- "./client": "./dist/client/index.js"
10
+ "./client": "./dist/client/index.js",
11
+ "./sw": "./dist/pwa-push-sw.js"
11
12
  },
12
13
  "scripts": {
13
- "build": "tsc",
14
+ "build": "tsc && npm run build:sw",
15
+ "build:sw": "esbuild src/sw.ts --bundle --outfile=dist/pwa-push-sw.js --format=iife --global-name=PwaPush",
14
16
  "clean": "rm -rf dist"
15
17
  },
16
18
  "peerDependencies": {
19
+ "@markwharton/pwa-core": "^1.1.0",
17
20
  "web-push": "^3.6.0"
18
21
  },
19
22
  "devDependencies": {
23
+ "@markwharton/pwa-core": "^1.1.0",
20
24
  "@types/node": "^20.10.0",
21
25
  "@types/web-push": "^3.6.4",
26
+ "esbuild": "^0.27.2",
22
27
  "typescript": "^5.3.0",
23
28
  "web-push": "^3.6.7"
24
29
  },
@@ -27,7 +32,7 @@
27
32
  ],
28
33
  "repository": {
29
34
  "type": "git",
30
- "url": "https://github.com/MarkWharton/pwa-packages.git",
35
+ "url": "git+https://github.com/MarkWharton/pwa-packages.git",
31
36
  "directory": "packages/push"
32
37
  },
33
38
  "author": "Mark Wharton",