@objectstack/runtime 0.6.1 → 0.7.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @objectstack/runtime
2
2
 
3
+ ## 0.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Patch release for maintenance and stability improvements
8
+ - Updated dependencies
9
+ - @objectstack/spec@0.7.1
10
+ - @objectstack/types@0.7.1
11
+ - @objectstack/core@0.7.1
12
+
3
13
  ## 0.6.1
4
14
 
5
15
  ### Patch Changes
@@ -19,34 +19,62 @@ export class AppPlugin {
19
19
  async init(ctx) {
20
20
  const sys = this.bundle.manifest || this.bundle;
21
21
  const appId = sys.id || sys.name;
22
- ctx.logger?.log(`[AppPlugin] Registering App Service: ${appId}`);
22
+ ctx.logger.info('Registering App Service', {
23
+ appId,
24
+ pluginName: this.name,
25
+ version: this.version
26
+ });
23
27
  // Register the app manifest as a service
24
28
  // ObjectQLPlugin will discover this and call ql.registerApp()
25
29
  const serviceName = `app.${appId}`;
26
- ctx.registerService(serviceName, this.bundle.manifest || this.bundle);
30
+ // Merge manifest with the bundle to ensure objects/apps are accessible at root
31
+ // This supports both Legacy Manifests and new Stack Definitions
32
+ const servicePayload = this.bundle.manifest
33
+ ? { ...this.bundle.manifest, ...this.bundle }
34
+ : this.bundle;
35
+ ctx.registerService(serviceName, servicePayload);
27
36
  }
28
37
  async start(ctx) {
38
+ const sys = this.bundle.manifest || this.bundle;
39
+ const appId = sys.id || sys.name;
29
40
  // Execute Runtime Step
30
41
  // Retrieve ObjectQL engine from services
31
42
  // We cast to any/ObjectQL because ctx.getService returns unknown
32
43
  const ql = ctx.getService('objectql');
33
44
  if (!ql) {
34
- ctx.logger?.warn(`[AppPlugin] ObjectQL engine service not found for app: ${this.name}`);
45
+ ctx.logger.warn('ObjectQL engine service not found', {
46
+ appName: this.name,
47
+ appId
48
+ });
35
49
  return;
36
50
  }
51
+ ctx.logger.debug('Retrieved ObjectQL engine service', { appId });
37
52
  const runtime = this.bundle.default || this.bundle;
38
53
  if (runtime && typeof runtime.onEnable === 'function') {
39
- ctx.logger?.log(`[AppPlugin] Executing runtime.onEnable for: ${this.name}`);
54
+ ctx.logger.info('Executing runtime.onEnable', {
55
+ appName: this.name,
56
+ appId
57
+ });
40
58
  // Construct the Host Context (mirroring old ObjectQL.use logic)
41
59
  const hostContext = {
42
60
  ...ctx,
43
61
  ql,
44
- logger: ctx.logger || console,
62
+ logger: ctx.logger,
45
63
  drivers: {
46
- register: (driver) => ql.registerDriver(driver)
64
+ register: (driver) => {
65
+ ctx.logger.debug('Registering driver via app runtime', {
66
+ driverName: driver.name,
67
+ appId
68
+ });
69
+ ql.registerDriver(driver);
70
+ }
47
71
  },
48
72
  };
49
73
  await runtime.onEnable(hostContext);
74
+ ctx.logger.debug('Runtime.onEnable completed', { appId });
75
+ }
76
+ else {
77
+ ctx.logger.debug('No runtime.onEnable function found', { appId });
50
78
  }
51
79
  }
52
80
  }
@@ -22,10 +22,14 @@ export class DriverPlugin {
22
22
  // Register driver as a service instead of directly to objectql
23
23
  const serviceName = `driver.${this.driver.name || 'unknown'}`;
24
24
  ctx.registerService(serviceName, this.driver);
25
- ctx.logger.log(`[DriverPlugin] Registered driver service: ${serviceName}`);
25
+ ctx.logger.info('Driver service registered', {
26
+ serviceName,
27
+ driverName: this.driver.name,
28
+ driverVersion: this.driver.version
29
+ });
26
30
  }
27
31
  async start(ctx) {
28
32
  // Drivers don't need start phase, initialization happens in init
29
- ctx.logger.log(`[DriverPlugin] Driver ready: ${this.driver.name || 'unknown'}`);
33
+ ctx.logger.debug('Driver plugin started', { driverName: this.driver.name || 'unknown' });
30
34
  }
