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

@onyx-engine/client

v1.6.0

Published

Onyx Engine SDK — auth, multiplayer, economy, social, and more

Downloads

2,505

Readme

@onyx-engine/client

TypeScript SDK for the Onyx game platform. One client for everything: authentication, multiplayer, economy, social, leaderboards, achievements, and more.

Works with React Native, Expo, and browsers. No native dependencies.

Versioning

SDK major version = API version. This ensures backwards compatibility:

  • SDK 1.x → always uses API v1. Safe to update within 1.x.
  • When API v2 is released → SDK 2.0.0 will be published.
  • ^1.0.0 in your package.json will never auto-update to 2.x.

Installation

npm install @onyx-engine/client

Quick Start

import { OnyxClient } from "@onyx-engine/client";

const client = new OnyxClient({
  apiKey: "YOUR_API_KEY", // Get from https://onyx.blessupgames.com/dashboard/api-keys
});

// Login
const session = await client.auth.guestLogin("unique-device-id", "ios");
console.log(session.playerId);

// Use any feature
const profile = await client.players.getProfile();
const balance = await client.economy.getBalance();
const friends = await client.friends.list();
const leaderboard = await client.leaderboard.get("daily", 10);

// Real-time multiplayer
await client.realtime.connect("default");
client.realtime.raiseEvent(1, { action: "move", x: 10 }, "others");
client.realtime.on("customEvent", (evt) => {
  console.log(`Player ${evt.senderRef} did:`, evt.data);
});

Authentication

All login methods return AuthResponse and automatically store the JWT for subsequent API calls.

// Guest (anonymous)
await client.auth.guestLogin("device-id", "ios");

// Google
await client.auth.googleLogin(googleIdToken, "device-id", "android");

// Apple
await client.auth.appleLogin(appleIdentityToken, "[email protected]", "Alice", "device-id", "ios");

// Email OTP
await client.auth.sendOtp("[email protected]");
await client.auth.verifyOtp("[email protected]", "123456");

// Link provider to guest account
await client.auth.linkAccount("google", { token: googleIdToken });

// Logout / delete
await client.auth.logout();
await client.auth.deleteAccount();

Token Persistence

JWT tokens expire after 1 hour. The SDK refreshes them automatically on 401. Persist tokens so users stay logged in:

client.onTokenRefresh((jwt, refreshToken) => {
  // Save to AsyncStorage, SecureStore, localStorage, etc.
  storage.set("jwt", jwt);
  storage.set("refreshToken", refreshToken);
});

// Restore session on app restart
const restored = await client.restoreSession(savedJwt, savedRefreshToken);
if (!restored) {
  // Token expired, show login screen
}

Player Profile

const profile = await client.players.getProfile();
// { playerId, username, avatar, stats: { totalScore, gamesPlayed, coins } }

await client.players.setUsername("NewName");     // 30-day cooldown
await client.players.setAvatar("preset", "emoji:🎮");

Friends & Social

const friends = await client.friends.list();
const results = await client.friends.search("ali");
await client.friends.sendRequest("player_xyz...");

const requests = await client.friends.getRequests();
await client.friends.respond(requestId, "accept");

await client.friends.remove(friendshipId);

Chat

const convos = await client.chat.getConversations();
const history = await client.chat.getHistory(friendId, { limit: 50 });
await client.chat.send(friendId, "Hello!");
await client.chat.markAsRead(friendId);
const unread = await client.chat.getUnreadCount();
const ban = await client.chat.getBanStatus();

Blocks & Moderation

await client.blocks.block(playerId);
await client.blocks.unblock(playerId);
const list = await client.blocks.list();
const status = await client.blocks.isBlocked(playerId);

await client.moderation.report({
  reportedPlayerId: 123,
  category: "chat_abuse",
  reason: "Abusive messages",
  evidence: "msg:1234,msg:1235",
});

Economy & IAP

const { coins } = await client.economy.getBalance();
const products = await client.economy.getProducts("chess");
const inventory = await client.economy.getInventory();

// Buy with coins
await client.economy.purchase("hint_pack", 50);

// Verify real-money IAP receipt
await client.economy.verifyPurchase({
  productId: "coins_500",
  platform: "ios",
  transactionId: "apple-tx-id",
  receipt: "base64-receipt",
});

const txns = await client.economy.getTransactions(20, 0);

Leaderboard

const lb = await client.leaderboard.get("weekly", 50);
// { period, entries: [{ rank, playerId, username, score }], myRank, total }

Periods: daily, weekly, monthly, alltime.

Achievements

const { achievements } = await client.achievements.list();
// [{ id, nameKey, progress, unlocked, rewardAmount, rewardClaimed }]

const reward = await client.achievements.claimReward("first_win");
// { coinsAwarded: 50, newBalance: 390 }

Push Notifications

await client.notifications.register(fcmToken, "ios", "device-id");
await client.notifications.updateToken(oldToken, newToken);

const prefs = await client.notifications.getPreferences();
await client.notifications.setPreferences({ challenges: false });

Sessions (Open Lobby)

Browse and create game sessions. Players join freely, leave anytime.

// List open sessions
const { sessions } = await client.sessions.list({ gameType: "chess", limit: 20 });
sessions.forEach(s => console.log(s.sessionRef, s.playerCount, "/", s.maxPlayers));

// Create a session
const session = await client.sessions.create({
  sessionName: "Friday Game Night",
  maxPlayers: 20,
  isOpen: true,
  properties: { gameMode: "casual", language: "tr" },
});

