@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
@@ -6,8 +6,7 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.ContextImpl = void 0;
8
8
  exports.parseBody = parseBody;
9
- const url_1 = require("url");
10
- const querystring_1 = require("querystring");
9
+ const fast_querystring_1 = require("fast-querystring");
11
10
  const store_1 = require("./store");
12
11
  /**
13
12
  * Cookie manager implementation
@@ -65,57 +64,94 @@ class CookieManager {
65
64
  });
66
65
  }
67
66
  }
67
+ /**
68
+ * Pre-cached headers for common content types
69
+ * This avoids object creation on every response
70
+ */
71
+ const CACHED_HEADERS = {
72
+ JSON: { 'Content-Type': 'application/json' },
73
+ HTML: { 'Content-Type': 'text/html; charset=utf-8' },
74
+ TEXT: { 'Content-Type': 'text/plain; charset=utf-8' },
75
+ };
68
76
  /**
69
77
  * Response builder implementation
78
+ * Supports fast-json-stringify for optimized JSON serialization
70
79
  */
71
80
  class ResponseBuilderImpl {
72
81
  _status = 200;
73
82
  _headers = {};
83
+ _hasCustomHeaders = false;
84
+ _serializers = null;
74
85
  status(code) {
75
86
  this._status = code;
76
87
  return this;
77
88
  }
78
89
  header(name, value) {
79
90
  this._headers[name] = value;
91
+ this._hasCustomHeaders = true;
80
92
  return this;
81
93
  }
94
+ /**
95
+ * Set serializers for this response builder (called by router)
96
+ * @internal
97
+ */
98
+ setSerializers(serializers) {
99
+ this._serializers = serializers;
100
+ }
101
+ /**
102
+ * Get the appropriate serializer for current status code
103
+ */
104
+ getSerializer() {
105
+ if (!this._serializers)
106
+ return null;
107
+ // Try exact match first
108
+ const exactMatch = this._serializers.get(this._status);
109
+ if (exactMatch)
110
+ return exactMatch;
111
+ // Try status code ranges (2xx, 4xx, 5xx)
112
+ const range = `${Math.floor(this._status / 100)}xx`;
113
+ const rangeMatch = this._serializers.get(range);
114
+ if (rangeMatch)
115
+ return rangeMatch;
116
+ // Try default
117
+ return this._serializers.get('default') || null;
118
+ }
82
119
  json(data) {
120
+ // Try to use fast serializer first
121
+ const serializer = this.getSerializer();
122
+ const body = serializer ? serializer(data) : JSON.stringify(data);
83
123
  return {
84
124
  statusCode: this._status,
85
- headers: {
86
- ...this._headers,
87
- 'Content-Type': 'application/json'
88
- },
89
- body: JSON.stringify(data)
125
+ headers: this._hasCustomHeaders
126
+ ? { ...this._headers, 'Content-Type': 'application/json' }
127
+ : CACHED_HEADERS.JSON,
128
+ body
90
129
  };
91
130
  }
92
131
  html(content) {
93
132
  return {
94
133
  statusCode: this._status,
95
- headers: {
96
- ...this._headers,
97
- 'Content-Type': 'text/html; charset=utf-8'
98
- },
134
+ headers: this._hasCustomHeaders
135
+ ? { ...this._headers, 'Content-Type': 'text/html; charset=utf-8' }
136
+ : CACHED_HEADERS.HTML,
99
137
  body: content
100
138
  };
101
139
  }
102
140
  text(content) {
103
141
  return {
104
142
  statusCode: this._status,
105
- headers: {
106
- ...this._headers,
107
- 'Content-Type': 'text/plain; charset=utf-8'
108
- },
143
+ headers: this._hasCustomHeaders
144
+ ? { ...this._headers, 'Content-Type': 'text/plain; charset=utf-8' }
145
+ : CACHED_HEADERS.TEXT,
109
146
  body: content
110
147
  };
111
148
  }
112
149
  redirect(url, status = 302) {
113
150
  return {
114
151
  statusCode: status,
115
- headers: {
116
- ...this._headers,
117
- 'Location': url
118
- },
152
+ headers: this._hasCustomHeaders
153
+ ? { ...this._headers, 'Location': url }
154
+ : { 'Location': url },
119
155
  body: ''
120
156
  };
121
157
  }
@@ -127,6 +163,33 @@ class ResponseBuilderImpl {
127
163
  stream: readable
128
164
  };
129
165
  }
