@morojs/moro 1.6.0 → 1.6.2
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/dist/core/config/config-sources.js +4 -0
- package/dist/core/config/config-sources.js.map +1 -1
- package/dist/core/config/config-validator.js +3 -0
- package/dist/core/config/config-validator.js.map +1 -1
- package/dist/core/config/file-loader.js +3 -1
- package/dist/core/config/file-loader.js.map +1 -1
- package/dist/core/config/schema.js +4 -1
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/events/event-bus.js +1 -1
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/framework.d.ts +1 -1
- package/dist/core/framework.js +13 -7
- package/dist/core/framework.js.map +1 -1
- package/dist/core/http/http-server.d.ts +55 -15
- package/dist/core/http/http-server.js +70 -146
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/index.d.ts +1 -1
- package/dist/core/http/index.js +1 -1
- package/dist/core/http/index.js.map +1 -1
- package/dist/core/http/uws-http-server.d.ts +4 -22
- package/dist/core/http/uws-http-server.js +43 -208
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/networking/adapters/uws-adapter.d.ts +1 -1
- package/dist/core/networking/adapters/uws-adapter.js +1 -1
- package/dist/core/pooling/object-pool-manager.d.ts +140 -0
- package/dist/core/pooling/object-pool-manager.js +502 -0
- package/dist/core/pooling/object-pool-manager.js.map +1 -0
- package/dist/core/routing/app-integration.d.ts +12 -10
- package/dist/core/routing/app-integration.js +43 -74
- package/dist/core/routing/app-integration.js.map +1 -1
- package/dist/core/routing/index.d.ts +15 -29
- package/dist/core/routing/index.js +43 -390
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/routing/path-matcher.d.ts +67 -0
- package/dist/core/routing/path-matcher.js +182 -0
- package/dist/core/routing/path-matcher.js.map +1 -0
- package/dist/core/{http → routing}/router.d.ts +21 -9
- package/dist/core/routing/router.js +68 -0
- package/dist/core/routing/router.js.map +1 -0
- package/dist/core/routing/unified-router.d.ts +148 -0
- package/dist/core/routing/unified-router.js +684 -0
- package/dist/core/routing/unified-router.js.map +1 -0
- package/dist/moro.d.ts +10 -7
- package/dist/moro.js +90 -41
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +3 -0
- package/package.json +1 -1
- package/src/core/config/config-sources.ts +4 -0
- package/src/core/config/config-validator.ts +3 -0
- package/src/core/config/file-loader.ts +4 -1
- package/src/core/config/schema.ts +4 -1
- package/src/core/events/event-bus.ts +1 -1
- package/src/core/framework.ts +14 -9
- package/src/core/http/http-server.ts +76 -161
- package/src/core/http/index.ts +1 -1
- package/src/core/http/uws-http-server.ts +43 -246
- package/src/core/networking/adapters/uws-adapter.ts +1 -1
- package/src/core/pooling/object-pool-manager.ts +630 -0
- package/src/core/routing/app-integration.ts +57 -109
- package/src/core/routing/index.ts +62 -473
- package/src/core/routing/path-matcher.ts +222 -0
- package/src/core/routing/router.ts +97 -0
- package/src/core/routing/unified-router.ts +870 -0
- package/src/moro.ts +107 -57
- package/src/types/config.ts +3 -0
- package/dist/core/http/router.js +0 -183
- package/dist/core/http/router.js.map +0 -1
- package/src/core/http/router.ts +0 -230
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// uWebSockets.js HTTP Server Implementation for Moro Framework
|
|
2
|
-
// Provides
|
|
2
|
+
// Provides high-performance HTTP and WebSocket server using uWebSockets.js
|
|
3
3
|
|
|
4
4
|
import cluster from 'cluster';
|
|
5
5
|
import { createFrameworkLogger } from '../logger/index.js';
|
|
6
|
+
import { ObjectPoolManager } from '../pooling/object-pool-manager.js';
|
|
6
7
|
import {
|
|
7
8
|
HttpRequest,
|
|
8
9
|
HttpResponse,
|
|
@@ -19,24 +20,18 @@ export class UWebSocketsHttpServer {
|
|
|
19
20
|
private app: any; // uWebSockets app instance
|
|
20
21
|
private uws: any; // uWebSockets module reference (stored to avoid re-importing)
|
|
21
22
|
private listenSocket: any; // uWebSockets listen socket
|
|
22
|
-
private routes: RouteEntry[] = [];
|
|
23
23
|
private globalMiddleware: Middleware[] = [];
|
|
24
24
|
private logger = createFrameworkLogger('UWSHttpServer');
|
|
25
25
|
private hookManager: any;
|
|
26
26
|
private requestCounter = 0;
|
|
27
|
+
private requestTrackingEnabled = true; // Generate request IDs
|
|
27
28
|
private isListening = false;
|
|
28
29
|
private port?: number;
|
|
29
30
|
private host?: string;
|
|
30
31
|
private initPromise: Promise<void>;
|
|
31
32
|
|
|
32
|
-
//
|
|
33
|
-
private
|
|
34
|
-
private dynamicRoutes: RouteEntry[] = [];
|
|
35
|
-
|
|
36
|
-
// Performance optimizations - object pooling
|
|
37
|
-
private paramObjectPool: Record<string, string>[] = [];
|
|
38
|
-
private queryObjectPool: Record<string, string>[] = [];
|
|
39
|
-
private readonly maxPoolSize = 100;
|
|
33
|
+
// Performance optimizations - shared object pooling
|
|
34
|
+
private poolManager = ObjectPoolManager.getInstance();
|
|
40
35
|
|
|
41
36
|
// String interning for common values
|
|
42
37
|
private static readonly INTERNED_METHODS = new Map([
|
|
@@ -102,118 +97,24 @@ export class UWebSocketsHttpServer {
|
|
|
102
97
|
}
|
|
103
98
|
|
|
104
99
|
private setupRouteHandlers(): void {
|
|
105
|
-
// Handle all HTTP methods through catchall
|
|
100
|
+
// Handle all HTTP methods through catchall
|
|
101
|
+
// All requests go through middleware chain (includes UnifiedRouter)
|
|
106
102
|
this.app.any('/*', (res: any, req: any) => {
|
|
107
|
-
// Try fast synchronous path first
|
|
108
|
-
const method = req.getMethod().toUpperCase();
|
|
109
|
-
const path = req.getUrl();
|
|
110
|
-
|
|
111
|
-
// Fast path: No body, no global middleware
|
|
112
|
-
if (this.globalMiddleware.length === 0 && method === 'GET') {
|
|
113
|
-
const route = this.findRoute(method, path);
|
|
114
|
-
if (route && (!route.middleware || route.middleware.length === 0)) {
|
|
115
|
-
this.handleRequestSync(req, res, route, method, path);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Slow path: Has middleware or body or other complexity
|
|
121
103
|
this.handleRequest(req, res);
|
|
122
104
|
});
|
|
123
105
|
}
|
|
124
106
|
|
|
125
|
-
// Fast synchronous handler for simple GET requests
|
|
126
|
-
private handleRequestSync(
|
|
127
|
-
req: any,
|
|
128
|
-
res: any,
|
|
129
|
-
route: RouteEntry,
|
|
130
|
-
method: string,
|
|
131
|
-
path: string
|
|
132
|
-
): void {
|
|
133
|
-
this.requestCounter++;
|
|
134
|
-
let httpReq: any;
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
httpReq = this.createMoroRequest(req, res);
|
|
138
|
-
const httpRes = this.createMoroResponse(req, res);
|
|
139
|
-
|
|
140
|
-
// Extract params if needed
|
|
141
|
-
if (route.paramNames.length > 0) {
|
|
142
|
-
const matches = path.match(route.pattern);
|
|
143
|
-
if (matches) {
|
|
144
|
-
httpReq.params = this.acquireParamObject();
|
|
145
|
-
route.paramNames.forEach((name: string, index: number) => {
|
|
146
|
-
httpReq.params![name] = matches[index + 1];
|
|
147
|
-
});
|
|
148
|
-
(httpReq as any)._pooledParams = httpReq.params;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Execute handler synchronously
|
|
153
|
-
const result = route.handler(httpReq, httpRes);
|
|
154
|
-
|
|
155
|
-
// Handle return value
|
|
156
|
-
if (result && typeof result.then === 'function') {
|
|
157
|
-
// Oops, handler is async - handle it
|
|
158
|
-
result
|
|
159
|
-
.then((data: any) => {
|
|
160
|
-
if (data !== undefined && data !== null && !httpRes.headersSent) {
|
|
161
|
-
httpRes.json(data);
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
.catch(() => {
|
|
165
|
-
if (!res.aborted) {
|
|
166
|
-
res.cork(() => {
|
|
167
|
-
res.writeStatus('500 Internal Server Error');
|
|
168
|
-
res.end('{"success":false,"error":"Internal server error"}');
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
} else if (result !== undefined && result !== null && !httpRes.headersSent) {
|
|
173
|
-
httpRes.json(result);
|
|
174
|
-
}
|
|
175
|
-
} finally {
|
|
176
|
-
const pooledParams = (httpReq as any)?._pooledParams;
|
|
177
|
-
if (pooledParams) this.releaseParamObject(pooledParams);
|
|
178
|
-
|
|
179
|
-
const pooledQuery = (httpReq as any)?._pooledQuery;
|
|
180
|
-
if (pooledQuery) this.releaseQueryObject(pooledQuery);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Object pooling methods for performance
|
|
185
|
-
private acquireParamObject(): Record<string, string> {
|
|
186
|
-
return this.paramObjectPool.pop() || {};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private releaseParamObject(obj: Record<string, string>): void {
|
|
190
|
-
if (this.paramObjectPool.length < this.maxPoolSize) {
|
|
191
|
-
for (const key in obj) delete obj[key];
|
|
192
|
-
this.paramObjectPool.push(obj);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private acquireQueryObject(): Record<string, string> {
|
|
197
|
-
return this.queryObjectPool.pop() || {};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private releaseQueryObject(obj: Record<string, string>): void {
|
|
201
|
-
if (this.queryObjectPool.length < this.maxPoolSize) {
|
|
202
|
-
for (const key in obj) delete obj[key];
|
|
203
|
-
this.queryObjectPool.push(obj);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
107
|
private async handleRequest(req: any, res: any): Promise<void> {
|
|
208
108
|
this.requestCounter++;
|
|
209
109
|
|
|
210
110
|
// Declare outside try block for cleanup in finally
|
|
211
111
|
let httpReq: any;
|
|
112
|
+
let httpRes: any;
|
|
212
113
|
|
|
213
114
|
try {
|
|
214
115
|
// Create Moro-compatible request object
|
|
215
116
|
httpReq = this.createMoroRequest(req, res);
|
|
216
|
-
|
|
117
|
+
httpRes = this.createMoroResponse(req, res);
|
|
217
118
|
|
|
218
119
|
// Parse body only if there's actually a body (check content-length)
|
|
219
120
|
const method = req.getMethod().toUpperCase();
|
|
@@ -234,47 +135,17 @@ export class UWebSocketsHttpServer {
|
|
|
234
135
|
});
|
|
235
136
|
}
|
|
236
137
|
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
138
|
+
// Execute middleware chain (includes UnifiedRouter for routing)
|
|
139
|
+
// The UnifiedRouter will handle route matching, params extraction, and handler execution
|
|
140
|
+
if (this.globalMiddleware.length > 0) {
|
|
141
|
+
await this.executeMiddleware(this.globalMiddleware, httpReq, httpRes);
|
|
142
|
+
} else {
|
|
143
|
+
// No middleware - send 404 (router middleware should be present)
|
|
242
144
|
if (!httpRes.headersSent) {
|
|
243
145
|
httpRes.statusCode = 404;
|
|
244
146
|
httpRes.setHeader('Content-Type', 'application/json');
|
|
245
147
|
httpRes.end('{"success":false,"error":"Not found"}');
|
|
246
148
|
}
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Extract path parameters early - use pooled object
|
|
251
|
-
const matches = httpReq.path.match(route.pattern);
|
|
252
|
-
if (matches) {
|
|
253
|
-
httpReq.params = this.acquireParamObject();
|
|
254
|
-
route.paramNames.forEach((name: string, index: number) => {
|
|
255
|
-
httpReq.params![name] = matches[index + 1];
|
|
256
|
-
});
|
|
257
|
-
// Store for cleanup
|
|
258
|
-
(httpReq as any)._pooledParams = httpReq.params;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Execute ALL middleware in one pass (global + route)
|
|
262
|
-
if (this.globalMiddleware.length > 0 || (route.middleware && route.middleware.length > 0)) {
|
|
263
|
-
const allMiddleware =
|
|
264
|
-
route.middleware && route.middleware.length > 0
|
|
265
|
-
? [...this.globalMiddleware, ...route.middleware]
|
|
266
|
-
: this.globalMiddleware;
|
|
267
|
-
|
|
268
|
-
await this.executeMiddleware(allMiddleware, httpReq, httpRes);
|
|
269
|
-
if (httpRes.headersSent) return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Execute route handler
|
|
273
|
-
const result = await route.handler(httpReq, httpRes);
|
|
274
|
-
|
|
275
|
-
// If handler returns data and response hasn't been sent, send it
|
|
276
|
-
if (result !== undefined && result !== null && !httpRes.headersSent) {
|
|
277
|
-
httpRes.json(result);
|
|
278
149
|
}
|
|
279
150
|
} catch (error) {
|
|
280
151
|
this.logger.error(
|
|
@@ -295,15 +166,18 @@ export class UWebSocketsHttpServer {
|
|
|
295
166
|
}
|
|
296
167
|
}
|
|
297
168
|
} finally {
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
169
|
+
// CRITICAL: Release pooled objects back to pool
|
|
170
|
+
if (httpReq) {
|
|
171
|
+
const pooledQuery = (httpReq as any)._pooledQuery;
|
|
172
|
+
const pooledHeaders = (httpReq as any)._pooledHeaders;
|
|
173
|
+
|
|
174
|
+
if (pooledQuery && Object.keys(pooledQuery).length > 0) {
|
|
175
|
+
this.poolManager.releaseQuery(pooledQuery);
|
|
176
|
+
}
|
|
303
177
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
178
|
+
if (pooledHeaders && Object.keys(pooledHeaders).length > 0) {
|
|
179
|
+
this.poolManager.releaseHeaders(pooledHeaders);
|
|
180
|
+
}
|
|
307
181
|
}
|
|
308
182
|
}
|
|
309
183
|
}
|
|
@@ -316,11 +190,11 @@ export class UWebSocketsHttpServer {
|
|
|
316
190
|
// Use interned method string if available
|
|
317
191
|
const method = UWebSocketsHttpServer.INTERNED_METHODS.get(methodRaw) || methodRaw.toUpperCase();
|
|
318
192
|
|
|
319
|
-
//
|
|
320
|
-
let queryParams: Record<string, string
|
|
193
|
+
// Optimized query parsing with pooled object
|
|
194
|
+
let queryParams: Record<string, string>;
|
|
321
195
|
if (queryString) {
|
|
322
|
-
queryParams = this.
|
|
323
|
-
//
|
|
196
|
+
queryParams = this.poolManager.acquireQuery();
|
|
197
|
+
// Query parsing without URLSearchParams overhead
|
|
324
198
|
const pairs = queryString.split('&');
|
|
325
199
|
for (let i = 0; i < pairs.length; i++) {
|
|
326
200
|
const pair = pairs[i];
|
|
@@ -331,10 +205,12 @@ export class UWebSocketsHttpServer {
|
|
|
331
205
|
queryParams[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
332
206
|
}
|
|
333
207
|
}
|
|
208
|
+
} else {
|
|
209
|
+
queryParams = {};
|
|
334
210
|
}
|
|
335
211
|
|
|
336
|
-
//
|
|
337
|
-
const headers
|
|
212
|
+
// Optimized header parsing with pooled object
|
|
213
|
+
const headers = this.poolManager.acquireHeaders();
|
|
338
214
|
req.forEach((key: string, value: string) => {
|
|
339
215
|
const lowerKey = key.toLowerCase();
|
|
340
216
|
headers[lowerKey] = value;
|
|
@@ -344,16 +220,17 @@ export class UWebSocketsHttpServer {
|
|
|
344
220
|
method,
|
|
345
221
|
path: url,
|
|
346
222
|
url: url,
|
|
347
|
-
query: queryParams
|
|
223
|
+
query: queryParams,
|
|
348
224
|
params: {}, // Will be filled by route matching
|
|
349
225
|
headers,
|
|
350
226
|
body: null,
|
|
351
227
|
ip: '', // Lazy - only compute if accessed
|
|
352
|
-
requestId: '', //
|
|
228
|
+
requestId: this.requestTrackingEnabled ? this.poolManager.generateRequestId() : '', // ID generation (if enabled)
|
|
353
229
|
} as HttpRequest;
|
|
354
230
|
|
|
355
|
-
// Store
|
|
231
|
+
// Store pooled objects for cleanup
|
|
356
232
|
(httpReq as any)._pooledQuery = queryParams;
|
|
233
|
+
(httpReq as any)._pooledHeaders = headers;
|
|
357
234
|
|
|
358
235
|
return httpReq;
|
|
359
236
|
}
|
|
@@ -599,99 +476,19 @@ export class UWebSocketsHttpServer {
|
|
|
599
476
|
}
|
|
600
477
|
}
|
|
601
478
|
|
|
602
|
-
private findRoute(method: string, path: string): RouteEntry | null {
|
|
603
|
-
// Fast path: static route lookup
|
|
604
|
-
const staticKey = `${method}:${path}`;
|
|
605
|
-
const staticRoute = this.staticRoutes.get(staticKey);
|
|
606
|
-
if (staticRoute) return staticRoute;
|
|
607
|
-
|
|
608
|
-
// Dynamic route matching
|
|
609
|
-
for (const route of this.dynamicRoutes) {
|
|
610
|
-
if (route.method === method && route.pattern.test(path)) {
|
|
611
|
-
return route;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
479
|
// Public API - matches MoroHttpServer interface
|
|
619
480
|
|
|
620
481
|
use(middleware: Middleware): void {
|
|
621
482
|
this.globalMiddleware.push(middleware);
|
|
622
483
|
}
|
|
623
484
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
post(path: string, handler: HttpHandler, middleware?: Middleware[]): void {
|
|
629
|
-
this.addRoute('POST', path, handler, middleware);
|
|
485
|
+
// Configure request tracking (ID generation)
|
|
486
|
+
setRequestTracking(enabled: boolean): void {
|
|
487
|
+
this.requestTrackingEnabled = enabled;
|
|
630
488
|
}
|
|
631
489
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
delete(path: string, handler: HttpHandler, middleware?: Middleware[]): void {
|
|
637
|
-
this.addRoute('DELETE', path, handler, middleware);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
patch(path: string, handler: HttpHandler, middleware?: Middleware[]): void {
|
|
641
|
-
this.addRoute('PATCH', path, handler, middleware);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
options(path: string, handler: HttpHandler, middleware?: Middleware[]): void {
|
|
645
|
-
this.addRoute('OPTIONS', path, handler, middleware);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
head(path: string, handler: HttpHandler, middleware?: Middleware[]): void {
|
|
649
|
-
this.addRoute('HEAD', path, handler, middleware);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
private addRoute(
|
|
653
|
-
method: string,
|
|
654
|
-
path: string,
|
|
655
|
-
handler: HttpHandler,
|
|
656
|
-
middleware?: Middleware[]
|
|
657
|
-
): void {
|
|
658
|
-
const { pattern, paramNames } = this.pathToRegex(path);
|
|
659
|
-
|
|
660
|
-
const route: RouteEntry = {
|
|
661
|
-
method,
|
|
662
|
-
path,
|
|
663
|
-
pattern,
|
|
664
|
-
paramNames,
|
|
665
|
-
handler,
|
|
666
|
-
middleware: middleware || [],
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
this.routes.push(route);
|
|
670
|
-
|
|
671
|
-
// Optimize route lookup
|
|
672
|
-
if (paramNames.length === 0) {
|
|
673
|
-
// Static route
|
|
674
|
-
this.staticRoutes.set(`${method}:${path}`, route);
|
|
675
|
-
} else {
|
|
676
|
-
// Dynamic route
|
|
677
|
-
this.dynamicRoutes.push(route);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
this.logger.debug(`Route registered: ${method} ${path}`, 'RouteRegistration');
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {
|
|
684
|
-
const paramNames: string[] = [];
|
|
685
|
-
const regexPath = path.replace(/\//g, '\\/').replace(/:([^/]+)/g, (match, paramName) => {
|
|
686
|
-
paramNames.push(paramName);
|
|
687
|
-
return '([^/]+)';
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
return {
|
|
691
|
-
pattern: new RegExp(`^${regexPath}$`),
|
|
692
|
-
paramNames,
|
|
693
|
-
};
|
|
694
|
-
}
|
|
490
|
+
// Note: Route registration methods (get, post, etc.) are not used by uWebSockets adapter
|
|
491
|
+
// All routing is handled by UnifiedRouter through the middleware chain
|
|
695
492
|
|
|
696
493
|
setHookManager(hookManager: any): void {
|
|
697
494
|
this.hookManager = hookManager;
|
|
@@ -15,7 +15,7 @@ import { createFrameworkLogger } from '../../logger/index.js';
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* uWebSockets adapter implementation
|
|
18
|
-
* Provides
|
|
18
|
+
* Provides high-performance WebSocket support using uWebSockets.js
|
|
19
19
|
*/
|
|
20
20
|
export class UWebSocketsAdapter implements WebSocketAdapter {
|
|
21
21
|
private app: any; // uWebSockets app instance
|