@lithia-js/core 1.0.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +13 -0
  3. package/LICENSE +21 -0
  4. package/README.md +60 -0
  5. package/dist/config.d.ts +101 -0
  6. package/dist/config.js +113 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/context/event-context.d.ts +53 -0
  9. package/dist/context/event-context.js +42 -0
  10. package/dist/context/event-context.js.map +1 -0
  11. package/dist/context/index.d.ts +16 -0
  12. package/dist/context/index.js +29 -0
  13. package/dist/context/index.js.map +1 -0
  14. package/dist/context/lithia-context.d.ts +47 -0
  15. package/dist/context/lithia-context.js +43 -0
  16. package/dist/context/lithia-context.js.map +1 -0
  17. package/dist/context/route-context.d.ts +74 -0
  18. package/dist/context/route-context.js +42 -0
  19. package/dist/context/route-context.js.map +1 -0
  20. package/dist/env.d.ts +1 -0
  21. package/dist/env.js +32 -0
  22. package/dist/env.js.map +1 -0
  23. package/dist/errors.d.ts +51 -0
  24. package/dist/errors.js +80 -0
  25. package/dist/errors.js.map +1 -0
  26. package/dist/hooks/dependency-hooks.d.ts +105 -0
  27. package/dist/hooks/dependency-hooks.js +96 -0
  28. package/dist/hooks/dependency-hooks.js.map +1 -0
  29. package/dist/hooks/event-hooks.d.ts +61 -0
  30. package/dist/hooks/event-hooks.js +70 -0
  31. package/dist/hooks/event-hooks.js.map +1 -0
  32. package/dist/hooks/index.d.ts +41 -0
  33. package/dist/hooks/index.js +59 -0
  34. package/dist/hooks/index.js.map +1 -0
  35. package/dist/hooks/route-hooks.d.ts +154 -0
  36. package/dist/hooks/route-hooks.js +174 -0
  37. package/dist/hooks/route-hooks.js.map +1 -0
  38. package/dist/lib.d.ts +10 -0
  39. package/dist/lib.js +30 -0
  40. package/dist/lib.js.map +1 -0
  41. package/dist/lithia.d.ts +447 -0
  42. package/dist/lithia.js +649 -0
  43. package/dist/lithia.js.map +1 -0
  44. package/dist/logger.d.ts +11 -0
  45. package/dist/logger.js +55 -0
  46. package/dist/logger.js.map +1 -0
  47. package/dist/module-loader.d.ts +12 -0
  48. package/dist/module-loader.js +78 -0
  49. package/dist/module-loader.js.map +1 -0
  50. package/dist/server/event-processor.d.ts +195 -0
  51. package/dist/server/event-processor.js +253 -0
  52. package/dist/server/event-processor.js.map +1 -0
  53. package/dist/server/http-server.d.ts +196 -0
  54. package/dist/server/http-server.js +295 -0
  55. package/dist/server/http-server.js.map +1 -0
  56. package/dist/server/middlewares/validation.d.ts +12 -0
  57. package/dist/server/middlewares/validation.js +34 -0
  58. package/dist/server/middlewares/validation.js.map +1 -0
  59. package/dist/server/request-processor.d.ts +400 -0
  60. package/dist/server/request-processor.js +652 -0
  61. package/dist/server/request-processor.js.map +1 -0
  62. package/dist/server/request.d.ts +73 -0
  63. package/dist/server/request.js +207 -0
  64. package/dist/server/request.js.map +1 -0
  65. package/dist/server/response.d.ts +69 -0
  66. package/dist/server/response.js +173 -0
  67. package/dist/server/response.js.map +1 -0
  68. package/package.json +46 -0
  69. package/src/config.ts +212 -0
  70. package/src/context/event-context.ts +66 -0
  71. package/src/context/index.ts +32 -0
  72. package/src/context/lithia-context.ts +59 -0
  73. package/src/context/route-context.ts +89 -0
  74. package/src/env.ts +31 -0
  75. package/src/errors.ts +96 -0
  76. package/src/hooks/dependency-hooks.ts +122 -0
  77. package/src/hooks/event-hooks.ts +69 -0
  78. package/src/hooks/index.ts +58 -0
  79. package/src/hooks/route-hooks.ts +177 -0
  80. package/src/lib.ts +27 -0
  81. package/src/lithia.ts +777 -0
  82. package/src/logger.ts +66 -0
  83. package/src/module-loader.ts +45 -0
  84. package/src/server/event-processor.ts +344 -0
  85. package/src/server/http-server.ts +371 -0
  86. package/src/server/middlewares/validation.ts +46 -0
  87. package/src/server/request-processor.ts +860 -0
  88. package/src/server/request.ts +247 -0
  89. package/src/server/response.ts +204 -0
  90. package/tsconfig.build.tsbuildinfo +1 -0
  91. package/tsconfig.json +8 -0
