@carno.js/core 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/Carno.d.ts +9 -7
  2. package/dist/Carno.js +14 -1
  3. package/dist/commons/decorators/index.d.ts +1 -0
  4. package/dist/commons/decorators/index.js +1 -0
  5. package/dist/commons/decorators/validation.decorator.d.ts +32 -0
  6. package/dist/commons/decorators/validation.decorator.js +40 -0
  7. package/dist/constants.d.ts +1 -0
  8. package/dist/constants.js +2 -1
  9. package/dist/container/InjectorService.d.ts +3 -1
  10. package/dist/container/InjectorService.js +4 -3
  11. package/dist/container/MethodInvoker.d.ts +3 -2
  12. package/dist/container/MethodInvoker.js +4 -17
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/route/ParamResolverFactory.d.ts +0 -5
  16. package/dist/route/ParamResolverFactory.js +0 -40
  17. package/dist/route/RouteCompiler.d.ts +3 -3
  18. package/dist/route/RouteCompiler.js +2 -11
  19. package/dist/utils/ValidationCache.d.ts +2 -0
  20. package/dist/utils/ValidationCache.js +10 -2
  21. package/dist/utils/index.d.ts +0 -1
  22. package/dist/utils/index.js +0 -1
  23. package/dist/validation/ValidatorAdapter.d.ts +66 -0
  24. package/dist/validation/ValidatorAdapter.js +20 -0
  25. package/dist/validation/adapters/ClassValidatorAdapter.d.ts +23 -0
  26. package/dist/validation/adapters/ClassValidatorAdapter.js +47 -0
  27. package/dist/validation/adapters/ZodAdapter.d.ts +14 -0
  28. package/dist/validation/adapters/ZodAdapter.js +56 -0
  29. package/dist/validation/adapters/index.d.ts +4 -0
  30. package/dist/validation/adapters/index.js +7 -0
  31. package/dist/validation/index.d.ts +3 -0
  32. package/dist/validation/index.js +20 -0
  33. package/package.json +17 -6
  34. package/dist/Cheetah.d.ts +0 -65
  35. package/dist/Cheetah.js +0 -307
  36. package/dist/default-routes-cheetah.d.ts +0 -3
  37. package/dist/default-routes-cheetah.js +0 -29
  38. package/dist/domain/CheetahClosure.d.ts +0 -1
  39. package/dist/domain/CheetahClosure.js +0 -2
  40. package/dist/domain/CheetahMiddleware.d.ts +0 -5
  41. package/dist/domain/CheetahMiddleware.js +0 -2
  42. package/dist/services/request-logger.service.d.ts +0 -15
  43. package/dist/services/request-logger.service.js +0 -50
  44. package/dist/utils/isClassValidator.d.ts +0 -6
  45. package/dist/utils/isClassValidator.js +0 -13
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?: ValidatorOptions;
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(".").InjectorService;
54
+ getInjector(): import("./container").InjectorService;
53
55
  private createHttpServer;
54
56
  private fetcher;
55
57
  private catcher;
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");
@@ -56,11 +58,21 @@ class Carno {
56
58
  console.error("Unhandled error:", error);
57
59
  return new Response("Internal Server Error", { status: 500 });
58
60
  };
61
+ this.validatorAdapter = this.resolveValidatorAdapter();
59
62
  if (config.cors) {
60
63
  this.corsCache = new cors_headers_cache_1.CorsHeadersCache(config.cors);
61
64
  }
62
65
  void this.bootstrapApplication();
63
66
  }
