@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.
- package/package.json +1 -1
- package/BENCHMARK_REPORT.md +0 -343
- package/documentation/01-getting-started.md +0 -240
- package/documentation/02-context.md +0 -335
- package/documentation/03-routing.md +0 -397
- package/documentation/04-middleware.md +0 -483
- package/documentation/05-validation.md +0 -514
- package/documentation/06-error-handling.md +0 -465
- package/documentation/07-performance.md +0 -364
- package/documentation/08-adapters.md +0 -470
- package/documentation/09-api-reference.md +0 -548
- package/documentation/10-examples.md +0 -582
- package/documentation/11-deployment.md +0 -477
- package/documentation/12-sentry.md +0 -620
- package/documentation/13-sentry-data-storage.md +0 -996
- package/documentation/14-sentry-data-reference.md +0 -457
- package/documentation/15-sentry-summary.md +0 -409
- package/documentation/16-alerts-system.md +0 -745
- package/documentation/17-alert-adapters.md +0 -696
- package/documentation/18-alerts-implementation-summary.md +0 -385
- package/documentation/19-class-based-routing.md +0 -840
- package/documentation/20-websocket-realtime.md +0 -813
- package/documentation/21-cache-system.md +0 -510
- package/documentation/22-job-queue.md +0 -772
- package/documentation/23-sentry-plugin.md +0 -551
- package/documentation/24-testing-utilities.md +0 -1287
- package/documentation/25-api-versioning.md +0 -533
- package/documentation/26-context-store.md +0 -607
- package/documentation/27-dependency-injection.md +0 -329
- package/documentation/28-lifecycle-hooks.md +0 -521
- package/documentation/29-package-structure.md +0 -196
- package/documentation/30-plugin-system.md +0 -414
- package/documentation/31-jwt-authentication.md +0 -597
- package/documentation/32-cli.md +0 -268
- package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
- package/documentation/ALERTS-INDEX.md +0 -330
- package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
- package/documentation/README.md +0 -178
- package/documentation/index.html +0 -34
- package/modern_framework_paper.md +0 -1870
- package/public/css/style.css +0 -87
- package/public/index.html +0 -34
- package/public/js/app.js +0 -27
- package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
- package/src/advanced/cache/MultiTierCache.ts +0 -194
- package/src/advanced/cache/RedisCacheStore.ts +0 -341
- package/src/advanced/cache/index.ts +0 -5
- package/src/advanced/cache/types.ts +0 -40
- package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
- package/src/advanced/graphql/index.ts +0 -22
- package/src/advanced/graphql/server.ts +0 -252
- package/src/advanced/graphql/types.ts +0 -42
- package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
- package/src/advanced/jobs/JobQueue.ts +0 -556
- package/src/advanced/jobs/RedisQueueStore.ts +0 -367
- package/src/advanced/jobs/index.ts +0 -5
- package/src/advanced/jobs/types.ts +0 -70
- package/src/advanced/observability/APMManager.ts +0 -163
- package/src/advanced/observability/AlertManager.ts +0 -109
- package/src/advanced/observability/MetricRegistry.ts +0 -151
- package/src/advanced/observability/ObservabilityCenter.ts +0 -304
- package/src/advanced/observability/StructuredLogger.ts +0 -154
- package/src/advanced/observability/TracingManager.ts +0 -117
- package/src/advanced/observability/adapters.ts +0 -304
- package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
- package/src/advanced/observability/index.ts +0 -11
- package/src/advanced/observability/types.ts +0 -174
- package/src/advanced/playground/extractPathParams.ts +0 -6
- package/src/advanced/playground/generateFieldExample.ts +0 -31
- package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
- package/src/advanced/playground/generateSummary.ts +0 -19
- package/src/advanced/playground/getTagFromPath.ts +0 -9
- package/src/advanced/playground/index.ts +0 -8
- package/src/advanced/playground/playground.ts +0 -250
- package/src/advanced/playground/types.ts +0 -49
- package/src/advanced/playground/zodToExample.ts +0 -16
- package/src/advanced/playground/zodToParams.ts +0 -15
- package/src/advanced/postman/buildAuth.ts +0 -31
- package/src/advanced/postman/buildBody.ts +0 -15
- package/src/advanced/postman/buildQueryParams.ts +0 -27
- package/src/advanced/postman/buildRequestItem.ts +0 -36
- package/src/advanced/postman/buildResponses.ts +0 -11
- package/src/advanced/postman/buildUrl.ts +0 -33
- package/src/advanced/postman/capitalize.ts +0 -4
- package/src/advanced/postman/generateCollection.ts +0 -59
- package/src/advanced/postman/generateEnvironment.ts +0 -34
- package/src/advanced/postman/generateExampleFromZod.ts +0 -21
- package/src/advanced/postman/generateFieldExample.ts +0 -45
- package/src/advanced/postman/generateName.ts +0 -20
- package/src/advanced/postman/generateUUID.ts +0 -11
- package/src/advanced/postman/getTagFromPath.ts +0 -10
- package/src/advanced/postman/index.ts +0 -28
- package/src/advanced/postman/postman.ts +0 -156
- package/src/advanced/postman/slugify.ts +0 -7
- package/src/advanced/postman/types.ts +0 -140
- package/src/advanced/realtime/index.ts +0 -18
- package/src/advanced/realtime/websocket.ts +0 -231
- package/src/advanced/sentry/index.ts +0 -1236
- package/src/advanced/sentry/types.ts +0 -355
- package/src/advanced/static/generateDirectoryListing.ts +0 -47
- package/src/advanced/static/generateETag.ts +0 -7
- package/src/advanced/static/getMimeType.ts +0 -9
- package/src/advanced/static/index.ts +0 -32
- package/src/advanced/static/isSafePath.ts +0 -13
- package/src/advanced/static/publicDir.ts +0 -21
- package/src/advanced/static/serveStatic.ts +0 -225
- package/src/advanced/static/spa.ts +0 -24
- package/src/advanced/static/types.ts +0 -159
- package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
- package/src/advanced/swagger/buildOperation.ts +0 -61
- package/src/advanced/swagger/buildParameters.ts +0 -61
- package/src/advanced/swagger/buildRequestBody.ts +0 -21
- package/src/advanced/swagger/buildResponses.ts +0 -54
- package/src/advanced/swagger/capitalize.ts +0 -5
- package/src/advanced/swagger/convertPath.ts +0 -9
- package/src/advanced/swagger/createSwagger.ts +0 -12
- package/src/advanced/swagger/generateOperationId.ts +0 -21
- package/src/advanced/swagger/generateSpec.ts +0 -105
- package/src/advanced/swagger/generateSummary.ts +0 -24
- package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
- package/src/advanced/swagger/generateThemeCss.ts +0 -53
- package/src/advanced/swagger/index.ts +0 -25
- package/src/advanced/swagger/swagger.ts +0 -237
- package/src/advanced/swagger/types.ts +0 -206
- package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
- package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
- package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
- package/src/advanced/testing/factory.ts +0 -509
- package/src/advanced/testing/harness.ts +0 -612
- package/src/advanced/testing/index.ts +0 -430
- package/src/advanced/testing/load-test.ts +0 -618
- package/src/advanced/testing/mock-server.ts +0 -498
- package/src/advanced/testing/mock.ts +0 -670
- package/src/cli/bin.ts +0 -9
- package/src/cli/cli.ts +0 -158
- package/src/cli/commands/add.ts +0 -178
- package/src/cli/commands/build.ts +0 -73
- package/src/cli/commands/create.ts +0 -166
- package/src/cli/commands/dev.ts +0 -85
- package/src/cli/commands/generate.ts +0 -99
- package/src/cli/commands/help.ts +0 -95
- package/src/cli/commands/init.ts +0 -91
- package/src/cli/commands/version.ts +0 -38
- package/src/cli/index.ts +0 -6
- package/src/cli/templates/generators.ts +0 -359
- package/src/cli/templates/index.ts +0 -680
- package/src/cli/utils/exec.ts +0 -52
- package/src/cli/utils/file-system.ts +0 -78
- package/src/cli/utils/logger.ts +0 -111
- package/src/core/adapter.ts +0 -88
- package/src/core/application.ts +0 -1453
- package/src/core/context-pool.ts +0 -79
- package/src/core/context.ts +0 -856
- package/src/core/index.ts +0 -94
- package/src/core/middleware.ts +0 -272
- package/src/core/performance/buffer-pool.ts +0 -108
- package/src/core/performance/middleware-optimizer.ts +0 -162
- package/src/core/plugin/PluginManager.ts +0 -435
- package/src/core/plugin/builder.ts +0 -358
- package/src/core/plugin/index.ts +0 -50
- package/src/core/plugin/types.ts +0 -214
- package/src/core/router/file-router.ts +0 -623
- package/src/core/router/index.ts +0 -260
- package/src/core/router/radix-tree.ts +0 -242
- package/src/core/serializer.ts +0 -397
- package/src/core/store/index.ts +0 -30
- package/src/core/store/registry.ts +0 -178
- package/src/core/store/request-store.ts +0 -240
- package/src/core/store/types.ts +0 -233
- package/src/core/types.ts +0 -616
- package/src/database/adapter.ts +0 -35
- package/src/database/adapters/index.ts +0 -1
- package/src/database/adapters/mysql.ts +0 -669
- package/src/database/database.ts +0 -70
- package/src/database/dialect.ts +0 -388
- package/src/database/index.ts +0 -12
- package/src/database/migrations.ts +0 -86
- package/src/database/optimizer.ts +0 -125
- package/src/database/query-builder.ts +0 -404
- package/src/database/realtime.ts +0 -53
- package/src/database/schema.ts +0 -71
- package/src/database/transactions.ts +0 -56
- package/src/database/types.ts +0 -87
- package/src/deployment/cluster.ts +0 -471
- package/src/deployment/config.ts +0 -454
- package/src/deployment/docker.ts +0 -599
- package/src/deployment/graceful-shutdown.ts +0 -373
- package/src/deployment/index.ts +0 -56
- package/src/index.ts +0 -281
- package/src/security/adapter.ts +0 -318
- package/src/security/auth/JWTPlugin.ts +0 -234
- package/src/security/auth/JWTProvider.ts +0 -316
- package/src/security/auth/adapter.ts +0 -12
- package/src/security/auth/jwt.ts +0 -234
- package/src/security/auth/middleware.ts +0 -188
- package/src/security/csrf.ts +0 -220
- package/src/security/headers.ts +0 -108
- package/src/security/index.ts +0 -60
- package/src/security/rate-limit/adapter.ts +0 -7
- package/src/security/rate-limit/memory.ts +0 -108
- package/src/security/rate-limit/middleware.ts +0 -181
- package/src/security/sanitization.ts +0 -75
- package/src/security/types.ts +0 -240
- package/src/security/utils.ts +0 -52
- package/tsconfig.json +0 -39
package/src/core/context.ts
DELETED
|
@@ -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
|
-
}
|