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

@dnotifier-realtime/dnotifier

v1.1.20

Published

Real-time communication & AI SDK

Readme

DNotifier JavaScript SDK

DNotifier is a real-time notification and messaging SDK for Node.js and browsers. It supports WebSocket and HTTP transports, binary streaming, framed packets, an AI pipeline, knowledge-base (RAG) APIs, optional session logging, and multi-step workflows with agents and observability.


Features

  • WebSocket and HTTP transport
  • Secure authentication handshake
  • Real-time messaging (text and binary)
  • Large payloads with automatic chunking and reassembly
  • AI: sendAI, fetchAIHistory, semantic search
  • Knowledge base: add, update, get, list, delete, and search documents
  • Chat history fetch and delete
  • Optional SDK session logging (logs: true)
  • Workflows: named agents, sequential pipelines, dashboard observability
  • Plan limits from auth (getPlanLimits)

Installation

npm install @dnotifier-realtime/dnotifier

Quick start

import { DNotifier } from "@dnotifier-realtime/dnotifier";

const notifier = new DNotifier({
  appId: "your-app-id",
  secret: "your-app-secret",
  transport: "ws", // or "http"
  userId: "user-123",
  onConnected: () => console.log("Connected"),
  onMessage: (msg) => {
    console.log("From:", msg.metadata.sender);
    console.log("Payload:", msg.payload.toJSON());
  },
  onDisconnected: () => console.log("Disconnected"),
});

await notifier.connect();

notifier.send({
  senderId: "user-123",
  receiverId: "user-456",
  data: { type: "text", text: "Hello from DNotifier" },
});

For Node.js WebSocket, pass a WebSocket implementation:

import WebSocket from "ws";

const notifier = new DNotifier({
  appId: "your-app-id",
  secret: "your-app-secret",
  transport: "ws",
  userId: "user-123",
  WebSocketImpl: WebSocket,
  onConnected: () => {},
  onMessage: () => {},
  onDisconnected: () => {},
});

Connection and configuration

| Option | Type | Description | |--------|------|-------------| | appId | string | Your DNotifier application id | | secret | string | Application secret | | transport | "ws" | "http" | "ws" for realtime messaging; "http" for RPC-style AI/RAG calls | | userId | string | End-user or agent id for auth and routing | | logs | boolean | When true, tracks AI sessions in the DNotifier logs dashboard | | url | string | Optional custom WebSocket URL | | WebSocketImpl | class | Required in Node when using transport: "ws" | | onConnected | function | Called when the client is ready | | onMessage | function | Called with { metadata, payload } for incoming messages | | onDisconnected | function | Called when the connection closes |

await notifier.connect();

const limits = notifier.getPlanLimits();
console.log(limits?.aiEnabled, limits?.maxAIRequestsPerMonth);

After connect(), these properties are available:

| Property | Description | |----------|-------------| | isConnected | Whether the client is connected | | aiEnabled | Whether AI is enabled for the current plan | | authToken | Auth token from the last successful connect | | messageSizeLimit | Max message size in bytes (from plan) |


Real-time messaging

Text and structured messages

Use any type in data that fits your app (text, image, audio, doc, etc.):

await notifier.send({
  senderId: "user-123",
  receiverId: "user-456",
  data: {
    type: "text",
    text: "hello from dnotifier sdk!",
  },
  saveHistory: true, // default true
});

Send to multiple receivers:

await notifier.send({
  senderId: "user-123",
  receiverIds: ["user-456", "user-789"],
  data: { type: "text", text: "Hello everyone" },
});

Images, audio, and binary

Prefer send() with a typed payload. The SDK chunks large payloads automatically.

import { readFileSync } from "fs";

const imageBytes = readFileSync("./photo.png");

await notifier.send({
  senderId: "user-123",
  receiverId: "user-456",
  data: {
    type: "image",
    content: imageBytes,
  },
});

Handle incoming messages in onMessage:

onMessage: (msg) => {
  const body = msg.payload.toJSON();
  if (body?.type === "image") {
    // body.content — image bytes or base64 depending on your app convention
  } else if (body?.type === "text") {
    console.log(body.text);
  }
},

Raw binary (sendBinary)

notifier.sendBinary({
  senderId: "user-123",
  receiverIds: ["user-456"],
  buffer: new Uint8Array([0x00, 0x01, 0x02]),
  type: "binary",
});

AI

Requires aiEnabled on your plan. Use transport: "http" for request/response AI calls.

Simple prompt

const response = await notifier.sendAI({
  senderId: "user-123",
  message: { text: "Summarize our refund policy in two sentences." },
});

