@webmobix/pulse-leo-sdk 0.0.1 → 0.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.
- package/README.md +217 -26
- package/dist/checkin.d.ts +76 -0
- package/dist/errors.d.ts +52 -0
- package/dist/events.d.ts +20 -0
- package/dist/index.d.ts +53 -31
- package/dist/index.js +448 -82
- package/dist/sw.js +88 -26
- package/dist/types.d.ts +77 -14
- package/dist/utils.d.ts +7 -8
- package/dist/vapid.d.ts +9 -0
- package/package.json +17 -7
- package/dist/sw.d.ts +0 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A lightweight browser SDK for integrating PulseLeo Push Notifications.
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install pulse-leo-sdk
|
|
8
|
+
npm install @webmobix/pulse-leo-sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
@@ -15,11 +15,11 @@ npm install pulse-leo-sdk
|
|
|
15
15
|
Initialize the client with your VAPID Public Key (found in your Dashboard).
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import { PushClient } from "pulse-leo-sdk";
|
|
18
|
+
import { PushClient } from "@webmobix/pulse-leo-sdk";
|
|
19
19
|
|
|
20
20
|
const pushClient = new PushClient({
|
|
21
21
|
vapidPublicKey: "YOUR_VAPID_PUBLIC_KEY",
|
|
22
|
-
backendRegisterUrl: "
|
|
22
|
+
backendRegisterUrl: "/api/register", // Or your backend proxy
|
|
23
23
|
serviceWorkerPath: "/sw.js", // Default
|
|
24
24
|
tags: {
|
|
25
25
|
category: "news",
|
|
@@ -43,7 +43,7 @@ Add a postinstall script to your `package.json`:
|
|
|
43
43
|
```json
|
|
44
44
|
{
|
|
45
45
|
"scripts": {
|
|
46
|
-
"postinstall": "cp node_modules/pulse-leo-sdk/dist/sw.js public/sw.js"
|
|
46
|
+
"postinstall": "cp node_modules/@webmobix/pulse-leo-sdk/dist/sw.js public/sw.js"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
```
|
|
@@ -66,7 +66,7 @@ export default defineConfig({
|
|
|
66
66
|
viteStaticCopy({
|
|
67
67
|
targets: [
|
|
68
68
|
{
|
|
69
|
-
src: 'node_modules/pulse-leo-sdk/dist/sw.js',
|
|
69
|
+
src: 'node_modules/@webmobix/pulse-leo-sdk/dist/sw.js',
|
|
70
70
|
dest: '.'
|
|
71
71
|
}
|
|
72
72
|
]
|
|
@@ -75,17 +75,9 @@ export default defineConfig({
|
|
|
75
75
|
});
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const pushClient = new PushClient({
|
|
82
|
-
vapidPublicKey: "YOUR_VAPID_PUBLIC_KEY",
|
|
83
|
-
backendRegisterUrl: "/api/register",
|
|
84
|
-
// serviceWorkerPath: '/sw.js' is the default, no need to specify
|
|
85
|
-
});
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
> **Note**: The `?url` import method (e.g., `import swUrl from 'pulse-leo-sdk/sw.js?url'`) is not recommended for service workers due to scope restrictions during development. Use the copy method instead.
|
|
78
|
+
> `dist/sw.js` is a **single self-contained file** (it imports nothing), so copying just that one file to
|
|
79
|
+
> your site root is all a host needs. (SDK 0.2.0 shipped a `sw.js` that imported a shared chunk and failed
|
|
80
|
+
> to load when copied alone; fixed in 0.2.1 and guarded by a build check.)
|
|
89
81
|
|
|
90
82
|
#### Option B: Custom Service Worker
|
|
91
83
|
|
|
@@ -93,7 +85,7 @@ If you need to add your own service worker logic, you can import the SDK's servi
|
|
|
93
85
|
|
|
94
86
|
```typescript
|
|
95
87
|
// In your custom-sw.js
|
|
96
|
-
import "pulse-leo-sdk/sw";
|
|
88
|
+
import "@webmobix/pulse-leo-sdk/sw";
|
|
97
89
|
|
|
98
90
|
// Add your own service worker logic here
|
|
99
91
|
self.addEventListener("install", (event) => {
|
|
@@ -116,18 +108,99 @@ const pushClient = new PushClient({
|
|
|
116
108
|
Call the `register()` method, typically in response to a user action (like a button click) to request permission.
|
|
117
109
|
|
|
118
110
|
```typescript
|
|
111
|
+
import { PermissionDeniedError, SubscriptionError } from "@webmobix/pulse-leo-sdk";
|
|
112
|
+
|
|
119
113
|
const button = document.getElementById("enable-push");
|
|
120
114
|
button.addEventListener("click", async () => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
115
|
+
try {
|
|
116
|
+
const success = await pushClient.register();
|
|
117
|
+
if (success) {
|
|
118
|
+
console.log("Successfully subscribed!");
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (error instanceof PermissionDeniedError) {
|
|
122
|
+
console.log("User denied notification permission");
|
|
123
|
+
} else if (error instanceof SubscriptionError) {
|
|
124
|
+
console.error("Subscription failed:", error.message);
|
|
125
|
+
}
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
If the browser already has a subscription, `register()` does not call your backend again. When your
|
|
131
|
+
server may have lost the device (for example it was removed from another browser), re-send it with
|
|
132
|
+
`register({ force: true })`; registration is an upsert keyed by endpoint, so this is idempotent.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
await pushClient.register({ force: true });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 4. Unsubscribe from Notifications
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const success = await pushClient.unsubscribe();
|
|
142
|
+
if (success) {
|
|
143
|
+
console.log("Successfully unsubscribed!");
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
When there is no authenticated backend session any more (after a logout, or a remote removal),
|
|
148
|
+
skip the unregister relay call and only remove the browser subscription:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
await pushClient.unsubscribe({ notifyBackend: false });
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 5. Subscriber Check-ins (Activity Analytics)
|
|
155
|
+
|
|
156
|
+
By default, every successfully displayed push triggers a lightweight
|
|
157
|
+
"check-in" to your backend relay, which PulseLeo uses for monthly
|
|
158
|
+
active-subscriber analytics (a check-in is also recorded when a subscriber
|
|
159
|
+
first registers). No secrets are involved — the service worker posts only
|
|
160
|
+
the subscription endpoint to your relay, which attaches the API key
|
|
161
|
+
server-side:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const pushClient = new PushClient({
|
|
165
|
+
vapidPublicKey: "YOUR_PUBLIC_KEY",
|
|
166
|
+
backendRegisterUrl: "/api/push/register",
|
|
167
|
+
// Required for activity analytics: explicit check-in relay URL.
|
|
168
|
+
// The SDK never derives it; missing it fails loudly with CheckinConfigError.
|
|
169
|
+
backendCheckinUrl: "/api/push/checkin",
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Example relay route (Express-style pseudocode for your backend):
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
app.post("/api/push/checkin", async (req, res) => {
|
|
177
|
+
await fetch("https://api.pulseleo.com/checkin", {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers: {
|
|
180
|
+
"Content-Type": "application/json",
|
|
181
|
+
Authorization: `Bearer ${process.env.PULSE_LEO_TOKEN}`, // never in the browser
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({ endpoint: req.body.endpoint }),
|
|
184
|
+
});
|
|
185
|
+
res.sendStatus(204);
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Push-driven check-ins are throttled (at most one every 6 hours per
|
|
190
|
+
subscriber). If you also want liveness pings while your site is open,
|
|
191
|
+
start the opt-in periodic check-in after registration:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// Fires immediately, then every 6h while the page stays open.
|
|
195
|
+
pushClient.startPeriodicCheckin();
|
|
196
|
+
// Custom interval:
|
|
197
|
+
pushClient.startPeriodicCheckin(60 * 60 * 1000); // hourly
|
|
198
|
+
|
|
199
|
+
// Stop when appropriate (unsubscribe() stops it automatically):
|
|
200
|
+
pushClient.stopPeriodicCheckin();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 6. Check Subscription Status
|
|
131
204
|
|
|
132
205
|
```typescript
|
|
133
206
|
const isSubscribed = await pushClient.isSubscribed();
|
|
@@ -136,15 +209,133 @@ if (isSubscribed) {
|
|
|
136
209
|
}
|
|
137
210
|
```
|
|
138
211
|
|
|
212
|
+
### 6. Check Support and Permission State
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { isPushSupported } from "@webmobix/pulse-leo-sdk";
|
|
216
|
+
|
|
217
|
+
// Works without a client or VAPID key, so it can gate UI early.
|
|
218
|
+
if (!isPushSupported()) {
|
|
219
|
+
// Show "not supported in this browser"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const permission = pushClient.getPermissionState();
|
|
223
|
+
// Returns: 'granted', 'denied', 'default', or 'unsupported'
|
|
224
|
+
|
|
225
|
+
if (permission === 'denied') {
|
|
226
|
+
// Show message that user needs to enable permissions in browser settings
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`pushClient.isSupported()` returns the same value as `isPushSupported()`.
|
|
231
|
+
|
|
232
|
+
> **Upgrading from 0.1.x:** in browsers without Web Push, `getPermissionState()` now returns
|
|
233
|
+
> `'unsupported'` instead of `'denied'`. Where you only cared about "cannot prompt", treat
|
|
234
|
+
> `'unsupported'` like the old `'denied'`. `PermissionState` is now exported from the package.
|
|
235
|
+
|
|
236
|
+
### 7. Update Expired Subscription
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
try {
|
|
240
|
+
await pushClient.updateSubscription();
|
|
241
|
+
console.log("Subscription updated!");
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error("Failed to update subscription:", error);
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 9. Event Handling
|
|
248
|
+
|
|
249
|
+
Subscribe to events for real-time updates:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Listen for subscription changes
|
|
253
|
+
pushClient.on('subscriptionChange', (data) => {
|
|
254
|
+
if (data.subscribed) {
|
|
255
|
+
console.log("User subscribed");
|
|
256
|
+
} else {
|
|
257
|
+
console.log("User unsubscribed");
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Listen for permission changes
|
|
262
|
+
pushClient.on('permissionChange', (data) => {
|
|
263
|
+
console.log("Permission changed to:", data.permission);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Remove listener when needed
|
|
267
|
+
const handler = (data) => console.log(data);
|
|
268
|
+
pushClient.on('subscriptionChange', handler);
|
|
269
|
+
pushClient.off('subscriptionChange', handler);
|
|
270
|
+
```
|
|
271
|
+
|
|
139
272
|
## Configuration Options
|
|
140
273
|
|
|
141
274
|
| Option | Type | Required | Description |
|
|
142
275
|
| -------------------- | ------------------------ | -------- | ----------------------------------------------------------------- |
|
|
143
|
-
| `vapidPublicKey` | `string` | Yes | Your VAPID Public Key.
|
|
144
|
-
| `backendRegisterUrl` | `string` | Yes
|
|
276
|
+
| `vapidPublicKey` | `string` | Yes | Your VAPID Public Key (base64url encoded, minimum 87 characters). |
|
|
277
|
+
| `backendRegisterUrl` | `string` | Yes* | Endpoint to send subscription data to. Use `endpoints.register` for new config. |
|
|
278
|
+
| `endpoints` | `object` | No | Custom endpoint paths `{ register: string, unregister?: string }`. |
|
|
279
|
+
| `headers` | `Record<string, string>` | No | Static headers for backend requests (e.g., auth tokens). |
|
|
280
|
+
| `getHeaders` | `function` | No | Async function returning dynamic headers (e.g., fresh JWT). |
|
|
145
281
|
| `serviceWorkerPath` | `string` | No | Path to your service worker (default: `/sw.js`). |
|
|
146
282
|
| `tags` | `Record<string, string>` | No | Tags to associate with the subscriber (e.g. `{ topic: "news" }`). |
|
|
283
|
+
| `backendCheckinUrl` | `string` | No* | Check-in relay URL for activity analytics (push-receipt + periodic check-ins). Explicit — never derived; missing it throws `CheckinConfigError`. |
|
|
284
|
+
| `retry` | `object` | No | Retry config `{ maxAttempts?: number, delayMs?: number, backoffMultiplier?: number, shouldRetry?: (status) => boolean }`. See [Retries](#retries). |
|
|
285
|
+
|
|
286
|
+
*Either `backendRegisterUrl` or `endpoints.register` is required.
|
|
287
|
+
|
|
288
|
+
*`backendCheckinUrl` is required whenever check-in analytics should be recorded (the dashboard's active-subscriber and inactive-cohort views): without it, register/update fails loudly with `CheckinConfigError` instead of silently skipping check-ins.
|
|
289
|
+
|
|
290
|
+
## Retries
|
|
291
|
+
|
|
292
|
+
Register and unregister relay calls are retried up to `maxAttempts` (default 3) with exponential
|
|
293
|
+
backoff, but only when a retry can help: network errors and the statuses `408`, `425`, `429` and
|
|
294
|
+
`5xx`. Any other status (`400`, `401`, `402`, `403`, `404`, ...) fails immediately, so a rejected
|
|
295
|
+
request does not wait through useless retries. `BackendRegistrationError.status` tells you which
|
|
296
|
+
status ended the registration.
|
|
297
|
+
|
|
298
|
+
Override the decision with `retry.shouldRetry`, which receives the HTTP status (or `undefined` for a
|
|
299
|
+
network failure) and replaces the default for both register and unregister:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
const pushClient = new PushClient({
|
|
303
|
+
vapidPublicKey: "...",
|
|
304
|
+
backendRegisterUrl: "/api/push/register",
|
|
305
|
+
backendCheckinUrl: "/api/push/checkin",
|
|
306
|
+
// My relay answers 503 when the plan's subscriber quota is exhausted: never retry that.
|
|
307
|
+
retry: { shouldRetry: (status) => status !== 503 },
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Use `shouldRetry: () => true` to restore the 0.1.x behaviour of retrying every failure.
|
|
312
|
+
|
|
313
|
+
## Error Types
|
|
314
|
+
|
|
315
|
+
The SDK throws typed errors for better handling:
|
|
316
|
+
|
|
317
|
+
| Error Class | Code | When Thrown |
|
|
318
|
+
| --------------------- | ------------------------- | ---------------------------------------- |
|
|
319
|
+
| `PermissionDeniedError` | `PERMISSION_DENIED` | User denies notification permission |
|
|
320
|
+
| `SubscriptionError` | `SUBSCRIPTION_FAILED` | PushManager subscription fails |
|
|
321
|
+
| `ValidationError` | `INVALID_VAPID_KEY` | VAPID key format is invalid |
|
|
322
|
+
| `BackendRegistrationError` | `BACKEND_REGISTRATION_FAILED` | Backend registration fails (carries `status`, the HTTP status of the last failed response, or `undefined` for a network failure) |
|
|
323
|
+
| `NotSupportedError` | `NOT_SUPPORTED` | Browser doesn't support push |
|
|
324
|
+
|
|
325
|
+
## Types
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { PushNotificationPayload } from "@webmobix/pulse-leo-sdk";
|
|
329
|
+
|
|
330
|
+
// Use for notification data structure
|
|
331
|
+
const notification: PushNotificationPayload = {
|
|
332
|
+
title: "New Message",
|
|
333
|
+
body: "You have a new notification",
|
|
334
|
+
icon: "/icon.png",
|
|
335
|
+
data: { url: "/messages" }
|
|
336
|
+
};
|
|
337
|
+
```
|
|
147
338
|
|
|
148
339
|
## License
|
|
149
340
|
|
|
150
|
-
MIT
|
|
341
|
+
MIT
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscriber check-in plumbing shared by the page context (PushClient) and
|
|
3
|
+
* the service worker (sw.ts).
|
|
4
|
+
*
|
|
5
|
+
* The service worker cannot receive configuration from the page directly, so
|
|
6
|
+
* the check-in target (the site's backend relay URL) and the subscription
|
|
7
|
+
* endpoint are exchanged through a small Cache Storage entry. On every push
|
|
8
|
+
* receipt the service worker reads that entry and fires a check-in to the
|
|
9
|
+
* site's relay, which forwards it to the PulseLeo API — proving the browser
|
|
10
|
+
* is still alive (used for monthly activity analytics).
|
|
11
|
+
*/
|
|
12
|
+
/** Configuration cached for the service worker to perform check-ins. */
|
|
13
|
+
export interface CheckinConfig {
|
|
14
|
+
/** The site's backend relay URL that forwards check-ins to PulseLeo. */
|
|
15
|
+
checkinUrl: string;
|
|
16
|
+
/** The push subscription endpoint identifying this browser. */
|
|
17
|
+
endpoint: string;
|
|
18
|
+
/** ISO timestamp of the last successful check-in (for throttling). */
|
|
19
|
+
lastCheckinAt?: string | null;
|
|
20
|
+
}
|
|
21
|
+
/** Cache Storage bucket holding the check-in configuration. */
|
|
22
|
+
export declare const CHECKIN_CACHE_NAME = "pulseleo-checkin";
|
|
23
|
+
/** Synthetic request URL used as the cache key for the config entry. */
|
|
24
|
+
export declare const CHECKIN_CONFIG_URL = "https://pulseleo.internal/checkin-config.json";
|
|
25
|
+
/**
|
|
26
|
+
* Minimum interval between two check-ins fired by the service worker.
|
|
27
|
+
* Push-driven check-ins are throttled to avoid hammering the relay; the
|
|
28
|
+
* monthly aggregation only needs one check-in per month per subscriber.
|
|
29
|
+
*/
|
|
30
|
+
export declare const CHECKIN_THROTTLE_MS: number;
|
|
31
|
+
/**
|
|
32
|
+
* Returns true when Cache Storage is available in the current context.
|
|
33
|
+
* Some environments (tests, insecure origins) do not provide it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isCacheStorageAvailable(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Read the check-in configuration cached by the page.
|
|
38
|
+
*
|
|
39
|
+
* @returns The stored config, or null when absent/unavailable.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readCheckinConfig(): Promise<CheckinConfig | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Persist the check-in configuration for the service worker.
|
|
44
|
+
*
|
|
45
|
+
* @param config - The configuration to cache.
|
|
46
|
+
*/
|
|
47
|
+
export declare function writeCheckinConfig(config: CheckinConfig): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Remove the cached check-in configuration (called on unsubscribe).
|
|
50
|
+
*/
|
|
51
|
+
export declare function clearCheckinConfig(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Pure throttle check: true when the last check-in is recent enough that a
|
|
54
|
+
* new one can be skipped.
|
|
55
|
+
*
|
|
56
|
+
* @param config - The current check-in config.
|
|
57
|
+
* @param now - Reference time (defaults to Date.now()).
|
|
58
|
+
*/
|
|
59
|
+
export declare function isCheckinThrottled(config: CheckinConfig, now?: number): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* POST a check-in to the site's relay endpoint and, on success, record the
|
|
62
|
+
* time in the cached config so the throttle kicks in for subsequent pushes.
|
|
63
|
+
*
|
|
64
|
+
* @param config - The check-in config (relay URL + endpoint).
|
|
65
|
+
* @param now - Reference time for the throttle bookkeeping (defaults to now).
|
|
66
|
+
* @returns true when the relay accepted the check-in.
|
|
67
|
+
*/
|
|
68
|
+
export declare function performCheckin(config: CheckinConfig, now?: number): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Convenience used by the service worker's push handler: read the cached
|
|
71
|
+
* config, respect the throttle, then perform the check-in.
|
|
72
|
+
*
|
|
73
|
+
* @param now - Reference time (defaults to now).
|
|
74
|
+
* @returns true when a check-in was sent and accepted.
|
|
75
|
+
*/
|
|
76
|
+
export declare function maybeCheckin(now?: number): Promise<boolean>;
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export declare enum PushClientErrorCode {
|
|
2
|
+
PERMISSION_DENIED = "PERMISSION_DENIED",
|
|
3
|
+
SUBSCRIPTION_FAILED = "SUBSCRIPTION_FAILED",
|
|
4
|
+
INVALID_VAPID_KEY = "INVALID_VAPID_KEY",
|
|
5
|
+
UNSUBSCRIPTION_FAILED = "UNSUBSCRIPTION_FAILED",
|
|
6
|
+
BACKEND_REGISTRATION_FAILED = "BACKEND_REGISTRATION_FAILED",
|
|
7
|
+
CHECKIN_CONFIG_MISSING = "CHECKIN_CONFIG_MISSING",
|
|
8
|
+
NOT_SUPPORTED = "NOT_SUPPORTED"
|
|
9
|
+
}
|
|
10
|
+
export declare class PushClientError extends Error {
|
|
11
|
+
readonly code: PushClientErrorCode;
|
|
12
|
+
constructor(code: PushClientErrorCode, message: string, options?: ErrorOptions);
|
|
13
|
+
}
|
|
14
|
+
export declare class PermissionDeniedError extends PushClientError {
|
|
15
|
+
constructor(message?: string, options?: ErrorOptions);
|
|
16
|
+
}
|
|
17
|
+
export declare class SubscriptionError extends PushClientError {
|
|
18
|
+
constructor(message: string, options?: ErrorOptions);
|
|
19
|
+
}
|
|
20
|
+
export declare class ValidationError extends PushClientError {
|
|
21
|
+
constructor(message: string, options?: ErrorOptions);
|
|
22
|
+
}
|
|
23
|
+
export declare class UnsubscriptionError extends PushClientError {
|
|
24
|
+
constructor(message: string, options?: ErrorOptions);
|
|
25
|
+
}
|
|
26
|
+
/** Options for {@link BackendRegistrationError}. */
|
|
27
|
+
export interface BackendRegistrationErrorOptions extends ErrorOptions {
|
|
28
|
+
/** HTTP status of the last failed relay response; omit for network failures. */
|
|
29
|
+
status?: number;
|
|
30
|
+
}
|
|
31
|
+
export declare class BackendRegistrationError extends PushClientError {
|
|
32
|
+
/**
|
|
33
|
+
* HTTP status code of the last failed relay response, or `undefined` when
|
|
34
|
+
* the last failure was not an HTTP response (network error, timeout).
|
|
35
|
+
*/
|
|
36
|
+
readonly status?: number;
|
|
37
|
+
constructor(message: string, options?: BackendRegistrationErrorOptions);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Thrown when the check-in relay URL is missing: `backendCheckinUrl` is the
|
|
41
|
+
* required explicit configuration for the activity-analytics check-ins
|
|
42
|
+
* (push-receipt and periodic). The SDK never guesses the URL, and a missing
|
|
43
|
+
* one fails loudly instead of silently skipping check-ins — a silent no-op
|
|
44
|
+
* would make subscriber activity decay to registration-time only without
|
|
45
|
+
* any detectable signal.
|
|
46
|
+
*/
|
|
47
|
+
export declare class CheckinConfigError extends PushClientError {
|
|
48
|
+
constructor(message?: string, options?: ErrorOptions);
|
|
49
|
+
}
|
|
50
|
+
export declare class NotSupportedError extends PushClientError {
|
|
51
|
+
constructor(message?: string, options?: ErrorOptions);
|
|
52
|
+
}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type EventHandler<T = unknown> = (data: T) => void;
|
|
2
|
+
interface SubscriptionChangeEvent {
|
|
3
|
+
subscribed: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface PermissionChangeEvent {
|
|
6
|
+
permission: 'granted' | 'denied' | 'default';
|
|
7
|
+
}
|
|
8
|
+
type EventMap = {
|
|
9
|
+
subscriptionChange: SubscriptionChangeEvent;
|
|
10
|
+
permissionChange: PermissionChangeEvent;
|
|
11
|
+
};
|
|
12
|
+
type EventKey = keyof EventMap;
|
|
13
|
+
export declare class PushClientEventEmitter {
|
|
14
|
+
private listeners;
|
|
15
|
+
on<K extends EventKey>(event: K, handler: EventHandler<EventMap[K]>): void;
|
|
16
|
+
off<K extends EventKey>(event: K, handler: EventHandler<EventMap[K]>): void;
|
|
17
|
+
emit<K extends EventKey>(event: K, data: EventMap[K]): void;
|
|
18
|
+
removeAllListeners(event?: EventKey): void;
|
|
19
|
+
}
|
|
20
|
+
export type { EventHandler, EventMap, SubscriptionChangeEvent, PermissionChangeEvent };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,48 +1,70 @@
|
|
|
1
|
-
import { PushClientOptions } from './types';
|
|
1
|
+
import { PermissionState, PushClientOptions, RegisterOptions, UnsubscribeOptions } from './types';
|
|
2
|
+
import { PushClientEventEmitter, EventMap, EventHandler } from './events';
|
|
2
3
|
export * from './types';
|
|
3
4
|
export * from './utils';
|
|
5
|
+
export * from './vapid';
|
|
6
|
+
export * from './errors';
|
|
7
|
+
export * from './events';
|
|
8
|
+
export * from './checkin';
|
|
4
9
|
/**
|
|
5
|
-
*
|
|
10
|
+
* Default retry policy for relay calls: retry network failures (no status),
|
|
11
|
+
* request timeouts (408), too-early (425), rate limiting (429) and server errors
|
|
12
|
+
* (5xx). Every other status (400, 401, 402, 403, 404, ...) cannot succeed on a
|
|
13
|
+
* retry and fails immediately.
|
|
6
14
|
*/
|
|
7
|
-
export declare
|
|
15
|
+
export declare function defaultShouldRetry(status: number | undefined): boolean;
|
|
16
|
+
export declare class PushClient extends PushClientEventEmitter {
|
|
8
17
|
private config;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* @param config - The configuration options for the client.
|
|
12
|
-
*/
|
|
18
|
+
private retryConfig;
|
|
19
|
+
private checkinTimer;
|
|
13
20
|
constructor(config: PushClientOptions);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @returns A promise that resolves to true if subscribed, false otherwise.
|
|
17
|
-
*/
|
|
21
|
+
on<K extends keyof EventMap>(event: K, handler: EventHandler<EventMap[K]>): void;
|
|
22
|
+
off<K extends keyof EventMap>(event: K, handler: EventHandler<EventMap[K]>): void;
|
|
18
23
|
isSubscribed(): Promise<boolean>;
|
|
24
|
+
getPermissionState(): PermissionState;
|
|
25
|
+
register(options?: RegisterOptions): Promise<boolean>;
|
|
26
|
+
unsubscribe(options?: UnsubscribeOptions): Promise<boolean>;
|
|
27
|
+
updateSubscription(): Promise<boolean>;
|
|
19
28
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* Requests permission and registers with backend if necessary.
|
|
23
|
-
* @returns A promise that resolves to true if registration is successful (or already exists), false otherwise.
|
|
29
|
+
* Whether this browser supports Web Push (service worker, PushManager and
|
|
30
|
+
* Notification). Same result as {@link isPushSupported}, which needs no client.
|
|
24
31
|
*/
|
|
25
|
-
|
|
32
|
+
isSupported(): boolean;
|
|
33
|
+
private validateConfig;
|
|
34
|
+
private getRegisterUrl;
|
|
35
|
+
private getUnregisterUrl;
|
|
26
36
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
37
|
+
* Requires an explicit `backendCheckinUrl`; throws {@link CheckinConfigError}
|
|
38
|
+
* instead of silently skipping check-ins, so a misconfigured integration
|
|
39
|
+
* fails loudly before any subscription is created or reused.
|
|
40
|
+
*
|
|
41
|
+
* @throws {CheckinConfigError} When `backendCheckinUrl` is not configured.
|
|
29
42
|
*/
|
|
30
|
-
private
|
|
43
|
+
private assertCheckinConfigured;
|
|
31
44
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
45
|
+
* Cache the check-in configuration for the service worker so received
|
|
46
|
+
* pushes can report liveness.
|
|
47
|
+
*
|
|
48
|
+
* @param subscription - The push subscription being registered.
|
|
49
|
+
* @throws {CheckinConfigError} When `backendCheckinUrl` is not configured.
|
|
34
50
|
*/
|
|
35
|
-
private
|
|
51
|
+
private persistCheckinConfig;
|
|
36
52
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
53
|
+
* Opt-in periodic check-in: reports liveness immediately and then every
|
|
54
|
+
* `intervalMs` while the page stays open, in addition to the automatic
|
|
55
|
+
* push-driven check-ins. Intended for quota-accurate monthly actives.
|
|
56
|
+
*
|
|
57
|
+
* @param intervalMs - Check-in interval in milliseconds (default 6h).
|
|
58
|
+
* @throws {CheckinConfigError} When `backendCheckinUrl` is not configured —
|
|
59
|
+
* periodic check-ins would silently do nothing, which must fail loudly.
|
|
39
60
|
*/
|
|
61
|
+
startPeriodicCheckin(intervalMs?: number): void;
|
|
62
|
+
/** Stop the periodic check-in started by startPeriodicCheckin. */
|
|
63
|
+
stopPeriodicCheckin(): void;
|
|
64
|
+
private getHeaders;
|
|
40
65
|
private registerServiceWorker;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* @throws Will throw an error if the backend request fails.
|
|
46
|
-
*/
|
|
47
|
-
private sendToBackend;
|
|
66
|
+
private sleep;
|
|
67
|
+
private backoffDelay;
|
|
68
|
+
private sendToBackendWithRetry;
|
|
69
|
+
private sendUnregisterToBackendWithRetry;
|
|
48
70
|
}
|