@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
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Request processor module for HTTP request handling.
3
+ *
4
+ * This module is the core of Lithia's request processing pipeline. It handles:
5
+ * - Route matching and parameter extraction
6
+ * - Static file serving
7
+ * - CORS preflight and headers
8
+ * - Middleware execution chain
9
+ * - Route handler invocation
10
+ * - Error handling and logging
11
+ *
12
+ * @module server/request-processor
13
+ */
14
+ import type { Lithia } from "../lithia";
15
+ import type { HttpServer } from "./http-server";
16
+ import type { LithiaRequest } from "./request";
17
+ import type { LithiaResponse } from "./response";
18
+ /**
19
+ * Route handler function signature.
20
+ *
21
+ * The primary function exported from route files to handle requests.
22
+ *
23
+ * @param req - The incoming HTTP request
24
+ * @param res - The HTTP response object
25
+ */
26
+ export type LithiaHandler = (req: LithiaRequest, res: LithiaResponse) => Promise<void>;
27
+ /**
28
+ * Middleware function signature.
29
+ *
30
+ * Middlewares run before the route handler and can modify the request/response,
31
+ * perform authentication, logging, or other cross-cutting concerns.
32
+ *
33
+ * @param req - The incoming HTTP request
34
+ * @param res - The HTTP response object
35
+ * @param next - Function to call to proceed to the next middleware or handler
36
+ *
37
+ * @remarks
38
+ * Middlewares must call `next()` to continue the chain. Not calling `next()`
39
+ * will stop execution and prevent the route handler from running.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * export const middlewares = [
44
+ * async (req, res, next) => {
45
+ * console.log('Before handler');
46
+ * await next();
47
+ * console.log('After handler');
48
+ * }
49
+ * ];
50
+ * ```
51
+ */
52
+ export type LithiaMiddleware = (req: LithiaRequest, res: LithiaResponse, next: () => void) => Promise<void>;
53
+ /**
54
+ * Structure of a route module loaded from the file system.
55
+ *
56
+ * Route files can export a default handler and optionally an array of
57
+ * middlewares to run before the handler.
58
+ */
59
+ export interface RouteModule {
60
+ /**
61
+ * The default route handler function.
62
+ *
63
+ * This is the main function that processes the request and sends a response.
64
+ */
65
+ default?: LithiaHandler;
66
+ /**
67
+ * Optional array of middlewares executed before the handler.
68
+ *
69
+ * Middlewares run in order and must call `next()` to continue.
70
+ */
71
+ middlewares?: Array<LithiaMiddleware>;
72
+ }
73
+ /**
74
+ * Standardized error response format.
75
+ *
76
+ * All errors are formatted consistently as JSON with this structure.
77
+ *
78
+ * @internal
79
+ */
80
+ export interface RequestErrorInfo {
81
+ /** Error details object. */
82
+ error: {
83
+ /** Human-readable error message. */
84
+ message: string;
85
+ /** HTTP status code. */
86
+ statusCode: number;
87
+ /** ISO timestamp of when the error occurred. */
88
+ timestamp: string;
89
+ /** Request path that caused the error. */
90
+ path: string;
91
+ /** HTTP method of the request. */
92
+ method: string;
93
+ /** Short error digest for correlation with logs. */
94
+ digest?: string;
95
+ /** Validation error details (for 400 errors). */
96
+ issues?: any[];
97
+ };
98
+ }
99
+ /**
100
+ * Request processor for the Lithia HTTP pipeline.
101
+ *
102
+ * This class orchestrates the entire request processing flow from receiving
103
+ * an HTTP request to sending a response. It manages:
104
+ *
105
+ * - **CORS handling**: Preflight requests and CORS headers
106
+ * - **Static files**: Serving files from configured static directories
107
+ * - **Route matching**: Finding the route that matches the request path
108
+ * - **Parameter extraction**: Extracting dynamic route parameters
109
+ * - **Middleware execution**: Running global and route-specific middlewares
110
+ * - **Handler invocation**: Executing the route handler function
111
+ * - **Error handling**: Converting exceptions to structured JSON responses
112
+ * - **Request logging**: Logging all requests with timing and status codes
113
+ *
114
+ * @remarks
115
+ * The processor uses AsyncLocalStorage to provide request context to hooks,
116
+ * allowing route handlers to access request/response without explicit parameters.
117
+ *
118
+ * In development mode, route modules are reloaded on each request (cache busting).
119
+ * In production, modules are cached for better performance.
120
+ */
121
+ export declare class RequestProcessor {
122
+ private lithia;
123
+ private httpServer;
124
+ /**
125
+ * Creates a new request processor.
126
+ *
127
+ * @param lithia - The Lithia application instance
128
+ */
129
+ constructor(lithia: Lithia, httpServer: HttpServer);
130
+ /**
131
+ * Processes an incoming HTTP request through the complete pipeline.
132
+ *
133
+ * This is the main entry point for request processing. The method executes
134
+ * the following steps in order:
135
+ *
136
+ * 1. Initialize request context for hooks
137
+ * 2. Handle CORS preflight (OPTIONS) requests
138
+ * 3. Add standard response headers
139
+ * 4. Attempt to serve static files
140
+ * 5. Match request to a route
141
+ * 6. Extract dynamic route parameters
142
+ * 7. Load the route module
143
+ * 8. Execute global middlewares
144
+ * 9. Execute route-specific middlewares
145
+ * 10. Execute the route handler
146
+ * 11. Log the request with timing
147
+ *
148
+ * Any errors thrown during this process are caught and handled by
149
+ * {@link handleError}, which sends a structured error response.
150
+ *
151
+ * @param req - The incoming HTTP request
152
+ * @param res - The HTTP response object
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * const processor = new RequestProcessor(lithia);
157
+ * await processor.processRequest(req, res);
158
+ * ```
159
+ */
160
+ processRequest(req: LithiaRequest, res: LithiaResponse): Promise<void>;
161
+ /**
162
+ * Executes an array of middlewares sequentially.
163
+ *
164
+ * Middlewares are executed in order. Each middleware must call `next()`
165
+ * to continue to the next middleware in the chain. If a middleware does
166
+ * not call `next()`, the chain stops and subsequent middlewares are not
167
+ * executed.
168
+ *
169
+ * If any middleware throws an error, execution stops immediately and the
170
+ * error is returned.
171
+ *
172
+ * @param middlewares - Array of middleware functions to execute
173
+ * @param req - The HTTP request object
174
+ * @param res - The HTTP response object
175
+ * @returns null if successful, or an Error if a middleware threw
176
+ *
177
+ * @private
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * const error = await this.executeMiddlewares(middlewares, req, res);
182
+ * if (error) {
183
+ * throw error;
184
+ * }
185
+ * ```
186
+ */
187
+ private executeMiddlewares;
188
+ /**
189
+ * Sends a structured 404 JSON response for unmatched routes.
190
+ *
191
+ * This method is called when no route matches the requested path.
192
+ * It sends a standardized error response with status 404.
193
+ *
194
+ * @param req - The HTTP request that didn't match any route
195
+ * @param res - The HTTP response object
196
+ *
197
+ * @private
198
+ */
199
+ private sendNotFound;
200
+ /**
201
+ * Centralized error handler for the request pipeline.
202
+ *
203
+ * This method handles all errors thrown during request processing:
204
+ *
205
+ * **Development mode:**
206
+ * - Returns detailed error messages
207
+ * - Includes full error stacks
208
+ * - Shows validation issues if present
209
+ *
210
+ * **Production mode:**
211
+ * - Returns generic error messages
212
+ * - Includes error digest for log correlation
213
+ * - Hides sensitive error details
214
+ *
215
+ * All server errors (5xx) are logged with the error digest for correlation
216
+ * between client responses and server logs.
217
+ *
218
+ * @param err - The error that was thrown
219
+ * @param req - The HTTP request that caused the error
220
+ * @param res - The HTTP response object
221
+ * @param start - High-resolution timestamp when request processing started
222
+ *
223
+ * @private
224
+ */
225
+ private handleError;
226
+ /**
227
+ * Logs a completed request with color-coded status and timing.
228
+ *
229
+ * The log includes:
230
+ * - HTTP status code (color-coded by range)
231
+ * - HTTP method (GET, POST, etc.)
232
+ * - Request pathname
233
+ * - Processing duration in milliseconds
234
+ *
235
+ * Status colors:
236
+ * - 2xx: Green (success)
237
+ * - 3xx: Cyan (redirect)
238
+ * - 4xx: Yellow (client error)
239
+ * - 5xx: Red (server error)
240
+ *
241
+ * @param req - The HTTP request that was processed
242
+ * @param res - The HTTP response that was sent
243
+ * @param start - High-resolution timestamp when processing started
244
+ *
245
+ * @private
246
+ */
247
+ /**
248
+ * Logs a completed request with color-coded status and timing.
249
+ *
250
+ * The log includes:
251
+ * - HTTP status code (color-coded by range)
252
+ * - HTTP method (GET, POST, etc.)
253
+ * - Request pathname
254
+ * - Processing duration in milliseconds
255
+ *
256
+ * Status colors:
257
+ * - 2xx: Green (success)
258
+ * - 3xx: Cyan (redirect)
259
+ * - 4xx: Yellow (client error)
260
+ * - 5xx: Red (server error)
261
+ *
262
+ * Respects the `logging.requests` configuration flag. Critical errors
263
+ * (5xx) are always logged regardless of the flag.
264
+ *
265
+ * @param req - The HTTP request that was processed
266
+ * @param res - The HTTP response that was sent
267
+ * @param start - High-resolution timestamp when processing started
268
+ *
269
+ * @private
270
+ */
271
+ private logRequest;
272
+ /**
273
+ * Generates a short hexadecimal digest for error correlation.
274
+ *
275
+ * The digest is used to correlate server-side error logs with client-facing
276
+ * error responses. This allows developers to find the detailed error in logs
277
+ * using the digest shown to the client.
278
+ *
279
+ * The digest is generated from:
280
+ * - Error message and stack
281
+ * - Current timestamp
282
+ * - Random factor for uniqueness
283
+ *
284
+ * @param err - The error to generate a digest for
285
+ * @returns An 8-character hexadecimal digest
286
+ *
287
+ * @private
288
+ */
289
+ private generateErrorDigest;
290
+ /**
291
+ * Handles CORS preflight requests and applies CORS headers.
292
+ *
293
+ * This method:
294
+ * 1. Checks if the request origin is allowed based on CORS configuration
295
+ * 2. Adds appropriate CORS headers to the response
296
+ * 3. Handles OPTIONS preflight requests
297
+ *
298
+ * **CORS Headers Applied:**
299
+ * - `Access-Control-Allow-Origin`: Allowed origin
300
+ * - `Access-Control-Allow-Credentials`: If credentials are enabled
301
+ * - `Access-Control-Allow-Methods`: Allowed HTTP methods
302
+ * - `Access-Control-Allow-Headers`: Allowed request headers
303
+ * - `Access-Control-Max-Age`: Preflight cache duration
304
+ * - `Access-Control-Expose-Headers`: Headers exposed to client
305
+ *
306
+ * @param req - The HTTP request
307
+ * @param res - The HTTP response
308
+ * @returns true if the request was an OPTIONS preflight that was handled, false otherwise
309
+ *
310
+ * @private
311
+ */
312
+ private handleCors;
313
+ /**
314
+ * Attempts to serve a static file from the configured static directory.
315
+ *
316
+ * This method:
317
+ * 1. Checks if static file serving is enabled
318
+ * 2. Only serves for GET and HEAD requests
319
+ * 3. Strips configured prefix from the path
320
+ * 4. Prevents directory traversal attacks
321
+ * 5. Determines MIME type from file extension
322
+ * 6. Sends the file with appropriate Content-Type header
323
+ *
324
+ * @param req - The HTTP request
325
+ * @param res - The HTTP response
326
+ * @returns true if a static file was served, false otherwise
327
+ * @throws {StaticFileMimeMissingError} If file extension has no configured MIME type
328
+ *
329
+ * @private
330
+ */
331
+ private serveStaticFile;
332
+ /**
333
+ * Finds a route matching the given pathname and HTTP method.
334
+ *
335
+ * Routes are matched in the order they were registered. The first route
336
+ * that matches both the path pattern (via regex) and HTTP method is returned.
337
+ *
338
+ * @param pathname - The request pathname to match
339
+ * @param method - The HTTP method (GET, POST, etc.)
340
+ * @returns The matched Route, or undefined if no match found
341
+ *
342
+ * @private
343
+ */
344
+ private findMatchingRoute;
345
+ /**
346
+ * Tests whether a pathname matches a route's regex pattern.
347
+ *
348
+ * @param pathname - The pathname to test
349
+ * @param route - The route with the regex pattern
350
+ * @returns true if the pathname matches the route's regex, false otherwise
351
+ *
352
+ * @private
353
+ */
354
+ private matchesPath;
355
+ /**
356
+ * Imports a route module from the file system.
357
+ *
358
+ * In **development mode**, uses cache-busting to reload the module on each
359
+ * request, enabling hot reloading without server restart.
360
+ *
361
+ * In **production mode**, uses standard dynamic imports with caching for
362
+ * better performance.
363
+ *
364
+ * The method also validates the module structure:
365
+ * - Must have a default export
366
+ * - Default export must be a function
367
+ * - Default export must be async
368
+ *
369
+ * @param route - The route whose module should be imported
370
+ * @returns The loaded and validated route module
371
+ * @throws {InvalidRouteModuleError} If the module structure is invalid
372
+ * @throws {Error} If the module fails to load
373
+ *
374
+ * @private
375
+ */
376
+ private importRouteModule;
377
+ /**
378
+ * Extracts named route parameters from the pathname.
379
+ *
380
+ * For dynamic routes like `/users/:id/posts/:postId`, this method:
381
+ * 1. Matches the pathname against the route's regex
382
+ * 2. Extracts parameter names from the route path (e.g., "id", "postId")
383
+ * 3. Maps regex capture groups to parameter names
384
+ * 4. URL-decodes parameter values
385
+ *
386
+ * @param pathname - The request pathname
387
+ * @param route - The matched route with dynamic segments
388
+ * @returns Object mapping parameter names to their values
389
+ *
390
+ * @private
391
+ *
392
+ * @example
393
+ * ```typescript
394
+ * // Route: /users/:id/posts/:postId
395
+ * // Pathname: /users/123/posts/456
396
+ * // Returns: { id: '123', postId: '456' }
397
+ * ```
398
+ */
399
+ private extractParams;
400
+ }