Skip to content

Commit 772d681

Browse files
committed
feat(runtime): add Runtime and ApiRegistryPlugin for enhanced API management
1 parent bc8e1ad commit 772d681

3 files changed

Lines changed: 137 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Plugin, PluginContext, IHttpServer } from '@objectstack/core';
2+
import { RestServer } from './rest-server.js';
3+
import { ObjectStackProtocol, RestServerConfig } from '@objectstack/spec/api';
4+
5+
export interface ApiRegistryConfig {
6+
serverServiceName?: string;
7+
protocolServiceName?: string;
8+
api?: RestServerConfig;
9+
}
10+
11+
/**
12+
* ApiRegistryPlugin
13+
*
14+
* Responsibilities:
15+
* 1. Consumes 'http.server' (or configured service)
16+
* 2. Consumes 'protocol' (ObjectStackProtocol)
17+
* 3. Instantiates RestServer to auto-generate routes
18+
*/
19+
export function createApiRegistryPlugin(config: ApiRegistryConfig = {}): Plugin {
20+
return {
21+
name: 'com.objectstack.runtime.api-registry',
22+
version: '1.0.0',
23+
24+
init: async (ctx: PluginContext) => {
25+
// No service registration, this is a consumer plugin
26+
},
27+
28+
start: async (ctx: PluginContext) => {
29+
const serverService = config.serverServiceName || 'http.server';
30+
const protocolService = config.protocolServiceName || 'protocol';
31+
32+
const server = ctx.getService<IHttpServer>(serverService);
33+
const protocol = ctx.getService<ObjectStackProtocol>(protocolService);
34+
35+
if (!server) {
36+
ctx.logger.warn(`ApiRegistryPlugin: HTTP Server service '${serverService}' not found. REST routes skipped.`);
37+
return;
38+
}
39+
40+
if (!protocol) {
41+
ctx.logger.warn(`ApiRegistryPlugin: Protocol service '${protocolService}' not found. REST routes skipped.`);
42+
return;
43+
}
44+
45+
ctx.logger.info('Hydrating REST API from Protocol...');
46+
47+
try {
48+
const restServer = new RestServer(server, protocol, config.api);
49+
restServer.registerRoutes();
50+
51+
ctx.logger.info('REST API successfully registered');
52+
} catch (err: any) {
53+
ctx.logger.error('Failed to register REST API routes', { error: err.message });
54+
throw err;
55+
}
56+
}
57+
};
58+
}

packages/runtime/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
// Export Kernels
22
export { ObjectKernel } from '@objectstack/core';
33

4+
// Export Runtime
5+
export { Runtime } from './runtime.js';
6+
export type { RuntimeConfig } from './runtime.js';
7+
48
// Export Plugins
59
export { DriverPlugin } from './driver-plugin.js';
610
export { AppPlugin } from './app-plugin.js';
11+
export { createApiRegistryPlugin } from './api-registry-plugin.js';
12+
export type { ApiRegistryConfig } from './api-registry-plugin.js';
713

814
// Export HTTP Server Components
915
export { HttpServer } from './http-server.js';

packages/runtime/src/runtime.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { ObjectKernel, Plugin, IHttpServer } from '@objectstack/core';
2+
import { HttpServer } from './http-server.js';
3+
import { createApiRegistryPlugin, ApiRegistryConfig } from './api-registry-plugin.js';
4+
5+
export interface RuntimeConfig {
6+
/**
7+
* Optional existing server instance (e.g. Hono, Express app)
8+
* If provided, Runtime will use it as the 'http.server' service.
9+
* If not provided, Runtime expects a server plugin (like HonoServerPlugin) to be registered manually.
10+
*/
11+
server?: IHttpServer;
12+
13+
/**
14+
* API Registry Configuration
15+
*/
16+
api?: ApiRegistryConfig;
17+
}
18+
19+
/**
20+
* ObjectStack Runtime
21+
*
22+
* High-level entry point for bootstrapping an ObjectStack application.
23+
* Wraps ObjectKernel and provides standard orchestration for:
24+
* - HTTP Server binding
25+
* - API Registry (REST Routes)
26+
* - Plugin Management
27+
*/
28+
export class Runtime {
29+
readonly kernel: ObjectKernel;
30+
31+
constructor(config: RuntimeConfig = {}) {
32+
this.kernel = new ObjectKernel();
33+
34+
// If external server provided, register it immediately
35+
if (config.server) {
36+
// If the provided server is not already an HttpServer wrapper, wrap it?
37+
// Since IHttpServer is the interface, we assume it complies.
38+
// But HttpServer class in runtime is an adapter.
39+
// If user passes raw Hono, it won't work unless they wrapped it.
40+
// We'll assume they pass a compliant IHttpServer.
41+
this.kernel.registerService('http.server', config.server);
42+
}
43+
44+
// Register API Registry by default
45+
// This plugin is passive (wait for services) so it's safe to add early.
46+
this.kernel.use(createApiRegistryPlugin(config.api));
47+
}
48+
49+
/**
50+
* Register a plugin
51+
*/
52+
use(plugin: Plugin) {
53+
this.kernel.use(plugin);
54+
return this;
55+
}
56+
57+
/**
58+
* Start the runtime
59+
* 1. Initializes all plugins (init phase)
60+
* 2. Starts all plugins (start phase)
61+
*/
62+
async start() {
63+
await this.kernel.bootstrap();
64+
return this;
65+
}
66+
67+
/**
68+
* Get the kernel instance
69+
*/
70+
getKernel() {
71+
return this.kernel;
72+
}
73+
}

0 commit comments

Comments
 (0)