@notifyhub/client
v0.0.7
Published
Transport client for NotifyHub.
Readme
@notifyhub/client
@notifyhub/client is the transport and state layer for recipient-facing NotifyHub integrations.
It handles:
- session-token authentication
- inbox and preference HTTP calls
- local snapshot state
- optimistic state updates
- SSE connection lifecycle and reconnects
- multiple client instances on the same page
It does not include privileged server APIs like send().
Install
npm install @notifyhub/clientWhen To Use This Package
Use @notifyhub/client when you want:
- a framework-agnostic NotifyHub client
- direct access to the recipient inbox and preference API
- full control over client lifecycle and state subscription
- a lower-level foundation for your own UI layer
Use @notifyhub/react instead when you want ready-made hooks, provider wiring, inbox UI, preference UI, and toasts.
Auth Model
@notifyhub/client expects a short-lived recipient session token.
Your app backend should mint that token using @notifyhub/node, then the browser should provide it through getSessionToken().
import {createNotifyHubClient} from '@notifyhub/client';
const client = createNotifyHubClient({
baseUrl: 'https://notifyhub.example.com',
getSessionToken: async () => {
const response = await fetch('/api/notifyhub/session', {
method: 'POST',
});
if (!response.ok) {
return null;
}
return (await response.json()) as {token: string; expiresAt: number | null};
},
});Quick Start
import {createNotifyHubClient} from '@notifyhub/client';
const client = createNotifyHubClient({
baseUrl: 'https://notifyhub.example.com',
getSessionToken: async () => {
const response = await fetch('/api/notifyhub/session', {
method: 'POST',
});
if (!response.ok) {
return null;
}
return await response.json();
},
});
const unsubscribe = client.subscribe(() => {
const snapshot = client.getSnapshot();
console.log(snapshot.notifications, snapshot.unreadCount, snapshot.realtimeStatus);
});
await client.connect();
const notifications = await client.listNotifications({limit: 20});
const firstNotification = notifications.notifications[0];
if (firstNotification) {
await client.markAsRead(firstNotification.id);
}
unsubscribe();
client.disconnect();Public API
Constructors
| Export | Description |
|--------|-------------|
| new NotifyHubClient(config) | Creates a client instance directly |
| createNotifyHubClient(config) | Factory helper |
NotifyHubClientConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| baseUrl | string | yes | Base URL for the NotifyHub public API |
| getSessionToken | () => Promise<NotifyHubSession \| null> \| NotifyHubSession \| null | yes | Returns the current recipient session token or null |
| fetch | typeof fetch | no | Override for custom runtimes, testing, or instrumentation |
Lifecycle And State Methods
| Method | Returns | Description |
|--------|---------|-------------|
| subscribe(listener) | () => void | Subscribes to local snapshot updates |
| getSnapshot() | NotifyHubClientSnapshot | Returns the current in-memory client state |
| connect() | Promise<void> | Hydrates state and starts the SSE loop |
| disconnect() | void | Stops realtime and marks status as stopped |
| refresh() | Promise<void> | Re-hydrates notifications, unread count, and preferences |
Inbox Methods
| Method | Returns | Description |
|--------|---------|-------------|
| listNotifications(params?) | Promise<ListNotificationsResponse> | Lists notifications, optionally including archived items and a limit |
| getUnreadCount() | Promise<UnreadCountResponse> | Fetches unread count |
| markAsRead(notificationId) | Promise<MarkAsReadResponse> | Marks a single notification as read |
| markAsUnread(notificationId) | Promise<MarkAsReadResponse> | Marks a single notification as unread |
| markAllAsRead() | Promise<MarkAllAsReadResponse> | Marks all active notifications as read |
| archive(notificationId) | Promise<ArchiveResponse> | Moves a notification into the archived collection |
| unarchive(notificationId) | Promise<ArchiveResponse> | Restores an archived notification |
| delete(notificationId) | Promise<DeleteResponse> | Deletes a notification |
Preference Methods
| Method | Returns | Description |
|--------|---------|-------------|
| getPreferences() | Promise<GetPreferencesResponse> | Fetches effective recipient preferences |
| getTopics() | Promise<GetTopicsResponse> | Fetches available topics and category grouping |
| updatePreferences(params) | Promise<UpdatePreferencesResponse> | Batch updates topic preferences |
| setGlobalMute(muted) | Promise<SetGlobalMuteResponse> | Toggles recipient-wide mute |
| subscribeToTopic(topicId) | Promise<SubscribeResponse> | Subscribes the recipient to a topic |
| unsubscribeFromTopic(topicId) | Promise<SubscribeResponse> | Unsubscribes the recipient from a topic |
| updateTopicPreference(topicId, params) | Promise<{success: boolean}> | Updates one topic's subscription, channel preferences, or frequency |
| setTopicChannelPreference(topicId, channel, enabled) | Promise<SubscribeResponse> | Enables or disables one channel for one topic |
| setGlobalChannelPreference(channel, enabled) | Promise<SetChannelPreferenceResponse> | Enables or disables a global default channel |
Exported Types
Core Auth And Lifecycle Types
NotifyHubSessionNotifyHubClientConfigNotifyHubClientSnapshotNotifyHubRealtimeStatusAPIError
Inbox Data Types
NotificationNotificationActionNotificationTypeNotificationPriorityListNotificationsParamsListNotificationsResponseUnreadCountResponseMarkAsReadResponseMarkAllAsReadResponseArchiveResponseDeleteResponse
Preference Data Types
TopicTopicPreferenceRecipientPreferencesChannelChannelPreferencesFrequencyGetPreferencesResponseGetTopicsResponseUpdatePreferenceParamsUpdatePreferencesParamsUpdatePreferencesResponseSetGlobalMuteResponseSetChannelPreferenceResponseSubscribeResponse
Runtime Behavior
- The client is instance-based. Each client maintains its own token cache, snapshot, and SSE connection.
connect()is idempotent for a single client instance.- The client uses HTTP for reads and writes, then SSE for live updates.
- The client supports
Last-Event-IDand reconnects with backoff. - A
401response causes one token refresh attempt throughgetSessionToken(). - Request methods throw an
APIErrorwhen the backend responds with an error status.
Important Constraints
- Browser auth is session-token only.
- Privileged operations such as
send()are intentionally not part of@notifyhub/client. - Public types use plain string IDs and timestamps.
Development
From the monorepo root:
bun install- Playground:
cd packages/client && bun run play- Playground typecheck:
cd packages/client && bun run play:check:types- Tests:
cd packages/client && bun run test:it- Build:
cd packages/client && bun run build- Typecheck:
cd packages/client && bun run check:types