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

@rhino-ai/orchard9-coretsx

v0.3.16

Published

A streamlined TypeScript library for building robust services and microservices with minimal boilerplate

Downloads

117

Readme

@rhino-ai/orchard9-coretsx

A comprehensive TypeScript library for building robust Node.js services with Express, PostgreSQL, Redis, WebSockets, and more.

Installation

npm install @rhino-ai/orchard9-coretsx

Features

  • Configuration Management - Load and validate configuration from YAML files and environment variables
  • Logging - Structured logging with child loggers and HTTP request tracking
  • Database Operations - Knex-based database operations with transaction support
  • Express Middleware - Common Express middleware for security, request parsing, and more
  • Error Handling - Standardized error handling with custom error classes
  • HTTP Responses - Standardized HTTP response formatting
  • Validation - Request validation with Zod schemas
  • Caching - Redis-based caching with memoization and batch loading
  • WebSockets - WebSocket server with room management and authentication
  • Event System - Pub/Sub event system for async communication
  • Testing Utilities - Mocks for testing database, HTTP, and WebSocket operations

Usage

Configuration Management

import { loadConfig, getConfigValue } from '@rhino-ai/orchard9-coretsx';

const appConfig = loadConfig({
  files: ['./config/default.yaml', `./config/${process.env.NODE_ENV}.yaml`],
  env: process.env
});

const httpLogs = getConfigValue(appConfig, 'http.logs');

Logging

import { logger, createChildLogger } from '@rhino-ai/orchard9-coretsx';

// Standard logging
logger.info({ userId, action: 'login' }, 'User logged in');
logger.error({ err, taskId }, 'Error performing task');

// Child loggers for per-request context
const log = createChildLogger({ requestId: req.id });
log.info('Handling user upload');

Database Operations

import { db, transaction } from '@rhino-ai/orchard9-coretsx';

// Simple query
const users = await db('users').where('active', true);

// In-transaction
await transaction(async (trx) => {
  await trx('orders').insert({ /* … */ });
});

Express Middleware

import {
  queryParser,
  bodySanitizer,
  responseFormatter,
  pagination,
  validateRequest,
  securityHeaders,
  cors
} from '@rhino-ai/orchard9-coretsx';

app.use(queryParser());
app.use(bodySanitizer());
app.use(validateRequest({ /* schemas… */ }));
app.use(responseFormatter());
app.use(securityHeaders());
app.use(cors({ origin: ['https://example.com'] }));

Error Handling

import {
  NotFoundError,
  ValidationError,
  createErrorHandler,
  errorHandler,
  logger
} from '@rhino-ai/orchard9-coretsx';

// Register routes first
app.use('/api/users', userRoutes);
app.use('/api/todos', todoRoutes);

// Then add error handling middleware
app.use((req, res, next) => next(new NotFoundError('Route not found')));
app.use(createErrorHandler({ logger }));

// Alternatively, when using createServer:
// 1. Set registerErrorHandler: false in createServer options
// 2. Register your routes
// 3. Manually add the error handler afterward
app.use(errorHandler);

HTTP Responses

import { 
  sendResponse, 
  sendErrorResponse, 
  sendPaginatedResponse 
} from '@rhino-ai/orchard9-coretsx';

// Success response
sendResponse(res, { 
  data: todo, 
  message: 'Todo retrieved successfully' 
});

// Error response
sendErrorResponse(res, 'Todo not found', 404);

// Paginated response
sendPaginatedResponse(res, todos, totalCount, { page, limit, offset });

Validation

import { validation, validateRequest } from '@rhino-ai/orchard9-coretsx';
import { z } from 'zod';

const todoSchema = z.object({
  title: z.string().min(1).max(100),
  description: z.string().max(500).optional(),
  completed: z.boolean().default(false)
});

app.post('/todos', 
  validateRequest({ body: todoSchema }),
  (req, res) => {
    // Handle validated request
  }
);

Caching

import { cache } from '@rhino-ai/orchard9-coretsx';

// Set a value
await cache.set('todo:123', { id: '123', title: 'Buy groceries' }, { ttl: 300 });

// Get a value
const todo = await cache.get('todo:123');

// Delete a value
await cache.del('todo:123');

WebSockets

import { websocket } from '@rhino-ai/orchard9-coretsx';

// Initialize
websocket.initialize(httpServer, { path: '/ws' });

// Handle connections
websocket.onConnection((socket, req, metadata) => {
  console.log(`Client connected: ${metadata.clientId}`);
  
  // Send welcome message
  websocket.send(metadata.clientId, 'welcome', { 
    message: 'Welcome!' 
  });
});

