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

@bernierllc/in-app-chat

v4.0.1

Published

A generic, role-based in-app chat system for Next.js applications

Downloads

116

Readme

@bernierllc/in-app-chat

A drop-in, role-based in-app messaging suite for Next.js applications. Bring your own data store and permission policy; the package supplies the React provider, hooks, and UI components. External platform bridging (Slack, Discord, Teams) and NeverHub slash-command routing are also included.

Note: Roles are fully configurable. You can use any string for roles (e.g., 'user', 'admin', 'moderator') and configure permissions as needed. No roles are hardcoded.

Naming migration — canonical InAppChat* naming

2.9.0 renamed the package-identity public API from Messaging*/MESSAGING_*/messaging.config.* to canonical InAppChat*/IN_APP_CHAT_*/in-app-chat.config.* to match the package name, keeping every old name as a working @deprecated alias. 3.0.0 removes those aliases — only the canonical names below remain. If you are upgrading from 2.9.x or earlier, rename any Messaging* symbol using this map. (Legacy MESSAGING_* env vars and messaging.config.* files are still read for runtime config, with a one-time deprecation warning.)

| Surface | Removed in 3.0.0 (was) | Canonical (use this) | |---|---|---| | Provider | MessagingProvider | InAppChatProvider | | Hook | useMessaging | useInAppChat | | Constants | MESSAGING_CONSTANTS | IN_APP_CHAT_CONSTANTS | | Config loader | loadMessagingConfiguration, loadMessagingConfigurationWithSources, getMessagingEnvironmentDocumentation, MessagingConfigurationLoader | loadInAppChatConfiguration, loadInAppChatConfigurationWithSources, getInAppChatEnvironmentDocumentation, InAppChatConfigurationLoader | | Config schema | messagingConfigSchema, validateMessagingConfig | inAppChatConfigSchema, validateInAppChatConfig | | Config types | MessagingRuntimeConfig, ResolvedMessagingConfig, MessagingConfig, MessagingProviderProps, UseMessagingReturn, MessagingStats, MessagingError, MessagingEvent, MessagingTheme, MessagingPermissions | InAppChatRuntimeConfig, ResolvedInAppChatConfig, InAppChatConfig, InAppChatProviderProps, UseInAppChatReturn, InAppChatStats, InAppChatErrorInfo, InAppChatEvent, InAppChatTheme, InAppChatPermissions | | Env vars | MESSAGING_* | IN_APP_CHAT_* (legacy MESSAGING_* still read, with a one-time deprecation warning) | | Config file | messaging.config.{js,json} | in-app-chat.config.{js,cjs,json} (legacy still discovered, with a one-time deprecation warning) |

Domain names that describe chat concepts — Message, MessageThread, MessagesPage, MessageInput, useConversations, sendMessage, etc. — are not renamed; they remain the canonical API.

3.0.0 — Breaking Changes

The following APIs are removed in 3.0.0. Replace them before upgrading.