31
35
  }
@@ -0,0 +1,84 @@
1
+ import { IHttpServer, RouteHandler, Middleware } from '@objectstack/core';
2
+ /**
3
+ * HttpServer - Unified HTTP Server Abstraction
4
+ *
5
+ * Provides a framework-agnostic HTTP server interface that wraps
6
+ * underlying server implementations (Hono, Express, Fastify, etc.)
7
+ *
8
+ * This class serves as an adapter between the IHttpServer interface
9
+ * and concrete server implementations, allowing plugins to register
10
+ * routes and middleware without depending on specific frameworks.
11
+ *
12
+ * Features:
13
+ * - Unified route registration API
14
+ * - Middleware management with ordering
15
+ * - Request/response lifecycle hooks
16
+ * - Framework-agnostic abstractions
17
+ */
18
+ export declare class HttpServer implements IHttpServer {
19
+ protected server: IHttpServer;
20
+ protected routes: Map<string, RouteHandler>;
21
+ protected middlewares: Middleware[];
22
+ /**
23
+ * Create an HTTP server wrapper
24
+ * @param server - The underlying server implementation (Hono, Express, etc.)
25
+ */
26
+ constructor(server: IHttpServer);
27
+ /**
28
+ * Register a GET route handler
29
+ * @param path - Route path (e.g., '/api/users/:id')
30
+ * @param handler - Route handler function
31
+ */
32
+ get(path: string, handler: RouteHandler): void;
33
+ /**
34
+ * Register a POST route handler
35
+ * @param path - Route path
36
+ * @param handler - Route handler function
37
+ */
38
+ post(path: string, handler: RouteHandler): void;
39
+ /**
40
+ * Register a PUT route handler
41
+ * @param path - Route path
42
+ * @param handler - Route handler function
43
+ */
44
+ put(path: string, handler: RouteHandler): void;
45
+ /**
46
+ * Register a DELETE route handler
47
+ * @param path - Route path
48
+ * @param handler - Route handler function
49
+ */
50
+ delete(path: string, handler: RouteHandler): void;
51
+ /**
52
+ * Register a PATCH route handler
53
+ * @param path - Route path
54
+ * @param handler - Route handler function
55
+ */
56
+ patch(path: string, handler: RouteHandler): void;
57
+ /**
58
+ * Register middleware
59
+ * @param path - Optional path to apply middleware to (if omitted, applies globally)
60
+ * @param handler - Middleware function
61
+ */
62
+ use(path: string | Middleware, handler?: Middleware): void;
63
+ /**
64
+ * Start the HTTP server
65
+ * @param port - Port number to listen on
66
+ * @returns Promise that resolves when server is ready
67
+ */
68
+ listen(port: number): Promise<void>;
69
+ /**
70
+ * Stop the HTTP server
71
+ * @returns Promise that resolves when server is stopped
72
+ */
73
+ close(): Promise<void>;
74
+ /**
75
+ * Get registered routes
76
+ * @returns Map of route keys to handlers
77
+ */
78
+ getRoutes(): Map<string, RouteHandler>;
79
+ /**
80
+ * Get registered middlewares
81
+ * @returns Array of middleware functions
82
+ */
83
+ getMiddlewares(): Middleware[];
84
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * HttpServer - Unified HTTP Server Abstraction
3
+ *
4
+ * Provides a framework-agnostic HTTP server interface that wraps
5
+ * underlying server implementations (Hono, Express, Fastify, etc.)
6
+ *
7
+ * This class serves as an adapter between the IHttpServer interface
8
+ * and concrete server implementations, allowing plugins to register
9
+ * routes and middleware without depending on specific frameworks.
10
+ *
11
+ * Features:
12
+ * - Unified route registration API
13
+ * - Middleware management with ordering
14
+ * - Request/response lifecycle hooks
15
+ * - Framework-agnostic abstractions
16
+ */
17
+ export class HttpServer {
18
+ /**
19
+ * Create an HTTP server wrapper
20
+ * @param server - The underlying server implementation (Hono, Express, etc.)
21
+ */
22
+ constructor(server) {
23
+ this.server = server;
24
+ this.routes = new Map();
25
+ this.middlewares = [];
26
+ }
27
+ /**
28
+ * Register a GET route handler
29
+ * @param path - Route path (e.g., '/api/users/:id')
30
+ * @param handler - Route handler function
31
+ */
32
+ get(path, handler) {
33
+ const key = `GET:${path}`;
34
+ this.routes.set(key, handler);
35
+ this.server.get(path, handler);
36
+ }
37
+ /**
38
+ * Register a POST route handler
39
+ * @param path - Route path
40
+ * @param handler - Route handler function
41
+ */
42
+ post(path, handler) {
43
+ const key = `POST:${path}`;
44
+ this.routes.set(key, handler);
45
+ this.server.post(path, handler);
46
+ }
47
+ /**
48
+ * Register a PUT route handler
49
+ * @param path - Route path
50
+ * @param handler - Route handler function
51
+ */
52
+ put(path, handler) {
53
+ const key = `PUT:${path}`;
54
+ this.routes.set(key, handler);
55
+ this.server.put(path, handler);
56
+ }
57
+ /**
58
+ * Register a DELETE route handler
59
+ * @param path - Route path
60
+ * @param handler - Route handler function
61
+ */
62
+ delete(path, handler) {
63
+ const key = `DELETE:${path}`;
64
+ this.routes.set(key, handler);
65
+ this.server.delete(path, handler);
66
+ }
67
+ /**
68
+ * Register a PATCH route handler
69
+ * @param path - Route path
70
+ * @param handler - Route handler function
71
+ */
72
+ patch(path, handler) {
73
+ const key = `PATCH:${path}`;
74
+ this.routes.set(key, handler);
75
+ this.server.patch(path, handler);
76
+ }
77
+ /**
78
+ * Register middleware
79
+ * @param path - Optional path to apply middleware to (if omitted, applies globally)
80
+ * @param handler - Middleware function
81
+ */
82
+ use(path, handler) {
83
+ if (typeof path === 'function') {
84
+ // Global middleware
85
+ this.middlewares.push(path);
86
+ this.server.use(path);
87
+ }
88
+ else if (handler) {
89
+ // Path-specific middleware
90
+ this.middlewares.push(handler);
91
+ this.server.use(path, handler);
92
+ }
93
+ }
94
+ /**
95
+ * Start the HTTP server
96
+ * @param port - Port number to listen on
97
+ * @returns Promise that resolves when server is ready
98
+ */
99
+ async listen(port) {
100
+ await this.server.listen(port);
101
+ }
102
+ /**
103
+ * Stop the HTTP server
104
+ * @returns Promise that resolves when server is stopped
105
+ */
106
+ async close() {
107
+ if (this.server.close) {
108
+ await this.server.close();
109
+ }
110
+ }
111
+ /**
112
+ * Get registered routes
113
+ * @returns Map of route keys to handlers
114
+ */
115
+ getRoutes() {
116
+ return new Map(this.routes);
117
+ }
118
+ /**
119
+ * Get registered middlewares
120
+ * @returns Array of middleware functions
121
+ */
122
+ getMiddlewares() {
123
+ return [...this.middlewares];
124
+ }
125
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  export { ObjectKernel } from '@objectstack/core';
2
- export { DriverPlugin } from './driver-plugin';
3
- export { AppPlugin } from './app-plugin';
2
+ export { DriverPlugin } from './driver-plugin.js';
3
+ export { AppPlugin } from './app-plugin.js';
4
+ export { HttpServer } from './http-server.js';
5
+ export { RestServer } from './rest-server.js';
6
+ export { RouteManager, RouteGroupBuilder } from './route-manager.js';
7
+ export { MiddlewareManager } from './middleware.js';
4
8
  export * from '@objectstack/core';
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  // Export Kernels
2
2
  export { ObjectKernel } from '@objectstack/core';
3
3
  // Export Plugins
4
- export { DriverPlugin } from './driver-plugin';
5
- export { AppPlugin } from './app-plugin';
4
+ export { DriverPlugin } from './driver-plugin.js';
5
+ export { AppPlugin } from './app-plugin.js';
6
+ // Export HTTP Server Components
7
+ export { HttpServer } from './http-server.js';
8
+ export { RestServer } from './rest-server.js';
9
+ export { RouteManager, RouteGroupBuilder } from './route-manager.js';
10
+ export { MiddlewareManager } from './middleware.js';
6
11
  // Export Types
7
12
  export * from '@objectstack/core';
@@ -0,0 +1,111 @@
1
+ import { Middleware } from '@objectstack/core';
2
+ import { MiddlewareConfig, MiddlewareType } from '@objectstack/spec/system';
3
+ /**
4
+ * Middleware Entry
5
+ * Internal representation of registered middleware
6
+ */
7
+ interface MiddlewareEntry {
8
+ name: string;
9
+ type: MiddlewareType;
10
+ middleware: Middleware;
11
+ order: number;
12
+ enabled: boolean;
13
+ paths?: {
14
+ include?: string[];
15
+ exclude?: string[];
16
+ };
17
+ }
18
+ /**
19
+ * MiddlewareManager
20
+ *
21
+ * Manages middleware registration, ordering, and execution.
22
+ * Provides fine-grained control over middleware chains with:
23
+ * - Execution order management
24
+ * - Path-based filtering
25
+ * - Enable/disable individual middleware
26
+ * - Middleware categorization by type
27
+ *
28
+ * @example
29
+ * const manager = new MiddlewareManager();
30
+ *
31
+ * // Register middleware with configuration
32
+ * manager.register({
33
+ * name: 'auth',
34
+ * type: 'authentication',
35
+ * order: 10,
36
+ * paths: { exclude: ['/health', '/metrics'] }
37
+ * }, authMiddleware);
38
+ *
39
+ * // Get sorted middleware chain
40
+ * const chain = manager.getMiddlewareChain();
41
+ * chain.forEach(mw => server.use(mw));
42
+ */
43
+ export declare class MiddlewareManager {
44
+ private middlewares;
45
+ constructor();
46
+ /**
47
+ * Register middleware with configuration
48
+ * @param config - Middleware configuration
49
+ * @param middleware - Middleware function
50
+ */
51
+ register(config: MiddlewareConfig, middleware: Middleware): void;
52
+ /**
53
+ * Unregister middleware by name
54
+ * @param name - Middleware name
55
+ */
56
+ unregister(name: string): void;
57
+ /**
58
+ * Enable middleware by name
59
+ * @param name - Middleware name
60
+ */
61
+ enable(name: string): void;
62
+ /**
63
+ * Disable middleware by name
64
+ * @param name - Middleware name
65
+ */
66
+ disable(name: string): void;
67
+ /**
68
+ * Get middleware entry by name
69
+ * @param name - Middleware name
70
+ */
71
+ get(name: string): MiddlewareEntry | undefined;
72
+ /**
73
+ * Get all middleware entries
74
+ */
75
+ getAll(): MiddlewareEntry[];
76
+ /**
77
+ * Get middleware by type
78
+ * @param type - Middleware type
79
+ */
80
+ getByType(type: MiddlewareType): MiddlewareEntry[];
81
+ /**
82
+ * Get middleware chain sorted by order
83
+ * Returns only enabled middleware
84
+ */
85
+ getMiddlewareChain(): Middleware[];
86
+ /**
87
+ * Get middleware chain with path filtering
88
+ * @param path - Request path to match against
89
+ */
90
+ getMiddlewareChainForPath(path: string): Middleware[];
91
+ /**
92
+ * Match path against pattern (simple glob matching)
93
+ * @param path - Request path
94
+ * @param pattern - Pattern to match (supports * wildcard)
95
+ */
96
+ private matchPath;
97
+ /**
98
+ * Clear all middleware
99
+ */
100
+ clear(): void;
101
+ /**
102
+ * Get middleware count
103
+ */
104
+ count(): number;
105
+ /**
106
+ * Create a composite middleware from the chain
107
+ * This can be used to apply all middleware at once
108
+ */
109
+ createCompositeMiddleware(): Middleware;
110
+ }
111
+ export {};
@@ -0,0 +1,176 @@
1
+ /**
2
+ * MiddlewareManager
3
+ *
4
+ * Manages middleware registration, ordering, and execution.
5
+ * Provides fine-grained control over middleware chains with:
6
+ * - Execution order management
7
+ * - Path-based filtering
8
+ * - Enable/disable individual middleware
9
+ * - Middleware categorization by type
10
+ *
11
+ * @example
12
+ * const manager = new MiddlewareManager();
13
+ *
14
+ * // Register middleware with configuration
15
+ * manager.register({
16
+ * name: 'auth',
17
+ * type: 'authentication',
18
+ * order: 10,
19
+ * paths: { exclude: ['/health', '/metrics'] }
20
+ * }, authMiddleware);
21
+ *
22
+ * // Get sorted middleware chain
23
+ * const chain = manager.getMiddlewareChain();
24
+ * chain.forEach(mw => server.use(mw));
25
+ */
26
+ export class MiddlewareManager {
27
+ constructor() {
28
+ this.middlewares = new Map();
29
+ }
30
+ /**
31
+ * Register middleware with configuration
32
+ * @param config - Middleware configuration
33
+ * @param middleware - Middleware function
34
+ */
35
+ register(config, middleware) {
36
+ const entry = {
37
+ name: config.name,
38
+ type: config.type,
39
+ middleware,
40
+ order: config.order ?? 100,
41
+ enabled: config.enabled ?? true,
42
+ paths: config.paths,
43
+ };
44
+ this.middlewares.set(config.name, entry);
45
+ }
46
+ /**
47
+ * Unregister middleware by name
48
+ * @param name - Middleware name
49
+ */
50
+ unregister(name) {
51
+ this.middlewares.delete(name);
52
+ }
53
+ /**
54
+ * Enable middleware by name
55
+ * @param name - Middleware name
56
+ */
57
+ enable(name) {
58
+ const entry = this.middlewares.get(name);
59
+ if (entry) {
60
+ entry.enabled = true;
61
+ }
62
+ }
63
+ /**
64
+ * Disable middleware by name
65
+ * @param name - Middleware name
66
+ */
67
+ disable(name) {
68
+ const entry = this.middlewares.get(name);
69
+ if (entry) {
70
+ entry.enabled = false;
71
+ }
72
+ }
73
+ /**
74
+ * Get middleware entry by name
75
+ * @param name - Middleware name
76
+ */
77
+ get(name) {
78
+ return this.middlewares.get(name);
79
+ }
80
+ /**
81
+ * Get all middleware entries
82
+ */
83
+ getAll() {
84
+ return Array.from(this.middlewares.values());
85
+ }
86
+ /**
87
+ * Get middleware by type
88
+ * @param type - Middleware type
89
+ */
90
+ getByType(type) {
91
+ return this.getAll().filter(entry => entry.type === type);
92
+ }
93
+ /**
94
+ * Get middleware chain sorted by order
95
+ * Returns only enabled middleware
96
+ */
97
+ getMiddlewareChain() {
98
+ return this.getAll()
99
+ .filter(entry => entry.enabled)
100
+ .sort((a, b) => a.order - b.order)
101
+ .map(entry => entry.middleware);
102
+ }
103
+ /**
104
+ * Get middleware chain with path filtering
105
+ * @param path - Request path to match against
106
+ */
107
+ getMiddlewareChainForPath(path) {
108
+ return this.getAll()
109
+ .filter(entry => {
110
+ if (!entry.enabled)
111
+ return false;
112
+ // Check path filters
113
+ if (entry.paths) {
114
+ // Check exclude patterns
115
+ if (entry.paths.exclude) {
116
+ const excluded = entry.paths.exclude.some(pattern => this.matchPath(path, pattern));
117
+ if (excluded)
118
+ return false;
119
+ }
120
+ // Check include patterns (if specified)
121
+ if (entry.paths.include) {
122
+ const included = entry.paths.include.some(pattern => this.matchPath(path, pattern));
123
+ if (!included)
124
+ return false;
125
+ }
126
+ }
127
+ return true;
128
+ })
129
+ .sort((a, b) => a.order - b.order)
130
+ .map(entry => entry.middleware);
131
+ }
132
+ /**
133
+ * Match path against pattern (simple glob matching)
134
+ * @param path - Request path
135
+ * @param pattern - Pattern to match (supports * wildcard)
136
+ */
137
+ matchPath(path, pattern) {
138
+ // Convert glob pattern to regex
139
+ const regexPattern = pattern
140
+ .replace(/\*/g, '.*')
141
+ .replace(/\?/g, '.');
142
+ const regex = new RegExp(`^${regexPattern}$`);
143
+ return regex.test(path);
144
+ }
145
+ /**
146
+ * Clear all middleware
147
+ */
148
+ clear() {
149
+ this.middlewares.clear();
150
+ }
151
+ /**
152
+ * Get middleware count
153
+ */
154
+ count() {
155
+ return this.middlewares.size;
156
+ }
157
+ /**
158
+ * Create a composite middleware from the chain
159
+ * This can be used to apply all middleware at once
160
+ */
161
+ createCompositeMiddleware() {
162
+ const chain = this.getMiddlewareChain();
163
+ return async (req, res, next) => {
164
+ let index = 0;
165
+ const executeNext = async () => {
166
+ if (index >= chain.length) {
167
+ await next();
168
+ return;
169
+ }
170
+ const middleware = chain[index++];
171
+ await middleware(req, res, executeNext);
172
+ };
173
+ await executeNext();
174
+ };
175
+ }
176
+ }