@carno.js/core 0.2.9 → 0.2.10
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/dist/Carno.d.ts +1 -1
- package/dist/Carno.js +17 -32
- package/dist/container/middleware.resolver.js +6 -6
- package/dist/domain/BaseContext.d.ts +15 -0
- package/dist/domain/BaseContext.js +2 -0
- package/dist/domain/Context.d.ts +45 -24
- package/dist/domain/Context.js +110 -89
- package/dist/domain/FastContext.d.ts +34 -0
- package/dist/domain/FastContext.js +59 -0
- package/dist/domain/cors-headers-cache.d.ts +2 -0
- package/dist/domain/cors-headers-cache.js +44 -0
- package/dist/route/FastPathExecutor.d.ts +10 -2
- package/dist/route/FastPathExecutor.js +43 -12
- package/dist/route/JITCompiler.d.ts +25 -1
- package/dist/route/JITCompiler.js +205 -98
- package/dist/route/RouteCompiler.d.ts +0 -1
- package/dist/route/RouteCompiler.js +1 -44
- package/dist/route/RouteExecutor.js +18 -1
- package/dist/route/memoirist.d.ts +3 -0
- package/dist/route/memoirist.js +33 -3
- package/package.json +2 -2
package/dist/Carno.d.ts
CHANGED
|
@@ -60,8 +60,8 @@ export declare class Carno<TAdapter extends ValidatorAdapterConstructor = Valida
|
|
|
60
60
|
private closeHttpServer;
|
|
61
61
|
private exitProcess;
|
|
62
62
|
private reportHookFailure;
|
|
63
|
+
private resolveLogger;
|
|
63
64
|
private isCorsEnabled;
|
|
64
|
-
private isOriginAllowed;
|
|
65
65
|
private handlePreflightRequest;
|
|
66
66
|
private applyCorsHeaders;
|
|
67
67
|
close(closeActiveConnections?: boolean): void;
|
package/dist/Carno.js
CHANGED
|
@@ -20,10 +20,8 @@ const HttpException_1 = require("./exceptions/HttpException");
|
|
|
20
20
|
const RouteExecutor_1 = require("./route/RouteExecutor");
|
|
21
21
|
const memoirist_1 = __importDefault(require("./route/memoirist"));
|
|
22
22
|
const CompiledRoute_1 = require("./route/CompiledRoute");
|
|
23
|
-
const FastPathExecutor_1 = require("./route/FastPathExecutor");
|
|
24
23
|
const logger_service_1 = require("./services/logger.service");
|
|
25
24
|
const parseUrl = require("parseurl-fast");
|
|
26
|
-
// todo: change console.log for LoggerService.
|
|
27
25
|
class Carno {
|
|
28
26
|
constructor(config = {}) {
|
|
29
27
|
this.config = config;
|
|
@@ -45,7 +43,7 @@ class Carno {
|
|
|
45
43
|
});
|
|
46
44
|
if (this.isCorsEnabled()) {
|
|
47
45
|
const origin = request.headers.get("origin");
|
|
48
|
-
if (origin && this.isOriginAllowed(origin)) {
|
|
46
|
+
if (origin && this.corsCache.isOriginAllowed(origin)) {
|
|
49
47
|
response = this.applyCorsHeaders(response, origin);
|
|
50
48
|
}
|
|
51
49
|
}
|
|
@@ -55,7 +53,7 @@ class Carno {
|
|
|
55
53
|
}
|
|
56
54
|
};
|
|
57
55
|
this.catcher = (error) => {
|
|
58
|
-
|
|
56
|
+
this.resolveLogger().error("Unhandled error", error);
|
|
59
57
|
return new Response("Internal Server Error", { status: 500 });
|
|
60
58
|
};
|
|
61
59
|
this.validatorAdapter = this.resolveValidatorAdapter();
|
|
@@ -171,7 +169,7 @@ class Carno {
|
|
|
171
169
|
}
|
|
172
170
|
registerShutdownHandlers() {
|
|
173
171
|
const shutdown = async (signal) => {
|
|
174
|
-
|
|
172
|
+
this.resolveLogger().info(`Received ${signal}, starting graceful shutdown...`);
|
|
175
173
|
await this.handleShutdownHook();
|
|
176
174
|
};
|
|
177
175
|
node_process_1.default.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
@@ -185,7 +183,7 @@ class Carno {
|
|
|
185
183
|
}
|
|
186
184
|
createHttpServer(port) {
|
|
187
185
|
this.server = Bun.serve({ port, fetch: this.fetch, error: this.catcher });
|
|
188
|
-
|
|
186
|
+
this.resolveLogger().info(`Server running on port ${port}`);
|
|
189
187
|
}
|
|
190
188
|
async fetcher(request, server) {
|
|
191
189
|
if (this.isCorsEnabled()) {
|
|
@@ -206,8 +204,9 @@ class Carno {
|
|
|
206
204
|
let response;
|
|
207
205
|
const isCompiledRoute = compiled.routeType !== undefined;
|
|
208
206
|
if (isCompiledRoute && compiled.routeType === CompiledRoute_1.RouteType.SIMPLE) {
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
response = compiled.isAsync
|
|
208
|
+
? await compiled.boundHandler(context)
|
|
209
|
+
: compiled.boundHandler(context);
|
|
211
210
|
}
|
|
212
211
|
else {
|
|
213
212
|
const needsLocalsContainer = isCompiledRoute
|
|
@@ -221,7 +220,7 @@ class Carno {
|
|
|
221
220
|
}
|
|
222
221
|
if (this.isCorsEnabled()) {
|
|
223
222
|
const origin = request.headers.get("origin");
|
|
224
|
-
if (origin && this.isOriginAllowed(origin)) {
|
|
223
|
+
if (origin && this.corsCache.isOriginAllowed(origin)) {
|
|
225
224
|
response = this.applyCorsHeaders(response, origin);
|
|
226
225
|
}
|
|
227
226
|
}
|
|
@@ -248,42 +247,28 @@ class Carno {
|
|
|
248
247
|
}
|
|
249
248
|
closeHttpServer() {
|
|
250
249
|
if (this.server) {
|
|
251
|
-
|
|
250
|
+
this.resolveLogger().info("Closing HTTP server...");
|
|
252
251
|
this.server.stop(true);
|
|
253
252
|
}
|
|
254
253
|
}
|
|
255
254
|
exitProcess(code = 0) {
|
|
256
|
-
|
|
255
|
+
this.resolveLogger().info("Shutdown complete.");
|
|
257
256
|
node_process_1.default.exit(code);
|
|
258
257
|
}
|
|
259
258
|
reportHookFailure(event, error) {
|
|
260
|
-
|
|
259
|
+
this.resolveLogger().error(`Lifecycle hook ${event} failed`, error);
|
|
260
|
+
}
|
|
261
|
+
resolveLogger() {
|
|
262
|
+
const provider = this.injector.get(logger_service_1.LoggerService);
|
|
263
|
+
const instance = provider?.instance;
|
|
264
|
+
return instance ?? new logger_service_1.LoggerService(this.injector);
|
|
261
265
|
}
|
|
262
266
|
isCorsEnabled() {
|
|
263
267
|
return !!this.config.cors;
|
|
264
268
|
}
|
|
265
|
-
isOriginAllowed(origin) {
|
|
266
|
-
if (!origin || !this.config.cors) {
|
|
267
|
-
return false;
|
|
268
|
-
}
|
|
269
|
-
const { origins } = this.config.cors;
|
|
270
|
-
if (typeof origins === "string") {
|
|
271
|
-
return origins === "*" || origins === origin;
|
|
272
|
-
}
|
|
273
|
-
if (Array.isArray(origins)) {
|
|
274
|
-
return origins.includes(origin);
|
|
275
|
-
}
|
|
276
|
-
if (origins instanceof RegExp) {
|
|
277
|
-
return origins.test(origin);
|
|
278
|
-
}
|
|
279
|
-
if (typeof origins === "function") {
|
|
280
|
-
return origins(origin);
|
|
281
|
-
}
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
269
|
handlePreflightRequest(request) {
|
|
285
270
|
const origin = request.headers.get("origin");
|
|
286
|
-
if (!this.isOriginAllowed(origin)) {
|
|
271
|
+
if (!origin || !this.corsCache.isOriginAllowed(origin)) {
|
|
287
272
|
return new Response(null, { status: 403 });
|
|
288
273
|
}
|
|
289
274
|
const corsHeaders = this.corsCache.get(origin);
|
|
@@ -14,21 +14,21 @@ class MiddlewareResolver {
|
|
|
14
14
|
let currentIndex = 0;
|
|
15
15
|
const next = async () => {
|
|
16
16
|
if (currentIndex >= middlewares.length) {
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
17
|
+
// If all middlewares are already processed, do nothing.
|
|
18
|
+
// This avoids "Middleware stack exhausted" if a middleware calls `next()`
|
|
19
|
+
// when there are no more middlewares.
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const middleware = middlewares[currentIndex++];
|
|
23
23
|
// @ts-ignore
|
|
24
24
|
const instance = injector.invoke(middleware, local);
|
|
25
|
-
// Await
|
|
26
|
-
//
|
|
25
|
+
// Await the middleware execution.
|
|
26
|
+
// If the middleware throws, the exception will propagate.
|
|
27
27
|
await instance.handle(context, next);
|
|
28
28
|
};
|
|
29
29
|
if (middlewares.length === 0)
|
|
30
30
|
return;
|
|
31
|
-
//
|
|
31
|
+
// Start the middleware execution
|
|
32
32
|
await next();
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseContext - Interface mínima que todo contexto deve implementar.
|
|
3
|
+
*
|
|
4
|
+
* Define o contrato básico para FastContext e Context,
|
|
5
|
+
* permitindo que handlers compilados trabalhem com ambos.
|
|
6
|
+
*/
|
|
7
|
+
export interface BaseContext {
|
|
8
|
+
readonly req: Request;
|
|
9
|
+
param: Record<string, string>;
|
|
10
|
+
readonly headers: Headers;
|
|
11
|
+
readonly query: Record<string, string>;
|
|
12
|
+
status: number;
|
|
13
|
+
setResponseStatus(status: number): void;
|
|
14
|
+
getResponseStatus(): number;
|
|
15
|
+
}
|
package/dist/domain/Context.d.ts
CHANGED
|
@@ -1,36 +1,57 @@
|
|
|
1
1
|
import { Server } from 'bun';
|
|
2
|
+
/**
|
|
3
|
+
* Context otimizado com shape mínimo e lazy loading.
|
|
4
|
+
*
|
|
5
|
+
* Shape fixo mínimo (sempre alocado):
|
|
6
|
+
* - req: Request
|
|
7
|
+
* - param: Record<string, string>
|
|
8
|
+
* - status: number
|
|
9
|
+
*
|
|
10
|
+
* Lazy loading (só aloca quando usado):
|
|
11
|
+
* - query: Record<string, string> (getter lazy)
|
|
12
|
+
* - headers: Headers (getter que retorna req.headers)
|
|
13
|
+
* - body: Record<string, any> (getter lazy)
|
|
14
|
+
* - locals: Record<string, any> (getter lazy)
|
|
15
|
+
* - rawBody: ArrayBuffer (lazy)
|
|
16
|
+
*
|
|
17
|
+
* V8/JSC otimiza shape consistente. Propriedades lazy não quebram
|
|
18
|
+
* monomorfismo porque são getters, não props dinâmicas.
|
|
19
|
+
*/
|
|
2
20
|
export declare class Context {
|
|
3
|
-
query: Record<string, any>;
|
|
4
|
-
private _body;
|
|
5
|
-
rawBody?: ArrayBuffer;
|
|
6
|
-
param: Record<string, any>;
|
|
7
21
|
req: Request;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
private
|
|
22
|
+
param: Record<string, string>;
|
|
23
|
+
status: number;
|
|
24
|
+
private _queryString;
|
|
25
|
+
private _query;
|
|
26
|
+
private _locals;
|
|
27
|
+
private _body;
|
|
28
|
+
private _rawBody;
|
|
12
29
|
private _bodyParsed;
|
|
13
30
|
private constructor();
|
|
31
|
+
get headers(): Headers;
|
|
32
|
+
get query(): Record<string, string>;
|
|
33
|
+
set query(value: Record<string, string>);
|
|
34
|
+
get locals(): Record<string, any>;
|
|
35
|
+
set locals(value: Record<string, any>);
|
|
14
36
|
get body(): Record<string, any>;
|
|
15
37
|
set body(value: Record<string, any>);
|
|
38
|
+
get rawBody(): ArrayBuffer | undefined;
|
|
39
|
+
set rawBody(value: ArrayBuffer | undefined);
|
|
16
40
|
getBody(): Promise<Record<string, any>>;
|
|
17
41
|
isBodyParsed(): boolean;
|
|
18
|
-
static createFromRequest(url: any, request: Request, server: Server<any>): Promise<Context>;
|
|
19
|
-
static createFromRequestSync(url: any, request: Request, server: Server<any>): Context;
|
|
20
|
-
static createFromRequestWithBody(url: any, request: Request, server: Server<any>): Promise<Context>;
|
|
21
|
-
static createFromJob(job: any): Context;
|
|
22
|
-
private setQuery;
|
|
23
|
-
private setBody;
|
|
24
|
-
private setReq;
|
|
25
|
-
private setHeaders;
|
|
26
|
-
setParam(param: Record<string, any>): void;
|
|
27
42
|
setResponseStatus(status: number): void;
|
|
28
43
|
getResponseStatus(): number;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
setParam(param: Record<string, string>): void;
|
|
45
|
+
static createFromRequestSync(url: {
|
|
46
|
+
query?: string;
|
|
47
|
+
}, request: Request, server: Server<any>): Context;
|
|
48
|
+
static createFromRequest(url: {
|
|
49
|
+
query?: string;
|
|
50
|
+
}, request: Request, server: Server<any>): Promise<Context>;
|
|
51
|
+
static createFromJob(job: any): Context;
|
|
52
|
+
private parseQueryString;
|
|
53
|
+
private parseBody;
|
|
54
|
+
private parseJsonBody;
|
|
55
|
+
private parseFormDataBody;
|
|
56
|
+
private parseUrlEncodedBody;
|
|
36
57
|
}
|
package/dist/domain/Context.js
CHANGED
|
@@ -15,144 +15,165 @@ const http_code_enum_1 = require("../commons/http-code.enum");
|
|
|
15
15
|
const Injectable_decorator_1 = require("../commons/decorators/Injectable.decorator");
|
|
16
16
|
const HttpException_1 = require("../exceptions/HttpException");
|
|
17
17
|
const provider_scope_1 = require("./provider-scope");
|
|
18
|
+
/**
|
|
19
|
+
* Context otimizado com shape mínimo e lazy loading.
|
|
20
|
+
*
|
|
21
|
+
* Shape fixo mínimo (sempre alocado):
|
|
22
|
+
* - req: Request
|
|
23
|
+
* - param: Record<string, string>
|
|
24
|
+
* - status: number
|
|
25
|
+
*
|
|
26
|
+
* Lazy loading (só aloca quando usado):
|
|
27
|
+
* - query: Record<string, string> (getter lazy)
|
|
28
|
+
* - headers: Headers (getter que retorna req.headers)
|
|
29
|
+
* - body: Record<string, any> (getter lazy)
|
|
30
|
+
* - locals: Record<string, any> (getter lazy)
|
|
31
|
+
* - rawBody: ArrayBuffer (lazy)
|
|
32
|
+
*
|
|
33
|
+
* V8/JSC otimiza shape consistente. Propriedades lazy não quebram
|
|
34
|
+
* monomorfismo porque são getters, não props dinâmicas.
|
|
35
|
+
*/
|
|
18
36
|
let Context = Context_1 = class Context {
|
|
19
37
|
constructor() {
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.locals = {};
|
|
25
|
-
this._pendingRequest = null;
|
|
38
|
+
this._query = null;
|
|
39
|
+
this._locals = null;
|
|
40
|
+
this._body = null;
|
|
41
|
+
this._rawBody = null;
|
|
26
42
|
this._bodyParsed = false;
|
|
43
|
+
this.req = undefined;
|
|
44
|
+
this.param = {};
|
|
45
|
+
this.status = 200;
|
|
46
|
+
}
|
|
47
|
+
get headers() {
|
|
48
|
+
return this.req.headers;
|
|
49
|
+
}
|
|
50
|
+
get query() {
|
|
51
|
+
if (this._query === null) {
|
|
52
|
+
this._query = this.parseQueryString();
|
|
53
|
+
}
|
|
54
|
+
return this._query;
|
|
55
|
+
}
|
|
56
|
+
set query(value) {
|
|
57
|
+
this._query = value;
|
|
58
|
+
}
|
|
59
|
+
get locals() {
|
|
60
|
+
if (this._locals === null) {
|
|
61
|
+
this._locals = {};
|
|
62
|
+
}
|
|
63
|
+
return this._locals;
|
|
64
|
+
}
|
|
65
|
+
set locals(value) {
|
|
66
|
+
this._locals = value;
|
|
27
67
|
}
|
|
28
68
|
get body() {
|
|
69
|
+
if (this._body === null) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
29
72
|
return this._body;
|
|
30
73
|
}
|
|
31
74
|
set body(value) {
|
|
32
75
|
this._body = value;
|
|
33
76
|
this._bodyParsed = true;
|
|
34
77
|
}
|
|
78
|
+
get rawBody() {
|
|
79
|
+
return this._rawBody ?? undefined;
|
|
80
|
+
}
|
|
81
|
+
set rawBody(value) {
|
|
82
|
+
this._rawBody = value ?? null;
|
|
83
|
+
}
|
|
35
84
|
async getBody() {
|
|
36
|
-
if (!this._bodyParsed
|
|
37
|
-
await this.
|
|
38
|
-
this._pendingRequest = null;
|
|
39
|
-
this._bodyParsed = true;
|
|
85
|
+
if (!this._bodyParsed) {
|
|
86
|
+
await this.parseBody();
|
|
40
87
|
}
|
|
41
|
-
return this._body;
|
|
88
|
+
return this._body ?? {};
|
|
42
89
|
}
|
|
43
90
|
isBodyParsed() {
|
|
44
91
|
return this._bodyParsed;
|
|
45
92
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
93
|
+
setResponseStatus(status) {
|
|
94
|
+
this.status = status;
|
|
95
|
+
}
|
|
96
|
+
getResponseStatus() {
|
|
97
|
+
return this.status;
|
|
98
|
+
}
|
|
99
|
+
setParam(param) {
|
|
100
|
+
this.param = param;
|
|
52
101
|
}
|
|
53
102
|
static createFromRequestSync(url, request, server) {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
103
|
+
const ctx = new Context_1();
|
|
104
|
+
ctx.req = request;
|
|
105
|
+
ctx.param = {};
|
|
106
|
+
ctx._queryString = url.query;
|
|
107
|
+
const method = request.method;
|
|
108
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
109
|
+
ctx._bodyParsed = false;
|
|
61
110
|
}
|
|
62
111
|
else {
|
|
63
|
-
|
|
112
|
+
ctx._bodyParsed = true;
|
|
64
113
|
}
|
|
65
|
-
return
|
|
114
|
+
return ctx;
|
|
66
115
|
}
|
|
67
|
-
static async
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
await
|
|
116
|
+
static async createFromRequest(url, request, server) {
|
|
117
|
+
const ctx = Context_1.createFromRequestSync(url, request, server);
|
|
118
|
+
if (!ctx._bodyParsed) {
|
|
119
|
+
await ctx.getBody();
|
|
71
120
|
}
|
|
72
|
-
return
|
|
121
|
+
return ctx;
|
|
73
122
|
}
|
|
74
123
|
static createFromJob(job) {
|
|
75
|
-
|
|
76
|
-
return context;
|
|
77
|
-
}
|
|
78
|
-
// @ts-ignore
|
|
79
|
-
setQuery({ query }) {
|
|
80
|
-
this.query = this.buildQueryObject(query);
|
|
124
|
+
return new Context_1();
|
|
81
125
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
126
|
+
parseQueryString() {
|
|
127
|
+
if (!this._queryString) {
|
|
128
|
+
return {};
|
|
85
129
|
}
|
|
130
|
+
return Object.fromEntries(new URLSearchParams(this._queryString));
|
|
86
131
|
}
|
|
87
|
-
|
|
88
|
-
this.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
setParam(param) {
|
|
94
|
-
this.param = param;
|
|
95
|
-
}
|
|
96
|
-
setResponseStatus(status) {
|
|
97
|
-
this.resultStatus = status;
|
|
98
|
-
}
|
|
99
|
-
getResponseStatus() {
|
|
100
|
-
return this.resultStatus;
|
|
101
|
-
}
|
|
102
|
-
buildQueryObject(query) {
|
|
103
|
-
return query ? Object.fromEntries(new URLSearchParams(query)) : {};
|
|
104
|
-
}
|
|
105
|
-
async resolveBody(request) {
|
|
106
|
-
const contentType = request.headers.get('content-type') || '';
|
|
107
|
-
// Clone request once - preserve original request untouched
|
|
108
|
-
const clonedRequest = request.clone();
|
|
109
|
-
// FormData multipart requires consuming as formData
|
|
110
|
-
if (contentType.includes('multipart/form-data')) {
|
|
111
|
-
// Need separate clone for rawBody since formData() consumes the body
|
|
112
|
-
this.rawBody = await request.clone().arrayBuffer();
|
|
113
|
-
this.setBody(await clonedRequest.formData());
|
|
132
|
+
async parseBody() {
|
|
133
|
+
this._bodyParsed = true;
|
|
134
|
+
const contentType = this.req.headers.get('content-type') ?? '';
|
|
135
|
+
if (contentType.includes('application/json')) {
|
|
136
|
+
this._body = await this.parseJsonBody();
|
|
114
137
|
return;
|
|
115
138
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (contentType.includes('application/json')) {
|
|
119
|
-
this._body = this.parseJsonFromBuffer(this.rawBody);
|
|
139
|
+
if (contentType.includes('multipart/form-data')) {
|
|
140
|
+
this._body = await this.parseFormDataBody();
|
|
120
141
|
return;
|
|
121
142
|
}
|
|
122
143
|
if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
123
|
-
this._body = this.
|
|
144
|
+
this._body = await this.parseUrlEncodedBody();
|
|
124
145
|
return;
|
|
125
146
|
}
|
|
126
|
-
|
|
127
|
-
this._body = { body: this.decodeBuffer(this.rawBody) };
|
|
147
|
+
this._body = {};
|
|
128
148
|
}
|
|
129
|
-
|
|
130
|
-
|
|
149
|
+
async parseJsonBody() {
|
|
150
|
+
const contentLength = this.req.headers.get('content-length');
|
|
151
|
+
if (contentLength === '0') {
|
|
131
152
|
return {};
|
|
132
153
|
}
|
|
133
|
-
return this.parseJsonText(this.decodeBuffer(buffer));
|
|
134
|
-
}
|
|
135
|
-
parseJsonText(text) {
|
|
136
154
|
try {
|
|
137
|
-
|
|
155
|
+
const payload = await this.req.json();
|
|
156
|
+
return payload;
|
|
138
157
|
}
|
|
139
158
|
catch {
|
|
140
|
-
throw new HttpException_1.HttpException(
|
|
159
|
+
throw new HttpException_1.HttpException('Invalid JSON body', http_code_enum_1.HttpCode.BAD_REQUEST);
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
|
-
|
|
144
|
-
|
|
162
|
+
async parseFormDataBody() {
|
|
163
|
+
const formData = await this.req.formData();
|
|
164
|
+
const result = {};
|
|
165
|
+
for (const [key, value] of formData.entries()) {
|
|
166
|
+
result[key] = value;
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
145
169
|
}
|
|
146
|
-
|
|
147
|
-
|
|
170
|
+
async parseUrlEncodedBody() {
|
|
171
|
+
const text = await this.req.text();
|
|
172
|
+
if (!text) {
|
|
148
173
|
return {};
|
|
149
174
|
}
|
|
150
|
-
const text = this.decodeBuffer(buffer);
|
|
151
175
|
return Object.fromEntries(new URLSearchParams(text));
|
|
152
176
|
}
|
|
153
|
-
decodeBuffer(buffer) {
|
|
154
|
-
return new TextDecoder().decode(buffer);
|
|
155
|
-
}
|
|
156
177
|
};
|
|
157
178
|
exports.Context = Context;
|
|
158
179
|
exports.Context = Context = Context_1 = __decorate([
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BaseContext } from './BaseContext';
|
|
2
|
+
/**
|
|
3
|
+
* FastContext - Contexto mínimo monomórfico para fast path.
|
|
4
|
+
*
|
|
5
|
+
* Shape fixo otimizado para V8/JSC JIT:
|
|
6
|
+
* - Apenas 3 propriedades diretas: req, param, status
|
|
7
|
+
* - Lazy getters para headers e query (zero alocação se não usados)
|
|
8
|
+
* - Sem locals, sem body, sem flags extras
|
|
9
|
+
* - Monomórfico: sempre mesmo shape, JIT otimiza agressivamente
|
|
10
|
+
*
|
|
11
|
+
* Usado quando:
|
|
12
|
+
* - Rota SIMPLE (RouteType.SIMPLE)
|
|
13
|
+
* - Sem middlewares
|
|
14
|
+
* - Sem DI
|
|
15
|
+
* - Sem body parsing
|
|
16
|
+
*/
|
|
17
|
+
export declare class FastContext implements BaseContext {
|
|
18
|
+
readonly req: Request;
|
|
19
|
+
param: Record<string, string>;
|
|
20
|
+
status: number;
|
|
21
|
+
private _query;
|
|
22
|
+
private _queryString;
|
|
23
|
+
constructor(req: Request, param: Record<string, string>, queryString: string | undefined);
|
|
24
|
+
get headers(): Headers;
|
|
25
|
+
get query(): Record<string, string>;
|
|
26
|
+
setResponseStatus(status: number): void;
|
|
27
|
+
getResponseStatus(): number;
|
|
28
|
+
private parseQueryString;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Factory otimizado para criar FastContext.
|
|
32
|
+
* Extrai queryString inline sem overhead de parseUrl.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createFastContext(req: Request, param: Record<string, string>, url: string): FastContext;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FastContext = void 0;
|
|
4
|
+
exports.createFastContext = createFastContext;
|
|
5
|
+
/**
|
|
6
|
+
* FastContext - Contexto mínimo monomórfico para fast path.
|
|
7
|
+
*
|
|
8
|
+
* Shape fixo otimizado para V8/JSC JIT:
|
|
9
|
+
* - Apenas 3 propriedades diretas: req, param, status
|
|
10
|
+
* - Lazy getters para headers e query (zero alocação se não usados)
|
|
11
|
+
* - Sem locals, sem body, sem flags extras
|
|
12
|
+
* - Monomórfico: sempre mesmo shape, JIT otimiza agressivamente
|
|
13
|
+
*
|
|
14
|
+
* Usado quando:
|
|
15
|
+
* - Rota SIMPLE (RouteType.SIMPLE)
|
|
16
|
+
* - Sem middlewares
|
|
17
|
+
* - Sem DI
|
|
18
|
+
* - Sem body parsing
|
|
19
|
+
*/
|
|
20
|
+
class FastContext {
|
|
21
|
+
constructor(req, param, queryString) {
|
|
22
|
+
this.req = req;
|
|
23
|
+
this.param = param;
|
|
24
|
+
this.status = 200;
|
|
25
|
+
this._query = null;
|
|
26
|
+
this._queryString = queryString;
|
|
27
|
+
}
|
|
28
|
+
get headers() {
|
|
29
|
+
return this.req.headers;
|
|
30
|
+
}
|
|
31
|
+
get query() {
|
|
32
|
+
if (this._query === null) {
|
|
33
|
+
this._query = this.parseQueryString();
|
|
34
|
+
}
|
|
35
|
+
return this._query;
|
|
36
|
+
}
|
|
37
|
+
setResponseStatus(status) {
|
|
38
|
+
this.status = status;
|
|
39
|
+
}
|
|
40
|
+
getResponseStatus() {
|
|
41
|
+
return this.status;
|
|
42
|
+
}
|
|
43
|
+
parseQueryString() {
|
|
44
|
+
if (!this._queryString) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
return Object.fromEntries(new URLSearchParams(this._queryString));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.FastContext = FastContext;
|
|
51
|
+
/**
|
|
52
|
+
* Factory otimizado para criar FastContext.
|
|
53
|
+
* Extrai queryString inline sem overhead de parseUrl.
|
|
54
|
+
*/
|
|
55
|
+
function createFastContext(req, param, url) {
|
|
56
|
+
const queryIdx = url.indexOf('?');
|
|
57
|
+
const queryString = queryIdx !== -1 ? url.slice(queryIdx + 1) : undefined;
|
|
58
|
+
return new FastContext(req, param, queryString);
|
|
59
|
+
}
|
|
@@ -8,8 +8,10 @@ export declare class CorsHeadersCache {
|
|
|
8
8
|
private readonly maxAgeString;
|
|
9
9
|
private readonly hasCredentials;
|
|
10
10
|
private readonly isWildcard;
|
|
11
|
+
private readonly originAllowed;
|
|
11
12
|
constructor(config: CorsConfig);
|
|
12
13
|
get(origin: string): Record<string, string>;
|
|
13
14
|
private buildHeaders;
|
|
14
15
|
applyToResponse(response: Response, origin: string): Response;
|
|
16
|
+
isOriginAllowed(origin: string): boolean;
|
|
15
17
|
}
|