166
+ /**
167
+ * Reset for reuse (object pooling)
168
+ */
169
+ reset() {
170
+ this._status = 200;
171
+ this._headers = {};
172
+ this._hasCustomHeaders = false;
173
+ this._serializers = null;
174
+ }
175
+ }
176
+ /**
177
+ * Reusable response builder pool
178
+ */
179
+ const responseBuilderPool = [];
180
+ const RESPONSE_BUILDER_POOL_SIZE = 100;
181
+ function acquireResponseBuilder() {
182
+ if (responseBuilderPool.length > 0) {
183
+ const builder = responseBuilderPool.pop();
184
+ builder.reset();
185
+ return builder;
186
+ }
187
+ return new ResponseBuilderImpl();
188
+ }
189
+ function releaseResponseBuilder(builder) {
190
+ if (responseBuilderPool.length < RESPONSE_BUILDER_POOL_SIZE) {
191
+ responseBuilderPool.push(builder);
192
+ }
130
193
  }
131
194
  /**
132
195
  * Context implementation
@@ -134,39 +197,328 @@ class ResponseBuilderImpl {
134
197
  class ContextImpl {
135
198
  method;
136
199
  path;
137
- url;
200
+ _url = null; // Lazy URL creation
201
+ _host = 'localhost';
138
202
  params = {};
139
- query = {};
140
- body = null;
203
+ _query = null; // Lazy query parsing
204
+ _queryString = ''; // Raw query string for lazy parsing
141
205
  headers;
142
- cookies;
206
+ _cookieHeader;
207
+ _cookies = null; // Lazy cookie parsing
143
208
  raw;
144
209
  response;
210
+ // Lazy body parsing - key optimization!
211
+ _parsedBody = undefined;
212
+ _bodyPromise = null;
213
+ _bodyParsed = false;
145
214
  // Store registry reference (set by Application)
146
215
  _storeRegistry;
147
- // Request-scoped store registry (created per request)
148
- _requestStoreRegistry;
149
- // Request-scoped simple key-value storage
150
- _data = new Map();
216
+ // Request-scoped store registry - now lazy!
217
+ _requestStoreRegistry = null;
218
+ // Request-scoped simple key-value storage - now lazy!
219
+ _data = null;
151
220
  // Debug mode
152
221
  _debug = false;
153
222
  constructor(req, res) {
154
223
  this.raw = { req, res };
155
- // Parse method
156
- this.method = (req.method?.toUpperCase() || 'GET');
157
- // Parse URL and query
158
- const parsedUrl = (0, url_1.parse)(req.url || '/', true);
159
- this.path = parsedUrl.pathname || '/';
160
- this.url = new URL(this.path, `http://${req.headers.host || 'localhost'}`);
161
- this.query = parsedUrl.query;
162
- // Parse headers
224
+ // Parse method - use direct access, avoid optional chaining overhead
225
+ this.method = (req.method ? req.method.toUpperCase() : 'GET');
226
+ // Fast URL parsing - just extract path, delay query parsing
227
+ const url = req.url || '/';
228
+ const queryIndex = url.indexOf('?');
229
+ if (queryIndex === -1) {
230
+ this.path = url;
231
+ this._queryString = '';
232
+ this._query = null; // Will be {} when accessed
233
+ }
234
+ else {
235
+ this.path = url.substring(0, queryIndex);
236
+ // Store query string for lazy parsing
237
+ this._queryString = url.substring(queryIndex + 1);
238
+ this._query = null; // Parse lazily
239
+ }
240
+ // Store host for lazy URL creation
241
+ this._host = req.headers.host || 'localhost';
242
+ // URL is now lazy - don't create here!
243
+ this._url = null;
244
+ // Parse headers (direct reference, no copy)
163
245
  this.headers = req.headers;
164
- // Parse cookies
165
- this.cookies = new CookieManager(req.headers.cookie);
166
- // Create response builder
167
- this.response = new ResponseBuilderImpl();
168
- // Create request-scoped store registry
169
- this._requestStoreRegistry = new store_1.RequestStoreRegistry(this._debug);
246
+ // Store cookie header for lazy parsing
247
+ this._cookieHeader = req.headers.cookie;
248
+ this._cookies = null;
249
+ // Get response builder from pool
250
+ this.response = acquireResponseBuilder();
251
+ }
252
+ /**
253
+ * Lazy URL getter - only create URL object when accessed
254
+ * Most handlers don't need the full URL object
255
+ */
256
+ get url() {
257
+ if (!this._url) {
258
+ this._url = new URL(this.path, `http://${this._host}`);
259
+ }
260
+ return this._url;
261
+ }
262
+ set url(value) {
263
+ this._url = value;
264
+ }
265
+ /**
266
+ * Lazy query getter - only parse query string when accessed
267
+ * Most simple endpoints like /json don't need query parsing
268
+ * Inline fast-querystring for minimal overhead
269
+ */
270
+ get query() {
271
+ if (this._query === null) {
272
+ // Inline fast-querystring call directly - no method call overhead
273
+ this._query = this._queryString
274
+ ? (0, fast_querystring_1.parse)(this._queryString)
275
+ : {};
276
+ }
277
+ return this._query;
278
+ }
279
+ set query(value) {
280
+ this._query = value;
281
+ }
282
+ /**
283
+ * Lazy cookies getter - only parse cookies when accessed
284
+ */
285
+ get cookies() {
286
+ if (!this._cookies) {
287
+ this._cookies = new CookieManager(this._cookieHeader);
288
+ }
289
+ return this._cookies;
290
+ }
291
+ set cookies(value) {
292
+ this._cookies = value;
293
+ }
294
+ /**
295
+ * Reinitialize context for pooling (avoids new object creation)
296
+ */
297
+ reinitialize(req, res) {
298
+ this.raw = { req, res };
299
+ this.method = (req.method ? req.method.toUpperCase() : 'GET');
300
+ // Fast URL parsing - delay query parsing
301
+ const url = req.url || '/';
302
+ const queryIndex = url.indexOf('?');
303
+ if (queryIndex === -1) {
304
+ this.path = url;
305
+ this._queryString = '';
306
+ this._query = null;
307
+ }
308
+ else {
309
+ this.path = url.substring(0, queryIndex);
310
+ this._queryString = url.substring(queryIndex + 1);
311
+ this._query = null; // Parse lazily
312
+ }
313
+ // Lazy URL - don't create here
314
+ this._host = req.headers.host || 'localhost';
315
+ this._url = null;
316
+ this.headers = req.headers;
317
+ // Lazy cookies
318
+ this._cookieHeader = req.headers.cookie;
319
+ this._cookies = null;
320
+ // Reuse or get new response builder from pool
321
+ if (this.response && typeof this.response.reset === 'function') {
322
+ this.response.reset();
323
+ }
324
+ else {
325
+ this.response = acquireResponseBuilder();
326
+ }
327
+ // Reset body state
328
+ this._parsedBody = undefined;
329
+ this._bodyPromise = null;
330
+ this._bodyParsed = false;
331
+ // Reset params
332
+ this.params = {};
333
+ // Lazy data and store - just null them, create on access
334
+ if (this._data) {
335
+ this._data.clear();
336
+ }
337
+ this._requestStoreRegistry = null;
338
+ }
339
+ /**
340
+ * Lazy body getter - parses body on first access
341
+ * This is the KEY optimization that fixes POST performance!
342
+ */
343
+ get body() {
344
+ // If already parsed synchronously, return it
345
+ if (this._bodyParsed) {
346
+ return this._parsedBody;
347
+ }
348
+ // Return undefined if not parsed yet
349
+ // Use getBody() for async access
350
+ return this._parsedBody;
351
+ }
352
+ /**
353
+ * Set body directly (for backwards compatibility)
354
+ */
355
+ set body(value) {
356
+ this._parsedBody = value;
357
+ this._bodyParsed = true;
358
+ }
359
+ /**
360
+ * Check if body is ready for sync access (no await needed)
361
+ */
362
+ get isBodyReady() {
363
+ return this._bodyParsed;
364
+ }
365
+ /**
366
+ * Wait for body to be parsed
367
+ * Use this if you need to ensure body is available for sync access
368
+ * @example
369
+ * ```typescript
370
+ * app.post('/data', async (ctx) => {
371
+ * await ctx.waitForBody();
372
+ * console.log(ctx.body); // Now safe to access synchronously
373
+ * });
374
+ * ```
375
+ */
376
+ async waitForBody() {
377
+ return this.getBody();
378
+ }
379
+ /**
380
+ * Async body getter - use this in handlers for POST/PUT/PATCH
381
+ * @example
382
+ * ```typescript
383
+ * app.post('/data', async (ctx) => {
384
+ * const body = await ctx.getBody();
385
+ * return { received: body };
386
+ * });
387
+ * ```
388
+ */
389
+ async getBody() {
390
+ // Already parsed
391
+ if (this._bodyParsed) {
392
+ return this._parsedBody;
393
+ }
394
+ // Already parsing (dedup concurrent calls)
395
+ if (this._bodyPromise) {
396
+ return this._bodyPromise;
397
+ }
398
+ // Start parsing with optimized parser
399
+ this._bodyPromise = this.parseBodyOptimized();
400
+ this._parsedBody = await this._bodyPromise;
401
+ this._bodyParsed = true;
402
+ this._bodyPromise = null;
403
+ return this._parsedBody;
404
+ }
405
+ /**
406
+ * Ultra-optimized body parser inspired by Fastify's approach
407
+ * Key optimizations:
408
+ * 1. Pre-check content-type before reading data
409
+ * 2. Use direct string concatenation with setEncoding
410
+ * 3. Minimal closure allocation
411
+ * 4. Fast-path for JSON (most common case)
412
+ */
413
+ parseBodyOptimized() {
414
+ const req = this.raw.req;
415
+ const contentType = req.headers['content-type'];
416
+ // Fast path: determine parser type once, before data collection
417
+ const isJSON = contentType ? contentType.charCodeAt(0) === 97 && contentType.startsWith('application/json') : false;
418
+ const isForm = !isJSON && contentType ? contentType.includes('x-www-form-urlencoded') : false;
419
+ return new Promise((resolve, reject) => {
420
+ // Set encoding for string mode - avoids Buffer.toString() overhead
421
+ req.setEncoding('utf8');
422
+ let body = '';
423
+ const onData = (chunk) => {
424
+ body += chunk;
425
+ };
426
+ const onEnd = () => {
427
+ // Cleanup listeners immediately
428
+ req.removeListener('data', onData);
429
+ req.removeListener('end', onEnd);
430
+ req.removeListener('error', onError);
431
+ if (!body) {
432
+ resolve({});
433
+ return;
434
+ }
435
+ try {
436
+ if (isJSON) {
437
+ resolve(JSON.parse(body));
438
+ }
439
+ else if (isForm) {
440
+ resolve((0, fast_querystring_1.parse)(body));
441
+ }
442
+ else {
443
+ resolve(body);
444
+ }
445
+ }
446
+ catch (e) {
447
+ reject(e);
448
+ }
449
+ };
450
+ const onError = (err) => {
451
+ req.removeListener('data', onData);
452
+ req.removeListener('end', onEnd);
453
+ req.removeListener('error', onError);
454
+ reject(err);
455
+ };
456
+ req.on('data', onData);
457
+ req.on('end', onEnd);
458
+ req.on('error', onError);
459
+ });
460
+ }
461
+ /**
462
+ * Internal body parser - optimized for performance
463
+ * Uses string accumulation instead of Buffer.concat for better perf
464
+ * @deprecated Use parseBodyOptimized instead
465
+ */
466
+ parseBodyInternal() {
467
+ return new Promise((resolve, reject) => {
468
+ const req = this.raw.req;
469
+ const contentType = req.headers['content-type'] || '';
470
+ // Use setEncoding to get strings directly - faster than Buffer.toString()
471
+ req.setEncoding('utf8');
472
+ let body = '';
473
+ req.on('data', (chunk) => {
474
+ body += chunk;
475
+ });
476
+ req.on('end', () => {
477
+ if (!body) {
478
+ resolve({});
479
+ return;
480
+ }
481
+ try {
482
+ // Inline content type check for hot path (JSON)
483
+ if (contentType.includes('application/json')) {
484
+ resolve(JSON.parse(body));
485
+ }
486
+ else if (contentType.includes('application/x-www-form-urlencoded')) {
487
+ resolve((0, fast_querystring_1.parse)(body));
488
+ }
489
+ else {
490
+ resolve(body);
491
+ }
492
+ }
493
+ catch (error) {
494
+ reject(error);
495
+ }
496
+ });
497
+ req.on('error', reject);
498
+ });
499
+ }
500
+ /**
501
+ * Parse body based on content type
502
+ */
503
+ parseContentType(body, contentType) {
504
+ if (contentType.includes('application/json')) {
505
+ return body ? JSON.parse(body) : {};
506
+ }
507
+ else if (contentType.includes('application/x-www-form-urlencoded')) {
508
+ return (0, fast_querystring_1.parse)(body);
509
+ }
510
+ else if (contentType.includes('text/')) {
511
+ return body;
512
+ }
513
+ return body;
514
+ }
515
+ /**
516
+ * Clear body state (for pooling)
517
+ */
518
+ clearBody() {
519
+ this._parsedBody = undefined;
520
+ this._bodyPromise = null;
521
+ this._bodyParsed = false;
170
522
  }
