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

@maleeshagimshan98/infinite-messenger-core

v3.0.1

Published

realtime messenging library built with Google Firebase/MongoDB, for Node.js servers

Readme

Messenger Core v2.0


Introduction

Messenger Core is a production-ready, real-time messaging library built with Firebase for Node.js servers. It provides a complete abstraction layer for building chat applications with minimal boilerplate code.

Why Messenger Core?

Building a messaging system from scratch is complex:

  • 📊 Database schema design for users, conversations, and messages
  • ⚡ Real-time synchronization across multiple clients
  • 🔄 Pagination and efficient data retrieval
  • 🏗️ Scalable architecture that supports growth

Messenger Core solves these challenges by providing:

  • Clean, layered architecture with separation of concerns
  • Real-time updates out of the box via Firebase listeners
  • Type-safe API with full TypeScript support
  • Database abstraction - swap implementations without code changes
  • Production-tested with comprehensive test coverage

What You Can Build

  • 💬 One-on-one chat applications
  • 👥 Group messaging platforms
  • 📱 Mobile backend APIs
  • 🎮 In-game chat systems
  • 💼 Customer support portals

Installation

npm install @maleeshagimshan98/infinite-messenger-core

Prerequisites

  • Node.js 14.x or higher
  • Firebase Project with Firestore enabled
  • Service Account Key (JSON file) from Firebase Console

Quick Start

1. Set Up Firebase

  1. Create a Firebase project at Firebase Console
  2. Enable Firestore Database
  3. Generate a service account key:
    • Go to Project Settings → Service Accounts
    • Click "Generate New Private Key"
    • Save the JSON file securely

2. Initialize the Library

import { MessengerCore } from '@maleeshagimshan98/infinite-messenger-core';
import { User, Conversation, Message } from '@maleeshagimshan98/infinite-messenger-core';

// Initialize with Firebase configuration
const messenger = new MessengerCore({
  dbDriver: 'firebase',
  dbConfig: './path/to/firebase-key.json',
});

3. Initialize User Session (Required)

⚠️ Important: You MUST call initialize() before performing any operations with the library. If the user doesn't exist, an error will be thrown - you must handle this appropriately.

try {
  // Initialize user session - REQUIRED before any other operations
  await messenger.initialize('user123');

  // If successful, you can now use the library
  const currentUser = messenger.getUser();
  console.log('Active user:', currentUser.getName());
} catch (error) {
  if (error.message.includes('User not found')) {
    console.log('User does not exist. Creating new user...');

    // Create a new user
    const newUser = await messenger.newUser({
      id: 'user123',
      name: 'John Doe',
      profileImg: 'https://example.com/avatar.jpg',
      lastSeen: new Date().toISOString(),
      permissions: ['read', 'write'],
      conversationsId: 'conv_user123', // Auto-generated if not provided
    });

    console.log('User created:', newUser.getName());

    // Now initialize with the newly created user
    await messenger.initialize('user123');
  } else {
    console.error('Initialization failed:', error);
    throw error;
  }
}

