Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions packages/foundation/types/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,77 @@
*/

import { IObjectQL } from './app';
import { UnifiedQuery } from './query';

export interface ObjectQLPlugin {
name: string;
setup(app: IObjectQL): void | Promise<void>;
}

/**
* Plugin metadata for dependency management and lifecycle
*/
export interface PluginMetadata {
/** Unique plugin name */
name: string;
/** Plugin version (semver format) */
version?: string;
/** Plugin type classification */
type?: 'driver' | 'repository' | 'query_processor' | 'extension';
/** Plugin dependencies (plugin names that must be loaded first) */
dependencies?: string[];
}

/**
* Base plugin interface with lifecycle and dependency support
*/
export interface BasePlugin {
/** Plugin metadata */
readonly metadata: PluginMetadata;

/** Setup hook called during plugin initialization */
setup?(runtime: any): void | Promise<void>;

/** Teardown hook called during plugin shutdown */
teardown?(): void | Promise<void>;
}

/**
* Context provided to query processor plugins
*/
export interface QueryProcessorContext {
/** The object being queried */
objectName: string;
/** Current user/session context */
user?: {
id: string | number;
[key: string]: any;
};
/** Additional runtime context */
[key: string]: any;
}

/**
* Plugin interface for query processing pipeline
*/
export interface QueryProcessorPlugin extends BasePlugin {
metadata: PluginMetadata & { type: 'query_processor' };

/**
* Validate query before execution
* Can throw errors to reject the query
*/
validateQuery?(query: UnifiedQuery, context: QueryProcessorContext): void | Promise<void>;

/**
* Transform query before execution
* Returns modified query (async waterfall pattern)
*/
beforeQuery?(query: UnifiedQuery, context: QueryProcessorContext): UnifiedQuery | Promise<UnifiedQuery>;

/**
* Process results after query execution
* Returns modified results (async waterfall pattern)
*/
afterQuery?(results: any[], context: QueryProcessorContext): any[] | Promise<any[]>;
}
185 changes: 185 additions & 0 deletions packages/runtime/core/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Runtime Core Architecture

## Overview

The `@objectql/runtime-core` package implements the core plugin system and query pipeline for ObjectQL, following the principle of **Protocol/Spec vs Runtime/Implementation** separation.

## Architecture Principles

### 1. Protocol Layer (from `@objectql/types`)
- **BasePlugin**: Interface defining plugin structure with metadata and lifecycle
- **QueryProcessorPlugin**: Interface for plugins that process queries
- **PluginMetadata**: Standardized plugin information including dependencies

### 2. Runtime Layer (this package)
- **PluginManager**: Implements dependency resolution and lifecycle management
- **QueryPipeline**: Implements async series waterfall query processing
- **Runtime**: Orchestrates plugins and provides query execution

## Key Components

### PluginManager

**Responsibilities:**
- Register plugins
- Resolve dependencies using topological sort
- Boot plugins in dependency order
- Manage plugin lifecycle (setup/teardown)

**Algorithm: Topological Sort**
```typescript
// Ensures dependencies are initialized before dependents
// Detects circular dependencies
// Throws errors for missing dependencies
```

**Example:**
```typescript
const manager = new PluginManager();
manager.register(pluginA); // No dependencies
manager.register(pluginB); // Depends on A
manager.register(pluginC); // Depends on B

await manager.boot(runtime);
// Execution order: A → B → C
```

### QueryPipeline

**Responsibilities:**
- Execute queries through registered processors
- Implement async series waterfall pattern
- Validate queries before execution
- Transform queries and results through plugin chain

**Execution Flow:**
```
1. validateQuery (all plugins)
2. beforeQuery (waterfall: plugin1 → plugin2 → ...)
3. execute (driver)
4. afterQuery (waterfall: plugin1 → plugin2 → ...)
5. return results
```

**Waterfall Pattern:**
- Each plugin receives output from previous plugin
- Plugins can transform queries/results
- Final output is returned to caller

**Example:**
```typescript
// Plugin 1 adds field
beforeQuery(query) {
return { ...query, fields: ['id', 'name'] };
}

// Plugin 2 adds filter (receives plugin 1's output)
beforeQuery(query) {
return { ...query, filters: [['active', '=', true]] };
}

// Final query: { fields: ['id', 'name'], filters: [['active', '=', true]] }
```

### Runtime

**Responsibilities:**
- Provide factory function `createRuntime()`
- Manage plugin manager and query pipeline
- Expose simple API for query execution
- Handle initialization and shutdown

**API:**
```typescript
interface Runtime {
pluginManager: PluginManager;
init(): Promise<void>;
query(object, query, context): Promise<any[]>;
shutdown(): Promise<void>;
setQueryExecutor(executor): void;
}
```

## Usage Pattern

```typescript
// 1. Define plugins
const myPlugin: BasePlugin = {
metadata: {
name: 'my-plugin',
dependencies: ['base-plugin']
},
async setup(runtime) {
// Initialize plugin
}
};

// 2. Create runtime
const runtime = createRuntime({
plugins: [myPlugin]
});

// 3. Set executor
runtime.setQueryExecutor(async (object, query) => {
// Execute query against database
});

// 4. Initialize
await runtime.init();

// 5. Execute queries
const results = await runtime.query('project', {
filters: [['status', '=', 'active']]
});

// 6. Shutdown
await runtime.shutdown();
```

## Design Decisions

### 1. Separation of Concerns
- **Types** define interfaces (what)
- **Runtime** implements logic (how)
- No circular dependencies between packages

### 2. Topological Sort for Dependencies
- Ensures correct initialization order
- Detects circular dependencies early
- Provides clear error messages

### 3. Async Series Waterfall
- Allows plugins to transform data sequentially
- Each plugin sees previous plugin's changes
- Enables powerful composition patterns

### 4. Error Handling
- Custom error types (PluginError, PipelineError)
- Include plugin name in errors for debugging
- Graceful shutdown even if teardown fails

## Testing

The package includes 39 tests covering:
- Plugin registration and lifecycle
- Dependency resolution (simple, complex, diamond, circular)
- Query pipeline execution (validation, waterfall, errors)
- Integration scenarios

Run tests:
```bash
pnpm test
```

## Future Enhancements

Potential improvements:
1. Plugin versioning and compatibility checking
2. Hot plugin reload
3. Plugin communication via events
4. Performance monitoring hooks
5. Plugin sandboxing for security
Loading
Loading