@latimer-woods-tech/push
v0.1.0
Published
Workers-native push notification primitive. Two delivery channels behind one small surface:
Readme
@latimer-woods-tech/push
Workers-native push notification primitive. Two delivery channels behind one small surface:
- Web Push (browsers / PWAs) — VAPID auth (RFC 8292) +
aes128gcmpayload encryption (RFC 8291), all viacrypto.subtle. - FCM v1 (Capacitor / Android) — device-token send with a self-minting Google service-account OAuth2 token provider (RS256 JWT bearer grant).
No process.env, no Node built-ins, no Buffer; every fetch is
error-handled. FCM is free — no vendor-spend gate applies.
Install
// package.json
"dependencies": {
"@latimer-woods-tech/push": "^0.1.0"
}Requires @latimer-woods-tech/errors and @latimer-woods-tech/logger as peers
(both are runtime dependencies of this package).
Web Push
import { sendWebPush } from '@latimer-woods-tech/push';
const vapid = {
publicKey: env.VAPID_PUBLIC_KEY, // base64url, 65-byte uncompressed P-256 point
privateKey: env.VAPID_PRIVATE_KEY, // base64url, 32-byte scalar
subject: env.VAPID_SUBJECT, // 'mailto:[email protected]'
};
const res = await sendWebPush(subscription, JSON.stringify({ title: 'Hi' }), vapid, {
ttl: 60 * 60,
urgency: 'high',
});
if (res.expired) {
// 404/410 → the subscription is gone; prune it from your store.
await store.deleteSubscription(subscription.endpoint);
}sendWebPush never throws on an HTTP status — a 2xx sets success, a 404/410
sets expired, anything else leaves both false (and logs at error if a
logger is supplied). Only a rejected transport (fetch) throws.
FCM (Android / Capacitor)
import { createGoogleAccessTokenProvider, createFcmSender } from '@latimer-woods-tech/push';
const getAccessToken = createGoogleAccessTokenProvider({
clientEmail: env.FCM_CLIENT_EMAIL,
privateKey: env.FCM_PRIVATE_KEY, // PEM PKCS#8
projectId: env.FCM_PROJECT_ID,
});
const fcm = createFcmSender({ projectId: env.FCM_PROJECT_ID, getAccessToken });
await fcm.send({ token: deviceToken, title: 'Focus', body: 'Time to lock in' });The token provider caches the minted OAuth token until ~1 minute before expiry.
Unified client
import { createPushClient } from '@latimer-woods-tech/push';
const push = createPushClient({
vapid,
fcm: { clientEmail, privateKey, projectId }, // omit to disable FCM
});
await push.sendWebPush(subscription, payload);
await push.sendFcm({ token, title: 'Hi' });Consumers
- FocusBro Phase A — web-push check-ins (build the package here, Bro consumes).
- Prime Self Android — FCM device delivery.
Versioning
Semantic-versioned per PLATFORM_STANDARDS.md §17.3. Published to npm on a
push/vX.Y.Z tag.
