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

@orbitconnect/server

v0.1.2

Published

OrbitConnect Server SDK — for use in developer backends

Readme

@orbitconnect/server

Node.js server SDK for OrbitConnect. Manage users, conversations, calls, meetings, media, webhooks, and billing from your backend.

All requests go to https://orbitxyz.muvle.org/api/v1. This base URL is fixed and cannot be overridden.

Install

npm install @orbitconnect/server

Quick start

import { OrbitServer } from '@orbitconnect/server'

const orbit = new OrbitServer({ secretKey: process.env.ORBIT_SECRET_KEY! })

// Register a user
const user = await orbit.users.create({
  external_id: 'your_db_user_id',
  display_name: 'Alice',
})

// Issue a short-lived token for the React SDK
const { token, expires_at } = await orbit.users.createToken(user.id)

// Pass token + clientId to your frontend
res.json({ token, expires_at, clientId: process.env.ORBIT_CLIENT_ID })

Constructor

new OrbitServer({ secretKey: string })

| Option | Type | Required | Description | | --- | --- | --- | --- | | secretKey | string | ✓ | Your secret API key (sk_live_… or sk_test_…) |

Never expose your secret key in client-side code.

Namespaces

| Namespace | Description | | --- | --- | | orbit.users | Create and manage app users, issue short-lived tokens | | orbit.conversations | Direct and group conversations, participant management | | orbit.messages | Send, list, search, and sync messages | | orbit.calls | Voice/video call sessions, WebRTC signaling, recordings | | orbit.meetings | Meeting rooms, participants, screen share, recordings | | orbit.media | Upload, transcode, stream, and access-control media files | | orbit.realtime | Session orchestration — escalate chat → call → meeting | | orbit.webhooks | Register endpoints, subscribe to events, replay deliveries | | orbit.billing | Wallet, usage events, invoices, and low-balance alerts |


orbit.users

// Create or upsert (idempotent on external_id)
const user = await orbit.users.create({ external_id, display_name, metadata? })

// List / get
const users = await orbit.users.list(activeOnly?)
const user  = await orbit.users.get(id)
const user  = await orbit.users.getByExternalId(externalId)

// Update / deactivate
await orbit.users.update(id, { display_name?, metadata? })
await orbit.users.deactivate(id)   // soft-delete, preserves history

// Token issuance
const { token, expires_at } = await orbit.users.createToken(userId, { ttl?: number })
// ttl in seconds — default 3600, max 86400

orbit.conversations

// Direct conversation
const conv = await orbit.conversations.create(
  { type: 'direct', participant_id: userBId },
  actingUserId
)

// Group conversation
const group = await orbit.conversations.create(
  { type: 'group', participant_ids: [a, b, c], metadata: { name: 'Team Alpha' } },
  actingUserId
)

await orbit.conversations.list(actingUserId)
await orbit.conversations.get(convId, actingUserId)
await orbit.conversations.update(convId, data, actingUserId)

// Participants
await orbit.conversations.listParticipants(convId, actingUserId)
await orbit.conversations.addParticipant(convId, userId, actingUserId)
await orbit.conversations.removeParticipant(convId, userId, actingUserId)

orbit.messages

await orbit.messages.send({ conversation_id, content, type?, metadata? }, actingUserId)
await orbit.messages.list(convId, { limit?, offset? }, actingUserId)
await orbit.messages.get(messageId, actingUserId)
await orbit.messages.delete(messageId, actingUserId)
await orbit.messages.search(convId, query, actingUserId, limit?)
await orbit.messages.sync(convId, sinceDate, actingUserId, limit?)

orbit.calls

// Lifecycle
const session = await orbit.calls.initiate({ callee_id, type: 'video' }, callerId)
await orbit.calls.accept(callId, calleeId)
await orbit.calls.reject(callId, calleeId)
await orbit.calls.end(callId, actingUserId)

// WebRTC (server-side signaling helpers)
const token = await orbit.calls.getWebrtcToken(actingUserId)
const turn  = await orbit.calls.getTurnCredentials(actingUserId)
await orbit.calls.submitOffer(callId, sdp, actingUserId)
await orbit.calls.submitAnswer(callId, sdp, actingUserId)
await orbit.calls.addIceCandidate(callId, { candidate }, actingUserId)

// Quality & recordings
await orbit.calls.submitMetrics(callId, { bitrate, packet_loss, jitter, rtt }, actingUserId)
await orbit.calls.getMetrics(callId, actingUserId)
await orbit.calls.toggleRecord(callId, actingUserId)
await orbit.calls.listRecordings(callId, actingUserId)

WebRTC offer/answer/ICE exchange is handled automatically by the React SDK's VideoCall widget and useCall() hook. You only need the server-side helpers for custom signaling flows.

orbit.meetings

// Lifecycle
const meeting = await orbit.meetings.create({ title, description?, scheduled_at? }, hostId)
await orbit.meetings.start(meetingId, hostId)
const { meeting, sfu_session } = await orbit.meetings.join(meetingId, userId, token?)
await orbit.meetings.leave(meetingId, userId)
await orbit.meetings.end(meetingId, hostId)