Chat-style messages

const response = await notifier.sendAI({
  senderId: "user-123",
  message: {
    useKnowledgeBase: true,
    messages: [
      { role: "system", content: "You are a helpful support agent." },
      { role: "user", content: "How do I reset my password?" },
    ],
  },
  saveHistory: true,
});

Continue an existing session

const first = await notifier.sendAI({
  senderId: "user-123",
  message: { text: "Start a new support session." },
});

const sessionId = first?.metadata?.packet?.id;

await notifier.sendAI({
  senderId: "user-123",
  sessionId,
  message: { text: "Follow-up question in the same session." },
});

AI history

const history = await notifier.fetchAIHistory({ senderId: "user-123" });

await notifier.deleteAIHistoryMessage({
  senderId: "user-123",
  messageId: "message-id-to-delete",
});

Knowledge base (RAG)

// Add
await notifier.addDocument({
  senderId: "user-123",
  recordId: "doc-001",
  content: "Refund requests are processed within 5 business days.",
  type: "text",
  metadata: { source: "help-center" },
});

// Update
await notifier.updateDocument({
  senderId: "user-123",
  recordId: "doc-001",
  content: "Updated refund policy text...",
  type: "text",
});

// Get one document
const doc = await notifier.getDocument({
  senderId: "user-123",
  recordId: "doc-001",
});

// List
const list = await notifier.listDocuments({
  senderId: "user-123",
  limit: 20,
  offset: 0,
  type: "text",
});

// Semantic search
const hits = await notifier.search({
  senderId: "user-123",
  query: "how long do refunds take",
  limit: 5,
  minSimilarity: 0.7,
  filterbySource: "help-center",
});

// Delete
await notifier.deleteDocument({
  senderId: "user-123",
  recordId: "doc-001",
});

Chat history

await notifier.fetchChatHistory({
  senderId: "user-123",
  receiverIds: ["user-456"],
});
// Server responds via onMessage with the history payload.

await notifier.deleteChatHistoryMessage({
  senderId: "user-123",
  messageId: "message-id-to-delete",
});

Session logging

Enable automatic AI session tracking in the DNotifier dashboard:

const notifier = new DNotifier({
  appId: "your-app-id",
  secret: "your-app-secret",
  transport: "http",
  userId: "user-123",
  logs: true,
  onConnected: () => {},
  onMessage: () => {},
  onDisconnected: () => {},
});

When logs: true, sendAI reports session start, completion, and token usage to DNotifier.


Workflows and agents

Build deterministic, multi-step AI pipelines with named agents, shared workflow state, and optional observability (execution and step telemetry in the DNotifier workflow dashboard).

1. Define agents

import { DNotifier } from "@dnotifier-realtime/dnotifier";

const intentAgent = DNotifier.defineAgent({
  name: "intent-agent",
  async run(ctx) {
    await ctx.sendAI({
      message: { text: "Classify: search or general?" },
      saveHistory: false,
      label: "Intent classification",
    });
    return { intent: "search" };
  },
});

const generalAgent = DNotifier.defineAgent({
  name: "general-agent",
  async run(ctx) {
    const question = ctx.input?.question ?? ctx.input;
    return { answer: `You asked: ${question}` };
  },
});

2. Define a workflow

const workflow = new DNotifier.Workflow({
  name: "intent-router",
  description: "Route user input to search or general Q&A",
  observability: true,
  async entry(ctx) {
    const { intent } = await ctx.runAgent("intent-agent");

    if (intent === "search") {
      await ctx.search({
        query: String(ctx.input),
        limit: 5,
        label: "Knowledge search",
      });
      return { branch: "search" };
    }

    const answer = await ctx.runAgent("general-agent", {
      input: { question: ctx.input },
    });
    return { branch: "general", answer };
  },
}).registerAgents({
  "intent-agent": intentAgent,
  "general-agent": generalAgent,
});

3. Run the workflow

const notifier = new DNotifier({
  appId: "your-app-id",
  secret: "your-app-secret",
  transport: "http",
  userId: "user-123",
  onConnected: () => {},
  onMessage: () => {},
  onDisconnected: () => {},
});

await notifier.connect();

const run = await notifier.runWorkflow({
  workflow,
  input: "How does messaging work?",
});

console.log(run.executionId); // present when observability: true
console.log(run.result);      // { branch: "search" } or similar
console.log(run.state);       // shared state from the run

Multi-stage pipeline example