| Removed | Why / Migration | |---|---| | Deprecated Messaging* / MESSAGING_CONSTANTS / messaging*Config* aliases | The 2.9.0 @deprecated aliases are removed. Rename to the canonical InAppChat* / IN_APP_CHAT_CONSTANTS / inAppChat*Config* symbols using the naming migration map above. | | encryptMessage / decryptMessage | These were btoa/atob base64 wrappers, never a security guarantee. At-rest and in-transit encryption belong to your MessageStore / RealTimeTransport implementation. Remove calls — there is no replacement in this package. | | inAppChatApi / createInAppChatApi (and 2.9.0 aliases messagingApi / createMessagingApi) | The api/* entry-point is removed. An HTTP boundary is now a MessageStore whose methods fetch your own routes. See the HTTP-backed store example below. | | createDatabaseConnection / runMigrations / seedDatabase | The database/* entry-point is removed. Replaced by the MessageStore port — implement it against your DB of choice. InMemoryMessageStore is the reference. | | InAppChatProviderProps.config | The provider shape changed from { config: { roles, permissions, database, ... } } to { store, policy, currentUser, transport?, children }. Update your provider instantiation. | | validateMessagePermissions(role, permission) (old positional signature) | Now validateMessagePermissions(policy: PermissionPolicy, role: string, action: ChatPermission): boolean. Pass the injected policy as the first argument. |


Features

  • Role-based messaging — Different interfaces and features for different user types
  • Real-time updates — Live message delivery via the optional RealTimeTransport port
  • File attachments — Support for documents and media sharing
  • Message templates — Role-specific pre-written messages
  • Bulk messaging — Send messages to multiple recipients
  • Search and filtering — Advanced conversation management
  • Responsive design — Works on desktop and mobile
  • Accessibility — WCAG 2.1 AA compliant
  • Customizable — Easy to theme and extend
  • External Integrations — Slack, Discord, Teams bridging with user mapping
  • Slash Commands — Dynamic command routing via NeverHub service discovery
  • Live Configuration — Admin UI toggles for features and integrations
  • Service Discovery — Auto-register commands when packages come online

Quick Start

1. Install

npm install @bernierllc/in-app-chat

2. Define a permission policy

import type { PermissionPolicy } from '@bernierllc/in-app-chat';

const policy: PermissionPolicy = {
  roles: {
    admin: ['send', 'delete', 'createTemplate', 'bulkSend'],
    user: ['send'],
    viewer: [],
  },
};

3. Create a store

For getting started quickly, use the shipped InMemoryMessageStore:

import { InMemoryMessageStore } from '@bernierllc/in-app-chat';

const store = new InMemoryMessageStore({
  conversations: [
    {
      conversationId: 'conv-1',
      participants: [{ id: 'user-1', role: 'admin', name: 'Alice' }],
      lastMessage: 'Hello!',
      timestamp: new Date().toISOString(),
      unread: 0,
    },
  ],
});

4. Mount the provider

store and policy come from steps 2–3 above.

// app/layout.tsx
import { InAppChatProvider } from '@bernierllc/in-app-chat';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const currentUser = { id: 'user-1', role: 'admin' }; // from your auth layer

  return (
    <html lang="en">
      <body>
        <InAppChatProvider
          store={store}
          policy={policy}
          currentUser={currentUser}
        >
          {children}
        </InAppChatProvider>
      </body>
    </html>
  );
}

5. Consume via context

import { useInAppChatContext } from '@bernierllc/in-app-chat';

export function ChatPanel() {
  const { conversations, loading, error, sendMessage, selectConversation } = useInAppChatContext();

  if (loading) return <p>Loading…</p>;
  if (error)   return <p>Error: {error.message}</p>;

  return (
    <ul>
      {conversations.map(c => (
        <li key={c.conversationId} onClick={() => selectConversation(c.conversationId)}>
          {c.lastMessage}
        </li>
      ))}
    </ul>
  );
}

6. Add messaging components to your navigation

import { MessagesIcon, MessagesDropdown } from '@bernierllc/in-app-chat';

export function Navigation() {
  return (
    <nav>
      <MessagesIcon />
      <MessagesDropdown />
    </nav>
  );
}

7. Full messaging page

import { MessagesPage } from '@bernierllc/in-app-chat';

export default function Messages() {
  return <MessagesPage />;
}

Provider Props

interface InAppChatProviderProps {
  store: MessageStore;                        // required — the ONLY data seam
  policy: PermissionPolicy;                   // required — role → permissions data
  currentUser: { id: string; role: string };  // required
  transport?: RealTimeTransport;              // optional — live updates; absent ⇒ no crash
  children: React.ReactNode;
}

Adapter Ports

MessageStore (required)

The persistence port. Back it with your database, an external API, or InMemoryMessageStore.

interface MessageStore {
  getConversations(userId: string, filters?: ConversationFilters): Promise<ConversationListItem[]>;
  getThread(conversationId: string): Promise<MessageThread | null>;
  sendMessage(input: SendMessageInput): Promise<Message>;
  markRead(conversationId: string, userId: string): Promise<void>;
  listTemplates(ownerId: string): Promise<MessageTemplate[]>;
  createTemplate(input: MessageTemplate): Promise<MessageTemplate>;
  updateTemplate(id: string, updates: Partial<Omit<MessageTemplate, 'id'>>): Promise<MessageTemplate>;
  deleteTemplate(id: string): Promise<void>;
}

// sendMessage input shape
interface SendMessageInput {
  conversationId: string;
  senderId: string;
  body: string;
  attachments?: MessageAttachment[];
}

RealTimeTransport (optional)

Pass this to get live message delivery. Omit it and the provider works in poll/refresh mode with no crash.

interface RealTimeTransport {
  subscribe(conversationId: string, onMessage: (msg: Message) => void): () => void;
  // return value is the unsubscribe function — the provider calls it on cleanup
}

PermissionPolicy (required)

Plain data — a role-to-permissions map. The package evaluates it internally.

type ChatPermission = 'send' | 'delete' | 'createTemplate' | 'bulkSend';

interface PermissionPolicy {
  roles: Record<string, ChatPermission[]>;
}

// Example
const policy: PermissionPolicy = {
  roles: {
    admin:  ['send', 'delete', 'createTemplate', 'bulkSend'],
    user:   ['send'],
    viewer: [],
  },
};

Bring Your Own Store

InMemoryMessageStore — reference implementation

InMemoryMessageStore is a real, exported implementation (not a mock). Use it to get running immediately, and read its source as the canonical reference for how to implement a DB-backed store.

import { InMemoryMessageStore } from '@bernierllc/in-app-chat';

const store = new InMemoryMessageStore({
  conversations: [...],  // seed data, optional
  threads:       [...],  // optional
  templates:     [...],  // optional
});

DB-backed store

For production, implement MessageStore against your database. @bernierllc/database-adapter-core is a suggested base.

import type { MessageStore, SendMessageInput } from '@bernierllc/in-app-chat';

export class PostgresMessageStore implements MessageStore {
  constructor(private db: YourDbClient) {}

  async getConversations(userId, filters) { /* SELECT … */ }
  async getThread(conversationId) { /* SELECT … */ }
  async sendMessage(input: SendMessageInput) { /* INSERT … */ }
  async markRead(conversationId, userId) { /* UPDATE … */ }
  // … listTemplates, createTemplate, updateTemplate, deleteTemplate
}