package/dist/logger.js ADDED
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logger = exports.Logger = void 0;
4
+ const utils_1 = require("@lithia-js/utils");
5
+ function formatMeta(meta) {
6
+ if (meta === undefined)
7
+ return "";
8
+ try {
9
+ if (typeof meta === "string")
10
+ return meta;
11
+ return JSON.stringify(meta);
12
+ }
13
+ catch {
14
+ return String(meta);
15
+ }
16
+ }
17
+ class Logger {
18
+ info(msg, meta) {
19
+ const m = formatMeta(meta);
20
+ console.log(` ${msg}${m ? ` — ${m}` : ""}`);
21
+ }
22
+ warn(msg, meta) {
23
+ const symbol = (0, utils_1.yellow)((0, utils_1.bold)("○"));
24
+ const m = formatMeta(meta);
25
+ console.warn(`${symbol} ${msg}${m ? ` — ${m}` : ""}`);
26
+ }
27
+ error(msg, meta) {
28
+ const symbol = (0, utils_1.red)((0, utils_1.bold)("○"));
29
+ const m = formatMeta(meta);
30
+ console.error(`${symbol} ${msg}${m ? ` — ${m}` : ""}`);
31
+ }
32
+ success(msg, meta) {
33
+ const symbol = (0, utils_1.green)((0, utils_1.bold)("▲"));
34
+ const m = formatMeta(meta);
35
+ console.log(`${symbol} ${msg}${m ? ` — ${m}` : ""}`);
36
+ }
37
+ event(msg, meta) {
38
+ const symbol = (0, utils_1.blue)((0, utils_1.bold)("▲"));
39
+ const m = formatMeta(meta);
40
+ console.log(`${symbol} ${msg}${m ? ` — ${m}` : ""}`);
41
+ }
42
+ debug(msg, meta) {
43
+ const symbol = (0, utils_1.white)((0, utils_1.bold)("»"));
44
+ const m = formatMeta(meta);
45
+ console.log(`${symbol} ${msg}${m ? ` — ${m}` : ""}`);
46
+ }
47
+ wait(msg, meta) {
48
+ const symbol = (0, utils_1.white)((0, utils_1.bold)("○"));
49
+ const m = formatMeta(meta);
50
+ console.log(`${symbol} ${msg}${m ? ` — ${m}` : ""}`);
51
+ }
52
+ }
53
+ exports.Logger = Logger;
54
+ exports.logger = new Logger();
55
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Import a module dynamically, bypassing the cache in development.
3
+ * This is useful for hot-reloading routes and configuration files.
4
+ *
5
+ * @param filePath Absolute path to the file to import
6
+ * @param isDevelopment Whether to use cache-busting (dev) or native import (prod)
7
+ */
8
+ export declare function coldImport<T = any>(filePath: string, isDevelopment: boolean): Promise<T>;
9
+ /**
10
+ * Check if a value is an async function.
11
+ */
12
+ export declare function isAsyncFunction(fn: unknown): boolean;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.coldImport = coldImport;
40
+ exports.isAsyncFunction = isAsyncFunction;
41
+ const node_url_1 = require("node:url");
42
+ const import_fresh_1 = __importDefault(require("import-fresh"));
43
+ /**
44
+ * Import a module dynamically, bypassing the cache in development.
45
+ * This is useful for hot-reloading routes and configuration files.
46
+ *
47
+ * @param filePath Absolute path to the file to import
48
+ * @param isDevelopment Whether to use cache-busting (dev) or native import (prod)
49
+ */
50
+ async function coldImport(filePath, isDevelopment) {
51
+ let mod;
52
+ if (isDevelopment) {
53
+ // Use import-fresh in development for cache-free imports
54
+ // Note: import-fresh uses require() under the hood
55
+ mod = (0, import_fresh_1.default)(filePath);
56
+ }
57
+ else {
58
+ // Production: use normal dynamic import via file URL
59
+ const importUrl = (0, node_url_1.pathToFileURL)(filePath).href;
60
+ mod = await Promise.resolve(`${importUrl}`).then(s => __importStar(require(s)));
61
+ }
62
+ // Normalize CommonJS module structure
63
+ // When importing CommonJS via dynamic import or some bundlers, it can return:
64
+ // { default: { default: fn, ... } }
65
+ // We need to unwrap it to: { default: fn, ... }
66
+ if (mod.default && typeof mod.default === "object" && mod.default.default) {
67
+ return mod.default;
68
+ }
69
+ return mod;
70
+ }
71
+ const AsyncFunction = (async () => { }).constructor;
72
+ /**
73
+ * Check if a value is an async function.
74
+ */
75
+ function isAsyncFunction(fn) {
76
+ return fn instanceof AsyncFunction;
77
+ }
78
+ //# sourceMappingURL=module-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-loader.js","sourceRoot":"","sources":["../src/module-loader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,gCAyBC;AAOD,0CAEC;AA5CD,uCAAyC;AACzC,gEAAuC;AAEvC;;;;;;GAMG;AACI,KAAK,UAAU,UAAU,CAC/B,QAAgB,EAChB,aAAsB;IAEtB,IAAI,GAAQ,CAAC;IAEb,IAAI,aAAa,EAAE,CAAC;QACnB,yDAAyD;QACzD,mDAAmD;QACnD,GAAG,GAAG,IAAA,sBAAW,EAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACP,qDAAqD;QACrD,MAAM,SAAS,GAAG,IAAA,wBAAa,EAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;QAC/C,GAAG,GAAG,yBAAa,SAAS,uCAAC,CAAC;IAC/B,CAAC;IAED,sCAAsC;IACtC,8EAA8E;IAC9E,oCAAoC;IACpC,gDAAgD;IAChD,IAAI,GAAG,CAAC,OAAO,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3E,OAAO,GAAG,CAAC,OAAY,CAAC;IACzB,CAAC;IAED,OAAO,GAAQ,CAAC;AACjB,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,CAAC,WAAW,CAAC;AAEnD;;GAEG;AACH,SAAgB,eAAe,CAAC,EAAW;IAC1C,OAAO,EAAE,YAAY,aAAa,CAAC;AACpC,CAAC"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Event processor module for Socket.IO event handling.
3
+ *
4
+ * This module is the core of Lithia's Socket.IO event processing pipeline. It handles:
5
+ * - Event handler module loading with cache busting in development
6
+ * - Event context initialization for hooks
7
+ * - Error handling with logging and digest generation
8
+ * - Error event emission for centralized error handling
9
+ *
10
+ * @module server/event-processor
11
+ */
12
+ import type { Event } from "@lithia-js/native";
13
+ import type { Socket } from "socket.io";
14
+ import type { Lithia } from "../lithia";
15
+ /**
16
+ * Event handler function signature.
17
+ *
18
+ * The primary function exported from event files to handle Socket.IO events.
19
+ *
20
+ * @param socket - The Socket.IO socket instance that triggered the event
21
+ */
22
+ export type EventHandler = (socket: Socket) => Promise<void>;
23
+ /**
24
+ * Structure of an event module loaded from the file system.
25
+ *
26
+ * Event files must export a default async function that handles the event.
27
+ */
28
+ export interface EventModule {
29
+ /**
30
+ * The default event handler function.
31
+ *
32
+ * This function is called when the event is triggered by a client.
33
+ */
34
+ default: (socket: Socket) => Promise<void>;
35
+ }
36
+ /**
37
+ * Standardized error information for event errors.
38
+ *
39
+ * Contains details about errors that occur during event processing.
40
+ *
41
+ * @internal
42
+ */
43
+ export interface EventErrorInfo {
44
+ /** The event name that caused the error. */
45
+ eventName: string;
46
+ /** Human-readable error message. */
47
+ message: string;
48
+ /** ISO timestamp of when the error occurred. */
49
+ timestamp: string;
50
+ /** Short error digest for correlation with logs. */
51
+ digest: string;
52
+ /** Socket ID that triggered the error. */
53
+ socketId: string;
54
+ /** Error stack trace (development only). */
55
+ stack?: string;
56
+ }
57
+ /**
58
+ * Event processor for the Lithia Socket.IO pipeline.
59
+ *
60
+ * This class orchestrates the entire event processing flow from receiving
61
+ * a Socket.IO event to executing the handler. It manages:
62
+ *
63
+ * - **Event context**: Initializes context with event data for hooks
64
+ * - **Module loading**: Loads event handlers with cache busting in development
65
+ * - **Error handling**: Catches and logs errors with digest generation
66
+ * - **Error events**: Emits error events for centralized error handling
67
+ * - **Logging**: Logs all events with timing and error details
68
+ *
69
+ * @remarks
70
+ * The processor uses AsyncLocalStorage to provide event context to hooks,
71
+ * allowing event handlers to access data without explicit parameters.
72
+ *
73
+ * In development mode, event modules are reloaded on each event (cache busting).
74
+ * In production, modules are cached for better performance.
75
+ */
76
+ export declare class EventProcessor {
77
+ private lithia;
78
+ /**
79
+ * Creates a new event processor.
80
+ *
81
+ * @param lithia - The Lithia application instance
82
+ */
83
+ constructor(lithia: Lithia);
84
+ /**
85
+ * Processes an incoming Socket.IO event through the complete pipeline.
86
+ *
87
+ * This is the main entry point for event processing. The method executes
88
+ * the following steps in order:
89
+ *
90
+ * 1. Initialize event context with data for hooks
91
+ * 2. Load the event handler module
92
+ * 3. Execute the event handler
93
+ * 4. Log the event with timing
94
+ *
95
+ * Any errors thrown during this process are caught and handled by
96
+ * {@link handleError}, which logs the error and emits an error event.
97
+ *
98
+ * @param socket - The Socket.IO socket that triggered the event
99
+ * @param event - The event definition from the manifest
100
+ * @param args - Additional arguments passed with the event
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * const processor = new EventProcessor(lithia);
105
+ * await processor.processEvent(socket, event, messageData);
106
+ * ```
107
+ */
108
+ processEvent(socket: Socket, event: Event, ...args: any[]): Promise<void>;
109
+ /**
110
+ * Centralized error handler for the event pipeline.
111
+ *
112
+ * This method handles all errors thrown during event processing:
113
+ *
114
+ * **Development mode:**
115
+ * - Returns detailed error messages to client
116
+ * - Includes full error stacks
117
+ * - Logs with error digest
118
+ *
119
+ * **Production mode:**
120
+ * - Returns generic error messages to client
121
+ * - Includes error digest for log correlation
122
+ * - Hides sensitive error details
123
+ *
124
+ * After logging, emits an 'error' event to the socket so the client
125
+ * can handle errors gracefully.
126
+ *
127
+ * @param err - The error that was thrown
128
+ * @param event - The event definition that caused the error
129
+ * @param socket - The Socket.IO socket that triggered the event
130
+ * @param start - High-resolution timestamp when event processing started
131
+ *
132
+ * @private
133
+ */
134
+ private handleError;
135
+ /**
136
+ * Logs a completed event with timing information.
137
+ *
138
+ * The log includes:
139
+ * - Event name
140
+ * - Socket ID
141
+ * - Processing duration in milliseconds
142
+ * - Success or error status
143
+ *
144
+ * Respects the `logging.events` configuration flag. Critical errors
145
+ * are always logged regardless of the flag.
146
+ *
147
+ * @param event - The event that was processed
148
+ * @param socket - The socket that triggered the event
149
+ * @param start - High-resolution timestamp when processing started
150
+ * @param isError - Whether the event resulted in an error
151
+ *
152
+ * @private
153
+ */
154
+ private logEvent;
155
+ /**
156
+ * Generates a short hexadecimal digest for error correlation.
157
+ *
158
+ * The digest is used to correlate server-side error logs with client-facing
159
+ * error responses. This allows developers to find the detailed error in logs
160
+ * using the digest shown to the client.
161
+ *
162
+ * The digest is generated from:
163
+ * - Error message and stack
164
+ * - Current timestamp
165
+ * - Random factor for uniqueness
166
+ *
167
+ * @param err - The error to generate a digest for
168
+ * @returns An 8-character hexadecimal digest
169
+ *
170
+ * @private
171
+ */
172
+ private generateErrorDigest;
173
+ /**
174
+ * Imports an event module from the file system.
175
+ *
176
+ * In **development mode**, uses cache-busting to reload the module on each
177
+ * event, enabling hot reloading without server restart.
178
+ *
179
+ * In **production mode**, uses standard dynamic imports with caching for
180
+ * better performance.
181
+ *
182
+ * The method also validates the module structure:
183
+ * - Must have a default export
184
+ * - Default export must be a function
185
+ * - Default export must be async
186
+ *
187
+ * @param event - The event whose module should be imported
188
+ * @returns The loaded and validated event module
189
+ * @throws {InvalidEventModuleError} If the module structure is invalid
190
+ * @throws {Error} If the module fails to load
191
+ *
192
+ * @private
193
+ */
194
+ private importEventModule;
195
+ }
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ /**
3
+ * Event processor module for Socket.IO event handling.
4
+ *
5
+ * This module is the core of Lithia's Socket.IO event processing pipeline. It handles:
6
+ * - Event handler module loading with cache busting in development
7
+ * - Event context initialization for hooks
8
+ * - Error handling with logging and digest generation
9
+ * - Error event emission for centralized error handling
10
+ *
11
+ * @module server/event-processor
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.EventProcessor = void 0;
15
+ const node_crypto_1 = require("node:crypto");
16
+ const utils_1 = require("@lithia-js/utils");
17
+ const event_context_1 = require("../context/event-context");
18
+ const errors_1 = require("../errors");
19
+ const logger_1 = require("../logger");
20
+ const module_loader_1 = require("../module-loader");
21
+ /**
22
+ * Event processor for the Lithia Socket.IO pipeline.
23
+ *
24
+ * This class orchestrates the entire event processing flow from receiving
25
+ * a Socket.IO event to executing the handler. It manages:
26
+ *
27
+ * - **Event context**: Initializes context with event data for hooks
28
+ * - **Module loading**: Loads event handlers with cache busting in development
29
+ * - **Error handling**: Catches and logs errors with digest generation
30
+ * - **Error events**: Emits error events for centralized error handling
31
+ * - **Logging**: Logs all events with timing and error details
32
+ *
33
+ * @remarks
34
+ * The processor uses AsyncLocalStorage to provide event context to hooks,
35
+ * allowing event handlers to access data without explicit parameters.
36
+ *
37
+ * In development mode, event modules are reloaded on each event (cache busting).
38
+ * In production, modules are cached for better performance.
39
+ */
40
+ class EventProcessor {
41
+ lithia;
42
+ /**
43
+ * Creates a new event processor.
44
+ *
45
+ * @param lithia - The Lithia application instance
46
+ */
47
+ constructor(lithia) {
48
+ this.lithia = lithia;
49
+ }
50
+ /**
51
+ * Processes an incoming Socket.IO event through the complete pipeline.
52
+ *
53
+ * This is the main entry point for event processing. The method executes
54
+ * the following steps in order:
55
+ *
56
+ * 1. Initialize event context with data for hooks
57
+ * 2. Load the event handler module
58
+ * 3. Execute the event handler
59
+ * 4. Log the event with timing
60
+ *
61
+ * Any errors thrown during this process are caught and handled by
62
+ * {@link handleError}, which logs the error and emits an error event.
63
+ *
64
+ * @param socket - The Socket.IO socket that triggered the event
65
+ * @param event - The event definition from the manifest
66
+ * @param args - Additional arguments passed with the event
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * const processor = new EventProcessor(lithia);
71
+ * await processor.processEvent(socket, event, messageData);
72
+ * ```
73
+ */
74
+ async processEvent(socket, event, ...args) {
75
+ const start = process.hrtime.bigint();
76
+ // Initialize context for hooks
77
+ const eventCtx = {
78
+ data: args[0],
79
+ socket,
80
+ };
81
+ await this.lithia.runWithContext(async () => {
82
+ await event_context_1.eventContext.run(eventCtx, async () => {
83
+ try {
84
+ // Import event module
85
+ const module = await this.importEventModule(event);
86
+ // Execute event handler
87
+ await module.default(socket);
88
+ // Log successful event
89
+ this.logEvent(event, start);
90
+ }
91
+ catch (err) {
92
+ // Handle and log error
93
+ await this.handleError(err, event, socket, start);
94
+ }
95
+ });
96
+ });
97
+ }
98
+ // ==================== Error Handling ====================
99
+ /**
100
+ * Centralized error handler for the event pipeline.
101
+ *
102
+ * This method handles all errors thrown during event processing:
103
+ *
104
+ * **Development mode:**
105
+ * - Returns detailed error messages to client
106
+ * - Includes full error stacks
107
+ * - Logs with error digest
108
+ *
109
+ * **Production mode:**
110
+ * - Returns generic error messages to client
111
+ * - Includes error digest for log correlation
112
+ * - Hides sensitive error details
113
+ *
114
+ * After logging, emits an 'error' event to the socket so the client
115
+ * can handle errors gracefully.
116
+ *
117
+ * @param err - The error that was thrown
118
+ * @param event - The event definition that caused the error
119
+ * @param socket - The Socket.IO socket that triggered the event
120
+ * @param start - High-resolution timestamp when event processing started
121
+ *
122
+ * @private
123
+ */
124
+ async handleError(err, event, socket, start) {
125
+ const isDevelopment = this.lithia.getEnvironment() === "development";
126
+ // Generate error digest
127
+ const digest = this.generateErrorDigest(err);
128
+ // Build error details
129
+ const errorMessage = err instanceof Error ? err.message : "Internal Server Error";
130
+ const errorStack = err instanceof Error ? err.stack : undefined;
131
+ // Log error with digest (same format for both environments)
132
+ logger_1.logger.error(`[Event: ${event.name}] [Digest: ${(0, utils_1.red)(digest)}] ${errorStack || errorMessage}`);
133
+ // Build error information for client
134
+ const errorInfo = {
135
+ eventName: event.name,
136
+ message: isDevelopment
137
+ ? errorMessage
138
+ : "An error occurred while processing the event",
139
+ timestamp: new Date().toISOString(),
140
+ digest: digest,
141
+ socketId: socket.id,
142
+ stack: isDevelopment ? errorStack : undefined,
143
+ };
144
+ // Emit error event to the client
145
+ socket.emit("error", errorInfo);
146
+ // Log the event as failed
147
+ this.logEvent(event, start, true);
148
+ }
149
+ // ==================== Logging & Diagnostics ====================
150
+ /**
151
+ * Logs a completed event with timing information.
152
+ *
153
+ * The log includes:
154
+ * - Event name
155
+ * - Socket ID
156
+ * - Processing duration in milliseconds
157
+ * - Success or error status
158
+ *
159
+ * Respects the `logging.events` configuration flag. Critical errors
160
+ * are always logged regardless of the flag.
161
+ *
162
+ * @param event - The event that was processed
163
+ * @param socket - The socket that triggered the event
164
+ * @param start - High-resolution timestamp when processing started
165
+ * @param isError - Whether the event resulted in an error
166
+ *
167
+ * @private
168
+ */
169
+ logEvent(event, start, isError = false) {
170
+ // Always log errors, otherwise respect the logging.events flag
171
+ const shouldLog = isError || this.lithia.options.logging?.events !== false;
172
+ if (!shouldLog)
173
+ return;
174
+ const end = process.hrtime.bigint();
175
+ const duration = Number(end - start) / 1_000_000;
176
+ const durationStr = `${duration.toFixed(2)}ms`;
177
+ // Use same color scheme as requests for consistency
178
+ const statusStr = isError ? (0, utils_1.red)("ERROR") : (0, utils_1.green)("OK");
179
+ logger_1.logger.info(`[${statusStr}] EVENT ${event.name} - ${durationStr}`);
180
+ }
181
+ /**
182
+ * Generates a short hexadecimal digest for error correlation.
183
+ *
184
+ * The digest is used to correlate server-side error logs with client-facing
185
+ * error responses. This allows developers to find the detailed error in logs
186
+ * using the digest shown to the client.
187
+ *
188
+ * The digest is generated from:
189
+ * - Error message and stack
190
+ * - Current timestamp
191
+ * - Random factor for uniqueness
192
+ *
193
+ * @param err - The error to generate a digest for
194
+ * @returns An 8-character hexadecimal digest
195
+ *
196
+ * @private
197
+ */
198
+ generateErrorDigest(err) {
199
+ // Create a unique digest based on error message, timestamp, and random factor
200
+ const errorString = err instanceof Error ? `${err.message}${err.stack}` : String(err);
201
+ const hash = (0, node_crypto_1.createHash)("sha256")
202
+ .update(`${errorString}${Date.now()}${Math.random()}`)
203
+ .digest("hex");
204
+ // Return first 8 characters (similar to request-processor)
205
+ return hash.substring(0, 8);
206
+ }
207
+ // ==================== Module Loading ====================
208
+ /**
209
+ * Imports an event module from the file system.
210
+ *
211
+ * In **development mode**, uses cache-busting to reload the module on each
212
+ * event, enabling hot reloading without server restart.
213
+ *
214
+ * In **production mode**, uses standard dynamic imports with caching for
215
+ * better performance.
216
+ *
217
+ * The method also validates the module structure:
218
+ * - Must have a default export
219
+ * - Default export must be a function
220
+ * - Default export must be async
221
+ *
222
+ * @param event - The event whose module should be imported
223
+ * @returns The loaded and validated event module
224
+ * @throws {InvalidEventModuleError} If the module structure is invalid
225
+ * @throws {Error} If the module fails to load
226
+ *
227
+ * @private
228
+ */
229
+ async importEventModule(event) {
230
+ try {
231
+ const isDevelopment = this.lithia.getEnvironment() === "development";
232
+ const mod = await (0, module_loader_1.coldImport)(event.filePath, isDevelopment);
233
+ if (!mod.default) {
234
+ throw new errors_1.InvalidEventModuleError(event.filePath, "Missing default export");
235
+ }
236
+ if (typeof mod.default !== "function") {
237
+ throw new errors_1.InvalidEventModuleError(event.filePath, "Default export is not a function");
238
+ }
239
+ if (!(0, module_loader_1.isAsyncFunction)(mod.default)) {
240
+ throw new errors_1.InvalidEventModuleError(event.filePath, "Default export is not an async function");
241
+ }
242
+ return mod;
243
+ }
244
+ catch (err) {
245
+ if (err instanceof errors_1.InvalidEventModuleError) {
246
+ throw err;
247
+ }
248
+ throw new Error(`Failed to import event: ${event.name}`);
249
+ }
250
+ }
251
+ }
252
+ exports.EventProcessor = EventProcessor;
253
+ //# sourceMappingURL=event-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-processor.js","sourceRoot":"","sources":["../../src/server/event-processor.ts"],"names":[],"mappings":"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"}