// Participants
await orbit.meetings.addParticipant(meetingId, userId, actingUserId)
await orbit.meetings.removeParticipant(meetingId, userId, actingUserId)
await orbit.meetings.kickParticipant(meetingId, userId, actingUserId)
await orbit.meetings.updateRole(meetingId, userId, role, actingUserId)
await orbit.meetings.mute(meetingId, userId, actingUserId)
await orbit.meetings.unmute(meetingId, userId, actingUserId)

// Tokens & recordings
const token = await orbit.meetings.generateToken(meetingId, { app_user_id?, role? }, actingUserId)
await orbit.meetings.startRecording(meetingId, actingUserId)
await orbit.meetings.stopRecording(meetingId, actingUserId)
await orbit.meetings.listRecordings(meetingId, actingUserId)

orbit.media

// Upload
const session = await orbit.media.generateUploadUrl(mimeType, actingUserId)
// PUT the file to session.upload_path, then listen for media:ready over WebSocket

// Multipart (large files)
const mp    = await orbit.media.initMultipart(mimeType, filename, actingUserId)
const media = await orbit.media.completeMultipart(mp.session_id, mimeType, size, actingUserId)

// Access
const file   = await orbit.media.get(mediaId, actingUserId)
const { url } = await orbit.media.getDownloadUrl(mediaId, actingUserId)
const { url } = await orbit.media.getStreamUrl(mediaId, actingUserId)
const meta   = await orbit.media.getMetadata(mediaId, actingUserId)
const status = await orbit.media.getStatus(mediaId, actingUserId)

// Management
await orbit.media.process(mediaId, actingUserId)   // trigger reprocessing
await orbit.media.delete(mediaId, actingUserId)
await orbit.media.grantAccess(mediaId, { app_user_id, access_type }, actingUserId)
await orbit.media.listAccess(mediaId, actingUserId)

orbit.realtime

// Session lifecycle
const session = await orbit.realtime.createSession({ type: 'chat_session', context_id? }, userId)
await orbit.realtime.endSession(session.id, userId)

// Orchestration helpers
await orbit.realtime.chatToCall(session.id, callId, userId)
await orbit.realtime.callToMeeting(session.id, meetingId, userId)
await orbit.realtime.meetingToChat(session.id, convId, userId)

// Generic transition
await orbit.realtime.transition(session.id, transitionType, contextId, userId)

// Events & context
await orbit.realtime.emitEvent(session.id, { event_type, payload? }, userId)
await orbit.realtime.updateContext(session.id, { conversation_id?, call_id?, meeting_id? }, userId)
const ctx         = await orbit.realtime.getContext(session.id, userId)
const transitions = await orbit.realtime.listTransitions(session.id, userId)

orbit.webhooks

// Setup
const wh = await orbit.webhooks.create('https://yourapp.com/webhooks/orbit')
await orbit.webhooks.subscribe(wh.id, 'message.sent')
await orbit.webhooks.subscribe(wh.id, 'call.ended')

// Verify signature in your handler
import crypto from 'crypto'
const expected = crypto.createHmac('sha256', wh.secret).update(rawBody).digest('hex')
if (sig !== `sha256=${expected}`) return res.status(401).end()

// Publish, replay, debug
await orbit.webhooks.publish('message.sent', { message_id, conversation_id })
await orbit.webhooks.replay(eventId)
const deliveries = await orbit.webhooks.listDeliveries(wh.id)
const attempts   = await orbit.webhooks.getAttempts(deliveries[0].id)

// Maintenance
await orbit.webhooks.rotateSecret(wh.id)
await orbit.webhooks.update(wh.id, { is_active: false })

orbit.billing

const { balance, currency } = await orbit.billing.getWallet()
await orbit.billing.topUp(amount)
await orbit.billing.getTransactions()

await orbit.billing.recordUsageEvent({ event_type, quantity, metadata? })
await orbit.billing.getUsage()
await orbit.billing.getAggregates()

await orbit.billing.getInvoices()
await orbit.billing.getInvoice(invoiceId)

await orbit.billing.createAlert({ type: 'low_balance', threshold: 5.00 })
await orbit.billing.getAlerts()

Error handling

import { OrbitServerError } from '@orbitconnect/server'

try {
  const user = await orbit.users.get(userId)
} catch (err) {
  if (err instanceof OrbitServerError) {
    console.error(err.status, err.code, err.message)
  }
}

Examples

Runnable examples live in examples/. Run them with:

# from sdk/orbit-server/
npm run example:basic
npm run example:group
npm run example:user
npm run example:call
npm run example:meeting
npm run example:media
npm run example:realtime
npm run example:webhook
npm run example:full

Build

npm run build      # compile to dist/
npm run dev        # watch mode
npm run typecheck  # type-check without emitting

License

MIT