HTTP-backed store

There is no shipped HTTP client or route factory. An HTTP boundary is simply a MessageStore whose methods call your own routes:

export class ApiMessageStore implements MessageStore {
  constructor(private baseUrl: string) {}

  async getConversations(userId) {
    const res = await fetch(`${this.baseUrl}/api/chat/conversations?userId=${userId}`);
    return res.json();
  }

  async sendMessage(input: SendMessageInput) {
    const res = await fetch(`${this.baseUrl}/api/chat/messages`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(input),
    });
    return res.json();
  }

  // … implement the remaining methods against your routes
}

useInAppChatContext() — context value

interface InAppChatContextValue {
  store: MessageStore;
  conversations: ConversationListItem[];
  loading: boolean;
  error: Error | null;
  selectedConversationId: string | null;
  selectConversation: (id: string) => void;
  sendMessage: (conversationId: string, body: string) => Promise<Message | undefined>;
}

External Platform Integrations

Slack Integration

Enable bidirectional messaging with Slack workspaces:

// in-app-chat.config.js
module.exports = {
  integrations: {
    slack: {
      enabled: true,
      userMapping: 'external',    // Show Slack users as "external" in browser
      bidirectional: true,        // Messages flow both ways
      slashCommands: true,        // Enable slash commands in Slack
      socketMode: true,           // Use Socket Mode for real-time
      channelWhitelist: ['#general', '#support']
    }
  }
};

