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

@objectstack/runtime

v3.0.6

Published

ObjectStack Core Runtime & Query Engine

Readme

@objectstack/runtime

ObjectStack Standard System Library

Overview

The runtime package provides the Standard Library for the ObjectStack Operating System. It bridges the pure ObjectKernel (from @objectstack/core) with the Data Engine (@objectstack/objectql) and provides essential infrastructure adapters.

Architecture Highlights

  • Standard Library: Contains essential plugins (AppPlugin, DriverPlugin)
  • Core Integration: Re-exports ObjectKernel for convenience
  • Capability Contracts: Abstract interfaces for HTTP server and data persistence

🤖 AI Development Context

Role: Server Runtime & REST API Usage:

  • Use RestServer to spawn HTTP endpoints.
  • Configures the HTTP layer.

Key Concepts:

  • RestServer: Generates /api/* endpoints from ObjectQL schemas.

Installation

npm install @objectstack/runtime

Quick Start

Basic Setup (Recommended)

import { ObjectKernel } from '@objectstack/core';
import { DriverPlugin, AppPlugin } from '@objectstack/runtime';
import { ObjectQLPlugin } from '@objectstack/objectql';
import { InMemoryDriver } from '@objectstack/driver-memory';

const kernel = new ObjectKernel();

kernel
  // Register ObjectQL engine
  .use(new ObjectQLPlugin())
  
  // Add database driver
  .use(new DriverPlugin(new InMemoryDriver(), 'memory'))
  
  // Add your app configurations
  // .use(new AppPlugin(appConfig));

await kernel.bootstrap();

Custom ObjectQL Instance

If you have a separate ObjectQL implementation or need custom configuration:

import { ObjectKernel, DriverPlugin } from '@objectstack/runtime';
import { ObjectQLPlugin, ObjectQL } from '@objectstack/objectql';

// Create custom ObjectQL instance
const customQL = new ObjectQL({
  env: 'production',
  customConfig: true
});

// Pre-configure with custom hooks
customQL.registerHook('beforeInsert', async (ctx) => {
  console.log(`Inserting into ${ctx.object}`);
});

const kernel = new ObjectKernel();

kernel
  // Use your custom ObjectQL instance
  .use(new ObjectQLPlugin(customQL))
  
  // Add driver
  .use(new DriverPlugin(new InMemoryDriver(), 'memory'));

await kernel.bootstrap();

// Access ObjectQL via service registry
const objectql = kernel.getService('objectql');

Architecture

ObjectKernel (MiniKernel)

The kernel provides:

  • Plugin Lifecycle Management: init → start → destroy phases
  • Service Registry: Dependency injection container
  • Event/Hook System: Inter-plugin communication
  • Dependency Resolution: Topological sort for plugin dependencies

Built-in Plugins

ObjectQLPlugin

Registers the ObjectQL data engine as a service.

new ObjectQLPlugin()                           // Default instance
new ObjectQLPlugin(customQL)                   // Custom instance
new ObjectQLPlugin(undefined, { env: 'prod' }) // With context

Services: 'objectql'

DriverPlugin

Registers a data driver with ObjectQL.

new DriverPlugin(driver, 'driver-name')

Dependencies: ['com.objectstack.engine.objectql']

AppPlugin

Wraps ObjectStack app manifests (objectstack.config.ts) as plugins.

new AppPlugin(appConfig)

Services: 'app.{id}'

API Reference

Capability Contract Interfaces

IHttpServer

Abstract interface for HTTP server capabilities. Allows plugins to work with any HTTP framework (Express, Fastify, Hono, etc.) without tight coupling.

import { IHttpServer, IHttpRequest, IHttpResponse } from '@objectstack/runtime';

// In your HTTP server plugin
class MyHttpServerPlugin implements Plugin {
  name = 'http-server';
  
  async init(ctx: PluginContext) {
    const server: IHttpServer = createMyServer(); // Express, Hono, etc.
    ctx.registerService('http-server', server);
  }
}

// In your API plugin
class MyApiPlugin implements Plugin {
  name = 'api';
  dependencies = ['http-server'];
  
  async start(ctx: PluginContext) {
    const server = ctx.getService<IHttpServer>('http-server');
    
    // Register routes - works with any HTTP framework
    server.get('/api/users', async (req, res) => {
      res.json({ users: [] });
    });
  }
}

Interface Methods:

  • get(path, handler) - Register GET route
  • post(path, handler) - Register POST route
  • put(path, handler) - Register PUT route
  • delete(path, handler) - Register DELETE route
  • patch(path, handler) - Register PATCH route
  • use(path, handler?) - Register middleware
  • listen(port) - Start server
  • close() - Stop server (optional)

IDataEngine

Abstract interface for data persistence. Allows plugins to work with any data layer (ObjectQL, Prisma, TypeORM, etc.) without tight coupling.

import { IDataEngine } from '@objectstack/runtime';

// In your data plugin
class MyDataPlugin implements Plugin {
  name = 'data';
  
  async init(ctx: PluginContext) {
    const engine: IDataEngine = createMyDataEngine(); // ObjectQL, Prisma, etc.
    ctx.registerService('data-engine', engine);
  }
}

// In your business logic plugin
class MyBusinessPlugin implements Plugin {
  name = 'business';
  dependencies = ['data'];
  
  async start(ctx: PluginContext) {
    const engine = ctx.getService<IDataEngine>('data-engine');
    
    // CRUD operations - works with any data layer
    const user = await engine.insert('user', { name: 'John' });
    const users = await engine.find('user', { filter: { active: true } });
    await engine.update('user', user.id, { name: 'Jane' });
    await engine.delete('user', user.id);
  }
}

Interface Methods:

  • insert(objectName, data) - Create a record
  • find(objectName, query?) - Query records
  • update(objectName, id, data) - Update a record
  • delete(objectName, id) - Delete a record

ObjectKernel

Methods

  • use(plugin: Plugin): Register a plugin
  • bootstrap(): Initialize and start all plugins
  • shutdown(): Stop all plugins in reverse order
  • getService<T>(name: string): Get a service from registry
  • isRunning(): Check if kernel is running
  • getState(): Get current kernel state

Plugin Interface

interface Plugin {
  name: string;                              // Unique identifier
  version?: string;                          // Plugin version
  dependencies?: string[];                   // Required plugin names
  
  init(ctx: PluginContext): Promise<void>;   // Register services
  start?(ctx: PluginContext): Promise<void>; // Execute business logic
  destroy?(): Promise<void>;                  // Cleanup
}

PluginContext

interface PluginContext {
  registerService(name: string, service: any): void;
  getService<T>(name: string): T;
  hook(name: string, handler: Function): void;
  trigger(name: string, ...args: any[]): Promise<void>;
  logger: Console;
  getKernel?(): any;
}

Examples

See the examples/ directory for complete examples:

  • examples/host/ - Full server setup with Hono
  • examples/msw-react-crud/ - Browser-based setup with MSW
  • test-mini-kernel.ts - Comprehensive kernel test suite
  • `packages/runtime/src/

Benefits of MiniKernel

  1. True Modularity: Each plugin is independent and reusable
  2. Capability Contracts: Plugins depend on interfaces, not implementations
  3. Testability: Mock services easily in tests
  4. Flexibility: Load plugins conditionally, swap implementations
  5. Extensibility: Add new plugins without modifying kernel
  6. Clear Dependencies: Explicit dependency declarations
  7. Better Architecture: Separation of concerns with Dependency Inversion

Best Practices

  1. Keep plugins focused: One responsibility per plugin
  2. Use services: Share functionality via service registry
  3. Declare dependencies: Make plugin requirements explicit
  4. Use hooks: Decouple plugins with event system
  5. Handle errors: Implement proper error handling in lifecycle methods

Common Plugin Patterns

Service Provider Pattern

import { Plugin, PluginContext } from '@objectstack/core';

export class DatabasePlugin implements Plugin {
  name = 'database';
  private connection: any;
  
  async init(ctx: PluginContext) {
    // Initialize connection
    this.connection = await createConnection({
      host: 'localhost',
      database: 'myapp'
    });
    
    // Register as service
    ctx.registerService('database', this.connection);
    
    ctx.logger.info('Database connected');
  }
  
  async destroy() {
    // Cleanup
    await this.connection.close();
  }
}

Service Consumer Pattern

import { Plugin, PluginContext } from '@objectstack/core';

export class RepositoryPlugin implements Plugin {
  name = 'repository';
  dependencies = ['database']; // Ensure database loads first
  
  async init(ctx: PluginContext) {
    // Get dependency
    const db = ctx.getService('database');
    
    // Create and register repository
    const repo = new UserRepository(db);
    ctx.registerService('user-repository', repo);
  }
}

Event-Driven Pattern

import { Plugin, PluginContext } from '@objectstack/core';

// Publisher
export class DataPlugin implements Plugin {
  name = 'data';
  
  async init(ctx: PluginContext) {
    const service = {
      async create(entity: string, data: any) {
        const result = await db.insert(entity, data);
        
        // Trigger event
        await ctx.trigger('data:created', { entity, data: result });
        
        return result;
      }
    };
    
    ctx.registerService('data', service);
  }
}

// Subscriber
export class AuditPlugin implements Plugin {
  name = 'audit';
  dependencies = ['data'];
  
  async init(ctx: PluginContext) {
    // Listen to events
    ctx.hook('data:created', async ({ entity, data }) => {
      ctx.logger.info(`Audit: ${entity} created`, { id: data.id });
      
      await auditLog.write({
        action: 'create',
        entity,
        entityId: data.id,
        timestamp: new Date()
      });
    });
  }
}

Configuration Pattern

import { Plugin, PluginContext } from '@objectstack/core';
import { z } from 'zod';

const ConfigSchema = z.object({
  apiKey: z.string(),
  endpoint: z.string().url(),
  timeout: z.number().default(5000)
});

export class ApiPlugin implements Plugin {
  name = 'api-client';
  
  constructor(private config: z.infer<typeof ConfigSchema>) {
    // Validate config
    ConfigSchema.parse(config);
  }
  
  async init(ctx: PluginContext) {
    const client = new ApiClient({
      apiKey: this.config.apiKey,
      endpoint: this.config.endpoint,
      timeout: this.config.timeout
    });
    
    ctx.registerService('api-client', client);
  }
}

// Usage
kernel.use(new ApiPlugin({
  apiKey: process.env.API_KEY,
  endpoint: 'https://api.example.com',
  timeout: 10000
}));

Factory Pattern

import { Plugin, PluginContext } from '@objectstack/core';

export class ConnectionPoolPlugin implements Plugin {
  name = 'connection-pool';
  private pool: any;
  
  async init(ctx: PluginContext) {
    this.pool = {
      connections: new Map(),
      
      getConnection(name: string) {
        if (!this.connections.has(name)) {
          this.connections.set(name, createConnection(name));
        }
        return this.connections.get(name);
      },
      
      closeAll() {
        for (const conn of this.connections.values()) {
          conn.close();
        }
        this.connections.clear();
      }
    };
    
    ctx.registerService('connection-pool', this.pool);
  }
  
  async destroy() {
    // Use stored reference from init phase
    if (this.pool) {
      this.pool.closeAll();
    }
  }
}

Middleware Pattern

import { Plugin, PluginContext } from '@objectstack/core';

export class LoggingMiddleware implements Plugin {
  name = 'logging-middleware';
  dependencies = ['http-server'];
  
  async start(ctx: PluginContext) {
    const server = ctx.getService('http-server');
    
    // Register middleware
    server.use(async (req, res, next) => {
      const start = Date.now();
      
      ctx.logger.info('Request', {
        method: req.method,
        path: req.path
      });
      
      await next();
      
      const duration = Date.now() - start;
      ctx.logger.info('Response', {
        method: req.method,
        path: req.path,
        status: res.statusCode,
        duration
      });
    });
  }
}

Lazy Loading Pattern

import { Plugin, PluginContext } from '@objectstack/core';

export class HeavyServicePlugin implements Plugin {
  name = 'heavy-service';
  private instance: any = null;
  
  async init(ctx: PluginContext) {
    // Register factory instead of instance
    const factory = {
      async getInstance() {
        if (!this.instance) {
          ctx.logger.info('Lazy loading heavy service...');
          this.instance = await loadHeavyService();
        }
        return this.instance;
      }
    };
    
    ctx.registerService('heavy-service', factory);
  }
}

// Usage
const factory = kernel.getService('heavy-service');
const service = await factory.getInstance(); // Loaded only when needed

Health Check Pattern

import { Plugin, PluginContext } from '@objectstack/core';

export class HealthCheckPlugin implements Plugin {
  name = 'health-check';
  dependencies = ['http-server', 'database', 'cache'];
  
  async start(ctx: PluginContext) {
    const server = ctx.getService('http-server');
    
    server.get('/health', async (req, res) => {
      const checks = await Promise.all([
        this.checkDatabase(ctx),
        this.checkCache(ctx),
        this.checkDiskSpace()
      ]);
      
      const healthy = checks.every(c => c.healthy);
      
      res.status(healthy ? 200 : 503).json({
        status: healthy ? 'healthy' : 'unhealthy',
        checks
      });
    });
  }
  
  private async checkDatabase(ctx: PluginContext) {
    try {
      const db = ctx.getService('database');
      await db.ping();
      return { name: 'database', healthy: true };
    } catch (error) {
      return { name: 'database', healthy: false, error: error.message };
    }
  }
  
  private async checkCache(ctx: PluginContext) {
    try {
      const cache = ctx.getService('cache');
      await cache.ping();
      return { name: 'cache', healthy: true };
    } catch (error) {
      return { name: 'cache', healthy: false, error: error.message };
    }
  }
  
  private async checkDiskSpace() {
    // Implementation
    return { name: 'disk', healthy: true };
  }
}

Documentation

License

Apache-2.0