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

elysia-nnn-socket-client

v1.0.0

Published

WebSocket client for elysia-nnn-socket 1.0 — room:join/leave with ack protocol, auto-prefixed room events, multi-arg payload packing, reconnect re-join

Downloads

141

Readme

elysia-nnn-socket-client

npm version license

English | Tiếng Việt

Phiên bản: 1.0.0 · Dùng cùng elysia-nnn-socket ^1.0

Client WebSocket native cho protocol JSON của elysia-nnn-socket — API giống nnn-socket.io-client: room:join, event có prefix theo room, giao thức ack, tự join lại sau reconnect.

Client này triển khai wire format mà elysia-nnn-socket 1.0 dùng:

  • Đi ra (client → server){ type, ...positionalArgs, __ack? } (mỗi top-level key sau type là một positional argument mà handler server nhận).
  • Broadcast đến (server → client){ type, ...payload } cho 1 object, { type, data } cho 1 primitive, { type, args } cho nhiều arg, { type } cho không có arg.
  • Ack đến{ __ack, ok: true, response } với response mang RoomAck ({ ok, room, params } hoặc { ok: false, error }).

Cài đặt

bun add elysia-nnn-socket-client

Không phụ thuộc Socket.IO — chỉ dùng WebSocket của trình duyệt / Bun. Chạy được trên browser, Bun, Deno, Node ≥ 18.


Ví dụ nhanh

import { createElysiaNnnClient } from "elysia-nnn-socket-client";

const client = createElysiaNnnClient({
  url: "http://localhost:3000",
  path: "/ws",
  query: { user: "alice" }, // upgrade query (vd: auth middleware)
});

await client.connect();

const chat = client.room("chat");
chat.on("welcome", (data) => console.log("welcome", data));

await chat.join();
chat.emit("message", { text: "chào" });

await chat.leave();
client.disconnect();

API chính

| API | Mô tả | |-----|--------| | createElysiaNnnClient(options) | Tạo client | | client.connect() | Mở WebSocket, trả Promise<void> | | client.disconnect() | Đóng và xóa danh sách room đã join | | client.closeSocket() | Chỉ đóng socket (giữ room cho autoRejoin) | | client.connected | true khi underlying WebSocket đang OPEN | | client.joinedRooms | string[] các room đang join | | client.room("chat") | Lấy / tạo RoomHandle | | client.room("room/[id]", { id: "a" }) | Room động qua buildRoom | | client.join("chat") / client.leave("chat") | Viết tắt của room().join() / .leave() | | room.join() / room.leave() | Join/leave có ack, trả RoomJoinResult | | room.emit("message", data) | Gửi <room>:message | | room.emitJoined("message", data) | join() + emit() trong 1 lệnh | | room.on("message", fn) | Nghe <room>:message | | room.off("message", fn?) | Gỡ listener (hoặc tất cả) | | room.once("message", fn) | Listener một lần | | client.on("ping", fn) | Event namespace (không có prefix) | | client.emit("ping", data) | Event namespace fire-and-forget | | client.sendWire({ type, ... }) | Gửi raw wire | | client.requestAck(event, payload) | Gửi + chờ ack, resolve response | | buildRoom("room/[id]", params) | Resolve template [param] |

Tuỳ chọn

{
  url: string;              // base http(s) — tự đổi sang ws(s)
  path?: "/ws";             // phải trùng server `endpoint`
  query?: Record<string, string>;
  joinEvent?: "room:join";  // phải trùng server `joinEvent`
  leaveEvent?: "room:leave";// phải trùng server `leaveEvent`
  autoRejoin?: true;
  ackTimeout?: 10_000;
  connectTimeout?: 10_000;
  onRejoinError?: (room, err) => void;
  logRejoinErrors?: false;
}

Lỗi

  • NnnRoomError — server ack { ok: false, error }
  • NnnAckTimeoutError — hết thời gian chờ ack
  • NnnSocketError — connect / not connected / thiếu param

Wire format — bảng tra cứu

emit(event, ...args) pack args theo cùng cách server unpack:

| Args | Wire shape | Server nhận | |------|------------|-------------| | không có | { type: "evt" } | handler() | | 1 object | { type: "evt", ...payload } | handler(payload) | | 1 primitive | { type: "evt", data: value } | handler(value) | | nhiều | { type: "evt", args: [...] } | handler(...args) |

on(event, handler) decode broadcast đến theo type và dispatch với cùng quy tắc — server broadcast socket.emit("chat:message", { from, text }) sẽ tới handler của bạn dưới dạng (payload) => ... với payload = { from, text }.

Luồng ack

// Gửi đi
client.requestAck<{ ok: true; room: string }>(
  "chat:save",
  { id: "doc-1", body: "..." },
);
// Wire: { type: "chat:save", id: "doc-1", body: "...", __ack: "<rand>" }

// Server handler (signature: `save(socket, ctx, id, body, ack)`)
on: {
  save(ws, ctx, id, body, ack) {
    ack({ ok: true, room: "chat" }); // → response của promise đang chờ
  },
}

// Envelope đến (resolve của requestAck nhận `response`)
{ __ack: "<rand>", ok: true, response: { ok: true, room: "chat" } }

Room động

import { buildRoom } from "elysia-nnn-socket-client";

buildRoom("room/[id]", { id: "alpha" }); // "room/alpha"

const room = client.room("room/[id]", { id: "alpha" });
await room.join();
room.emit("say", "hello");               // → server `say(socket, ctx, "hello")`
room.on("echo", (payload) => console.log(payload));

Reconnect

const client = createElysiaNnnClient({ url, autoRejoin: true });

await client.connect();
await client.join("chat");

client.closeSocket();   // đóng connection, giữ joinedRooms
await client.connect(); // tự join lại "chat" khi mở lại

onRejoinError(room, error) được gọi khi ack re-join về { ok: false, error }. Error là NnnRoomError. Room bị xoá khỏi joinedRooms trong mọi trường hợp.


Test

bun test tests --concurrency 1

E2E test chạy với package elysia-nnn-socket ngay trong monorepo — tạo fixture room file, khởi động Bun server thật, exercise đầy đủ flow join → emit → ack.


License

MIT