// Handle messages
websocket.onMessage('todo:create', (message, socket, metadata) => {
  // Process message
});

WebSocket Message Format

Messages sent to and from the WebSocket server follow a standard format:

interface WebSocketMessage<T = any> {
  // Message type: 'system', 'user', 'error', 'notification', or 'control'
  type: MessageType;
  
  // Event name that identifies the action or topic
  event: string;
  
  // Message payload containing the data
  payload: T;
  
  // Optional unique message ID for correlation
  id?: string;
  
  // Optional timestamp (defaults to Date.now())
  timestamp?: number;
  
  // Optional message version
  version?: string;
}
Message Types
  • system - System-level messages for connection management, heartbeats, and room operations
  • user - Regular application-specific messages (default if not specified)
  • error - Error notifications
  • notification - Notifications that don't require a response
  • control - Messages that control connection state or behavior
Event Names
  • For system messages: ping, room:join, room:leave, room:list
  • For custom messages: Use descriptive names in the format resource:action (e.g., user:update, message:send)
Example Client Message
// Send a chat message
socket.send(JSON.stringify({
  type: 'user',
  event: 'chat:message',
  payload: {
    text: 'Hello world!',
    roomId: 'room-123'
  }
}));

WebSocket Error Handling

The WebSocket server provides detailed error information when messages are invalid:

// Example error response for invalid JSON
{
  "type": "error",
  "event": "error",
  "payload": {
    "code": "invalid_json",
    "message": "Failed to parse JSON",
    "details": {
      "error": "Unexpected token { in JSON at position 12",
      "data": "{malformed{json"
    }
  },
  "id": "err-123",
  "timestamp": 1619712345678
}

Common error codes include:

  • invalid_data_type - Message data is not a string, Buffer, or ArrayBuffer
  • invalid_json - Message contains malformed JSON
  • missing_event - Message is missing the required "event" property
  • event_not_found - No handlers registered for the specified event
  • handler_error - Error occurred while processing the message

Event System

import { events } from '@rhino-ai/orchard9-coretsx';

// Publish an event
await events.publish('user:created', {
  id: 'user-123',
  name: 'John Doe'
});

// Subscribe to an event
events.subscribe('user:created', async (payload) => {
  console.log(`New user created: ${payload.name}`);
});

Testing Utilities

import {
  createMockDbClient,
  createMockHttpRequest,
  createMockResponse
} from '@rhino-ai/orchard9-coretsx';

// Setup test mocks
const dbMock = createMockDbClient([{ id: 1, name: 'Alice' }]);
const req = createMockHttpRequest({ method: 'GET', url: '/users' });
const res = createMockResponse();

// Test your handler
await listUsers(req, res, () => {});
expect(res.jsonSpy).toHaveBeenCalledWith([{ id: 1, name: 'Alice' }]);

Complete Server Setup

import { 
  createServer, 
  initializeDatabase,
  errorHandler,
  logger,
  config
} from '@rhino-ai/orchard9-coretsx';

import routes from './api/routes';

async function bootstrap() {
  try {
    // Initialize database
    initializeDatabase({
      client: 'pg',
      connection: {
        host: config.get('db.host', 'localhost'),
        port: config.get('db.port', 5432),
        user: config.get('db.user', 'postgres'),
        password: config.get('db.password', 'postgres'),
        database: config.get('db.database', 'app')
      }
    });
    
    // Create and start the server
    const { app, server } = await createServer({
      port: config.get('port', 8080),
      host: config.get('host', '0.0.0.0'),
      httpLogs: true,
      autoStart: true,
      registerErrorHandler: false, // Don't register error handler yet
      health: {
        path: '/health',
        includeConnections: true
      }
    });
    
    // Register API routes
    app.use('/api', routes);
    
    // Register error handler after all routes
    app.use(errorHandler);
    
    logger.info({ port: config.get('port', 8080) }, 'Service started');
    
    return { app, server };
  } catch (error) {
    logger.error({ error }, 'Failed to start service');
    process.exit(1);
  }
}

bootstrap();

Best Practices

Import Order

// 1. Built-ins (node, express, etc.)
import { Router } from 'express';

// 2. Third-party (e.g. lodash)
import { z } from 'zod';

// 3. Core library
import { 
  logger, 
  db, 
  sendResponse 
} from '@rhino-ai/orchard9-coretsx';

// 4. Local modules
import { todoService } from './services';

Naming Conventions

  • Classes / Types: PascalCase
  • Functions / Methods: camelCase
  • Variables: camelCase
  • Files: kebab-case.ts

License

MIT