@objectstack/runtime 4.0.3 → 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,142 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { IHttpServer, RouteHandler, Middleware } from '@objectstack/core';
4
-
5
- /**
6
- * HttpServer - Unified HTTP Server Abstraction
7
- *
8
- * Provides a framework-agnostic HTTP server interface that wraps
9
- * underlying server implementations (Hono, Express, Fastify, etc.)
10
- *
11
- * This class serves as an adapter between the IHttpServer interface
12
- * and concrete server implementations, allowing plugins to register
13
- * routes and middleware without depending on specific frameworks.
14
- *
15
- * Features:
16
- * - Unified route registration API
17
- * - Middleware management with ordering
18
- * - Request/response lifecycle hooks
19
- * - Framework-agnostic abstractions
20
- */
21
- export class HttpServer implements IHttpServer {
22
- protected server: IHttpServer;
23
- protected routes: Map<string, RouteHandler>;
24
- protected middlewares: Middleware[];
25
-
26
- /**
27
- * Create an HTTP server wrapper
28
- * @param server - The underlying server implementation (Hono, Express, etc.)
29
- */
30
- constructor(server: IHttpServer) {
31
- this.server = server;
32
- this.routes = new Map();
33
- this.middlewares = [];
34
- }
35
-
36
- /**
37
- * Register a GET route handler
38
- * @param path - Route path (e.g., '/api/users/:id')
39
- * @param handler - Route handler function
40
- */
41
- get(path: string, handler: RouteHandler): void {
42
- const key = `GET:${path}`;
43
- this.routes.set(key, handler);
44
- this.server.get(path, handler);
45
- }
46
-
47
- /**
48
- * Register a POST route handler
49
- * @param path - Route path
50
- * @param handler - Route handler function
51
- */
52
- post(path: string, handler: RouteHandler): void {
53
- const key = `POST:${path}`;
54
- this.routes.set(key, handler);
55
- this.server.post(path, handler);
56
- }
57
-
58
- /**
59
- * Register a PUT route handler
60
- * @param path - Route path
61
- * @param handler - Route handler function
62
- */
63
- put(path: string, handler: RouteHandler): void {
64
- const key = `PUT:${path}`;
65
- this.routes.set(key, handler);
66
- this.server.put(path, handler);
67
- }
68
-
69
- /**
70
- * Register a DELETE route handler
71
- * @param path - Route path
72
- * @param handler - Route handler function
73
- */
74
- delete(path: string, handler: RouteHandler): void {
75
- const key = `DELETE:${path}`;
76
- this.routes.set(key, handler);
77
- this.server.delete(path, handler);
78
- }
79
-
80
- /**
81
- * Register a PATCH route handler
82
- * @param path - Route path
83
- * @param handler - Route handler function
84
- */
85
- patch(path: string, handler: RouteHandler): void {
86
- const key = `PATCH:${path}`;
87
- this.routes.set(key, handler);
88
- this.server.patch(path, handler);
89
- }
90
-
91
- /**
92
- * Register middleware
93
- * @param path - Optional path to apply middleware to (if omitted, applies globally)
94
- * @param handler - Middleware function
95
- */
96
- use(path: string | Middleware, handler?: Middleware): void {
97
- if (typeof path === 'function') {
98
- // Global middleware
99
- this.middlewares.push(path);
100
- this.server.use(path);
101
- } else if (handler) {
102
- // Path-specific middleware
103
- this.middlewares.push(handler);
104
- this.server.use(path, handler);
105
- }
106
- }
107
-
108
- /**
109
- * Start the HTTP server
110
- * @param port - Port number to listen on
111
- * @returns Promise that resolves when server is ready
112
- */
113
- async listen(port: number): Promise<void> {
114
- await this.server.listen(port);
115
- }
116
-
117
- /**
118
- * Stop the HTTP server
119
- * @returns Promise that resolves when server is stopped
120
- */
121
- async close(): Promise<void> {
122
- if (this.server.close) {
123
- await this.server.close();
124
- }
125
- }
126
-
127
- /**
128
- * Get registered routes
129
- * @returns Map of route keys to handlers
130
- */
131
- getRoutes(): Map<string, RouteHandler> {
132
- return new Map(this.routes);
133
- }
134
-
135
- /**
136
- * Get registered middlewares
137
- * @returns Array of middleware functions
138
- */
139
- getMiddlewares(): Middleware[] {
140
- return [...this.middlewares];
141
- }
142
- }
package/src/index.ts DELETED
@@ -1,39 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- // Export Kernels
4
- export { ObjectKernel } from '@objectstack/core';
5
-
6
- // Export Runtime
7
- export { Runtime } from './runtime.js';
8
- export type { RuntimeConfig } from './runtime.js';
9
-
10
- // Export Plugins
11
- export { DriverPlugin } from './driver-plugin.js';
12
- export { AppPlugin } from './app-plugin.js';
13
- export { SeedLoaderService } from './seed-loader.js';
14
- export { createDispatcherPlugin } from './dispatcher-plugin.js';
15
- export type { DispatcherPluginConfig } from './dispatcher-plugin.js';
16
-
17
- // Export HTTP Server Components
18
- export { HttpServer } from './http-server.js';
19
- export { HttpDispatcher } from './http-dispatcher.js';
20
- export type { HttpProtocolContext, HttpDispatcherResult } from './http-dispatcher.js';
21
- export { MiddlewareManager } from './middleware.js';
22
-
23
- // Re-export from @objectstack/rest
24
- export {
25
- RestServer,
26
- RouteManager,
27
- RouteGroupBuilder,
28
- createRestApiPlugin,
29
- } from '@objectstack/rest';
30
- export type {
31
- RouteEntry,
32
- RestApiPluginConfig,
33
- } from '@objectstack/rest';
34
-
35
- // Export Types
36
- export * from '@objectstack/core';
37
-
38
-
39
-
package/src/middleware.ts DELETED
@@ -1,222 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { Middleware, IHttpRequest, IHttpResponse } from '@objectstack/core';
4
- import { MiddlewareConfig, MiddlewareType } from '@objectstack/spec/system';
5
-
6
- /**
7
- * Middleware Entry
8
- * Internal representation of registered middleware
9
- */
10
- interface MiddlewareEntry {
11
- name: string;
12
- type: MiddlewareType;
13
- middleware: Middleware;
14
- order: number;
15
- enabled: boolean;
16
- paths?: {
17
- include?: string[];
18
- exclude?: string[];
19
- };
20
- }
21
-
22
- /**
23
- * MiddlewareManager
24
- *
25
- * Manages middleware registration, ordering, and execution.
26
- * Provides fine-grained control over middleware chains with:
27
- * - Execution order management
28
- * - Path-based filtering
29
- * - Enable/disable individual middleware
30
- * - Middleware categorization by type
31
- *
32
- * @example
33
- * const manager = new MiddlewareManager();
34
- *
35
- * // Register middleware with configuration
36
- * manager.register({
37
- * name: 'auth',
38
- * type: 'authentication',
39
- * order: 10,
40
- * paths: { exclude: ['/health', '/metrics'] }
41
- * }, authMiddleware);
42
- *
43
- * // Get sorted middleware chain
44
- * const chain = manager.getMiddlewareChain();
45
- * chain.forEach(mw => server.use(mw));
46
- */
47
- export class MiddlewareManager {
48
- private middlewares: Map<string, MiddlewareEntry>;
49
-
50
- constructor() {
51
- this.middlewares = new Map();
52
- }
53
-
54
- /**
55
- * Register middleware with configuration
56
- * @param config - Middleware configuration
57
- * @param middleware - Middleware function
58
- */
59
- register(config: MiddlewareConfig, middleware: Middleware): void {
60
- const entry: MiddlewareEntry = {
61
- name: config.name,
62
- type: config.type,
63
- middleware,
64
- order: config.order ?? 100,
65
- enabled: config.enabled ?? true,
66
- paths: config.paths,
67
- };
68
-
69
- this.middlewares.set(config.name, entry);
70
- }
71
-
72
- /**
73
- * Unregister middleware by name
74
- * @param name - Middleware name
75
- */
76
- unregister(name: string): void {
77
- this.middlewares.delete(name);
78
- }
79
-
80
- /**
81
- * Enable middleware by name
82
- * @param name - Middleware name
83
- */
84
- enable(name: string): void {
85
- const entry = this.middlewares.get(name);
86
- if (entry) {
87
- entry.enabled = true;
88
- }
89
- }
90
-
91
- /**
92
- * Disable middleware by name
93
- * @param name - Middleware name
94
- */
95
- disable(name: string): void {
96
- const entry = this.middlewares.get(name);
97
- if (entry) {
98
- entry.enabled = false;
99
- }
100
- }
101
-
102
- /**
103
- * Get middleware entry by name
104
- * @param name - Middleware name
105
- */
106
- get(name: string): MiddlewareEntry | undefined {
107
- return this.middlewares.get(name);
108
- }
109
-
110
- /**
111
- * Get all middleware entries
112
- */
113
- getAll(): MiddlewareEntry[] {
114
- return Array.from(this.middlewares.values());
115
- }
116
-
117
- /**
118
- * Get middleware by type
119
- * @param type - Middleware type
120
- */
121
- getByType(type: MiddlewareType): MiddlewareEntry[] {
122
- return this.getAll().filter(entry => entry.type === type);
123
- }
124
-
125
- /**
126
- * Get middleware chain sorted by order
127
- * Returns only enabled middleware
128
- */
129
- getMiddlewareChain(): Middleware[] {
130
- return this.getAll()
131
- .filter(entry => entry.enabled)
132
- .sort((a, b) => a.order - b.order)
133
- .map(entry => entry.middleware);
134
- }
135
-
136
- /**
137
- * Get middleware chain with path filtering
138
- * @param path - Request path to match against
139
- */
140
- getMiddlewareChainForPath(path: string): Middleware[] {
141
- return this.getAll()
142
- .filter(entry => {
143
- if (!entry.enabled) return false;
144
-
145
- // Check path filters
146
- if (entry.paths) {
147
- // Check exclude patterns
148
- if (entry.paths.exclude) {
149
- const excluded = entry.paths.exclude.some(pattern =>
150
- this.matchPath(path, pattern)
151
- );
152
- if (excluded) return false;
153
- }
154
-
155
- // Check include patterns (if specified)
156
- if (entry.paths.include) {
157
- const included = entry.paths.include.some(pattern =>
158
- this.matchPath(path, pattern)
159
- );
160
- if (!included) return false;
161
- }
162
- }
163
-
164
- return true;
165
- })
166
- .sort((a, b) => a.order - b.order)
167
- .map(entry => entry.middleware);
168
- }
169
-
170
- /**
171
- * Match path against pattern (simple glob matching)
172
- * @param path - Request path
173
- * @param pattern - Pattern to match (supports * wildcard)
174
- */
175
- private matchPath(path: string, pattern: string): boolean {
176
- // Convert glob pattern to regex
177
- const regexPattern = pattern
178
- .replace(/\*/g, '.*')
179
- .replace(/\?/g, '.');
180
-
181
- const regex = new RegExp(`^${regexPattern}$`);
182
- return regex.test(path);
183
- }
184
-
185
- /**
186
- * Clear all middleware
187
- */
188
- clear(): void {
189
- this.middlewares.clear();
190
- }
191
-
192
- /**
193
- * Get middleware count
194
- */
195
- count(): number {
196
- return this.middlewares.size;
197
- }
198
-
199
- /**
200
- * Create a composite middleware from the chain
201
- * This can be used to apply all middleware at once
202
- */
203
- createCompositeMiddleware(): Middleware {
204
- const chain = this.getMiddlewareChain();
205
-
206
- return async (req: IHttpRequest, res: IHttpResponse, next: () => void | Promise<void>) => {
207
- let index = 0;
208
-
209
- const executeNext = async (): Promise<void> => {
210
- if (index >= chain.length) {
211
- await next();
212
- return;
213
- }
214
-
215
- const middleware = chain[index++];
216
- await middleware(req, res, executeNext);
217
- };
218
-
219
- await executeNext();
220
- };
221
- }
222
- }
@@ -1,65 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { Runtime } from './runtime';
3
- import { IHttpServer, PluginContext } from '@objectstack/core';
4
-
5
- // Mock ObjectKernel to isolate Runtime logic
6
- vi.mock('@objectstack/core', async () => {
7
- const actual = await vi.importActual<any>('@objectstack/core');
8
- return {
9
- ...actual,
10
- ObjectKernel: class {
11
- use = vi.fn();
12
- registerService = vi.fn();
13
- bootstrap = vi.fn().mockResolvedValue(undefined);
14
- getServices = vi.fn().mockReturnValue(new Map());
15
- }
16
- };
17
- });
18
-
19
- describe('Runtime', () => {
20
- it('should initialize successfully', () => {
21
- const runtime = new Runtime();
22
- expect(runtime).toBeDefined();
23
- // Should create a kernel
24
- expect(runtime.getKernel()).toBeDefined();
25
- });
26
-
27
- it('should not auto-register any plugins', () => {
28
- const runtime = new Runtime();
29
- const kernel = runtime.getKernel();
30
- // Runtime is a clean slate — no plugins auto-registered
31
- expect(kernel.use).not.toHaveBeenCalled();
32
- });
33
-
34
- it('should register external http server if provided', () => {
35
- const mockServer: IHttpServer = {
36
- listen: vi.fn(),
37
- close: vi.fn(),
38
- get: vi.fn(),
39
- post: vi.fn(),
40
- put: vi.fn(),
41
- delete: vi.fn(),
42
- patch: vi.fn(),
43
- use: vi.fn(),
44
- };
45
-
46
- const runtime = new Runtime({ server: mockServer });
47
- const kernel = runtime.getKernel();
48
-
49
- expect(kernel.registerService).toHaveBeenCalledWith('http.server', mockServer);
50
- });
51
-
52
- it('should delegate use() to kernel', () => {
53
- const runtime = new Runtime();
54
- const mockPlugin = { name: 'test', init: vi.fn() };
55
-
56
- runtime.use(mockPlugin);
57
- expect(runtime.getKernel().use).toHaveBeenCalledWith(mockPlugin);
58
- });
59
-
60
- it('should delegate start() to kernel.bootstrap()', async () => {
61
- const runtime = new Runtime();
62
- await runtime.start();
63
- expect(runtime.getKernel().bootstrap).toHaveBeenCalled();
64
- });
65
- });
package/src/runtime.ts DELETED
@@ -1,69 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { ObjectKernel, Plugin, IHttpServer, ObjectKernelConfig } from '@objectstack/core';
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
- * Kernel Configuration
15
- */
16
- kernel?: ObjectKernelConfig;
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
- * - Plugin Management
26
- *
27
- * REST API is opt-in — register it explicitly:
28
- * ```ts
29
- * import { createRestApiPlugin } from '@objectstack/rest';
30
- * runtime.use(createRestApiPlugin());
31
- * ```
32
- */
33
- export class Runtime {
34
- readonly kernel: ObjectKernel;
35
-
36
- constructor(config: RuntimeConfig = {}) {
37
- this.kernel = new ObjectKernel(config.kernel);
38
-
39
- // If external server provided, register it immediately
40
- if (config.server) {
41
- this.kernel.registerService('http.server', config.server);
42
- }
43
- }
44
-
45
- /**
46
- * Register a plugin
47
- */
48
- use(plugin: Plugin) {
49
- this.kernel.use(plugin);
50
- return this;
51
- }
52
-
53
- /**
54
- * Start the runtime
55
- * 1. Initializes all plugins (init phase)
56
- * 2. Starts all plugins (start phase)
57
- */
58
- async start() {
59
- await this.kernel.bootstrap();
60
- return this;
61
- }
62
-
63
- /**
64
- * Get the kernel instance
65
- */
66
- getKernel() {
67
- return this.kernel;
68
- }
69
- }