Skip to content

Latest commit

 

History

History
419 lines (317 loc) · 11.5 KB

File metadata and controls

419 lines (317 loc) · 11.5 KB

@objectstack/runtime Implementation Summary

Overview

This document summarizes the implementation of @objectstack/runtime, the micro-kernel architecture for ObjectStack that enables plugin-based extensibility.

Implementation Goals

✅ Implement plugin loading and service startup using @objectstack/runtime
✅ Follow the micro-kernel architecture pattern from https://protocol.objectstack.ai/docs/developers/micro-kernel
✅ Enable dependency injection and event-driven communication
✅ Provide a clean separation between core functionality and business logic

Architecture

Micro-Kernel Pattern

The implementation follows the micro-kernel architectural pattern where:

  1. Core Kernel (ObjectKernel) provides minimal essential services
  2. Plugins implement all business functionality
  3. Service Registry enables dependency injection
  4. Event Bus enables inter-plugin communication
  5. Dependency Resolver ensures correct initialization order
┌─────────────────────────────────────────────────────────┐
│               ObjectKernel (Core Runtime)                │
│  • Plugin Lifecycle Manager                             │
│  • Service Registry (DI Container)                      │
│  • Event Bus (Hook System)                              │
│  • Dependency Resolver                                  │
└──────────────┬──────────────────────────────────────────┘
               │
       ┌───────┴────────┬────────────┬──────────┐
       │                │            │          │
  ┌────▼─────┐   ┌─────▼─────┐  ┌──▼───┐  ┌───▼────┐
  │ ObjectQL │   │  Driver   │  │Server│  │ Custom │
  │  Plugin  │   │  Plugin   │  │Plugin│  │ Plugin │
  └──────────┘   └───────────┘  └──────┘  └────────┘

Components Implemented

1. Core Types (src/types.ts)

Defines the fundamental interfaces:

  • Plugin: Interface all plugins must implement

    • name: Unique plugin identifier
    • version?: Optional version
    • dependencies?: Array of plugin dependencies
    • init?(): Registration phase
    • start?(): Startup phase
    • destroy?(): Cleanup phase
  • PluginContext: Provides access to kernel services

    • Service registry methods
    • Event bus methods
    • Logger instance
  • Logger: Logging interface

2. ObjectKernel (src/kernel.ts)

The core kernel implementation:

Key Features:

  • Plugin registration via use() method (chainable)
  • Dependency resolution using topological sort
  • Circular dependency detection
  • Lifecycle management (init → start → destroy)
  • Service registry access
  • Event system integration

Public API:

class ObjectKernel {
  use(plugin: Plugin): this;
  bootstrap(): Promise<void>;
  shutdown(): Promise<void>;
  getService<T>(name: string): T;
  hasService(name: string): boolean;
}

3. Plugin Context (src/plugin-context.ts)

Implementation of the plugin context:

Features:

  • Service registration and retrieval
  • Event hook registration
  • Event triggering
  • Logger access

Public API:

class PluginContextImpl implements PluginContext {
  registerService(name: string, service: any): void;
  getService<T>(name: string): T;
  hasService(name: string): boolean;
  hook(name: string, handler: Function): void;
  trigger(name: string, ...args: any[]): Promise<void>;
}

4. Built-in Plugins

ObjectQLPlugin (src/plugins/objectql-plugin.ts)

Registers ObjectQL as a service in the kernel.

Features:

  • Accepts optional custom ObjectQL instance
  • Initializes ObjectQL on start
  • Registers service as 'objectql'

Usage:

kernel.use(new ObjectQLPlugin());
// or
kernel.use(new ObjectQLPlugin(customQL));

DriverPlugin (src/plugins/driver-plugin.ts)

Registers database drivers with ObjectQL.

Features:

  • Depends on ObjectQLPlugin
  • Auto-connects driver on start
  • Auto-disconnects on destroy
  • Configurable driver name

Usage:

kernel.use(new DriverPlugin(driver, 'postgres'));

5. Logger (src/logger.ts)

Simple console-based logger implementation.

Features:

  • Debug, Info, Warn, Error levels
  • Prefix support for component identification
  • Timestamp formatting

Plugin Lifecycle

Phases

  1. Registration (kernel.use())

    kernel.use(new ObjectQLPlugin())
          .use(new DriverPlugin(driver, 'default'));
  2. Initialization (kernel.bootstrap())

    • Resolves dependencies (topological sort)
    • Calls init() on each plugin in order
    • Plugins register services and subscribe to events
  3. Start

    • Calls start() on each plugin
    • Plugins connect to databases, start servers, etc.
  4. Running

    • Application is operational
    • Services accessible via kernel.getService()
  5. Shutdown (kernel.shutdown())

    • Calls destroy() in reverse order
    • Cleanup and resource release

Example Flow

// 1. Create kernel
const kernel = new ObjectKernel();

// 2. Register plugins
kernel.use(new ObjectQLPlugin())
      .use(new DriverPlugin(driver, 'default'));

// 3. Bootstrap (init + start all plugins)
await kernel.bootstrap();

// 4. Use services
const ql = kernel.getService('objectql');

// 5. Shutdown
await kernel.shutdown();

Integration with ObjectOS Server

The server integration was updated to use the runtime:

Before (Direct instantiation):

const objectos = new ObjectOS({
  datasources: { default: driver },
  presets: ['@objectos/preset-base']
});
await objectos.init();

