npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@collab-kit/client

v0.0.17

Published

Browser SDK for adding real-time collaboration to any application. Connects to a Collab-Kit server over WebSockets and provides modules for users, rooms, file storage, broadcasts, and schema-driven KV stores.

Readme

Collab-Kit Client

Browser SDK for adding real-time collaboration to any application. Connects to a Collab-Kit server over WebSockets and provides modules for users, rooms, file storage, broadcasts, and schema-driven KV stores.

Install

npm install @collab-kit/client @collab-kit/utils

Quick Start

import CollabKitClient from '@collab-kit/client';

const client = new CollabKitClient({
  serverUrl: 'https://api.collab-kit.com',
  authToken: '<jwt-token>', // from POST /users on your server
});

await client.join(); // defaults to { role: 'editor' }

// Viewers can receive updates and follow users, but cannot write collaboration state.
// await client.join({ role: 'viewer' });

console.log(client.currentUser);
console.log(client.currentRoom);

Constructor Options

import { defineStores } from '@collab-kit/utils';

const client = new CollabKitClient({
  serverUrl: 'https://api.collab-kit.com',
  authToken: '<jwt-token>',
  stores: defineStores({ /* optional store schemas */ }),
});

| Option | Type | Description | |---|---|---| | serverUrl | string | Base HTTP(S) URL of the Collab-Kit server | | authToken | string | JWT returned when creating a user via POST /users | | stores | StoresConfig | Optional schema config created with defineStores() |

Users

Track all collaborators in the current room.

// Active editor collaborators, excluding the current user
client.users.active; // Map<string, CollabKitUser>

// Explicit paginated cache of all users, excluding the current user.
// This map is only populated by client.users.list(...).
client.users.all; // Map<string, CollabKitUser>

const page = await client.users.list({ pageSize: 100, offset: 0 });
// page: { users, total, pageSize, offset, hasMore }

// Listen for user events
client.users.on('userJoined', (user) => {
  console.log(`${user.name} joined`);
});

client.users.on('userLeft', (user) => {
  console.log(`${user.name} left`);
});

// Update the current user
await client.currentUser?.update({ name: 'New Name' });

pageSize is limited to 100. users.active and users.all both exclude the current user.

Roles

Each SDK connection joins as either an editor or a viewer. Roles are session-scoped and can be changed by the client at any time.

await client.join({ role: 'viewer' });

console.log(client.role); // 'viewer'

await client.updateRole('editor');

Editors can write stores, comments, presence, Yjs updates, storage, and broadcasts. Viewers receive realtime updates and can follow/unfollow users, but client-side write methods reject before sending network requests.

User Events

| Event | Payload | Description | |---|---|---| | userJoined | CollabKitUser | An editor became active | | userLeft | CollabKitUser | An editor stopped being active | | userCreated | CollabKitUser | A new user was added to the room | | userUpdated | CollabKitUser | A user's fields changed | | userDeleted | CollabKitUser | A user was removed |

Individual User Events

const user = client.users.all.get(userId);

user.on('updated', (user) => { /* fields changed */ });
user.on('statusChanged', (user) => { /* online <-> offline */ });
user.on('deleted', (user) => { /* removed */ });

Room

// Room info is available after joining
console.log(client.currentRoom);
// { id, name, state, active_participants, duration_seconds, ... }

// Fetch room details
const room = await client.room.get();

Storage

Upload and manage files scoped to the current room.

// Upload a file
const { key, url } = await client.storage.upload({ file });

// Get a file URL
const url = await client.storage.getUrl({ key });

// List all files
const files = await client.storage.getAll();

// Filter by MIME type
const images = await client.storage.getAll({ mimeType: 'image/' });

// Filter by user
const myFiles = await client.storage.getAll({ userId: client.userId });

// Delete a file
await client.storage.delete({ key });

Broadcasts

Send custom events to other participants in the room.

// Send to everyone
client.notifications.broadcast('cursor-move', { x: 100, y: 200 });