// Join the session
await client.realtime.connect(session.sessionRef);

Matchmaking

Matchmaking runs over WebSocket. Connect to realtime first.

await client.realtime.connect("default");

// Join queue
client.matchmaking.join("chess", "en", 1200);

// Listen for match
client.realtime.onEvent("EvMatchFound", (evt) => {
  console.log("Match found:", evt.sessionRef, evt.opponent);
});

// Cancel
client.matchmaking.cancel();

// Query queue stats
const stats = await client.matchmaking.getQueueStats("word_puzzle", "tr");
console.log(`${stats.playersQueued} players queued, avg wait ${stats.averageWaitSeconds}s`);

// Challenge a friend
client.matchmaking.challenge("player_abc", "chess", "en");
client.realtime.onEvent("EvChallengeResult", (evt) => {
  if (evt.accepted) console.log("Game session:", evt.sessionRef);
});

Real-time Multiplayer

await client.realtime.connect("session-name");

// Send events to other players
client.realtime.raiseEvent(1, { x: 10, y: 20 }, "others");
client.realtime.raiseEvent(2, { card: 42 }, "all");
client.realtime.raiseEvent(3, { msg: "hi" }, ["Player(2)"]);

// Shared room state
client.realtime.setRoomProperties({ phase: "playing", round: 1 });
client.realtime.setRoomProperties(
  { turn: "Player(3)" },
  { turn: "Player(2)" }  // CAS: only if current value matches
);

// Your player state
client.realtime.setPlayerProperties({ score: 42, ready: true });

// Listen for events
client.realtime.on("customEvent", (evt) => console.log(evt.senderRef, evt.data));
client.realtime.on("propertiesChanged", (evt) => console.log(evt.properties));
client.realtime.on("playerJoined", (evt) => console.log(evt.displayName, "joined"));
client.realtime.on("playerLeft", (evt) => console.log(evt.playerRef, "left"));
client.realtime.on("snapshot", (snap) => console.log("Players:", snap.playerCount));
client.realtime.on("disconnected", () => console.log("Lost connection"));

// Time helpers
client.realtime.tickRate;                    // 20
client.realtime.serverTick;                  // current tick
client.realtime.serverTimeSeconds;           // elapsed seconds
client.realtime.remainingSeconds(endTick);   // countdown

// Use power-up
client.realtime.usePowerUp("freeze");

// Disconnect
client.realtime.disconnect();

Live Video (WebRTC)

const creds = await client.video.getTurnCredentials();
// { urls, username, credential, ttl }

const pc = new RTCPeerConnection({
  iceServers: [{ urls: creds.urls, username: creds.username, credential: creds.credential }],
});

const ban = await client.video.getBanStatus();

Error Handling

import { OnyxApiError } from "@onyx-engine/client";

try {
  await client.economy.purchase("item", 100);
} catch (err) {
  if (err instanceof OnyxApiError) {
    console.log(err.status); // 400
    console.log(err.code);   // "insufficient_coins"
    console.log(err.message);
  }
}

The SDK automatically refreshes expired JWTs on 401 responses.

Configuration

const client = new OnyxClient({
  // baseUrl defaults to "https://api.blessupgames.com"
  // wsUrl defaults to "wss://api.blessupgames.com/ws"
  ws: {
    clientVersion: "1.0.0",
    autoReconnect: true,
    reconnectBaseDelayMs: 500,
    reconnectMaxDelayMs: 30000,
    reconnectMaxAttempts: 10,
    debug: false,
  },
});

Low-level GameClient

For advanced use cases, you can use the low-level GameClient directly:

import { GameClient, registerCommand, registerEvent } from "@onyx-engine/client";

const gc = new GameClient("wss://server/ws", { autoReconnect: true });
await gc.connect();
await gc.join("session", "Alice");

gc.raiseEvent(1, { data: "test" }, "others");
gc.setRoomProperties({ key: "value" });
gc.sendCommand("CmdCustom", { field: 1 });

Exports

// High-level client
export { OnyxClient, OnyxApiError };
export type {
  OnyxClientOptions, AuthResponse, PlayerProfile, Friend,
  ChatMessage, Product, Achievement, LeaderboardEntry,
  MatchmakingStatus, BanStatus, BlockedPlayer, Transaction,
  NotificationPreferences, TurnCredentials,
};

// Low-level client
export { GameClient };
export type { GameClientOptions };

// Protocol types
export { MessageType, ConnectionState, DisconnectCode, EventTarget, PropertyScope };

// Serialization
export { registerCommand, registerEvent };

React Native

import { useEffect, useRef, useState } from "react";
import { OnyxClient } from "@onyx-engine/client";
import AsyncStorage from "@react-native-async-storage/async-storage";

const client = new OnyxClient({ apiKey: process.env.ONYX_API_KEY! });

client.onTokenRefresh(async (jwt, rt) => {
  await AsyncStorage.multiSet([["@jwt", jwt], ["@rt", rt]]);
});

function useOnyx() {
  const [loggedIn, setLoggedIn] = useState(false);

  useEffect(() => {
    AsyncStorage.multiGet(["@jwt", "@rt"]).then(async ([[, jwt], [, rt]]) => {
      if (jwt && rt) {
        const ok = await client.restoreSession(jwt, rt);
        setLoggedIn(ok);
      }
    });
  }, []);

  return { client, loggedIn, setLoggedIn };
}