@engjts/nexus 0.1.7 → 0.1.8

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 (72) hide show
  1. package/BENCHMARK_REPORT.md +343 -0
  2. package/dist/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
  3. package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
  4. package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
  5. package/dist/advanced/playground/playground.d.ts +19 -0
  6. package/dist/advanced/playground/playground.d.ts.map +1 -1
  7. package/dist/advanced/playground/playground.js +70 -0
  8. package/dist/advanced/playground/playground.js.map +1 -1
  9. package/dist/advanced/playground/types.d.ts +20 -0
  10. package/dist/advanced/playground/types.d.ts.map +1 -1
  11. package/dist/core/application.d.ts +14 -0
  12. package/dist/core/application.d.ts.map +1 -1
  13. package/dist/core/application.js +173 -71
  14. package/dist/core/application.js.map +1 -1
  15. package/dist/core/context-pool.d.ts +2 -13
  16. package/dist/core/context-pool.d.ts.map +1 -1
  17. package/dist/core/context-pool.js +7 -45
  18. package/dist/core/context-pool.js.map +1 -1
  19. package/dist/core/context.d.ts +108 -5
  20. package/dist/core/context.d.ts.map +1 -1
  21. package/dist/core/context.js +449 -53
  22. package/dist/core/context.js.map +1 -1
  23. package/dist/core/index.d.ts +1 -0
  24. package/dist/core/index.d.ts.map +1 -1
  25. package/dist/core/index.js +9 -1
  26. package/dist/core/index.js.map +1 -1
  27. package/dist/core/middleware.d.ts +6 -0
  28. package/dist/core/middleware.d.ts.map +1 -1
  29. package/dist/core/middleware.js +83 -84
  30. package/dist/core/middleware.js.map +1 -1
  31. package/dist/core/performance/fast-json.d.ts +149 -0
  32. package/dist/core/performance/fast-json.d.ts.map +1 -0
  33. package/dist/core/performance/fast-json.js +473 -0
  34. package/dist/core/performance/fast-json.js.map +1 -0
  35. package/dist/core/router/file-router.d.ts +20 -7
  36. package/dist/core/router/file-router.d.ts.map +1 -1
  37. package/dist/core/router/file-router.js +41 -13
  38. package/dist/core/router/file-router.js.map +1 -1
  39. package/dist/core/router/index.d.ts +6 -0
  40. package/dist/core/router/index.d.ts.map +1 -1
  41. package/dist/core/router/index.js +33 -6
  42. package/dist/core/router/index.js.map +1 -1
  43. package/dist/core/router/radix-tree.d.ts +4 -1
  44. package/dist/core/router/radix-tree.d.ts.map +1 -1
  45. package/dist/core/router/radix-tree.js +7 -3
  46. package/dist/core/router/radix-tree.js.map +1 -1
  47. package/dist/core/serializer.d.ts +251 -0
  48. package/dist/core/serializer.d.ts.map +1 -0
  49. package/dist/core/serializer.js +290 -0
  50. package/dist/core/serializer.js.map +1 -0
  51. package/dist/core/types.d.ts +39 -1
  52. package/dist/core/types.d.ts.map +1 -1
  53. package/dist/core/types.js.map +1 -1
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +12 -2
  57. package/dist/index.js.map +1 -1
  58. package/package.json +3 -1
  59. package/src/advanced/playground/generatePlaygroundHTML.ts +107 -0
  60. package/src/advanced/playground/playground.ts +225 -145
  61. package/src/advanced/playground/types.ts +29 -0
  62. package/src/core/application.ts +202 -84
  63. package/src/core/context-pool.ts +8 -56
  64. package/src/core/context.ts +497 -53
  65. package/src/core/index.ts +14 -0
  66. package/src/core/middleware.ts +99 -89
  67. package/src/core/router/file-router.ts +41 -12
  68. package/src/core/router/index.ts +213 -180
  69. package/src/core/router/radix-tree.ts +20 -4
  70. package/src/core/serializer.ts +397 -0
  71. package/src/core/types.ts +43 -1
  72. package/src/index.ts +17 -0
@@ -4,8 +4,7 @@
4
4
  */
5
5
 
6
6
  import { IncomingMessage, ServerResponse } from 'http';