4. Alternative: Create User First (If You Know User Doesn't Exist)

// If you're setting up a new user for the first time
const newUser = await messenger.newUser({
  id: 'user456',
  name: 'Jane Smith',
  profileImg: 'https://example.com/avatar.jpg',
  lastSeen: new Date().toISOString(),
  permissions: ['read', 'write'],
  conversationsId: 'conv_user456',
});

// Then initialize the session
await messenger.initialize('user456');

5. Send Your First Message

// Load conversations (after successful initialization)
await messenger.initConversations();

// Start a new conversation
const conversation = new Conversation({
  id: 'conv_' + Date.now(),
  participants: ['user123', 'user456'],
});

await messenger.conversationService().startConversation(messenger.getUser(), conversation);

// Send a message
const message = new Message({
  senderId: 'user123',
  content: 'Hello, World! 👋',
});

await messenger.messageService().sendMessage(conversation, message);

console.log('Message sent successfully!');

Important: Initialization Workflow

Every session MUST follow this pattern:

  1. Create MessengerCore instance with database configuration
  2. Call initialize(userId) - This is MANDATORY before any operations
    • ✅ If user exists: Session starts successfully
    • ❌ If user doesn't exist: Error is thrown - handle by creating the user first
  3. Perform operations - Only after successful initialization
// Recommended pattern
const messenger = new MessengerCore({ dbDriver: 'firebase', dbConfig: './key.json' });

try {
  await messenger.initialize('user123');
  // ✅ User exists - proceed with operations
  await messenger.initConversations();
  // ... rest of your code
} catch (error) {
  if (error.message.includes('User not found')) {
    // ❌ User doesn't exist - create first, then initialize
    await messenger.newUser({ id: 'user123', name: 'New User' /* ... */ });
    await messenger.initialize('user123');
  }
}

Features

🔐 User Management

Create, retrieve, update, and manage users with ease.

Create a User

const user = await messenger.newUser({
  id: 'user789',
  name: 'Jane Smith',
  profileImg: 'https://example.com/jane.jpg',
  lastSeen: new Date().toISOString(),
  permissions: ['read', 'write', 'admin'],
  conversationsId: 'conv_user789',
});

Get User Information

const userService = messenger.userService();
const user = await userService.getUser('user789');

console.log('User name:', user.getName());
console.log('Last seen:', user.getLastSeen());
console.log('Is active:', user.getIsActive());

Update User Profile

const user = messenger.getUser();
user.setName('Jane Doe');
user.setProfileImg('https://example.com/new-avatar.jpg');

await messenger.updateUser(user);

💬 Conversation Management

Manage conversations between users with real-time synchronization.

Start a New Conversation

const conversationService = messenger.conversationService();

const conversation = new Conversation({
  id: 'conv_' + Date.now(),
  participants: ['user123', 'user456', 'user789'], // Supports multiple participants
});

await conversationService.startConversation(messenger.getUser(), conversation);

Retrieve User's Conversations

// Get all conversations
await messenger.initConversations();
const conversations = conversationService.getConversations();

// Iterate through conversations
Object.values(conversations).forEach((conv) => {
  console.log('Conversation ID:', conv.getId());
  console.log('Participants:', conv.getParticipants());
  console.log('Last updated:', conv.getLastUpdatedTime());
});

Listen for New Conversations (Real-time)

await conversationService.listenToConversations((conversationsData) => {
  if (conversationsData.hasData()) {
    const newConversations = conversationsData.data();

    newConversations.forEach((conv) => {
      console.log('New conversation:', conv.getId());
      console.log('With:', conv.getParticipants());
    });
  }
});

Delete a Conversation

conversationService.deleteConversation(conversation);

Stop Listening

// Clean up listeners to prevent memory leaks
conversationService.detachListener();

📨 Message Management

Send, retrieve, and listen to messages with real-time updates.

Send a Message

const messageService = messenger.messageService();

const message = new Message({
  senderId: 'user123',
  content: 'Hey! How are you doing?',
  timestamp: Date.now(),
});

await messageService.sendMessage(conversation, message);

Retrieve Messages

const messagesResult = await messageService.getMessages(conversation);

if (messagesResult.hasData()) {
  const messages = messagesResult.data();

  messages.forEach((msg) => {
    console.log(`${msg.getSenderId()}: ${msg.getContent()}`);
    console.log(`Sent at: ${msg.getTime()}`);
  });
}

Listen for New Messages (Real-time)

messageService.listen(conversation, (messagesData) => {
  if (messagesData.hasData()) {
    const newMessages = messagesData.data();

    newMessages.forEach((msg) => {
      console.log(`📩 New message from ${msg.getSenderId()}`);
      console.log(`Content: ${msg.getContent()}`);
    });
  }
});

Delete a Message

await messageService.deleteMessage(conversation, 'message-id-123');

Stop Listening to Messages

messageService.detachListener(conversation);

⚡ Real-time Updates

The library provides built-in real-time synchronization using Firebase's snapshot listeners.

Complete Real-time Example

// Start listening to conversations
await conversationService.listenToConversations((conversations) => {
  console.log('Conversations updated!');
});

// Start listening to messages in a specific conversation
messageService.listen(conversation, (messages) => {
  console.log('New messages received!');

  if (messages.hasData()) {
    const messageList = messages.data();
    // Update UI with new messages
    updateChatUI(messageList);
  }
});

// When done, clean up
conversationService.detachListener();
messageService.detachListener(conversation);

📄 Pagination Support

Efficiently load large datasets with built-in pagination.

// Get first batch of messages (default limit: 25)
const firstBatch = await messageService.getMessages(conversation);

// Get next batch using the last message ID
const messages = firstBatch.data();
const lastMessageId = messages[messages.length - 1].getId();

const nextBatch = await messageService.getMessages(conversation, lastMessageId);

Documentation

API Reference

For detailed API documentation, see:

UserService API

| Method | Parameters | Returns | Description | | ------------------ | ---------------- | ---------------------------- | ----------------------- | | getUser(userId) | userId: string | Promise<User \| undefined> | Fetch a user by ID | | newUser(user) | user: NewUser | Promise<User> | Create a new user | | updateUser(user) | user: User | Promise<User> | Update user information | | deleteUser(user) | user: User | Promise<void> | Delete a user |

ConversationService API

| Method | Parameters | Returns | Description | | --------------------------------------- | --------------------------------------------- | --------------------------------------- | ------------------------------- | | getConversations() | None | Promise<Record<string, Conversation>> | Get all conversations | | startConversation(user, conversation) | user: Userconversation: Conversation | Promise<Conversation> | Start a new conversation | | listenToConversations(callback) | callback: Function | Promise<void> | Listen for conversation updates | | deleteConversation(conversation) | conversation: Conversation | void | Delete a conversation | | detachListener() | None | void | Stop listening |

MessageService API

| Method | Parameters | Returns | Description | | ---------------------------------------- | ----------------------------------------------------- | --------------------------------------- | ---------------------------- | | getMessages(conversation, start?) | conversation: Conversationstart?: string | Promise<DatabaseResultSet<Message[]>> | Get messages with pagination | | sendMessage(conversation, message) | conversation: Conversationmessage: Message | Promise<void> | Send a message | | listen(conversation, callback) | conversation: Conversationcallback: Function | void | Listen for new messages | | deleteMessage(conversation, messageId) | conversation: ConversationmessageId: string | Promise<void> | Delete a message | | detachListener(conversation) | conversation: Conversation | void | Stop listening |

Data Models

Data Models

User

Represents a user in the messaging system.

type NewUser = {
  id: string; // Unique user identifier
  name: string; // Display name
  profileImg: string; // Profile image URL
  lastSeen: string; // Last activity timestamp
  permissions: string[]; // User permissions/roles
  conversationsId: string; // Unique conversation collection ID (format: conv_{userId})
};

Key Methods:

  • getId() - Get user ID
  • getName() - Get user name
  • getIsActive() - Check if user is online
  • getConversationsId() - Get conversation collection ID
  • setIsActive(status) - Set online/offline status
  • toObj() - Convert to plain object for storage

Conversation

Represents a conversation/thread between participants.

interface NewConversation {
  id: string; // Unique conversation identifier
  participants: string[]; // Array of participant user IDs
  startedDate?: string; // When conversation was created
  lastUpdatedTime?: string; // Last activity timestamp
  lastMessageId?: string; // ID of most recent message
  timestamp?: number; // Unix timestamp for sorting
  messages?: Record<string, Message>; // Cached messages
}

Key Methods:

  • getId() - Get conversation ID
  • getParticipants() - Get participant IDs
  • getMessages() - Get cached messages
  • setMessage(message) - Add a message
  • deleteMessage(messageId) - Remove a message
  • toObj() - Convert to plain object for storage

Message

Represents a single message in a conversation.

type NewMessage = {
  id?: string; // Unique message identifier (auto-generated if not provided)
  senderId: string; // ID of sender
  content?: string; // Message text
  time?: string; // Human-readable timestamp
  timestamp?: number; // Unix timestamp for ordering
};

Key Methods:

  • getId() - Get message ID
  • getSenderId() - Get sender ID
  • getContent() - Get message text
  • getTime() - Get formatted time
  • setContent(message) - Update content
  • toObj() - Convert to plain object for storage

Architecture

Overview

Messenger Core follows a clean, layered architecture with clear separation of concerns:

┌─────────────────────────────────┐
│      MessengerCore (Facade)     │  ← Entry point
└────────────┬────────────────────┘
             │
    ┌────────┴────────┐
    │   Services      │  ← Business logic
    │  (User, Conv,   │
    │   Message)      │
    └────────┬────────┘
             │
    ┌────────┴────────┐
    │   Datastore     │  ← Database abstraction
    │  (Interface)    │
    └────────┬────────┘
             │
    ┌────────┴────────┐
    │  Firebase Impl  │  ← Firebase repositories
    └─────────────────┘

Key Design Patterns

  • Facade Pattern: MessengerCore provides a simplified interface to complex subsystems
  • Repository Pattern: Data access abstracted through repository interfaces
  • Service Layer: Business logic separated into dedicated service classes
  • Strategy Pattern: Database implementations are pluggable via interfaces
  • Observer Pattern: Real-time listeners notify subscribers of data changes

Data Structure in Firebase

Firestore Database
│
├── users/                          # User collection
│   └── {userId}/                   # User document
│
├── conv_{userId}/                  # Each user's conversations
│   └── {conversationId}/           # Conversation document
│
└── {conversationId}/               # Messages for each conversation
    └── {messageId}/                # Message document

Key Design Decisions:

  • Conversation Duplication: Each conversation is stored in all participants' collections for efficient querying
  • Separate Message Collections: Messages are stored in collections named by conversationId for scalability
  • User-specific Collection IDs: Format conv_{userId} ensures data isolation

For In-Depth Understanding

For a comprehensive architectural overview, see LIBRARY_ARCHITECTURE.md, which includes:

  • Detailed component breakdown
  • Complete execution paths
  • Data flow diagrams
  • Implementation details
  • Future enhancement roadmap

Examples

Complete Chat Application

import { MessengerCore, User, Conversation, Message } from '@maleeshagimshan98/infinite-messenger-core';

// Initialize
const messenger = new MessengerCore({
  dbDriver: 'firebase',
  dbConfig: './firebase-key.json',
});

// Initialize user session (REQUIRED - must be called first)
try {
  await messenger.initialize('user123');
} catch (error) {
  if (error.message.includes('User not found')) {
    // Create user if doesn't exist
    await messenger.newUser({
      id: 'user123',
      name: 'John Doe',
      profileImg: '',
      lastSeen: new Date().toISOString(),
      permissions: ['read', 'write'],
      conversationsId: 'conv_user123',
    });
    await messenger.initialize('user123');
  } else {
    throw error;
  }
}

// Get services
const conversationService = messenger.conversationService();
const messageService = messenger.messageService();

// Load conversations
await messenger.initConversations();

// Listen for new conversations
await conversationService.listenToConversations((conversations) => {
  console.log('Conversations updated!');
});

// Start a conversation
const conversation = new Conversation({
  id: 'conv_' + Date.now(),
  participants: ['user123', 'user456'],
});

await conversationService.startConversation(messenger.getUser(), conversation);

// Listen for messages
messageService.listen(conversation, (messages) => {
  if (messages.hasData()) {
    messages.data().forEach((msg) => {
      console.log(`${msg.getSenderId()}: ${msg.getContent()}`);
    });
  }
});

// Send a message
const message = new Message({
  senderId: 'user123',
  content: 'Hello!',
});

await messageService.sendMessage(conversation, message);

// Clean up when done
messageService.detachListener(conversation);
conversationService.detachListener();

Proper Initialization & Error Handling

Always handle the initialization properly:

const messenger = new MessengerCore({
  dbDriver: 'firebase',
  dbConfig: './firebase-key.json',
});

try {
  // Attempt to initialize with existing user
  await messenger.initialize('user123');
  console.log('✅ User session initialized successfully');

  // Proceed with your application logic
  await messenger.initConversations();
  // ... rest of your code
} catch (error) {
  if (error.message.includes('User not found')) {
    console.log('⚠️ User not found. Creating new user...');

    // Create user if not exists
    const newUser = await messenger.newUser({
      id: 'user123',
      name: 'New User',
      profileImg: '',
      lastSeen: new Date().toISOString(),
      permissions: ['read', 'write'],
      conversationsId: 'conv_user123',
    });

    console.log('✅ User created:', newUser.getName());

    // Initialize with newly created user
    await messenger.initialize('user123');
    console.log('✅ User session initialized successfully');
  } else {
    console.error('❌ Initialization failed:', error);
    throw error;
  }
}

Common Initialization Patterns

Pattern 1: Existing User (Most Common)

// For existing users in your system
const messenger = new MessengerCore({ dbDriver: 'firebase', dbConfig: './key.json' });
await messenger.initialize('existingUserId123');
// ✅ Ready to use

Pattern 2: New User Registration

// When registering a new user
const messenger = new MessengerCore({ dbDriver: 'firebase', dbConfig: './key.json' });

// Create user first
await messenger.newUser({
  id: 'newUserId456',
  name: 'Alice Johnson',
  profileImg: 'https://example.com/alice.jpg',
  lastSeen: new Date().toISOString(),
  permissions: ['read', 'write'],
  conversationsId: 'conv_newUserId456',
});

// Then initialize
await messenger.initialize('newUserId456');
// ✅ Ready to use

Pattern 3: Safe Initialization (Try existing, create if needed)

const messenger = new MessengerCore({ dbDriver: 'firebase', dbConfig: './key.json' });

async function safeInitialize(userId: string, userData: NewUser) {
  try {
    await messenger.initialize(userId);
    console.log('✅ Existing user initialized');
  } catch (error) {
    if (error.message.includes('User not found')) {
      console.log('⚠️ Creating new user...');
      await messenger.newUser(userData);
      await messenger.initialize(userId);
      console.log('✅ New user created and initialized');
    } else {
      throw error;
    }
  }
}

// Use it
await safeInitialize('user789', {
  id: 'user789',
  name: 'Bob Wilson',
  profileImg: '',
  lastSeen: new Date().toISOString(),
  permissions: ['read', 'write'],
  conversationsId: 'conv_user789',
});
// ✅ Ready to use, whether user existed or not

TypeScript Support

try { await messenger.initialize('user123'); } catch (error) { if (error.message.includes('User not found')) { // Create user if not exists const newUser = await messenger.newUser({ id: 'user123', name: 'New User', profileImg: '', lastSeen: new Date().toISOString(), permissions: ['read', 'write'], conversationsId: 'conv_user123', });

await messenger.initialize('user123');

} else { console.error('Initialization failed:', error); } }


### TypeScript Support

Full TypeScript support with type definitions:

```typescript
import type {
  User,
  Conversation,
  Message,
  NewUser,
  NewConversation,
  NewMessage,
} from '@maleeshagimshan98/infinite-messenger-core';

// Type-safe user creation
const userdata: NewUser = {
  id: 'user123',
  name: 'John Doe',
  profileImg: 'https://example.com/avatar.jpg',
  lastSeen: new Date().toISOString(),
  permissions: ['read', 'write'],
  conversationsId: 'conv_user123',
};

const user: User = await messenger.newUser(userData);

Testing

The library includes comprehensive test coverage:

# Run all tests
npm test

# Run tests with coverage
npm run test:coverage

# Run specific test file
npm test -- conversation.service.test.ts

Test Structure:

  • Unit tests for models (User, Conversation, Message)
  • Integration tests for services
  • Firebase repository tests
  • End-to-end workflow tests

License

This project is licensed under the MIT License - see below for details.

MIT License

Copyright (c) 2025 Maleesha Gimshan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Contributing

Contributions are welcome and greatly appreciated! 🎉

How to Contribute

  1. Fork the repository

    git clone https://github.com/maleeshagimshan98/messenger-core-v2.0.git
    cd messenger-core-v2.0
  2. Create a feature branch

    git checkout -b feature/amazing-feature
  3. Make your changes

    • Write clean, documented code
    • Add tests for new features
    • Ensure all tests pass: npm test
    • Follow the existing code style: npm run lint
  4. Commit your changes

    git commit -m 'Add some amazing feature'
  5. Push to your branch

    git push origin feature/amazing-feature
  6. Open a Pull Request

    • Provide a clear description of changes
    • Reference any related issues
    • Ensure CI checks pass

Development Setup

# Install dependencies
npm install

# Run tests
npm test

# Run linter
npm run lint

# Fix lint issues
npm run lint:fix

# Format code
npm run format

# Build
npm run build

Code of Conduct

Please be respectful and constructive in all interactions. We aim to maintain a welcoming and inclusive community.


Bug Reports & Feature Requests

Reporting Bugs

If you find a bug, please open an issue with:

  • Clear title describing the issue
  • Detailed description of the problem
  • Steps to reproduce the bug
  • Expected behavior vs actual behavior
  • Environment details:
    • Node.js version
    • Library version
    • Operating system
    • Database (Firebase/MongoDB)
  • Code sample or minimal reproduction
  • Error messages or stack traces

Example:

## Bug: Messages not appearing in real-time

**Description**: When using `messageService.listen()`, new messages don't appear until page refresh.

**Steps to Reproduce**:

1. Initialize messenger with user123
2. Start listening to conversation: `messageService.listen(conv, callback)`
3. Send message from another client
4. Callback is not invoked

**Expected**: Callback should be invoked with new messages
**Actual**: No callback, messages appear only after refresh

**Environment**:

- Node.js: v16.14.0
- Library: v2.0.0
- OS: Windows 11
- Database: Firebase

**Code**:
\```typescript
messageService.listen(conversation, (messages) => {
console.log('New messages:', messages.data());
});
\```

**Error**: None in console

Feature Requests

Have an idea? Open an issue with:

  • Feature description - What should it do?
  • Use case - Why is it needed?
  • Example API - How would you use it?
  • Alternatives considered - Other approaches you've thought about

Support & Community


Author

Maleesha Gimshan


Acknowledgments

  • Firebase team for the amazing real-time database
  • All contributors who have helped improve this library
  • The Node.js and TypeScript communities

Changelog

See CHANGELOG.md for a detailed history of changes (coming soon).


Roadmap

Upcoming Features

  • 🔄 MongoDB Support - Alternative database implementation
  • 📎 File Attachments - Support for media messages
  • Message Status - Delivery and read receipts
  • 🔔 Push Notifications - Real-time notification support
  • 👥 Group Management - Add/remove participants
  • 🔍 Message Search - Full-text search capabilities
  • 🔐 Enhanced Security - Encryption and permission systems

See GitHub Issues for detailed roadmap and progress.