@iron-stack/push
v1.0.0
Published
Declarative push notification framework with multi-provider support (APNs, FCM, Expo), socket-aware delivery, and built-in throttling and batching.
Readme
@iron-stack/push
Declarative push notification framework with multi-provider support (APNs, FCM, Expo), socket-aware delivery, and built-in throttling and batching.
Installation
npm install @iron-stack/pushQuick Start
1. Define push rules (shared)
import { definePushRules } from '@iron-stack/push';
const pushRules = definePushRules<{
'message:new': MessageWithSender;
}>({
'message:new': {
shouldPush: (msg, recipient) => !recipient.isOnline,
title: (msg) => msg.sender.displayName,
body: (msg) => msg.content,
deepLink: (msg) => `myapp://conversation/${msg.conversationId}`,
collapseKey: (msg) => `conv:${msg.conversationId}`,
threadId: (msg) => msg.conversationId,
throttleMs: 2000,
batchWindowMs: 3000,
batchTitle: (events) => events[0].sender.displayName,
batchBody: (_, count) => `${count} new messages`,
},
});2. Server -- send push notifications
import { PushService, createExpoProvider, createSocketTracker } from '@iron-stack/push/server';
const push = new PushService({
registry: yourDeviceRegistry,
onlineTracker: createSocketTracker(),
rules: pushRules,
providers: [createExpoProvider()],
});
// Trigger push for affected users
await push.notify('message:new', messageData, recipientUserIds);3. Client -- configure push (type only)
import type { PushClientConfig } from '@iron-stack/push/client';
const pushConfig: PushClientConfig = {
platform: 'ios',
registerForToken: async () => { /* get push token from OS */ },
requestPermission: async () => { /* request notification permission */ },
sendTokenToServer: async (token) => { /* register token with server */ },
};API Reference
@iron-stack/push (shared)
| Export | Description |
|---|---|
| definePushRules(rules) | Type-safe factory for declaring per-event push behavior |
| PlatformSchema | Zod schema for platform enum (ios, android, web) |
| PushTokenSchema | Zod schema for push token objects |
| Platform | Type: "ios" \| "android" \| "web" |
| PushToken | Type for stored device tokens |
| NotificationPayload | Type for the notification content (title, body, data, badge, sound, etc.) |
| PushResult | Type for send results per token |
| PushRule | Type for a single push rule definition |
| PushRules | Type for the complete rules map |
| PushRecipient | Type for resolved recipient (userId, isOnline, tokens) |
| PushProvider | Interface that push providers must implement |
| DeviceRegistry | Interface for device token storage (register, remove, query, prune) |
| OnlineTracker | Interface for checking user online status |
@iron-stack/push/server
| Export | Description |
|---|---|
| PushService | Main push engine -- socket-aware, declarative, multi-provider |
| PushService.notify(event, data, recipientIds) | Trigger push for an event based on rules |
| PushService.sendDirect(userId, payload) | Send a push directly, bypassing rules |
| PushService.addProvider(provider) | Register an additional push provider |
| PushService.pruneStaleTokens(days) | Remove tokens unused for N days |
| PushService.flushAll() | Flush all pending batched notifications |
| PushQueue | Queue with built-in throttling and batching |
| createSocketTracker() | Creates an in-memory online tracker backed by Socket.IO |
| createMemoryRegistry() | In-memory device registry for development and testing |
| createAPNsProvider(config) | Apple Push Notification Service provider (HTTP/2) |
| createFCMProvider(config) | Firebase Cloud Messaging v1 API provider |
| createExpoProvider(config?) | Expo push notification provider |
| PushServiceConfig | Configuration for PushService |
| APNsConfig | Configuration for APNs provider |
| FCMConfig | Configuration for FCM provider |
| ExpoConfig | Configuration for Expo provider |
@iron-stack/push/client
| Export | Description |
|---|---|
| PushClientConfig | Interface for platform-agnostic client push configuration |
| NotificationHandler | Interface with onForeground, onTap, onBackground callbacks |
| PushPermissionStatus | Type: "granted" \| "denied" \| "undetermined" |
Configuration
PushServiceConfig
| Option | Type | Default | Description |
|---|---|---|---|
| registry | DeviceRegistry | required | Device token storage backend |
| onlineTracker | OnlineTracker | undefined | Socket-based online status (skip pushes for online users) |
| rules | PushRules | required | Declarative per-event push config |
| providers | PushProvider[] | [] | Registered push providers (APNs, FCM, Expo) |
| autoRemoveInvalidTokens | boolean | true | Auto-remove expired/invalid tokens |
| verbose | boolean | true | Log push events to console |
PushRule Options
| Option | Type | Description |
|---|---|---|
| shouldPush | (event, recipient) => boolean | Skip push when returning false (e.g., user is online) |
| title | string \| (event) => string | Notification title |
| body | string \| (event) => string | Notification body |
| data | (event) => Record<string, string> | Custom data payload |
| deepLink | (event) => string | Deep link URL opened on tap |
| collapseKey | (event) => string | Newer pushes with same key replace older ones |
| threadId | (event) => string | Group notifications by thread |
| imageUrl | (event) => string | Rich notification image |
| priority | "normal" \| "high" | Push priority (default: "high") |
| silent | boolean | Silent/background push |
| throttleMs | number | Min ms between pushes per recipient + collapse key |
| batchWindowMs | number | Combine events within this window into one push |
| batchTitle | (events, count) => string | Title for batched notifications |
| batchBody | (events, count) => string | Body for batched notifications |
Provider Configs
APNsConfig
| Option | Type | Default | Description |
|---|---|---|---|
| key | string | required | .p8 key file contents |
| keyId | string | required | Key ID from Apple Developer portal |
| teamId | string | required | Team ID from Apple Developer portal |
| bundleId | string | required | App bundle identifier |
| production | boolean | false | Use production APNs endpoint |
FCMConfig
| Option | Type | Description |
|---|---|---|
| projectId | string | Firebase project ID |
| serviceAccountKey | string | Service account JSON string |
ExpoConfig
| Option | Type | Description |
|---|---|---|
| accessToken | string? | Expo access token (optional, for higher rate limits) |
License
MIT
