@morojs/moro 1.3.0 → 1.5.0

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.
Files changed (86) hide show
  1. package/README.md +61 -7
  2. package/dist/core/config/index.d.ts +0 -1
  3. package/dist/core/config/index.js +0 -4
  4. package/dist/core/config/index.js.map +1 -1
  5. package/dist/core/config/loader.js +219 -226
  6. package/dist/core/config/loader.js.map +1 -1
  7. package/dist/core/config/schema.d.ts +30 -335
  8. package/dist/core/config/schema.js +133 -224
  9. package/dist/core/config/schema.js.map +1 -1
  10. package/dist/core/config/utils.d.ts +3 -2
  11. package/dist/core/config/utils.js.map +1 -1
  12. package/dist/core/config/validation.d.ts +17 -0
  13. package/dist/core/config/validation.js +129 -0
  14. package/dist/core/config/validation.js.map +1 -0
  15. package/dist/core/docs/index.js +1 -1
  16. package/dist/core/docs/index.js.map +1 -1
  17. package/dist/core/docs/openapi-generator.js +6 -6
  18. package/dist/core/docs/openapi-generator.js.map +1 -1
  19. package/dist/core/docs/schema-to-openapi.d.ts +7 -0
  20. package/dist/core/docs/schema-to-openapi.js +124 -0
  21. package/dist/core/docs/schema-to-openapi.js.map +1 -0
  22. package/dist/core/docs/simple-docs.js +5 -5
  23. package/dist/core/docs/zod-to-openapi.d.ts +4 -3
  24. package/dist/core/docs/zod-to-openapi.js +28 -0
  25. package/dist/core/docs/zod-to-openapi.js.map +1 -1
  26. package/dist/core/framework.d.ts +29 -6
  27. package/dist/core/framework.js +117 -18
  28. package/dist/core/framework.js.map +1 -1
  29. package/dist/core/networking/adapters/index.d.ts +3 -0
  30. package/dist/core/networking/adapters/index.js +10 -0
  31. package/dist/core/networking/adapters/index.js.map +1 -0
  32. package/dist/core/networking/adapters/socketio-adapter.d.ts +16 -0
  33. package/dist/core/networking/adapters/socketio-adapter.js +244 -0
  34. package/dist/core/networking/adapters/socketio-adapter.js.map +1 -0
  35. package/dist/core/networking/adapters/ws-adapter.d.ts +54 -0
  36. package/dist/core/networking/adapters/ws-adapter.js +383 -0
  37. package/dist/core/networking/adapters/ws-adapter.js.map +1 -0
  38. package/dist/core/networking/websocket-adapter.d.ts +171 -0
  39. package/dist/core/networking/websocket-adapter.js +5 -0
  40. package/dist/core/networking/websocket-adapter.js.map +1 -0
  41. package/dist/core/networking/websocket-manager.d.ts +53 -17
  42. package/dist/core/networking/websocket-manager.js +184 -126
  43. package/dist/core/networking/websocket-manager.js.map +1 -1
  44. package/dist/core/routing/index.d.ts +13 -13
  45. package/dist/core/routing/index.js.map +1 -1
  46. package/dist/core/validation/adapters.d.ts +51 -0
  47. package/dist/core/validation/adapters.js +135 -0
  48. package/dist/core/validation/adapters.js.map +1 -0
  49. package/dist/core/validation/index.d.ts +12 -11
  50. package/dist/core/validation/index.js +32 -26
  51. package/dist/core/validation/index.js.map +1 -1
  52. package/dist/core/validation/schema-interface.d.ts +36 -0
  53. package/dist/core/validation/schema-interface.js +68 -0
  54. package/dist/core/validation/schema-interface.js.map +1 -0
  55. package/dist/index.d.ts +9 -2
  56. package/dist/index.js +23 -4
  57. package/dist/index.js.map +1 -1
  58. package/dist/moro.js +24 -17
  59. package/dist/moro.js.map +1 -1
  60. package/dist/types/config.d.ts +146 -0
  61. package/dist/types/config.js +4 -0
  62. package/dist/types/config.js.map +1 -0
  63. package/package.json +30 -7
  64. package/src/core/config/index.ts +0 -3
  65. package/src/core/config/loader.ts +571 -247
  66. package/src/core/config/schema.ts +146 -279
  67. package/src/core/config/utils.ts +1 -2
  68. package/src/core/config/validation.ts +140 -0
  69. package/src/core/docs/index.ts +1 -1
  70. package/src/core/docs/openapi-generator.ts +7 -6
  71. package/src/core/docs/schema-to-openapi.ts +148 -0
  72. package/src/core/docs/simple-docs.ts +5 -5
  73. package/src/core/docs/zod-to-openapi.ts +52 -20
  74. package/src/core/framework.ts +121 -28
  75. package/src/core/networking/adapters/index.ts +16 -0
  76. package/src/core/networking/adapters/socketio-adapter.ts +252 -0
  77. package/src/core/networking/adapters/ws-adapter.ts +425 -0
  78. package/src/core/networking/websocket-adapter.ts +217 -0
  79. package/src/core/networking/websocket-manager.ts +201 -143
  80. package/src/core/routing/index.ts +13 -13
  81. package/src/core/validation/adapters.ts +147 -0
  82. package/src/core/validation/index.ts +54 -38
  83. package/src/core/validation/schema-interface.ts +100 -0
  84. package/src/index.ts +36 -3
  85. package/src/moro.ts +27 -17
  86. package/src/types/config.ts +157 -0
