@engjts/nexus 0.1.8 → 0.1.9

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 (205) hide show
  1. package/package.json +1 -1
  2. package/BENCHMARK_REPORT.md +0 -343
  3. package/documentation/01-getting-started.md +0 -240
  4. package/documentation/02-context.md +0 -335
  5. package/documentation/03-routing.md +0 -397
  6. package/documentation/04-middleware.md +0 -483
  7. package/documentation/05-validation.md +0 -514
  8. package/documentation/06-error-handling.md +0 -465
  9. package/documentation/07-performance.md +0 -364
  10. package/documentation/08-adapters.md +0 -470
  11. package/documentation/09-api-reference.md +0 -548
  12. package/documentation/10-examples.md +0 -582
  13. package/documentation/11-deployment.md +0 -477
  14. package/documentation/12-sentry.md +0 -620
  15. package/documentation/13-sentry-data-storage.md +0 -996
  16. package/documentation/14-sentry-data-reference.md +0 -457
  17. package/documentation/15-sentry-summary.md +0 -409
  18. package/documentation/16-alerts-system.md +0 -745
  19. package/documentation/17-alert-adapters.md +0 -696
  20. package/documentation/18-alerts-implementation-summary.md +0 -385
  21. package/documentation/19-class-based-routing.md +0 -840
  22. package/documentation/20-websocket-realtime.md +0 -813
  23. package/documentation/21-cache-system.md +0 -510
  24. package/documentation/22-job-queue.md +0 -772
  25. package/documentation/23-sentry-plugin.md +0 -551
  26. package/documentation/24-testing-utilities.md +0 -1287
  27. package/documentation/25-api-versioning.md +0 -533
  28. package/documentation/26-context-store.md +0 -607
  29. package/documentation/27-dependency-injection.md +0 -329
  30. package/documentation/28-lifecycle-hooks.md +0 -521
  31. package/documentation/29-package-structure.md +0 -196
  32. package/documentation/30-plugin-system.md +0 -414
  33. package/documentation/31-jwt-authentication.md +0 -597
  34. package/documentation/32-cli.md +0 -268
  35. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  36. package/documentation/ALERTS-INDEX.md +0 -330
  37. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  38. package/documentation/README.md +0 -178
  39. package/documentation/index.html +0 -34
  40. package/modern_framework_paper.md +0 -1870
  41. package/public/css/style.css +0 -87
  42. package/public/index.html +0 -34
  43. package/public/js/app.js +0 -27
  44. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  45. package/src/advanced/cache/MultiTierCache.ts +0 -194
  46. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  47. package/src/advanced/cache/index.ts +0 -5
  48. package/src/advanced/cache/types.ts +0 -40
  49. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  50. package/src/advanced/graphql/index.ts +0 -22
  51. package/src/advanced/graphql/server.ts +0 -252
  52. package/src/advanced/graphql/types.ts +0 -42
  53. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  54. package/src/advanced/jobs/JobQueue.ts +0 -556
  55. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  56. package/src/advanced/jobs/index.ts +0 -5
  57. package/src/advanced/jobs/types.ts +0 -70
  58. package/src/advanced/observability/APMManager.ts +0 -163
  59. package/src/advanced/observability/AlertManager.ts +0 -109
  60. package/src/advanced/observability/MetricRegistry.ts +0 -151
  61. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  62. package/src/advanced/observability/StructuredLogger.ts +0 -154
  63. package/src/advanced/observability/TracingManager.ts +0 -117
  64. package/src/advanced/observability/adapters.ts +0 -304
  65. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  66. package/src/advanced/observability/index.ts +0 -11
  67. package/src/advanced/observability/types.ts +0 -174
  68. package/src/advanced/playground/extractPathParams.ts +0 -6
  69. package/src/advanced/playground/generateFieldExample.ts +0 -31
  70. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
  71. package/src/advanced/playground/generateSummary.ts +0 -19
  72. package/src/advanced/playground/getTagFromPath.ts +0 -9
  73. package/src/advanced/playground/index.ts +0 -8
  74. package/src/advanced/playground/playground.ts +0 -250
  75. package/src/advanced/playground/types.ts +0 -49
  76. package/src/advanced/playground/zodToExample.ts +0 -16
  77. package/src/advanced/playground/zodToParams.ts +0 -15
  78. package/src/advanced/postman/buildAuth.ts +0 -31
  79. package/src/advanced/postman/buildBody.ts +0 -15
  80. package/src/advanced/postman/buildQueryParams.ts +0 -27
  81. package/src/advanced/postman/buildRequestItem.ts +0 -36
  82. package/src/advanced/postman/buildResponses.ts +0 -11
  83. package/src/advanced/postman/buildUrl.ts +0 -33
  84. package/src/advanced/postman/capitalize.ts +0 -4
  85. package/src/advanced/postman/generateCollection.ts +0 -59
  86. package/src/advanced/postman/generateEnvironment.ts +0 -34
  87. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  88. package/src/advanced/postman/generateFieldExample.ts +0 -45
  89. package/src/advanced/postman/generateName.ts +0 -20
  90. package/src/advanced/postman/generateUUID.ts +0 -11
  91. package/src/advanced/postman/getTagFromPath.ts +0 -10
  92. package/src/advanced/postman/index.ts +0 -28
  93. package/src/advanced/postman/postman.ts +0 -156
  94. package/src/advanced/postman/slugify.ts +0 -7
  95. package/src/advanced/postman/types.ts +0 -140
  96. package/src/advanced/realtime/index.ts +0 -18
  97. package/src/advanced/realtime/websocket.ts +0 -231
  98. package/src/advanced/sentry/index.ts +0 -1236
  99. package/src/advanced/sentry/types.ts +0 -355
  100. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  101. package/src/advanced/static/generateETag.ts +0 -7
  102. package/src/advanced/static/getMimeType.ts +0 -9
  103. package/src/advanced/static/index.ts +0 -32
  104. package/src/advanced/static/isSafePath.ts +0 -13
  105. package/src/advanced/static/publicDir.ts +0 -21
  106. package/src/advanced/static/serveStatic.ts +0 -225
  107. package/src/advanced/static/spa.ts +0 -24
  108. package/src/advanced/static/types.ts +0 -159
  109. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  110. package/src/advanced/swagger/buildOperation.ts +0 -61
  111. package/src/advanced/swagger/buildParameters.ts +0 -61
  112. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  113. package/src/advanced/swagger/buildResponses.ts +0 -54
  114. package/src/advanced/swagger/capitalize.ts +0 -5
  115. package/src/advanced/swagger/convertPath.ts +0 -9
  116. package/src/advanced/swagger/createSwagger.ts +0 -12
  117. package/src/advanced/swagger/generateOperationId.ts +0 -21
  118. package/src/advanced/swagger/generateSpec.ts +0 -105
  119. package/src/advanced/swagger/generateSummary.ts +0 -24
  120. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  121. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  122. package/src/advanced/swagger/index.ts +0 -25
  123. package/src/advanced/swagger/swagger.ts +0 -237
  124. package/src/advanced/swagger/types.ts +0 -206
  125. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  126. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  127. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  128. package/src/advanced/testing/factory.ts +0 -509
  129. package/src/advanced/testing/harness.ts +0 -612
  130. package/src/advanced/testing/index.ts +0 -430
  131. package/src/advanced/testing/load-test.ts +0 -618
  132. package/src/advanced/testing/mock-server.ts +0 -498
  133. package/src/advanced/testing/mock.ts +0 -670
  134. package/src/cli/bin.ts +0 -9
  135. package/src/cli/cli.ts +0 -158
  136. package/src/cli/commands/add.ts +0 -178
  137. package/src/cli/commands/build.ts +0 -73
  138. package/src/cli/commands/create.ts +0 -166
  139. package/src/cli/commands/dev.ts +0 -85
  140. package/src/cli/commands/generate.ts +0 -99
  141. package/src/cli/commands/help.ts +0 -95
  142. package/src/cli/commands/init.ts +0 -91
  143. package/src/cli/commands/version.ts +0 -38
  144. package/src/cli/index.ts +0 -6
  145. package/src/cli/templates/generators.ts +0 -359
  146. package/src/cli/templates/index.ts +0 -680
  147. package/src/cli/utils/exec.ts +0 -52
  148. package/src/cli/utils/file-system.ts +0 -78
  149. package/src/cli/utils/logger.ts +0 -111
  150. package/src/core/adapter.ts +0 -88
  151. package/src/core/application.ts +0 -1453
  152. package/src/core/context-pool.ts +0 -79
  153. package/src/core/context.ts +0 -856
  154. package/src/core/index.ts +0 -94
  155. package/src/core/middleware.ts +0 -272
  156. package/src/core/performance/buffer-pool.ts +0 -108
  157. package/src/core/performance/middleware-optimizer.ts +0 -162
  158. package/src/core/plugin/PluginManager.ts +0 -435
  159. package/src/core/plugin/builder.ts +0 -358
  160. package/src/core/plugin/index.ts +0 -50
  161. package/src/core/plugin/types.ts +0 -214
  162. package/src/core/router/file-router.ts +0 -623
  163. package/src/core/router/index.ts +0 -260
  164. package/src/core/router/radix-tree.ts +0 -242
  165. package/src/core/serializer.ts +0 -397
  166. package/src/core/store/index.ts +0 -30
  167. package/src/core/store/registry.ts +0 -178
  168. package/src/core/store/request-store.ts +0 -240
  169. package/src/core/store/types.ts +0 -233
  170. package/src/core/types.ts +0 -616
  171. package/src/database/adapter.ts +0 -35
  172. package/src/database/adapters/index.ts +0 -1
  173. package/src/database/adapters/mysql.ts +0 -669
  174. package/src/database/database.ts +0 -70
  175. package/src/database/dialect.ts +0 -388
  176. package/src/database/index.ts +0 -12
  177. package/src/database/migrations.ts +0 -86
  178. package/src/database/optimizer.ts +0 -125
  179. package/src/database/query-builder.ts +0 -404
  180. package/src/database/realtime.ts +0 -53
  181. package/src/database/schema.ts +0 -71
  182. package/src/database/transactions.ts +0 -56
  183. package/src/database/types.ts +0 -87
  184. package/src/deployment/cluster.ts +0 -471
  185. package/src/deployment/config.ts +0 -454
  186. package/src/deployment/docker.ts +0 -599
  187. package/src/deployment/graceful-shutdown.ts +0 -373
  188. package/src/deployment/index.ts +0 -56
  189. package/src/index.ts +0 -281
  190. package/src/security/adapter.ts +0 -318
  191. package/src/security/auth/JWTPlugin.ts +0 -234
  192. package/src/security/auth/JWTProvider.ts +0 -316
  193. package/src/security/auth/adapter.ts +0 -12
  194. package/src/security/auth/jwt.ts +0 -234
  195. package/src/security/auth/middleware.ts +0 -188
  196. package/src/security/csrf.ts +0 -220
  197. package/src/security/headers.ts +0 -108
  198. package/src/security/index.ts +0 -60
  199. package/src/security/rate-limit/adapter.ts +0 -7
  200. package/src/security/rate-limit/memory.ts +0 -108
  201. package/src/security/rate-limit/middleware.ts +0 -181
  202. package/src/security/sanitization.ts +0 -75
  203. package/src/security/types.ts +0 -240
  204. package/src/security/utils.ts +0 -52
  205. package/tsconfig.json +0 -39