171
523
  // Convenience methods
172
524
  json(data, status) {
@@ -215,6 +567,24 @@ class ContextImpl {
215
567
  }
216
568
  return this._storeRegistry.get(StoreClass);
217
569
  }
570
+ /**
571
+ * Lazy getter for request store registry
572
+ */
573
+ getOrCreateRequestStoreRegistry() {
574
+ if (!this._requestStoreRegistry) {
575
+ this._requestStoreRegistry = new store_1.RequestStoreRegistry(this._debug);
576
+ }
577
+ return this._requestStoreRegistry;
578
+ }
579
+ /**
580
+ * Lazy getter for request data map
581
+ */
582
+ getOrCreateData() {
583
+ if (!this._data) {
584
+ this._data = new Map();
585
+ }
586
+ return this._data;
587
+ }
218
588
  /**
219
589
  * Access a request-scoped store by its class
220
590
  * Store only exists for this request, disposed after response
@@ -244,7 +614,7 @@ class ContextImpl {
244
614
  * ```
245
615
  */
246
616
  requestStore(StoreClass) {
247
- return this._requestStoreRegistry.get(StoreClass);
617
+ return this.getOrCreateRequestStoreRegistry().get(StoreClass);
248
618
  }
249
619
  /**
250
620
  * Set a value in request-scoped storage
@@ -264,7 +634,7 @@ class ContextImpl {
264
634
  * ```
265
635
  */