// Send to specific users
client.notifications.broadcast('ping', { message: 'hello' }, ['user-id-1']);

// Listen for broadcasts
client.notifications.on('cursor-move', (data) => {
  console.log(data); // { x: 100, y: 200 }
});

// Listen once
client.notifications.once('ping', (data) => {
  console.log(data.message);
});

Stores

Schema-driven KV stores synced in real-time across all connected clients. Define your schemas with defineStores() from @collab-kit/utils and get full type safety.

import CollabKitClient from '@collab-kit/client';
import { defineStores } from '@collab-kit/utils';

const stores = defineStores({
  settings: {
    theme: { type: 'string', required: true, default: 'light' },
    fontSize: { type: 'number', required: true, default: 14 },
    notifications: { type: 'boolean', default: true },
  },
});

const client = new CollabKitClient({
  serverUrl: 'https://api.collab-kit.com',
  authToken: '<jwt-token>',
  stores,
});

await client.join({ role: 'editor' });

// Fully typed — theme is string, fontSize is number
await client.stores.settings.set({
  key: 'user-prefs',
  value: { theme: 'dark', fontSize: 16 },
});

const prefs = await client.stores.settings.get({ key: 'user-prefs' });
// prefs: { theme: string; fontSize: number; notifications?: boolean } | null

// Partial updates
await client.stores.settings.update({
  key: 'user-prefs',
  value: { fontSize: 18 },
});

// List all entries
const all = await client.stores.settings.getAll();
// [{ key: 'user-prefs', value: { theme: 'dark', fontSize: 18, notifications: true } }]

// Delete
await client.stores.settings.delete({ key: 'user-prefs' });

Store Events

// Listen for any change in the store
client.stores.settings.on('changed', (event) => {
  console.log(event.key, event.action, event.value);
  // event.action: 'set' | 'update' | 'delete'
});

// Listen for changes to a specific key
client.stores.settings.on('user-prefs', (value) => {
  console.log('prefs changed:', value);
});

Comments

Room-scoped comments with replies, reactions, and tags. Comments are synced automatically on join and reconnect.

// Add a top-level comment
const comment = await client.comments.add('Hello world');

// Add a comment with tagged users
const comment = await client.comments.add('Check this out', {
  tags: ['user-id-1', 'user-id-2'],
});

// Get all cached top-level comments
const comments = client.comments.getAll();

// Delete a comment (and all its replies)
await client.comments.delete(commentId);

// Sync comments from the server (called automatically on join)
await client.comments.sync();

Comment Instance

Each comment returned by add() or getAll() is a CollabKitCommentInstance with methods for replies, reactions, and tags.

// Replies (one level of nesting)
const reply = await comment.reply('Thanks!');
await comment.deleteReply(reply.id);

// Reactions
await comment.addReaction('thumbsup');
await comment.deleteReaction('thumbsup');

// Tags
await comment.addTag(userId);
await comment.deleteTag(userId);

// Access comment data
comment.id;        // string
comment.userId;    // string
comment.text;      // string
comment.reactions; // Record<string, string[]>
comment.tags;      // string[]
comment.replies;   // CollabKitComment[]
comment.createdAt; // string

Comment Events

// Listen for remote changes
client.comments.on('add', (comment) => { /* new comment from another client */ });
client.comments.on('delete', (commentId) => { /* comment deleted */ });
client.comments.on('update', (comment) => { /* reaction/tag/reply change */ });

// Per-comment events
comment.on('update', (comment) => { /* this comment was updated */ });

Socket Lifecycle

Access low-level socket events for connection state management.

client.socket.on('connected', () => { /* initial connection */ });
client.socket.on('disconnected', () => { /* socket closed */ });
client.socket.on('reconnecting', () => { /* attempting reconnect */ });
client.socket.on('reconnected', () => { /* reconnected & state rehydrated */ });
client.socket.on('failed', () => { /* reconnection attempts exhausted */ });
client.socket.on('authFailed', () => { /* authentication rejected */ });

Disconnect

await client.disconnect();

This gracefully notifies other participants before closing the connection.