@@ -0,0 +1,148 @@
1
+ // Universal Schema to OpenAPI Converter
2
+ // Converts ValidationSchema (Zod, Joi, etc.) to OpenAPI 3.0 schema definitions
3
+
4
+ import { ValidationSchema } from '../validation/schema-interface';
5
+ import { OpenAPISchema } from './zod-to-openapi';
6
+ import { createFrameworkLogger } from '../logger';
7
+
8
+ const logger = createFrameworkLogger('SchemaToOpenAPI');
9
+
10
+ // Check if a schema is a Zod schema
11
+ function isZodSchema(schema: any): boolean {
12
+ return (
13
+ schema && typeof schema === 'object' && schema._def && typeof schema.parseAsync === 'function'
14
+ );
15
+ }
16
+
17
+ // Check if schema is Joi
18
+ function isJoiSchema(schema: any): boolean {
19
+ return (
20
+ schema &&
21
+ typeof schema === 'object' &&
22
+ schema.type &&
23
+ typeof schema.validateAsync === 'function'
24
+ );
25
+ }
26
+
27
+ // Convert any ValidationSchema to OpenAPI
28
+ export function schemaToOpenAPI(
29
+ schema: ValidationSchema,
30
+ options: { includeExamples?: boolean; includeDescriptions?: boolean } = {}
31
+ ): OpenAPISchema {
32
+ // If it's a Zod schema, use the existing zod converter
33
+ if (isZodSchema(schema)) {
34
+ try {
35
+ // Import zod converter dynamically
36
+ const { zodToOpenAPI } = require('./zod-to-openapi');
37
+ return zodToOpenAPI(schema, options);
38
+ } catch (error) {
39
+ logger.warn('Zod converter not available, using fallback', String(error));
40
+ }
41
+ }
42
+
43
+ // If it's a Joi schema, convert from Joi
44
+ if (isJoiSchema(schema)) {
45
+ return convertJoiToOpenAPI(schema as any, options);
46
+ }
47
+
48
+ // For other schemas (custom validators, etc.), return a generic object schema
49
+ logger.debug('Using generic schema conversion for unknown validation type');
50
+ return {
51
+ type: 'object',
52
+ description: options.includeDescriptions ? 'Validated object' : undefined,
53
+ additionalProperties: true,
54
+ };
55
+ }
56
+
57
+ // Generate example from any ValidationSchema
58
+ export function generateExampleFromValidationSchema(schema: ValidationSchema): any {
59
+ // If it's a Zod schema, use existing example generator
60
+ if (isZodSchema(schema)) {
61
+ try {
62
+ const { generateExampleFromSchema } = require('./zod-to-openapi');
63
+ return generateExampleFromSchema(schema);
64
+ } catch (error) {
65
+ logger.warn('Zod example generator not available', String(error));
66
+ }
67
+ }
68
+
69
+ // For other schemas, return a generic example
70
+ return {
71
+ example: 'Validated data structure',
72
+ };
73
+ }
74
+
75
+ // Convert Joi schema to OpenAPI (basic implementation)
76
+ function convertJoiToOpenAPI(
77
+ joiSchema: any,
78
+ options: { includeDescriptions?: boolean }
79
+ ): OpenAPISchema {
80
+ const schemaType = joiSchema.type;
81
+
82
+ switch (schemaType) {
83
+ case 'string':
84
+ return {
85
+ type: 'string',
86
+ description: options.includeDescriptions ? joiSchema._description : undefined,
87
+ minLength: joiSchema._rules?.find((r: any) => r.name === 'min')?.args?.limit,
88
+ maxLength: joiSchema._rules?.find((r: any) => r.name === 'max')?.args?.limit,
89
+ pattern: joiSchema._rules?.find((r: any) => r.name === 'pattern')?.args?.regex?.source,
90
+ };
91
+
92
+ case 'number':
93
+ return {
94
+ type: 'number',
95
+ description: options.includeDescriptions ? joiSchema._description : undefined,
96
+ minimum: joiSchema._rules?.find((r: any) => r.name === 'min')?.args?.limit,
97
+ maximum: joiSchema._rules?.find((r: any) => r.name === 'max')?.args?.limit,
98
+ };
99
+
100
+ case 'boolean':
101
+ return {
102
+ type: 'boolean',
103
+ description: options.includeDescriptions ? joiSchema._description : undefined,
104
+ };
105
+
106
+ case 'object': {
107
+ const properties: Record<string, OpenAPISchema> = {};
108
+ const required: string[] = [];
109
+
110
+ if (joiSchema._inner?.children) {
111
+ for (const child of joiSchema._inner.children) {
112
+ const key = child.key;
113
+ properties[key] = convertJoiToOpenAPI(child.schema, options);
114
+
115
+ if (child.schema._flags?.presence === 'required') {
116
+ required.push(key);
117
+ }
118
+ }
119
+ }
120
+
121
+ return {
122
+ type: 'object',
123
+ properties,
124
+ required: required.length > 0 ? required : undefined,
125
+ description: options.includeDescriptions ? joiSchema._description : undefined,
126
+ };
127
+ }
128
+
129
+ case 'array':
130
+ return {
131
+ type: 'array',
132
+ items: joiSchema._inner?.items?.[0]
133
+ ? convertJoiToOpenAPI(joiSchema._inner.items[0], options)
134
+ : { type: 'object' },
135
+ description: options.includeDescriptions ? joiSchema._description : undefined,
136
+ minItems: joiSchema._rules?.find((r: any) => r.name === 'min')?.args?.limit,
137
+ maxItems: joiSchema._rules?.find((r: any) => r.name === 'max')?.args?.limit,
138
+ };
139
+
140
+ default:
141
+ logger.warn(`Unsupported Joi schema type: ${schemaType}`);
142
+ return {
143
+ type: 'object',
144
+ additionalProperties: true,
145
+ description: options.includeDescriptions ? 'Complex validation schema' : undefined,
146
+ };
147
+ }
148
+ }
@@ -132,7 +132,7 @@ export class SimpleDocsGenerator {
132
132
  <div class="container">
133
133
  <h1>${this.options.title}</h1>
134
134
  <p>${this.options.description}</p>
135
-
135
+
136
136
  <div class="example">
137
137
  <strong>Interactive Swagger UI:</strong> <a href="${this.options.basePath}" target="_blank">${this.options.basePath}</a><br>
138
138
  <strong>OpenAPI JSON:</strong> <a href="${this.options.basePath}/openapi.json" target="_blank">${this.options.basePath}/openapi.json</a>
@@ -189,11 +189,11 @@ export class SimpleDocsGenerator {
189
189
  <span class="method ${route.method}">${route.method}</span>
190
190
  <span class="path">${route.path}</span>
191
191
  </div>
192
-
192
+
193
193
  ${route.description ? `<div class="description">${route.description}</div>` : ''}
194
-
194
+
195
195
  ${route.tags ? `<div class="tags">${route.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}</div>` : ''}
196
-
196
+
197
197
  ${this.generateValidationInfo(route)}
198
198
  ${this.generateAuthInfo(route)}
199
199
  ${this.generateRateLimitInfo(route)}
@@ -215,7 +215,7 @@ export class SimpleDocsGenerator {
215
215
  return `
216
216
  <div class="validation">
217
217
  <strong>Validation:</strong> ${validationTypes.join(', ')}
218
- <br><small>Request will be validated with Zod schemas for type safety</small>
218
+ <br><small>Request will be validated with Validation schemas for type safety</small>
219
219
  </div>`;
220
220
  }
221
221
 
@@ -1,22 +1,40 @@
1
1
  // Zod to OpenAPI Schema Converter
2
2
  // Transforms Zod schemas into OpenAPI 3.0 schema definitions
3
3
 
4
- import {
5
- ZodSchema,
6
- ZodType,
7
- ZodObject,
8
- ZodArray,
9
- ZodString,
10
- ZodNumber,
11
- ZodBoolean,
12
- ZodEnum,
13
- ZodOptional,
14
- ZodDefault,
15
- ZodUnion,
16
- ZodLiteral,
17
- } from 'zod';
18
4
  import { createFrameworkLogger } from '../logger';
19
5
 
6
+ // Dynamic Zod imports (optional dependency)
7
+ let ZodSchema: any,
8
+ ZodType: any,
9
+ ZodObject: any,
10
+ ZodArray: any,
11
+ ZodString: any,
12
+ ZodNumber: any,
13
+ ZodBoolean: any,
14
+ ZodEnum: any,
15
+ ZodOptional: any,
16
+ ZodDefault: any,
17
+ ZodUnion: any,
18
+ ZodLiteral: any;
19
+
20
+ try {
21
+ const zod = require('zod');
22
+ ZodSchema = zod.ZodSchema;
23
+ ZodType = zod.ZodType;
24
+ ZodObject = zod.ZodObject;
25
+ ZodArray = zod.ZodArray;
26
+ ZodString = zod.ZodString;
27
+ ZodNumber = zod.ZodNumber;
28
+ ZodBoolean = zod.ZodBoolean;
29
+ ZodEnum = zod.ZodEnum;
30
+ ZodOptional = zod.ZodOptional;
31
+ ZodDefault = zod.ZodDefault;
32
+ ZodUnion = zod.ZodUnion;
33
+ ZodLiteral = zod.ZodLiteral;
34
+ } catch {
35
+ // Zod not available - that's fine!
36
+ }
37
+
20
38
  const logger = createFrameworkLogger('ZodToOpenAPI');
21
39
 
22
40
  // OpenAPI schema types
@@ -33,6 +51,8 @@ export interface OpenAPISchema {
33
51
  maximum?: number;
34
52
  minLength?: number;
35
53
  maxLength?: number;
54
+ minItems?: number;
55
+ maxItems?: number;
36
56
  pattern?: string;
37
57
  default?: any;
38
58
  oneOf?: OpenAPISchema[];
@@ -50,7 +70,12 @@ export interface ConversionOptions {
50
70
  }
51
71
 
52
72
  // Main conversion function
53
- export function zodToOpenAPI(schema: ZodSchema, options: ConversionOptions = {}): OpenAPISchema {
73
+ export function zodToOpenAPI(schema: any, options: ConversionOptions = {}): OpenAPISchema {
74
+ // Check if Zod is available
75
+ if (!ZodSchema) {
76
+ throw new Error('Zod is not installed. Please install zod to use zodToOpenAPI function.');
77
+ }
78
+
54
79
  const opts = {
55
80
  includeExamples: true,
56
81
  includeDescriptions: true,
@@ -365,7 +390,7 @@ function convertZodDefault(def: any, options: ConversionOptions): OpenAPISchema
365
390
 
366
391
  // Convert ZodUnion
367
392
  function convertZodUnion(def: any, options: ConversionOptions): OpenAPISchema {
368
- const schemas = def.options.map((option: ZodType) => convertZodType(option._def, options));
393
+ const schemas = def.options.map((option: any) => convertZodType(option._def, options));
369
394
 
370
395
  return {
371
396
  oneOf: schemas,
@@ -384,16 +409,23 @@ function convertZodLiteral(def: any, options: ConversionOptions): OpenAPISchema
384
409
  }
385
410
 
386
411
  // Helper functions
387
- function isOptionalType(type: ZodType): boolean {
412
+ function isOptionalType(type: any): boolean {
388
413
  return (type._def as any).typeName === 'ZodOptional';
389
414
  }
390
415
 
391
- function hasDefault(type: ZodType): boolean {
416
+ function hasDefault(type: any): boolean {
392
417
  return (type._def as any).typeName === 'ZodDefault';
393
418
  }
394
419
 
395
420
  // Generate example data from Zod schema
396
- export function generateExampleFromSchema(schema: ZodSchema): any {
421
+ export function generateExampleFromSchema(schema: any): any {
422
+ // Check if Zod is available
423
+ if (!ZodSchema) {
424
+ throw new Error(
425
+ 'Zod is not installed. Please install zod to use generateExampleFromSchema function.'
426
+ );
427
+ }
428
+
397
429
  try {
398
430
  return generateExample(schema._def);
399
431
  } catch (error) {
@@ -439,7 +471,7 @@ function generateExample(def: any): any {
439
471
  }
440
472
 
441
473
  for (const [key, value] of Object.entries(shape)) {
442
- const zodType = value as ZodType;
474
+ const zodType = value as any;
443
475
  if (!isOptionalType(zodType)) {
444
476
  example[key] = generateExample(zodType._def);
445
477
  }
@@ -1,10 +1,9 @@
1
- // src/core/framework.ts
1
+ // Core Moro Framework with Pluggable WebSocket Adapters
2
2
  import { createServer, Server } from 'http';
3
3
  import {
4
4
  createSecureServer as createHttp2SecureServer,
5
5
  createServer as createHttp2Server,
6
6
  } from 'http2';
7
- import { Server as SocketIOServer } from 'socket.io';
8
7
  import { EventEmitter } from 'events';
9
8
  import { MoroHttpServer, HttpRequest, HttpResponse, middleware } from './http';
10
9
  import { Router } from './http';
@@ -16,6 +15,7 @@ import { MoroEventBus } from './events';
16
15
  import { createFrameworkLogger, logger as globalLogger } from './logger';
17
16
  import { ModuleConfig, InternalRouteDefinition } from '../types/module';
18
17
  import { LogLevel, LoggerOptions } from '../types/logger';
18
+ import { WebSocketAdapter, WebSocketAdapterOptions } from './networking/websocket-adapter';
19
19
 
20
20
  export interface MoroOptions {
21
21
  http2?: boolean;
@@ -28,23 +28,27 @@ export interface MoroOptions {
28
28
  enabled?: boolean;
29
29
  threshold?: number;
30
30
  };
31
- websocket?: {
32
- compression?: boolean;
33
- customIdGenerator?: () => string;
34
- };
31
+ websocket?:
32
+ | {
33
+ enabled?: boolean;
34
+ adapter?: WebSocketAdapter;
35
+ compression?: boolean;
36
+ customIdGenerator?: () => string;
37
+ options?: WebSocketAdapterOptions;
38
+ }
39
+ | false;
35
40
  logger?: LoggerOptions | boolean;
36
41
  }
37
42
 
38
43
  export class Moro extends EventEmitter {
39
44
  private httpServer: MoroHttpServer;
40
45
  private server: Server | any; // HTTP/2 server type
41
- private io: SocketIOServer;
46
+ private websocketAdapter?: WebSocketAdapter;
42
47
  private container: Container;
43
48
  private moduleLoader: ModuleLoader;
44
- private websocketManager: WebSocketManager;
49
+ private websocketManager?: WebSocketManager;
45
50
  private circuitBreakers = new Map<string, CircuitBreaker>();
46
51
  private rateLimiters = new Map<string, Map<string, { count: number; resetTime: number }>>();
47
- private ioInstance: SocketIOServer;
48
52
  // Enterprise-grade event system
49
53
  private eventBus: MoroEventBus;
50
54
  // Framework logger
@@ -99,23 +103,12 @@ export class Moro extends EventEmitter {
99
103
  this.server = this.httpServer.getServer();
100
104
  }
101
105
 
102
- this.io = new SocketIOServer(this.server, {
103
- cors: { origin: '*' },
104
- path: '/socket.io/',
105
- });
106
-
107
- this.ioInstance = this.io;
108
106
  this.container = new Container();
109
107
  this.moduleLoader = new ModuleLoader(this.container);
110
- this.websocketManager = new WebSocketManager(this.io, this.container);
111
-
112
- // Configure WebSocket advanced features
113
- if (options.websocket?.customIdGenerator) {
114
- this.websocketManager.setCustomIdGenerator(options.websocket.customIdGenerator);
115
- }
116
108
 
117
- if (options.websocket?.compression) {
118
- this.websocketManager.enableCompression();
109
+ // Setup WebSocket adapter if enabled
110
+ if (options.websocket !== false) {
111
+ this.setupWebSockets(options.websocket || {});
119
112
  }
120
113
 
121
114
  // Initialize enterprise event bus
@@ -153,6 +146,71 @@ export class Moro extends EventEmitter {
153
146
  this.httpServer.use(this.errorBoundaryMiddleware());
154
147
  }
155
148
 
149
+ /**
150
+ * Setup WebSocket adapter and manager
151
+ */
152
+ private async setupWebSockets(wsConfig: any): Promise<void> {
153
+ try {
154
+ // Use provided adapter or try to auto-detect
155
+ if (wsConfig.adapter) {
156
+ this.websocketAdapter = wsConfig.adapter;
157
+ } else {
158
+ this.websocketAdapter = (await this.detectWebSocketAdapter()) || undefined;
159
+ }
160
+
161
+ if (this.websocketAdapter) {
162
+ await this.websocketAdapter.initialize(this.server, wsConfig.options);
163
+ this.websocketManager = new WebSocketManager(this.websocketAdapter, this.container);
164
+
165
+ // Configure adapter features
166
+ if (wsConfig.compression) {
167
+ this.websocketAdapter.setCompression(true);
168
+ }
169
+ if (wsConfig.customIdGenerator) {
170
+ this.websocketAdapter.setCustomIdGenerator(wsConfig.customIdGenerator);
171
+ }
172
+
173
+ this.logger.info(
174
+ `WebSocket adapter initialized: ${this.websocketAdapter.getAdapterName()}`,
175
+ 'WebSocketSetup'
176
+ );
177
+ }
178
+ } catch (error) {
179
+ this.logger.warn(
180
+ 'WebSocket setup failed, continuing without WebSocket support',
181
+ 'WebSocketSetup',
182
+ { error: error instanceof Error ? error.message : String(error) }
183
+ );
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Auto-detect available WebSocket adapter
189
+ */
190
+ private async detectWebSocketAdapter(): Promise<WebSocketAdapter | null> {
191
+ // Try socket.io first
192
+ try {
193
+ const { SocketIOAdapter } = await import('./networking/adapters');
194
+ return new SocketIOAdapter();
195
+ } catch {
196
+ // socket.io not available
197
+ }
198
+
199
+ // Try native ws library
200
+ try {
201
+ const { WSAdapter } = await import('./networking/adapters');
202
+ return new WSAdapter();
203
+ } catch {
204
+ // ws not available
205
+ }
206
+
207
+ this.logger.warn(
208
+ 'No WebSocket adapter found. Install socket.io or ws for WebSocket support',
209
+ 'AdapterDetection'
210
+ );
211
+ return null;
212
+ }
213
+
156
214
  private requestTrackingMiddleware() {
157
215
  return (req: HttpRequest, res: HttpResponse, next: () => void) => {
158
216
  const startTime = Date.now();
@@ -206,8 +264,31 @@ export class Moro extends EventEmitter {
206
264
  }
207
265
 
208
266
  // Public API for accessing Socket.IO server
267
+ /**
268
+ * Get WebSocket adapter (for backward compatibility)
269
+ * @deprecated Use getWebSocketAdapter() instead
270
+ */
209
271
  getIOServer() {
210
- return this.io;
272
+ if (!this.websocketAdapter) {
273
+ throw new Error(
274
+ 'WebSocket adapter not available. Install socket.io or configure a WebSocket adapter.'
275
+ );
276
+ }
277
+ return this.websocketAdapter;
278
+ }
279
+
280
+ /**
281
+ * Get the WebSocket adapter
282
+ */
283
+ getWebSocketAdapter(): WebSocketAdapter | undefined {
284
+ return this.websocketAdapter;
285
+ }
286
+
287
+ /**
288
+ * Get the WebSocket manager
289
+ */
290
+ getWebSocketManager(): WebSocketManager | undefined {
291
+ return this.websocketManager;
211
292
  }
212
293
 
213
294
  async loadModule(moduleConfig: ModuleConfig): Promise<void> {
@@ -392,7 +473,7 @@ export class Moro extends EventEmitter {
392
473
  : undefined,
393
474
  events: moduleEventBus, // Use pre-created event bus
394
475
  app: {
395
- get: (key: string) => (key === 'io' ? this.ioInstance : undefined),
476
+ get: (key: string) => (key === 'io' ? this.websocketAdapter : undefined),
396
477
  },
397
478
  };
398
479
  this.logger.debug(`Database available: ${!!requestToUse.database}`, 'Handler', {
@@ -466,7 +547,15 @@ export class Moro extends EventEmitter {
466
547
  }
467
548
 
468
549
  private async setupWebSocketHandlers(config: ModuleConfig): Promise<void> {
469
- const namespace = this.io.of(`/${config.name}`);
550
+ if (!this.websocketAdapter || !this.websocketManager) {
551
+ this.logger.warn(
552
+ `Module ${config.name} defines WebSocket handlers but no WebSocket adapter is available`,
553
+ 'WebSocketSetup'
554
+ );
555
+ return;
556
+ }
557
+
558
+ const namespace = this.websocketAdapter.createNamespace(`/${config.name}`);
470
559
 
471
560
  for (const wsConfig of config.websockets || []) {
472
561
  await this.websocketManager.registerHandler(namespace, wsConfig, config);
@@ -530,13 +619,17 @@ export class Moro extends EventEmitter {
530
619
  // Compatibility method for existing controllers
531
620
  set(key: string, value: any): void {
532
621
  if (key === 'io') {
533
- this.ioInstance = value;
622
+ // Deprecated: Use websocket adapter instead
623
+ this.logger.warn(
624
+ 'Setting io instance is deprecated. Use websocket adapter configuration.',
625
+ 'Deprecated'
626
+ );
534
627
  }
535
628
  }
536
629
 
537
630
  get(key: string): any {
538
631
  if (key === 'io') {
539
- return this.ioInstance;
632
+ return this.websocketAdapter;
540
633
  }
541
634
  return undefined;
542
635
  }
@@ -0,0 +1,16 @@
1
+ // WebSocket Adapters for Moro Framework
2
+ // Export all available adapters from this centralized location
3
+
4
+ export { SocketIOAdapter } from './socketio-adapter';
5
+ export { WSAdapter } from './ws-adapter';
6
+
7
+ // Re-export the adapter interface for convenience
8
+ export type {
9
+ WebSocketAdapter,
10
+ WebSocketAdapterOptions,
11
+ WebSocketNamespace,
12
+ WebSocketConnection,
13
+ WebSocketEmitter,
14
+ WebSocketMiddleware,
15
+ WebSocketEventHandler,
16
+ } from '../websocket-adapter';