266
636
  set(key, value) {
267
- this._data.set(key, value);
637
+ this.getOrCreateData().set(key, value);
268
638
  }
269
639
  /**
270
640
  * Get a value from request-scoped storage
@@ -279,7 +649,7 @@ class ContextImpl {
279
649
  * ```
280
650
  */
281
651
  get(key) {
282
- return this._data.get(key);
652
+ return this._data?.get(key);
283
653
  }
284
654
  /**
285
655
  * Set store registry (called by Application)
@@ -294,23 +664,32 @@ class ContextImpl {
294
664
  */
295
665
  setDebugMode(debug) {
296
666
  this._debug = debug;
297
- // Re-create request store registry with debug mode
298
- this._requestStoreRegistry = new store_1.RequestStoreRegistry(debug);
667
+ // Reset request store registry - will be created lazily with new debug mode
668
+ this._requestStoreRegistry = null;
299
669
  }
300
670
  /**
301
671
  * Dispose request-scoped stores and data (called after response)
302
672
  * @internal
303
673
  */
304
674
  disposeRequestStores() {
305
- this._requestStoreRegistry.dispose();
306
- this._data.clear();
675
+ if (this._requestStoreRegistry) {
676
+ this._requestStoreRegistry.dispose();
677
+ this._requestStoreRegistry = null;
678
+ }
679
+ if (this._data) {
680
+ this._data.clear();
681
+ }
682
+ // Release response builder back to pool
683
+ if (this.response && typeof this.response.reset === 'function') {
684
+ releaseResponseBuilder(this.response);
685
+ }
307
686
  }