7
- import { parse as parseUrl } from 'url';
8
- import { parse as parseQueryString } from 'querystring';
7
+ import { parse as fastQueryParse } from 'fast-querystring';
9
8
  import {
10
9
  Context,
11
10
  Headers,
@@ -16,6 +15,7 @@ import {
16
15
  HTTPMethod
17
16
  } from './types';
18
17
  import { ContextStore, StoreConstructor, StoreRegistry, RequestStore, RequestStoreConstructor, RequestStoreRegistry } from './store';
18
+ import { SerializerFunction } from './serializer';
19
19
 
20
20
  /**
21
21
  * Cookie manager implementation
@@ -74,12 +74,25 @@ class CookieManager implements Cookies {
74
74
  }
75
75
  }
76
76
 
77
+ /**
78
+ * Pre-cached headers for common content types
79
+ * This avoids object creation on every response
80
+ */
81
+ const CACHED_HEADERS = {
82
+ JSON: { 'Content-Type': 'application/json' } as Headers,
83
+ HTML: { 'Content-Type': 'text/html; charset=utf-8' } as Headers,
84
+ TEXT: { 'Content-Type': 'text/plain; charset=utf-8' } as Headers,
85
+ };
86
+
77
87
  /**
78
88
  * Response builder implementation
89
+ * Supports fast-json-stringify for optimized JSON serialization
79
90
  */
80
91
  class ResponseBuilderImpl implements ResponseBuilder {
81
92
  private _status: number = 200;
82
93
  private _headers: Headers = {};
94
+ private _hasCustomHeaders: boolean = false;
95
+ private _serializers: Map<number | string, SerializerFunction> | null = null;
83
96
 
84
97
  status(code: number): ResponseBuilder {
85
98
  this._status = code;
@@ -88,27 +101,57 @@ class ResponseBuilderImpl implements ResponseBuilder {
88
101
 
89
102
  header(name: string, value: string): ResponseBuilder {
90
103
  this._headers[name] = value;
104
+ this._hasCustomHeaders = true;
91
105
  return this;
92
106
  }
93
107
 
108
+ /**
109
+ * Set serializers for this response builder (called by router)
110
+ * @internal
111
+ */
112
+ setSerializers(serializers: Map<number | string, SerializerFunction>): void {
113
+ this._serializers = serializers;
114
+ }
115
+
116
+ /**
117
+ * Get the appropriate serializer for current status code
118
+ */
119
+ private getSerializer(): SerializerFunction | null {
120
+ if (!this._serializers) return null;
121
+
122
+ // Try exact match first
123
+ const exactMatch = this._serializers.get(this._status);
124
+ if (exactMatch) return exactMatch;
125
+
126
+ // Try status code ranges (2xx, 4xx, 5xx)
127
+ const range = `${Math.floor(this._status / 100)}xx`;
128
+ const rangeMatch = this._serializers.get(range);
129
+ if (rangeMatch) return rangeMatch;
130
+
131
+ // Try default
132
+ return this._serializers.get('default') || null;
133
+ }
134
+
94
135
  json<T>(data: T): Response {
136
+ // Try to use fast serializer first
137
+ const serializer = this.getSerializer();
138
+ const body = serializer ? serializer(data) : JSON.stringify(data);
139
+
95
140
  return {
96
141
  statusCode: this._status,
97
- headers: {
98
- ...this._headers,
99
- 'Content-Type': 'application/json'
100
- },
101
- body: JSON.stringify(data)
142
+ headers: this._hasCustomHeaders
143
+ ? { ...this._headers, 'Content-Type': 'application/json' }
144
+ : CACHED_HEADERS.JSON,
145
+ body
102
146
  };
103
147
  }
104
148
 
105
149
  html(content: string): Response {
106
150
  return {
107
151
  statusCode: this._status,
108
- headers: {
109
- ...this._headers,
110
- 'Content-Type': 'text/html; charset=utf-8'
111
- },
152
+ headers: this._hasCustomHeaders
153
+ ? { ...this._headers, 'Content-Type': 'text/html; charset=utf-8' }
154
+ : CACHED_HEADERS.HTML,
112
155
  body: content
113
156
  };
114
157
  }
@@ -116,10 +159,9 @@ class ResponseBuilderImpl implements ResponseBuilder {
116
159
  text(content: string): Response {
117
160
  return {
118
161
  statusCode: this._status,
119
- headers: {
120
- ...this._headers,
121
- 'Content-Type': 'text/plain; charset=utf-8'
122
- },
162
+ headers: this._hasCustomHeaders
163
+ ? { ...this._headers, 'Content-Type': 'text/plain; charset=utf-8' }
164
+ : CACHED_HEADERS.TEXT,
123
165
  body: content
124
166
  };
125
167
  }
@@ -127,10 +169,9 @@ class ResponseBuilderImpl implements ResponseBuilder {
127
169
  redirect(url: string, status: number = 302): Response {
128
170
  return {
129
171
  statusCode: status,
130
- headers: {
131
- ...this._headers,
132
- 'Location': url
133
- },
172
+ headers: this._hasCustomHeaders
173
+ ? { ...this._headers, 'Location': url }
174
+ : { 'Location': url },
134
175
  body: ''
135
176
  };
136
177
  }
@@ -143,6 +184,37 @@ class ResponseBuilderImpl implements ResponseBuilder {
143
184
  stream: readable
144
185
  };
145
186
  }
187
+
188
+ /**
189
+ * Reset for reuse (object pooling)
190
+ */
191
+ reset(): void {
192
+ this._status = 200;
193
+ this._headers = {};
194
+ this._hasCustomHeaders = false;
195
+ this._serializers = null;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Reusable response builder pool
201
+ */
202
+ const responseBuilderPool: ResponseBuilderImpl[] = [];
203
+ const RESPONSE_BUILDER_POOL_SIZE = 100;
204
+
205
+ function acquireResponseBuilder(): ResponseBuilderImpl {
206
+ if (responseBuilderPool.length > 0) {
207
+ const builder = responseBuilderPool.pop()!;
208
+ builder.reset();
209
+ return builder;
210
+ }
211
+ return new ResponseBuilderImpl();
212
+ }
213
+
214
+ function releaseResponseBuilder(builder: ResponseBuilderImpl): void {
215
+ if (responseBuilderPool.length < RESPONSE_BUILDER_POOL_SIZE) {
216
+ responseBuilderPool.push(builder);
217
+ }
146
218
  }
147
219
 
148
220
  /**
@@ -151,23 +223,30 @@ class ResponseBuilderImpl implements ResponseBuilder {
151
223
  export class ContextImpl implements Context {
152
224
  method: HTTPMethod;
153
225
  path: string;
154
- url: URL;
226
+ private _url: URL | null = null; // Lazy URL creation
227
+ private _host: string = 'localhost';
155
228
  params: Record<string, string> = {};
156
- query: Record<string, any> = {};
157
- body: any = null;
229
+ private _query: Record<string, any> | null = null; // Lazy query parsing
230
+ private _queryString: string = ''; // Raw query string for lazy parsing
158
231
  headers: Headers;
159
- cookies: Cookies;
232
+ private _cookieHeader: string | undefined;
233
+ private _cookies: CookieManager | null = null; // Lazy cookie parsing
160
234
  raw: { req: IncomingMessage; res: ServerResponse };
161
235
  response: ResponseBuilder;
162
236
 
237
+ // Lazy body parsing - key optimization!
238
+ private _parsedBody: any = undefined;
239
+ private _bodyPromise: Promise<any> | null = null;
240
+ private _bodyParsed: boolean = false;
241
+
163
242
  // Store registry reference (set by Application)
164
243
  private _storeRegistry?: StoreRegistry;
165
244
 
166
- // Request-scoped store registry (created per request)
167
- private _requestStoreRegistry: RequestStoreRegistry;
245
+ // Request-scoped store registry - now lazy!
246
+ private _requestStoreRegistry: RequestStoreRegistry | null = null;
168
247
 
169
- // Request-scoped simple key-value storage
170
- private _data: Map<string, any> = new Map();
248
+ // Request-scoped simple key-value storage - now lazy!
249
+ private _data: Map<string, any> | null = null;
171
250
 
172
251
  // Debug mode
173
252
  private _debug: boolean = false;
@@ -175,26 +254,344 @@ export class ContextImpl implements Context {
175
254
  constructor(req: IncomingMessage, res: ServerResponse) {
176
255
  this.raw = { req, res };
177
256
 
178
- // Parse method
179
- this.method = (req.method?.toUpperCase() || 'GET') as HTTPMethod;
257
+ // Parse method - use direct access, avoid optional chaining overhead
258
+ this.method = (req.method ? req.method.toUpperCase() : 'GET') as HTTPMethod;
180
259
 
181
- // Parse URL and query
182
- const parsedUrl = parseUrl(req.url || '/', true);
183
- this.path = parsedUrl.pathname || '/';
184
- this.url = new URL(this.path, `http://${req.headers.host || 'localhost'}`);
185
- this.query = parsedUrl.query as Record<string, any>;
260
+ // Fast URL parsing - just extract path, delay query parsing
261
+ const url = req.url || '/';
262
+ const queryIndex = url.indexOf('?');
263
+
264
+ if (queryIndex === -1) {
265
+ this.path = url;
266
+ this._queryString = '';
267
+ this._query = null; // Will be {} when accessed
268
+ } else {
269
+ this.path = url.substring(0, queryIndex);
270
+ // Store query string for lazy parsing
271
+ this._queryString = url.substring(queryIndex + 1);
272
+ this._query = null; // Parse lazily
273
+ }
274
+
275
+ // Store host for lazy URL creation
276
+ this._host = (req.headers.host as string) || 'localhost';
277
+
278
+ // URL is now lazy - don't create here!
279
+ this._url = null;
186
280
 
187
- // Parse headers
281
+ // Parse headers (direct reference, no copy)
188
282
  this.headers = req.headers as Headers;
189
283
 
190
- // Parse cookies
191
- this.cookies = new CookieManager(req.headers.cookie);
284
+ // Store cookie header for lazy parsing
285
+ this._cookieHeader = req.headers.cookie;
286
+ this._cookies = null;
287
+
288
+ // Get response builder from pool
289
+ this.response = acquireResponseBuilder();
290
+ }
192
291
 
193
- // Create response builder
194
- this.response = new ResponseBuilderImpl();
292
+ /**
293
+ * Lazy URL getter - only create URL object when accessed
294
+ * Most handlers don't need the full URL object
295
+ */
296
+ get url(): URL {
297
+ if (!this._url) {
298
+ this._url = new URL(this.path, `http://${this._host}`);
299
+ }
300
+ return this._url;
301
+ }
302
+
303
+ set url(value: URL) {
304
+ this._url = value;
305
+ }
306
+
307
+ /**
308
+ * Lazy query getter - only parse query string when accessed
309
+ * Most simple endpoints like /json don't need query parsing
310
+ * Inline fast-querystring for minimal overhead
311
+ */
312
+ get query(): Record<string, any> {
313
+ if (this._query === null) {
314
+ // Inline fast-querystring call directly - no method call overhead
315
+ this._query = this._queryString
316
+ ? fastQueryParse(this._queryString) as Record<string, any>
317
+ : {};
318
+ }
319
+ return this._query;
320
+ }
321
+
322
+ set query(value: Record<string, any>) {
323
+ this._query = value;
324
+ }
325
+
326
+ /**
327
+ * Lazy cookies getter - only parse cookies when accessed
328
+ */
329
+ get cookies(): Cookies {
330
+ if (!this._cookies) {
331
+ this._cookies = new CookieManager(this._cookieHeader);
332
+ }
333
+ return this._cookies;
334
+ }
335
+
336
+ set cookies(value: Cookies) {
337
+ this._cookies = value as CookieManager;
338
+ }
339
+
340
+ /**
341
+ * Reinitialize context for pooling (avoids new object creation)
342
+ */
343
+ reinitialize(req: IncomingMessage, res: ServerResponse): void {
344
+ this.raw = { req, res };
345
+ this.method = (req.method ? req.method.toUpperCase() : 'GET') as HTTPMethod;
346
+
347
+ // Fast URL parsing - delay query parsing
348
+ const url = req.url || '/';
349
+ const queryIndex = url.indexOf('?');
350
+
351
+ if (queryIndex === -1) {
352
+ this.path = url;
353
+ this._queryString = '';
354
+ this._query = null;
355
+ } else {
356
+ this.path = url.substring(0, queryIndex);
357
+ this._queryString = url.substring(queryIndex + 1);
358
+ this._query = null; // Parse lazily
359
+ }
360
+
361
+ // Lazy URL - don't create here
362
+ this._host = (req.headers.host as string) || 'localhost';
363
+ this._url = null;
364
+
365
+ this.headers = req.headers as Headers;
366
+
367
+ // Lazy cookies
368
+ this._cookieHeader = req.headers.cookie;
369
+ this._cookies = null;
370
+
371
+ // Reuse or get new response builder from pool
372
+ if (this.response && typeof (this.response as ResponseBuilderImpl).reset === 'function') {
373
+ (this.response as ResponseBuilderImpl).reset();
374
+ } else {
375
+ this.response = acquireResponseBuilder();
376
+ }
377
+
378
+ // Reset body state
379
+ this._parsedBody = undefined;
380
+ this._bodyPromise = null;
381
+ this._bodyParsed = false;
382
+
383
+ // Reset params
384
+ this.params = {};
385
+
386
+ // Lazy data and store - just null them, create on access
387
+ if (this._data) {
388
+ this._data.clear();
389
+ }
390
+ this._requestStoreRegistry = null;
391
+ }
392
+
393
+ /**
394
+ * Lazy body getter - parses body on first access
395
+ * This is the KEY optimization that fixes POST performance!
396
+ */
397
+ get body(): any {
398
+ // If already parsed synchronously, return it
399
+ if (this._bodyParsed) {
400
+ return this._parsedBody;
401
+ }
195
402
 
196
- // Create request-scoped store registry
197
- this._requestStoreRegistry = new RequestStoreRegistry(this._debug);
403
+ // Return undefined if not parsed yet
404
+ // Use getBody() for async access
405
+ return this._parsedBody;
406
+ }
407
+
408
+ /**
409
+ * Set body directly (for backwards compatibility)
410
+ */
411
+ set body(value: any) {
412
+ this._parsedBody = value;
413
+ this._bodyParsed = true;
414
+ }
415
+
416
+ /**
417
+ * Check if body is ready for sync access (no await needed)
418
+ */
419
+ get isBodyReady(): boolean {
420
+ return this._bodyParsed;
421
+ }
422
+
423
+ /**
424
+ * Wait for body to be parsed
425
+ * Use this if you need to ensure body is available for sync access
426
+ * @example
427
+ * ```typescript
428
+ * app.post('/data', async (ctx) => {
429
+ * await ctx.waitForBody();
430
+ * console.log(ctx.body); // Now safe to access synchronously
431
+ * });
432
+ * ```
433
+ */
434
+ async waitForBody(): Promise<any> {
435
+ return this.getBody();
436
+ }
437
+
438
+ /**
439
+ * Async body getter - use this in handlers for POST/PUT/PATCH
440
+ * @example
441
+ * ```typescript
442
+ * app.post('/data', async (ctx) => {
443
+ * const body = await ctx.getBody();
444
+ * return { received: body };
445
+ * });
446
+ * ```
447
+ */
448
+ async getBody<T = any>(): Promise<T> {
449
+ // Already parsed
450
+ if (this._bodyParsed) {
451
+ return this._parsedBody as T;
452
+ }
453
+
454
+ // Already parsing (dedup concurrent calls)
455
+ if (this._bodyPromise) {
456
+ return this._bodyPromise as Promise<T>;
457
+ }
458
+
459
+ // Start parsing with optimized parser
460
+ this._bodyPromise = this.parseBodyOptimized();
461
+ this._parsedBody = await this._bodyPromise;
462
+ this._bodyParsed = true;
463
+ this._bodyPromise = null;
464
+
465
+ return this._parsedBody as T;
466
+ }
467
+
468
+ /**
469
+ * Ultra-optimized body parser inspired by Fastify's approach
470
+ * Key optimizations:
471
+ * 1. Pre-check content-type before reading data
472
+ * 2. Use direct string concatenation with setEncoding
473
+ * 3. Minimal closure allocation
474
+ * 4. Fast-path for JSON (most common case)
475
+ */
476
+ private parseBodyOptimized(): Promise<any> {
477
+ const req = this.raw.req;
478
+ const contentType = req.headers['content-type'];
479
+
480
+ // Fast path: determine parser type once, before data collection
481
+ const isJSON = contentType ? contentType.charCodeAt(0) === 97 && contentType.startsWith('application/json') : false;
482
+ const isForm = !isJSON && contentType ? contentType.includes('x-www-form-urlencoded') : false;
483
+
484
+ return new Promise((resolve, reject) => {
485
+ // Set encoding for string mode - avoids Buffer.toString() overhead
486
+ req.setEncoding('utf8');
487
+
488
+ let body = '';
489
+
490
+ const onData = (chunk: string) => {
491
+ body += chunk;
492
+ };
493
+
494
+ const onEnd = () => {
495
+ // Cleanup listeners immediately
496
+ req.removeListener('data', onData);
497
+ req.removeListener('end', onEnd);
498
+ req.removeListener('error', onError);
499
+
500
+ if (!body) {
501
+ resolve({});
502
+ return;
503
+ }
504
+
505
+ try {
506
+ if (isJSON) {
507
+ resolve(JSON.parse(body));
508
+ } else if (isForm) {
509
+ resolve(fastQueryParse(body));
510
+ } else {
511
+ resolve(body);
512
+ }
513
+ } catch (e) {
514
+ reject(e);
515
+ }
516
+ };
517
+
518
+ const onError = (err: Error) => {
519
+ req.removeListener('data', onData);
520
+ req.removeListener('end', onEnd);
521
+ req.removeListener('error', onError);
522
+ reject(err);
523
+ };
524
+
525
+ req.on('data', onData);
526
+ req.on('end', onEnd);
527
+ req.on('error', onError);
528
+ });
529
+ }
530
+
531
+ /**
532
+ * Internal body parser - optimized for performance
533
+ * Uses string accumulation instead of Buffer.concat for better perf
534
+ * @deprecated Use parseBodyOptimized instead
535
+ */
536
+ private parseBodyInternal(): Promise<any> {
537
+ return new Promise((resolve, reject) => {
538
+ const req = this.raw.req;
539
+ const contentType = req.headers['content-type'] || '';
540
+
541
+ // Use setEncoding to get strings directly - faster than Buffer.toString()
542
+ req.setEncoding('utf8');
543
+
544
+ let body = '';
545
+
546
+ req.on('data', (chunk: string) => {
547
+ body += chunk;
548
+ });
549
+
550
+ req.on('end', () => {
551
+ if (!body) {
552
+ resolve({});
553
+ return;
554
+ }
555
+
556
+ try {
557
+ // Inline content type check for hot path (JSON)
558
+ if (contentType.includes('application/json')) {
559
+ resolve(JSON.parse(body));
560
+ } else if (contentType.includes('application/x-www-form-urlencoded')) {
561
+ resolve(fastQueryParse(body));
562
+ } else {
563
+ resolve(body);
564
+ }
565
+ } catch (error) {
566
+ reject(error);
567
+ }
568
+ });
569
+
570
+ req.on('error', reject);
571
+ });
572
+ }
573
+
574
+ /**
575
+ * Parse body based on content type
576
+ */
577
+ private parseContentType(body: string, contentType: string): any {
578
+ if (contentType.includes('application/json')) {
579
+ return body ? JSON.parse(body) : {};
580
+ } else if (contentType.includes('application/x-www-form-urlencoded')) {
581
+ return fastQueryParse(body);
582
+ } else if (contentType.includes('text/')) {
583
+ return body;
584
+ }
585
+ return body;
586
+ }
587
+
588
+ /**
589
+ * Clear body state (for pooling)
590
+ */
591
+ clearBody(): void {
592
+ this._parsedBody = undefined;
593
+ this._bodyPromise = null;
594
+ this._bodyParsed = false;
198
595
  }
199
596
 
200
597
  // Convenience methods
@@ -252,6 +649,26 @@ export class ContextImpl implements Context {
252
649
  return this._storeRegistry.get(StoreClass);
253
650
  }
254
651
 
652
+ /**
653
+ * Lazy getter for request store registry
654
+ */
655
+ private getOrCreateRequestStoreRegistry(): RequestStoreRegistry {
656
+ if (!this._requestStoreRegistry) {
657
+ this._requestStoreRegistry = new RequestStoreRegistry(this._debug);
658
+ }
659
+ return this._requestStoreRegistry;
660
+ }
661
+
662
+ /**
663
+ * Lazy getter for request data map
664
+ */
665
+ private getOrCreateData(): Map<string, any> {
666
+ if (!this._data) {
667
+ this._data = new Map();
668
+ }
669
+ return this._data;
670
+ }
671
+
255
672
  /**
256
673
  * Access a request-scoped store by its class
257
674
  * Store only exists for this request, disposed after response
@@ -281,7 +698,7 @@ export class ContextImpl implements Context {
281
698
  * ```
282
699
  */
283
700
  requestStore<T extends RequestStore<any>>(StoreClass: RequestStoreConstructor<T>): T {
284
- return this._requestStoreRegistry.get(StoreClass);
701
+ return this.getOrCreateRequestStoreRegistry().get(StoreClass);
285
702
  }
286
703
 
287
704
  /**
@@ -302,7 +719,7 @@ export class ContextImpl implements Context {
302
719
  * ```
303
720
  */
304
721
  set<T = any>(key: string, value: T): void {
305
- this._data.set(key, value);
722
+ this.getOrCreateData().set(key, value);
306
723
  }
307
724
 
308
725
  /**
@@ -318,7 +735,7 @@ export class ContextImpl implements Context {
318
735
  * ```
319
736
  */
320
737
  get<T = any>(key: string): T | undefined {
321
- return this._data.get(key) as T | undefined;
738
+ return this._data?.get(key) as T | undefined;
322
739
  }
323
740
 
324
741
  /**
@@ -335,8 +752,8 @@ export class ContextImpl implements Context {
335
752
  */
336
753
  setDebugMode(debug: boolean): void {
337
754
  this._debug = debug;
338
- // Re-create request store registry with debug mode
339
- this._requestStoreRegistry = new RequestStoreRegistry(debug);
755
+ // Reset request store registry - will be created lazily with new debug mode
756
+ this._requestStoreRegistry = null;
340
757
  }
341
758
 
342
759
  /**
@@ -344,8 +761,17 @@ export class ContextImpl implements Context {
344
761
  * @internal
345
762
  */
346
763
  disposeRequestStores(): void {
347
- this._requestStoreRegistry.dispose();
348
- this._data.clear();
764
+ if (this._requestStoreRegistry) {
765
+ this._requestStoreRegistry.dispose();
766
+ this._requestStoreRegistry = null;
767
+ }
768
+ if (this._data) {
769
+ this._data.clear();
770
+ }
771
+ // Release response builder back to pool
772
+ if (this.response && typeof (this.response as ResponseBuilderImpl).reset === 'function') {
773
+ releaseResponseBuilder(this.response as ResponseBuilderImpl);
774
+ }
349
775
  }
350
776
 
351
777
  /**
@@ -353,7 +779,7 @@ export class ContextImpl implements Context {
353
779
  * @internal
354
780
  */
355
781
  getRequestStoreRegistry(): RequestStoreRegistry {
356
- return this._requestStoreRegistry;
782
+ return this.getOrCreateRequestStoreRegistry();
357
783
  }
358
784
 
359
785
  /**
@@ -364,22 +790,40 @@ export class ContextImpl implements Context {
364
790
  }
365
791
 
366
792
  /**
367
- * Set request body (called after parsing)
793
+ * Set response serializers for fast JSON serialization
794
+ * Called by router when route has response schema
795
+ * @internal
796
+ */
797
+ setSerializers(serializers: Map<number | string, SerializerFunction>): void {
798
+ if (this.response && typeof (this.response as ResponseBuilderImpl).setSerializers === 'function') {
799
+ (this.response as ResponseBuilderImpl).setSerializers(serializers);
800
+ }
801
+ }
802
+
803
+ /**
804
+ * Set request body (called after parsing or by middleware)
805
+ * @deprecated Use ctx.getBody() for async body access
368
806
  */
369
807
  setBody(body: any): void {
370
- this.body = body;
808
+ this._parsedBody = body;
809
+ this._bodyParsed = true;
371
810
  }
372
811
 
373
812
  /**
374
813
  * Get all Set-Cookie headers
375
814
  */
376
815
  getSetCookieHeaders(): string[] {
377
- return (this.cookies as CookieManager).getSetCookieHeaders();
816
+ // Use _cookies directly to avoid creating CookieManager if not needed
817
+ if (!this._cookies) {
818
+ return [];
819
+ }
820
+ return this._cookies.getSetCookieHeaders();
378
821
  }
379
822
  }
380
823
 
381
824
  /**
382
825
  * Parse request body based on Content-Type
826
+ * @deprecated Use ctx.getBody() instead for lazy parsing
383
827
  */
384
828
  export async function parseBody(req: IncomingMessage): Promise<any> {
385
829
  return new Promise((resolve, reject) => {
@@ -396,7 +840,7 @@ export async function parseBody(req: IncomingMessage): Promise<any> {
396
840
  if (contentType.includes('application/json')) {
397
841
  resolve(body ? JSON.parse(body) : {});
398
842
  } else if (contentType.includes('application/x-www-form-urlencoded')) {
399
- resolve(parseQueryString(body));
843
+ resolve(fastQueryParse(body));
400
844
  } else if (contentType.includes('text/')) {
401
845
  resolve(body);
402
846
  } else {