Skip to content

Commit 77d3ce7

Browse files
committed
feat(service-registry): introduce CoreServiceName enum and service registry protocol for enhanced service management
1 parent af6bc74 commit 77d3ce7

3 files changed

Lines changed: 91 additions & 11 deletions

File tree

packages/runtime/src/http-dispatcher.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ObjectKernel } from '@objectstack/core';
2+
import { CoreServiceName } from '@objectstack/spec/src/system/service-registry.zod';
23

34
export interface HttpProtocolContext {
45
request: any;
@@ -49,12 +50,12 @@ export class HttpDispatcher {
4950
getDiscoveryInfo(prefix: string) {
5051
const services = this.getServicesMap();
5152

52-
const hasGraphQL = !!(services['graphql'] || this.kernel.graphql);
53-
const hasSearch = !!services['search'];
54-
const hasWebSockets = !!services['realtime'];
55-
const hasFiles = !!(services['file-storage'] || services['storage']?.supportsFiles);
56-
const hasAnalytics = !!services['analytics'];
57-
const hasHub = !!services['hub'];
53+
const hasGraphQL = !!(services[CoreServiceName.enum.graphql] || this.kernel.graphql);
54+
const hasSearch = !!services[CoreServiceName.enum.search];
55+
const hasWebSockets = !!services[CoreServiceName.enum.realtime];
56+
const hasFiles = !!(services[CoreServiceName.enum['file-storage']] || services['storage']?.supportsFiles);
57+
const hasAnalytics = !!services[CoreServiceName.enum.analytics];
58+
const hasHub = !!services[CoreServiceName.enum.hub];
5859

5960
return {
6061
name: 'ObjectOS',
@@ -108,7 +109,7 @@ export class HttpDispatcher {
108109
*/
109110
async handleAuth(path: string, method: string, body: any, context: HttpProtocolContext): Promise<HttpDispatcherResult> {
110111
// 1. Try generic Auth Service
111-
const authService = this.getService('auth');
112+
const authService = this.getService(CoreServiceName.enum.auth);
112113
if (authService && typeof authService.handler === 'function') {
113114
const response = await authService.handler(context.request, context.response);
114115
return { handled: true, result: response };
@@ -267,7 +268,7 @@ export class HttpDispatcher {
267268
* path: sub-path after /analytics/
268269
*/
269270
async handleAnalytics(path: string, method: string, body: any, context: HttpProtocolContext): Promise<HttpDispatcherResult> {
270-
const analyticsService = this.getService('analytics');
271+
const analyticsService = this.getService(CoreServiceName.enum.analytics);
271272
if (!analyticsService) return { handled: false }; // 404 handled by caller if unhandled
272273

273274
const m = method.toUpperCase();
@@ -300,7 +301,7 @@ export class HttpDispatcher {
300301
* path: sub-path after /hub/
301302
*/
302303
async handleHub(path: string, method: string, body: any, query: any, context: HttpProtocolContext): Promise<HttpDispatcherResult> {
303-
const hubService = this.getService('hub');
304+
const hubService = this.getService(CoreServiceName.enum.hub);
304305
if (!hubService) return { handled: false };
305306

306307
const m = method.toUpperCase();
@@ -375,7 +376,7 @@ export class HttpDispatcher {
375376
* path: sub-path after /storage/
376377
*/
377378
async handleStorage(path: string, method: string, file: any, context: HttpProtocolContext): Promise<HttpDispatcherResult> {
378-
const storageService = this.getService('file-storage') || this.kernel.services?.['file-storage'];
379+
const storageService = this.getService(CoreServiceName.enum['file-storage']) || this.kernel.services?.['file-storage'];
379380
if (!storageService) {
380381
return { handled: true, response: this.error('File storage not configured', 501) };
381382
}
@@ -431,7 +432,7 @@ export class HttpDispatcher {
431432
return this.kernel.services || {};
432433
}
433434

434-
private getService(name: string) {
435+
private getService(name: CoreServiceName) {
435436
if (typeof this.kernel.getService === 'function') {
436437
return this.kernel.getService(name);
437438
}

packages/spec/src/system/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ export * from './collaboration.zod';
3838

3939
// Types
4040
export * from './types';
41+
export * from './service-registry.zod';
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { z } from 'zod';
2+
3+
/**
4+
* # Service Registry Protocol
5+
*
6+
* Defines the standard built-in services that constitute the ObjectStack Kernel.
7+
* This registry is used by the `ObjectKernel` and `HttpDispatcher` to:
8+
* 1. Verify service availability.
9+
* 2. Route requests to the correct service handler.
10+
* 3. Type-check service interactions.
11+
*/
12+
13+
// ==========================================
14+
// Service Identifiers
15+
// ==========================================
16+
17+
export const CoreServiceName = z.enum([
18+
// Core Data & Metadata
19+
'metadata', // Object/Field Definitions
20+
'data', // CRUD & Query Engine
21+
'auth', // Authentication & Identity
22+
23+
// Infrastructure
24+
'file-storage', // Storage Driver (Local/S3)
25+
'search', // Search Engine (Elastic/Meili)
26+
'cache', // Cache Driver (Redis/Memory)
27+
'queue', // Job Queue (BullMQ/Redis)
28+
29+
// Advanced Capabilities
30+
'automation', // Flow & Script Engine
31+
'graphql', // GraphQL API Engine
32+
'analytics', // BI & Semantic Layer
33+
'hub', // Multi-tenant & Marketplace Management
34+
'realtime', // WebSocket & PubSub
35+
'job', // Background Job Manager
36+
'notification', // Email/Push/SMS
37+
]);
38+
39+
export type CoreServiceName = z.infer<typeof CoreServiceName>;
40+
41+
// ==========================================
42+
// Service Capabilities
43+
// ==========================================
44+
45+
/**
46+
* Describes the availability and health of a service
47+
*/
48+
export const ServiceStatusSchema = z.object({
49+
name: CoreServiceName,
50+
enabled: z.boolean(),
51+
status: z.enum(['running', 'stopped', 'degraded', 'initializing']),
52+
version: z.string().optional(),
53+
provider: z.string().optional().describe('Implementation provider (e.g. "s3" for storage)'),
54+
features: z.array(z.string()).optional().describe('List of supported sub-features'),
55+
});
56+
57+
/**
58+
* The Contract definition for what the Kernel MUST expose
59+
* map<ServiceName, ServiceInstance>
60+
*/
61+
export const KernelServiceMapSchema = z.record(
62+
CoreServiceName,
63+
z.any().describe('Service Instance implementing the protocol interface')
64+
);
65+
66+
// ==========================================
67+
// Service Interfaces (Stub definitions)
68+
// ==========================================
69+
// Ideally, we would define strict Typescript interfaces here
70+
// for what methods each service must expose to the Registry.
71+
// For Zod, we primarily validate configuration and status.
72+
73+
// e.g.
74+
export const ServiceConfigSchema = z.object({
75+
id: z.string(),
76+
name: CoreServiceName,
77+
options: z.record(z.string(), z.any()).optional(),
78+
});

0 commit comments

Comments
 (0)