67
+ resolveValidatorAdapter() {
68
+ const config = this.config.validation;
69
+ if (!config?.adapter) {
70
+ return new ZodAdapter_1.ZodAdapter();
71
+ }
72
+ const AdapterClass = config.adapter;
73
+ const options = config.options || {};
74
+ return new AdapterClass(options);
75
+ }
64
76
  /**
65
77
  * Use the Carno plugin.
66
78
  *
@@ -148,8 +160,9 @@ class Carno {
148
160
  };
149
161
  }
150
162
  async init() {
163
+ (0, ValidationCache_1.setValidatorAdapter)(this.validatorAdapter);
151
164
  this.loadProvidersAndControllers();
152
- await this.injector.loadModule((0, createContainer_1.createContainer)(), this.config, this.router);
165
+ await this.injector.loadModule((0, createContainer_1.createContainer)(), this.config, this.router, this.validatorAdapter);
153
166
  }
154
167
  async listen(port = 3000) {
155
168
  this.registerShutdownHandlers();
@@ -3,3 +3,4 @@ export * from './http.decorators';
3
3
  export * from './service.decorator';
4
4
  export * from './middleware.decorator';
5
5
  export * from './Injectable.decorator';
6
+ export * from './validation.decorator';
@@ -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
+ }
@@ -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
- loadModule(container: Container, applicationConfig: ApplicationConfig, router: Memoirist<any>): Promise<void>;
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
- validationConfig: this.applicationConfig.validation,
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 (0, isClassValidator_1.isClassValidator)(token)
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;
package/dist/index.d.ts CHANGED
@@ -13,3 +13,4 @@ export * from "./services/logger.service";
13
13
  export * from "./cache/cache.service";
14
14
  export * from "./cache/bento-cache.driver";
15
15
  export * from "./testing";
16
+ export * from "./validation";
package/dist/index.js CHANGED
@@ -29,3 +29,4 @@ __exportStar(require("./services/logger.service"), exports);
29
29
  __exportStar(require("./cache/cache.service"), exports);
30
30
  __exportStar(require("./cache/bento-cache.driver"), exports);
31
31
  __exportStar(require("./testing"), exports);
32
+ __exportStar(require("./validation"), exports);
@@ -1,6 +1,4 @@
1
- import { type ValidatorOptions } from 'class-validator';
2
1
  import type { Context } from '../domain/Context';
3
- import type { ParamResolver, AsyncParamResolver } from './CompiledRoute';
4
2
  export type ParamDecoratorType = 'body' | 'query' | 'param' | 'headers' | 'req' | 'locals';
5
3
  export interface ParamDecoratorMeta {
6
4
  fun: (context: Context, data?: any) => any;
@@ -14,6 +12,3 @@ export interface ParamInfo {
14
12
  token?: any;
15
13
  }
16
14
  export declare function analyzeParamDecorator(decoratorMeta: ParamDecoratorMeta | undefined, token: any): ParamInfo;
17
- export declare function createParamResolver(decoratorMeta: ParamDecoratorMeta | undefined, token: any, validationConfig?: ValidatorOptions): ParamResolver | AsyncParamResolver | null;
18
- export declare function createParamResolvers(paramMetas: Record<number, ParamDecoratorMeta> | undefined, argTypes: any[], validationConfig?: ValidatorOptions): (ParamResolver | AsyncParamResolver | null)[];
19
- export declare function hasAnyDIParam(resolvers: (ParamResolver | AsyncParamResolver | null)[]): boolean;
@@ -1,12 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.analyzeParamDecorator = analyzeParamDecorator;
4
- exports.createParamResolver = createParamResolver;
5
- exports.createParamResolvers = createParamResolvers;
6
- exports.hasAnyDIParam = hasAnyDIParam;
7
- const class_transformer_1 = require("class-transformer");
8
- const class_validator_1 = require("class-validator");
9
- const HttpException_1 = require("../exceptions/HttpException");
10
4
  const ValidationCache_1 = require("../utils/ValidationCache");
11
5
  function analyzeParamDecorator(decoratorMeta, token) {
12
6
  if (!decoratorMeta) {
@@ -53,37 +47,3 @@ function inferTypeFromSource(resolver) {
53
47
  }
54
48
  return null;
55
49
  }
56
- function createValidationResolver(extractFn, token, validationConfig) {
57
- return (context) => {
58
- const value = extractFn(context);
59
- const obj = (0, class_transformer_1.plainToInstance)(token, value);
60
- const errors = (0, class_validator_1.validateSync)(obj, validationConfig);
61
- if (errors.length > 0) {
62
- throw new HttpException_1.HttpException(errors, 400);
63
- }
64
- return obj;
65
- };
66
- }
67
- function createParamResolver(decoratorMeta, token, validationConfig) {
68
- if (!decoratorMeta) {
69
- return null;
70
- }
71
- const extractFn = (context) => decoratorMeta.fun(context, decoratorMeta.param);
72
- const needsValidation = typeof token === 'function' && (0, ValidationCache_1.isValidatable)(token);
73
- if (needsValidation) {
74
- return createValidationResolver(extractFn, token, validationConfig);
75
- }
76
- return extractFn;
77
- }
78
- function createParamResolvers(paramMetas, argTypes, validationConfig) {
79
- const resolvers = [];
80
- for (let i = 0; i < argTypes.length; i++) {
81
- const meta = paramMetas?.[i];
82
- const token = argTypes[i];
83
- resolvers.push(createParamResolver(meta, token, validationConfig));
84
- }
85
- return resolvers;
86
- }
87
- function hasAnyDIParam(resolvers) {
88
- return resolvers.some((r) => r === null);
89
- }
@@ -1,19 +1,19 @@
1
- import { type ValidatorOptions } from 'class-validator';
2
1
  import type { TokenRouteWithProvider } from '../container/ContainerConfiguration';
2
+ import type { ValidatorAdapter } from '../validation/ValidatorAdapter';
3
3
  import type { Container } from '../container/container';
4
4
  import { ProviderScope } from '../domain/provider-scope';
5
5
  import { type CompiledRoute } from './CompiledRoute';
6
6
  export interface RouteCompilerOptions {
7
7
  container: Container;
8
8
  controllerScopes: Map<any, ProviderScope>;
9
- validationConfig?: ValidatorOptions;
9
+ validatorAdapter: ValidatorAdapter;
10
10
  hasOnRequestHook: boolean;
11
11
  hasOnResponseHook: boolean;
12
12
  }
13
13
  export declare class RouteCompiler {
14
14
  private container;
15
15
  private controllerScopes;
16
- private validationConfig?;
16
+ private validatorAdapter;
17
17
  private hasOnRequestHook;
18
18
  private hasOnResponseHook;
19
19
  constructor(options: RouteCompilerOptions);
@@ -1,11 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RouteCompiler = void 0;
4
- const class_transformer_1 = require("class-transformer");
5
- const class_validator_1 = require("class-validator");
6
4
  const provider_scope_1 = require("../domain/provider-scope");
7
5
  const Metadata_1 = require("../domain/Metadata");
8
- const HttpException_1 = require("../exceptions/HttpException");
9
6
  const getMethodArgTypes_1 = require("../utils/getMethodArgTypes");
10
7
  const CompiledRoute_1 = require("./CompiledRoute");
11
8
  const ParamResolverFactory_1 = require("./ParamResolverFactory");
@@ -14,7 +11,7 @@ class RouteCompiler {
14
11
  constructor(options) {
15
12
  this.container = options.container;
16
13
  this.controllerScopes = options.controllerScopes;
17
- this.validationConfig = options.validationConfig;
14
+ this.validatorAdapter = options.validatorAdapter;
18
15
  this.hasOnRequestHook = options.hasOnRequestHook;
19
16
  this.hasOnResponseHook = options.hasOnResponseHook;
20
17
  }
@@ -158,7 +155,6 @@ class RouteCompiler {
158
155
  }
159
156
  createValidatedHandler(instance, methodName, paramInfos, hasBodyParam) {
160
157
  const handler = instance[methodName].bind(instance);
161
- const config = this.validationConfig;
162
158
  const resolveArg = (ctx, param) => {
163
159
  let value;
164
160
  switch (param.type) {
@@ -184,12 +180,7 @@ class RouteCompiler {
184
180
  value = undefined;
185
181
  }
186
182
  if (param.needsValidation && param.token) {
187
- const obj = (0, class_transformer_1.plainToInstance)(param.token, value);
188
- const errors = (0, class_validator_1.validateSync)(obj, config);
189
- if (errors.length > 0) {
190
- throw new HttpException_1.HttpException(errors, 400);
191
- }
192
- return obj;
183
+ return this.validatorAdapter.validateAndTransform(param.token, value);
193
184
  }
194
185
  return value;
195
186
  };
@@ -1,3 +1,5 @@
1
+ import type { ValidatorAdapter } from '../validation/ValidatorAdapter';
2
+ export declare function setValidatorAdapter(adapter: ValidatorAdapter): void;
1
3
  export declare function isValidatable(token: Function): boolean;
2
4
  export declare function preloadValidationForParams(args: any[]): number[];
3
5
  export declare function clearValidationCache(): void;
@@ -1,14 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setValidatorAdapter = setValidatorAdapter;
3
4
  exports.isValidatable = isValidatable;
4
5
  exports.preloadValidationForParams = preloadValidationForParams;
5
6
  exports.clearValidationCache = clearValidationCache;
6
- const isClassValidator_1 = require("./isClassValidator");
7
+ let currentAdapter = null;
7
8
  const cache = new Map();
9
+ function setValidatorAdapter(adapter) {
10
+ currentAdapter = adapter;
11
+ cache.clear();
12
+ }
8
13
  function isValidatable(token) {
14
+ if (!currentAdapter) {
15
+ throw new Error('Validator adapter not initialized. Call setValidatorAdapter() first.');
16
+ }
9
17
  let result = cache.get(token);
10
18
  if (result === undefined) {
11
- result = (0, isClassValidator_1.isClassValidator)(token);
19
+ result = currentAdapter.hasValidation(token);
12
20
  cache.set(token, result);
13
21
  }
14
22
  return result;
@@ -4,7 +4,6 @@ export * from './isString';
4
4
  export * from './isFunction';
5
5
  export * from './getConstructorArgNames';
6
6
  export * from './getValue';
7
- export * from './isClassValidator';
8
7
  export * from './getMethodArgTypes';
9
8
  export * from './getClassOrSymbol';
10
9
  export * from './isRequestScope';
@@ -20,7 +20,6 @@ __exportStar(require("./isString"), exports);
20
20
  __exportStar(require("./isFunction"), exports);
21
21
  __exportStar(require("./getConstructorArgNames"), exports);
22
22
  __exportStar(require("./getValue"), exports);
23
- __exportStar(require("./isClassValidator"), exports);
24
23
  __exportStar(require("./getMethodArgTypes"), exports);
25
24
  __exportStar(require("./getClassOrSymbol"), exports);
26
25
  __exportStar(require("./isRequestScope"), exports);
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Base interface for validation adapters
3
+ * Adapters provide validation capabilities for different libraries (Zod, class-validator, etc.)
4
+ */
5
+ export interface ValidatorAdapter<TOptions = any> {
6
+ /**
7
+ * Checks if a class/function has validation metadata
8
+ * Used by ValidationCache for detection and optimization
9
+ *
10
+ * @param target - The class/function to check
11
+ * @returns true if validation metadata exists
12
+ */
13
+ hasValidation(target: Function): boolean;
14
+ /**
15
+ * Validates and transforms a value to the target type
16
+ *
17
+ * @param target - The DTO class to validate against
18
+ * @param value - The raw value to validate (usually from request body/query/params)
19
+ * @returns Transformed and validated instance
20
+ * @throws HttpException with formatted errors on validation failure
21
+ */
22
+ validateAndTransform(target: Function, value: any): any;
23
+ /**
24
+ * Get the name of the validator (for debugging/logging)
25
+ *
26
+ * @returns The validator name (e.g., "ZodAdapter", "ClassValidatorAdapter")
27
+ */
28
+ getName(): string;
29
+ /**
30
+ * Get the adapter options (for internal use)
31
+ */
32
+ getOptions(): TOptions;
33
+ }
34
+ /**
35
+ * Constructor type for validator adapters
36
+ */
37
+ export type ValidatorAdapterConstructor<TOptions = any> = new (options?: TOptions) => ValidatorAdapter<TOptions>;
38
+ /**
39
+ * Type to extract options type from a ValidatorAdapter constructor
40
+ */
41
+ export type ValidatorAdapterOptions<TAdapter> = TAdapter extends new (options?: infer TOptions) => any ? TOptions : never;
42
+ /**
43
+ * Validation configuration with adapter and options
44
+ */
45
+ export interface ValidationConfig<TAdapter extends ValidatorAdapterConstructor = ValidatorAdapterConstructor> {
46
+ adapter?: TAdapter;
47
+ options?: ValidatorAdapterOptions<TAdapter>;
48
+ }
49
+ /**
50
+ * Helper function to create a strongly-typed validation config.
51
+ * Use this to get proper autocomplete for adapter options.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const app = new Carno({
56
+ * validation: defineValidation({
57
+ * adapter: ClassValidatorAdapter,
58
+ * options: { whitelist: true } // ✓ Autocomplete works!
59
+ * })
60
+ * });
61
+ * ```
62
+ */
63
+ export declare function defineValidation<TAdapter extends ValidatorAdapterConstructor>(config: {
64
+ adapter: TAdapter;
65
+ options?: ValidatorAdapterOptions<TAdapter>;
66
+ }): ValidationConfig<TAdapter>;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineValidation = defineValidation;
4
+ /**
5
+ * Helper function to create a strongly-typed validation config.
6
+ * Use this to get proper autocomplete for adapter options.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const app = new Carno({
11
+ * validation: defineValidation({
12
+ * adapter: ClassValidatorAdapter,
13
+ * options: { whitelist: true } // ✓ Autocomplete works!
14
+ * })
15
+ * });
16
+ * ```
17
+ */
18
+ function defineValidation(config) {
19
+ return config;
20
+ }
@@ -0,0 +1,23 @@
1
+ import type { ValidatorAdapter } from "../ValidatorAdapter";
2
+ export interface ClassValidatorAdapterOptions {
3
+ skipMissingProperties?: boolean;
4
+ whitelist?: boolean;
5
+ forbidNonWhitelisted?: boolean;
6
+ forbidUnknownValues?: boolean;
7
+ disableErrorMessages?: boolean;
8
+ errorHttpStatusCode?: number;
9
+ dismissDefaultMessages?: boolean;
10
+ validationError?: {
11
+ target?: boolean;
12
+ value?: boolean;
13
+ };
14
+ stopAtFirstError?: boolean;
15
+ }
16
+ export declare class ClassValidatorAdapter implements ValidatorAdapter<ClassValidatorAdapterOptions> {
17
+ private options;
18
+ constructor(options?: ClassValidatorAdapterOptions);
19
+ getName(): string;
20
+ getOptions(): ClassValidatorAdapterOptions;
21
+ hasValidation(target: Function): boolean;
22
+ validateAndTransform(target: Function, value: any): any;
23
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClassValidatorAdapter = void 0;
4
+ const HttpException_1 = require("../../exceptions/HttpException");
5
+ const formatValidationErrors_1 = require("../../utils/formatValidationErrors");
6
+ let classValidator;
7
+ let classTransformer;
8
+ try {
9
+ classValidator = require("class-validator");
10
+ classTransformer = require("class-transformer");
11
+ }
12
+ catch (error) {
13
+ // Will be checked in constructor
14
+ }
15
+ class ClassValidatorAdapter {
16
+ constructor(options = {}) {
17
+ this.options = options;
18
+ if (!classValidator || !classTransformer) {
19
+ throw new Error("ClassValidatorAdapter requires class-validator and class-transformer. " +
20
+ "Install with: npm install class-validator class-transformer");
21
+ }
22
+ }
23
+ getName() {
24
+ return "ClassValidatorAdapter";
25
+ }
26
+ getOptions() {
27
+ return this.options;
28
+ }
29
+ hasValidation(target) {
30
+ const { getMetadataStorage } = classValidator;
31
+ const metadataStorage = getMetadataStorage();
32
+ const validationMetadata = metadataStorage.getTargetValidationMetadatas(target, "", false, false, []);
33
+ return validationMetadata.length > 0;
34
+ }
35
+ validateAndTransform(target, value) {
36
+ const { validateSync } = classValidator;
37
+ const { plainToInstance } = classTransformer;
38
+ const instance = plainToInstance(target, value);
39
+ const errors = validateSync(instance, this.options);
40
+ if (errors.length > 0) {
41
+ const formatted = (0, formatValidationErrors_1.formatValidationErrors)(errors);
42
+ throw new HttpException_1.HttpException(formatted, 400);
43
+ }
44
+ return instance;
45
+ }
46
+ }
47
+ exports.ClassValidatorAdapter = ClassValidatorAdapter;