const createAgent = DNotifier.defineAgent({
  name: "content-creator",
  async run(ctx) {
    const brief = ctx.input;
    const aiResponse = await ctx.sendAI({
      message: {
        useKnowledgeBase: false,
        messages: [
          { role: "system", content: "Write a blog draft in markdown." },
          { role: "user", content: `Topic: ${brief.topic}` },
        ],
      },
      saveHistory: false,
      label: "Draft creation",
    });
    const content = aiResponse?.data?.content ?? "";
    ctx.state.draft = content;
    return { content };
  },
});

const workflow = new DNotifier.Workflow({
  name: "blog-article-writer",
  description: "Create, refine, and optimize a blog post",
  observability: true,
  async entry(ctx) {
    const draft = await ctx.runAgent("content-creator");
    const refined = await ctx.runAgent("clarity-editor", {
      input: { content: draft.content },
    });
    return { article: refined.content };
  },
}).registerAgents({
  "content-creator": createAgent,
  "clarity-editor": clarityEditorAgent,
});

WorkflowContext (inside entry and agent run)

| Member / method | Description | |-----------------|-------------| | ctx.input | Workflow input passed to runWorkflow (or overridden per runAgent) | | ctx.state | Shared mutable object for the current execution | | ctx.agentName | Name of the agent currently running (inside agent handlers) | | ctx.runAgent(name, { input? }) | Invoke a registered agent | | ctx.sendAI({ message, saveHistory?, label? }) | AI call; steps recorded when observability is on | | ctx.fetchAIHistory({ label? }) | Fetch AI history | | ctx.search({ query, limit?, minSimilarity?, filterbySource?, label? }) | Semantic search | | ctx.addDocument(opts) | Add knowledge-base document | | ctx.updateDocument(opts) | Update document | | ctx.deleteDocument(opts) | Delete document | | ctx.listDocuments(opts?) | List documents | | ctx.getDocument(opts) | Get document by id | | ctx.recordStep({ label, input?, output?, status?, type? }) | Record a custom step when observability is enabled |

When observability: true on the workflow, SDK calls and recordStep are sent to the DNotifier workflow dashboard. No extra setup is required beyond connect().


API reference

DNotifier

| Method | Description | |--------|-------------| | connect() | Authenticate and connect | | disconnect() | Close the WebSocket connection | | getPlanLimits() | Plan limits from last auth | | send({ senderId, receiverId?, receiverIds?, data, saveHistory? }) | Send a message | | sendBinary({ senderId, receiverIds, buffer, type? }) | Send raw bytes | | sendAI({ senderId, message, saveHistory?, sessionId? }) | Send to AI pipeline | | fetchAIHistory({ senderId }) | Fetch AI conversation history | | deleteAIHistoryMessage({ senderId, messageId }) | Delete one AI history message | | fetchChatHistory({ senderId, receiverIds }) | Request chat history (response via onMessage) | | deleteChatHistoryMessage({ senderId, messageId }) | Delete one chat history message | | search({ senderId, query, limit?, minSimilarity?, filterbySource? }) | Semantic search over knowledge base | | addDocument({ senderId, recordId, content, type?, metadata? }) | Add a document | | updateDocument({ senderId, recordId, content, type?, metadata? }) | Update a document | | getDocument({ senderId, recordId }) | Get a document | | listDocuments({ senderId, limit?, offset?, type? }) | List documents | | deleteDocument({ senderId, recordId }) | Delete a document | | runWorkflow({ workflow, input, senderId? }) | Run a workflow after connect() | | sendWithOpenAI(...) | Deprecated — use sendAI |

Workflow exports

| Export | Description | |--------|-------------| | DNotifier.defineAgent({ name, run }) | Create a named agent | | DNotifier.Workflow | Workflow definition class | | DNotifier.WorkflowContext | Context type passed to entry and agent run | | DNotifier.WorkflowError | Workflow validation and runtime errors | | Agent | Agent class (also available via defineAgent) |

runWorkflow is the recommended way to execute a workflow. WorkflowRunner is exported for advanced custom hosting but is not required for typical use.

Message and payload types

DNotifierMessage

  • metadata{ id, sender, timestamp, type }
  • payloadPayload instance

Payload

  • toJSON() — parse JSON body
  • toString() — string body
  • toBase64() — base64 string
  • raw() — raw bytes

getPlanLimits() return value

  • messagesHardLimit, maxAIRequestsPerMonth, maxAIWordsPerMonth
  • knowledgeBaseMaxWords, aiEnabled, maxUsers, maxRowsPerUser

runWorkflow return value

  • result — return value of the workflow entry function
  • state — final shared workflow state
  • executionId — present when observability: true

Links