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

mezon-light-sdk

v1.0.9

Published

lightweight SDK for mezon chat.

Downloads

409

Readme

Mezon Light SDK

A lightweight SDK for Mezon chat integration, providing simple APIs for authentication, real-time messaging, and direct message management.

Installation

npm install mezon-light-sdk

Features

  • 🔐 Simple authentication with ID tokens
  • 💬 Real-time messaging via WebSocket (protobuf-based)
  • 📨 Direct message (DM) and group DM support
  • 🔄 Automatic session management with token refresh
  • 📎 File upload and attachment support (presigned URLs)
  • 🎯 TypeScript-first with full type definitions
  • ⚡ Exponential backoff for socket connection reliability

Quick Start

1. Authenticate with ID Token

import { LightClient, AuthenticationError } from 'mezon-light-sdk';

try {
  const client = await LightClient.authenticate({
    id_token: 'your-id-token',
    user_id: 'user-123',
    username: 'johndoe',
    serverkey: 'your-server-key',     // optional, uses DEFAULT_SERVER_KEY
    gateway_url: 'https://gw.mezon.ai' // optional, uses MEZON_GW_URL
  });
  
  console.log('Authenticated as:', client.userId);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Auth failed:', error.message, error.statusCode);
  }
}

2. Restore Session from Storage

After login, persist the session data and restore it later:

import { LightClient, SessionError } from 'mezon-light-sdk';

// Export session for storage
const sessionData = client.exportSession();
// Returns: { token, refresh_token, api_url, user_id }
localStorage.setItem('mezon_session', JSON.stringify(sessionData));

// Later: restore from storage
try {
  const savedData = JSON.parse(localStorage.getItem('mezon_session')!);
  const client = LightClient.initClient(savedData);
} catch (error) {
  if (error instanceof SessionError) {
    console.error('Session restore failed:', error.message);
  }
}

3. Session Management

Check and refresh the session before connecting:

// Check if session token is expired
if (client.isSessionExpired()) {
  // Check if refresh token is still valid
  if (!client.isRefreshSessionExpired()) {
    await client.refreshSession();
    // Update stored session data
    localStorage.setItem('mezon_session', JSON.stringify(client.exportSession()));
  } else {
    // Both tokens expired - need to re-authenticate
    console.log('Session fully expired, please login again');
  }
}

// Access tokens and session directly if needed
const token = client.getToken();
const refreshToken = client.getRefreshToken();
const session = client.getSession();

4. Connect to Real-time Socket

import { LightSocket, SocketError } from 'mezon-light-sdk';

const socket = new LightSocket(client, client.session);

await socket.connect({
  onError: (error) => console.error('Socket error:', error),
  onDisconnect: () => console.log('Socket disconnected'),
  verbose: false // set to true for debug logging
});

// Check connection status
console.log('Connected:', socket.isConnected);

5. Listen for Messages

// Register a message handler (returns unsubscribe function)
const unsubscribe = socket.onChannelMessage((message) => {
  console.log(`Message from ${message.sender_id}: ${message.content}`);
  console.log('Channel:', message.channel_id);
  console.log('Timestamp:', message.create_time_seconds);
});

// Alternative: use setChannelMessageHandler (does not return unsubscribe)
socket.setChannelMessageHandler((message) => {
  console.log('Received:', message.content);
});

// Multiple handlers can be registered
const unsubscribe2 = socket.onChannelMessage((message) => {
  // Another handler for the same messages
});

// Unsubscribe when no longer needed
unsubscribe();
unsubscribe2();

6. Create and Join Channels

// Create a DM with a single user
const dmChannel = await client.createDM('peer-user-id');
await socket.joinDMChannel(dmChannel.channel_id!);

// Create a group DM with multiple users
const groupDM = await client.createGroupDM(['user-1', 'user-2', 'user-3']);
await socket.joinGroupChannel(groupDM.channel_id!);

// Leave channels
await socket.leaveDMChannel(dmChannel.channel_id!);
await socket.leaveGroupChannel(groupDM.channel_id!);

7. Send Messages

// Send a DM message
await socket.sendDM({
  channelId: 'channel-123',
  content: { t: 'Hello, world!' },
  hideLink: false // optional, whether to hide link previews
});

// Send a group message
await socket.sendGroup({
  channelId: 'group-channel-456',
  content: { t: 'Hello everyone!' },
  attachments: [
    {
      filename: 'image.png',
      url: 'https://cdn.mezon.ai/path/to/image.png',
      filetype: 'image/png',
      size: 42439,
      width: 716,
      height: 522
    }
  ],
  hideLink: true
});

8. Upload Attachments

Upload Flow (Presigned URL):

Mezon uses Presigned URLs for secure file uploads:

  1. Client calls uploadAttachment() with file metadata → Server returns a Presigned URL (with temporary credentials in query params)
  2. Client uses the Presigned URL to PUT file directly to CDN (bypassing backend)
  3. After upload, Client extracts the base URL (removes query params) to use in message attachment

Note: The SDK supports uploading any file type (images, documents, archives, etc.). when uploading non-image files, you can omit the width and height parameters.