After (Using ObjectKernel):

const kernel = new ObjectKernel();
const objectos = new ObjectOS({ presets });

kernel.use(new ObjectQLPlugin(objectos))
      .use(new DriverPlugin(driver, 'default'));

await kernel.bootstrap();
const ql = kernel.getService('objectql');

Testing

Test Coverage

  • 19 tests covering all functionality
  • 2 test suites (kernel.test.ts, plugins.test.ts)
  • 100% pass rate

Test Categories

  1. Plugin Registration

    • Register plugins
    • Chain registration
    • Duplicate prevention
  2. Plugin Lifecycle

    • Init/start/destroy hooks
    • Hook execution order
  3. Service Registry

    • Register and retrieve services
    • Service not found errors
  4. Event System

    • Hook registration
    • Event triggering
    • Multiple handlers
  5. Dependency Resolution

    • Correct initialization order
    • Circular dependency detection
    • Missing dependency errors
  6. Built-in Plugins

    • ObjectQLPlugin service registration
    • DriverPlugin dependency handling
    • Driver connect/disconnect

Benefits of This Architecture

1. Modularity

  • Clear separation of concerns
  • Easy to add/remove features
  • Independent plugin development

2. Testability

  • Mock services easily
  • Test plugins in isolation
  • Integration testing with real kernel

3. Extensibility

  • Add new plugins without modifying core
  • Plugin marketplace potential
  • Third-party plugin support

4. Dependency Management

  • Automatic ordering
  • Clear dependency declarations
  • Circular dependency prevention

5. Type Safety

  • Full TypeScript support
  • Compile-time error detection
  • IDE autocomplete

Usage Examples

Basic Usage

import { ObjectKernel, ObjectQLPlugin, DriverPlugin } from '@objectstack/runtime';
import { KnexDriver } from '@objectql/driver-sql';

const kernel = new ObjectKernel();
const driver = new KnexDriver({
  client: 'sqlite3',
  connection: { filename: ':memory:' },
  useNullAsDefault: true
});

kernel.use(new ObjectQLPlugin())
      .use(new DriverPlugin(driver, 'default'));

await kernel.bootstrap();

const ql = kernel.getService('objectql');
// Use ObjectQL...

await kernel.shutdown();

Custom Plugin

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

class MyPlugin implements Plugin {
  name = 'com.mycompany.my-plugin';
  dependencies = ['com.objectstack.engine.objectql'];
  
  async init(ctx: PluginContext): Promise<void> {
    // Register services
    ctx.registerService('my-service', { /* ... */ });
    
    // Subscribe to events
    ctx.hook('kernel:ready', async () => {
      ctx.logger.info('Ready!');
    });
  }
  
  async start(ctx: PluginContext): Promise<void> {
    const ql = ctx.getService('objectql');
    // Use other services...
  }
}

kernel.use(new MyPlugin());

Documentation

  1. README.md - Package overview and quick start
  2. USAGE_EXAMPLE.md - Detailed examples and patterns
  3. Main README - Updated with architecture diagram

Compliance with Protocol

The implementation fully complies with the micro-kernel architecture specification at: https://protocol.objectstack.ai/docs/developers/micro-kernel

Implemented Features

✅ ObjectKernel with plugin lifecycle manager
✅ Service Registry (DI Container)
✅ Event Bus (Hook System)
✅ Dependency Resolver
✅ Plugin Interface (name, version, dependencies, init, start, destroy)
✅ PluginContext (registerService, getService, hook, trigger, logger)
✅ ObjectQLPlugin
✅ DriverPlugin
✅ Standard events (kernel:init, kernel:ready, kernel:shutdown)

Files Created

packages/runtime/
├── src/
│   ├── index.ts                    # Main exports
│   ├── types.ts                    # Core type definitions
│   ├── kernel.ts                   # ObjectKernel implementation
│   ├── plugin-context.ts           # PluginContext implementation
│   ├── logger.ts                   # Logger implementation
│   └── plugins/
│       ├── objectql-plugin.ts      # ObjectQL plugin
│       └── driver-plugin.ts        # Driver plugin
├── test/
│   ├── kernel.test.ts              # Kernel tests
│   └── plugins.test.ts             # Plugin tests
├── package.json                    # Package configuration
├── tsconfig.json                   # TypeScript configuration
├── jest.config.js                  # Jest configuration
├── README.md                       # Package documentation
└── USAGE_EXAMPLE.md                # Usage examples

Next Steps (Future Enhancements)

  1. Additional Plugins

    • HonoServerPlugin for HTTP server
    • WorkflowPlugin for business processes
    • CachePlugin for caching layer
  2. Plugin Configuration

    • Load plugins from config files
    • Dynamic plugin discovery
    • Plugin hot-reload in development
  3. Advanced Features

    • Plugin versioning and compatibility
    • Plugin marketplace
    • Plugin sandboxing
  4. Performance

    • Lazy plugin loading
    • Parallel plugin initialization
    • Resource pooling

Conclusion

The @objectstack/runtime implementation provides a solid foundation for building extensible, modular applications following the micro-kernel architecture pattern. It enables:

  • Clean separation of concerns
  • Easy testing and maintainability
  • Flexible extensibility through plugins
  • Type-safe development
  • Protocol compliance

All functionality has been tested and verified to work correctly with the existing ObjectOS server infrastructure.