Client SDK
The browser pub/sub client — pingerchips-js.
pingerchips-js (v3.0.0) is the browser/Node client. It wraps Phoenix Channels
in a small pub/sub API and also ships the Chat and
Spaces clients.
3.0.0 has no client-facing API changes — it's bumped to match the server SDK.
The client forwards whatever token your authEndpoint returns, so the move to
capability tokens is transparent. Upgrade
pingerchips-js and pingerchips-js-server together.
Install
npm install pingerchips-jsESM only ("type": "module" or .mjs). Node ≥ 18.
Initialise
import Pingerchips from 'pingerchips-js';
const client = new Pingerchips('YOUR_APP_KEY', options);The socket connects immediately on construction.
Constructor options
| Option | Type | Description |
|---|---|---|
endpoint | string | WebSocket URL. Default wss://queue.pingerchips.com/socket (prod), ws://localhost:4000/socket (dev). |
authEndpoint | string | Your server route that signs private/presence joins. |
authInfo | object | Sent to authEndpoint in the auth_info field. |
authHeaders | object | Extra headers on the auth request. |
params | object | Extra params merged into the socket connect. |
serializer / messageFormat | "msgpack" | Switch to MessagePack binary frames. |
transport | class | Override the WebSocket transport. |
reconnectAfterMs / rejoinAfterMs | number | fn | Phoenix backoff. |
There is no cluster or host option, and the client never takes the App
Secret.
Subscribe
// public
const lobby = await client.subscribe('lobby');
// private — needs authEndpoint (or an explicit `auth` token)
const inbox = await client.subscribe('private-user-123');
// presence — needs authEndpoint + user data on the server side
const room = await client.subscribe('presence-lobby');
// server-side filter (ExSift) — only matching messages reach this subscriber
const alerts = await client.subscribe('sensor-data', {
filter: { 'data.level': { $eq: 'critical' } },
});
// delta compression — server sends diffs, client reconstructs
const prices = await client.subscribe('live-prices', { delta: 'fossil' });
// resume from a serial after a reload
const orders = await client.subscribe('orders', {
after_serial: Number(localStorage.getItem('ordersSerial')) || undefined,
});subscribe(name, options?) returns Promise<ChannelWrapper>. Re-subscribing to
the same channel returns the existing wrapper.
options key | Meaning |
|---|---|
filter | ExSift query — $eq $ne $gt $gte $lt $lte $in $nin over data.* / metadata.* paths |
delta | "fossil" | "xdelta3" | true — enable delta compression |
after_serial | Replay messages after this serial on join |
auth | A pre-minted auth token, bypassing authEndpoint |
ChannelWrapper
| Method | Description |
|---|---|
bind(event, cb) → this | Listen for an event. |
unbind(event, cb?) → this | Remove one or all listeners for an event. |
trigger(event, data) → this | Client-side publish. Needs Enable Client Messages on the app; rate-limited. Add msg_serial: "<uuid>" to data for idempotent retries. |
onRecoveryFailed(cb) → this | cb({ reason: "position_expired" | "no_buffer", channel, afterSerial }) — the server couldn't replay after reconnect; re-fetch state from your own API. |
leave() | Unsubscribe. |
The wrapper transparently handles message, message:delta,
pingerchips:delta_compression_enabled and pingerchips:recovery_failed — you
just bind('message', …) (or your event name).
Client methods
| Method | Description |
|---|---|
connect() / disconnect() | Manual socket control. |
getLastSerial(channelName) → number | null | Last serial seen — checkpoint it for cross-reload resume. |
getSocketId() → string | Server socket id. Throws if not connected. |
getSocketIdAsync(timeoutMs?) → Promise<string> | Await a socket id. |
getHttpEndpoint() → string | HTTP base derived from the WS endpoint. |
unsubscribe(channelName) | Leave a channel by name. |
authenticate(channelName) | Manually fetch a capability token via authEndpoint. |
registerPushToken() / unregisterPushToken() throw — push registration
moved server-side. Use the server SDK's registerPushToken.
class Pingerchips has no connection-state event emitter (pc.on('connected')
etc). It reconnects and re-joins automatically, passing after_serial so the
server replays missed messages.
Example
import Pingerchips from 'pingerchips-js';
const client = new Pingerchips('YOUR_APP_KEY', {
authEndpoint: '/pingerchips/auth',
authInfo: { token: sessionToken },
});
const alerts = await client.subscribe('sensor-data', {
filter: { 'data.severity': { $gte: 2 } },
});
alerts.bind('message', (data) => console.log('Alert:', data));
const room = await client.subscribe('presence-lobby');
room.bind('user-joined', ({ user_info }) => addToOnlineList(user_info));
room.bind('user-left', ({ user_info }) => removeFromOnlineList(user_info));
room.trigger('chat-message', { text: 'Hello!' });@pingerchips/ai (Vercel AI SDK)
For AI chat UIs, @pingerchips/ai gives you a useChat()-compatible transport
with resumable token streams:
npm install @pingerchips/ai'use client';
import { useChat } from '@ai-sdk/react';
import { createChatTransport, createClientSession } from '@pingerchips/ai/vercel';
const session = createClientSession({
appKey: 'YOUR_APP_KEY',
channelName: threadId,
clientId: 'user-42',
authEndpoint: '/pingerchips/auth',
});
export function Chat() {
const { messages, sendMessage } = useChat({
transport: createChatTransport(session),
});
// …
}It also exports createAgentSession for the server side and a React
ClientSessionProvider / useClientSession. See the
chat quickstart.
Python
The pingerchips Python package is agent-only — it has no pub/sub client,
no subscribe, no presence. For pub/sub from Python, call the
HTTP trigger API. For chat agents, see the
Agent SDK.
Next steps
- Channel Types — public, private, presence
- Server SDK — trigger events from your backend
- App Settings — client messages, user authentication