@arubiku/pulse-lib
v1.0.1
Published
Library for generating Pulse Auth Tickets and handling client sync
Downloads
239
Readme
@arubiku/pulse-lib
Typed auth helpers, a WebSocket client, framework adapters and a small state layer for apps that connect to Pulse rooms running on Cloudflare Workers.
Use this package when you want to:
- generate short-lived JWT tickets from your backend
- connect clients to a Pulse Worker from vanilla JS
- use React through
usePulse - manage a shared connection with Zustand
- build your own adapter on top of the framework-agnostic state store
Install
npm install @arubiku/pulse-libOptional peers depending on your stack:
npm install react zustandEntry points
@arubiku/pulse-libAuth helpers, base client and shared exports.@arubiku/pulse-lib/reactReact hookusePulse.@arubiku/pulse-lib/zustandZustand vanilla store factory.@arubiku/pulse-lib/protocolShared protocol types and snapshot shape.@arubiku/pulse-lib/storeFramework-agnostic subscribable state store.
Quick start
1. Generate a ticket on your backend
import { generatePulseTicket } from '@arubiku/pulse-lib';
const token = await generatePulseTicket({
roomId: 'board-1',
userId: 'user-42',
secret: process.env.PULSE_SECRET!,
expiresIn: '15m',
features: {
presence: true,
presenceSync: true,
selfEcho: false,
},
metadata: {
name: 'Jane',
role: 'editor',
},
});2. Connect from the frontend
import { PulseClient } from '@arubiku/pulse-lib';
const client = new PulseClient('https://your-worker.workers.dev', token, {
reconnectInterval: 1500,
});
client.on('message', (message) => {
console.log(message);
});
client.connect();Why Pulse instead of Ably?
If you already like Ably, the point of Pulse is not that Ably is bad. The point is control and economics.
Why teams may prefer Pulse:
- your realtime layer runs in your own Cloudflare account
- your auth model stays fully under your control through JWT tickets
- your transport lives closer to users through Cloudflare's edge network
- you can add your own rules, scopes, validation and room behavior without waiting for vendor features
- your frontend and worker can stay in the same Cloudflare-centric architecture
Practical difference in the free tier model:
- managed realtime vendors like Ably usually gate free usage with explicit connection and message limits that can change over time by plan
- with Pulse on Cloudflare Workers, the important limit is the Worker request quota and each new WebSocket handshake counts as a request
- that means you are not paying or budgeting the same way as a per-message SaaS transport layer
For example, on Cloudflare Workers Free, the commonly relevant quota is on the order of 100k requests per day, not per month. In a WebSocket setup that means up to 100k new connection handshakes per day before you hit that specific quota. Existing sockets and message flow are a different cost model than a hosted Pub/Sub product. Always verify current Cloudflare and Ably pricing pages before quoting exact limits because plans change.
Latency angle:
- if your app already serves traffic through Cloudflare, Pulse can reduce extra network hops because the socket entrypoint is already on the edge
- that usually gives you a better path for browser-to-edge communication than sending traffic first to a separate vendor platform and then back into your own stack
Choose Pulse when you want:
- a custom realtime layer inside your own infra
- lower vendor dependency
- Cloudflare-native deployment
- control over auth and room semantics
Choose Ably when you want:
- a fully managed realtime product
- built-in vendor features you do not want to maintain yourself
- less infrastructure ownership in exchange for platform limits and pricing
API overview
generatePulseTicket
Creates a signed JWT that pulse-worker can verify.
Supported options:
roomIduserIdsecretexpiresInfeaturesmetadatascopes
buildPulseWebSocketUrl
Builds the final wss://.../ws?token=... URL from a base worker URL and a token.
PulseClient
Low-level client with:
- auto reconnect
- reconnect backoff
- reconnect jitter
- pause-on-hidden and pause-on-offline behavior
- conditional reconnect policies
- application-level heartbeat support
- offline queue
- parser and serializer hooks
- event listeners
- subscribable snapshots
- presence tracking
- basic churn metrics in the connection snapshot
Useful options for lowering reconnect churn:
reconnectJitterRatiopauseWhenHiddenpauseWhenOfflineheartbeatIntervalMsheartbeatTimeoutMsshouldReconnect
usePulse
React adapter that wraps PulseClient and exposes:
- connection
status presenceMemberslastMessagelastPresencelastSystemlastErrorsendsendRaw
createPulseStore
Zustand adapter for a shared app-level connection.
createPulseStateStore
Framework-neutral state layer useful for Astro islands, custom state managers or your own hooks.
Usage by stack
Vanilla JS
import { PulseClient } from '@arubiku/pulse-lib';
const client = new PulseClient('https://your-worker.workers.dev', token);
client.connect();React
import { usePulse } from '@arubiku/pulse-lib/react';
export function Board({ token }: { token: string }) {
const { status, presenceMembers, send } = usePulse('https://your-worker.workers.dev', token);
return (
<button onClick={() => send({ type: 'update', entity: 'card', id: '1' })}>
{status} / {presenceMembers.length}
</button>
);
}Zustand
import { createPulseStore } from '@arubiku/pulse-lib/zustand';
export const pulseStore = createPulseStore();
pulseStore.getState().connect('https://your-worker.workers.dev', token);Custom adapter
import { createPulseStateStore } from '@arubiku/pulse-lib/store';
const pulse = createPulseStateStore('https://your-worker.workers.dev', token);
pulse.connect();Event model
The client automatically recognizes three message groups coming from the worker:
systempresence- user messages
Presence snapshots and incremental events update the internal presenceMembers list when presenceTracking is enabled.
The client snapshot also exposes light connection metrics such as scheduled reconnects, successful reconnects, heartbeat timeouts, queued messages, visibility state and online state.
Local development
If you are working inside this repo:
npm install
npm run buildRelease flow
This package ships with a simple npm release flow similar to the CLI workflow used elsewhere in your workspace.
Available scripts:
npm run buildnpm run typechecknpm run npm:auth:checknpm run deploy:publishnpm run release:autonpm run bump:patch:deploy:publishnpm run bump:minor:deploy:publishnpm run bump:major:deploy:publish
Related repos
pulse-worker: Cloudflare Worker and Durable Object brokerpulse-samples: examples for native JS, server scripts, Astro, React and Zustand
Troubleshooting
Invalid ticket
Make sure the backend signs the JWT with the same PULSE_SECRET configured in the worker.
No messages arrive
Check that both clients are connecting to the same roomId and the same deployed worker URL.
Reconnect churn is too high
Try these first:
- increase
reconnectInterval - keep
reconnectJitterRatioenabled - use
pauseWhenHiddenandpauseWhenOffline - enable
heartbeatIntervalMsonly when you actually need faster dead-socket detection - share one socket per app instead of one socket per component
Presence is empty
Enable features.presenceSync in the token if you want an initial snapshot on connect.
My sender does not receive its own message
That is expected by default. Set features.selfEcho = true in the token if you want echo behavior.
Imports fail in React or Zustand
Install the corresponding peer dependencies in the consuming project:
npm install react zustandNotes
This package intentionally does not own your business data. It only handles ticket generation, connection state and message transport helpers around the worker protocol.
