@scpxl/nodejs-framework 1.0.28 → 1.0.29
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/README.md +13 -6
- package/dist/application/base-application.interface.d.ts +0 -9
- package/dist/application/base-application.interface.d.ts.map +1 -1
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +5 -1
- package/dist/config/schema.js.map +2 -2
- package/dist/websocket/define-subscriber.d.ts +26 -0
- package/dist/websocket/define-subscriber.d.ts.map +1 -0
- package/dist/websocket/define-subscriber.js +33 -0
- package/dist/websocket/define-subscriber.js.map +7 -0
- package/dist/websocket/index.d.ts +2 -0
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +3 -1
- package/dist/websocket/index.js.map +2 -2
- package/dist/websocket/websocket-server.d.ts +12 -0
- package/dist/websocket/websocket-server.d.ts.map +1 -1
- package/dist/websocket/websocket-server.js +259 -8
- package/dist/websocket/websocket-server.js.map +2 -2
- package/dist/websocket/websocket.interface.d.ts +41 -2
- package/dist/websocket/websocket.interface.d.ts.map +1 -1
- package/dist/websocket/websocket.interface.js.map +2 -2
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -469,10 +469,17 @@ cd examples/hello-world/backend && npm install
|
|
|
469
469
|
cd ../frontend && npm install
|
|
470
470
|
|
|
471
471
|
# Run backend
|
|
472
|
-
npm run example --
|
|
472
|
+
npm run example -- hello-world/backend
|
|
473
|
+
# or
|
|
474
|
+
PXL_EXAMPLE=hello-world/backend npm run example
|
|
475
|
+
# The backend auto-starts an embedded Redis instance if none is available locally.
|
|
476
|
+
# Disable this behaviour with PXL_EMBEDDED_REDIS=false when you want to supply your own Redis.
|
|
473
477
|
|
|
474
|
-
#
|
|
475
|
-
npm run example
|
|
478
|
+
# Run backend + frontend together (watch mode)
|
|
479
|
+
npm run example:hello-world
|
|
480
|
+
|
|
481
|
+
# Run frontend (in another terminal)
|
|
482
|
+
npm run example -- hello-world/frontend
|
|
476
483
|
```
|
|
477
484
|
|
|
478
485
|
Then open http://localhost:5173 to see the app.
|
|
@@ -486,9 +493,9 @@ Demonstrates the command framework with examples:
|
|
|
486
493
|
cd examples/commands && npm install
|
|
487
494
|
|
|
488
495
|
# Run commands from repository root
|
|
489
|
-
npm run example --
|
|
490
|
-
npm run example --
|
|
491
|
-
npm run example --
|
|
496
|
+
npm run example -- commands -- hello
|
|
497
|
+
npm run example -- commands -- database-seed
|
|
498
|
+
npm run example -- commands -- queue-process
|
|
492
499
|
```
|
|
493
500
|
|
|
494
501
|
See [examples/README.md](examples/README.md) for more details.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { ClusterManagerConfig } from '../cluster/cluster-manager.interface.js';
|
|
2
|
-
import type DatabaseInstance from '../database/instance.js';
|
|
3
2
|
import type { EventDefinition } from '../event/manager.interface.js';
|
|
4
3
|
import type { PerformanceMonitorOptions, PerformanceThresholds } from '../performance/performance-monitor.js';
|
|
5
4
|
import type { QueueItem } from '../queue/index.interface.js';
|
|
6
5
|
import type { WebServerDebugOptions, WebServerLogConfig, WebServerOptions, WebServerRoute } from '../webserver/webserver.interface.js';
|
|
7
|
-
import type WebSocketServer from '../websocket/websocket-server.js';
|
|
8
6
|
import type { WebSocketOptions, WebSocketRoute, WebSocketType } from '../websocket/websocket.interface.js';
|
|
9
7
|
export type OnStartedEvent = ({ startupTime }: {
|
|
10
8
|
startupTime: number;
|
|
@@ -87,13 +85,6 @@ export interface ApplicationWebSocketConfig extends WebSocketOptions {
|
|
|
87
85
|
clientId: string;
|
|
88
86
|
parsedMessage: any;
|
|
89
87
|
}) => void;
|
|
90
|
-
/** WebSocket subscriber event handler */
|
|
91
|
-
subscriberEventHandler?: (options: {
|
|
92
|
-
channel: string;
|
|
93
|
-
message: any;
|
|
94
|
-
webSocketServer: WebSocketServer;
|
|
95
|
-
databaseInstance: DatabaseInstance;
|
|
96
|
-
}) => void;
|
|
97
88
|
}
|
|
98
89
|
export interface ApplicationWebServerConfig extends WebServerOptions {
|
|
99
90
|
/** Whether to enable web server */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-application.interface.d.ts","sourceRoot":"","sources":["../../src/application/base-application.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"base-application.interface.d.ts","sourceRoot":"","sources":["../../src/application/base-application.interface.ts"],"names":[],"mappings":"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"}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -208,6 +208,10 @@ export declare const WebSocketConfigSchema: z.ZodObject<{
|
|
|
208
208
|
action: z.ZodString;
|
|
209
209
|
controller: z.ZodOptional<z.ZodUnknown>;
|
|
210
210
|
}, z.core.$strip>>>>;
|
|
211
|
+
subscriberHandlers: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
212
|
+
directory: z.ZodOptional<z.ZodString>;
|
|
213
|
+
handlers: z.ZodOptional<z.ZodArray<z.ZodAny>>;
|
|
214
|
+
}, z.core.$strip>>>;
|
|
211
215
|
}, z.core.$strip>;
|
|
212
216
|
export declare const ClusterConfigSchema: z.ZodObject<{
|
|
213
217
|
enabled: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
@@ -412,6 +416,10 @@ export declare const FrameworkConfigSchema: z.ZodObject<{
|
|
|
412
416
|
action: z.ZodString;
|
|
413
417
|
controller: z.ZodOptional<z.ZodUnknown>;
|
|
414
418
|
}, z.core.$strip>>>>;
|
|
419
|
+
subscriberHandlers: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
420
|
+
directory: z.ZodOptional<z.ZodString>;
|
|
421
|
+
handlers: z.ZodOptional<z.ZodArray<z.ZodAny>>;
|
|
422
|
+
}, z.core.$strip>>>;
|
|
415
423
|
}, z.core.$strip>>;
|
|
416
424
|
}, z.core.$strip>;
|
|
417
425
|
export type InferFrameworkConfig = z.infer<typeof FrameworkConfigSchema>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AAGH,eAAO,MAAM,oBAAoB;;;;;;;;iBAUM,CAAC;AAGxC,eAAO,MAAM,oBAAoB;;;;;;;kBASpB,CAAC;AAEd,eAAO,MAAM,cAAc;;;iBAGzB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;iBAI1B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;iBAI5B,CAAC;AAGH,eAAO,MAAM,qBAAqB;;iBAEhC,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;iBAI5B,CAAC;AAGH,eAAO,MAAM,eAAe;;;kBAKf,CAAC;AAGd,eAAO,MAAM,2BAA2B;;;;;;iBAQ5B,CAAC;AAEb,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;iBAe5B,CAAC;AAGb,eAAO,MAAM,gBAAgB;;kBAIhB,CAAC;AAGd,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;kBA+BpB,CAAC;AAGd,eAAO,MAAM,oBAAoB;;;;;;;;;;;;iBAcjB,CAAC;AAEjB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCwE,CAAC;AAG3G,eAAO,MAAM,oBAAoB;;;;;iBAK/B,CAAC;AAEH,eAAO,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AAGH,eAAO,MAAM,oBAAoB;;;;;;;;iBAUM,CAAC;AAGxC,eAAO,MAAM,oBAAoB;;;;;;;kBASpB,CAAC;AAEd,eAAO,MAAM,cAAc;;;iBAGzB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;iBAI1B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;iBAI5B,CAAC;AAGH,eAAO,MAAM,qBAAqB;;iBAEhC,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;iBAI5B,CAAC;AAGH,eAAO,MAAM,eAAe;;;kBAKf,CAAC;AAGd,eAAO,MAAM,2BAA2B;;;;;;iBAQ5B,CAAC;AAEb,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;iBAe5B,CAAC;AAGb,eAAO,MAAM,gBAAgB;;kBAIhB,CAAC;AAGd,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;kBA+BpB,CAAC;AAGd,eAAO,MAAM,oBAAoB;;;;;;;;;;;;iBAcjB,CAAC;AAEjB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCwE,CAAC;AAG3G,eAAO,MAAM,oBAAoB;;;;;iBAK/B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;iBAYtB,CAAC;AAGb,eAAO,MAAM,mBAAmB;;;iBAKpB,CAAC;AAGb,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiBhC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEzE,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IACvC,MAAM,EAAE,qBAAqB,EAAE,CAAC;gBAC3B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,EAAE;CAK7D;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,GAAE,qBAA0B,GAAG,oBAAoB,CAUhH;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAE1E;AAED,eAAe,qBAAqB,CAAC"}
|
package/dist/config/schema.js
CHANGED
|
@@ -147,7 +147,11 @@ const WebSocketRouteSchema = z.object({
|
|
|
147
147
|
const WebSocketConfigSchema = z.object({
|
|
148
148
|
type: z.string().default("native"),
|
|
149
149
|
enabled: z.boolean().default(false),
|
|
150
|
-
routes: z.array(WebSocketRouteSchema).default([])
|
|
150
|
+
routes: z.array(WebSocketRouteSchema).default([]),
|
|
151
|
+
subscriberHandlers: z.object({
|
|
152
|
+
directory: z.string().optional(),
|
|
153
|
+
handlers: z.array(z.any()).optional()
|
|
154
|
+
}).optional()
|
|
151
155
|
}).partial();
|
|
152
156
|
const ClusterConfigSchema = z.object({
|
|
153
157
|
enabled: z.boolean().default(false),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/config/schema.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod';\nimport crypto from 'node:crypto';\n\n// Redis configuration schema\nexport const RedisConfigSchema = z.object({\n host: z.string().min(1, 'redis.host required'),\n port: z.number().int().positive().default(6379),\n password: z.string().min(1).optional(),\n});\n\n// Database configuration schema\nexport const DatabaseConfigSchema = z\n .object({\n enabled: z.boolean().default(false),\n host: z.string().min(1, 'database.host required'),\n port: z.number().int().positive().default(5432),\n username: z.string().min(1, 'database.username required'),\n password: z.string().min(1, 'database.password required'),\n databaseName: z.string().min(1, 'database.databaseName required'),\n entitiesDirectory: z.string().min(1).optional(),\n })\n .partial({ entitiesDirectory: true });\n\n// Queue configuration schema\nexport const QueueLogConfigSchema = z\n .object({\n jobRegistered: z.boolean().optional(),\n jobAdded: z.boolean().optional(),\n jobCompleted: z.boolean().optional(),\n queueRegistered: z.boolean().optional(),\n queuesRegistered: z.boolean().optional(),\n queueWaiting: z.boolean().optional(),\n })\n .optional();\n\nexport const QueueJobSchema = z.object({\n id: z.string().min(1),\n maxConcurrency: z.number().int().positive().optional(),\n});\n\nexport const QueueItemSchema = z.object({\n name: z.string(),\n isExternal: z.boolean().optional(),\n jobs: z.array(QueueJobSchema).default([]),\n});\n\nexport const QueueConfigSchema = z.object({\n queues: z.array(QueueItemSchema).default([]),\n processorsDirectory: z.string().min(1, 'queue.processorsDirectory required'),\n log: QueueLogConfigSchema,\n});\n\n// Event configuration schema\nexport const EventDefinitionSchema = z.object({\n name: z.string().min(1),\n});\n\nexport const EventConfigSchema = z.object({\n enabled: z.boolean().default(false),\n controllersDirectory: z.string().min(1),\n events: z.array(EventDefinitionSchema).default([]),\n});\n\n// Log configuration schema\nexport const LogConfigSchema = z\n .object({\n startUp: z.boolean().optional(),\n shutdown: z.boolean().optional(),\n })\n .optional();\n\n// Performance monitoring schema\nexport const PerformanceThresholdsSchema = z\n .object({\n httpMs: z.number().int().positive().optional(),\n dbMs: z.number().int().positive().optional(),\n queueMs: z.number().int().positive().optional(),\n cacheMs: z.number().int().positive().optional(),\n wsMs: z.number().int().positive().optional(),\n })\n .partial();\n\nexport const PerformanceMonitoringSchema = z\n .object({\n enabled: z.boolean().default(false),\n thresholds: PerformanceThresholdsSchema.optional(),\n maxMetricsHistory: z.number().int().positive().optional(),\n logSlowOperations: z.boolean().optional(),\n logAllOperations: z.boolean().optional(),\n monitorHttpRequests: z.boolean().default(true).optional(),\n monitorDatabaseOperations: z.boolean().default(true).optional(),\n monitorWebSocketOperations: z.boolean().default(true).optional(),\n monitorQueueOperations: z.boolean().default(true).optional(),\n monitorCacheOperations: z.boolean().default(true).optional(),\n reportInterval: z.number().int().positive().default(60_000).optional(),\n reportFormat: z.enum(['simple', 'detailed']).default('simple').optional(),\n })\n .partial();\n\n// Auth configuration schema\nexport const AuthConfigSchema = z\n .object({\n jwtSecretKey: z.string().min(1, 'auth.jwtSecretKey required'),\n })\n .optional();\n\n// Security configuration schema\nexport const SecurityConfigSchema = z\n .object({\n helmet: z\n .object({\n enabled: z.boolean().optional(),\n contentSecurityPolicy: z.boolean().optional(),\n crossOriginEmbedderPolicy: z.boolean().optional(),\n crossOriginOpenerPolicy: z.boolean().optional(),\n crossOriginResourcePolicy: z.boolean().optional(),\n dnsPrefetchControl: z.boolean().optional(),\n frameguard: z.boolean().optional(),\n hidePoweredBy: z.boolean().optional(),\n hsts: z.boolean().optional(),\n ieNoOpen: z.boolean().optional(),\n noSniff: z.boolean().optional(),\n originAgentCluster: z.boolean().optional(),\n permittedCrossDomainPolicies: z.boolean().optional(),\n referrerPolicy: z.boolean().optional(),\n xssFilter: z.boolean().optional(),\n })\n .optional(),\n rateLimit: z\n .object({\n enabled: z.boolean().optional(),\n max: z.number().int().positive().optional(),\n timeWindow: z.string().optional(),\n ban: z.number().int().optional(),\n cache: z.number().int().optional(),\n })\n .optional(),\n })\n .optional();\n\n// Web server configuration schema\nexport const WebServerRouteSchema = z\n .object({\n type: z.string().optional(),\n method: z.union([z.string(), z.array(z.string())]).optional(),\n path: z.string(),\n url: z.string().optional(), // Keep for backwards compatibility\n controller: z.unknown().optional(), // Controller class reference\n controllerName: z.string().optional(),\n action: z.string().optional(),\n entityName: z.string().optional(),\n validation: z.unknown().optional(), // Validation schema reference\n handler: z.unknown().optional(),\n schema: z.unknown().optional(),\n })\n .passthrough(); // Allow additional properties to pass through\n\nexport const WebServerConfigSchema = z\n .object({\n enabled: z.boolean().default(false),\n host: z.string().default('0.0.0.0'),\n port: z.number().int().positive().default(3001),\n bodyLimit: z\n .number()\n .int()\n .positive()\n .default(25 * 1024 * 1024), // 25MB default (was 100MB)\n connectionTimeout: z\n .number()\n .int()\n .positive()\n .default(10 * 1000), // 10s default (was 30s)\n routes: z.array(WebServerRouteSchema).default([]),\n controllersDirectory: z.string().optional(), // Controllers directory path\n routesDirectory: z.string().optional(),\n cors: z\n .object({\n enabled: z.boolean().default(false),\n urls: z.array(z.string()).default([]),\n })\n .optional(),\n security: SecurityConfigSchema.optional(),\n debug: z\n .object({\n logAllRegisteredRoutes: z.boolean().optional(),\n })\n .default({})\n .optional(),\n })\n .partial({ cors: true, debug: true, controllersDirectory: true, routesDirectory: true, security: true });\n\n// WebSocket configuration schema\nexport const WebSocketRouteSchema = z.object({\n type: z.string().min(1, 'webSocket.routes.type required'),\n controllerName: z.string().min(1, 'webSocket.routes.controllerName required'),\n action: z.string().min(1, 'webSocket.routes.action required'),\n controller: z.unknown().optional(), // Controller class reference\n});\n\nexport const WebSocketConfigSchema = z\n .object({\n type: z.string().default('native'),\n enabled: z.boolean().default(false),\n routes: z.array(WebSocketRouteSchema).default([]),\n })\n .partial();\n\n// Cluster configuration schema\nexport const ClusterConfigSchema = z\n .object({\n enabled: z.boolean().default(false),\n workers: z.number().int().positive().optional(),\n })\n .partial();\n\n// Top-level framework configuration schema\nexport const FrameworkConfigSchema = z.object({\n name: z.string().min(1, 'name required'),\n instanceId: z.string().default(() => crypto.randomUUID()),\n rootDirectory: z.string().min(1, 'rootDirectory required'),\n cluster: ClusterConfigSchema.optional(),\n redis: RedisConfigSchema,\n cache: z.object({}).optional(),\n database: DatabaseConfigSchema.optional(),\n queue: QueueConfigSchema,\n event: EventConfigSchema.optional(),\n log: LogConfigSchema,\n performanceMonitoring: PerformanceMonitoringSchema.optional(),\n email: z.object({}).optional(),\n auth: AuthConfigSchema,\n web: WebServerConfigSchema.optional(),\n webServer: WebServerConfigSchema.optional(), // Support both 'web' and 'webServer' for compatibility\n webSocket: WebSocketConfigSchema.optional(),\n});\n\nexport type InferFrameworkConfig = z.infer<typeof FrameworkConfigSchema>;\n\nexport interface ValidateConfigOptions {\n collectAllErrors?: boolean; // Reserved for future use; Zod currently throws aggregate anyway\n}\n\nexport interface ValidationIssueDetail {\n path: string;\n message: string;\n}\n\nexport class ConfigValidationError extends Error {\n public issues: ValidationIssueDetail[];\n constructor(message: string, issues: ValidationIssueDetail[]) {\n super(message);\n this.name = 'ConfigValidationError';\n this.issues = issues;\n }\n}\n\nexport function validateFrameworkConfig(raw: unknown, _options: ValidateConfigOptions = {}): InferFrameworkConfig {\n const result = FrameworkConfigSchema.safeParse(raw);\n if (!result.success) {\n const issues: ValidationIssueDetail[] = result.error.issues.map(i => ({\n path: i.path.join('.') || '(root)',\n message: i.message,\n }));\n throw new ConfigValidationError('Invalid framework configuration', issues);\n }\n return result.data;\n}\n\nexport function formatConfigIssues(issues: ValidationIssueDetail[]): string {\n return issues.map(i => ` - ${i.path}: ${i.message}`).join('\\n');\n}\n\nexport default FrameworkConfigSchema;\n"],
|
|
5
|
-
"mappings": ";;AAAA,SAAS,SAAS;AAClB,OAAO,YAAY;AAGZ,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,qBAAqB;AAAA,EAC7C,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,IAAI;AAAA,EAC9C,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AACvC,CAAC;AAGM,MAAM,uBAAuB,EACjC,OAAO;AAAA,EACN,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAClC,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,wBAAwB;AAAA,EAChD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,IAAI;AAAA,EAC9C,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,4BAA4B;AAAA,EACxD,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,4BAA4B;AAAA,EACxD,cAAc,EAAE,OAAO,EAAE,IAAI,GAAG,gCAAgC;AAAA,EAChE,mBAAmB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAChD,CAAC,EACA,QAAQ,EAAE,mBAAmB,KAAK,CAAC;AAG/B,MAAM,uBAAuB,EACjC,OAAO;AAAA,EACN,eAAe,EAAE,QAAQ,EAAE,SAAS;AAAA,EACpC,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC/B,cAAc,EAAE,QAAQ,EAAE,SAAS;AAAA,EACnC,iBAAiB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACtC,kBAAkB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACvC,cAAc,EAAE,QAAQ,EAAE,SAAS;AACrC,CAAC,EACA,SAAS;AAEL,MAAM,iBAAiB,EAAE,OAAO;AAAA,EACrC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AACvD,CAAC;AAEM,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,MAAM,EAAE,OAAO;AAAA,EACf,YAAY,EAAE,QAAQ,EAAE,SAAS;AAAA,EACjC,MAAM,EAAE,MAAM,cAAc,EAAE,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAEM,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,QAAQ,EAAE,MAAM,eAAe,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC3C,qBAAqB,EAAE,OAAO,EAAE,IAAI,GAAG,oCAAoC;AAAA,EAC3E,KAAK;AACP,CAAC;AAGM,MAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAEM,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAClC,sBAAsB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtC,QAAQ,EAAE,MAAM,qBAAqB,EAAE,QAAQ,CAAC,CAAC;AACnD,CAAC;AAGM,MAAM,kBAAkB,EAC5B,OAAO;AAAA,EACN,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC9B,UAAU,EAAE,QAAQ,EAAE,SAAS;AACjC,CAAC,EACA,SAAS;AAGL,MAAM,8BAA8B,EACxC,OAAO;AAAA,EACN,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EAC7C,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAC7C,CAAC,EACA,QAAQ;AAEJ,MAAM,8BAA8B,EACxC,OAAO;AAAA,EACN,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAClC,YAAY,4BAA4B,SAAS;AAAA,EACjD,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,mBAAmB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACxC,kBAAkB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACvC,qBAAqB,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS;AAAA,EACxD,2BAA2B,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS;AAAA,EAC9D,4BAA4B,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS;AAAA,EAC/D,wBAAwB,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS;AAAA,EAC3D,wBAAwB,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS;AAAA,EAC3D,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,GAAM,EAAE,SAAS;AAAA,EACrE,cAAc,EAAE,KAAK,CAAC,UAAU,UAAU,CAAC,EAAE,QAAQ,QAAQ,EAAE,SAAS;AAC1E,CAAC,EACA,QAAQ;AAGJ,MAAM,mBAAmB,EAC7B,OAAO;AAAA,EACN,cAAc,EAAE,OAAO,EAAE,IAAI,GAAG,4BAA4B;AAC9D,CAAC,EACA,SAAS;AAGL,MAAM,uBAAuB,EACjC,OAAO;AAAA,EACN,QAAQ,EACL,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC9B,uBAAuB,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC5C,2BAA2B,EAAE,QAAQ,EAAE,SAAS;AAAA,IAChD,yBAAyB,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC9C,2BAA2B,EAAE,QAAQ,EAAE,SAAS;AAAA,IAChD,oBAAoB,EAAE,QAAQ,EAAE,SAAS;AAAA,IACzC,YAAY,EAAE,QAAQ,EAAE,SAAS;AAAA,IACjC,eAAe,EAAE,QAAQ,EAAE,SAAS;AAAA,IACpC,MAAM,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC3B,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC/B,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC9B,oBAAoB,EAAE,QAAQ,EAAE,SAAS;AAAA,IACzC,8BAA8B,EAAE,QAAQ,EAAE,SAAS;AAAA,IACnD,gBAAgB,EAAE,QAAQ,EAAE,SAAS;AAAA,IACrC,WAAW,EAAE,QAAQ,EAAE,SAAS;AAAA,EAClC,CAAC,EACA,SAAS;AAAA,EACZ,WAAW,EACR,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC9B,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,IAC1C,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,IAChC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,IAC/B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACnC,CAAC,EACA,SAAS;AACd,CAAC,EACA,SAAS;AAGL,MAAM,uBAAuB,EACjC,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,QAAQ,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS;AAAA,EAC5D,MAAM,EAAE,OAAO;AAAA,EACf,KAAK,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EACzB,YAAY,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA,EACjC,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,YAAY,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA,EACjC,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC9B,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAC/B,CAAC,EACA,YAAY;AAER,MAAM,wBAAwB,EAClC,OAAO;AAAA,EACN,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAClC,MAAM,EAAE,OAAO,EAAE,QAAQ,SAAS;AAAA,EAClC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,IAAI;AAAA,EAC9C,WAAW,EACR,OAAO,EACP,IAAI,EACJ,SAAS,EACT,QAAQ,KAAK,OAAO,IAAI;AAAA;AAAA,EAC3B,mBAAmB,EAChB,OAAO,EACP,IAAI,EACJ,SAAS,EACT,QAAQ,KAAK,GAAI;AAAA;AAAA,EACpB,QAAQ,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAChD,sBAAsB,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAC1C,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,MAAM,EACH,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAClC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EACtC,CAAC,EACA,SAAS;AAAA,EACZ,UAAU,qBAAqB,SAAS;AAAA,EACxC,OAAO,EACJ,OAAO;AAAA,IACN,wBAAwB,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC/C,CAAC,EACA,QAAQ,CAAC,CAAC,EACV,SAAS;AACd,CAAC,EACA,QAAQ,EAAE,MAAM,MAAM,OAAO,MAAM,sBAAsB,MAAM,iBAAiB,MAAM,UAAU,KAAK,CAAC;AAGlG,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,gCAAgC;AAAA,EACxD,gBAAgB,EAAE,OAAO,EAAE,IAAI,GAAG,0CAA0C;AAAA,EAC5E,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,kCAAkC;AAAA,EAC5D,YAAY,EAAE,QAAQ,EAAE,SAAS;AAAA;AACnC,CAAC;AAEM,MAAM,wBAAwB,EAClC,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,EAAE,QAAQ,QAAQ;AAAA,EACjC,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAClC,QAAQ,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod';\nimport crypto from 'node:crypto';\n\n// Redis configuration schema\nexport const RedisConfigSchema = z.object({\n host: z.string().min(1, 'redis.host required'),\n port: z.number().int().positive().default(6379),\n password: z.string().min(1).optional(),\n});\n\n// Database configuration schema\nexport const DatabaseConfigSchema = z\n .object({\n enabled: z.boolean().default(false),\n host: z.string().min(1, 'database.host required'),\n port: z.number().int().positive().default(5432),\n username: z.string().min(1, 'database.username required'),\n password: z.string().min(1, 'database.password required'),\n databaseName: z.string().min(1, 'database.databaseName required'),\n entitiesDirectory: z.string().min(1).optional(),\n })\n .partial({ entitiesDirectory: true });\n\n// Queue configuration schema\nexport const QueueLogConfigSchema = z\n .object({\n jobRegistered: z.boolean().optional(),\n jobAdded: z.boolean().optional(),\n jobCompleted: z.boolean().optional(),\n queueRegistered: z.boolean().optional(),\n queuesRegistered: z.boolean().optional(),\n queueWaiting: z.boolean().optional(),\n })\n .optional();\n\nexport const QueueJobSchema = z.object({\n id: z.string().min(1),\n maxConcurrency: z.number().int().positive().optional(),\n});\n\nexport const QueueItemSchema = z.object({\n name: z.string(),\n isExternal: z.boolean().optional(),\n jobs: z.array(QueueJobSchema).default([]),\n});\n\nexport const QueueConfigSchema = z.object({\n queues: z.array(QueueItemSchema).default([]),\n processorsDirectory: z.string().min(1, 'queue.processorsDirectory required'),\n log: QueueLogConfigSchema,\n});\n\n// Event configuration schema\nexport const EventDefinitionSchema = z.object({\n name: z.string().min(1),\n});\n\nexport const EventConfigSchema = z.object({\n enabled: z.boolean().default(false),\n controllersDirectory: z.string().min(1),\n events: z.array(EventDefinitionSchema).default([]),\n});\n\n// Log configuration schema\nexport const LogConfigSchema = z\n .object({\n startUp: z.boolean().optional(),\n shutdown: z.boolean().optional(),\n })\n .optional();\n\n// Performance monitoring schema\nexport const PerformanceThresholdsSchema = z\n .object({\n httpMs: z.number().int().positive().optional(),\n dbMs: z.number().int().positive().optional(),\n queueMs: z.number().int().positive().optional(),\n cacheMs: z.number().int().positive().optional(),\n wsMs: z.number().int().positive().optional(),\n })\n .partial();\n\nexport const PerformanceMonitoringSchema = z\n .object({\n enabled: z.boolean().default(false),\n thresholds: PerformanceThresholdsSchema.optional(),\n maxMetricsHistory: z.number().int().positive().optional(),\n logSlowOperations: z.boolean().optional(),\n logAllOperations: z.boolean().optional(),\n monitorHttpRequests: z.boolean().default(true).optional(),\n monitorDatabaseOperations: z.boolean().default(true).optional(),\n monitorWebSocketOperations: z.boolean().default(true).optional(),\n monitorQueueOperations: z.boolean().default(true).optional(),\n monitorCacheOperations: z.boolean().default(true).optional(),\n reportInterval: z.number().int().positive().default(60_000).optional(),\n reportFormat: z.enum(['simple', 'detailed']).default('simple').optional(),\n })\n .partial();\n\n// Auth configuration schema\nexport const AuthConfigSchema = z\n .object({\n jwtSecretKey: z.string().min(1, 'auth.jwtSecretKey required'),\n })\n .optional();\n\n// Security configuration schema\nexport const SecurityConfigSchema = z\n .object({\n helmet: z\n .object({\n enabled: z.boolean().optional(),\n contentSecurityPolicy: z.boolean().optional(),\n crossOriginEmbedderPolicy: z.boolean().optional(),\n crossOriginOpenerPolicy: z.boolean().optional(),\n crossOriginResourcePolicy: z.boolean().optional(),\n dnsPrefetchControl: z.boolean().optional(),\n frameguard: z.boolean().optional(),\n hidePoweredBy: z.boolean().optional(),\n hsts: z.boolean().optional(),\n ieNoOpen: z.boolean().optional(),\n noSniff: z.boolean().optional(),\n originAgentCluster: z.boolean().optional(),\n permittedCrossDomainPolicies: z.boolean().optional(),\n referrerPolicy: z.boolean().optional(),\n xssFilter: z.boolean().optional(),\n })\n .optional(),\n rateLimit: z\n .object({\n enabled: z.boolean().optional(),\n max: z.number().int().positive().optional(),\n timeWindow: z.string().optional(),\n ban: z.number().int().optional(),\n cache: z.number().int().optional(),\n })\n .optional(),\n })\n .optional();\n\n// Web server configuration schema\nexport const WebServerRouteSchema = z\n .object({\n type: z.string().optional(),\n method: z.union([z.string(), z.array(z.string())]).optional(),\n path: z.string(),\n url: z.string().optional(), // Keep for backwards compatibility\n controller: z.unknown().optional(), // Controller class reference\n controllerName: z.string().optional(),\n action: z.string().optional(),\n entityName: z.string().optional(),\n validation: z.unknown().optional(), // Validation schema reference\n handler: z.unknown().optional(),\n schema: z.unknown().optional(),\n })\n .passthrough(); // Allow additional properties to pass through\n\nexport const WebServerConfigSchema = z\n .object({\n enabled: z.boolean().default(false),\n host: z.string().default('0.0.0.0'),\n port: z.number().int().positive().default(3001),\n bodyLimit: z\n .number()\n .int()\n .positive()\n .default(25 * 1024 * 1024), // 25MB default (was 100MB)\n connectionTimeout: z\n .number()\n .int()\n .positive()\n .default(10 * 1000), // 10s default (was 30s)\n routes: z.array(WebServerRouteSchema).default([]),\n controllersDirectory: z.string().optional(), // Controllers directory path\n routesDirectory: z.string().optional(),\n cors: z\n .object({\n enabled: z.boolean().default(false),\n urls: z.array(z.string()).default([]),\n })\n .optional(),\n security: SecurityConfigSchema.optional(),\n debug: z\n .object({\n logAllRegisteredRoutes: z.boolean().optional(),\n })\n .default({})\n .optional(),\n })\n .partial({ cors: true, debug: true, controllersDirectory: true, routesDirectory: true, security: true });\n\n// WebSocket configuration schema\nexport const WebSocketRouteSchema = z.object({\n type: z.string().min(1, 'webSocket.routes.type required'),\n controllerName: z.string().min(1, 'webSocket.routes.controllerName required'),\n action: z.string().min(1, 'webSocket.routes.action required'),\n controller: z.unknown().optional(), // Controller class reference\n});\n\nexport const WebSocketConfigSchema = z\n .object({\n type: z.string().default('native'),\n enabled: z.boolean().default(false),\n routes: z.array(WebSocketRouteSchema).default([]),\n subscriberHandlers: z\n .object({\n directory: z.string().optional(),\n handlers: z.array(z.any()).optional(),\n })\n .optional(),\n })\n .partial();\n\n// Cluster configuration schema\nexport const ClusterConfigSchema = z\n .object({\n enabled: z.boolean().default(false),\n workers: z.number().int().positive().optional(),\n })\n .partial();\n\n// Top-level framework configuration schema\nexport const FrameworkConfigSchema = z.object({\n name: z.string().min(1, 'name required'),\n instanceId: z.string().default(() => crypto.randomUUID()),\n rootDirectory: z.string().min(1, 'rootDirectory required'),\n cluster: ClusterConfigSchema.optional(),\n redis: RedisConfigSchema,\n cache: z.object({}).optional(),\n database: DatabaseConfigSchema.optional(),\n queue: QueueConfigSchema,\n event: EventConfigSchema.optional(),\n log: LogConfigSchema,\n performanceMonitoring: PerformanceMonitoringSchema.optional(),\n email: z.object({}).optional(),\n auth: AuthConfigSchema,\n web: WebServerConfigSchema.optional(),\n webServer: WebServerConfigSchema.optional(), // Support both 'web' and 'webServer' for compatibility\n webSocket: WebSocketConfigSchema.optional(),\n});\n\nexport type InferFrameworkConfig = z.infer<typeof FrameworkConfigSchema>;\n\nexport interface ValidateConfigOptions {\n collectAllErrors?: boolean; // Reserved for future use; Zod currently throws aggregate anyway\n}\n\nexport interface ValidationIssueDetail {\n path: string;\n message: string;\n}\n\nexport class ConfigValidationError extends Error {\n public issues: ValidationIssueDetail[];\n constructor(message: string, issues: ValidationIssueDetail[]) {\n super(message);\n this.name = 'ConfigValidationError';\n this.issues = issues;\n }\n}\n\nexport function validateFrameworkConfig(raw: unknown, _options: ValidateConfigOptions = {}): InferFrameworkConfig {\n const result = FrameworkConfigSchema.safeParse(raw);\n if (!result.success) {\n const issues: ValidationIssueDetail[] = result.error.issues.map(i => ({\n path: i.path.join('.') || '(root)',\n message: i.message,\n }));\n throw new ConfigValidationError('Invalid framework configuration', issues);\n }\n return result.data;\n}\n\nexport function formatConfigIssues(issues: ValidationIssueDetail[]): string {\n return issues.map(i => ` - ${i.path}: ${i.message}`).join('\\n');\n}\n\nexport default FrameworkConfigSchema;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { WebSocketSubscriberDefinition, WebSocketSubscriberHandler, WebSocketSubscriberMatcher } from './websocket.interface.js';
|
|
2
|
+
type ChannelConfig = {
|
|
3
|
+
channel: string;
|
|
4
|
+
channels?: never;
|
|
5
|
+
match?: never;
|
|
6
|
+
};
|
|
7
|
+
type ChannelsConfig = {
|
|
8
|
+
channels: string[];
|
|
9
|
+
channel?: never;
|
|
10
|
+
match?: never;
|
|
11
|
+
};
|
|
12
|
+
type MatchConfig = {
|
|
13
|
+
match: WebSocketSubscriberMatcher | WebSocketSubscriberMatcher[];
|
|
14
|
+
channel?: never;
|
|
15
|
+
channels?: never;
|
|
16
|
+
};
|
|
17
|
+
type BaseConfig = {
|
|
18
|
+
name?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
priority?: number;
|
|
21
|
+
handle: WebSocketSubscriberHandler;
|
|
22
|
+
};
|
|
23
|
+
export type DefineWebSocketSubscriberConfig = BaseConfig & (ChannelConfig | ChannelsConfig | MatchConfig);
|
|
24
|
+
export declare function defineWebSocketSubscriber(config: DefineWebSocketSubscriberConfig): WebSocketSubscriberDefinition;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=define-subscriber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-subscriber.d.ts","sourceRoot":"","sources":["../../src/websocket/define-subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,0BAA0B,EAC1B,0BAA0B,EAC3B,MAAM,0BAA0B,CAAC;AAElC,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,KAAK,EAAE,0BAA0B,GAAG,0BAA0B,EAAE,CAAC;IACjE,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,0BAA0B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,CAAC,aAAa,GAAG,cAAc,GAAG,WAAW,CAAC,CAAC;AAE1G,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,+BAA+B,GAAG,6BAA6B,CA4BhH"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
function defineWebSocketSubscriber(config) {
|
|
4
|
+
let matchers = [];
|
|
5
|
+
if ("match" in config) {
|
|
6
|
+
const matchConfig = config;
|
|
7
|
+
matchers = Array.isArray(matchConfig.match) ? [...matchConfig.match] : [matchConfig.match];
|
|
8
|
+
}
|
|
9
|
+
let channels = [];
|
|
10
|
+
if ("channel" in config) {
|
|
11
|
+
const channelConfig = config;
|
|
12
|
+
channels = [channelConfig.channel];
|
|
13
|
+
} else if ("channels" in config) {
|
|
14
|
+
const channelsConfig = config;
|
|
15
|
+
channels = [...channelsConfig.channels];
|
|
16
|
+
}
|
|
17
|
+
if (channels.length === 0 && matchers.length === 0) {
|
|
18
|
+
throw new Error("defineWebSocketSubscriber requires either channel(s) or a match function.");
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
name: config.name,
|
|
22
|
+
description: config.description,
|
|
23
|
+
priority: config.priority,
|
|
24
|
+
channels,
|
|
25
|
+
matchers,
|
|
26
|
+
handle: config.handle
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
__name(defineWebSocketSubscriber, "defineWebSocketSubscriber");
|
|
30
|
+
export {
|
|
31
|
+
defineWebSocketSubscriber
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=define-subscriber.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/websocket/define-subscriber.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n WebSocketSubscriberDefinition,\n WebSocketSubscriberHandler,\n WebSocketSubscriberMatcher,\n} from './websocket.interface.js';\n\ntype ChannelConfig = {\n channel: string;\n channels?: never;\n match?: never;\n};\n\ntype ChannelsConfig = {\n channels: string[];\n channel?: never;\n match?: never;\n};\n\ntype MatchConfig = {\n match: WebSocketSubscriberMatcher | WebSocketSubscriberMatcher[];\n channel?: never;\n channels?: never;\n};\n\ntype BaseConfig = {\n name?: string;\n description?: string;\n priority?: number;\n handle: WebSocketSubscriberHandler;\n};\n\nexport type DefineWebSocketSubscriberConfig = BaseConfig & (ChannelConfig | ChannelsConfig | MatchConfig);\n\nexport function defineWebSocketSubscriber(config: DefineWebSocketSubscriberConfig): WebSocketSubscriberDefinition {\n let matchers: WebSocketSubscriberMatcher[] = [];\n if ('match' in config) {\n const matchConfig = config as MatchConfig & BaseConfig;\n matchers = Array.isArray(matchConfig.match) ? [...matchConfig.match] : [matchConfig.match];\n }\n\n let channels: string[] = [];\n if ('channel' in config) {\n const channelConfig = config as ChannelConfig & BaseConfig;\n channels = [channelConfig.channel];\n } else if ('channels' in config) {\n const channelsConfig = config as ChannelsConfig & BaseConfig;\n channels = [...channelsConfig.channels];\n }\n\n if (channels.length === 0 && matchers.length === 0) {\n throw new Error('defineWebSocketSubscriber requires either channel(s) or a match function.');\n }\n\n return {\n name: config.name,\n description: config.description,\n priority: config.priority,\n channels,\n matchers,\n handle: config.handle,\n };\n}\n"],
|
|
5
|
+
"mappings": ";;AAiCO,SAAS,0BAA0B,QAAwE;AAChH,MAAI,WAAyC,CAAC;AAC9C,MAAI,WAAW,QAAQ;AACrB,UAAM,cAAc;AACpB,eAAW,MAAM,QAAQ,YAAY,KAAK,IAAI,CAAC,GAAG,YAAY,KAAK,IAAI,CAAC,YAAY,KAAK;AAAA,EAC3F;AAEA,MAAI,WAAqB,CAAC;AAC1B,MAAI,aAAa,QAAQ;AACvB,UAAM,gBAAgB;AACtB,eAAW,CAAC,cAAc,OAAO;AAAA,EACnC,WAAW,cAAc,QAAQ;AAC/B,UAAM,iBAAiB;AACvB,eAAW,CAAC,GAAG,eAAe,QAAQ;AAAA,EACxC;AAEA,MAAI,SAAS,WAAW,KAAK,SAAS,WAAW,GAAG;AAClD,UAAM,IAAI,MAAM,2EAA2E;AAAA,EAC7F;AAEA,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,aAAa,OAAO;AAAA,IACpB,UAAU,OAAO;AAAA,IACjB;AAAA,IACA;AAAA,IACA,QAAQ,OAAO;AAAA,EACjB;AACF;AA5BgB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export type { WebSocketRoute } from './websocket.interface.js';
|
|
2
2
|
export { WebSocketRedisSubscriberEvent } from './websocket.interface.js';
|
|
3
|
+
export type { WebSocketSubscriberDefinition, WebSocketSubscriberHandler, WebSocketSubscriberHandlerContext, WebSocketSubscriberMatcher, WebSocketSubscriberHandlersConfig, } from './websocket.interface.js';
|
|
3
4
|
export { default as WebSocketServerBaseController } from './controller/server/base.js';
|
|
4
5
|
export { default as WebSocketClientBaseController } from './controller/client/base.js';
|
|
5
6
|
export { WebSocketService } from './websocket-service.js';
|
|
6
7
|
export type { WebSocketMessage, WebSocketServiceOptions } from './websocket-service.js';
|
|
7
8
|
export { WebSocketAuthService } from './websocket-auth.js';
|
|
8
9
|
export type { WebSocketAuthResult } from './websocket-auth.js';
|
|
10
|
+
export { defineWebSocketSubscriber } from './define-subscriber.js';
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/websocket/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/websocket/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACzE,YAAY,EACV,6BAA6B,EAC7B,0BAA0B,EAC1B,iCAAiC,EACjC,0BAA0B,EAC1B,iCAAiC,GAClC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,OAAO,IAAI,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/websocket/index.js
CHANGED
|
@@ -3,11 +3,13 @@ import { default as default2 } from "./controller/server/base.js";
|
|
|
3
3
|
import { default as default3 } from "./controller/client/base.js";
|
|
4
4
|
import { WebSocketService } from "./websocket-service.js";
|
|
5
5
|
import { WebSocketAuthService } from "./websocket-auth.js";
|
|
6
|
+
import { defineWebSocketSubscriber } from "./define-subscriber.js";
|
|
6
7
|
export {
|
|
7
8
|
WebSocketAuthService,
|
|
8
9
|
default3 as WebSocketClientBaseController,
|
|
9
10
|
WebSocketRedisSubscriberEvent,
|
|
10
11
|
default2 as WebSocketServerBaseController,
|
|
11
|
-
WebSocketService
|
|
12
|
+
WebSocketService,
|
|
13
|
+
defineWebSocketSubscriber
|
|
12
14
|
};
|
|
13
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/websocket/index.ts"],
|
|
4
|
-
"sourcesContent": ["export type { WebSocketRoute } from './websocket.interface.js';\nexport { WebSocketRedisSubscriberEvent } from './websocket.interface.js';\nexport { default as WebSocketServerBaseController } from './controller/server/base.js';\nexport { default as WebSocketClientBaseController } from './controller/client/base.js';\nexport { WebSocketService } from './websocket-service.js';\nexport type { WebSocketMessage, WebSocketServiceOptions } from './websocket-service.js';\nexport { WebSocketAuthService } from './websocket-auth.js';\nexport type { WebSocketAuthResult } from './websocket-auth.js';\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,qCAAqC;
|
|
4
|
+
"sourcesContent": ["export type { WebSocketRoute } from './websocket.interface.js';\nexport { WebSocketRedisSubscriberEvent } from './websocket.interface.js';\nexport type {\n WebSocketSubscriberDefinition,\n WebSocketSubscriberHandler,\n WebSocketSubscriberHandlerContext,\n WebSocketSubscriberMatcher,\n WebSocketSubscriberHandlersConfig,\n} from './websocket.interface.js';\nexport { default as WebSocketServerBaseController } from './controller/server/base.js';\nexport { default as WebSocketClientBaseController } from './controller/client/base.js';\nexport { WebSocketService } from './websocket-service.js';\nexport type { WebSocketMessage, WebSocketServiceOptions } from './websocket-service.js';\nexport { WebSocketAuthService } from './websocket-auth.js';\nexport type { WebSocketAuthResult } from './websocket-auth.js';\nexport { defineWebSocketSubscriber } from './define-subscriber.js';\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qCAAqC;AAQ9C,SAAoB,WAAXA,gBAAgD;AACzD,SAAoB,WAAXA,gBAAgD;AACzD,SAAS,wBAAwB;AAEjC,SAAS,4BAA4B;AAErC,SAAS,iCAAiC;",
|
|
6
6
|
"names": ["default"]
|
|
7
7
|
}
|
|
@@ -22,12 +22,24 @@ export default class WebSocketServer extends WebSocketBase {
|
|
|
22
22
|
private redisInstance;
|
|
23
23
|
private queueManager;
|
|
24
24
|
private databaseInstance;
|
|
25
|
+
private subscriberHandlersByChannel;
|
|
26
|
+
private subscriberMatcherHandlers;
|
|
27
|
+
private wildcardSubscriberHandlers;
|
|
25
28
|
/** Redis subscriber events */
|
|
26
29
|
private redisSubscriberEvents;
|
|
27
30
|
constructor(props: WebSocketServerProps);
|
|
28
31
|
get type(): WebSocketType;
|
|
29
32
|
private validateWebSocketAuth;
|
|
30
33
|
load(): Promise<void>;
|
|
34
|
+
private loadSubscriberHandlers;
|
|
35
|
+
private normalizeSubscriberExport;
|
|
36
|
+
private normalizeSubscriberObject;
|
|
37
|
+
private normalizeChannels;
|
|
38
|
+
private normalizeMatchers;
|
|
39
|
+
private registerSubscriberDefinition;
|
|
40
|
+
private doesDefinitionMatch;
|
|
41
|
+
private evaluateMatcher;
|
|
42
|
+
private executeSubscriberHandlers;
|
|
31
43
|
start({ fastifyServer }: {
|
|
32
44
|
fastifyServer: FastifyInstance;
|
|
33
45
|
}): Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/websocket/websocket-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,eAAe,IAAI,EAAE,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpE,OAAO,EAGL,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/websocket/websocket-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,7 @@ import { baseDir } from "../index.js";
|
|
|
13
13
|
import WebSocketRoomManager from "./websocket-room-manager.js";
|
|
14
14
|
import logger from "../logger/logger.js";
|
|
15
15
|
import { WebSocketAuthService } from "./websocket-auth.js";
|
|
16
|
+
import { File, Loader } from "../util/index.js";
|
|
16
17
|
class WebSocketServer extends WebSocketBase {
|
|
17
18
|
static {
|
|
18
19
|
__name(this, "WebSocketServer");
|
|
@@ -46,6 +47,9 @@ class WebSocketServer extends WebSocketBase {
|
|
|
46
47
|
redisInstance;
|
|
47
48
|
queueManager;
|
|
48
49
|
databaseInstance;
|
|
50
|
+
subscriberHandlersByChannel = /* @__PURE__ */ new Map();
|
|
51
|
+
subscriberMatcherHandlers = [];
|
|
52
|
+
wildcardSubscriberHandlers = [];
|
|
49
53
|
/** Redis subscriber events */
|
|
50
54
|
redisSubscriberEvents = [
|
|
51
55
|
WebSocketRedisSubscriberEvent.ClientConnected,
|
|
@@ -82,6 +86,260 @@ class WebSocketServer extends WebSocketBase {
|
|
|
82
86
|
const libraryControllersDirectory = path.join(baseDir, "websocket", "controllers", "server");
|
|
83
87
|
await this.configureRoutes(this.defaultRoutes, libraryControllersDirectory);
|
|
84
88
|
await this.configureRoutes(this.routes, this.options.controllersDirectory);
|
|
89
|
+
await this.loadSubscriberHandlers();
|
|
90
|
+
}
|
|
91
|
+
async loadSubscriberHandlers() {
|
|
92
|
+
this.subscriberHandlersByChannel.clear();
|
|
93
|
+
this.subscriberMatcherHandlers = [];
|
|
94
|
+
this.wildcardSubscriberHandlers = [];
|
|
95
|
+
const config = this.options.subscriberHandlers;
|
|
96
|
+
if (!config) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const definitions = [];
|
|
100
|
+
if (config.directory) {
|
|
101
|
+
const directoryExists = await File.pathExists(config.directory);
|
|
102
|
+
if (!directoryExists) {
|
|
103
|
+
logger.warn("WebSocket subscriber handlers directory not found", {
|
|
104
|
+
Directory: config.directory
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
const modules = await Loader.loadModulesInDirectory({
|
|
108
|
+
directory: config.directory,
|
|
109
|
+
extensions: [".ts", ".js"]
|
|
110
|
+
});
|
|
111
|
+
for (const [moduleName, moduleExport] of Object.entries(modules)) {
|
|
112
|
+
definitions.push(
|
|
113
|
+
...this.normalizeSubscriberExport(moduleExport, {
|
|
114
|
+
source: "file",
|
|
115
|
+
moduleName,
|
|
116
|
+
directory: config.directory
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(config.handlers)) {
|
|
123
|
+
for (const handler of config.handlers) {
|
|
124
|
+
definitions.push(
|
|
125
|
+
...this.normalizeSubscriberExport(handler, {
|
|
126
|
+
source: "inline"
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const definition of definitions) {
|
|
132
|
+
this.registerSubscriberDefinition(definition);
|
|
133
|
+
}
|
|
134
|
+
if (definitions.length > 0) {
|
|
135
|
+
logger.info("WebSocket subscriber handlers loaded", {
|
|
136
|
+
Channels: Array.from(this.subscriberHandlersByChannel.keys()),
|
|
137
|
+
Matchers: this.subscriberMatcherHandlers.length,
|
|
138
|
+
Wildcards: this.wildcardSubscriberHandlers.length
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
normalizeSubscriberExport(value, metadata) {
|
|
143
|
+
if (!value) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
if (Array.isArray(value)) {
|
|
147
|
+
return value.flatMap((entry) => this.normalizeSubscriberExport(entry, metadata));
|
|
148
|
+
}
|
|
149
|
+
if (typeof value === "function") {
|
|
150
|
+
const moduleChannel = metadata.moduleName ?? value.name ?? "anonymous";
|
|
151
|
+
return [
|
|
152
|
+
{
|
|
153
|
+
name: metadata.moduleName ?? value.name,
|
|
154
|
+
channels: [moduleChannel],
|
|
155
|
+
handle: /* @__PURE__ */ __name(async (context) => {
|
|
156
|
+
await value(context);
|
|
157
|
+
}, "handle")
|
|
158
|
+
}
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
if (typeof value === "object") {
|
|
162
|
+
const definition = this.normalizeSubscriberObject(value, metadata);
|
|
163
|
+
if (definition) {
|
|
164
|
+
return [definition];
|
|
165
|
+
}
|
|
166
|
+
return Object.values(value).flatMap(
|
|
167
|
+
(entry) => this.normalizeSubscriberExport(entry, metadata)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
normalizeSubscriberObject(value, metadata) {
|
|
173
|
+
const handle = value.handle;
|
|
174
|
+
if (typeof handle !== "function") {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
let channelSource;
|
|
178
|
+
if (Object.prototype.hasOwnProperty.call(value, "channel")) {
|
|
179
|
+
channelSource = value.channel;
|
|
180
|
+
} else if (Object.prototype.hasOwnProperty.call(value, "channels")) {
|
|
181
|
+
channelSource = value.channels;
|
|
182
|
+
}
|
|
183
|
+
const channels = this.normalizeChannels(channelSource);
|
|
184
|
+
let matcherSource;
|
|
185
|
+
if (Object.prototype.hasOwnProperty.call(value, "match")) {
|
|
186
|
+
matcherSource = value.match;
|
|
187
|
+
} else if (Object.prototype.hasOwnProperty.call(value, "matcher")) {
|
|
188
|
+
matcherSource = value.matcher;
|
|
189
|
+
} else if (Object.prototype.hasOwnProperty.call(value, "matchers")) {
|
|
190
|
+
matcherSource = value.matchers;
|
|
191
|
+
}
|
|
192
|
+
const matchers = this.normalizeMatchers(matcherSource);
|
|
193
|
+
if (channels.length === 0 && matchers.length === 0) {
|
|
194
|
+
logger.warn("Skipping WebSocket subscriber handler without channels or matchers", {
|
|
195
|
+
Source: metadata.source,
|
|
196
|
+
Module: metadata.moduleName
|
|
197
|
+
});
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const definition = {
|
|
201
|
+
name: typeof value.name === "string" ? value.name : metadata.moduleName,
|
|
202
|
+
description: typeof value.description === "string" ? value.description : void 0,
|
|
203
|
+
priority: typeof value.priority === "number" ? value.priority : void 0,
|
|
204
|
+
channels,
|
|
205
|
+
matchers,
|
|
206
|
+
handle
|
|
207
|
+
};
|
|
208
|
+
return definition;
|
|
209
|
+
}
|
|
210
|
+
normalizeChannels(input) {
|
|
211
|
+
if (typeof input === "string") {
|
|
212
|
+
const trimmed = input.trim();
|
|
213
|
+
return trimmed.length > 0 ? [trimmed] : [];
|
|
214
|
+
}
|
|
215
|
+
if (Array.isArray(input)) {
|
|
216
|
+
return input.filter((channel) => typeof channel === "string").map((channel) => channel.trim()).filter((channel) => channel.length > 0);
|
|
217
|
+
}
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
normalizeMatchers(input) {
|
|
221
|
+
if (!input) {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
const matchers = [];
|
|
225
|
+
const addMatcher = /* @__PURE__ */ __name((matcher) => {
|
|
226
|
+
if (matcher instanceof RegExp || typeof matcher === "function") {
|
|
227
|
+
matchers.push(matcher);
|
|
228
|
+
} else if (typeof matcher === "string") {
|
|
229
|
+
const trimmed = matcher.trim();
|
|
230
|
+
if (trimmed.length > 0) {
|
|
231
|
+
matchers.push(trimmed);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}, "addMatcher");
|
|
235
|
+
if (Array.isArray(input)) {
|
|
236
|
+
input.forEach(addMatcher);
|
|
237
|
+
} else {
|
|
238
|
+
addMatcher(input);
|
|
239
|
+
}
|
|
240
|
+
return matchers;
|
|
241
|
+
}
|
|
242
|
+
registerSubscriberDefinition(definition) {
|
|
243
|
+
const normalizedPriority = definition.priority ?? 0;
|
|
244
|
+
const channels = definition.channels?.map((channel) => channel.trim()).filter(Boolean) ?? [];
|
|
245
|
+
const normalizedDefinition = {
|
|
246
|
+
...definition,
|
|
247
|
+
priority: normalizedPriority,
|
|
248
|
+
channels,
|
|
249
|
+
matchers: definition.matchers ?? []
|
|
250
|
+
};
|
|
251
|
+
if (channels.length === 0 && normalizedDefinition.matchers?.length === 0) {
|
|
252
|
+
logger.warn("Skipping WebSocket subscriber handler registration due to missing filters", {
|
|
253
|
+
Handler: definition.name ?? "(anonymous)"
|
|
254
|
+
});
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (channels.length > 0) {
|
|
258
|
+
for (const channel of channels) {
|
|
259
|
+
if (channel === "*") {
|
|
260
|
+
this.wildcardSubscriberHandlers.push(normalizedDefinition);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const channelHandlers = this.subscriberHandlersByChannel.get(channel) ?? [];
|
|
264
|
+
channelHandlers.push(normalizedDefinition);
|
|
265
|
+
this.subscriberHandlersByChannel.set(channel, channelHandlers);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (normalizedDefinition.matchers && normalizedDefinition.matchers.length > 0) {
|
|
269
|
+
this.subscriberMatcherHandlers.push(normalizedDefinition);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
doesDefinitionMatch(definition, context) {
|
|
273
|
+
if (!definition.matchers || definition.matchers.length === 0) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
return definition.matchers.some((matcher) => this.evaluateMatcher(matcher, context));
|
|
277
|
+
}
|
|
278
|
+
evaluateMatcher(matcher, context) {
|
|
279
|
+
if (typeof matcher === "string") {
|
|
280
|
+
if (matcher === "*") {
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
return matcher === context.channel;
|
|
284
|
+
}
|
|
285
|
+
if (matcher instanceof RegExp) {
|
|
286
|
+
return matcher.test(context.channel);
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
return Boolean(matcher(context));
|
|
290
|
+
} catch (error) {
|
|
291
|
+
logger.error({
|
|
292
|
+
error,
|
|
293
|
+
message: "WebSocket subscriber matcher threw an error",
|
|
294
|
+
meta: {
|
|
295
|
+
Channel: context.channel,
|
|
296
|
+
Matcher: String(matcher)
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async executeSubscriberHandlers(channel, message) {
|
|
303
|
+
if (this.subscriberHandlersByChannel.size === 0 && this.subscriberMatcherHandlers.length === 0 && this.wildcardSubscriberHandlers.length === 0) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const context = {
|
|
307
|
+
channel,
|
|
308
|
+
message,
|
|
309
|
+
webSocketServer: this,
|
|
310
|
+
databaseInstance: this.databaseInstance,
|
|
311
|
+
redisInstance: this.redisInstance,
|
|
312
|
+
queueManager: this.queueManager
|
|
313
|
+
};
|
|
314
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
315
|
+
const channelHandlers = this.subscriberHandlersByChannel.get(channel);
|
|
316
|
+
if (channelHandlers) {
|
|
317
|
+
channelHandlers.forEach((handler) => candidates.add(handler));
|
|
318
|
+
}
|
|
319
|
+
this.wildcardSubscriberHandlers.forEach((handler) => candidates.add(handler));
|
|
320
|
+
for (const definition of this.subscriberMatcherHandlers) {
|
|
321
|
+
if (this.doesDefinitionMatch(definition, context)) {
|
|
322
|
+
candidates.add(definition);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (candidates.size === 0) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const orderedHandlers = Array.from(candidates).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
329
|
+
for (const handler of orderedHandlers) {
|
|
330
|
+
try {
|
|
331
|
+
await handler.handle(context);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
logger.error({
|
|
334
|
+
error,
|
|
335
|
+
message: "WebSocket subscriber handler failed",
|
|
336
|
+
meta: {
|
|
337
|
+
Handler: handler.name ?? "(anonymous)",
|
|
338
|
+
Channel: channel
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
85
343
|
}
|
|
86
344
|
async start({ fastifyServer }) {
|
|
87
345
|
return new Promise((resolve) => {
|
|
@@ -280,14 +538,7 @@ class WebSocketServer extends WebSocketBase {
|
|
|
280
538
|
});
|
|
281
539
|
}
|
|
282
540
|
}
|
|
283
|
-
|
|
284
|
-
this.applicationConfig.webSocket.subscriberEventHandler({
|
|
285
|
-
channel,
|
|
286
|
-
message: parsedMessage,
|
|
287
|
-
webSocketServer: this,
|
|
288
|
-
databaseInstance: this.databaseInstance
|
|
289
|
-
});
|
|
290
|
-
}
|
|
541
|
+
await this.executeSubscriberHandlers(channel, parsedMessage);
|
|
291
542
|
}, "handleSubscriberMessage");
|
|
292
543
|
handleServerError = /* @__PURE__ */ __name((error) => {
|
|
293
544
|
Logger.error({ error });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/websocket/websocket-server.ts"],
|
|
4
|
-
"sourcesContent": ["import { type RawData, WebSocketServer as WS, WebSocket } from 'ws';\nimport {\n type WebSocketOptions,\n WebSocketRedisSubscriberEvent,\n type WebSocketRoute,\n type WebSocketType,\n} from './websocket.interface.js';\nimport type RedisInstance from '../redis/instance.js';\nimport type QueueManager from '../queue/manager.js';\nimport type DatabaseInstance from '../database/instance.js';\nimport type { WebSocketServerProps } from './websocket-server.interface.js';\nimport WebSocketClientManager from './websocket-client-manager.js';\nimport { generateClientId, log } from './utils.js';\nimport WebSocketBase from './websocket-base.js';\nimport { Logger } from '../logger/index.js';\nimport path from 'path';\nimport { type WebApplicationConfig, baseDir } from '../index.js';\nimport WebSocketRoomManager from './websocket-room-manager.js';\nimport logger from '../logger/logger.js';\nimport type { FastifyInstance } from 'fastify';\nimport { WebSocketAuthService } from './websocket-auth.js';\n\nexport default class WebSocketServer extends WebSocketBase {\n protected defaultRoutes: WebSocketRoute[] = [\n {\n type: 'system',\n action: 'joinRoom',\n controllerName: 'system',\n },\n {\n type: 'system',\n action: 'leaveRoom',\n controllerName: 'system',\n },\n ];\n\n private server?: WS;\n\n private abortController = new AbortController();\n private workerId: number | null;\n private uniqueInstanceId: string;\n private applicationConfig: WebApplicationConfig;\n private options: WebSocketOptions;\n public clientManager = new WebSocketClientManager();\n private roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n private authService: WebSocketAuthService;\n\n public get rooms() {\n return this.roomManager.rooms;\n }\n private redisInstance: RedisInstance;\n private queueManager: QueueManager;\n private databaseInstance: DatabaseInstance;\n\n /** Redis subscriber events */\n private redisSubscriberEvents: string[] = [\n WebSocketRedisSubscriberEvent.ClientConnected,\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n WebSocketRedisSubscriberEvent.DisconnectClient,\n WebSocketRedisSubscriberEvent.SendMessage,\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n WebSocketRedisSubscriberEvent.MessageError,\n WebSocketRedisSubscriberEvent.QueueJobCompleted,\n WebSocketRedisSubscriberEvent.QueueJobError,\n WebSocketRedisSubscriberEvent.Custom,\n ];\n\n constructor(props: WebSocketServerProps) {\n super();\n\n this.uniqueInstanceId = props.uniqueInstanceId;\n this.applicationConfig = props.applicationConfig;\n this.options = props.options;\n this.redisInstance = props.redisInstance;\n this.queueManager = props.queueManager;\n this.databaseInstance = props.databaseInstance;\n this.routes = props.routes;\n this.workerId = props.workerId;\n this.authService = new WebSocketAuthService(props.applicationConfig);\n }\n\n public get type(): WebSocketType {\n return 'server';\n }\n\n private async validateWebSocketAuth(\n url: string,\n ): Promise<{ userId: number; payload: Record<string, unknown> } | null> {\n return this.authService.validateAuth(url);\n }\n\n public async load(): Promise<void> {\n const libraryControllersDirectory = path.join(baseDir, 'websocket', 'controllers', 'server');\n\n // Configure default routes\n await this.configureRoutes(this.defaultRoutes, libraryControllersDirectory);\n\n // Configure custom routes\n await this.configureRoutes(this.routes, this.options.controllersDirectory);\n }\n\n public async start({ fastifyServer }: { fastifyServer: FastifyInstance }): Promise<{ server: WS }> {\n return new Promise(resolve => {\n const server = new WS({\n noServer: true, // We're handling the server externally\n });\n\n this.server = server;\n\n // Ensure this is called after the server has been properly set up\n this.handleServerStart();\n\n fastifyServer.server.on('upgrade', async (request, socket, head) => {\n if (request.url?.startsWith('/ws')) {\n try {\n // Validate authentication token if provided\n const authenticatedUser = await this.validateWebSocketAuth(request.url);\n\n server.handleUpgrade(request, socket, head, ws => {\n server.emit('connection', ws, request, authenticatedUser);\n });\n } catch (error: any) {\n log('WebSocket authentication failed', { error: error.message });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n }\n } else {\n socket.destroy();\n }\n });\n\n server.on('error', this.handleServerError);\n\n server.on(\n 'connection',\n (ws: WebSocket, request: any, authenticatedUser: { userId: number; payload: any } | null) => {\n this.handleServerClientConnection(ws, authenticatedUser);\n },\n );\n\n // Resolve the promise with the server instance\n resolve({ server });\n });\n }\n\n public async stop(): Promise<void> {\n // Abort all ongoing operations (intervals, etc.)\n this.abortController.abort();\n\n // Clean up Redis subscriber listeners\n this.redisInstance.subscriberClient?.removeListener('message', this.handleSubscriberMessage);\n\n // Unsubscribe from all Redis events\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n this.redisInstance.subscriberClient?.unsubscribe(subscriberEventName);\n });\n\n // Close all client connections and clean up\n if (this.server) {\n this.server.clients.forEach(client => {\n client.removeAllListeners();\n client.close();\n });\n\n this.server.removeAllListeners();\n this.server.close();\n }\n\n // Clean up client manager and room manager\n this.clientManager.cleanup();\n this.roomManager.cleanup();\n\n // Reset managers\n this.clientManager = new WebSocketClientManager();\n this.roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n\n // Create new AbortController for potential restart\n this.abortController = new AbortController();\n\n log('Server stopped');\n }\n\n protected getControllerDependencies(): {\n webSocketServer: WebSocketServer;\n redisInstance: RedisInstance;\n queueManager: QueueManager;\n databaseInstance: DatabaseInstance;\n } {\n return {\n webSocketServer: this,\n redisInstance: this.redisInstance,\n queueManager: this.queueManager,\n databaseInstance: this.databaseInstance,\n };\n }\n\n protected shouldPrintRoutes(): boolean {\n return this.options.debug?.printRoutes ?? false;\n }\n\n private handleServerStart = (): void => {\n if (!this.server) {\n throw new Error('WebSocket server not started');\n }\n\n if (this.options.disconnectInactiveClients?.enabled && this.options.disconnectInactiveClients.intervalCheckTime) {\n // Note: setInterval with signal option requires Node.js 15+\n // TypeScript types may not reflect this, so we use type assertion\n (setInterval as (fn: () => void, ms: number, options?: { signal: AbortSignal }) => NodeJS.Timeout)(\n () => this.checkInactiveClients(),\n this.options.disconnectInactiveClients.intervalCheckTime,\n { signal: this.abortController.signal },\n );\n }\n\n // Go through each event and subscribe to it\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n // Subscribe to event\n this.redisInstance.subscriberClient?.subscribe(subscriberEventName);\n });\n\n // Handle subscriber message\n this.redisInstance.subscriberClient.on('message', this.handleSubscriberMessage);\n\n log('Server started', {\n Host: this.options.host,\n URL: this.options.url,\n });\n\n if (this.options.events?.onServerStarted) {\n this.options.events.onServerStarted({\n webSocketServer: this.server,\n });\n }\n };\n\n /**\n * Handle subscriber message.\n */\n private handleSubscriberMessage = async (channel: string, message: string): Promise<void> => {\n let parsedMessage: { [key: string]: any };\n\n try {\n parsedMessage = JSON.parse(message);\n } catch (error) {\n log('Failed to parse subscriber message', {\n Channel: channel,\n Message: message,\n Error: error,\n });\n\n return;\n }\n\n const includeSender = parsedMessage.includeSender === true;\n\n const isSameWorker = parsedMessage.workerId === this.workerId;\n\n // Check if message is from the same worker\n if (includeSender !== true && isSameWorker) {\n // Ignore the message if it's from the same worker\n return;\n }\n\n log('Incoming subscriber message', {\n Channel: channel,\n // 'Run Same Worker': parsedMessage.includeSender ? 'Yes' : 'No',\n 'Client ID': parsedMessage.clientId ?? '-',\n });\n\n switch (channel) {\n case WebSocketRedisSubscriberEvent.ClientConnected: {\n this.onClientConnect({\n clientId: parsedMessage.clientId,\n lastActivity: parsedMessage.lastActivity,\n user: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientDisconnected: {\n this.onClientDisconnect({\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.DisconnectClient: {\n const clientToDisconnect = this.clientManager.getClient({\n clientId: parsedMessage.clientId,\n // requireWs: true,\n });\n\n if (clientToDisconnect) {\n this.clientManager.disconnectClient({\n clientId: parsedMessage.clientId,\n });\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({\n clientId: parsedMessage.clientId,\n });\n }\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientJoinedRoom: {\n this.onJoinRoom({\n clientId: parsedMessage.clientId,\n roomName: parsedMessage.roomName,\n userData: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientLeftRoom: {\n this.roomManager.removeClientFromRoom({\n roomName: parsedMessage.room,\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessage: {\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessageToAll: {\n this.broadcastToAllClients({ data: parsedMessage });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.MessageError: {\n this.sendMessageError({\n webSocketClientId: parsedMessage.clientId,\n error: parsedMessage.error,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobCompleted: {\n // const parsedMessage = JSON.parse(message);\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobError: {\n // For queue job errors, merge error information into data field\n // This maintains backward compatibility while allowing flexible error data\n parsedMessage.data = {\n ...(parsedMessage.data ?? {}),\n error: parsedMessage.error,\n };\n\n break;\n }\n case WebSocketRedisSubscriberEvent.Custom: {\n // Custom logic is being handled in the app\n\n break;\n }\n default: {\n log('Unknown subscriber message received', {\n Channel: channel,\n Message: message,\n });\n }\n }\n\n if (typeof this.applicationConfig.webSocket?.subscriberEventHandler === 'function') {\n // Execute custom application subscriber event handler\n this.applicationConfig.webSocket.subscriberEventHandler({\n channel,\n message: parsedMessage,\n webSocketServer: this,\n databaseInstance: this.databaseInstance,\n });\n }\n };\n\n private handleServerError = (error: Error): void => {\n Logger.error({ error });\n };\n\n private handleServerClientConnection = (\n ws: WebSocket,\n authenticatedUser?: { userId: number; payload: any } | null,\n ): void => {\n const clientId = generateClientId();\n\n const lastActivity = Date.now();\n\n ws.on('message', (message: RawData) => this.handleClientMessage(ws, message));\n\n ws.on('close', () => {\n this.handleServerClientDisconnection(clientId);\n this.clientManager.removeClient(clientId);\n\n // Clean up event listeners to prevent memory leaks\n ws.removeAllListeners();\n });\n\n try {\n this.clientManager.addClient({\n clientId,\n ws,\n lastActivity,\n user: authenticatedUser,\n });\n\n // Let other workers know that the client has connected\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientConnected,\n JSON.stringify({\n clientId,\n lastActivity,\n workerId: this.workerId,\n user: authenticatedUser,\n }),\n );\n\n // Send authentication success message if user is authenticated\n if (authenticatedUser) {\n this.sendClientMessage(ws, {\n type: 'auth',\n action: 'authenticated',\n data: {\n userId: authenticatedUser.userId,\n message: 'Authentication successful',\n },\n });\n }\n } catch (error) {\n logger.error({ error });\n }\n };\n\n public leaveRoom({ ws, roomName }: { ws: WebSocket; roomName: string }): void {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n log('Client ID not found when removing client from room');\n\n return;\n }\n\n // Check if client is in room\n const clientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (!clientInRoom) {\n log('Client not in room when removing client from room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n this.roomManager.removeClientFromRoom({\n roomName,\n clientId,\n });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n JSON.stringify({\n clientId,\n room: roomName,\n workerId: this.workerId,\n }),\n );\n\n // Optionally send a message to the client\n this.sendClientMessage(ws, {\n type: 'user',\n action: 'leftRoom',\n data: {\n roomName,\n },\n });\n }\n\n private onClientConnect({\n clientId,\n lastActivity,\n user,\n }: {\n clientId: string;\n lastActivity: number;\n user?: { userId: number; payload: any } | null;\n }): void {\n this.clientManager.addClient({\n clientId,\n ws: null,\n lastActivity,\n user,\n });\n }\n\n private onClientDisconnect({ clientId }: { clientId: string }): void {\n // Set client as disconnected\n this.clientManager.removeClient(clientId);\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({ clientId });\n }\n\n private handleServerClientDisconnection = (clientId: string): void => {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when handling server client disconnection', {\n 'Client ID': clientId || '-',\n });\n\n return;\n }\n\n this.onClientDisconnect({ clientId });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n JSON.stringify({\n clientId,\n workerId: this.workerId,\n }),\n );\n\n // log('Client disconnected', { ID: clientId });\n };\n\n private handleClientMessage = async (ws: WebSocket, message: RawData): Promise<void> => {\n try {\n const clientId = this.clientManager.getClientId({\n ws,\n });\n\n if (!clientId) {\n log('Client ID not found when handling server message');\n\n return;\n }\n\n // Handle server message\n const serverMessageResponse = await this.handleServerMessage(ws, message, clientId);\n\n if (serverMessageResponse) {\n this.sendClientMessage(ws, {\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n response: serverMessageResponse?.response,\n });\n\n if (\n serverMessageResponse?.response &&\n typeof serverMessageResponse.response === 'object' &&\n 'error' in serverMessageResponse.response\n ) {\n // Log error but don't throw to prevent connection disruption\n Logger.error({\n error: serverMessageResponse.response.error,\n meta: {\n clientId,\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n },\n });\n }\n }\n } catch (error) {\n Logger.error({ error });\n\n log('Error handling client message', {\n Error: error,\n });\n }\n };\n\n protected handleMessageError(clientId: string, error: string): void {\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.MessageError,\n JSON.stringify({\n includeSender: true,\n clientId,\n error,\n }),\n );\n }\n\n /**\n * Check and disconnect inactive clients based on configuration\n * This helps prevent stale connections from accumulating\n */\n private checkInactiveClients(): void {\n const config = this.options.disconnectInactiveClients;\n\n if (!config?.enabled || !config.inactiveTime) {\n return;\n }\n\n if (config.log) {\n log('Checking inactive clients...');\n }\n\n const now = Date.now();\n const clients = this.clientManager.getClients();\n\n for (const client of clients) {\n const inactiveTime = now - client.lastActivity;\n\n if (inactiveTime > config.inactiveTime) {\n this.clientManager.disconnectClient(client.clientId);\n\n if (config.log) {\n log('Disconnected inactive client', {\n 'Client ID': client.clientId,\n 'Inactive Time': `${inactiveTime}ms`,\n });\n }\n }\n }\n }\n\n /**\n * Broadcast a message to all connected WebSocket clients\n * @param data - The data to broadcast (will be JSON stringified)\n * @param excludeClientId - Optional client ID to exclude from broadcast\n */\n public broadcastToAllClients({\n data,\n excludeClientId,\n }: {\n data: { [key: string]: any };\n excludeClientId?: string;\n }): void {\n if (!this.server) {\n log('Server not started when broadcasting to all clients');\n return;\n }\n\n for (const client of this.server.clients) {\n if (client.readyState !== WebSocket.OPEN) {\n continue;\n }\n\n // Skip excluded client if specified\n if (excludeClientId) {\n const clientId = this.clientManager.getClientId({ ws: client });\n if (clientId === excludeClientId) {\n continue;\n }\n }\n\n client.send(JSON.stringify(data));\n }\n }\n\n public sendMessageError({ webSocketClientId, error }: { webSocketClientId: string; error: string }): void {\n const client = this.clientManager.getClient({\n clientId: webSocketClientId,\n });\n\n if (!client) {\n log('Client not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n } else if (!client.ws) {\n log('Client WebSocket not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n }\n\n this.sendClientMessage(client.ws, {\n type: 'error',\n action: 'message',\n data: {\n error,\n },\n });\n }\n\n // private getClientId({\n // client,\n // }: {\n // client: WebSocket;\n // }): string | undefined {\n // return [...this.connectedClients.entries()].find(\n // ([_, value]) => value.ws === client,\n // )?.[0];\n // }\n\n private onJoinRoom({ clientId, roomName, userData }: { clientId: string; roomName: string; userData: any }): void {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when joining room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n // Check if client can join multiple rooms\n const canJoinMultipleRooms = this.options.rooms?.clientCanJoinMultipleRooms ?? true; // Default to true for backward compatibility\n\n if (!canJoinMultipleRooms && client.roomName) {\n // Remove client from current room before joining new one\n this.roomManager.removeClientFromRoom({\n roomName: client.roomName,\n clientId,\n broadcast: false, // Don't broadcast here, will broadcast after adding to new room\n });\n }\n\n // Update client with user in client manager\n this.clientManager.updateClient({\n clientId,\n key: 'user',\n data: userData,\n });\n\n this.roomManager.addClientToRoom({\n clientId,\n user: userData,\n roomName,\n });\n }\n\n public async joinRoom({\n ws,\n userId,\n userType,\n username,\n roomName,\n }: {\n ws: WebSocket;\n userId?: number;\n userType?: string;\n username?: string;\n roomName: string;\n }) {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n // throw new Error('Client ID not found when joining room');\n\n logger.warn({\n message: 'Client ID not found when joining room',\n meta: {\n // 'WebSocket ID': ws?.id || '-',\n 'Room Name': roomName,\n },\n });\n\n return;\n }\n\n // Check if client is already in room\n const isClientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (isClientInRoom) {\n // throw new Error('Client already in room when joining');\n\n logger.warn({\n message: 'Client already in room when joining',\n meta: {\n // 'WebSocket ID': ws?. || '-',\n 'Room Name': roomName,\n 'Client ID': clientId,\n },\n });\n\n return;\n }\n\n let userData: any = {};\n\n // // Get WebSocket client ID\n // const webSocketId = this.clientManager.getClientId({ ws });\n\n if (userId) {\n // Get user email from database\n const dbEntityManager = this.databaseInstance.getEntityManager();\n\n const getUserQuery = 'SELECT email FROM users WHERE id = ?';\n const getUserParams = [userId];\n\n const getUserResult = await dbEntityManager.execute(getUserQuery, getUserParams);\n\n if (!getUserResult || getUserResult.length === 0) {\n throw new Error('User not found in database');\n }\n\n const user = getUserResult[0];\n\n userData = {\n id: userId,\n ...user,\n };\n }\n\n // userData.uniqueId = webSocketId;\n\n if (username) {\n userData.username = username;\n }\n\n userData.userType = userType;\n\n // if user with same email is already connected, disconnect the previous connection\n // const existingClient =\n // this.clientManager.getClientByKey({\n // key: 'user.email',\n // value: user.email,\n // });\n\n // if (existingClient) {\n // if (existingClient.ws) {\n // this.clientManager.disconnectClient({\n // clientId: existingClient.clientId,\n // });\n // } else {\n // // Publish to Redis that we should disconnect this client\n // this.redisInstance.publisherClient.publish(\n // WebSocketRedisSubscriberEvent.DisconnectClient,\n // JSON.stringify({\n // clientId,\n // workerId: this.workerId,\n // }),\n // );\n // }\n // }\n\n this.onJoinRoom({\n clientId,\n roomName,\n userData,\n });\n\n // Let other workers know that the client has joined the room\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n JSON.stringify({\n clientId,\n user: userData,\n roomName,\n workerId: this.workerId,\n }),\n );\n\n return true;\n }\n\n public sendClientMessage = (ws: WebSocket, data: unknown, binary: boolean = false): void => {\n const webSocketMessage = JSON.stringify(data);\n\n ws.send(webSocketMessage, { binary });\n };\n\n public sendMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessage,\n JSON.stringify(formattedData),\n );\n };\n\n public sendMessageToAll = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n JSON.stringify(formattedData),\n );\n };\n\n public sendCustomMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(WebSocketRedisSubscriberEvent.Custom, JSON.stringify(formattedData));\n };\n\n public getClients({ userType }: { userType?: string }): any[] {\n return this.clientManager.getClients({ userType });\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { type RawData, WebSocketServer as WS, WebSocket } from 'ws';\nimport {\n type WebSocketOptions,\n WebSocketRedisSubscriberEvent,\n type WebSocketRoute,\n type WebSocketSubscriberDefinition,\n type WebSocketSubscriberHandlerContext,\n type WebSocketSubscriberMatcher,\n type WebSocketType,\n} from './websocket.interface.js';\nimport type RedisInstance from '../redis/instance.js';\nimport type QueueManager from '../queue/manager.js';\nimport type DatabaseInstance from '../database/instance.js';\nimport type { WebSocketServerProps } from './websocket-server.interface.js';\nimport WebSocketClientManager from './websocket-client-manager.js';\nimport { generateClientId, log } from './utils.js';\nimport WebSocketBase from './websocket-base.js';\nimport { Logger } from '../logger/index.js';\nimport path from 'path';\nimport { type WebApplicationConfig, baseDir } from '../index.js';\nimport WebSocketRoomManager from './websocket-room-manager.js';\nimport logger from '../logger/logger.js';\nimport type { FastifyInstance } from 'fastify';\nimport { WebSocketAuthService } from './websocket-auth.js';\nimport { File, Loader } from '../util/index.js';\n\nexport default class WebSocketServer extends WebSocketBase {\n protected defaultRoutes: WebSocketRoute[] = [\n {\n type: 'system',\n action: 'joinRoom',\n controllerName: 'system',\n },\n {\n type: 'system',\n action: 'leaveRoom',\n controllerName: 'system',\n },\n ];\n\n private server?: WS;\n\n private abortController = new AbortController();\n private workerId: number | null;\n private uniqueInstanceId: string;\n private applicationConfig: WebApplicationConfig;\n private options: WebSocketOptions;\n public clientManager = new WebSocketClientManager();\n private roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n private authService: WebSocketAuthService;\n\n public get rooms() {\n return this.roomManager.rooms;\n }\n private redisInstance: RedisInstance;\n private queueManager: QueueManager;\n private databaseInstance: DatabaseInstance;\n private subscriberHandlersByChannel: Map<string, WebSocketSubscriberDefinition[]> = new Map();\n private subscriberMatcherHandlers: WebSocketSubscriberDefinition[] = [];\n private wildcardSubscriberHandlers: WebSocketSubscriberDefinition[] = [];\n\n /** Redis subscriber events */\n private redisSubscriberEvents: string[] = [\n WebSocketRedisSubscriberEvent.ClientConnected,\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n WebSocketRedisSubscriberEvent.DisconnectClient,\n WebSocketRedisSubscriberEvent.SendMessage,\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n WebSocketRedisSubscriberEvent.MessageError,\n WebSocketRedisSubscriberEvent.QueueJobCompleted,\n WebSocketRedisSubscriberEvent.QueueJobError,\n WebSocketRedisSubscriberEvent.Custom,\n ];\n\n constructor(props: WebSocketServerProps) {\n super();\n\n this.uniqueInstanceId = props.uniqueInstanceId;\n this.applicationConfig = props.applicationConfig;\n this.options = props.options;\n this.redisInstance = props.redisInstance;\n this.queueManager = props.queueManager;\n this.databaseInstance = props.databaseInstance;\n this.routes = props.routes;\n this.workerId = props.workerId;\n this.authService = new WebSocketAuthService(props.applicationConfig);\n }\n\n public get type(): WebSocketType {\n return 'server';\n }\n\n private async validateWebSocketAuth(\n url: string,\n ): Promise<{ userId: number; payload: Record<string, unknown> } | null> {\n return this.authService.validateAuth(url);\n }\n\n public async load(): Promise<void> {\n const libraryControllersDirectory = path.join(baseDir, 'websocket', 'controllers', 'server');\n\n // Configure default routes\n await this.configureRoutes(this.defaultRoutes, libraryControllersDirectory);\n\n // Configure custom routes\n await this.configureRoutes(this.routes, this.options.controllersDirectory);\n\n await this.loadSubscriberHandlers();\n }\n\n private async loadSubscriberHandlers(): Promise<void> {\n this.subscriberHandlersByChannel.clear();\n this.subscriberMatcherHandlers = [];\n this.wildcardSubscriberHandlers = [];\n\n const config = this.options.subscriberHandlers;\n\n if (!config) {\n return;\n }\n\n const definitions: WebSocketSubscriberDefinition[] = [];\n\n if (config.directory) {\n const directoryExists = await File.pathExists(config.directory);\n\n if (!directoryExists) {\n logger.warn('WebSocket subscriber handlers directory not found', {\n Directory: config.directory,\n });\n } else {\n const modules = await Loader.loadModulesInDirectory<unknown>({\n directory: config.directory,\n extensions: ['.ts', '.js'],\n });\n\n for (const [moduleName, moduleExport] of Object.entries(modules)) {\n definitions.push(\n ...this.normalizeSubscriberExport(moduleExport, {\n source: 'file',\n moduleName,\n directory: config.directory,\n }),\n );\n }\n }\n }\n\n if (Array.isArray(config.handlers)) {\n for (const handler of config.handlers) {\n definitions.push(\n ...this.normalizeSubscriberExport(handler, {\n source: 'inline',\n }),\n );\n }\n }\n\n for (const definition of definitions) {\n this.registerSubscriberDefinition(definition);\n }\n\n if (definitions.length > 0) {\n logger.info('WebSocket subscriber handlers loaded', {\n Channels: Array.from(this.subscriberHandlersByChannel.keys()),\n Matchers: this.subscriberMatcherHandlers.length,\n Wildcards: this.wildcardSubscriberHandlers.length,\n });\n }\n }\n\n private normalizeSubscriberExport(\n value: unknown,\n metadata: { source: 'file' | 'inline'; moduleName?: string; directory?: string },\n ): WebSocketSubscriberDefinition[] {\n if (!value) {\n return [];\n }\n\n if (Array.isArray(value)) {\n return value.flatMap(entry => this.normalizeSubscriberExport(entry, metadata));\n }\n\n if (typeof value === 'function') {\n const moduleChannel = metadata.moduleName ?? value.name ?? 'anonymous';\n\n return [\n {\n name: metadata.moduleName ?? value.name,\n channels: [moduleChannel],\n handle: async context => {\n await value(context);\n },\n },\n ];\n }\n\n if (typeof value === 'object') {\n const definition = this.normalizeSubscriberObject(value as Record<string, unknown>, metadata);\n if (definition) {\n return [definition];\n }\n\n return Object.values(value as Record<string, unknown>).flatMap(entry =>\n this.normalizeSubscriberExport(entry, metadata),\n );\n }\n\n return [];\n }\n\n private normalizeSubscriberObject(\n value: Record<string, unknown>,\n metadata: { source: 'file' | 'inline'; moduleName?: string },\n ): WebSocketSubscriberDefinition | null {\n const handle = value.handle;\n\n if (typeof handle !== 'function') {\n return null;\n }\n\n let channelSource: unknown;\n if (Object.prototype.hasOwnProperty.call(value, 'channel')) {\n channelSource = value.channel;\n } else if (Object.prototype.hasOwnProperty.call(value, 'channels')) {\n channelSource = value.channels;\n }\n\n const channels = this.normalizeChannels(channelSource);\n\n let matcherSource: unknown;\n if (Object.prototype.hasOwnProperty.call(value, 'match')) {\n matcherSource = value.match;\n } else if (Object.prototype.hasOwnProperty.call(value, 'matcher')) {\n matcherSource = value.matcher;\n } else if (Object.prototype.hasOwnProperty.call(value, 'matchers')) {\n matcherSource = value.matchers;\n }\n\n const matchers = this.normalizeMatchers(matcherSource);\n\n if (channels.length === 0 && matchers.length === 0) {\n logger.warn('Skipping WebSocket subscriber handler without channels or matchers', {\n Source: metadata.source,\n Module: metadata.moduleName,\n });\n\n return null;\n }\n\n const definition: WebSocketSubscriberDefinition = {\n name: typeof value.name === 'string' ? value.name : metadata.moduleName,\n description: typeof value.description === 'string' ? value.description : undefined,\n priority: typeof value.priority === 'number' ? value.priority : undefined,\n channels,\n matchers,\n handle: handle as (context: WebSocketSubscriberHandlerContext) => unknown | Promise<unknown>,\n };\n\n return definition;\n }\n\n private normalizeChannels(input: unknown): string[] {\n if (typeof input === 'string') {\n const trimmed = input.trim();\n return trimmed.length > 0 ? [trimmed] : [];\n }\n\n if (Array.isArray(input)) {\n return input\n .filter((channel): channel is string => typeof channel === 'string')\n .map(channel => channel.trim())\n .filter(channel => channel.length > 0);\n }\n\n return [];\n }\n\n private normalizeMatchers(input: unknown): WebSocketSubscriberMatcher[] {\n if (!input) {\n return [];\n }\n\n const matchers: WebSocketSubscriberMatcher[] = [];\n const addMatcher = (matcher: unknown) => {\n if (matcher instanceof RegExp || typeof matcher === 'function') {\n matchers.push(matcher as WebSocketSubscriberMatcher);\n } else if (typeof matcher === 'string') {\n const trimmed = matcher.trim();\n if (trimmed.length > 0) {\n matchers.push(trimmed as WebSocketSubscriberMatcher);\n }\n }\n };\n\n if (Array.isArray(input)) {\n input.forEach(addMatcher);\n } else {\n addMatcher(input);\n }\n\n return matchers;\n }\n\n private registerSubscriberDefinition(definition: WebSocketSubscriberDefinition): void {\n const normalizedPriority = definition.priority ?? 0;\n const channels = definition.channels?.map(channel => channel.trim()).filter(Boolean) ?? [];\n\n const normalizedDefinition: WebSocketSubscriberDefinition = {\n ...definition,\n priority: normalizedPriority,\n channels,\n matchers: definition.matchers ?? [],\n };\n\n if (channels.length === 0 && normalizedDefinition.matchers?.length === 0) {\n logger.warn('Skipping WebSocket subscriber handler registration due to missing filters', {\n Handler: definition.name ?? '(anonymous)',\n });\n\n return;\n }\n\n if (channels.length > 0) {\n for (const channel of channels) {\n if (channel === '*') {\n this.wildcardSubscriberHandlers.push(normalizedDefinition);\n continue;\n }\n\n const channelHandlers = this.subscriberHandlersByChannel.get(channel) ?? [];\n channelHandlers.push(normalizedDefinition);\n this.subscriberHandlersByChannel.set(channel, channelHandlers);\n }\n }\n\n if (normalizedDefinition.matchers && normalizedDefinition.matchers.length > 0) {\n this.subscriberMatcherHandlers.push(normalizedDefinition);\n }\n }\n\n private doesDefinitionMatch(\n definition: WebSocketSubscriberDefinition,\n context: WebSocketSubscriberHandlerContext,\n ): boolean {\n if (!definition.matchers || definition.matchers.length === 0) {\n return false;\n }\n\n return definition.matchers.some(matcher => this.evaluateMatcher(matcher, context));\n }\n\n private evaluateMatcher(matcher: WebSocketSubscriberMatcher, context: WebSocketSubscriberHandlerContext): boolean {\n if (typeof matcher === 'string') {\n if (matcher === '*') {\n return true;\n }\n\n return matcher === context.channel;\n }\n\n if (matcher instanceof RegExp) {\n return matcher.test(context.channel);\n }\n\n try {\n return Boolean(matcher(context));\n } catch (error) {\n logger.error({\n error,\n message: 'WebSocket subscriber matcher threw an error',\n meta: {\n Channel: context.channel,\n Matcher: String(matcher),\n },\n });\n\n return false;\n }\n }\n\n private async executeSubscriberHandlers(channel: string, message: any): Promise<void> {\n if (\n this.subscriberHandlersByChannel.size === 0 &&\n this.subscriberMatcherHandlers.length === 0 &&\n this.wildcardSubscriberHandlers.length === 0\n ) {\n return;\n }\n\n const context: WebSocketSubscriberHandlerContext = {\n channel,\n message,\n webSocketServer: this,\n databaseInstance: this.databaseInstance,\n redisInstance: this.redisInstance,\n queueManager: this.queueManager,\n };\n\n const candidates = new Set<WebSocketSubscriberDefinition>();\n\n const channelHandlers = this.subscriberHandlersByChannel.get(channel);\n\n if (channelHandlers) {\n channelHandlers.forEach(handler => candidates.add(handler));\n }\n\n this.wildcardSubscriberHandlers.forEach(handler => candidates.add(handler));\n\n for (const definition of this.subscriberMatcherHandlers) {\n if (this.doesDefinitionMatch(definition, context)) {\n candidates.add(definition);\n }\n }\n\n if (candidates.size === 0) {\n return;\n }\n\n const orderedHandlers = Array.from(candidates).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n\n for (const handler of orderedHandlers) {\n try {\n await handler.handle(context);\n } catch (error) {\n logger.error({\n error,\n message: 'WebSocket subscriber handler failed',\n meta: {\n Handler: handler.name ?? '(anonymous)',\n Channel: channel,\n },\n });\n }\n }\n }\n\n public async start({ fastifyServer }: { fastifyServer: FastifyInstance }): Promise<{ server: WS }> {\n return new Promise(resolve => {\n const server = new WS({\n noServer: true, // We're handling the server externally\n });\n\n this.server = server;\n\n // Ensure this is called after the server has been properly set up\n this.handleServerStart();\n\n fastifyServer.server.on('upgrade', async (request, socket, head) => {\n if (request.url?.startsWith('/ws')) {\n try {\n // Validate authentication token if provided\n const authenticatedUser = await this.validateWebSocketAuth(request.url);\n\n server.handleUpgrade(request, socket, head, ws => {\n server.emit('connection', ws, request, authenticatedUser);\n });\n } catch (error: any) {\n log('WebSocket authentication failed', { error: error.message });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n }\n } else {\n socket.destroy();\n }\n });\n\n server.on('error', this.handleServerError);\n\n server.on(\n 'connection',\n (ws: WebSocket, request: any, authenticatedUser: { userId: number; payload: any } | null) => {\n this.handleServerClientConnection(ws, authenticatedUser);\n },\n );\n\n // Resolve the promise with the server instance\n resolve({ server });\n });\n }\n\n public async stop(): Promise<void> {\n // Abort all ongoing operations (intervals, etc.)\n this.abortController.abort();\n\n // Clean up Redis subscriber listeners\n this.redisInstance.subscriberClient?.removeListener('message', this.handleSubscriberMessage);\n\n // Unsubscribe from all Redis events\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n this.redisInstance.subscriberClient?.unsubscribe(subscriberEventName);\n });\n\n // Close all client connections and clean up\n if (this.server) {\n this.server.clients.forEach(client => {\n client.removeAllListeners();\n client.close();\n });\n\n this.server.removeAllListeners();\n this.server.close();\n }\n\n // Clean up client manager and room manager\n this.clientManager.cleanup();\n this.roomManager.cleanup();\n\n // Reset managers\n this.clientManager = new WebSocketClientManager();\n this.roomManager = new WebSocketRoomManager({\n clientManager: this.clientManager,\n });\n\n // Create new AbortController for potential restart\n this.abortController = new AbortController();\n\n log('Server stopped');\n }\n\n protected getControllerDependencies(): {\n webSocketServer: WebSocketServer;\n redisInstance: RedisInstance;\n queueManager: QueueManager;\n databaseInstance: DatabaseInstance;\n } {\n return {\n webSocketServer: this,\n redisInstance: this.redisInstance,\n queueManager: this.queueManager,\n databaseInstance: this.databaseInstance,\n };\n }\n\n protected shouldPrintRoutes(): boolean {\n return this.options.debug?.printRoutes ?? false;\n }\n\n private handleServerStart = (): void => {\n if (!this.server) {\n throw new Error('WebSocket server not started');\n }\n\n if (this.options.disconnectInactiveClients?.enabled && this.options.disconnectInactiveClients.intervalCheckTime) {\n // Note: setInterval with signal option requires Node.js 15+\n // TypeScript types may not reflect this, so we use type assertion\n (setInterval as (fn: () => void, ms: number, options?: { signal: AbortSignal }) => NodeJS.Timeout)(\n () => this.checkInactiveClients(),\n this.options.disconnectInactiveClients.intervalCheckTime,\n { signal: this.abortController.signal },\n );\n }\n\n // Go through each event and subscribe to it\n this.redisSubscriberEvents.forEach(subscriberEventName => {\n // Subscribe to event\n this.redisInstance.subscriberClient?.subscribe(subscriberEventName);\n });\n\n // Handle subscriber message\n this.redisInstance.subscriberClient.on('message', this.handleSubscriberMessage);\n\n log('Server started', {\n Host: this.options.host,\n URL: this.options.url,\n });\n\n if (this.options.events?.onServerStarted) {\n this.options.events.onServerStarted({\n webSocketServer: this.server,\n });\n }\n };\n\n /**\n * Handle subscriber message.\n */\n private handleSubscriberMessage = async (channel: string, message: string): Promise<void> => {\n let parsedMessage: { [key: string]: any };\n\n try {\n parsedMessage = JSON.parse(message);\n } catch (error) {\n log('Failed to parse subscriber message', {\n Channel: channel,\n Message: message,\n Error: error,\n });\n\n return;\n }\n\n const includeSender = parsedMessage.includeSender === true;\n\n const isSameWorker = parsedMessage.workerId === this.workerId;\n\n // Check if message is from the same worker\n if (includeSender !== true && isSameWorker) {\n // Ignore the message if it's from the same worker\n return;\n }\n\n log('Incoming subscriber message', {\n Channel: channel,\n // 'Run Same Worker': parsedMessage.includeSender ? 'Yes' : 'No',\n 'Client ID': parsedMessage.clientId ?? '-',\n });\n\n switch (channel) {\n case WebSocketRedisSubscriberEvent.ClientConnected: {\n this.onClientConnect({\n clientId: parsedMessage.clientId,\n lastActivity: parsedMessage.lastActivity,\n user: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientDisconnected: {\n this.onClientDisconnect({\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.DisconnectClient: {\n const clientToDisconnect = this.clientManager.getClient({\n clientId: parsedMessage.clientId,\n // requireWs: true,\n });\n\n if (clientToDisconnect) {\n this.clientManager.disconnectClient({\n clientId: parsedMessage.clientId,\n });\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({\n clientId: parsedMessage.clientId,\n });\n }\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientJoinedRoom: {\n this.onJoinRoom({\n clientId: parsedMessage.clientId,\n roomName: parsedMessage.roomName,\n userData: parsedMessage.user,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.ClientLeftRoom: {\n this.roomManager.removeClientFromRoom({\n roomName: parsedMessage.room,\n clientId: parsedMessage.clientId,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessage: {\n break;\n }\n case WebSocketRedisSubscriberEvent.SendMessageToAll: {\n this.broadcastToAllClients({ data: parsedMessage });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.MessageError: {\n this.sendMessageError({\n webSocketClientId: parsedMessage.clientId,\n error: parsedMessage.error,\n });\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobCompleted: {\n // const parsedMessage = JSON.parse(message);\n\n break;\n }\n case WebSocketRedisSubscriberEvent.QueueJobError: {\n // For queue job errors, merge error information into data field\n // This maintains backward compatibility while allowing flexible error data\n parsedMessage.data = {\n ...(parsedMessage.data ?? {}),\n error: parsedMessage.error,\n };\n\n break;\n }\n case WebSocketRedisSubscriberEvent.Custom: {\n // Custom logic is being handled in the app\n\n break;\n }\n default: {\n log('Unknown subscriber message received', {\n Channel: channel,\n Message: message,\n });\n }\n }\n\n await this.executeSubscriberHandlers(channel, parsedMessage);\n };\n\n private handleServerError = (error: Error): void => {\n Logger.error({ error });\n };\n\n private handleServerClientConnection = (\n ws: WebSocket,\n authenticatedUser?: { userId: number; payload: any } | null,\n ): void => {\n const clientId = generateClientId();\n\n const lastActivity = Date.now();\n\n ws.on('message', (message: RawData) => this.handleClientMessage(ws, message));\n\n ws.on('close', () => {\n this.handleServerClientDisconnection(clientId);\n this.clientManager.removeClient(clientId);\n\n // Clean up event listeners to prevent memory leaks\n ws.removeAllListeners();\n });\n\n try {\n this.clientManager.addClient({\n clientId,\n ws,\n lastActivity,\n user: authenticatedUser,\n });\n\n // Let other workers know that the client has connected\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientConnected,\n JSON.stringify({\n clientId,\n lastActivity,\n workerId: this.workerId,\n user: authenticatedUser,\n }),\n );\n\n // Send authentication success message if user is authenticated\n if (authenticatedUser) {\n this.sendClientMessage(ws, {\n type: 'auth',\n action: 'authenticated',\n data: {\n userId: authenticatedUser.userId,\n message: 'Authentication successful',\n },\n });\n }\n } catch (error) {\n logger.error({ error });\n }\n };\n\n public leaveRoom({ ws, roomName }: { ws: WebSocket; roomName: string }): void {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n log('Client ID not found when removing client from room');\n\n return;\n }\n\n // Check if client is in room\n const clientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (!clientInRoom) {\n log('Client not in room when removing client from room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n this.roomManager.removeClientFromRoom({\n roomName,\n clientId,\n });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientLeftRoom,\n JSON.stringify({\n clientId,\n room: roomName,\n workerId: this.workerId,\n }),\n );\n\n // Optionally send a message to the client\n this.sendClientMessage(ws, {\n type: 'user',\n action: 'leftRoom',\n data: {\n roomName,\n },\n });\n }\n\n private onClientConnect({\n clientId,\n lastActivity,\n user,\n }: {\n clientId: string;\n lastActivity: number;\n user?: { userId: number; payload: any } | null;\n }): void {\n this.clientManager.addClient({\n clientId,\n ws: null,\n lastActivity,\n user,\n });\n }\n\n private onClientDisconnect({ clientId }: { clientId: string }): void {\n // Set client as disconnected\n this.clientManager.removeClient(clientId);\n\n // Remove client from rooms\n this.roomManager.removeClientFromAllRooms({ clientId });\n }\n\n private handleServerClientDisconnection = (clientId: string): void => {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when handling server client disconnection', {\n 'Client ID': clientId || '-',\n });\n\n return;\n }\n\n this.onClientDisconnect({ clientId });\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientDisconnected,\n JSON.stringify({\n clientId,\n workerId: this.workerId,\n }),\n );\n\n // log('Client disconnected', { ID: clientId });\n };\n\n private handleClientMessage = async (ws: WebSocket, message: RawData): Promise<void> => {\n try {\n const clientId = this.clientManager.getClientId({\n ws,\n });\n\n if (!clientId) {\n log('Client ID not found when handling server message');\n\n return;\n }\n\n // Handle server message\n const serverMessageResponse = await this.handleServerMessage(ws, message, clientId);\n\n if (serverMessageResponse) {\n this.sendClientMessage(ws, {\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n response: serverMessageResponse?.response,\n });\n\n if (\n serverMessageResponse?.response &&\n typeof serverMessageResponse.response === 'object' &&\n 'error' in serverMessageResponse.response\n ) {\n // Log error but don't throw to prevent connection disruption\n Logger.error({\n error: serverMessageResponse.response.error,\n meta: {\n clientId,\n type: serverMessageResponse.type,\n action: serverMessageResponse.action,\n },\n });\n }\n }\n } catch (error) {\n Logger.error({ error });\n\n log('Error handling client message', {\n Error: error,\n });\n }\n };\n\n protected handleMessageError(clientId: string, error: string): void {\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.MessageError,\n JSON.stringify({\n includeSender: true,\n clientId,\n error,\n }),\n );\n }\n\n /**\n * Check and disconnect inactive clients based on configuration\n * This helps prevent stale connections from accumulating\n */\n private checkInactiveClients(): void {\n const config = this.options.disconnectInactiveClients;\n\n if (!config?.enabled || !config.inactiveTime) {\n return;\n }\n\n if (config.log) {\n log('Checking inactive clients...');\n }\n\n const now = Date.now();\n const clients = this.clientManager.getClients();\n\n for (const client of clients) {\n const inactiveTime = now - client.lastActivity;\n\n if (inactiveTime > config.inactiveTime) {\n this.clientManager.disconnectClient(client.clientId);\n\n if (config.log) {\n log('Disconnected inactive client', {\n 'Client ID': client.clientId,\n 'Inactive Time': `${inactiveTime}ms`,\n });\n }\n }\n }\n }\n\n /**\n * Broadcast a message to all connected WebSocket clients\n * @param data - The data to broadcast (will be JSON stringified)\n * @param excludeClientId - Optional client ID to exclude from broadcast\n */\n public broadcastToAllClients({\n data,\n excludeClientId,\n }: {\n data: { [key: string]: any };\n excludeClientId?: string;\n }): void {\n if (!this.server) {\n log('Server not started when broadcasting to all clients');\n return;\n }\n\n for (const client of this.server.clients) {\n if (client.readyState !== WebSocket.OPEN) {\n continue;\n }\n\n // Skip excluded client if specified\n if (excludeClientId) {\n const clientId = this.clientManager.getClientId({ ws: client });\n if (clientId === excludeClientId) {\n continue;\n }\n }\n\n client.send(JSON.stringify(data));\n }\n }\n\n public sendMessageError({ webSocketClientId, error }: { webSocketClientId: string; error: string }): void {\n const client = this.clientManager.getClient({\n clientId: webSocketClientId,\n });\n\n if (!client) {\n log('Client not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n } else if (!client.ws) {\n log('Client WebSocket not found when sending message error', {\n 'Client ID': webSocketClientId || '-',\n Error: error,\n });\n\n return;\n }\n\n this.sendClientMessage(client.ws, {\n type: 'error',\n action: 'message',\n data: {\n error,\n },\n });\n }\n\n // private getClientId({\n // client,\n // }: {\n // client: WebSocket;\n // }): string | undefined {\n // return [...this.connectedClients.entries()].find(\n // ([_, value]) => value.ws === client,\n // )?.[0];\n // }\n\n private onJoinRoom({ clientId, roomName, userData }: { clientId: string; roomName: string; userData: any }): void {\n const client = this.clientManager.getClient({\n clientId,\n });\n\n if (!client) {\n log('Client not found when joining room', {\n 'Client ID': clientId || '-',\n 'Room Name': roomName,\n });\n\n return;\n }\n\n // Check if client can join multiple rooms\n const canJoinMultipleRooms = this.options.rooms?.clientCanJoinMultipleRooms ?? true; // Default to true for backward compatibility\n\n if (!canJoinMultipleRooms && client.roomName) {\n // Remove client from current room before joining new one\n this.roomManager.removeClientFromRoom({\n roomName: client.roomName,\n clientId,\n broadcast: false, // Don't broadcast here, will broadcast after adding to new room\n });\n }\n\n // Update client with user in client manager\n this.clientManager.updateClient({\n clientId,\n key: 'user',\n data: userData,\n });\n\n this.roomManager.addClientToRoom({\n clientId,\n user: userData,\n roomName,\n });\n }\n\n public async joinRoom({\n ws,\n userId,\n userType,\n username,\n roomName,\n }: {\n ws: WebSocket;\n userId?: number;\n userType?: string;\n username?: string;\n roomName: string;\n }) {\n const clientId = this.clientManager.getClientId({ ws });\n\n if (!clientId) {\n // throw new Error('Client ID not found when joining room');\n\n logger.warn({\n message: 'Client ID not found when joining room',\n meta: {\n // 'WebSocket ID': ws?.id || '-',\n 'Room Name': roomName,\n },\n });\n\n return;\n }\n\n // Check if client is already in room\n const isClientInRoom = this.roomManager.isClientInRoom({\n clientId,\n roomName,\n });\n\n if (isClientInRoom) {\n // throw new Error('Client already in room when joining');\n\n logger.warn({\n message: 'Client already in room when joining',\n meta: {\n // 'WebSocket ID': ws?. || '-',\n 'Room Name': roomName,\n 'Client ID': clientId,\n },\n });\n\n return;\n }\n\n let userData: any = {};\n\n // // Get WebSocket client ID\n // const webSocketId = this.clientManager.getClientId({ ws });\n\n if (userId) {\n // Get user email from database\n const dbEntityManager = this.databaseInstance.getEntityManager();\n\n const getUserQuery = 'SELECT email FROM users WHERE id = ?';\n const getUserParams = [userId];\n\n const getUserResult = await dbEntityManager.execute(getUserQuery, getUserParams);\n\n if (!getUserResult || getUserResult.length === 0) {\n throw new Error('User not found in database');\n }\n\n const user = getUserResult[0];\n\n userData = {\n id: userId,\n ...user,\n };\n }\n\n // userData.uniqueId = webSocketId;\n\n if (username) {\n userData.username = username;\n }\n\n userData.userType = userType;\n\n // if user with same email is already connected, disconnect the previous connection\n // const existingClient =\n // this.clientManager.getClientByKey({\n // key: 'user.email',\n // value: user.email,\n // });\n\n // if (existingClient) {\n // if (existingClient.ws) {\n // this.clientManager.disconnectClient({\n // clientId: existingClient.clientId,\n // });\n // } else {\n // // Publish to Redis that we should disconnect this client\n // this.redisInstance.publisherClient.publish(\n // WebSocketRedisSubscriberEvent.DisconnectClient,\n // JSON.stringify({\n // clientId,\n // workerId: this.workerId,\n // }),\n // );\n // }\n // }\n\n this.onJoinRoom({\n clientId,\n roomName,\n userData,\n });\n\n // Let other workers know that the client has joined the room\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.ClientJoinedRoom,\n JSON.stringify({\n clientId,\n user: userData,\n roomName,\n workerId: this.workerId,\n }),\n );\n\n return true;\n }\n\n public sendClientMessage = (ws: WebSocket, data: unknown, binary: boolean = false): void => {\n const webSocketMessage = JSON.stringify(data);\n\n ws.send(webSocketMessage, { binary });\n };\n\n public sendMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessage,\n JSON.stringify(formattedData),\n );\n };\n\n public sendMessageToAll = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.SendMessageToAll,\n JSON.stringify(formattedData),\n );\n };\n\n public sendCustomMessage = ({ data }: { data: unknown }): void => {\n const formattedData = {\n ...(data as object),\n workerId: this.workerId,\n };\n\n this.redisInstance.publisherClient.publish(WebSocketRedisSubscriberEvent.Custom, JSON.stringify(formattedData));\n };\n\n public getClients({ userType }: { userType?: string }): any[] {\n return this.clientManager.getClients({ userType });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,10 +1,47 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { WebSocketServer as NativeWebSocketServer, WebSocket } from 'ws';
|
|
2
|
+
import type WebSocketServerInstance from './websocket-server.js';
|
|
2
3
|
import type DatabaseInstance from '../database/instance.js';
|
|
3
4
|
import type QueueManager from '../queue/manager.js';
|
|
4
5
|
import type RedisInstance from '../redis/instance.js';
|
|
5
6
|
import type { WebSocketServerBaseControllerType } from './controller/server/base.interface.js';
|
|
6
7
|
import type { WebSocketClientBaseControllerType } from './controller/client/base.interface.js';
|
|
7
8
|
export type WebSocketType = 'server' | 'client';
|
|
9
|
+
export interface WebSocketSubscriberHandlerContext<TMessage = any> {
|
|
10
|
+
/** Redis channel that emitted the message */
|
|
11
|
+
channel: string;
|
|
12
|
+
/** Parsed message payload */
|
|
13
|
+
message: TMessage;
|
|
14
|
+
/** Running WebSocket server instance */
|
|
15
|
+
webSocketServer: WebSocketServerInstance;
|
|
16
|
+
/** Database instance bound to the application */
|
|
17
|
+
databaseInstance: DatabaseInstance;
|
|
18
|
+
/** Redis instance used by the framework */
|
|
19
|
+
redisInstance: RedisInstance;
|
|
20
|
+
/** Queue manager instance used by the framework */
|
|
21
|
+
queueManager: QueueManager;
|
|
22
|
+
}
|
|
23
|
+
export type WebSocketSubscriberHandler<TMessage = any> = (context: WebSocketSubscriberHandlerContext<TMessage>) => unknown | Promise<unknown>;
|
|
24
|
+
export type WebSocketSubscriberMatcher<TMessage = any> = string | RegExp | ((context: WebSocketSubscriberHandlerContext<TMessage>) => boolean);
|
|
25
|
+
export interface WebSocketSubscriberDefinition<TMessage = any> {
|
|
26
|
+
/** Optional identifier, used for logging and debugging */
|
|
27
|
+
name?: string;
|
|
28
|
+
/** Optional description to document the handler */
|
|
29
|
+
description?: string;
|
|
30
|
+
/** Optional priority to control execution order (higher runs first) */
|
|
31
|
+
priority?: number;
|
|
32
|
+
/** Optional explicit channel filters */
|
|
33
|
+
channels?: string[];
|
|
34
|
+
/** Optional matchers that allow advanced filtering */
|
|
35
|
+
matchers?: WebSocketSubscriberMatcher<TMessage>[];
|
|
36
|
+
/** Handler that will be invoked when the message is matched */
|
|
37
|
+
handle: WebSocketSubscriberHandler<TMessage>;
|
|
38
|
+
}
|
|
39
|
+
export interface WebSocketSubscriberHandlersConfig {
|
|
40
|
+
/** Directory that contains subscriber handler modules */
|
|
41
|
+
directory?: string;
|
|
42
|
+
/** Additional handlers defined inline */
|
|
43
|
+
handlers?: WebSocketSubscriberDefinition[];
|
|
44
|
+
}
|
|
8
45
|
export interface WebSocketDebugOptions {
|
|
9
46
|
printRoutes?: boolean;
|
|
10
47
|
printConnectedClients?: 'simple' | 'table';
|
|
@@ -16,7 +53,7 @@ export interface WebSocketMessage<TData = unknown> {
|
|
|
16
53
|
}
|
|
17
54
|
export interface WebSocketEventsConfig {
|
|
18
55
|
onServerStarted?: ({ webSocketServer }: {
|
|
19
|
-
webSocketServer:
|
|
56
|
+
webSocketServer: NativeWebSocketServer;
|
|
20
57
|
}) => void;
|
|
21
58
|
onConnected?: ({ ws, clientId, joinRoom, }: {
|
|
22
59
|
ws: WebSocket;
|
|
@@ -84,6 +121,8 @@ export interface WebSocketOptions {
|
|
|
84
121
|
debug?: WebSocketDebugOptions;
|
|
85
122
|
/** WebSocket events */
|
|
86
123
|
events?: WebSocketEventsConfig;
|
|
124
|
+
/** WebSocket subscriber handlers configuration */
|
|
125
|
+
subscriberHandlers?: WebSocketSubscriberHandlersConfig;
|
|
87
126
|
}
|
|
88
127
|
export interface WebSocketRoute {
|
|
89
128
|
/** WebSocket route type */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.interface.d.ts","sourceRoot":"","sources":["../../src/websocket/websocket.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"websocket.interface.d.ts","sourceRoot":"","sources":["../../src/websocket/websocket.interface.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/websocket/websocket.interface.ts"],
|
|
4
|
-
"sourcesContent": ["import type {
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { WebSocketServer as NativeWebSocketServer, WebSocket } from 'ws';\nimport type WebSocketServerInstance from './websocket-server.js';\nimport type DatabaseInstance from '../database/instance.js';\nimport type QueueManager from '../queue/manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport type { WebSocketServerBaseControllerType } from './controller/server/base.interface.js';\nimport type { WebSocketClientBaseControllerType } from './controller/client/base.interface.js';\n\nexport type WebSocketType = 'server' | 'client';\n\nexport interface WebSocketSubscriberHandlerContext<TMessage = any> {\n /** Redis channel that emitted the message */\n channel: string;\n\n /** Parsed message payload */\n message: TMessage;\n\n /** Running WebSocket server instance */\n webSocketServer: WebSocketServerInstance;\n\n /** Database instance bound to the application */\n databaseInstance: DatabaseInstance;\n\n /** Redis instance used by the framework */\n redisInstance: RedisInstance;\n\n /** Queue manager instance used by the framework */\n queueManager: QueueManager;\n}\n\nexport type WebSocketSubscriberHandler<TMessage = any> = (\n context: WebSocketSubscriberHandlerContext<TMessage>,\n) => unknown | Promise<unknown>;\n\nexport type WebSocketSubscriberMatcher<TMessage = any> =\n | string\n | RegExp\n | ((context: WebSocketSubscriberHandlerContext<TMessage>) => boolean);\n\nexport interface WebSocketSubscriberDefinition<TMessage = any> {\n /** Optional identifier, used for logging and debugging */\n name?: string;\n\n /** Optional description to document the handler */\n description?: string;\n\n /** Optional priority to control execution order (higher runs first) */\n priority?: number;\n\n /** Optional explicit channel filters */\n channels?: string[];\n\n /** Optional matchers that allow advanced filtering */\n matchers?: WebSocketSubscriberMatcher<TMessage>[];\n\n /** Handler that will be invoked when the message is matched */\n handle: WebSocketSubscriberHandler<TMessage>;\n}\n\nexport interface WebSocketSubscriberHandlersConfig {\n /** Directory that contains subscriber handler modules */\n directory?: string;\n\n /** Additional handlers defined inline */\n handlers?: WebSocketSubscriberDefinition[];\n}\n\nexport interface WebSocketDebugOptions {\n printRoutes?: boolean;\n printConnectedClients?: 'simple' | 'table';\n}\n\nexport interface WebSocketMessage<TData = unknown> {\n type: string;\n action: string;\n data: TData;\n}\n\nexport interface WebSocketEventsConfig {\n onServerStarted?: ({ webSocketServer }: { webSocketServer: NativeWebSocketServer }) => void;\n onConnected?: ({\n ws,\n clientId,\n joinRoom,\n }: {\n ws: WebSocket;\n clientId: string;\n joinRoom({\n userId,\n userType,\n username,\n roomName,\n }: {\n userId?: string;\n userType?: string;\n username?: string;\n roomName: string;\n }): void;\n }) => void;\n onDisconnected?: ({ clientId }: { clientId?: string }) => void;\n onError?: ({ error }: { error: Error }) => void;\n onMessage?: <TData = unknown>({\n ws,\n clientId,\n data,\n redisInstance,\n queueManager,\n databaseInstance,\n }: {\n ws: WebSocket;\n clientId: string;\n data: WebSocketMessage<TData>;\n redisInstance: RedisInstance;\n queueManager: QueueManager;\n databaseInstance: DatabaseInstance;\n }) => void;\n /** Fired when attempting to reconnect */\n onReconnecting?: ({ attempt, delay }: { attempt: number; delay: number }) => void;\n /** Fired when reconnection is successful */\n onReconnected?: ({ clientId }: { clientId?: string }) => void;\n /** Fired when max reconnection attempts are reached */\n onReconnectFailed?: ({ attempts }: { attempts: number }) => void;\n}\n\nexport interface WebSocketOptions {\n /** WebSocket type */\n type: WebSocketType;\n\n /** WebSocket host */\n host?: string;\n\n /** WebSocket URL */\n url: string;\n\n /** WebSocket controllers directory */\n controllersDirectory: string;\n\n /** Disconnect inactive WebSocket clients configuration */\n disconnectInactiveClients?: {\n /** Whether to enable disconnection of inactive WebSocket clients */\n enabled?: boolean;\n\n /** Interval check time in milliseconds */\n intervalCheckTime?: number;\n\n /** Inactive time in milliseconds */\n inactiveTime?: number;\n\n /** Whether to log disconnection of inactive WebSocket clients */\n log?: boolean;\n };\n\n rooms?: {\n enabled?: boolean;\n clientCanJoinMultipleRooms?: boolean;\n };\n\n /** WebSocket debug options */\n debug?: WebSocketDebugOptions;\n\n /** WebSocket events */\n events?: WebSocketEventsConfig;\n\n /** WebSocket subscriber handlers configuration */\n subscriberHandlers?: WebSocketSubscriberHandlersConfig;\n}\n\nexport interface WebSocketRoute {\n /** WebSocket route type */\n type: string;\n\n /** WebSocket route controller name */\n controllerName: string;\n\n /** WebSocket route controller */\n controller?: WebSocketServerBaseControllerType | WebSocketClientBaseControllerType;\n\n /** WebSocket route action */\n action: string;\n}\n\nexport interface WebSocketConstructorParams {\n /** WebSocket options */\n options: WebSocketOptions;\n\n /** WebSocket routes */\n routes: WebSocketRoute[];\n\n /** Redis instance */\n redisInstance: RedisInstance;\n\n /** Queue manager */\n queueManager: QueueManager;\n\n /** Database instance */\n databaseInstance: DatabaseInstance;\n}\n\nexport interface WebSocketConnectedClientData {\n /** WebSocket client */\n ws: WebSocket | null;\n\n /** Last activity time */\n lastActivity: number;\n\n /** Client username */\n username?: string;\n}\n\nexport interface WebSocketMessageResponse {\n error?: string;\n}\n\nexport interface WebSocketMessageHandler<TData = unknown, TResponse = WebSocketMessageResponse> {\n (ws: WebSocket, clientId: string, data: TData): Promise<TResponse> | TResponse;\n}\n\nexport enum WebSocketRedisSubscriberEvent {\n ClientConnected = 'clientConnected',\n ClientDisconnected = 'clientDisconnected',\n DisconnectClient = 'disconnectClient',\n ClientJoinedRoom = 'clientJoinedRoom',\n ClientLeftRoom = 'clientLeftRoom',\n SendMessage = 'sendMessage',\n SendMessageToAll = 'sendMessageToAll',\n MessageError = 'messageError',\n QueueJobCompleted = 'queueJobCompleted',\n QueueJobError = 'queueJobError',\n Custom = 'custom',\n}\n"],
|
|
5
|
+
"mappings": "AAyNO,IAAK,gCAAL,kBAAKA,mCAAL;AACL,EAAAA,+BAAA,qBAAkB;AAClB,EAAAA,+BAAA,wBAAqB;AACrB,EAAAA,+BAAA,sBAAmB;AACnB,EAAAA,+BAAA,sBAAmB;AACnB,EAAAA,+BAAA,oBAAiB;AACjB,EAAAA,+BAAA,iBAAc;AACd,EAAAA,+BAAA,sBAAmB;AACnB,EAAAA,+BAAA,kBAAe;AACf,EAAAA,+BAAA,uBAAoB;AACpB,EAAAA,+BAAA,mBAAgB;AAChB,EAAAA,+BAAA,YAAS;AAXC,SAAAA;AAAA,GAAA;",
|
|
6
6
|
"names": ["WebSocketRedisSubscriberEvent"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scpxl/nodejs-framework",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
4
4
|
"description": "PXL Node.js Framework",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/PXLbros/pxl-nodejs-framework.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/PXLbros/pxl-nodejs-framework#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/PXLbros/pxl-nodejs-framework/issues"
|
|
12
|
+
},
|
|
5
13
|
"engines": {
|
|
6
14
|
"node": ">=22.0.0"
|
|
7
15
|
},
|
|
@@ -127,7 +135,8 @@
|
|
|
127
135
|
"yalc:publish": "yalc publish",
|
|
128
136
|
"yalc:push": "yalc push",
|
|
129
137
|
"dev:yalc": "npm run build && npm run yalc:push",
|
|
130
|
-
"example": "
|
|
138
|
+
"example": "node scripts/run-example.js",
|
|
139
|
+
"example:hello-world": "concurrently --kill-others-on-fail \"npm run example -- hello-world/backend\" \"npm run example -- hello-world/frontend\""
|
|
131
140
|
},
|
|
132
141
|
"keywords": [],
|
|
133
142
|
"author": "PXL <devops@pxlagency.com>",
|