@carno.js/core 0.2.8 → 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 +10 -8
- package/dist/Carno.js +31 -33
- package/dist/commons/decorators/index.d.ts +1 -0
- package/dist/commons/decorators/index.js +1 -0
- package/dist/commons/decorators/validation.decorator.d.ts +32 -0
- package/dist/commons/decorators/validation.decorator.js +40 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/container/InjectorService.d.ts +3 -1
- package/dist/container/InjectorService.js +4 -3
- package/dist/container/MethodInvoker.d.ts +3 -2
- package/dist/container/MethodInvoker.js +4 -17
- package/dist/container/middleware.resolver.js +6 -6
- package/dist/domain/BaseContext.d.ts +15 -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/index.d.ts +1 -0
- package/dist/index.js +1 -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/ParamResolverFactory.d.ts +0 -5
- package/dist/route/ParamResolverFactory.js +0 -40
- package/dist/route/RouteCompiler.d.ts +3 -4
- package/dist/route/RouteCompiler.js +2 -54
- package/dist/route/RouteExecutor.js +18 -1
- package/dist/route/memoirist.d.ts +3 -0
- package/dist/route/memoirist.js +33 -3
- package/dist/utils/ValidationCache.d.ts +2 -0
- package/dist/utils/ValidationCache.js +10 -2
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/validation/ValidatorAdapter.d.ts +66 -0
- package/dist/validation/ValidatorAdapter.js +20 -0
- package/dist/validation/adapters/ClassValidatorAdapter.d.ts +23 -0
- package/dist/validation/adapters/ClassValidatorAdapter.js +47 -0
- package/dist/validation/adapters/ZodAdapter.d.ts +14 -0
- package/dist/validation/adapters/ZodAdapter.js +56 -0
- package/dist/validation/adapters/index.d.ts +4 -0
- package/dist/validation/adapters/index.js +7 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.js +20 -0
- package/package.json +17 -6
- package/dist/Cheetah.d.ts +0 -65
- package/dist/Cheetah.js +0 -307
- package/dist/default-routes-cheetah.d.ts +0 -3
- package/dist/default-routes-cheetah.js +0 -29
- package/dist/domain/CheetahClosure.d.ts +0 -1
- package/dist/domain/CheetahMiddleware.d.ts +0 -5
- package/dist/domain/CheetahMiddleware.js +0 -2
- package/dist/services/request-logger.service.d.ts +0 -15
- package/dist/services/request-logger.service.js +0 -50
- package/dist/utils/isClassValidator.d.ts +0 -6
- package/dist/utils/isClassValidator.js +0 -13
- /package/dist/domain/{CheetahClosure.js → BaseContext.js} +0 -0
package/dist/Carno.d.ts
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import { Server } from "bun";
|
|
2
|
-
import { ValidatorOptions } from "class-validator";
|
|
3
2
|
import * as pino from "pino";
|
|
3
|
+
import type { ValidationConfig, ValidatorAdapterConstructor } from "./validation/ValidatorAdapter";
|
|
4
4
|
import { TokenRouteWithProvider } from "./container/ContainerConfiguration";
|
|
5
5
|
import { CorsConfig } from "./domain/cors-config";
|
|
6
6
|
import Memoirist from "./route/memoirist";
|
|
7
7
|
import { type CompiledRoute } from "./route/CompiledRoute";
|
|
8
|
-
export interface ApplicationConfig {
|
|
9
|
-
validation?:
|
|
8
|
+
export interface ApplicationConfig<TAdapter extends ValidatorAdapterConstructor = ValidatorAdapterConstructor> {
|
|
9
|
+
validation?: ValidationConfig<TAdapter>;
|
|
10
10
|
logger?: pino.LoggerOptions;
|
|
11
11
|
exports?: any[];
|
|
12
12
|
providers?: any[];
|
|
13
13
|
cors?: CorsConfig;
|
|
14
14
|
globalMiddlewares?: any[];
|
|
15
15
|
}
|
|
16
|
-
export declare class Carno {
|
|
17
|
-
config: ApplicationConfig
|
|
16
|
+
export declare class Carno<TAdapter extends ValidatorAdapterConstructor = ValidatorAdapterConstructor> {
|
|
17
|
+
config: ApplicationConfig<TAdapter>;
|
|
18
18
|
router: Memoirist<CompiledRoute | TokenRouteWithProvider>;
|
|
19
19
|
private injector;
|
|
20
20
|
private corsCache?;
|
|
21
21
|
private readonly emptyLocals;
|
|
22
|
+
private validatorAdapter;
|
|
22
23
|
private fetch;
|
|
23
24
|
private server;
|
|
24
|
-
constructor(config?: ApplicationConfig);
|
|
25
|
+
constructor(config?: ApplicationConfig<TAdapter>);
|
|
26
|
+
private resolveValidatorAdapter;
|
|
25
27
|
/**
|
|
26
28
|
* Use the Carno plugin.
|
|
27
29
|
*
|
|
@@ -49,7 +51,7 @@ export declare class Carno {
|
|
|
49
51
|
listen(port?: number): Promise<void>;
|
|
50
52
|
private registerShutdownHandlers;
|
|
51
53
|
getHttpServer(): Server<any>;
|
|
52
|
-
getInjector(): import("
|
|
54
|
+
getInjector(): import("./container").InjectorService;
|
|
53
55
|
private createHttpServer;
|
|
54
56
|
private fetcher;
|
|
55
57
|
private catcher;
|
|
@@ -58,8 +60,8 @@ export declare class Carno {
|
|
|
58
60
|
private closeHttpServer;
|
|
59
61
|
private exitProcess;
|
|
60
62
|
private reportHookFailure;
|
|
63
|
+
private resolveLogger;
|
|
61
64
|
private isCorsEnabled;
|
|
62
|
-
private isOriginAllowed;
|
|
63
65
|
private handlePreflightRequest;
|
|
64
66
|
private applyCorsHeaders;
|
|
65
67
|
close(closeActiveConnections?: boolean): void;
|
package/dist/Carno.js
CHANGED
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Carno = void 0;
|
|
7
7
|
const node_process_1 = __importDefault(require("node:process"));
|
|
8
|
+
const ZodAdapter_1 = require("./validation/adapters/ZodAdapter");
|
|
9
|
+
const ValidationCache_1 = require("./utils/ValidationCache");
|
|
8
10
|
const index_1 = require("./commons/index");
|
|
9
11
|
const constants_1 = require("./constants");
|
|
10
12
|
const createContainer_1 = require("./container/createContainer");
|
|
@@ -18,10 +20,8 @@ const HttpException_1 = require("./exceptions/HttpException");
|
|
|
18
20
|
const RouteExecutor_1 = require("./route/RouteExecutor");
|
|
19
21
|
const memoirist_1 = __importDefault(require("./route/memoirist"));
|
|
20
22
|
const CompiledRoute_1 = require("./route/CompiledRoute");
|
|
21
|
-
const FastPathExecutor_1 = require("./route/FastPathExecutor");
|
|
22
23
|
const logger_service_1 = require("./services/logger.service");
|
|
23
24
|
const parseUrl = require("parseurl-fast");
|
|
24
|
-
// todo: change console.log for LoggerService.
|
|
25
25
|
class Carno {
|
|
26
26
|
constructor(config = {}) {
|
|
27
27
|
this.config = config;
|
|
@@ -43,7 +43,7 @@ class Carno {
|
|
|
43
43
|
});
|
|
44
44
|
if (this.isCorsEnabled()) {
|
|
45
45
|
const origin = request.headers.get("origin");
|
|
46
|
-
if (origin && this.isOriginAllowed(origin)) {
|
|
46
|
+
if (origin && this.corsCache.isOriginAllowed(origin)) {
|
|
47
47
|
response = this.applyCorsHeaders(response, origin);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -53,14 +53,24 @@ class Carno {
|
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
55
|
this.catcher = (error) => {
|
|
56
|
-
|
|
56
|
+
this.resolveLogger().error("Unhandled error", error);
|
|
57
57
|
return new Response("Internal Server Error", { status: 500 });
|
|
58
58
|
};
|
|
59
|
+
this.validatorAdapter = this.resolveValidatorAdapter();
|
|
59
60
|
if (config.cors) {
|
|
60
61
|
this.corsCache = new cors_headers_cache_1.CorsHeadersCache(config.cors);
|
|
61
62
|
}
|
|
62
63
|
void this.bootstrapApplication();
|
|
63
64
|
}
|
|
65
|
+
resolveValidatorAdapter() {
|
|
66
|
+
const config = this.config.validation;
|
|
67
|
+
if (!config?.adapter) {
|
|
68
|
+
return new ZodAdapter_1.ZodAdapter();
|
|
69
|
+
}
|
|
70
|
+
const AdapterClass = config.adapter;
|
|
71
|
+
const options = config.options || {};
|
|
72
|
+
return new AdapterClass(options);
|
|
73
|
+
}
|
|
64
74
|
/**
|
|
65
75
|
* Use the Carno plugin.
|
|
66
76
|
*
|
|
@@ -148,8 +158,9 @@ class Carno {
|
|
|
148
158
|
};
|
|
149
159
|
}
|
|
150
160
|
async init() {
|
|
161
|
+
(0, ValidationCache_1.setValidatorAdapter)(this.validatorAdapter);
|
|
151
162
|
this.loadProvidersAndControllers();
|
|
152
|
-
await this.injector.loadModule((0, createContainer_1.createContainer)(), this.config, this.router);
|
|
163
|
+
await this.injector.loadModule((0, createContainer_1.createContainer)(), this.config, this.router, this.validatorAdapter);
|
|
153
164
|
}
|
|
154
165
|
async listen(port = 3000) {
|
|
155
166
|
this.registerShutdownHandlers();
|
|
@@ -158,7 +169,7 @@ class Carno {
|
|
|
158
169
|
}
|
|
159
170
|
registerShutdownHandlers() {
|
|
160
171
|
const shutdown = async (signal) => {
|
|
161
|
-
|
|
172
|
+
this.resolveLogger().info(`Received ${signal}, starting graceful shutdown...`);
|
|
162
173
|
await this.handleShutdownHook();
|
|
163
174
|
};
|
|
164
175
|
node_process_1.default.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
@@ -172,7 +183,7 @@ class Carno {
|
|
|
172
183
|
}
|
|
173
184
|
createHttpServer(port) {
|
|
174
185
|
this.server = Bun.serve({ port, fetch: this.fetch, error: this.catcher });
|
|
175
|
-
|
|
186
|
+
this.resolveLogger().info(`Server running on port ${port}`);
|
|
176
187
|
}
|
|
177
188
|
async fetcher(request, server) {
|
|
178
189
|
if (this.isCorsEnabled()) {
|
|
@@ -193,8 +204,9 @@ class Carno {
|
|
|
193
204
|
let response;
|
|
194
205
|
const isCompiledRoute = compiled.routeType !== undefined;
|
|
195
206
|
if (isCompiledRoute && compiled.routeType === CompiledRoute_1.RouteType.SIMPLE) {
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
response = compiled.isAsync
|
|
208
|
+
? await compiled.boundHandler(context)
|
|
209
|
+
: compiled.boundHandler(context);
|
|
198
210
|
}
|
|
199
211
|
else {
|
|
200
212
|
const needsLocalsContainer = isCompiledRoute
|
|
@@ -208,7 +220,7 @@ class Carno {
|
|
|
208
220
|
}
|
|
209
221
|
if (this.isCorsEnabled()) {
|
|
210
222
|
const origin = request.headers.get("origin");
|
|
211
|
-
if (origin && this.isOriginAllowed(origin)) {
|
|
223
|
+
if (origin && this.corsCache.isOriginAllowed(origin)) {
|
|
212
224
|
response = this.applyCorsHeaders(response, origin);
|
|
213
225
|
}
|
|
214
226
|
}
|
|
@@ -235,42 +247,28 @@ class Carno {
|
|
|
235
247
|
}
|
|
236
248
|
closeHttpServer() {
|
|
237
249
|
if (this.server) {
|
|
238
|
-
|
|
250
|
+
this.resolveLogger().info("Closing HTTP server...");
|
|
239
251
|
this.server.stop(true);
|
|
240
252
|
}
|
|
241
253
|
}
|
|
242
254
|
exitProcess(code = 0) {
|
|
243
|
-
|
|
255
|
+
this.resolveLogger().info("Shutdown complete.");
|
|
244
256
|
node_process_1.default.exit(code);
|
|
245
257
|
}
|
|
246
258
|
reportHookFailure(event, error) {
|
|
247
|
-
|
|
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);
|
|
248
265
|
}
|
|
249
266
|
isCorsEnabled() {
|
|
250
267
|
return !!this.config.cors;
|
|
251
268
|
}
|
|
252
|
-
isOriginAllowed(origin) {
|
|
253
|
-
if (!origin || !this.config.cors) {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
const { origins } = this.config.cors;
|
|
257
|
-
if (typeof origins === "string") {
|
|
258
|
-
return origins === "*" || origins === origin;
|
|
259
|
-
}
|
|
260
|
-
if (Array.isArray(origins)) {
|
|
261
|
-
return origins.includes(origin);
|
|
262
|
-
}
|
|
263
|
-
if (origins instanceof RegExp) {
|
|
264
|
-
return origins.test(origin);
|
|
265
|
-
}
|
|
266
|
-
if (typeof origins === "function") {
|
|
267
|
-
return origins(origin);
|
|
268
|
-
}
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
269
|
handlePreflightRequest(request) {
|
|
272
270
|
const origin = request.headers.get("origin");
|
|
273
|
-
if (!this.isOriginAllowed(origin)) {
|
|
271
|
+
if (!origin || !this.corsCache.isOriginAllowed(origin)) {
|
|
274
272
|
return new Response(null, { status: 403 });
|
|
275
273
|
}
|
|
276
274
|
const corsHeaders = this.corsCache.get(origin);
|
|
@@ -19,3 +19,4 @@ __exportStar(require("./http.decorators"), exports);
|
|
|
19
19
|
__exportStar(require("./service.decorator"), exports);
|
|
20
20
|
__exportStar(require("./middleware.decorator"), exports);
|
|
21
21
|
__exportStar(require("./Injectable.decorator"), exports);
|
|
22
|
+
__exportStar(require("./validation.decorator"), exports);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Decorator to attach a Zod schema to a DTO class
|
|
4
|
+
* The schema will be used for validation when the DTO is used in route handlers
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* const CreateUserSchema = z.object({
|
|
9
|
+
* name: z.string().min(3),
|
|
10
|
+
* email: z.string().email()
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* @ZodSchema(CreateUserSchema)
|
|
14
|
+
* class CreateUserDto {
|
|
15
|
+
* name: string;
|
|
16
|
+
* email: string;
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* @Controller('/users')
|
|
20
|
+
* class UserController {
|
|
21
|
+
* @Post()
|
|
22
|
+
* create(@Body() dto: CreateUserDto) {
|
|
23
|
+
* // dto is validated and typed
|
|
24
|
+
* return dto;
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @param schema - The Zod schema to use for validation
|
|
30
|
+
* @returns Class decorator
|
|
31
|
+
*/
|
|
32
|
+
export declare function ZodSchema<T extends z.ZodType>(schema: T): ClassDecorator;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZodSchema = ZodSchema;
|
|
4
|
+
const Metadata_1 = require("../../domain/Metadata");
|
|
5
|
+
const constants_1 = require("../../constants");
|
|
6
|
+
/**
|
|
7
|
+
* Decorator to attach a Zod schema to a DTO class
|
|
8
|
+
* The schema will be used for validation when the DTO is used in route handlers
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const CreateUserSchema = z.object({
|
|
13
|
+
* name: z.string().min(3),
|
|
14
|
+
* email: z.string().email()
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* @ZodSchema(CreateUserSchema)
|
|
18
|
+
* class CreateUserDto {
|
|
19
|
+
* name: string;
|
|
20
|
+
* email: string;
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* @Controller('/users')
|
|
24
|
+
* class UserController {
|
|
25
|
+
* @Post()
|
|
26
|
+
* create(@Body() dto: CreateUserDto) {
|
|
27
|
+
* // dto is validated and typed
|
|
28
|
+
* return dto;
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @param schema - The Zod schema to use for validation
|
|
34
|
+
* @returns Class decorator
|
|
35
|
+
*/
|
|
36
|
+
function ZodSchema(schema) {
|
|
37
|
+
return (target) => {
|
|
38
|
+
Metadata_1.Metadata.set(constants_1.VALIDATION_ZOD_SCHEMA, schema, target);
|
|
39
|
+
};
|
|
40
|
+
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export declare const CONTROLLER_EVENTS = "carno:controller:events";
|
|
|
5
5
|
export declare const ROUTE_PARAM = "carno:route:param";
|
|
6
6
|
export declare const ROUTE_MIDDLEWARES = "carno:route:middlewares";
|
|
7
7
|
export declare const PROVIDER = "carno:provider";
|
|
8
|
+
export declare const VALIDATION_ZOD_SCHEMA = "carno:validation:zod";
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PROVIDER = exports.ROUTE_MIDDLEWARES = exports.ROUTE_PARAM = exports.CONTROLLER_EVENTS = exports.CONTROLLER_MIDDLEWARES = exports.CONTROLLER_ROUTES = exports.CONTROLLER = void 0;
|
|
3
|
+
exports.VALIDATION_ZOD_SCHEMA = exports.PROVIDER = exports.ROUTE_MIDDLEWARES = exports.ROUTE_PARAM = exports.CONTROLLER_EVENTS = exports.CONTROLLER_MIDDLEWARES = exports.CONTROLLER_ROUTES = exports.CONTROLLER = void 0;
|
|
4
4
|
exports.CONTROLLER = "carno:controller";
|
|
5
5
|
exports.CONTROLLER_ROUTES = "carno:controller:routes";
|
|
6
6
|
exports.CONTROLLER_MIDDLEWARES = "carno:controller:middlewares";
|
|
@@ -8,3 +8,4 @@ exports.CONTROLLER_EVENTS = "carno:controller:events";
|
|
|
8
8
|
exports.ROUTE_PARAM = "carno:route:param";
|
|
9
9
|
exports.ROUTE_MIDDLEWARES = "carno:route:middlewares";
|
|
10
10
|
exports.PROVIDER = "carno:provider";
|
|
11
|
+
exports.VALIDATION_ZOD_SCHEMA = "carno:validation:zod";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ApplicationConfig } from "../Carno";
|
|
2
2
|
import { TokenProvider } from "../commons/registries/ProviderControl";
|
|
3
|
+
import type { ValidatorAdapter } from "../validation/ValidatorAdapter";
|
|
3
4
|
import { Context } from "../domain/Context";
|
|
4
5
|
import { LocalsContainer } from "../domain/LocalsContainer";
|
|
5
6
|
import { Provider } from "../domain/provider";
|
|
@@ -20,7 +21,8 @@ export declare class InjectorService {
|
|
|
20
21
|
private routeResolver;
|
|
21
22
|
private dependencyResolver;
|
|
22
23
|
private methodInvoker;
|
|
23
|
-
|
|
24
|
+
private validatorAdapter;
|
|
25
|
+
loadModule(container: Container, applicationConfig: ApplicationConfig, router: Memoirist<any>, validatorAdapter: ValidatorAdapter): Promise<void>;
|
|
24
26
|
private initializeResolvers;
|
|
25
27
|
private ensureProvider;
|
|
26
28
|
get(token: TokenProvider): Provider | undefined;
|
|
@@ -40,10 +40,11 @@ let InjectorService = InjectorService_1 = class InjectorService {
|
|
|
40
40
|
this._hasOnResponseHook = false;
|
|
41
41
|
this.controllerScopes = new Map();
|
|
42
42
|
}
|
|
43
|
-
async loadModule(container, applicationConfig, router) {
|
|
43
|
+
async loadModule(container, applicationConfig, router, validatorAdapter) {
|
|
44
44
|
this.container = container;
|
|
45
45
|
this.router = router;
|
|
46
46
|
this.applicationConfig = applicationConfig;
|
|
47
|
+
this.validatorAdapter = validatorAdapter;
|
|
47
48
|
this.initializeResolvers();
|
|
48
49
|
this.removeUnknownProviders();
|
|
49
50
|
this.saveInjector();
|
|
@@ -57,7 +58,7 @@ let InjectorService = InjectorService_1 = class InjectorService {
|
|
|
57
58
|
initializeResolvers() {
|
|
58
59
|
this.routeResolver = new RouteResolver_1.RouteResolver(this.router, this.applicationConfig.globalMiddlewares);
|
|
59
60
|
this.dependencyResolver = new DependencyResolver_1.DependencyResolver(this.container);
|
|
60
|
-
this.methodInvoker = new MethodInvoker_1.MethodInvoker(this.applicationConfig);
|
|
61
|
+
this.methodInvoker = new MethodInvoker_1.MethodInvoker(this.applicationConfig, this.validatorAdapter);
|
|
61
62
|
}
|
|
62
63
|
ensureProvider(token) {
|
|
63
64
|
if (!this.container.has(token) && ProviderControl_1.GlobalProvider.has(token)) {
|
|
@@ -175,7 +176,7 @@ let InjectorService = InjectorService_1 = class InjectorService {
|
|
|
175
176
|
const compiler = new RouteCompiler_1.RouteCompiler({
|
|
176
177
|
container: this.container,
|
|
177
178
|
controllerScopes: this.controllerScopes,
|
|
178
|
-
|
|
179
|
+
validatorAdapter: this.validatorAdapter,
|
|
179
180
|
hasOnRequestHook: this._hasOnRequestHook,
|
|
180
181
|
hasOnResponseHook: this._hasOnResponseHook,
|
|
181
182
|
});
|
|
@@ -2,10 +2,12 @@ import { ApplicationConfig } from "../Carno";
|
|
|
2
2
|
import { TokenProvider } from "../commons/registries/ProviderControl";
|
|
3
3
|
import { Context } from "../domain/Context";
|
|
4
4
|
import { LocalsContainer } from "../domain/LocalsContainer";
|
|
5
|
+
import type { ValidatorAdapter } from "../validation/ValidatorAdapter";
|
|
5
6
|
export declare class MethodInvoker {
|
|
6
7
|
private applicationConfig;
|
|
8
|
+
private validatorAdapter;
|
|
7
9
|
private historyMethods;
|
|
8
|
-
constructor(applicationConfig: ApplicationConfig);
|
|
10
|
+
constructor(applicationConfig: ApplicationConfig, validatorAdapter: ValidatorAdapter);
|
|
9
11
|
invoke(instance: any, methodName: string, locals: LocalsContainer, context: Context, invokeCallback: (token: TokenProvider, locals: LocalsContainer) => any): Promise<any>;
|
|
10
12
|
private getMethodInfo;
|
|
11
13
|
private getCachedMethod;
|
|
@@ -16,5 +18,4 @@ export declare class MethodInvoker {
|
|
|
16
18
|
private hasBodyParam;
|
|
17
19
|
private isBodyParam;
|
|
18
20
|
private resolveService;
|
|
19
|
-
private validateAndTransform;
|
|
20
21
|
}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MethodInvoker = void 0;
|
|
4
|
-
const class_transformer_1 = require("class-transformer");
|
|
5
|
-
const class_validator_1 = require("class-validator");
|
|
6
4
|
const Metadata_1 = require("../domain/Metadata");
|
|
7
|
-
const HttpException_1 = require("../exceptions/HttpException");
|
|
8
5
|
const getClassOrSymbol_1 = require("../utils/getClassOrSymbol");
|
|
9
6
|
const getMethodArgTypes_1 = require("../utils/getMethodArgTypes");
|
|
10
|
-
const isClassValidator_1 = require("../utils/isClassValidator");
|
|
11
7
|
class MethodInvoker {
|
|
12
|
-
constructor(applicationConfig) {
|
|
8
|
+
constructor(applicationConfig, validatorAdapter) {
|
|
13
9
|
this.applicationConfig = applicationConfig;
|
|
10
|
+
this.validatorAdapter = validatorAdapter;
|
|
14
11
|
this.historyMethods = new WeakMap();
|
|
15
12
|
}
|
|
16
13
|
async invoke(instance, methodName, locals, context, invokeCallback) {
|
|
@@ -78,19 +75,9 @@ class MethodInvoker {
|
|
|
78
75
|
return invokeCallback((0, getClassOrSymbol_1.getClassOrSymbol)(token), locals);
|
|
79
76
|
}
|
|
80
77
|
const value = param.fun(context, param.param);
|
|
81
|
-
return
|
|
82
|
-
? this.validateAndTransform(token, value)
|
|
78
|
+
return this.validatorAdapter.hasValidation(token)
|
|
79
|
+
? this.validatorAdapter.validateAndTransform(token, value)
|
|
83
80
|
: value;
|
|
84
81
|
}
|
|
85
|
-
validateAndTransform(token, value) {
|
|
86
|
-
const obj = (0, class_transformer_1.plainToInstance)(token, value);
|
|
87
|
-
const errors = (0, class_validator_1.validateSync)(obj, this.applicationConfig.validation);
|
|
88
|
-
// todo: deve retornar apenas os erros e no o objeto class-validator intei
|
|
89
|
-
// ro.
|
|
90
|
-
if (errors.length > 0) {
|
|
91
|
-
throw new HttpException_1.HttpException(errors, 400);
|
|
92
|
-
}
|
|
93
|
-
return obj;
|
|
94
|
-
}
|
|
95
82
|
}
|
|
96
83
|
exports.MethodInvoker = MethodInvoker;
|
|
@@ -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
|
}
|