Skip to content

Commit 4acea69

Browse files
authored
fix: [WPN-32] Add playground documentation and complete API reference (#67)
1 parent 08d3424 commit 4acea69

4 files changed

Lines changed: 172 additions & 58 deletions

File tree

README.md

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,23 @@ Zero dependencies · Works everywhere · TypeScript-first
1515

1616
[Documentation](packages/builder) · [npm](https://www.npmjs.com/package/@pushforge/builder) · [Report Bug](https://github.com/draphy/pushforge/issues)
1717

18-
**[Try the Live Demo](https://pushforge.draphy.org)**
18+
**[Try the Playground](https://pushforge.draphy.org)**
1919

2020
</div>
2121

2222
---
2323

24-
## Live Demo
24+
## Playground
2525

26-
See PushForge in action at **[pushforge.draphy.org](https://pushforge.draphy.org)**a fully working test site powered by PushForge on Cloudflare Workers.
26+
Test PushForge at **[pushforge.draphy.org](https://pushforge.draphy.org)**an interactive playground for testing push notifications, powered by Cloudflare Workers.
2727

28-
- **Enable push notifications** on your device with a single toggle
29-
- **Send a test notification** to all active devices — anyone visiting the page can send and receive
30-
- **See it working across browsers** — Chrome, Firefox, Edge, Safari 16+, and more
31-
- Subscriptions auto-expire after 5 minutes — no permanent data stored
28+
- **Quick Test** — enable notifications, send test messages, see them arrive instantly
29+
- **Topic Channels** — test targeted notifications by subscribing to channels
30+
- **Notification Customization** — experiment with title, body, icon, image, action buttons, vibration
31+
- **Push Options** — test urgency (battery hints) and TTL (message expiry)
32+
- **Cross-Browser Testing** — works on Chrome, Firefox, Edge, Safari 16+
3233

33-
The entire backend is a single Cloudflare Worker using `buildPushHTTPRequest()` from `@pushforge/builder` with zero additional dependencies.
34+
Subscriptions auto-expire (5 min for quick test, 1 hour for topics) — no permanent data stored. The backend is a single Cloudflare Worker using `buildPushHTTPRequest()` with zero dependencies.
3435

3536
## The Problem
3637

@@ -103,7 +104,12 @@ const { endpoint, headers, body } = await buildPushHTTPRequest({
103104
subscription,
104105
message: {
105106
payload: { title: "New Message", body: "You have a notification!" },
106-
adminContact: "mailto:admin@example.com"
107+
adminContact: "mailto:admin@example.com",
108+
options: { // Optional push delivery settings
109+
ttl: 3600, // Message expires in 1 hour
110+
urgency: "high", // Battery priority hint
111+
topic: "alerts" // Replace pending message with same topic
112+
}
107113
}
108114
});
109115

packages/builder/README.md

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Send push notifications from any JavaScript runtime · Zero dependencies
1515

1616
[GitHub](https://github.com/draphy/pushforge) · [npm](https://www.npmjs.com/package/@pushforge/builder) · [Report Bug](https://github.com/draphy/pushforge/issues)
1717

18-
**[Try the Live Demo](https://pushforge.draphy.org)**
18+
**[Try the Playground](https://pushforge.draphy.org)**
1919

2020
</div>
2121

@@ -25,14 +25,17 @@ Send push notifications from any JavaScript runtime · Zero dependencies
2525
npm install @pushforge/builder
2626
```
2727

28-
## Live Demo
28+
## Playground
2929

30-
Try PushForge in your browser at **[pushforge.draphy.org](https://pushforge.draphy.org)**a live test site running on Cloudflare Workers.
30+
Test PushForge in your browser at **[pushforge.draphy.org](https://pushforge.draphy.org)**an interactive playground for testing push notifications, powered by Cloudflare Workers.
3131

32-
- Toggle push notifications on, send a test message, and see it arrive in real time
33-
- Works across all supported browsers — Chrome, Firefox, Edge, Safari 16+
34-
- The backend is a single Cloudflare Worker using `buildPushHTTPRequest()` with zero additional dependencies
35-
- Subscriptions auto-expire after 5 minutes — no permanent data stored
32+
- **Quick Test** — enable notifications, send a test message, see it arrive in real time
33+
- **Topic Channels** — test targeted notifications by subscribing to specific channels
34+
- **Notification Customization** — experiment with title, body, icon, image, action buttons, vibration, click URL
35+
- **Push Options** — test urgency levels (battery hints) and TTL (message expiry)
36+
- **Cross-Browser** — test across Chrome, Firefox, Edge, Safari 16+
37+
- Subscriptions auto-expire (5 min for quick test, 1 hour for topics) — no permanent data stored
38+
- The backend is a single Cloudflare Worker using `buildPushHTTPRequest()` with zero dependencies
3639

3740
## Why PushForge?
3841

@@ -176,16 +179,48 @@ const { endpoint, headers, body } = await buildPushHTTPRequest({
176179
payload, // Any JSON-serializable data
177180
adminContact, // Contact email (mailto:...) or URL
178181
options: { // Optional
179-
ttl, // Time-to-live in seconds (default: 86400)
182+
ttl, // Time-to-live in seconds (default: 86400, max: 86400)
180183
urgency, // "very-low" | "low" | "normal" | "high"
181-
topic // Topic for notification coalescing
184+
topic // Topic for notification replacement
182185
}
183186
}
184187
});
185188
```
186189

187190
**Returns:** `{ endpoint: string, headers: Headers, body: ArrayBuffer }`
188191

192+
#### Parameters
193+
194+
| Parameter | Type | Required | Description |
195+
|-----------|------|----------|-------------|
196+
| `privateJWK` | JsonWebKey \| string | Yes | Your VAPID private key (JWK object or JSON string) |
197+
| `subscription` | PushSubscription | Yes | User's push subscription with `endpoint` and `keys` |
198+
| `message.payload` | any | Yes | Any JSON-serializable data to send (see [Notification Payload](#notification-payload)) |
199+
| `message.adminContact` | string | Yes | Contact for push service (`mailto:you@example.com` or URL) |
200+
| `message.options` | object | No | Push delivery options (see below) |
201+
202+
#### Push Options (Web Push Protocol Headers)
203+
204+
These options control how the push service handles message delivery:
205+
206+
| Option | Type | Default | Description |
207+
|--------|------|---------|-------------|
208+
| `ttl` | number | 86400 | Time-to-live in seconds. How long the push service retains the message if user is offline. Max 24 hours. |
209+
| `urgency` | string | - | Battery hint: `"very-low"` (ads), `"low"` (topic updates), `"normal"` (chat), `"high"` (calls/time-sensitive). |
210+
| `topic` | string | - | Topic identifier. New message with same topic replaces pending one at push service level (before delivery). |
211+
212+
#### TypeScript Types
213+
214+
For TypeScript users, these types are exported:
215+
216+
```typescript
217+
import type {
218+
BuilderOptions, // Parameter type for buildPushHTTPRequest
219+
PushMessage, // The message object type
220+
PushSubscription // The subscription object type
221+
} from "@pushforge/builder";
222+
```
223+
189224
## Platform Examples
190225

191226
### Cloudflare Workers
@@ -295,6 +330,74 @@ const { endpoint, headers, body } = await buildPushHTTPRequest({
295330
await fetch(endpoint, { method: "POST", headers, body });
296331
```
297332

333+
## How It Works
334+
335+
```
336+
Your Server (PushForge) → Push Service (FCM/APNs) → Service Worker → User's Device
337+
```
338+
339+
**PushForge handles:**
340+
- Encrypts payload
341+
- Signs with VAPID
342+
- Sets ttl/urgency/topic headers
343+
344+
**Your service worker handles:**
345+
- Displays notification (title, body, icon, actions, etc.)
346+
- Handles clicks
347+
348+
## Notification Payload
349+
350+
The `payload` field accepts any JSON-serializable data — PushForge encrypts and delivers it as-is. Your service worker receives this payload and passes it to the browser's `showNotification()` API.
351+
352+
> **Note:** These are standard [Web Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notification) options, not PushForge-specific. PushForge handles the transport; your service worker handles the display.
353+
354+
Common fields:
355+
356+
| Field | Type | Description |
357+
|-------|------|-------------|
358+
| `title` | string | Notification title (required) |
359+
| `body` | string | Notification body text |
360+
| `icon` | string | URL for the notification icon |
361+
| `badge` | string | URL for the badge (small monochrome icon) |
362+
| `image` | string | URL for a large image |
363+
| `dir` | string | Text direction: `"auto"`, `"ltr"`, or `"rtl"` |
364+
| `lang` | string | Language tag (e.g., `"en-US"`, `"es"`) |
365+
| `tag` | string | Tag for notification replacement (same tag = replace, not stack) |
366+
| `renotify` | boolean | Vibrate/alert again when replacing a notification with same tag |
367+
| `requireInteraction` | boolean | Keep notification visible until user interacts |
368+
| `silent` | boolean | Suppress sound and vibration |
369+
| `timestamp` | number | Timestamp in milliseconds (e.g., `Date.now()`) |
370+
| `vibrate` | number[] | Vibration pattern `[vibrate, pause, vibrate, ...]` |
371+
| `actions` | array | Action buttons (max 2): `[{ action: "id", title: "Label", icon?: "url" }]` |
372+
| `data` | object | Custom data (e.g., `{ url: "/page" }` for click handling) |
373+
374+
Example with full options:
375+
376+
```typescript
377+
const { endpoint, headers, body } = await buildPushHTTPRequest({
378+
privateJWK,
379+
subscription,
380+
message: {
381+
payload: {
382+
title: "New Message",
383+
body: "John: Hey, are you free?",
384+
icon: "/icons/chat.png",
385+
badge: "/icons/badge.png",
386+
image: "/images/preview.jpg",
387+
tag: "chat-john",
388+
renotify: true,
389+
actions: [
390+
{ action: "reply", title: "Reply" },
391+
{ action: "dismiss", title: "Dismiss" }
392+
],
393+
data: { url: "/chat/john", messageId: "123" }
394+
},
395+
adminContact: "mailto:admin@example.com",
396+
options: { urgency: "high", ttl: 3600 }
397+
}
398+
});
399+
```
400+
298401
## Service Worker Setup
299402

300403
Handle incoming push notifications in your service worker:
@@ -309,17 +412,33 @@ self.addEventListener('push', (event) => {
309412
body: data.body,
310413
icon: data.icon,
311414
badge: data.badge,
312-
data: data.url
415+
image: data.image,
416+
dir: data.dir,
417+
lang: data.lang,
418+
tag: data.tag,
419+
renotify: data.renotify,
420+
requireInteraction: data.requireInteraction,
421+
silent: data.silent,
422+
timestamp: data.timestamp,
423+
vibrate: data.vibrate,
424+
actions: data.actions,
425+
data: data.data
313426
})
314427
);
315428
});
316429

317430
self.addEventListener('notificationclick', (event) => {
318431
event.notification.close();
319432

320-
if (event.notification.data) {
321-
event.waitUntil(clients.openWindow(event.notification.data));
433+
// Handle action button clicks
434+
if (event.action === 'reply') {
435+
clients.openWindow('/chat?action=reply');
436+
return;
322437
}
438+
439+
// Handle main notification click
440+
const url = event.notification.data?.url || '/';
441+
event.waitUntil(clients.openWindow(url));
323442
});
324443
```
325444
@@ -362,6 +481,10 @@ PushForge validates all inputs before processing:
362481
- Payload size (max 4KB per Web Push spec)
363482
- TTL bounds (max 24 hours per VAPID spec)
364483
484+
## Contributing
485+
486+
Contributions welcome! See [CONTRIBUTING.md](https://github.com/draphy/pushforge/blob/master/CONTRIBUTING.md) for guidelines.
487+
365488
## License
366489
367-
MIT
490+
MIT © [David Raphi](https://github.com/draphy)

packages/builder/package.json

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@pushforge/builder",
33
"version": "0.0.0-development",
4-
"description": "A robust, cross-platform Web Push notification library that handles VAPID authentication and payload encryption following the Web Push Protocol standard. Works in Node.js 16+, Browsers, Deno, Bun and Cloudflare Workers.",
4+
"description": "Zero-dependency Web Push library for Cloudflare Workers, Vercel Edge, Convex, Deno, Bun, and Node.js 20+. A web-push alternative that uses Web Crypto API instead of Node.js crypto.",
55
"private": false,
66
"main": "dist/lib/main.js",
77
"module": "dist/lib/main.js",
@@ -20,7 +20,7 @@
2020
"type": "git",
2121
"url": "https://github.com/draphy/pushforge"
2222
},
23-
"homepage": "https://github.com/draphy/pushforge",
23+
"homepage": "https://pushforge.draphy.org",
2424
"license": "MIT",
2525
"type": "module",
2626
"exports": {
@@ -52,42 +52,27 @@
5252
"vitest": "^3.1.2"
5353
},
5454
"keywords": [
55-
"David Raphi",
56-
"David",
57-
"draphy",
58-
"david",
59-
"draphi",
60-
"draphy.org",
61-
"www.draphy.org",
62-
"non profit organization",
63-
"open source",
64-
"web push",
6555
"pushforge",
66-
"@pushforge/builder",
67-
"pushforge builder",
68-
"draphy pushforge builder",
69-
"draphy pushforge",
70-
"web push builder",
71-
"web push notification",
72-
"web push protocol",
73-
"pushforge npm",
74-
"web push service",
75-
"@pushforge/builder npm package",
76-
"web push npm package",
77-
"web push vapid",
78-
"notification",
79-
"cross-platform",
80-
"cross platform",
8156
"web-push",
82-
"webcrypto",
57+
"web-push alternative",
58+
"web push",
59+
"push notifications",
8360
"vapid",
84-
"web-push-protocol",
61+
"cloudflare workers",
62+
"vercel edge",
63+
"convex",
64+
"deno",
65+
"bun",
66+
"nodejs",
67+
"edge runtime",
68+
"edge functions",
69+
"zero dependencies",
70+
"web crypto api",
71+
"crypto.createECDH",
8572
"typescript",
86-
"node",
87-
"cloudflare",
88-
"browser",
89-
"ecdh",
90-
"hkdf",
91-
"push-service"
73+
"fcm",
74+
"apns",
75+
"service worker",
76+
"pwa"
9277
]
9378
}

pnpm-lock.yaml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)