@scpxl/nodejs-framework 1.0.32 → 1.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/base-application.d.ts +7 -7
- package/dist/application/base-application.d.ts.map +1 -1
- package/dist/application/base-application.interface.d.ts +1 -1
- package/dist/application/base-application.interface.d.ts.map +1 -1
- package/dist/application/base-application.js +114 -14
- package/dist/application/base-application.js.map +2 -2
- package/dist/application/web-application.d.ts.map +1 -1
- package/dist/application/web-application.js +2 -1
- package/dist/application/web-application.js.map +2 -2
- package/dist/cache/manager.d.ts +1 -0
- package/dist/cache/manager.d.ts.map +1 -1
- package/dist/cache/manager.js +11 -2
- package/dist/cache/manager.js.map +2 -2
- package/dist/cli/index.js +28 -12
- package/dist/cli/index.js.map +2 -2
- package/dist/config/schema.d.ts +2 -63
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +1 -7
- package/dist/config/schema.js.map +2 -2
- package/dist/event/manager.js +2 -2
- package/dist/event/manager.js.map +2 -2
- package/dist/logger/logger.js +0 -1
- package/dist/logger/logger.js.map +2 -2
- package/dist/queue/manager.d.ts.map +1 -1
- package/dist/queue/manager.js +19 -7
- package/dist/queue/manager.js.map +2 -2
- package/dist/redis/instance.d.ts.map +1 -1
- package/dist/redis/instance.js +9 -1
- package/dist/redis/instance.js.map +2 -2
- package/dist/redis/manager.d.ts.map +1 -1
- package/dist/redis/manager.js +26 -16
- package/dist/redis/manager.js.map +3 -3
- package/dist/webserver/controller/entity.js +1 -1
- package/dist/webserver/controller/entity.js.map +2 -2
- package/dist/webserver/controller/health.js.map +1 -1
- package/dist/webserver/util.d.ts +1 -1
- package/dist/webserver/util.d.ts.map +1 -1
- package/dist/webserver/util.js +5 -23
- package/dist/webserver/util.js.map +2 -2
- package/dist/webserver/webserver.d.ts +2 -13
- package/dist/webserver/webserver.d.ts.map +1 -1
- package/dist/webserver/webserver.interface.d.ts +1 -21
- package/dist/webserver/webserver.interface.d.ts.map +1 -1
- package/dist/webserver/webserver.interface.js.map +1 -1
- package/dist/webserver/webserver.js +34 -36
- package/dist/webserver/webserver.js.map +2 -2
- package/dist/websocket/define-subscriber.d.ts +8 -1
- package/dist/websocket/define-subscriber.d.ts.map +1 -1
- package/dist/websocket/define-subscriber.js +2 -1
- package/dist/websocket/define-subscriber.js.map +2 -2
- package/dist/websocket/index.d.ts +4 -0
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +43 -1
- package/dist/websocket/index.js.map +2 -2
- package/dist/websocket/subscriber-middleware.d.ts +52 -0
- package/dist/websocket/subscriber-middleware.d.ts.map +1 -0
- package/dist/websocket/subscriber-middleware.js +200 -0
- package/dist/websocket/subscriber-middleware.js.map +7 -0
- package/dist/websocket/subscriber-utils.d.ts +88 -0
- package/dist/websocket/subscriber-utils.d.ts.map +1 -0
- package/dist/websocket/subscriber-utils.js +227 -0
- package/dist/websocket/subscriber-utils.js.map +7 -0
- package/dist/websocket/utils.d.ts.map +1 -1
- package/dist/websocket/utils.js +5 -1
- package/dist/websocket/utils.js.map +2 -2
- package/dist/websocket/websocket-base.d.ts.map +1 -1
- package/dist/websocket/websocket-base.js +9 -1
- package/dist/websocket/websocket-base.js.map +2 -2
- package/dist/websocket/websocket-server.d.ts +41 -1
- package/dist/websocket/websocket-server.d.ts.map +1 -1
- package/dist/websocket/websocket-server.js +90 -7
- package/dist/websocket/websocket-server.js.map +2 -2
- package/dist/websocket/websocket.interface.d.ts +7 -0
- package/dist/websocket/websocket.interface.d.ts.map +1 -1
- package/dist/websocket/websocket.interface.js.map +2 -2
- package/package.json +5 -6
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
import { Logger } from "../logger/index.js";
|
|
4
|
+
async function executeWithMiddleware(handler, middleware, context) {
|
|
5
|
+
for (const mw of middleware) {
|
|
6
|
+
try {
|
|
7
|
+
const shouldContinue = mw.onBefore ? await mw.onBefore(context) : true;
|
|
8
|
+
if (!shouldContinue) {
|
|
9
|
+
Logger.info({
|
|
10
|
+
message: "Middleware skipped handler execution",
|
|
11
|
+
meta: {
|
|
12
|
+
middleware: mw.name,
|
|
13
|
+
channel: context.channel
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
} catch (error) {
|
|
19
|
+
Logger.error({
|
|
20
|
+
message: "Middleware onBefore failed",
|
|
21
|
+
meta: {
|
|
22
|
+
middleware: mw.name,
|
|
23
|
+
channel: context.channel,
|
|
24
|
+
error: error instanceof Error ? error.message : String(error)
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
let result;
|
|
31
|
+
try {
|
|
32
|
+
result = await handler(context);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
for (const mw of middleware) {
|
|
35
|
+
if (!mw.onError) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const shouldSuppress = await mw.onError(context, error instanceof Error ? error : new Error(String(error)));
|
|
40
|
+
if (shouldSuppress) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
} catch (mwError) {
|
|
44
|
+
Logger.error({
|
|
45
|
+
message: "Middleware onError failed",
|
|
46
|
+
meta: {
|
|
47
|
+
middleware: mw.name,
|
|
48
|
+
channel: context.channel,
|
|
49
|
+
error: mwError instanceof Error ? mwError.message : String(mwError)
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
for (const mw of middleware) {
|
|
57
|
+
if (!mw.onAfter) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
await mw.onAfter(context, result);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
Logger.error({
|
|
64
|
+
message: "Middleware onAfter failed",
|
|
65
|
+
meta: {
|
|
66
|
+
middleware: mw.name,
|
|
67
|
+
channel: context.channel,
|
|
68
|
+
error: error instanceof Error ? error.message : String(error)
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
__name(executeWithMiddleware, "executeWithMiddleware");
|
|
75
|
+
const loggingMiddleware = /* @__PURE__ */ __name((handlerName) => ({
|
|
76
|
+
name: "logging",
|
|
77
|
+
onBefore: /* @__PURE__ */ __name((context) => {
|
|
78
|
+
Logger.info({
|
|
79
|
+
message: `${handlerName}: Starting execution`,
|
|
80
|
+
meta: {
|
|
81
|
+
channel: context.channel
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return true;
|
|
85
|
+
}, "onBefore"),
|
|
86
|
+
onAfter: /* @__PURE__ */ __name((context, result) => {
|
|
87
|
+
Logger.info({
|
|
88
|
+
message: `${handlerName}: Completed successfully`,
|
|
89
|
+
meta: {
|
|
90
|
+
channel: context.channel,
|
|
91
|
+
resultType: typeof result
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}, "onAfter"),
|
|
95
|
+
onError: /* @__PURE__ */ __name((context, error) => {
|
|
96
|
+
Logger.error({
|
|
97
|
+
message: `${handlerName}: Failed`,
|
|
98
|
+
meta: {
|
|
99
|
+
channel: context.channel,
|
|
100
|
+
error: error.message
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return false;
|
|
104
|
+
}, "onError")
|
|
105
|
+
}), "loggingMiddleware");
|
|
106
|
+
const timingMiddleware = /* @__PURE__ */ __name(() => {
|
|
107
|
+
const startTimes = /* @__PURE__ */ new Map();
|
|
108
|
+
return {
|
|
109
|
+
name: "timing",
|
|
110
|
+
onBefore: /* @__PURE__ */ __name((context) => {
|
|
111
|
+
startTimes.set(context.channel, Date.now());
|
|
112
|
+
return true;
|
|
113
|
+
}, "onBefore"),
|
|
114
|
+
onAfter: /* @__PURE__ */ __name((context) => {
|
|
115
|
+
const startTime = startTimes.get(context.channel);
|
|
116
|
+
if (startTime) {
|
|
117
|
+
const duration = Date.now() - startTime;
|
|
118
|
+
startTimes.delete(context.channel);
|
|
119
|
+
Logger.info({
|
|
120
|
+
message: "Handler execution timing",
|
|
121
|
+
meta: {
|
|
122
|
+
channel: context.channel,
|
|
123
|
+
durationMs: duration
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}, "onAfter"),
|
|
128
|
+
onError: /* @__PURE__ */ __name((context) => {
|
|
129
|
+
startTimes.delete(context.channel);
|
|
130
|
+
return false;
|
|
131
|
+
}, "onError")
|
|
132
|
+
};
|
|
133
|
+
}, "timingMiddleware");
|
|
134
|
+
const validationMiddleware = /* @__PURE__ */ __name((validator) => ({
|
|
135
|
+
name: "validation",
|
|
136
|
+
onBefore: /* @__PURE__ */ __name(async (context) => {
|
|
137
|
+
try {
|
|
138
|
+
await validator(context.message);
|
|
139
|
+
return true;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
Logger.warn({
|
|
142
|
+
message: "Message validation failed",
|
|
143
|
+
meta: {
|
|
144
|
+
channel: context.channel,
|
|
145
|
+
error: error instanceof Error ? error.message : String(error)
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}, "onBefore")
|
|
151
|
+
}), "validationMiddleware");
|
|
152
|
+
const rateLimitMiddleware = /* @__PURE__ */ __name((maxExecutions, windowMs) => {
|
|
153
|
+
const executionTimes = /* @__PURE__ */ new Map();
|
|
154
|
+
return {
|
|
155
|
+
name: "rate-limit",
|
|
156
|
+
onBefore: /* @__PURE__ */ __name((context) => {
|
|
157
|
+
const channel = context.channel;
|
|
158
|
+
const now = Date.now();
|
|
159
|
+
const times = executionTimes.get(channel) ?? [];
|
|
160
|
+
const recentTimes = times.filter((t) => now - t < windowMs);
|
|
161
|
+
if (recentTimes.length >= maxExecutions) {
|
|
162
|
+
Logger.warn({
|
|
163
|
+
message: "Rate limit exceeded",
|
|
164
|
+
meta: {
|
|
165
|
+
channel,
|
|
166
|
+
maxExecutions,
|
|
167
|
+
windowMs
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
recentTimes.push(now);
|
|
173
|
+
executionTimes.set(channel, recentTimes);
|
|
174
|
+
return true;
|
|
175
|
+
}, "onBefore")
|
|
176
|
+
};
|
|
177
|
+
}, "rateLimitMiddleware");
|
|
178
|
+
const errorRecoveryMiddleware = /* @__PURE__ */ __name((maxRetries = 3, _delayMs = 1e3) => ({
|
|
179
|
+
name: "error-recovery",
|
|
180
|
+
onError: /* @__PURE__ */ __name(async (context, error) => {
|
|
181
|
+
Logger.warn({
|
|
182
|
+
message: "Handler error, could implement retry logic",
|
|
183
|
+
meta: {
|
|
184
|
+
channel: context.channel,
|
|
185
|
+
error: error.message,
|
|
186
|
+
suggestedRetries: maxRetries
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
return false;
|
|
190
|
+
}, "onError")
|
|
191
|
+
}), "errorRecoveryMiddleware");
|
|
192
|
+
export {
|
|
193
|
+
errorRecoveryMiddleware,
|
|
194
|
+
executeWithMiddleware,
|
|
195
|
+
loggingMiddleware,
|
|
196
|
+
rateLimitMiddleware,
|
|
197
|
+
timingMiddleware,
|
|
198
|
+
validationMiddleware
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=subscriber-middleware.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/websocket/subscriber-middleware.ts"],
|
|
4
|
+
"sourcesContent": ["import type { WebSocketSubscriberHandler, WebSocketSubscriberHandlerContext } from './websocket.interface.js';\nimport { Logger } from '../logger/index.js';\n\n/**\n * Middleware that can intercept and modify subscriber handler execution\n */\nexport interface WebSocketSubscriberMiddleware {\n /**\n * Unique identifier for the middleware\n */\n name: string;\n\n /**\n * Runs before the handler\n * Return false to skip handler execution\n */\n onBefore?: (context: WebSocketSubscriberHandlerContext) => boolean | Promise<boolean>;\n\n /**\n * Runs after successful handler execution\n */\n onAfter?: (context: WebSocketSubscriberHandlerContext, result: unknown) => void | Promise<void>;\n\n /**\n * Runs on handler error\n * Return true to suppress the error, false to rethrow\n */\n onError?: (context: WebSocketSubscriberHandlerContext, error: Error) => boolean | Promise<boolean>;\n}\n\n/**\n * Execute middleware pipeline and handler\n * @param handler - The handler to execute\n * @param middleware - Array of middleware to apply\n * @param context - Handler context\n */\nexport async function executeWithMiddleware(\n handler: WebSocketSubscriberHandler,\n middleware: WebSocketSubscriberMiddleware[],\n context: WebSocketSubscriberHandlerContext,\n): Promise<void> {\n // Execute \"before\" middleware\n for (const mw of middleware) {\n try {\n const shouldContinue = mw.onBefore ? await mw.onBefore(context) : true;\n if (!shouldContinue) {\n Logger.info({\n message: 'Middleware skipped handler execution',\n meta: {\n middleware: mw.name,\n channel: context.channel,\n },\n });\n return;\n }\n } catch (error) {\n Logger.error({\n message: 'Middleware onBefore failed',\n meta: {\n middleware: mw.name,\n channel: context.channel,\n error: error instanceof Error ? error.message : String(error),\n },\n });\n throw error;\n }\n }\n\n // Execute handler\n let result: unknown;\n try {\n result = await handler(context);\n } catch (error) {\n // Execute \"error\" middleware\n for (const mw of middleware) {\n if (!mw.onError) {\n continue;\n }\n\n try {\n const shouldSuppress = await mw.onError(context, error instanceof Error ? error : new Error(String(error)));\n if (shouldSuppress) {\n return;\n }\n } catch (mwError) {\n Logger.error({\n message: 'Middleware onError failed',\n meta: {\n middleware: mw.name,\n channel: context.channel,\n error: mwError instanceof Error ? mwError.message : String(mwError),\n },\n });\n }\n }\n\n throw error;\n }\n\n // Execute \"after\" middleware\n for (const mw of middleware) {\n if (!mw.onAfter) {\n continue;\n }\n\n try {\n await mw.onAfter(context, result);\n } catch (error) {\n Logger.error({\n message: 'Middleware onAfter failed',\n meta: {\n middleware: mw.name,\n channel: context.channel,\n error: error instanceof Error ? error.message : String(error),\n },\n });\n }\n }\n}\n\n/**\n * Built-in middleware for logging handler execution\n */\nexport const loggingMiddleware = (handlerName: string): WebSocketSubscriberMiddleware => ({\n name: 'logging',\n onBefore: context => {\n Logger.info({\n message: `${handlerName}: Starting execution`,\n meta: {\n channel: context.channel,\n },\n });\n return true;\n },\n onAfter: (context, result) => {\n Logger.info({\n message: `${handlerName}: Completed successfully`,\n meta: {\n channel: context.channel,\n resultType: typeof result,\n },\n });\n },\n onError: (context, error) => {\n Logger.error({\n message: `${handlerName}: Failed`,\n meta: {\n channel: context.channel,\n error: error.message,\n },\n });\n return false; // Don't suppress the error\n },\n});\n\n/**\n * Built-in middleware for timing handler execution\n */\nexport const timingMiddleware = (): WebSocketSubscriberMiddleware => {\n const startTimes = new Map<string, number>();\n\n return {\n name: 'timing',\n onBefore: context => {\n startTimes.set(context.channel, Date.now());\n return true;\n },\n onAfter: context => {\n const startTime = startTimes.get(context.channel);\n if (startTime) {\n const duration = Date.now() - startTime;\n startTimes.delete(context.channel);\n Logger.info({\n message: 'Handler execution timing',\n meta: {\n channel: context.channel,\n durationMs: duration,\n },\n });\n }\n },\n onError: context => {\n startTimes.delete(context.channel);\n return false;\n },\n };\n};\n\n/**\n * Built-in middleware for validating message structure\n */\nexport const validationMiddleware = (\n validator: (message: unknown) => void | Promise<void>,\n): WebSocketSubscriberMiddleware => ({\n name: 'validation',\n onBefore: async context => {\n try {\n await validator(context.message);\n return true;\n } catch (error) {\n Logger.warn({\n message: 'Message validation failed',\n meta: {\n channel: context.channel,\n error: error instanceof Error ? error.message : String(error),\n },\n });\n throw error;\n }\n },\n});\n\n/**\n * Built-in middleware for rate limiting\n */\nexport const rateLimitMiddleware = (maxExecutions: number, windowMs: number): WebSocketSubscriberMiddleware => {\n const executionTimes = new Map<string, number[]>();\n\n return {\n name: 'rate-limit',\n onBefore: context => {\n const channel = context.channel;\n const now = Date.now();\n const times = executionTimes.get(channel) ?? [];\n\n // Remove old entries\n const recentTimes = times.filter(t => now - t < windowMs);\n\n if (recentTimes.length >= maxExecutions) {\n Logger.warn({\n message: 'Rate limit exceeded',\n meta: {\n channel,\n maxExecutions,\n windowMs,\n },\n });\n return false;\n }\n\n recentTimes.push(now);\n executionTimes.set(channel, recentTimes);\n return true;\n },\n };\n};\n\n/**\n * Built-in middleware for error handling and recovery\n */\nexport const errorRecoveryMiddleware = (maxRetries = 3, _delayMs = 1000): WebSocketSubscriberMiddleware => ({\n name: 'error-recovery',\n onError: async (context, error) => {\n Logger.warn({\n message: 'Handler error, could implement retry logic',\n meta: {\n channel: context.channel,\n error: error.message,\n suggestedRetries: maxRetries,\n },\n });\n return false; // Don't suppress - let error bubble up\n },\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { WebSocketSubscriberHandler, WebSocketSubscriberHandlerContext, WebSocketSubscriberMatcher } from './websocket.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for building and composing WebSocket subscriber handlers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Create a predicate matcher for message properties
|
|
7
|
+
* @param key - The property key to check
|
|
8
|
+
* @param value - The expected value
|
|
9
|
+
*/
|
|
10
|
+
export declare function matchByProperty(key: string, value: unknown): WebSocketSubscriberMatcher;
|
|
11
|
+
/**
|
|
12
|
+
* Create a predicate matcher for message property patterns
|
|
13
|
+
* @param key - The property key to check
|
|
14
|
+
* @param predicate - Function to test the value
|
|
15
|
+
*/
|
|
16
|
+
export declare function matchByPropertyPredicate<_T = unknown>(key: string, predicate: (value: unknown) => boolean): WebSocketSubscriberMatcher;
|
|
17
|
+
/**
|
|
18
|
+
* Safely get nested property from an object
|
|
19
|
+
* @param obj - The object to search
|
|
20
|
+
* @param path - Dot-notation path (e.g., 'user.id' or 'data.items.0.name')
|
|
21
|
+
*/
|
|
22
|
+
export declare function getNestedProperty(obj: any, path: string): unknown;
|
|
23
|
+
/**
|
|
24
|
+
* Wrap a handler with error handling
|
|
25
|
+
* @param handler - The handler to wrap
|
|
26
|
+
* @param onError - Error handler callback
|
|
27
|
+
* @param throwError - Whether to rethrow the error after handling
|
|
28
|
+
*/
|
|
29
|
+
export declare function withErrorHandler<TMessage = any>(handler: WebSocketSubscriberHandler<TMessage>, onError?: (error: Error, context: WebSocketSubscriberHandlerContext<TMessage>) => void | Promise<void>, _throwError?: boolean): WebSocketSubscriberHandler<TMessage>;
|
|
30
|
+
/**
|
|
31
|
+
* Wrap a handler with logging
|
|
32
|
+
* @param handler - The handler to wrap
|
|
33
|
+
* @param handlerName - Name for logging purposes
|
|
34
|
+
*/
|
|
35
|
+
export declare function withLogging<TMessage = any>(handler: WebSocketSubscriberHandler<TMessage>, handlerName?: string): WebSocketSubscriberHandler<TMessage>;
|
|
36
|
+
/**
|
|
37
|
+
* Wrap a handler with rate limiting
|
|
38
|
+
* @param handler - The handler to wrap
|
|
39
|
+
* @param maxExecutions - Max executions allowed
|
|
40
|
+
* @param windowMs - Time window in milliseconds
|
|
41
|
+
* @param onRateLimited - Optional callback when rate limited
|
|
42
|
+
*/
|
|
43
|
+
export declare function withRateLimit<TMessage = any>(handler: WebSocketSubscriberHandler<TMessage>, maxExecutions: number, windowMs: number, onRateLimited?: (context: WebSocketSubscriberHandlerContext<TMessage>) => void | Promise<void>): WebSocketSubscriberHandler<TMessage>;
|
|
44
|
+
/**
|
|
45
|
+
* Wrap a handler with retry logic
|
|
46
|
+
* @param handler - The handler to wrap
|
|
47
|
+
* @param maxRetries - Maximum number of retries
|
|
48
|
+
* @param delayMs - Delay between retries in milliseconds
|
|
49
|
+
* @param backoffMultiplier - Multiplier for exponential backoff (default: 1, no backoff)
|
|
50
|
+
*/
|
|
51
|
+
export declare function withRetry<TMessage = any>(handler: WebSocketSubscriberHandler<TMessage>, maxRetries: number, delayMs: number, backoffMultiplier?: number): WebSocketSubscriberHandler<TMessage>;
|
|
52
|
+
/**
|
|
53
|
+
* Compose multiple handlers into a single handler
|
|
54
|
+
* Executes handlers sequentially, passing context through each one
|
|
55
|
+
* @param handlers - Array of handlers to compose
|
|
56
|
+
*/
|
|
57
|
+
export declare function composeHandlers<TMessage = any>(handlers: WebSocketSubscriberHandler<TMessage>[]): WebSocketSubscriberHandler<TMessage>;
|
|
58
|
+
/**
|
|
59
|
+
* Create a filter handler that conditionally executes based on a predicate
|
|
60
|
+
* @param predicate - Function that returns true if handler should execute
|
|
61
|
+
* @param handler - The handler to execute conditionally
|
|
62
|
+
*/
|
|
63
|
+
export declare function withFilter<TMessage = any>(predicate: (context: WebSocketSubscriberHandlerContext<TMessage>) => boolean | Promise<boolean>, handler: WebSocketSubscriberHandler<TMessage>): WebSocketSubscriberHandler<TMessage>;
|
|
64
|
+
/**
|
|
65
|
+
* Validate message structure before execution
|
|
66
|
+
* @param validator - Function that validates the message and throws if invalid
|
|
67
|
+
* @param handler - The handler to wrap
|
|
68
|
+
*/
|
|
69
|
+
export declare function withValidation<TMessage = any>(validator: (message: TMessage) => void | Promise<void>, handler: WebSocketSubscriberHandler<TMessage>): WebSocketSubscriberHandler<TMessage>;
|
|
70
|
+
/**
|
|
71
|
+
* Add metadata/context to a handler execution
|
|
72
|
+
* @param metadata - Metadata to add to context
|
|
73
|
+
* @param handler - The handler to wrap
|
|
74
|
+
*/
|
|
75
|
+
export declare function withMetadata<TMessage = any>(metadata: Record<string, unknown>, handler: WebSocketSubscriberHandler<TMessage>): WebSocketSubscriberHandler<TMessage>;
|
|
76
|
+
/**
|
|
77
|
+
* Debounce handler execution by channel
|
|
78
|
+
* @param handler - The handler to wrap
|
|
79
|
+
* @param delayMs - Debounce delay in milliseconds
|
|
80
|
+
*/
|
|
81
|
+
export declare function withDebounce<TMessage = any>(handler: WebSocketSubscriberHandler<TMessage>, delayMs: number): WebSocketSubscriberHandler<TMessage>;
|
|
82
|
+
/**
|
|
83
|
+
* Throttle handler execution by channel
|
|
84
|
+
* @param handler - The handler to wrap
|
|
85
|
+
* @param intervalMs - Throttle interval in milliseconds
|
|
86
|
+
*/
|
|
87
|
+
export declare function withThrottle<TMessage = any>(handler: WebSocketSubscriberHandler<TMessage>, intervalMs: number): WebSocketSubscriberHandler<TMessage>;
|
|
88
|
+
//# sourceMappingURL=subscriber-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriber-utils.d.ts","sourceRoot":"","sources":["../../src/websocket/subscriber-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
import { Logger } from "../logger/index.js";
|
|
4
|
+
function matchByProperty(key, value) {
|
|
5
|
+
return (context) => {
|
|
6
|
+
try {
|
|
7
|
+
const messageValue = getNestedProperty(context.message, key);
|
|
8
|
+
return messageValue === value;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
__name(matchByProperty, "matchByProperty");
|
|
15
|
+
function matchByPropertyPredicate(key, predicate) {
|
|
16
|
+
return (context) => {
|
|
17
|
+
try {
|
|
18
|
+
const messageValue = getNestedProperty(context.message, key);
|
|
19
|
+
return predicate(messageValue);
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
__name(matchByPropertyPredicate, "matchByPropertyPredicate");
|
|
26
|
+
function getNestedProperty(obj, path) {
|
|
27
|
+
const keys = path.split(".");
|
|
28
|
+
let current = obj;
|
|
29
|
+
for (const key of keys) {
|
|
30
|
+
if (current == null) {
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
current = current[key];
|
|
34
|
+
}
|
|
35
|
+
return current;
|
|
36
|
+
}
|
|
37
|
+
__name(getNestedProperty, "getNestedProperty");
|
|
38
|
+
function withErrorHandler(handler, onError, _throwError = false) {
|
|
39
|
+
return async (context) => {
|
|
40
|
+
try {
|
|
41
|
+
return await handler(context);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
44
|
+
if (onError) {
|
|
45
|
+
try {
|
|
46
|
+
await onError(err, context);
|
|
47
|
+
} catch (callbackError) {
|
|
48
|
+
Logger.error({
|
|
49
|
+
message: "Error handler callback failed",
|
|
50
|
+
meta: { originalError: err.message, callbackError }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (_throwError) {
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
__name(withErrorHandler, "withErrorHandler");
|
|
61
|
+
function withLogging(handler, handlerName = "subscriber-handler") {
|
|
62
|
+
return async (context) => {
|
|
63
|
+
const startTime = Date.now();
|
|
64
|
+
const messageKeys = context.message && typeof context.message === "object" ? Object.keys(context.message).slice(0, 5) : [];
|
|
65
|
+
Logger.info({
|
|
66
|
+
message: `${handlerName}: Starting handler execution`,
|
|
67
|
+
meta: {
|
|
68
|
+
channel: context.channel,
|
|
69
|
+
messageKeys
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
try {
|
|
73
|
+
const result = await handler(context);
|
|
74
|
+
const duration = Date.now() - startTime;
|
|
75
|
+
Logger.info({
|
|
76
|
+
message: `${handlerName}: Handler completed successfully`,
|
|
77
|
+
meta: { channel: context.channel, durationMs: duration }
|
|
78
|
+
});
|
|
79
|
+
return result;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const duration = Date.now() - startTime;
|
|
82
|
+
Logger.error({
|
|
83
|
+
message: `${handlerName}: Handler failed`,
|
|
84
|
+
meta: {
|
|
85
|
+
channel: context.channel,
|
|
86
|
+
error: error instanceof Error ? error.message : String(error),
|
|
87
|
+
durationMs: duration
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
__name(withLogging, "withLogging");
|
|
95
|
+
function withRateLimit(handler, maxExecutions, windowMs, onRateLimited) {
|
|
96
|
+
const executionTimes = [];
|
|
97
|
+
return async (context) => {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
while (executionTimes.length > 0 && executionTimes[0] < now - windowMs) {
|
|
100
|
+
executionTimes.shift();
|
|
101
|
+
}
|
|
102
|
+
if (executionTimes.length >= maxExecutions) {
|
|
103
|
+
if (onRateLimited) {
|
|
104
|
+
await onRateLimited(context);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
executionTimes.push(now);
|
|
109
|
+
return await handler(context);
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
__name(withRateLimit, "withRateLimit");
|
|
113
|
+
function withRetry(handler, maxRetries, delayMs, backoffMultiplier = 1) {
|
|
114
|
+
return async (context) => {
|
|
115
|
+
let lastError = null;
|
|
116
|
+
let currentDelay = delayMs;
|
|
117
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
118
|
+
try {
|
|
119
|
+
return await handler(context);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
122
|
+
if (attempt < maxRetries) {
|
|
123
|
+
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
124
|
+
currentDelay = Math.floor(currentDelay * backoffMultiplier);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw lastError;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
__name(withRetry, "withRetry");
|
|
132
|
+
function composeHandlers(handlers) {
|
|
133
|
+
return async (context) => {
|
|
134
|
+
for (const handler of handlers) {
|
|
135
|
+
await handler(context);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
__name(composeHandlers, "composeHandlers");
|
|
140
|
+
function withFilter(predicate, handler) {
|
|
141
|
+
return async (context) => {
|
|
142
|
+
const shouldExecute = await predicate(context);
|
|
143
|
+
if (shouldExecute) {
|
|
144
|
+
return await handler(context);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
__name(withFilter, "withFilter");
|
|
149
|
+
function withValidation(validator, handler) {
|
|
150
|
+
return async (context) => {
|
|
151
|
+
try {
|
|
152
|
+
await validator(context.message);
|
|
153
|
+
return await handler(context);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
Logger.warn({
|
|
156
|
+
message: "Message validation failed",
|
|
157
|
+
meta: {
|
|
158
|
+
channel: context.channel,
|
|
159
|
+
error: error instanceof Error ? error.message : String(error)
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
__name(withValidation, "withValidation");
|
|
167
|
+
function withMetadata(metadata, handler) {
|
|
168
|
+
return async (context) => {
|
|
169
|
+
const enrichedContext = {
|
|
170
|
+
...context,
|
|
171
|
+
metadata
|
|
172
|
+
};
|
|
173
|
+
return await handler(enrichedContext);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
__name(withMetadata, "withMetadata");
|
|
177
|
+
function withDebounce(handler, delayMs) {
|
|
178
|
+
const timers = /* @__PURE__ */ new Map();
|
|
179
|
+
return async (context) => {
|
|
180
|
+
const channel = context.channel;
|
|
181
|
+
const existingTimer = timers.get(channel);
|
|
182
|
+
if (existingTimer) {
|
|
183
|
+
clearTimeout(existingTimer);
|
|
184
|
+
}
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
const timer = setTimeout(async () => {
|
|
187
|
+
try {
|
|
188
|
+
await handler(context);
|
|
189
|
+
} finally {
|
|
190
|
+
timers.delete(channel);
|
|
191
|
+
resolve();
|
|
192
|
+
}
|
|
193
|
+
}, delayMs);
|
|
194
|
+
timers.set(channel, timer);
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
__name(withDebounce, "withDebounce");
|
|
199
|
+
function withThrottle(handler, intervalMs) {
|
|
200
|
+
const lastExecutionTime = /* @__PURE__ */ new Map();
|
|
201
|
+
return async (context) => {
|
|
202
|
+
const channel = context.channel;
|
|
203
|
+
const now = Date.now();
|
|
204
|
+
const lastTime = lastExecutionTime.get(channel) ?? 0;
|
|
205
|
+
if (now - lastTime >= intervalMs) {
|
|
206
|
+
lastExecutionTime.set(channel, now);
|
|
207
|
+
return await handler(context);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
__name(withThrottle, "withThrottle");
|
|
212
|
+
export {
|
|
213
|
+
composeHandlers,
|
|
214
|
+
getNestedProperty,
|
|
215
|
+
matchByProperty,
|
|
216
|
+
matchByPropertyPredicate,
|
|
217
|
+
withDebounce,
|
|
218
|
+
withErrorHandler,
|
|
219
|
+
withFilter,
|
|
220
|
+
withLogging,
|
|
221
|
+
withMetadata,
|
|
222
|
+
withRateLimit,
|
|
223
|
+
withRetry,
|
|
224
|
+
withThrottle,
|
|
225
|
+
withValidation
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=subscriber-utils.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/websocket/subscriber-utils.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n WebSocketSubscriberHandler,\n WebSocketSubscriberHandlerContext,\n WebSocketSubscriberMatcher,\n} from './websocket.interface.js';\nimport { Logger } from '../logger/index.js';\n\n/**\n * Utility functions for building and composing WebSocket subscriber handlers\n */\n\n/**\n * Create a predicate matcher for message properties\n * @param key - The property key to check\n * @param value - The expected value\n */\nexport function matchByProperty(key: string, value: unknown): WebSocketSubscriberMatcher {\n return (context: WebSocketSubscriberHandlerContext) => {\n try {\n const messageValue = getNestedProperty(context.message, key);\n return messageValue === value;\n } catch {\n return false;\n }\n };\n}\n\n/**\n * Create a predicate matcher for message property patterns\n * @param key - The property key to check\n * @param predicate - Function to test the value\n */\nexport function matchByPropertyPredicate<_T = unknown>(\n key: string,\n predicate: (value: unknown) => boolean,\n): WebSocketSubscriberMatcher {\n return (context: WebSocketSubscriberHandlerContext) => {\n try {\n const messageValue = getNestedProperty(context.message, key);\n return predicate(messageValue);\n } catch {\n return false;\n }\n };\n}\n\n/**\n * Safely get nested property from an object\n * @param obj - The object to search\n * @param path - Dot-notation path (e.g., 'user.id' or 'data.items.0.name')\n */\nexport function getNestedProperty(obj: any, path: string): unknown {\n const keys = path.split('.');\n let current = obj;\n\n for (const key of keys) {\n if (current == null) {\n return undefined;\n }\n // eslint-disable-next-line security/detect-object-injection\n current = current[key];\n }\n\n return current;\n}\n\n/**\n * Wrap a handler with error handling\n * @param handler - The handler to wrap\n * @param onError - Error handler callback\n * @param throwError - Whether to rethrow the error after handling\n */\nexport function withErrorHandler<TMessage = any>(\n handler: WebSocketSubscriberHandler<TMessage>,\n onError?: (error: Error, context: WebSocketSubscriberHandlerContext<TMessage>) => void | Promise<void>,\n _throwError = false,\n): WebSocketSubscriberHandler<TMessage> {\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n try {\n return await handler(context);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (onError) {\n try {\n await onError(err, context);\n } catch (callbackError) {\n Logger.error({\n message: 'Error handler callback failed',\n meta: { originalError: err.message, callbackError },\n });\n }\n }\n\n if (_throwError) {\n throw err;\n }\n }\n };\n}\n\n/**\n * Wrap a handler with logging\n * @param handler - The handler to wrap\n * @param handlerName - Name for logging purposes\n */\nexport function withLogging<TMessage = any>(\n handler: WebSocketSubscriberHandler<TMessage>,\n handlerName = 'subscriber-handler',\n): WebSocketSubscriberHandler<TMessage> {\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n const startTime = Date.now();\n const messageKeys =\n context.message && typeof context.message === 'object'\n ? Object.keys(context.message as Record<string, unknown>).slice(0, 5)\n : [];\n Logger.info({\n message: `${handlerName}: Starting handler execution`,\n meta: {\n channel: context.channel,\n messageKeys,\n },\n });\n\n try {\n const result = await handler(context);\n const duration = Date.now() - startTime;\n Logger.info({\n message: `${handlerName}: Handler completed successfully`,\n meta: { channel: context.channel, durationMs: duration },\n });\n return result;\n } catch (error) {\n const duration = Date.now() - startTime;\n Logger.error({\n message: `${handlerName}: Handler failed`,\n meta: {\n channel: context.channel,\n error: error instanceof Error ? error.message : String(error),\n durationMs: duration,\n },\n });\n throw error;\n }\n };\n}\n\n/**\n * Wrap a handler with rate limiting\n * @param handler - The handler to wrap\n * @param maxExecutions - Max executions allowed\n * @param windowMs - Time window in milliseconds\n * @param onRateLimited - Optional callback when rate limited\n */\nexport function withRateLimit<TMessage = any>(\n handler: WebSocketSubscriberHandler<TMessage>,\n maxExecutions: number,\n windowMs: number,\n onRateLimited?: (context: WebSocketSubscriberHandlerContext<TMessage>) => void | Promise<void>,\n): WebSocketSubscriberHandler<TMessage> {\n const executionTimes: number[] = [];\n\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n const now = Date.now();\n\n // Remove old entries outside the window\n while (executionTimes.length > 0 && executionTimes[0] < now - windowMs) {\n executionTimes.shift();\n }\n\n if (executionTimes.length >= maxExecutions) {\n if (onRateLimited) {\n await onRateLimited(context);\n }\n return;\n }\n\n executionTimes.push(now);\n return await handler(context);\n };\n}\n\n/**\n * Wrap a handler with retry logic\n * @param handler - The handler to wrap\n * @param maxRetries - Maximum number of retries\n * @param delayMs - Delay between retries in milliseconds\n * @param backoffMultiplier - Multiplier for exponential backoff (default: 1, no backoff)\n */\nexport function withRetry<TMessage = any>(\n handler: WebSocketSubscriberHandler<TMessage>,\n maxRetries: number,\n delayMs: number,\n backoffMultiplier = 1,\n): WebSocketSubscriberHandler<TMessage> {\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n let lastError: Error | null = null;\n let currentDelay = delayMs;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await handler(context);\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n if (attempt < maxRetries) {\n await new Promise(resolve => setTimeout(resolve, currentDelay));\n currentDelay = Math.floor(currentDelay * backoffMultiplier);\n }\n }\n }\n\n throw lastError;\n };\n}\n\n/**\n * Compose multiple handlers into a single handler\n * Executes handlers sequentially, passing context through each one\n * @param handlers - Array of handlers to compose\n */\nexport function composeHandlers<TMessage = any>(\n handlers: WebSocketSubscriberHandler<TMessage>[],\n): WebSocketSubscriberHandler<TMessage> {\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n for (const handler of handlers) {\n await handler(context);\n }\n };\n}\n\n/**\n * Create a filter handler that conditionally executes based on a predicate\n * @param predicate - Function that returns true if handler should execute\n * @param handler - The handler to execute conditionally\n */\nexport function withFilter<TMessage = any>(\n predicate: (context: WebSocketSubscriberHandlerContext<TMessage>) => boolean | Promise<boolean>,\n handler: WebSocketSubscriberHandler<TMessage>,\n): WebSocketSubscriberHandler<TMessage> {\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n const shouldExecute = await predicate(context);\n if (shouldExecute) {\n return await handler(context);\n }\n };\n}\n\n/**\n * Validate message structure before execution\n * @param validator - Function that validates the message and throws if invalid\n * @param handler - The handler to wrap\n */\nexport function withValidation<TMessage = any>(\n validator: (message: TMessage) => void | Promise<void>,\n handler: WebSocketSubscriberHandler<TMessage>,\n): WebSocketSubscriberHandler<TMessage> {\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n try {\n await validator(context.message);\n return await handler(context);\n } catch (error) {\n Logger.warn({\n message: 'Message validation failed',\n meta: {\n channel: context.channel,\n error: error instanceof Error ? error.message : String(error),\n },\n });\n throw error;\n }\n };\n}\n\n/**\n * Add metadata/context to a handler execution\n * @param metadata - Metadata to add to context\n * @param handler - The handler to wrap\n */\nexport function withMetadata<TMessage = any>(\n metadata: Record<string, unknown>,\n handler: WebSocketSubscriberHandler<TMessage>,\n): WebSocketSubscriberHandler<TMessage> {\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n const enrichedContext = {\n ...context,\n metadata,\n } as any;\n\n return await handler(enrichedContext);\n };\n}\n\n/**\n * Debounce handler execution by channel\n * @param handler - The handler to wrap\n * @param delayMs - Debounce delay in milliseconds\n */\nexport function withDebounce<TMessage = any>(\n handler: WebSocketSubscriberHandler<TMessage>,\n delayMs: number,\n): WebSocketSubscriberHandler<TMessage> {\n const timers = new Map<string, NodeJS.Timeout>();\n\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n const channel = context.channel;\n\n // Cancel previous timer for this channel\n const existingTimer = timers.get(channel);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Set new timer\n return new Promise<void>(resolve => {\n const timer = setTimeout(async () => {\n try {\n await handler(context);\n } finally {\n timers.delete(channel);\n resolve();\n }\n }, delayMs);\n\n timers.set(channel, timer);\n });\n };\n}\n\n/**\n * Throttle handler execution by channel\n * @param handler - The handler to wrap\n * @param intervalMs - Throttle interval in milliseconds\n */\nexport function withThrottle<TMessage = any>(\n handler: WebSocketSubscriberHandler<TMessage>,\n intervalMs: number,\n): WebSocketSubscriberHandler<TMessage> {\n const lastExecutionTime = new Map<string, number>();\n\n return async (context: WebSocketSubscriberHandlerContext<TMessage>) => {\n const channel = context.channel;\n const now = Date.now();\n const lastTime = lastExecutionTime.get(channel) ?? 0;\n\n if (now - lastTime >= intervalMs) {\n lastExecutionTime.set(channel, now);\n return await handler(context);\n }\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/websocket/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/websocket/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAGhC,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAE/F;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBnF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhE"}
|
package/dist/websocket/utils.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
import crypto from "node:crypto";
|
|
3
4
|
import { Logger } from "../logger/index.js";
|
|
4
5
|
function generateClientId() {
|
|
5
|
-
|
|
6
|
+
if (typeof crypto.randomUUID === "function") {
|
|
7
|
+
return crypto.randomUUID().replace(/-/g, "");
|
|
8
|
+
}
|
|
9
|
+
return crypto.randomBytes(16).toString("hex");
|
|
6
10
|
}
|
|
7
11
|
__name(generateClientId, "generateClientId");
|
|
8
12
|
function log(message, meta, options) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/websocket/utils.ts"],
|
|
4
|
-
"sourcesContent": ["import type WebSocket from 'ws';\nimport { Logger } from '../logger/index.js';\n\nexport interface LogOptions {\n muteWorker?: boolean;\n}\n\nexport function generateClientId(): string {\n
|
|
5
|
-
"mappings": ";;
|
|
4
|
+
"sourcesContent": ["import crypto from 'node:crypto';\nimport type WebSocket from 'ws';\nimport { Logger } from '../logger/index.js';\n\nexport interface LogOptions {\n muteWorker?: boolean;\n}\n\nexport function generateClientId(): string {\n if (typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID().replace(/-/g, '');\n }\n\n return crypto.randomBytes(16).toString('hex');\n}\n\nexport function log(message: string, meta?: Record<string, unknown>, options?: LogOptions): void {\n Logger.custom({ level: 'webSocket', message, meta, options });\n}\n\nexport function parseServerMessage(message: WebSocket.Data): Record<string, unknown> {\n let parsedMessage;\n\n try {\n parsedMessage = JSON.parse(message.toString());\n } catch {\n throw new Error('Failed to parse JSON');\n }\n\n if (!parsedMessage) {\n throw new Error('Invalid WebSocket message');\n } else if (!parsedMessage.type) {\n throw new Error('Missing WebSocket message type');\n } else if (!parsedMessage.action) {\n throw new Error('Missing WebSocket message action');\n }\n\n return parsedMessage;\n}\n\nexport function getRouteKey(type: string, action: string): string {\n return `${type}:${action}`;\n}\n"],
|
|
5
|
+
"mappings": ";;AAAA,OAAO,YAAY;AAEnB,SAAS,cAAc;AAMhB,SAAS,mBAA2B;AACzC,MAAI,OAAO,OAAO,eAAe,YAAY;AAC3C,WAAO,OAAO,WAAW,EAAE,QAAQ,MAAM,EAAE;AAAA,EAC7C;AAEA,SAAO,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAC9C;AANgB;AAQT,SAAS,IAAI,SAAiB,MAAgC,SAA4B;AAC/F,SAAO,OAAO,EAAE,OAAO,aAAa,SAAS,MAAM,QAAQ,CAAC;AAC9D;AAFgB;AAIT,SAAS,mBAAmB,SAAkD;AACnF,MAAI;AAEJ,MAAI;AACF,oBAAgB,KAAK,MAAM,QAAQ,SAAS,CAAC;AAAA,EAC/C,QAAQ;AACN,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACxC;AAEA,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C,WAAW,CAAC,cAAc,MAAM;AAC9B,UAAM,IAAI,MAAM,gCAAgC;AAAA,EAClD,WAAW,CAAC,cAAc,QAAQ;AAChC,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AAEA,SAAO;AACT;AAlBgB;AAoBT,SAAS,YAAY,MAAc,QAAwB;AAChE,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAFgB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-base.d.ts","sourceRoot":"","sources":["../../src/websocket/websocket-base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAIvG,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAGhC,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,aAAa;IACzC,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,CAAM;IACxC,SAAS,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAa;IAE1E,SAAS,CAAC,aAAa,EAAE,cAAc,EAAE,CAAM;IAE/C,aAAoB,IAAI,IAAI,aAAa,CAAC;IAE1C,SAAS,CAAC,QAAQ,CAAC,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvE,SAAS,CAAC,QAAQ,CAAC,iBAAiB,IAAI,OAAO;IAC/C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;cAE5D,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"websocket-base.d.ts","sourceRoot":"","sources":["../../src/websocket/websocket-base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAIvG,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAGhC,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,aAAa;IACzC,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,CAAM;IACxC,SAAS,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAa;IAE1E,SAAS,CAAC,aAAa,EAAE,cAAc,EAAE,CAAM;IAE/C,aAAoB,IAAI,IAAI,aAAa,CAAC;IAE1C,SAAS,CAAC,QAAQ,CAAC,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvE,SAAS,CAAC,QAAQ,CAAC,iBAAiB,IAAI,OAAO;IAC/C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;cAE5D,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAiFtF,mBAAmB,CACjC,EAAE,EAAE,SAAS,EACb,OAAO,EAAE,SAAS,CAAC,IAAI,EACvB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAuDxE,SAAS,CAAC,WAAW,IAAI,IAAI;CAiB9B"}
|
|
@@ -46,7 +46,15 @@ class WebSocketBase {
|
|
|
46
46
|
const controllerInstance = new ControllerClass(controllerDependencies);
|
|
47
47
|
const controllerHandler = controllerInstance[route.action];
|
|
48
48
|
const routeKey = getRouteKey(route.type, route.action);
|
|
49
|
-
|
|
49
|
+
if (typeof controllerHandler !== "function") {
|
|
50
|
+
log("Controller action not found", {
|
|
51
|
+
Controller: route.controllerName ?? ControllerClass.name,
|
|
52
|
+
Action: route.action,
|
|
53
|
+
RouteKey: routeKey
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
this.routeHandlers.set(routeKey, controllerHandler.bind(controllerInstance));
|
|
50
58
|
}
|
|
51
59
|
if (this.shouldPrintRoutes()) {
|
|
52
60
|
log("Routes:", { Type: this.type });
|