Required Environment Variables:

SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret

Install Integration Package:

npm install @bernierllc/chat-integration-slack

Discord Integration

npm install @bernierllc/chat-integration-discord

Microsoft Teams Integration

npm install @bernierllc/chat-integration-teams

Slash Commands via NeverHub

Dynamic Command Registration

Commands are automatically discovered and registered when their packages come online:

// in-app-chat.config.js
module.exports = {
  slashCommands: {
    '/task': {
      enabled: true,
      package: '@bernierllc/slash-commands-tasks',
      description: 'Create and manage tasks',
      requiresAuth: true,
      platforms: ['in-app', 'slack', 'discord'],
      aliases: ['/tasks', '/todo']
    },
    '/schedule': {
      enabled: true,
      package: '@bernierllc/slash-commands-calendar',
      description: 'Schedule meetings and events'
    }
  }
};

Available Slash Command Packages

npm install @bernierllc/slash-commands-tasks
npm install @bernierllc/slash-commands-calendar
npm install @bernierllc/slash-commands-auth

Service Discovery Flow

  1. in-app-chat registers with NeverHub as command router
  2. Command packages register their capabilities with NeverHub
  3. in-app-chat automatically discovers and registers commands
  4. Commands work across all enabled platforms (in-app, Slack, Discord, Teams)
  5. When packages go offline, commands are automatically deregistered

Admin UI Integration

  • Enable/disable individual slash commands
  • Toggle integrations (Slack, Discord, Teams) on/off
  • Manage permissions for role-based command access
  • View integration status and health monitoring

Components

Core Components

  • InAppChatProvider — Main provider component
  • MessagesIcon — Navigation icon with unread count
  • MessagesDropdown — Quick access dropdown
  • MessagesPage — Full messaging interface
  • ConversationList — List of conversations
  • MessageThread — Message thread view
  • MessageInput — Message input component
  • MessageBubble — Individual message display

Role-Specific Components

  • RoleBasedFilters — Role-specific filtering
  • BulkMessageModal — Bulk messaging interface
  • TemplateSelector — Message template selector
  • FileUpload — File attachment component
  • MessageSearch — Search functionality

Customization

Styling

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    './node_modules/@bernierllc/in-app-chat/dist/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      colors: {
        messaging: {
          primary: '#3b82f6',
          secondary: '#64748b',
          success: '#10b981',
          warning: '#f59e0b',
          error: '#ef4444',
        },
      },
    },
  },
};

Custom Components

import { MessageBubble, MessageBubbleProps } from '@bernierllc/in-app-chat';

export function CustomMessageBubble(props: MessageBubbleProps) {
  return (
    <div className="custom-message-bubble">
      <MessageBubble {...props} />
    </div>
  );
}

Performance

  • Code splitting — Only loads components when needed
  • Virtual scrolling — Handles large message lists efficiently
  • Optimistic updates — Immediate UI feedback
  • Efficient caching — Reduces store calls
  • Bundle optimization — Minimal impact on app size

Security

  • Access validation — Role-based permissions evaluated via injected PermissionPolicy
  • Input sanitization — XSS protection applied by the provider before persistence
  • Rate limiting — Prevents abuse
  • Audit logging — Track all activities

At-rest / in-transit encryption belongs in your MessageStore and RealTimeTransport implementations, not in this package. The old encryptMessage/decryptMessage helpers (base64 only, never a security guarantee) were removed in 3.0.0.


Support

License

MIT License - see LICENSE for details.

Monorepo Workspaces & Dependency Management

This package is part of a monorepo using npm workspaces. All dependencies are hoisted to the root. Always run npm install from the root directory.

React 19 and Testing Library Compatibility

This package uses React 19.1.0. If you see peer dependency warnings with Testing Library, use:

npm install --legacy-peer-deps

This is a temporary workaround until official support is released.