@growth-labs/push 0.1.3
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 +106 -0
- package/dist/client/push-optin.d.ts +24 -0
- package/dist/client/push-optin.d.ts.map +1 -0
- package/dist/client/push-optin.js +122 -0
- package/dist/client/push-optin.js.map +1 -0
- package/dist/client/push-sw.d.ts +3 -0
- package/dist/client/push-sw.d.ts.map +1 -0
- package/dist/client/push-sw.js +36 -0
- package/dist/client/push-sw.js.map +1 -0
- package/dist/db/schema.d.ts +178 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +14 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/push.d.ts +2 -0
- package/dist/middleware/push.d.ts.map +1 -0
- package/dist/middleware/push.js +10 -0
- package/dist/middleware/push.js.map +1 -0
- package/dist/options.d.ts +108 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +36 -0
- package/dist/options.js.map +1 -0
- package/dist/routes/subscribe.d.ts +3 -0
- package/dist/routes/subscribe.d.ts.map +1 -0
- package/dist/routes/subscribe.js +68 -0
- package/dist/routes/subscribe.js.map +1 -0
- package/dist/routes/unsubscribe.d.ts +3 -0
- package/dist/routes/unsubscribe.d.ts.map +1 -0
- package/dist/routes/unsubscribe.js +24 -0
- package/dist/routes/unsubscribe.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/analytics.d.ts +47 -0
- package/dist/utils/analytics.d.ts.map +1 -0
- package/dist/utils/analytics.js +78 -0
- package/dist/utils/analytics.js.map +1 -0
- package/dist/utils/cleanup.d.ts +12 -0
- package/dist/utils/cleanup.d.ts.map +1 -0
- package/dist/utils/cleanup.js +18 -0
- package/dist/utils/cleanup.js.map +1 -0
- package/dist/utils/composer.d.ts +95 -0
- package/dist/utils/composer.d.ts.map +1 -0
- package/dist/utils/composer.js +98 -0
- package/dist/utils/composer.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +10 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/queue.d.ts +40 -0
- package/dist/utils/queue.d.ts.map +1 -0
- package/dist/utils/queue.js +69 -0
- package/dist/utils/queue.js.map +1 -0
- package/dist/utils/scheduled.d.ts +105 -0
- package/dist/utils/scheduled.d.ts.map +1 -0
- package/dist/utils/scheduled.js +75 -0
- package/dist/utils/scheduled.js.map +1 -0
- package/dist/utils/send.d.ts +23 -0
- package/dist/utils/send.d.ts.map +1 -0
- package/dist/utils/send.js +99 -0
- package/dist/utils/send.js.map +1 -0
- package/dist/utils/subscriptions.d.ts +21 -0
- package/dist/utils/subscriptions.d.ts.map +1 -0
- package/dist/utils/subscriptions.js +84 -0
- package/dist/utils/subscriptions.js.map +1 -0
- package/dist/utils/turnstile.d.ts +5 -0
- package/dist/utils/turnstile.d.ts.map +1 -0
- package/dist/utils/turnstile.js +22 -0
- package/dist/utils/turnstile.js.map +1 -0
- package/dist/utils/vapid.d.ts +9 -0
- package/dist/utils/vapid.d.ts.map +1 -0
- package/dist/utils/vapid.js +23 -0
- package/dist/utils/vapid.js.map +1 -0
- package/package.json +77 -0
- package/src/client/push-optin.ts +189 -0
- package/src/client/push-sw.d.ts +3 -0
- package/src/client/push-sw.d.ts.map +1 -0
- package/src/client/push-sw.js +25 -0
- package/src/client/push-sw.js.map +1 -0
- package/src/client/push-sw.ts +45 -0
- package/src/components/PushOptIn.astro +53 -0
- package/src/components/index.ts +1 -0
- package/src/virtual.d.ts +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @growth-labs/push
|
|
2
|
+
|
|
3
|
+
Web Push API integration for browser notifications. VAPID auth, D1 subscription storage, Turnstile-protected endpoints, Queue-based batch delivery. Sends directly to FCM/Mozilla/Apple push endpoints — no third-party push service.
|
|
4
|
+
|
|
5
|
+
**No consent package dependency.** Browser's native permission prompt ("Allow"/"Block") constitutes GDPR consent for push.
|
|
6
|
+
|
|
7
|
+
**Web Crypto API only.** Uses `@block65/webcrypto-web-push` for VAPID signing (ECDSA P-256) and RFC 8291 encryption — no Node.js `crypto` dependency.
|
|
8
|
+
|
|
9
|
+
## Config
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import push from '@growth-labs/push'
|
|
13
|
+
|
|
14
|
+
push({
|
|
15
|
+
vapidPublicKey: 'BDd8...', // URL-safe base64
|
|
16
|
+
vapidPrivateKey: '...', // JWK JSON string (via Cloudflare Secrets)
|
|
17
|
+
vapidSubject: 'mailto:admin@fedweek.com',
|
|
18
|
+
d1Binding: 'SITE_DB',
|
|
19
|
+
queueBinding: 'PUSH_QUEUE',
|
|
20
|
+
turnstileSiteKey: '...',
|
|
21
|
+
turnstileSecretKey: '...',
|
|
22
|
+
defaultIcon: '/icons/notification.png',
|
|
23
|
+
defaultTtl: 86400, // 24 hours
|
|
24
|
+
defaultUrgency: 'normal',
|
|
25
|
+
batchSize: 100, // Subscriptions per Queue message
|
|
26
|
+
cleanupAfterDays: 90, // Remove stale subscriptions
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## What It Injects
|
|
31
|
+
|
|
32
|
+
**Middleware:** Injects VAPID public key into `context.locals.pushVapidKey`.
|
|
33
|
+
|
|
34
|
+
**Routes:**
|
|
35
|
+
- `POST /api/push/subscribe` — Turnstile-protected subscription endpoint
|
|
36
|
+
- `POST /api/push/unsubscribe` — remove subscription
|
|
37
|
+
|
|
38
|
+
**Component:** `<PushOptIn />` — opt-in/opt-out button. Vanilla JS. Handles full lifecycle: check support → check permission → request → subscribe → POST to server.
|
|
39
|
+
|
|
40
|
+
## Sending Notifications (Consumer Calls)
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { sendPushNotification, enqueuePushBatchAll } from '@growth-labs/push/utils'
|
|
44
|
+
|
|
45
|
+
// Single send
|
|
46
|
+
await sendPushNotification(subscription, { title: 'Breaking', body: '...' }, sendContext)
|
|
47
|
+
|
|
48
|
+
// Batch send to all subscribers (via Queue)
|
|
49
|
+
await enqueuePushBatchAll(env.SITE_DB, payload, { queue: env.PUSH_QUEUE, batchSize: 100 })
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Queue Consumer
|
|
53
|
+
|
|
54
|
+
Package provides handler, consumer wires it up:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { handlePushQueue } from '@growth-labs/push/utils'
|
|
58
|
+
|
|
59
|
+
export default {
|
|
60
|
+
async queue(batch, env) {
|
|
61
|
+
await handlePushQueue(batch, {
|
|
62
|
+
db: env.SITE_DB,
|
|
63
|
+
vapid: { subject: '...', publicKey: '...', privateKey: env.VAPID_PRIVATE_KEY },
|
|
64
|
+
defaults: { icon: '/icons/notification.png', ttl: 86400, urgency: 'normal' },
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**VAPID credentials passed via `QueueConsumerContext` at runtime — never in Queue messages.**
|
|
71
|
+
|
|
72
|
+
## D1 Tables (prefixed `gl_`)
|
|
73
|
+
|
|
74
|
+
- `gl_push_subscriptions` — endpoint, p256dh key, auth key, topics, failure_count, last_delivery_at
|
|
75
|
+
|
|
76
|
+
## VAPID Key Setup (One-Time)
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx @growth-labs/push generate-vapid
|
|
80
|
+
# Store private key:
|
|
81
|
+
npx wrangler secret put VAPID_PRIVATE_KEY
|
|
82
|
+
npx wrangler secret put VAPID_PUBLIC_KEY
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Wrangler Bindings
|
|
86
|
+
|
|
87
|
+
```toml
|
|
88
|
+
[[d1_databases]]
|
|
89
|
+
binding = "SITE_DB"
|
|
90
|
+
database_id = "..."
|
|
91
|
+
|
|
92
|
+
[[queues.producers]]
|
|
93
|
+
binding = "PUSH_QUEUE"
|
|
94
|
+
queue = "push-sends"
|
|
95
|
+
|
|
96
|
+
[[queues.consumers]]
|
|
97
|
+
queue = "push-sends"
|
|
98
|
+
max_batch_size = 5
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Key Patterns
|
|
102
|
+
|
|
103
|
+
- Virtual module: `virtual:growth-labs/push/config`
|
|
104
|
+
- Expired subscriptions (404/410 from push service) auto-cleaned
|
|
105
|
+
- 10+ consecutive failures → subscription removed
|
|
106
|
+
- `.astro` component files ship as source, not compiled
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface PushOptInDependencies {
|
|
2
|
+
navigator: {
|
|
3
|
+
serviceWorker: {
|
|
4
|
+
register(url: string): Promise<ServiceWorkerRegistration>;
|
|
5
|
+
ready: Promise<ServiceWorkerRegistration>;
|
|
6
|
+
getRegistration(url?: string): Promise<ServiceWorkerRegistration | undefined>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
window: Window & {
|
|
10
|
+
turnstile?: {
|
|
11
|
+
render(container: HTMLElement, options: {
|
|
12
|
+
sitekey: string;
|
|
13
|
+
callback(token: string): void;
|
|
14
|
+
'error-callback'(): void;
|
|
15
|
+
}): void;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
document: Document & {
|
|
19
|
+
dispatchEvent(event: CustomEvent): boolean;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare function initPushOptIn(root: ParentNode, deps?: PushOptInDependencies): void;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=push-optin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-optin.d.ts","sourceRoot":"","sources":["../../src/client/push-optin.ts"],"names":[],"mappings":"AASA,UAAU,qBAAqB;IAC9B,SAAS,EAAE;QACV,aAAa,EAAE;YACd,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;YACzD,KAAK,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAA;YACzC,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,SAAS,CAAC,CAAA;SAC7E,CAAA;KACD,CAAA;IACD,MAAM,EAAE,MAAM,GAAG;QAChB,SAAS,CAAC,EAAE;YACX,MAAM,CACL,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE;gBACR,OAAO,EAAE,MAAM,CAAA;gBACf,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;gBAC7B,gBAAgB,IAAI,IAAI,CAAA;aACxB,GACC,IAAI,CAAA;SACP,CAAA;KACD,CAAA;IACD,QAAQ,EAAE,QAAQ,GAAG;QACpB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAA;KAC1C,CAAA;CACD;AAuCD,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,wBAAoB,GAAG,IAAI,CAqH9E"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/// <reference lib="dom.iterable" />
|
|
3
|
+
function getDependencies() {
|
|
4
|
+
return globalThis;
|
|
5
|
+
}
|
|
6
|
+
function urlSafeBase64ToUint8Array(base64) {
|
|
7
|
+
const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
|
|
8
|
+
const binary = atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
|
|
9
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
10
|
+
}
|
|
11
|
+
function getTurnstileToken(windowObject, siteKey, container) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
container.hidden = false;
|
|
14
|
+
if (typeof windowObject.turnstile === 'undefined') {
|
|
15
|
+
reject(new Error('Turnstile script not loaded'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
windowObject.turnstile.render(container, {
|
|
19
|
+
sitekey: siteKey,
|
|
20
|
+
callback: (token) => {
|
|
21
|
+
container.hidden = true;
|
|
22
|
+
resolve(token);
|
|
23
|
+
},
|
|
24
|
+
'error-callback': () => {
|
|
25
|
+
container.hidden = true;
|
|
26
|
+
reject(new Error('Turnstile challenge failed'));
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function initPushOptIn(root, deps = getDependencies()) {
|
|
32
|
+
const containers = root.querySelectorAll('.gl-push-optin');
|
|
33
|
+
containers.forEach((container) => {
|
|
34
|
+
const btn = container.querySelector('[data-push-action="toggle"]');
|
|
35
|
+
const unsupported = container.querySelector('[data-push-state="unsupported"]');
|
|
36
|
+
const turnstileContainer = container.querySelector('[data-push-turnstile]');
|
|
37
|
+
if (!btn || !unsupported || !turnstileContainer) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const button = btn;
|
|
41
|
+
const vapidPublicKey = container.getAttribute('data-vapid-public-key') ?? '';
|
|
42
|
+
const subscribeUrl = container.getAttribute('data-subscribe-url') ?? '/api/push/subscribe';
|
|
43
|
+
const unsubscribeUrl = container.getAttribute('data-unsubscribe-url') ?? '/api/push/unsubscribe';
|
|
44
|
+
const swUrl = container.getAttribute('data-sw-url') ?? '/sw.js';
|
|
45
|
+
const turnstileSiteKey = container.getAttribute('data-turnstile-site-key') ?? '';
|
|
46
|
+
if (!('serviceWorker' in deps.navigator) || !('PushManager' in deps.window)) {
|
|
47
|
+
unsupported.hidden = false;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
button.hidden = false;
|
|
51
|
+
function showState(state) {
|
|
52
|
+
container.querySelectorAll('[data-push-state]').forEach((el) => {
|
|
53
|
+
;
|
|
54
|
+
el.hidden = el.getAttribute('data-push-state') !== state;
|
|
55
|
+
});
|
|
56
|
+
button.hidden = state === 'unsupported';
|
|
57
|
+
button.disabled = state === 'subscribing' || state === 'denied';
|
|
58
|
+
}
|
|
59
|
+
async function checkState() {
|
|
60
|
+
const registration = await deps.navigator.serviceWorker.getRegistration(swUrl);
|
|
61
|
+
if (!registration) {
|
|
62
|
+
showState('prompt');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const subscription = await registration.pushManager.getSubscription();
|
|
66
|
+
showState(subscription ? 'subscribed' : 'prompt');
|
|
67
|
+
}
|
|
68
|
+
button.addEventListener('click', async () => {
|
|
69
|
+
try {
|
|
70
|
+
// Register before awaiting `ready` so the first click cannot stall forever.
|
|
71
|
+
await deps.navigator.serviceWorker.register(swUrl);
|
|
72
|
+
const registration = await deps.navigator.serviceWorker.ready;
|
|
73
|
+
const existing = await registration.pushManager.getSubscription();
|
|
74
|
+
if (existing) {
|
|
75
|
+
await existing.unsubscribe();
|
|
76
|
+
await fetch(unsubscribeUrl, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
body: JSON.stringify({ endpoint: existing.endpoint }),
|
|
80
|
+
});
|
|
81
|
+
deps.document.dispatchEvent(new CustomEvent('gl:push_unsubscribed', {
|
|
82
|
+
detail: { endpoint: existing.endpoint },
|
|
83
|
+
}));
|
|
84
|
+
showState('prompt');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
showState('subscribing');
|
|
88
|
+
const turnstileToken = await getTurnstileToken(deps.window, turnstileSiteKey, turnstileContainer);
|
|
89
|
+
const subscription = await registration.pushManager.subscribe({
|
|
90
|
+
userVisibleOnly: true,
|
|
91
|
+
applicationServerKey: urlSafeBase64ToUint8Array(vapidPublicKey),
|
|
92
|
+
});
|
|
93
|
+
const response = await fetch(subscribeUrl, {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers: { 'Content-Type': 'application/json' },
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
subscription: subscription.toJSON(),
|
|
98
|
+
turnstileToken,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error('Server rejected subscription');
|
|
103
|
+
}
|
|
104
|
+
deps.document.dispatchEvent(new CustomEvent('gl:push_subscribed', {
|
|
105
|
+
detail: { endpoint: subscription.endpoint },
|
|
106
|
+
}));
|
|
107
|
+
showState('subscribed');
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
if (Notification.permission === 'denied') {
|
|
111
|
+
showState('denied');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
showState('error');
|
|
115
|
+
console.error('[gl-push]', err);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
checkState();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=push-optin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-optin.js","sourceRoot":"","sources":["../../src/client/push-optin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-sw.d.ts","sourceRoot":"","sources":["../../src/client/push-sw.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAiBjD;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAsBtE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/// <reference lib="webworker" />
|
|
2
|
+
export function handlePush(event) {
|
|
3
|
+
const data = event.data?.json() ?? {};
|
|
4
|
+
const options = {
|
|
5
|
+
body: data.body ?? '',
|
|
6
|
+
icon: data.icon,
|
|
7
|
+
image: data.image,
|
|
8
|
+
tag: data.tag,
|
|
9
|
+
data: {
|
|
10
|
+
url: data.url,
|
|
11
|
+
actionUrl: data.action?.url,
|
|
12
|
+
tag: data.tag,
|
|
13
|
+
},
|
|
14
|
+
actions: data.action ? [{ action: 'primary', title: data.action.title }] : [],
|
|
15
|
+
};
|
|
16
|
+
event.waitUntil(self.registration.showNotification(data.title ?? 'Notification', options));
|
|
17
|
+
}
|
|
18
|
+
export function handleNotificationClick(event) {
|
|
19
|
+
event.notification.close();
|
|
20
|
+
const data = event.notification.data ?? {};
|
|
21
|
+
const url = event.action === 'primary' && data.actionUrl ? data.actionUrl : data.url;
|
|
22
|
+
if (url) {
|
|
23
|
+
// Notify all visible clients so they can emit gl:push_notification_clicked
|
|
24
|
+
event.waitUntil(self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((windowClients) => {
|
|
25
|
+
for (const client of windowClients) {
|
|
26
|
+
client.postMessage({
|
|
27
|
+
type: 'gl:push_notification_clicked',
|
|
28
|
+
tag: data.tag,
|
|
29
|
+
url,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return self.clients.openWindow(url);
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=push-sw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-sw.js","sourceRoot":"","sources":["../../src/client/push-sw.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAGjC,MAAM,UAAU,UAAU,CAAC,KAAgB;IAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAErC,MAAM,OAAO,GAAG;QACf,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;QACrB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,IAAI,EAAE;YACL,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;YAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;SACb;QACD,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;KACrB,CAAA;IAEzD,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,IAAI,cAAc,EAAE,OAAO,CAAC,CAAC,CAAA;AAC3F,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAwB;IAC/D,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,CAAA;IAE1B,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,CAAA;IAE1C,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAA;IAEpF,IAAI,GAAG,EAAE,CAAC;QACT,2EAA2E;QAC3E,KAAK,CAAC,SAAS,CACd,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,EAAE;YAC3F,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;gBACpC,MAAM,CAAC,WAAW,CAAC;oBAClB,IAAI,EAAE,8BAA8B;oBACpC,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,GAAG;iBACH,CAAC,CAAA;YACH,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QACpC,CAAC,CAAC,CACF,CAAA;IACF,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
2
|
+
name: "gl_push_subscriptions";
|
|
3
|
+
schema: undefined;
|
|
4
|
+
columns: {
|
|
5
|
+
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
6
|
+
name: "id";
|
|
7
|
+
tableName: "gl_push_subscriptions";
|
|
8
|
+
dataType: "string";
|
|
9
|
+
columnType: "SQLiteText";
|
|
10
|
+
data: string;
|
|
11
|
+
driverParam: string;
|
|
12
|
+
notNull: true;
|
|
13
|
+
hasDefault: false;
|
|
14
|
+
isPrimaryKey: true;
|
|
15
|
+
isAutoincrement: false;
|
|
16
|
+
hasRuntimeDefault: false;
|
|
17
|
+
enumValues: [string, ...string[]];
|
|
18
|
+
baseColumn: never;
|
|
19
|
+
identity: undefined;
|
|
20
|
+
generated: undefined;
|
|
21
|
+
}, object>;
|
|
22
|
+
endpoint: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
23
|
+
name: "endpoint";
|
|
24
|
+
tableName: "gl_push_subscriptions";
|
|
25
|
+
dataType: "string";
|
|
26
|
+
columnType: "SQLiteText";
|
|
27
|
+
data: string;
|
|
28
|
+
driverParam: string;
|
|
29
|
+
notNull: true;
|
|
30
|
+
hasDefault: false;
|
|
31
|
+
isPrimaryKey: false;
|
|
32
|
+
isAutoincrement: false;
|
|
33
|
+
hasRuntimeDefault: false;
|
|
34
|
+
enumValues: [string, ...string[]];
|
|
35
|
+
baseColumn: never;
|
|
36
|
+
identity: undefined;
|
|
37
|
+
generated: undefined;
|
|
38
|
+
}, object>;
|
|
39
|
+
keyP256dh: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
40
|
+
name: "key_p256dh";
|
|
41
|
+
tableName: "gl_push_subscriptions";
|
|
42
|
+
dataType: "string";
|
|
43
|
+
columnType: "SQLiteText";
|
|
44
|
+
data: string;
|
|
45
|
+
driverParam: string;
|
|
46
|
+
notNull: true;
|
|
47
|
+
hasDefault: false;
|
|
48
|
+
isPrimaryKey: false;
|
|
49
|
+
isAutoincrement: false;
|
|
50
|
+
hasRuntimeDefault: false;
|
|
51
|
+
enumValues: [string, ...string[]];
|
|
52
|
+
baseColumn: never;
|
|
53
|
+
identity: undefined;
|
|
54
|
+
generated: undefined;
|
|
55
|
+
}, object>;
|
|
56
|
+
keyAuth: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
57
|
+
name: "key_auth";
|
|
58
|
+
tableName: "gl_push_subscriptions";
|
|
59
|
+
dataType: "string";
|
|
60
|
+
columnType: "SQLiteText";
|
|
61
|
+
data: string;
|
|
62
|
+
driverParam: string;
|
|
63
|
+
notNull: true;
|
|
64
|
+
hasDefault: false;
|
|
65
|
+
isPrimaryKey: false;
|
|
66
|
+
isAutoincrement: false;
|
|
67
|
+
hasRuntimeDefault: false;
|
|
68
|
+
enumValues: [string, ...string[]];
|
|
69
|
+
baseColumn: never;
|
|
70
|
+
identity: undefined;
|
|
71
|
+
generated: undefined;
|
|
72
|
+
}, object>;
|
|
73
|
+
topics: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
74
|
+
name: "topics";
|
|
75
|
+
tableName: "gl_push_subscriptions";
|
|
76
|
+
dataType: "string";
|
|
77
|
+
columnType: "SQLiteText";
|
|
78
|
+
data: string;
|
|
79
|
+
driverParam: string;
|
|
80
|
+
notNull: false;
|
|
81
|
+
hasDefault: false;
|
|
82
|
+
isPrimaryKey: false;
|
|
83
|
+
isAutoincrement: false;
|
|
84
|
+
hasRuntimeDefault: false;
|
|
85
|
+
enumValues: [string, ...string[]];
|
|
86
|
+
baseColumn: never;
|
|
87
|
+
identity: undefined;
|
|
88
|
+
generated: undefined;
|
|
89
|
+
}, object>;
|
|
90
|
+
createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
91
|
+
name: "created_at";
|
|
92
|
+
tableName: "gl_push_subscriptions";
|
|
93
|
+
dataType: "date";
|
|
94
|
+
columnType: "SQLiteTimestamp";
|
|
95
|
+
data: Date;
|
|
96
|
+
driverParam: number;
|
|
97
|
+
notNull: true;
|
|
98
|
+
hasDefault: false;
|
|
99
|
+
isPrimaryKey: false;
|
|
100
|
+
isAutoincrement: false;
|
|
101
|
+
hasRuntimeDefault: false;
|
|
102
|
+
enumValues: undefined;
|
|
103
|
+
baseColumn: never;
|
|
104
|
+
identity: undefined;
|
|
105
|
+
generated: undefined;
|
|
106
|
+
}, object>;
|
|
107
|
+
lastDeliveryAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
108
|
+
name: "last_delivery_at";
|
|
109
|
+
tableName: "gl_push_subscriptions";
|
|
110
|
+
dataType: "date";
|
|
111
|
+
columnType: "SQLiteTimestamp";
|
|
112
|
+
data: Date;
|
|
113
|
+
driverParam: number;
|
|
114
|
+
notNull: false;
|
|
115
|
+
hasDefault: false;
|
|
116
|
+
isPrimaryKey: false;
|
|
117
|
+
isAutoincrement: false;
|
|
118
|
+
hasRuntimeDefault: false;
|
|
119
|
+
enumValues: undefined;
|
|
120
|
+
baseColumn: never;
|
|
121
|
+
identity: undefined;
|
|
122
|
+
generated: undefined;
|
|
123
|
+
}, object>;
|
|
124
|
+
failureCount: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
125
|
+
name: "failure_count";
|
|
126
|
+
tableName: "gl_push_subscriptions";
|
|
127
|
+
dataType: "number";
|
|
128
|
+
columnType: "SQLiteInteger";
|
|
129
|
+
data: number;
|
|
130
|
+
driverParam: number;
|
|
131
|
+
notNull: true;
|
|
132
|
+
hasDefault: true;
|
|
133
|
+
isPrimaryKey: false;
|
|
134
|
+
isAutoincrement: false;
|
|
135
|
+
hasRuntimeDefault: false;
|
|
136
|
+
enumValues: undefined;
|
|
137
|
+
baseColumn: never;
|
|
138
|
+
identity: undefined;
|
|
139
|
+
generated: undefined;
|
|
140
|
+
}, object>;
|
|
141
|
+
userAgent: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
142
|
+
name: "user_agent";
|
|
143
|
+
tableName: "gl_push_subscriptions";
|
|
144
|
+
dataType: "string";
|
|
145
|
+
columnType: "SQLiteText";
|
|
146
|
+
data: string;
|
|
147
|
+
driverParam: string;
|
|
148
|
+
notNull: false;
|
|
149
|
+
hasDefault: false;
|
|
150
|
+
isPrimaryKey: false;
|
|
151
|
+
isAutoincrement: false;
|
|
152
|
+
hasRuntimeDefault: false;
|
|
153
|
+
enumValues: [string, ...string[]];
|
|
154
|
+
baseColumn: never;
|
|
155
|
+
identity: undefined;
|
|
156
|
+
generated: undefined;
|
|
157
|
+
}, object>;
|
|
158
|
+
country: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
159
|
+
name: "country";
|
|
160
|
+
tableName: "gl_push_subscriptions";
|
|
161
|
+
dataType: "string";
|
|
162
|
+
columnType: "SQLiteText";
|
|
163
|
+
data: string;
|
|
164
|
+
driverParam: string;
|
|
165
|
+
notNull: false;
|
|
166
|
+
hasDefault: false;
|
|
167
|
+
isPrimaryKey: false;
|
|
168
|
+
isAutoincrement: false;
|
|
169
|
+
hasRuntimeDefault: false;
|
|
170
|
+
enumValues: [string, ...string[]];
|
|
171
|
+
baseColumn: never;
|
|
172
|
+
identity: undefined;
|
|
173
|
+
generated: undefined;
|
|
174
|
+
}, object>;
|
|
175
|
+
};
|
|
176
|
+
dialect: "sqlite";
|
|
177
|
+
}>;
|
|
178
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW5B,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
2
|
+
export const pushSubscriptions = sqliteTable('gl_push_subscriptions', {
|
|
3
|
+
id: text('id').primaryKey(),
|
|
4
|
+
endpoint: text('endpoint').notNull().unique(),
|
|
5
|
+
keyP256dh: text('key_p256dh').notNull(),
|
|
6
|
+
keyAuth: text('key_auth').notNull(),
|
|
7
|
+
topics: text('topics'),
|
|
8
|
+
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
|
9
|
+
lastDeliveryAt: integer('last_delivery_at', { mode: 'timestamp' }),
|
|
10
|
+
failureCount: integer('failure_count').notNull().default(0),
|
|
11
|
+
userAgent: text('user_agent'),
|
|
12
|
+
country: text('country'),
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAEpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAC,uBAAuB,EAAE;IACrE,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;IAC7C,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACvC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACnC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE;IACjE,cAAc,EAAE,OAAO,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAClE,YAAY,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;IAC7B,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;CACxB,CAAC,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,cAAc,CAAA;AAElE,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,CA0DnE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { pushOptionsSchema } from './options.js';
|
|
2
|
+
export default function push(options) {
|
|
3
|
+
const resolved = pushOptionsSchema.parse(options);
|
|
4
|
+
const virtualConfig = {
|
|
5
|
+
vapidPublicKey: resolved.vapidPublicKey,
|
|
6
|
+
d1Binding: resolved.d1Binding,
|
|
7
|
+
queueBinding: resolved.queueBinding,
|
|
8
|
+
defaults: resolved.defaults,
|
|
9
|
+
queue: resolved.queue,
|
|
10
|
+
turnstile: resolved.turnstile,
|
|
11
|
+
routes: resolved.routes,
|
|
12
|
+
cleanup: resolved.cleanup,
|
|
13
|
+
};
|
|
14
|
+
const virtualModuleId = 'virtual:growth-labs/push/config';
|
|
15
|
+
const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
16
|
+
return {
|
|
17
|
+
name: '@growth-labs/push',
|
|
18
|
+
hooks: {
|
|
19
|
+
'astro:config:setup': ({ addMiddleware, injectRoute, updateConfig }) => {
|
|
20
|
+
updateConfig({
|
|
21
|
+
vite: {
|
|
22
|
+
plugins: [
|
|
23
|
+
{
|
|
24
|
+
name: 'growth-labs-push-config',
|
|
25
|
+
resolveId(id) {
|
|
26
|
+
if (id === virtualModuleId)
|
|
27
|
+
return resolvedVirtualModuleId;
|
|
28
|
+
},
|
|
29
|
+
load(id) {
|
|
30
|
+
if (id === resolvedVirtualModuleId) {
|
|
31
|
+
return `export default ${JSON.stringify(virtualConfig)}`;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
addMiddleware({
|
|
39
|
+
entrypoint: '@growth-labs/push/middleware',
|
|
40
|
+
order: 'pre',
|
|
41
|
+
});
|
|
42
|
+
injectRoute({
|
|
43
|
+
pattern: resolved.routes.subscribe,
|
|
44
|
+
entrypoint: '@growth-labs/push/routes/subscribe',
|
|
45
|
+
prerender: false,
|
|
46
|
+
});
|
|
47
|
+
injectRoute({
|
|
48
|
+
pattern: resolved.routes.unsubscribe,
|
|
49
|
+
entrypoint: '@growth-labs/push/routes/unsubscribe',
|
|
50
|
+
prerender: false,
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAElE,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAoB;IAChD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAEjD,MAAM,aAAa,GAAG;QACrB,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;KACzB,CAAA;IAED,MAAM,eAAe,GAAG,iCAAiC,CAAA;IACzD,MAAM,uBAAuB,GAAG,KAAK,eAAe,EAAE,CAAA;IAEtD,OAAO;QACN,IAAI,EAAE,mBAAmB;QACzB,KAAK,EAAE;YACN,oBAAoB,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,EAAE;gBACtE,YAAY,CAAC;oBACZ,IAAI,EAAE;wBACL,OAAO,EAAE;4BACR;gCACC,IAAI,EAAE,yBAAyB;gCAC/B,SAAS,CAAC,EAAE;oCACX,IAAI,EAAE,KAAK,eAAe;wCAAE,OAAO,uBAAuB,CAAA;gCAC3D,CAAC;gCACD,IAAI,CAAC,EAAE;oCACN,IAAI,EAAE,KAAK,uBAAuB,EAAE,CAAC;wCACpC,OAAO,kBAAkB,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CAAA;oCACzD,CAAC;gCACF,CAAC;6BACD;yBACD;qBACD;iBACD,CAAC,CAAA;gBAEF,aAAa,CAAC;oBACb,UAAU,EAAE,8BAA8B;oBAC1C,KAAK,EAAE,KAAK;iBACZ,CAAC,CAAA;gBAEF,WAAW,CAAC;oBACX,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS;oBAClC,UAAU,EAAE,oCAAoC;oBAChD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBAEF,WAAW,CAAC;oBACX,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW;oBACpC,UAAU,EAAE,sCAAsC;oBAClD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;YACH,CAAC;SACD;KACD,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/middleware/push.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,mCAMpB,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import config from 'virtual:growth-labs/push/config';
|
|
2
|
+
import { defineMiddleware } from 'astro/middleware';
|
|
3
|
+
export const onRequest = defineMiddleware(async (context, next) => {
|
|
4
|
+
;
|
|
5
|
+
context.locals.push = {
|
|
6
|
+
vapidPublicKey: config.vapidPublicKey,
|
|
7
|
+
};
|
|
8
|
+
return next();
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/middleware/push.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,iCAAiC,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAEnD,MAAM,CAAC,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACjE,CAAC;IAAC,OAAO,CAAC,MAAkC,CAAC,IAAI,GAAG;QACnD,cAAc,EAAE,MAAM,CAAC,cAAc;KACrC,CAAA;IAED,OAAO,IAAI,EAAE,CAAA;AACd,CAAC,CAAC,CAAA"}
|