@@ -1,856 +0,0 @@
1
- /**
2
- * Context implementation
3
- * Provides a unified request/response context with immutable properties
4
- */
5
-
6
- import { IncomingMessage, ServerResponse } from 'http';
7
- import { parse as fastQueryParse } from 'fast-querystring';
8
- import {
9
- Context,
10
- Headers,
11
- Cookies,
12
- CookieOptions,
13
- ResponseBuilder,
14
- Response,
15
- HTTPMethod
16
- } from './types';
17
- import { ContextStore, StoreConstructor, StoreRegistry, RequestStore, RequestStoreConstructor, RequestStoreRegistry } from './store';
18
- import { SerializerFunction } from './serializer';
19
-
20
- /**
21
- * Cookie manager implementation
22
- */
23
- class CookieManager implements Cookies {
24
- private cookies: Map<string, string>;
25
- private setCookies: Array<{ name: string; value: string; options?: CookieOptions }> = [];
26
-
27
- constructor(cookieHeader?: string) {
28
- this.cookies = new Map();
29
- if (cookieHeader) {
30
- const pairs = cookieHeader.split(';');
31
- for (const pair of pairs) {
32
- const [name, value] = pair.trim().split('=');
33
- if (name && value) {
34
- this.cookies.set(name, decodeURIComponent(value));
35
- }
36
- }
37
- }
38
- }
39
-
40
- get(name: string): string | undefined {
41
- return this.cookies.get(name);
42
- }
43
-
44
- set(name: string, value: string, options?: CookieOptions): void {
45
- this.cookies.set(name, value);
46
- this.setCookies.push({ name, value, options });
47
- }
48
-
49
- delete(name: string): void {
50
- this.cookies.delete(name);
51
- this.setCookies.push({
52
- name,
53
- value: '',
54
- options: { expires: new Date(0) }
55
- });
56
- }
57
-
58
- getSetCookieHeaders(): string[] {
59
- return this.setCookies.map(({ name, value, options }) => {
60
- let cookie = `${name}=${encodeURIComponent(value)}`;
61
-
62
- if (options) {
63
- if (options.maxAge) cookie += `; Max-Age=${options.maxAge}`;
64
- if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
65
- if (options.path) cookie += `; Path=${options.path}`;
66
- if (options.domain) cookie += `; Domain=${options.domain}`;
67
- if (options.secure) cookie += '; Secure';
68
- if (options.httpOnly) cookie += '; HttpOnly';
69
- if (options.sameSite) cookie += `; SameSite=${options.sameSite}`;
70
- }
71
-
72
- return cookie;
73
- });
74
- }
75
- }
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
-
87
- /**
88
- * Response builder implementation
89
- * Supports fast-json-stringify for optimized JSON serialization
90
- */
91
- class ResponseBuilderImpl implements ResponseBuilder {
92
- private _status: number = 200;
93
- private _headers: Headers = {};
94
- private _hasCustomHeaders: boolean = false;
95
- private _serializers: Map<number | string, SerializerFunction> | null = null;
96
-
97
- status(code: number): ResponseBuilder {
98
- this._status = code;
99
- return this;
100
- }
101
-
102
- header(name: string, value: string): ResponseBuilder {
103
- this._headers[name] = value;
104
- this._hasCustomHeaders = true;
105
- return this;
106
- }
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
-
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
-
140
- return {
141
- statusCode: this._status,
142
- headers: this._hasCustomHeaders
143
- ? { ...this._headers, 'Content-Type': 'application/json' }
144
- : CACHED_HEADERS.JSON,
145
- body
146
- };
147
- }
148
-
149
- html(content: string): Response {
150
- return {
151
- statusCode: this._status,
152
- headers: this._hasCustomHeaders
153
- ? { ...this._headers, 'Content-Type': 'text/html; charset=utf-8' }
154
- : CACHED_HEADERS.HTML,
155
- body: content
156
- };
157
- }
158
-
159
- text(content: string): Response {
160
- return {
161
- statusCode: this._status,
162
- headers: this._hasCustomHeaders
163
- ? { ...this._headers, 'Content-Type': 'text/plain; charset=utf-8' }
164
- : CACHED_HEADERS.TEXT,
165
- body: content
166
- };
167
- }
168
-
169
- redirect(url: string, status: number = 302): Response {
170
- return {
171
- statusCode: status,
172
- headers: this._hasCustomHeaders
173
- ? { ...this._headers, 'Location': url }
174
- : { 'Location': url },
175
- body: ''
176
- };
177
- }
178
-
179
- stream(readable: NodeJS.ReadableStream): Response {
180
- return {
181
- statusCode: this._status,
182
- headers: this._headers,
183
- body: null,
184
- stream: readable
185
- };
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
- }
218
- }
219
-
220
- /**
221
- * Context implementation
222
- */
223
- export class ContextImpl implements Context {
224
- method: HTTPMethod;
225
- path: string;
226
- private _url: URL | null = null; // Lazy URL creation
227
- private _host: string = 'localhost';
228
- params: Record<string, string> = {};
229
- private _query: Record<string, any> | null = null; // Lazy query parsing
230
- private _queryString: string = ''; // Raw query string for lazy parsing
231
- headers: Headers;
232
- private _cookieHeader: string | undefined;
233
- private _cookies: CookieManager | null = null; // Lazy cookie parsing
234
- raw: { req: IncomingMessage; res: ServerResponse };
235
- response: ResponseBuilder;
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
-
242
- // Store registry reference (set by Application)
243
- private _storeRegistry?: StoreRegistry;
244
-
245
- // Request-scoped store registry - now lazy!
246
- private _requestStoreRegistry: RequestStoreRegistry | null = null;
247
-
248
- // Request-scoped simple key-value storage - now lazy!
249
- private _data: Map<string, any> | null = null;
250
-
251
- // Debug mode
252
- private _debug: boolean = false;
253
-
254
- constructor(req: IncomingMessage, res: ServerResponse) {
255
- this.raw = { req, res };
256
-
257
- // Parse method - use direct access, avoid optional chaining overhead
258
- this.method = (req.method ? req.method.toUpperCase() : 'GET') as HTTPMethod;
259
-
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;
280
-
281
- // Parse headers (direct reference, no copy)
282
- this.headers = req.headers as Headers;
283
-
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
- }
291
-
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
- }
402
-
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;
595
- }
596
-
597
- // Convenience methods
598
- json<T>(data: T, status?: number): Response {
599
- if (status !== undefined) {
600
- return this.response.status(status).json(data);
601
- }
602
- return this.response.json(data);
603
- }
604
-
605
- html(content: string, status?: number): Response {
606
- if (status !== undefined) {
607
- return this.response.status(status).html(content);
608
- }
609
- return this.response.html(content);
610
- }
611
-
612
- text(content: string, status?: number): Response {
613
- if (status !== undefined) {
614
- return this.response.status(status).text(content);
615
- }
616
- return this.response.text(content);
617
- }
618
-
619
- redirect(url: string, status?: number): Response {
620
- return this.response.redirect(url, status);
621
- }
622
-
623
- stream(readable: NodeJS.ReadableStream): Response {
624
- return this.response.stream(readable);
625
- }
626
-
627
- /**
628
- * Access a registered global store by its class
629
- * Store persist across all requests (singleton)
630
- *
631
- * @param StoreClass - Store constructor class
632
- * @returns Store instance
633
- *
634
- * @example
635
- * ```typescript
636
- * app.get('/users', async (ctx) => {
637
- * const userStore = ctx.store(UserStore);
638
- * return { users: userStore.state.users };
639
- * });
640
- * ```
641
- */
642
- store<T extends ContextStore<any>>(StoreClass: StoreConstructor<T>): T {
643
- if (!this._storeRegistry) {
644
- throw new Error(
645
- '[Context] Store registry not initialized. ' +
646
- 'Make sure to call app.stores([...]) before accessing stores.'
647
- );
648
- }
649
- return this._storeRegistry.get(StoreClass);
650
- }
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
-
672
- /**
673
- * Access a request-scoped store by its class
674
- * Store only exists for this request, disposed after response
675
- *
676
- * @param StoreClass - RequestStore constructor class
677
- * @returns Store instance (created on first access)
678
- *
679
- * @example
680
- * ```typescript
681
- * class CheckoutStore extends RequestStore<CheckoutState> {
682
- * protected initial() { return { items: [], total: 0 }; }
683
- *
684
- * addItem(item: Item) {
685
- * this.update({
686
- * items: [...this.state.items, item],
687
- * total: this.state.total + item.price
688
- * });
689
- * }
690
- * }
691
- *
692
- * app.post('/checkout', async (ctx) => {
693
- * const checkout = ctx.requestStore(CheckoutStore);
694
- * checkout.addItem(ctx.body.item);
695
- * return { total: checkout.state.total };
696
- * });
697
- * // checkout store is automatically disposed after response
698
- * ```
699
- */
700
- requestStore<T extends RequestStore<any>>(StoreClass: RequestStoreConstructor<T>): T {
701
- return this.getOrCreateRequestStoreRegistry().get(StoreClass);
702
- }
703
-
704
- /**
705
- * Set a value in request-scoped storage
706
- * Data is automatically cleared after the request completes
707
- *
708
- * @param key - Storage key
709
- * @param value - Value to store
710
- *
711
- * @example
712
- * ```typescript
713
- * // In middleware or onBefore
714
- * ctx.set('user', { id: '123', name: 'John' });
715
- * ctx.set('startTime', Date.now());
716
- *
717
- * // In handler
718
- * const user = ctx.get('user');
719
- * ```
720
- */
721
- set<T = any>(key: string, value: T): void {
722
- this.getOrCreateData().set(key, value);
723
- }
724
-
725
- /**
726
- * Get a value from request-scoped storage
727
- *
728
- * @param key - Storage key
729
- * @returns The stored value or undefined
730
- *
731
- * @example
732
- * ```typescript
733
- * const user = ctx.get<User>('user');
734
- * const startTime = ctx.get<number>('startTime');
735
- * ```
736
- */
737
- get<T = any>(key: string): T | undefined {
738
- return this._data?.get(key) as T | undefined;
739
- }
740
-
741
- /**
742
- * Set store registry (called by Application)
743
- * @internal
744
- */
745
- setStoreRegistry(registry: StoreRegistry): void {
746
- this._storeRegistry = registry;
747
- }
748
-
749
- /**
750
- * Set debug mode (called by Application)
751
- * @internal
752
- */
753
- setDebugMode(debug: boolean): void {
754
- this._debug = debug;
755
- // Reset request store registry - will be created lazily with new debug mode
756
- this._requestStoreRegistry = null;
757
- }
758
-
759
- /**
760
- * Dispose request-scoped stores and data (called after response)
761
- * @internal
762
- */
763
- disposeRequestStores(): void {
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
- }
775
- }
776
-
777
- /**
778
- * Get request store registry for advanced usage
779
- * @internal
780
- */
781
- getRequestStoreRegistry(): RequestStoreRegistry {
782
- return this.getOrCreateRequestStoreRegistry();
783
- }
784
-
785
- /**
786
- * Set route parameters (called by router)
787
- */
788
- setParams(params: Record<string, string>): void {
789
- this.params = params;
790
- }
791
-
792
- /**
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
806
- */
807
- setBody(body: any): void {
808
- this._parsedBody = body;
809
- this._bodyParsed = true;
810
- }
811
-
812
- /**
813
- * Get all Set-Cookie headers
814
- */
815
- getSetCookieHeaders(): string[] {
816
- // Use _cookies directly to avoid creating CookieManager if not needed
817
- if (!this._cookies) {
818
- return [];
819
- }
820
- return this._cookies.getSetCookieHeaders();
821
- }
822
- }
823
-
824
- /**
825
- * Parse request body based on Content-Type
826
- * @deprecated Use ctx.getBody() instead for lazy parsing
827
- */
828
- export async function parseBody(req: IncomingMessage): Promise<any> {
829
- return new Promise((resolve, reject) => {
830
- const contentType = req.headers['content-type'] || '';
831
- const chunks: Buffer[] = [];
832
-
833
- req.on('data', (chunk: Buffer) => chunks.push(chunk));
834
-
835
- req.on('end', () => {
836
- try {
837
- const buffer = Buffer.concat(chunks);
838
- const body = buffer.toString('utf-8');
839
-
840
- if (contentType.includes('application/json')) {
841
- resolve(body ? JSON.parse(body) : {});
842
- } else if (contentType.includes('application/x-www-form-urlencoded')) {
843
- resolve(fastQueryParse(body));
844
- } else if (contentType.includes('text/')) {
845
- resolve(body);
846
- } else {
847
- resolve(buffer);
848
- }
849
- } catch (error) {
850
- reject(error);
851
- }
852
- });
853
-
854
- req.on('error', reject);
855
- });
856
- }