Example:

  • Presigned URL: https://cdn.mezon.ai/file.png?X-Amz-Signature=... → Used for upload
  • CDN URL: https://cdn.mezon.ai/file.png → Used in message
import * as fs from 'fs';
import * as path from 'path';

// Step 1: Get file information
const imagePath = path.join(__dirname, 'image.png');
const stats = fs.statSync(imagePath);

// Step 2: Request presigned URL from server
const uploadResponse = await client.uploadAttachment({
  filename: 'image.png',
  filetype: 'image/png',
  size: stats.size,
  width: 800,
  height: 600
});

// uploadResponse.url = "https://cdn.mezon.ai/file.png?X-Amz-Algorithm=..."

// Step 3: Upload file to CDN via presigned URL
const fileBuffer = fs.readFileSync(imagePath);
await fetch(uploadResponse.url!, {
  method: 'PUT',
  headers: { 'Content-Type': 'image/png' },
  body: fileBuffer
});

// Step 4: Extract clean CDN URL (remove query params)
const urlObj = new URL(uploadResponse.url!);
const cdnUrl = `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;
// cdnUrl = "https://cdn.mezon.ai/file.png"

// Step 5: Send message with attachment
await socket.sendDM({
  channelId: 'channel-123',
  content: { t: 'Check out this image!' },
  attachments: [{
    filename: 'image.png',
    url: cdnUrl,        // Use clean CDN URL
    filetype: 'image/png',
    size: stats.size,
    width: 800,
    height: 600
  }]
});

⚠️ Important:

  • Presigned URLs have short expiration (typically 10-60s), upload immediately after receiving
  • Only use clean CDN URL (without query params) in message attachment

9. Disconnect

// Disconnect when done
socket.disconnect();

API Reference

LightClient

| Property/Method | Description | |-----------------|-------------| | LightClient.authenticate(config) | Static: Authenticate with ID token | | LightClient.initClient(config) | Static: Initialize from existing tokens | | client.userId | Get current user ID | | client.session | Get underlying Session object | | client.client | Get underlying MezonApi client | | client.createDM(peerId) | Create a DM channel with one user | | client.createGroupDM(userIds) | Create a group DM with multiple users | | client.uploadAttachment(request) | Upload file and get presigned URL + CDN URL | | client.refreshSession() | Refresh the session using refresh token | | client.isSessionExpired() | Check if session token is expired | | client.isRefreshSessionExpired() | Check if refresh token is expired | | client.getToken() | Get the current auth token | | client.getRefreshToken() | Get the refresh token | | client.getSession() | Get the current session object | | client.exportSession() | Export session data for persistence | | client.createSocket(verbose?, adapter?, timeout?) | Create a raw socket instance |

LightSocket

| Property/Method | Description | |-----------------|-------------| | new LightSocket(client, session) | Create a new socket instance | | socket.connect(options?) | Connect to real-time server | | socket.disconnect() | Disconnect from server | | socket.isConnected | Check if socket is connected | | socket.socket | Get underlying Socket (throws if not connected) | | socket.onChannelMessage(handler) | Register message handler, returns unsubscribe fn | | socket.setChannelMessageHandler(handler) | Register message handler (alias) | | socket.joinDMChannel(channelId) | Join a DM channel | | socket.joinGroupChannel(channelId) | Join a group channel | | socket.leaveDMChannel(channelId) | Leave a DM channel | | socket.leaveGroupChannel(channelId) | Leave a group channel | | socket.sendDM(payload) | Send a DM message | | socket.sendGroup(payload) | Send a group message | | socket.setErrorHandler(handler) | Set custom error handler |

Types

interface ClientInitConfig {
  token: string;           // Auth token
  refresh_token: string;   // Refresh token
  api_url: string;         // API URL
  user_id: string;         // User ID
  serverkey?: string;      // Optional server key
}

interface AuthenticateConfig {
  id_token: string;        // ID token from provider
  user_id: string;         // User ID
  username: string;        // Username
  serverkey?: string;      // Optional server key
  gateway_url?: string;    // Optional gateway URL
}

interface SendMessagePayload {
  channelId: string;                  // Target channel
  content: unknown;                   // Message content
  attachments?: ApiMessageAttachment[]; // Optional attachments
  hideLink?: boolean;                 // Hide link previews
}

interface SocketConnectOptions {
  onError?: (error: unknown) => void;  // Error callback
  onDisconnect?: () => void;           // Disconnect callback
  verbose?: boolean;                    // Enable debug logging
}

interface ApiUploadAttachmentRequest {
  filename?: string;     // File name
  filetype?: string;     // MIME type (e.g., 'image/png')
  size?: number;         // File size in bytes
  width?: number;        // Image width (for images)
  height?: number;       // Image height (for images)
}

interface ApiUploadAttachment {
  filename?: string;     // Uploaded file name
  url?: string;          // Presigned URL (with query params)
}

interface ApiMessageAttachment {
  filename?: string;     // File name
  url?: string;          // Clean CDN URL (without query params)
  filetype?: string;     // MIME type
  size?: number;         // File size in bytes
  width?: number;        // Image width
  height?: number;       // Image height
  thumbnail?: string;    // Thumbnail URL (optional)
  duration?: number;     // Video duration in seconds (optional)
}