@webmobix/pulse-leo-sdk 0.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/README.md +150 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +125 -0
- package/dist/sw.d.ts +1 -0
- package/dist/sw.js +55 -0
- package/dist/types.d.ts +35 -0
- package/dist/utils.d.ts +12 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# PulseLeo SDK
|
|
2
|
+
|
|
3
|
+
A lightweight browser SDK for integrating PulseLeo Push Notifications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install pulse-leo-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### 1. Initialize
|
|
14
|
+
|
|
15
|
+
Initialize the client with your VAPID Public Key (found in your Dashboard).
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { PushClient } from "pulse-leo-sdk";
|
|
19
|
+
|
|
20
|
+
const pushClient = new PushClient({
|
|
21
|
+
vapidPublicKey: "YOUR_VAPID_PUBLIC_KEY",
|
|
22
|
+
backendRegisterUrl: "https://api.pulseleo.com/register", // Or your backend proxy
|
|
23
|
+
serviceWorkerPath: "/sw.js", // Default
|
|
24
|
+
tags: {
|
|
25
|
+
category: "news",
|
|
26
|
+
source: "landing_page",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 2. Service Worker Setup
|
|
32
|
+
|
|
33
|
+
The SDK requires a service worker to handle push notifications. Choose one of the following options:
|
|
34
|
+
|
|
35
|
+
#### Option A: Copy to Public Directory (Recommended)
|
|
36
|
+
|
|
37
|
+
Service workers must be served from your domain's root to properly control your application. The most reliable method is to copy the service worker file to your public directory.
|
|
38
|
+
|
|
39
|
+
**Method 1: Using postinstall script**
|
|
40
|
+
|
|
41
|
+
Add a postinstall script to your `package.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"scripts": {
|
|
46
|
+
"postinstall": "cp node_modules/pulse-leo-sdk/dist/sw.js public/sw.js"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Method 2: Using build tools (Vite example)**
|
|
52
|
+
|
|
53
|
+
Install `vite-plugin-static-copy`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install -D vite-plugin-static-copy
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Add to your `vite.config.ts`:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
|
63
|
+
|
|
64
|
+
export default defineConfig({
|
|
65
|
+
plugins: [
|
|
66
|
+
viteStaticCopy({
|
|
67
|
+
targets: [
|
|
68
|
+
{
|
|
69
|
+
src: 'node_modules/pulse-leo-sdk/dist/sw.js',
|
|
70
|
+
dest: '.'
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then initialize the client (default path is `/sw.js`):
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
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.
|
|
89
|
+
|
|
90
|
+
#### Option B: Custom Service Worker
|
|
91
|
+
|
|
92
|
+
If you need to add your own service worker logic, you can import the SDK's service worker in your own:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// In your custom-sw.js
|
|
96
|
+
import "pulse-leo-sdk/sw";
|
|
97
|
+
|
|
98
|
+
// Add your own service worker logic here
|
|
99
|
+
self.addEventListener("install", (event) => {
|
|
100
|
+
console.log("Custom service worker installed");
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then set `serviceWorkerPath` to your custom service worker:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const pushClient = new PushClient({
|
|
108
|
+
vapidPublicKey: "YOUR_VAPID_PUBLIC_KEY",
|
|
109
|
+
backendRegisterUrl: "/api/register",
|
|
110
|
+
serviceWorkerPath: "/custom-sw.js",
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Register for Notifications
|
|
115
|
+
|
|
116
|
+
Call the `register()` method, typically in response to a user action (like a button click) to request permission.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const button = document.getElementById("enable-push");
|
|
120
|
+
button.addEventListener("click", async () => {
|
|
121
|
+
const success = await pushClient.register();
|
|
122
|
+
if (success) {
|
|
123
|
+
console.log("Successfully subscribed!");
|
|
124
|
+
} else {
|
|
125
|
+
console.error("Failed to subscribe.");
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 4. Check Subscription Status
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const isSubscribed = await pushClient.isSubscribed();
|
|
134
|
+
if (isSubscribed) {
|
|
135
|
+
// Update UI to show "Subscribed"
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Configuration Options
|
|
140
|
+
|
|
141
|
+
| Option | Type | Required | Description |
|
|
142
|
+
| -------------------- | ------------------------ | -------- | ----------------------------------------------------------------- |
|
|
143
|
+
| `vapidPublicKey` | `string` | Yes | Your VAPID Public Key. |
|
|
144
|
+
| `backendRegisterUrl` | `string` | Yes | Endpoint to send subscription data to. |
|
|
145
|
+
| `serviceWorkerPath` | `string` | No | Path to your service worker (default: `/sw.js`). |
|
|
146
|
+
| `tags` | `Record<string, string>` | No | Tags to associate with the subscriber (e.g. `{ topic: "news" }`). |
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PushClientOptions } from './types';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export * from './utils';
|
|
4
|
+
/**
|
|
5
|
+
* Client for managing push notifications.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PushClient {
|
|
8
|
+
private config;
|
|
9
|
+
/**
|
|
10
|
+
* Creates an instance of PushClient.
|
|
11
|
+
* @param config - The configuration options for the client.
|
|
12
|
+
*/
|
|
13
|
+
constructor(config: PushClientOptions);
|
|
14
|
+
/**
|
|
15
|
+
* Checks if the client is already subscribed to push notifications.
|
|
16
|
+
* @returns A promise that resolves to true if subscribed, false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
isSubscribed(): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Registers the client for push notifications.
|
|
21
|
+
* Checks support, config, and existing subscription.
|
|
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.
|
|
24
|
+
*/
|
|
25
|
+
register(): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Checks if push notifications are supported in the browser.
|
|
28
|
+
* @returns True if supported, false otherwise.
|
|
29
|
+
*/
|
|
30
|
+
private isSupported;
|
|
31
|
+
/**
|
|
32
|
+
* Validates the configuration.
|
|
33
|
+
* @returns True if valid, false otherwise.
|
|
34
|
+
*/
|
|
35
|
+
private validateConfig;
|
|
36
|
+
/**
|
|
37
|
+
* Registers the service worker.
|
|
38
|
+
* @returns A promise that resolves with the ServiceWorkerRegistration.
|
|
39
|
+
*/
|
|
40
|
+
private registerServiceWorker;
|
|
41
|
+
/**
|
|
42
|
+
* Sends the subscription to the backend.
|
|
43
|
+
* @param subscription - The push subscription object.
|
|
44
|
+
* @returns A promise that resolves when the subscription is sent.
|
|
45
|
+
* @throws Will throw an error if the backend request fails.
|
|
46
|
+
*/
|
|
47
|
+
private sendToBackend;
|
|
48
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
function o(n) {
|
|
2
|
+
const e = "=".repeat((4 - n.length % 4) % 4), i = (n + e).replace(/-/g, "+").replace(/_/g, "/"), r = atob(i), s = new Uint8Array(r.length);
|
|
3
|
+
for (let t = 0; t < r.length; ++t)
|
|
4
|
+
s[t] = r.charCodeAt(t);
|
|
5
|
+
return s;
|
|
6
|
+
}
|
|
7
|
+
function a(n) {
|
|
8
|
+
const e = new Uint8Array(n);
|
|
9
|
+
let i = "";
|
|
10
|
+
for (let r = 0; r < e.byteLength; r++)
|
|
11
|
+
i += String.fromCharCode(e[r]);
|
|
12
|
+
return btoa(i).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
13
|
+
}
|
|
14
|
+
class c {
|
|
15
|
+
config;
|
|
16
|
+
/**
|
|
17
|
+
* Creates an instance of PushClient.
|
|
18
|
+
* @param config - The configuration options for the client.
|
|
19
|
+
*/
|
|
20
|
+
constructor(e) {
|
|
21
|
+
console.debug("PushClient initialized"), this.config = e;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Checks if the client is already subscribed to push notifications.
|
|
25
|
+
* @returns A promise that resolves to true if subscribed, false otherwise.
|
|
26
|
+
*/
|
|
27
|
+
async isSubscribed() {
|
|
28
|
+
if (!this.isSupported())
|
|
29
|
+
return !1;
|
|
30
|
+
const e = await navigator.serviceWorker.getRegistration();
|
|
31
|
+
return e ? !!await e.pushManager.getSubscription() : !1;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Registers the client for push notifications.
|
|
35
|
+
* Checks support, config, and existing subscription.
|
|
36
|
+
* Requests permission and registers with backend if necessary.
|
|
37
|
+
* @returns A promise that resolves to true if registration is successful (or already exists), false otherwise.
|
|
38
|
+
*/
|
|
39
|
+
async register() {
|
|
40
|
+
console.debug("Registering push notifications...");
|
|
41
|
+
try {
|
|
42
|
+
if (!this.isSupported())
|
|
43
|
+
return console.warn("Push notifications are not supported in this browser."), !1;
|
|
44
|
+
if (!this.validateConfig())
|
|
45
|
+
return console.error("Invalid PushClient configuration."), !1;
|
|
46
|
+
const e = await this.registerServiceWorker();
|
|
47
|
+
if (await e.pushManager.getSubscription())
|
|
48
|
+
return !0;
|
|
49
|
+
const r = await Notification.requestPermission();
|
|
50
|
+
if (console.debug(`Notification permission: ${r}`), r !== "granted")
|
|
51
|
+
return console.warn("Notification permission denied."), !1;
|
|
52
|
+
const s = o(
|
|
53
|
+
this.config.vapidPublicKey
|
|
54
|
+
), t = await e.pushManager.subscribe({
|
|
55
|
+
userVisibleOnly: !0,
|
|
56
|
+
applicationServerKey: s
|
|
57
|
+
});
|
|
58
|
+
return await this.sendToBackend(t), !0;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return console.error("Failed to register push client:", e), !1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Checks if push notifications are supported in the browser.
|
|
65
|
+
* @returns True if supported, false otherwise.
|
|
66
|
+
*/
|
|
67
|
+
isSupported() {
|
|
68
|
+
return typeof window < "u" && "serviceWorker" in navigator && !!navigator.serviceWorker && "PushManager" in window && "Notification" in window;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validates the configuration.
|
|
72
|
+
* @returns True if valid, false otherwise.
|
|
73
|
+
*/
|
|
74
|
+
validateConfig() {
|
|
75
|
+
return !!(this.config.vapidPublicKey && this.config.backendRegisterUrl);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Registers the service worker.
|
|
79
|
+
* @returns A promise that resolves with the ServiceWorkerRegistration.
|
|
80
|
+
*/
|
|
81
|
+
async registerServiceWorker() {
|
|
82
|
+
const e = this.config.serviceWorkerPath || "/sw.js";
|
|
83
|
+
console.debug(`[PushClient] Registering service worker at: ${e}`);
|
|
84
|
+
try {
|
|
85
|
+
const i = await navigator.serviceWorker.register(e, {
|
|
86
|
+
scope: "/"
|
|
87
|
+
});
|
|
88
|
+
console.debug("[PushClient] Service Worker registered:", i), console.debug(
|
|
89
|
+
`[PushClient] SW state: ${i.installing ? "installing" : i.waiting ? "waiting" : i.active ? "active" : "unknown"}`
|
|
90
|
+
);
|
|
91
|
+
const r = await navigator.serviceWorker.ready;
|
|
92
|
+
return console.debug("[PushClient] Service Worker ready"), r;
|
|
93
|
+
} catch (i) {
|
|
94
|
+
throw console.error("[PushClient] Failed to register service worker:", i), i;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Sends the subscription to the backend.
|
|
99
|
+
* @param subscription - The push subscription object.
|
|
100
|
+
* @returns A promise that resolves when the subscription is sent.
|
|
101
|
+
* @throws Will throw an error if the backend request fails.
|
|
102
|
+
*/
|
|
103
|
+
async sendToBackend(e) {
|
|
104
|
+
console.debug("[PushClient] Sending subscription to backend", e);
|
|
105
|
+
const i = {
|
|
106
|
+
...e.toJSON(),
|
|
107
|
+
tags: this.config.tags
|
|
108
|
+
}, r = await fetch(this.config.backendRegisterUrl, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: {
|
|
111
|
+
"Content-Type": "application/json"
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify(i)
|
|
114
|
+
});
|
|
115
|
+
if (!r.ok)
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Failed to register push subscription: ${r.statusText}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
c as PushClient,
|
|
123
|
+
a as arrayBufferToUrlBase64,
|
|
124
|
+
o as urlBase64ToUint8Array
|
|
125
|
+
};
|
package/dist/sw.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/sw.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const n = self;
|
|
2
|
+
n.addEventListener("install", (i) => {
|
|
3
|
+
console.log("[SW] Service Worker installing..."), i.waitUntil(n.skipWaiting());
|
|
4
|
+
});
|
|
5
|
+
n.addEventListener("activate", (i) => {
|
|
6
|
+
console.log("[SW] Service Worker activated"), i.waitUntil(n.clients.claim());
|
|
7
|
+
});
|
|
8
|
+
n.addEventListener("push", (i) => {
|
|
9
|
+
if (!i.data) {
|
|
10
|
+
console.warn("Push event received with no data.");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const t = i.data.json(), o = t.title || "Notification", a = {
|
|
15
|
+
body: t.body,
|
|
16
|
+
icon: t.icon,
|
|
17
|
+
badge: t.badge,
|
|
18
|
+
data: t.data,
|
|
19
|
+
tag: t.tag,
|
|
20
|
+
renotify: t.renotify,
|
|
21
|
+
requireInteraction: t.requireInteraction,
|
|
22
|
+
silent: t.silent,
|
|
23
|
+
timestamp: t.timestamp,
|
|
24
|
+
actions: t.actions,
|
|
25
|
+
dir: t.dir,
|
|
26
|
+
lang: t.lang,
|
|
27
|
+
vibrate: t.vibrate,
|
|
28
|
+
image: t.image
|
|
29
|
+
};
|
|
30
|
+
i.waitUntil(n.registration.showNotification(o, a));
|
|
31
|
+
} catch (t) {
|
|
32
|
+
console.error("Error handling push event:", t);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
n.addEventListener("notificationclick", (i) => {
|
|
36
|
+
i.notification.close();
|
|
37
|
+
const t = i.notification.data?.url || "/", o = n.clients.matchAll({
|
|
38
|
+
type: "window",
|
|
39
|
+
includeUncontrolled: !0
|
|
40
|
+
}).then((a) => {
|
|
41
|
+
let e = null;
|
|
42
|
+
for (const r of a) {
|
|
43
|
+
const l = new URL(r.url, self.location.href), c = new URL(t, self.location.href);
|
|
44
|
+
if (l.href === c.href) {
|
|
45
|
+
e = r;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (e && "focus" in e)
|
|
50
|
+
return e.focus();
|
|
51
|
+
if (n.clients.openWindow)
|
|
52
|
+
return n.clients.openWindow(t);
|
|
53
|
+
});
|
|
54
|
+
i.waitUntil(o);
|
|
55
|
+
});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for initializing the PushClient
|
|
3
|
+
*/
|
|
4
|
+
export interface PushClientOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The public VAPID key for the push service.
|
|
7
|
+
*/
|
|
8
|
+
vapidPublicKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* The path to the service worker file (e.g., "/sw.js").
|
|
11
|
+
* Defaults to "/sw.js" if not provided.
|
|
12
|
+
*/
|
|
13
|
+
serviceWorkerPath?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The URL of your backend endpoint that registers the subscription with PulseLeo.
|
|
16
|
+
*/
|
|
17
|
+
backendRegisterUrl: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional tags to associate with the subscription (key-value pairs).
|
|
20
|
+
* Example: { category: "news", plan: "premium" }
|
|
21
|
+
*/
|
|
22
|
+
tags?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Data structure required for push subscription
|
|
26
|
+
*/
|
|
27
|
+
export interface PushSubscriptionData {
|
|
28
|
+
endpoint: string;
|
|
29
|
+
expirationTime?: number | null;
|
|
30
|
+
keys: {
|
|
31
|
+
p256dh: string;
|
|
32
|
+
auth: string;
|
|
33
|
+
};
|
|
34
|
+
tags?: Record<string, string>;
|
|
35
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a VAPID public key (base64url string) to a Uint8Array.
|
|
3
|
+
* @param base64String The base64url encoded VAPID public key.
|
|
4
|
+
* @returns The Uint8Array representation of the key.
|
|
5
|
+
*/
|
|
6
|
+
export declare function urlBase64ToUint8Array(base64String: string): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Converts an ArrayBuffer or Uint8Array to a base64url string.
|
|
9
|
+
* @param buffer The buffer to convert.
|
|
10
|
+
* @returns The base64url string.
|
|
11
|
+
*/
|
|
12
|
+
export declare function arrayBufferToUrlBase64(buffer: ArrayBuffer | Uint8Array): string;
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webmobix/pulse-leo-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./sw.js": "./dist/sw.js",
|
|
14
|
+
"./sw": "./dist/sw.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@playwright/test": "^1.56.1",
|
|
21
|
+
"@types/node": "^22.19.1",
|
|
22
|
+
"happy-dom": "^20.0.10",
|
|
23
|
+
"typescript": "^5.9.3",
|
|
24
|
+
"vite": "^7.2.4",
|
|
25
|
+
"vite-plugin-dts": "^4.5.4",
|
|
26
|
+
"vitest": "^4.0.13"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "vite",
|
|
30
|
+
"build": "tsc && tsc -p tsconfig.worker.json && vite build",
|
|
31
|
+
"test": "vitest"
|
|
32
|
+
}
|
|
33
|
+
}
|