308
687
  /**
309
688
  * Get request store registry for advanced usage
310
689
  * @internal
311
690
  */
312
691
  getRequestStoreRegistry() {
313
- return this._requestStoreRegistry;
692
+ return this.getOrCreateRequestStoreRegistry();
314
693
  }
315
694
  /**
316
695
  * Set route parameters (called by router)
@@ -319,21 +698,38 @@ class ContextImpl {
319
698
  this.params = params;
320
699
  }
321
700
  /**
322
- * Set request body (called after parsing)
701
+ * Set response serializers for fast JSON serialization
702
+ * Called by router when route has response schema
703
+ * @internal
704
+ */
705
+ setSerializers(serializers) {
706
+ if (this.response && typeof this.response.setSerializers === 'function') {
707
+ this.response.setSerializers(serializers);
708
+ }
709
+ }
710
+ /**
711
+ * Set request body (called after parsing or by middleware)
712
+ * @deprecated Use ctx.getBody() for async body access
323
713
  */
324
714
  setBody(body) {
325
- this.body = body;
715
+ this._parsedBody = body;
716
+ this._bodyParsed = true;
326
717
  }
327
718
  /**
328
719
  * Get all Set-Cookie headers
329
720
  */
330
721
  getSetCookieHeaders() {
331
- return this.cookies.getSetCookieHeaders();
722
+ // Use _cookies directly to avoid creating CookieManager if not needed
723
+ if (!this._cookies) {
724
+ return [];
725
+ }
726
+ return this._cookies.getSetCookieHeaders();
332
727
  }
333
728
  }
334
729
  exports.ContextImpl = ContextImpl;
335
730
  /**
336
731
  * Parse request body based on Content-Type
732
+ * @deprecated Use ctx.getBody() instead for lazy parsing
337
733
  */
338
734
  async function parseBody(req) {
339
735
  return new Promise((resolve, reject) => {
@@ -348,7 +744,7 @@ async function parseBody(req) {
348
744
  resolve(body ? JSON.parse(body) : {});
349
745
  }
350
746
  else if (contentType.includes('application/x-www-form-urlencoded')) {
351
- resolve((0, querystring_1.parse)(body));
747
+ resolve((0, fast_querystring_1.parse)(body));
352
748
  }
353
749
  else if (contentType.includes('text/')) {
354
750
  resolve(body);