@carno.js/core 0.2.11 → 1.0.2
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/LICENSE +673 -673
- package/README.md +188 -0
- package/dist/Carno.js +272 -333
- package/dist/Carno.mjs +268 -0
- package/dist/DefaultRoutes.js +51 -0
- package/dist/DefaultRoutes.mjs +34 -0
- package/dist/bun/index.js +183 -0
- package/dist/bun/index.js.map +86 -0
- package/dist/cache/CacheDriver.js +13 -0
- package/dist/cache/CacheDriver.mjs +0 -0
- package/dist/cache/CacheService.js +113 -0
- package/dist/cache/CacheService.mjs +93 -0
- package/dist/cache/MemoryDriver.js +66 -0
- package/dist/cache/MemoryDriver.mjs +46 -0
- package/dist/cache/RedisDriver.js +81 -0
- package/dist/cache/RedisDriver.mjs +61 -0
- package/dist/compiler/JITCompiler.js +111 -0
- package/dist/compiler/JITCompiler.mjs +89 -0
- package/dist/container/Container.js +99 -0
- package/dist/container/Container.mjs +78 -0
- package/dist/context/Context.js +97 -0
- package/dist/context/Context.mjs +77 -0
- package/dist/cors/CorsHandler.js +90 -0
- package/dist/cors/CorsHandler.mjs +68 -0
- package/dist/decorators/Controller.js +42 -0
- package/dist/decorators/Controller.mjs +22 -0
- package/dist/decorators/Inject.js +30 -0
- package/dist/decorators/Inject.mjs +10 -0
- package/dist/decorators/Middleware.js +35 -0
- package/dist/decorators/Middleware.mjs +15 -0
- package/dist/decorators/Service.js +31 -0
- package/dist/decorators/Service.mjs +12 -0
- package/dist/decorators/methods.js +60 -0
- package/dist/decorators/methods.mjs +34 -0
- package/dist/decorators/params.js +63 -0
- package/dist/decorators/params.mjs +37 -0
- package/dist/events/Lifecycle.js +69 -0
- package/dist/events/Lifecycle.mjs +41 -0
- package/dist/exceptions/HttpException.js +112 -26
- package/dist/exceptions/HttpException.mjs +82 -0
- package/dist/index.js +129 -31
- package/dist/index.mjs +94 -0
- package/dist/metadata.js +34 -0
- package/dist/metadata.mjs +9 -0
- package/dist/middleware/CarnoMiddleware.js +13 -0
- package/dist/middleware/CarnoMiddleware.mjs +0 -0
- package/dist/router/RadixRouter.js +121 -0
- package/dist/router/RadixRouter.mjs +101 -0
- package/dist/testing/TestHarness.js +81 -0
- package/dist/testing/TestHarness.mjs +60 -0
- package/dist/utils/Metadata.js +53 -0
- package/dist/utils/Metadata.mjs +31 -0
- package/dist/validation/ValibotAdapter.js +67 -0
- package/dist/validation/ValibotAdapter.mjs +48 -0
- package/dist/validation/ValidatorAdapter.js +35 -19
- package/dist/validation/ValidatorAdapter.mjs +14 -0
- package/dist/validation/ZodAdapter.js +80 -0
- package/dist/validation/ZodAdapter.mjs +59 -0
- package/package.json +21 -63
- package/src/Carno.ts +605 -0
- package/src/DefaultRoutes.ts +34 -0
- package/src/cache/CacheDriver.ts +50 -0
- package/src/cache/CacheService.ts +139 -0
- package/src/cache/MemoryDriver.ts +104 -0
- package/src/cache/RedisDriver.ts +116 -0
- package/src/compiler/JITCompiler.ts +167 -0
- package/src/container/Container.ts +168 -0
- package/src/context/Context.ts +128 -0
- package/src/cors/CorsHandler.ts +145 -0
- package/src/decorators/Controller.ts +63 -0
- package/src/decorators/Inject.ts +16 -0
- package/src/decorators/Middleware.ts +22 -0
- package/src/decorators/Service.ts +18 -0
- package/src/decorators/methods.ts +58 -0
- package/src/decorators/params.ts +47 -0
- package/src/events/Lifecycle.ts +97 -0
- package/src/exceptions/HttpException.ts +99 -0
- package/src/index.ts +92 -0
- package/src/metadata.ts +46 -0
- package/src/middleware/CarnoMiddleware.ts +14 -0
- package/src/router/RadixRouter.ts +225 -0
- package/src/testing/TestHarness.ts +177 -0
- package/src/utils/Metadata.ts +43 -0
- package/src/validation/ValibotAdapter.ts +95 -0
- package/src/validation/ValidatorAdapter.ts +69 -0
- package/src/validation/ZodAdapter.ts +102 -0
- package/dist/Carno.d.ts +0 -75
- package/dist/cache/bento-cache.driver.d.ts +0 -13
- package/dist/cache/bento-cache.driver.js +0 -55
- package/dist/cache/cache.service.d.ts +0 -8
- package/dist/cache/cache.service.js +0 -6
- package/dist/commons/decorators/Injectable.decorator.d.ts +0 -20
- package/dist/commons/decorators/Injectable.decorator.js +0 -33
- package/dist/commons/decorators/controller.decorator.d.ts +0 -8
- package/dist/commons/decorators/controller.decorator.js +0 -22
- package/dist/commons/decorators/http.decorators.d.ts +0 -13
- package/dist/commons/decorators/http.decorators.js +0 -63
- package/dist/commons/decorators/index.d.ts +0 -6
- package/dist/commons/decorators/index.js +0 -22
- package/dist/commons/decorators/inject.decorator.d.ts +0 -1
- package/dist/commons/decorators/inject.decorator.js +0 -5
- package/dist/commons/decorators/middleware.decorator.d.ts +0 -2
- package/dist/commons/decorators/middleware.decorator.js +0 -30
- package/dist/commons/decorators/service.decorator.d.ts +0 -2
- package/dist/commons/decorators/service.decorator.js +0 -7
- package/dist/commons/decorators/validation.decorator.d.ts +0 -32
- package/dist/commons/decorators/validation.decorator.js +0 -40
- package/dist/commons/http-code.enum.d.ts +0 -50
- package/dist/commons/http-code.enum.js +0 -54
- package/dist/commons/index.d.ts +0 -3
- package/dist/commons/index.js +0 -19
- package/dist/commons/registries/ProviderControl.d.ts +0 -77
- package/dist/commons/registries/ProviderControl.js +0 -112
- package/dist/commons/registries/ProviderRegistry.d.ts +0 -7
- package/dist/commons/registries/ProviderRegistry.js +0 -20
- package/dist/constants.d.ts +0 -8
- package/dist/constants.js +0 -11
- package/dist/container/ContainerConfiguration.d.ts +0 -45
- package/dist/container/ContainerConfiguration.js +0 -121
- package/dist/container/DependencyResolver.d.ts +0 -20
- package/dist/container/DependencyResolver.js +0 -85
- package/dist/container/InjectorService.d.ts +0 -58
- package/dist/container/InjectorService.js +0 -286
- package/dist/container/MethodInvoker.d.ts +0 -21
- package/dist/container/MethodInvoker.js +0 -83
- package/dist/container/RouteResolver.d.ts +0 -27
- package/dist/container/RouteResolver.js +0 -173
- package/dist/container/container.d.ts +0 -41
- package/dist/container/container.js +0 -71
- package/dist/container/createContainer.d.ts +0 -3
- package/dist/container/createContainer.js +0 -12
- package/dist/container/createInjector.d.ts +0 -2
- package/dist/container/createInjector.js +0 -7
- package/dist/container/index.d.ts +0 -6
- package/dist/container/index.js +0 -22
- package/dist/container/middleware.resolver.d.ts +0 -9
- package/dist/container/middleware.resolver.js +0 -35
- package/dist/default-routes-carno.d.ts +0 -3
- package/dist/default-routes-carno.js +0 -29
- package/dist/domain/BaseContext.d.ts +0 -15
- package/dist/domain/BaseContext.js +0 -2
- package/dist/domain/CarnoClosure.d.ts +0 -1
- package/dist/domain/CarnoClosure.js +0 -2
- package/dist/domain/CarnoMiddleware.d.ts +0 -5
- package/dist/domain/CarnoMiddleware.js +0 -2
- package/dist/domain/Context.d.ts +0 -58
- package/dist/domain/Context.js +0 -188
- package/dist/domain/FastContext.d.ts +0 -34
- package/dist/domain/FastContext.js +0 -59
- package/dist/domain/LocalsContainer.d.ts +0 -4
- package/dist/domain/LocalsContainer.js +0 -10
- package/dist/domain/Metadata.d.ts +0 -449
- package/dist/domain/Metadata.js +0 -511
- package/dist/domain/cors-config.d.ts +0 -12
- package/dist/domain/cors-config.js +0 -18
- package/dist/domain/cors-headers-cache.d.ts +0 -17
- package/dist/domain/cors-headers-cache.js +0 -101
- package/dist/domain/http-method.d.ts +0 -7
- package/dist/domain/http-method.js +0 -11
- package/dist/domain/index.d.ts +0 -10
- package/dist/domain/index.js +0 -26
- package/dist/domain/provider-scope.d.ts +0 -5
- package/dist/domain/provider-scope.js +0 -9
- package/dist/domain/provider-type.d.ts +0 -6
- package/dist/domain/provider-type.js +0 -10
- package/dist/domain/provider.d.ts +0 -37
- package/dist/domain/provider.js +0 -70
- package/dist/events/hooks.decorator.d.ts +0 -3
- package/dist/events/hooks.decorator.js +0 -29
- package/dist/events/index.d.ts +0 -2
- package/dist/events/index.js +0 -18
- package/dist/events/on-event.d.ts +0 -13
- package/dist/events/on-event.js +0 -11
- package/dist/exceptions/HttpException.d.ts +0 -9
- package/dist/exceptions/index.d.ts +0 -1
- package/dist/exceptions/index.js +0 -17
- package/dist/index.d.ts +0 -16
- package/dist/route/CompiledRoute.d.ts +0 -23
- package/dist/route/CompiledRoute.js +0 -9
- package/dist/route/FastPathExecutor.d.ts +0 -12
- package/dist/route/FastPathExecutor.js +0 -50
- package/dist/route/JITCompiler.d.ts +0 -28
- package/dist/route/JITCompiler.js +0 -245
- package/dist/route/Matcher.d.ts +0 -11
- package/dist/route/Matcher.js +0 -48
- package/dist/route/ParamResolverFactory.d.ts +0 -14
- package/dist/route/ParamResolverFactory.js +0 -49
- package/dist/route/RouteCompiler.d.ts +0 -28
- package/dist/route/RouteCompiler.js +0 -157
- package/dist/route/RouteExecutor.d.ts +0 -12
- package/dist/route/RouteExecutor.js +0 -84
- package/dist/route/memoirist.d.ts +0 -31
- package/dist/route/memoirist.js +0 -373
- package/dist/services/logger.service.d.ts +0 -23
- package/dist/services/logger.service.js +0 -47
- package/dist/testing/core-testing.d.ts +0 -24
- package/dist/testing/core-testing.js +0 -102
- package/dist/testing/index.d.ts +0 -1
- package/dist/testing/index.js +0 -17
- package/dist/utils/ValidationCache.d.ts +0 -5
- package/dist/utils/ValidationCache.js +0 -35
- package/dist/utils/ancestorOf.d.ts +0 -2
- package/dist/utils/ancestorOf.js +0 -10
- package/dist/utils/ancestorsOf.d.ts +0 -6
- package/dist/utils/ancestorsOf.js +0 -20
- package/dist/utils/classOf.d.ts +0 -13
- package/dist/utils/classOf.js +0 -21
- package/dist/utils/cleanObject.d.ts +0 -6
- package/dist/utils/cleanObject.js +0 -22
- package/dist/utils/constructorOf.d.ts +0 -11
- package/dist/utils/constructorOf.js +0 -18
- package/dist/utils/createInstance.d.ts +0 -1
- package/dist/utils/createInstance.js +0 -7
- package/dist/utils/decoratorTypeOf.d.ts +0 -11
- package/dist/utils/decoratorTypeOf.js +0 -32
- package/dist/utils/deepClone.d.ts +0 -6
- package/dist/utils/deepClone.js +0 -63
- package/dist/utils/deepMerge.d.ts +0 -9
- package/dist/utils/deepMerge.js +0 -62
- package/dist/utils/descriptorOf.d.ts +0 -8
- package/dist/utils/descriptorOf.js +0 -16
- package/dist/utils/formatValidationErrors.d.ts +0 -5
- package/dist/utils/formatValidationErrors.js +0 -80
- package/dist/utils/getClassOrSymbol.d.ts +0 -1
- package/dist/utils/getClassOrSymbol.js +0 -8
- package/dist/utils/getConstructorArgNames.d.ts +0 -1
- package/dist/utils/getConstructorArgNames.js +0 -12
- package/dist/utils/getMethodArgTypes.d.ts +0 -1
- package/dist/utils/getMethodArgTypes.js +0 -9
- package/dist/utils/getValue.d.ts +0 -32
- package/dist/utils/getValue.js +0 -47
- package/dist/utils/hasJsonMethod.d.ts +0 -1
- package/dist/utils/hasJsonMethod.js +0 -6
- package/dist/utils/index.d.ts +0 -15
- package/dist/utils/index.js +0 -31
- package/dist/utils/isArray.d.ts +0 -13
- package/dist/utils/isArray.js +0 -21
- package/dist/utils/isArrowFn.d.ts +0 -1
- package/dist/utils/isArrowFn.js +0 -7
- package/dist/utils/isBoolean.d.ts +0 -7
- package/dist/utils/isBoolean.js +0 -15
- package/dist/utils/isBuffer.d.ts +0 -7
- package/dist/utils/isBuffer.js +0 -19
- package/dist/utils/isClass.d.ts +0 -1
- package/dist/utils/isClass.js +0 -26
- package/dist/utils/isCollection.d.ts +0 -6
- package/dist/utils/isCollection.js +0 -20
- package/dist/utils/isDate.d.ts +0 -6
- package/dist/utils/isDate.js +0 -11
- package/dist/utils/isEmpty.d.ts +0 -6
- package/dist/utils/isEmpty.js +0 -12
- package/dist/utils/isFunction.d.ts +0 -1
- package/dist/utils/isFunction.js +0 -6
- package/dist/utils/isInheritedFrom.d.ts +0 -1
- package/dist/utils/isInheritedFrom.js +0 -24
- package/dist/utils/isMomentObject.d.ts +0 -1
- package/dist/utils/isMomentObject.js +0 -6
- package/dist/utils/isMongooseObject.d.ts +0 -2
- package/dist/utils/isMongooseObject.js +0 -11
- package/dist/utils/isNil.d.ts +0 -1
- package/dist/utils/isNil.js +0 -6
- package/dist/utils/isNumber.d.ts +0 -7
- package/dist/utils/isNumber.js +0 -15
- package/dist/utils/isObject.d.ts +0 -1
- package/dist/utils/isObject.js +0 -6
- package/dist/utils/isObservable.d.ts +0 -1
- package/dist/utils/isObservable.js +0 -6
- package/dist/utils/isPlainObject.d.ts +0 -7
- package/dist/utils/isPlainObject.js +0 -16
- package/dist/utils/isPrimitive.d.ts +0 -14
- package/dist/utils/isPrimitive.js +0 -28
- package/dist/utils/isPrimitiveType.d.ts +0 -1
- package/dist/utils/isPrimitiveType.js +0 -11
- package/dist/utils/isPromise.d.ts +0 -7
- package/dist/utils/isPromise.js +0 -14
- package/dist/utils/isProtectedKey.d.ts +0 -5
- package/dist/utils/isProtectedKey.js +0 -10
- package/dist/utils/isRegExp.d.ts +0 -1
- package/dist/utils/isRegExp.js +0 -6
- package/dist/utils/isRequestScope.d.ts +0 -11
- package/dist/utils/isRequestScope.js +0 -23
- package/dist/utils/isSerializable.d.ts +0 -1
- package/dist/utils/isSerializable.js +0 -11
- package/dist/utils/isStream.d.ts +0 -1
- package/dist/utils/isStream.js +0 -6
- package/dist/utils/isString.d.ts +0 -6
- package/dist/utils/isString.js +0 -14
- package/dist/utils/isSymbol.d.ts +0 -6
- package/dist/utils/isSymbol.js +0 -14
- package/dist/utils/methodsOf.d.ts +0 -9
- package/dist/utils/methodsOf.js +0 -24
- package/dist/utils/nameOf.d.ts +0 -14
- package/dist/utils/nameOf.js +0 -31
- package/dist/utils/objectKeys.d.ts +0 -1
- package/dist/utils/objectKeys.js +0 -7
- package/dist/utils/primitiveOf.d.ts +0 -1
- package/dist/utils/primitiveOf.js +0 -18
- package/dist/utils/prototypeOf.d.ts +0 -6
- package/dist/utils/prototypeOf.js +0 -12
- package/dist/utils/setValue.d.ts +0 -1
- package/dist/utils/setValue.js +0 -32
- package/dist/utils/toMap.d.ts +0 -3
- package/dist/utils/toMap.js +0 -34
- package/dist/utils/toStringConstructor.d.ts +0 -1
- package/dist/utils/toStringConstructor.js +0 -10
- package/dist/validation/ValidatorAdapter.d.ts +0 -66
- package/dist/validation/adapters/ClassValidatorAdapter.d.ts +0 -23
- package/dist/validation/adapters/ClassValidatorAdapter.js +0 -47
- package/dist/validation/adapters/ZodAdapter.d.ts +0 -14
- package/dist/validation/adapters/ZodAdapter.js +0 -56
- package/dist/validation/adapters/index.d.ts +0 -4
- package/dist/validation/adapters/index.js +0 -7
- package/dist/validation/index.d.ts +0 -3
- package/dist/validation/index.js +0 -20
package/src/index.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carno - Ultra-Fast HTTP Framework
|
|
3
|
+
*
|
|
4
|
+
* Design principles:
|
|
5
|
+
* 1. ZERO abstraction at runtime - everything compiled at startup
|
|
6
|
+
* 2. Direct Bun.serve() with native routes
|
|
7
|
+
* 3. JIT compiled handlers with AOT async detection
|
|
8
|
+
* 4. No intermediate layers in hot path
|
|
9
|
+
* 5. Radix tree router for dynamic routes
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Application
|
|
13
|
+
export { Carno } from './Carno';
|
|
14
|
+
export type { MiddlewareHandler, CarnoConfig } from './Carno';
|
|
15
|
+
|
|
16
|
+
// Context
|
|
17
|
+
export { Context } from './context/Context';
|
|
18
|
+
|
|
19
|
+
// Decorators - Controller
|
|
20
|
+
export { Controller } from './decorators/Controller';
|
|
21
|
+
export type { ControllerOptions } from './metadata';
|
|
22
|
+
|
|
23
|
+
// Decorators - HTTP Methods
|
|
24
|
+
export { Get, Post, Put, Delete, Patch, Head, Options } from './decorators/methods';
|
|
25
|
+
|
|
26
|
+
// Decorators - Parameters
|
|
27
|
+
export { Param, Query, Body, Header, Req, Ctx, Locals } from './decorators/params';
|
|
28
|
+
|
|
29
|
+
// Decorators - Middleware
|
|
30
|
+
export { Use, Use as Middleware } from './decorators/Middleware';
|
|
31
|
+
|
|
32
|
+
// Middleware Interface
|
|
33
|
+
export type { CarnoMiddleware, CarnoClosure } from './middleware/CarnoMiddleware';
|
|
34
|
+
|
|
35
|
+
// Decorators - DI
|
|
36
|
+
export { Service } from './decorators/Service';
|
|
37
|
+
export { Inject } from './decorators/Inject';
|
|
38
|
+
|
|
39
|
+
// Container
|
|
40
|
+
export { Container, Scope } from './container/Container';
|
|
41
|
+
export type { Token, ProviderConfig } from './container/Container';
|
|
42
|
+
|
|
43
|
+
// Router
|
|
44
|
+
export { RadixRouter } from './router/RadixRouter';
|
|
45
|
+
export type { RouteMatch } from './router/RadixRouter';
|
|
46
|
+
|
|
47
|
+
// CORS
|
|
48
|
+
export { CorsHandler } from './cors/CorsHandler';
|
|
49
|
+
export type { CorsConfig, CorsOrigin } from './cors/CorsHandler';
|
|
50
|
+
|
|
51
|
+
// Validation
|
|
52
|
+
export type { ValidatorAdapter, ValidationResult, ValidationError, ValidationConfig } from './validation/ValidatorAdapter';
|
|
53
|
+
export { Schema, getSchema, VALIDATION_SCHEMA } from './validation/ValidatorAdapter';
|
|
54
|
+
export { ZodAdapter, ValidationException } from './validation/ZodAdapter';
|
|
55
|
+
export { ValibotAdapter } from './validation/ValibotAdapter';
|
|
56
|
+
|
|
57
|
+
// Exceptions
|
|
58
|
+
export {
|
|
59
|
+
HttpException,
|
|
60
|
+
BadRequestException,
|
|
61
|
+
UnauthorizedException,
|
|
62
|
+
ForbiddenException,
|
|
63
|
+
NotFoundException,
|
|
64
|
+
MethodNotAllowedException,
|
|
65
|
+
ConflictException,
|
|
66
|
+
UnprocessableEntityException,
|
|
67
|
+
TooManyRequestsException,
|
|
68
|
+
InternalServerErrorException,
|
|
69
|
+
ServiceUnavailableException
|
|
70
|
+
} from './exceptions/HttpException';
|
|
71
|
+
|
|
72
|
+
// Lifecycle Events
|
|
73
|
+
export {
|
|
74
|
+
EventType,
|
|
75
|
+
OnApplicationInit,
|
|
76
|
+
OnApplicationBoot,
|
|
77
|
+
OnApplicationShutdown
|
|
78
|
+
} from './events/Lifecycle';
|
|
79
|
+
|
|
80
|
+
// Cache
|
|
81
|
+
export { CacheService } from './cache/CacheService';
|
|
82
|
+
export { MemoryDriver } from './cache/MemoryDriver';
|
|
83
|
+
export { RedisDriver } from './cache/RedisDriver';
|
|
84
|
+
export type { RedisConfig } from './cache/RedisDriver';
|
|
85
|
+
export type { CacheDriver, CacheConfig } from './cache/CacheDriver';
|
|
86
|
+
|
|
87
|
+
// Testing
|
|
88
|
+
export { createTestHarness, withTestApp } from './testing/TestHarness';
|
|
89
|
+
export type { TestHarness, TestOptions } from './testing/TestHarness';
|
|
90
|
+
|
|
91
|
+
// Utils
|
|
92
|
+
export { Metadata, isObject, isString } from './utils/Metadata';
|
package/src/metadata.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Scope } from './container/Container';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Controller options for the @Controller decorator.
|
|
5
|
+
*/
|
|
6
|
+
export interface ControllerOptions {
|
|
7
|
+
path?: string;
|
|
8
|
+
scope?: Scope;
|
|
9
|
+
children?: any[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Controller metadata stored on controller classes.
|
|
14
|
+
*/
|
|
15
|
+
export interface ControllerMeta {
|
|
16
|
+
path: string;
|
|
17
|
+
scope?: Scope;
|
|
18
|
+
children?: any[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Route metadata stored on controllers.
|
|
23
|
+
*/
|
|
24
|
+
export interface RouteInfo {
|
|
25
|
+
method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options';
|
|
26
|
+
path: string;
|
|
27
|
+
handlerName: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Middleware metadata.
|
|
32
|
+
*/
|
|
33
|
+
export interface MiddlewareInfo {
|
|
34
|
+
handler: Function;
|
|
35
|
+
target?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Metadata keys.
|
|
40
|
+
*/
|
|
41
|
+
export const CONTROLLER_META = Symbol('turbo:controller');
|
|
42
|
+
export const ROUTES_META = Symbol('turbo:routes');
|
|
43
|
+
export const PARAMS_META = Symbol('turbo:params');
|
|
44
|
+
export const MIDDLEWARE_META = Symbol('turbo:middleware');
|
|
45
|
+
export const SERVICE_META = Symbol('turbo:service');
|
|
46
|
+
export const INJECT_META = Symbol('turbo:inject');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from '../context/Context';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Closure function to call the next middleware in the chain.
|
|
5
|
+
*/
|
|
6
|
+
export type CarnoClosure = () => void | Promise<void>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Interface for onion-style middleware.
|
|
10
|
+
* Middleware must call next() to continue the chain.
|
|
11
|
+
*/
|
|
12
|
+
export interface CarnoMiddleware {
|
|
13
|
+
handle(ctx: Context, next: CarnoClosure): void | Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultra-fast Radix Router for Turbo.
|
|
3
|
+
*
|
|
4
|
+
* Optimizations:
|
|
5
|
+
* - Char code comparisons (no string allocations)
|
|
6
|
+
* - Frozen empty params object
|
|
7
|
+
* - O(1) method lookup via char code index
|
|
8
|
+
* - Minimal allocations in hot path
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const EMPTY_PARAMS: Readonly<Record<string, string>> = Object.freeze({});
|
|
12
|
+
|
|
13
|
+
interface Node<T> {
|
|
14
|
+
part: string;
|
|
15
|
+
store: T | null;
|
|
16
|
+
children: Map<number, Node<T>> | null;
|
|
17
|
+
paramChild: ParamNode<T> | null;
|
|
18
|
+
wildcardStore: T | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ParamNode<T> {
|
|
22
|
+
name: string;
|
|
23
|
+
store: T | null;
|
|
24
|
+
child: Node<T> | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RouteMatch<T> {
|
|
28
|
+
store: T;
|
|
29
|
+
params: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createNode<T>(part: string): Node<T> {
|
|
33
|
+
return {
|
|
34
|
+
part,
|
|
35
|
+
store: null,
|
|
36
|
+
children: null,
|
|
37
|
+
paramChild: null,
|
|
38
|
+
wildcardStore: null
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class RadixRouter<T> {
|
|
43
|
+
private roots: Record<string, Node<T>> = {};
|
|
44
|
+
|
|
45
|
+
add(method: string, path: string, store: T): void {
|
|
46
|
+
if (path === '') path = '/';
|
|
47
|
+
if (path[0] !== '/') path = '/' + path;
|
|
48
|
+
|
|
49
|
+
const isWildcard = path.endsWith('*');
|
|
50
|
+
|
|
51
|
+
if (isWildcard) path = path.slice(0, -1);
|
|
52
|
+
|
|
53
|
+
let node = this.roots[method];
|
|
54
|
+
|
|
55
|
+
if (!node) {
|
|
56
|
+
node = this.roots[method] = createNode('/');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let i = 0;
|
|
60
|
+
const len = path.length;
|
|
61
|
+
|
|
62
|
+
while (i < len) {
|
|
63
|
+
const char = path.charCodeAt(i);
|
|
64
|
+
|
|
65
|
+
if (char === 58) {
|
|
66
|
+
const paramStart = i + 1;
|
|
67
|
+
let paramEnd = paramStart;
|
|
68
|
+
|
|
69
|
+
while (paramEnd < len && path.charCodeAt(paramEnd) !== 47) {
|
|
70
|
+
paramEnd++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const paramName = path.slice(paramStart, paramEnd);
|
|
74
|
+
|
|
75
|
+
if (!node.paramChild) {
|
|
76
|
+
node.paramChild = { name: paramName, store: null, child: null };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (paramEnd >= len) {
|
|
80
|
+
node.paramChild.store = store;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!node.paramChild.child) {
|
|
85
|
+
node.paramChild.child = createNode(path.slice(paramEnd));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
node = node.paramChild.child;
|
|
89
|
+
i = paramEnd;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let segmentEnd = i;
|
|
94
|
+
|
|
95
|
+
while (segmentEnd < len && path.charCodeAt(segmentEnd) !== 47 && path.charCodeAt(segmentEnd) !== 58) {
|
|
96
|
+
segmentEnd++;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (segmentEnd < len && path.charCodeAt(segmentEnd) === 47) {
|
|
100
|
+
segmentEnd++;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const segment = path.slice(i, segmentEnd);
|
|
104
|
+
|
|
105
|
+
if (segment === node.part) {
|
|
106
|
+
i = segmentEnd;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!node.children) {
|
|
111
|
+
node.children = new Map();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const firstChar = segment.charCodeAt(0);
|
|
115
|
+
let child = node.children.get(firstChar);
|
|
116
|
+
|
|
117
|
+
if (!child) {
|
|
118
|
+
child = createNode(segment);
|
|
119
|
+
node.children.set(firstChar, child);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
node = child;
|
|
123
|
+
i = segmentEnd;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (isWildcard) {
|
|
127
|
+
node.wildcardStore = store;
|
|
128
|
+
} else {
|
|
129
|
+
node.store = store;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
find(method: string, path: string): RouteMatch<T> | null {
|
|
134
|
+
const root = this.roots[method];
|
|
135
|
+
|
|
136
|
+
if (!root) return null;
|
|
137
|
+
|
|
138
|
+
return this.matchPath(root, path, 0, path.length);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private matchPath(
|
|
142
|
+
node: Node<T>,
|
|
143
|
+
path: string,
|
|
144
|
+
start: number,
|
|
145
|
+
len: number
|
|
146
|
+
): RouteMatch<T> | null {
|
|
147
|
+
const partLen = node.part.length;
|
|
148
|
+
const end = start + partLen;
|
|
149
|
+
|
|
150
|
+
if (partLen > 1) {
|
|
151
|
+
if (end > len) return null;
|
|
152
|
+
|
|
153
|
+
for (let i = 1, j = start + 1; i < partLen; i++, j++) {
|
|
154
|
+
if (node.part.charCodeAt(i) !== path.charCodeAt(j)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (end === len) {
|
|
161
|
+
if (node.store !== null) {
|
|
162
|
+
return { store: node.store, params: EMPTY_PARAMS };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (node.wildcardStore !== null) {
|
|
166
|
+
return { store: node.wildcardStore, params: { '*': '' } };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (node.children) {
|
|
173
|
+
const child = node.children.get(path.charCodeAt(end));
|
|
174
|
+
|
|
175
|
+
if (child) {
|
|
176
|
+
const result = this.matchPath(child, path, end, len);
|
|
177
|
+
|
|
178
|
+
if (result) return result;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (node.paramChild) {
|
|
183
|
+
const param = node.paramChild;
|
|
184
|
+
let paramEnd = end;
|
|
185
|
+
|
|
186
|
+
while (paramEnd < len && path.charCodeAt(paramEnd) !== 47) {
|
|
187
|
+
paramEnd++;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (paramEnd === end) return null;
|
|
191
|
+
|
|
192
|
+
const paramValue = path.slice(end, paramEnd);
|
|
193
|
+
|
|
194
|
+
if (paramEnd >= len) {
|
|
195
|
+
if (param.store !== null) {
|
|
196
|
+
return {
|
|
197
|
+
store: param.store,
|
|
198
|
+
params: { [param.name]: paramValue }
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
} else if (param.child) {
|
|
202
|
+
const result = this.matchPath(param.child, path, paramEnd, len);
|
|
203
|
+
|
|
204
|
+
if (result) {
|
|
205
|
+
if (result.params === EMPTY_PARAMS) {
|
|
206
|
+
result.params = { [param.name]: paramValue };
|
|
207
|
+
} else {
|
|
208
|
+
result.params[param.name] = paramValue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (node.wildcardStore !== null) {
|
|
217
|
+
return {
|
|
218
|
+
store: node.wildcardStore,
|
|
219
|
+
params: { '*': path.slice(end) }
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Server } from 'bun';
|
|
2
|
+
import { Carno, type CarnoConfig } from '../Carno';
|
|
3
|
+
import { Container, type Token } from '../container/Container';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test configuration options.
|
|
7
|
+
*/
|
|
8
|
+
export interface TestOptions {
|
|
9
|
+
config?: CarnoConfig;
|
|
10
|
+
listen?: boolean | number;
|
|
11
|
+
port?: number;
|
|
12
|
+
controllers?: (new (...args: any[]) => any)[];
|
|
13
|
+
services?: (Token | any)[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Test harness - provides utilities for testing Carno applications.
|
|
18
|
+
*/
|
|
19
|
+
export interface TestHarness {
|
|
20
|
+
/** The Carno app instance */
|
|
21
|
+
app: Carno;
|
|
22
|
+
|
|
23
|
+
/** The internal DI container */
|
|
24
|
+
container: Container;
|
|
25
|
+
|
|
26
|
+
/** The HTTP server (if listening) */
|
|
27
|
+
server?: Server<any>;
|
|
28
|
+
|
|
29
|
+
/** The port the server is running on */
|
|
30
|
+
port?: number;
|
|
31
|
+
|
|
32
|
+
/** Resolve a service from the container */
|
|
33
|
+
resolve<T>(token: Token<T>): T;
|
|
34
|
+
|
|
35
|
+
/** Make an HTTP request to the app */
|
|
36
|
+
request(path: string, init?: RequestInit): Promise<Response>;
|
|
37
|
+
|
|
38
|
+
/** Make a GET request */
|
|
39
|
+
get(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
|
|
40
|
+
|
|
41
|
+
/** Make a POST request */
|
|
42
|
+
post(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
|
|
43
|
+
|
|
44
|
+
/** Make a PUT request */
|
|
45
|
+
put(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
|
|
46
|
+
|
|
47
|
+
/** Make a DELETE request */
|
|
48
|
+
delete(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
|
|
49
|
+
|
|
50
|
+
/** Close the test harness and cleanup */
|
|
51
|
+
close(): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a test harness for Turbo applications.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const harness = await createTestHarness({
|
|
60
|
+
* controllers: [UserController],
|
|
61
|
+
* services: [UserService],
|
|
62
|
+
* listen: true
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* const response = await harness.get('/users');
|
|
66
|
+
* expect(response.status).toBe(200);
|
|
67
|
+
*
|
|
68
|
+
* await harness.close();
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export async function createTestHarness(options: TestOptions = {}): Promise<TestHarness> {
|
|
72
|
+
const config: CarnoConfig = {
|
|
73
|
+
...options.config,
|
|
74
|
+
disableStartupLog: true
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const app = new Carno(config);
|
|
78
|
+
|
|
79
|
+
// Register controllers
|
|
80
|
+
if (options.controllers) {
|
|
81
|
+
app.controllers(options.controllers);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Register services
|
|
85
|
+
if (options.services) {
|
|
86
|
+
app.services(options.services);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const port = resolvePort(options);
|
|
90
|
+
let server: Server<any> | undefined;
|
|
91
|
+
|
|
92
|
+
if (shouldListen(options.listen)) {
|
|
93
|
+
app.listen(port);
|
|
94
|
+
server = (app as any).server;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const actualPort = server?.port ?? port;
|
|
98
|
+
const container = (app as any).container as Container;
|
|
99
|
+
|
|
100
|
+
// Pre-bind methods for performance
|
|
101
|
+
const baseUrl = `http://127.0.0.1:${actualPort}`;
|
|
102
|
+
|
|
103
|
+
const request = async (path: string, init?: RequestInit): Promise<Response> => {
|
|
104
|
+
if (!server) {
|
|
105
|
+
throw new Error('Server not running. Set listen: true in options.');
|
|
106
|
+
}
|
|
107
|
+
const url = path.startsWith('http') ? path : `${baseUrl}${path.startsWith('/') ? path : '/' + path}`;
|
|
108
|
+
return fetch(url, init);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
app,
|
|
113
|
+
container,
|
|
114
|
+
server,
|
|
115
|
+
port: actualPort,
|
|
116
|
+
|
|
117
|
+
resolve: <T>(token: Token<T>): T => container.get(token),
|
|
118
|
+
|
|
119
|
+
request,
|
|
120
|
+
|
|
121
|
+
get: (path, init) => request(path, { ...init, method: 'GET' }),
|
|
122
|
+
|
|
123
|
+
post: (path, body, init) => request(path, {
|
|
124
|
+
...init,
|
|
125
|
+
method: 'POST',
|
|
126
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
127
|
+
headers: { 'Content-Type': 'application/json', ...init?.headers }
|
|
128
|
+
}),
|
|
129
|
+
|
|
130
|
+
put: (path, body, init) => request(path, {
|
|
131
|
+
...init,
|
|
132
|
+
method: 'PUT',
|
|
133
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
134
|
+
headers: { 'Content-Type': 'application/json', ...init?.headers }
|
|
135
|
+
}),
|
|
136
|
+
|
|
137
|
+
delete: (path, init) => request(path, { ...init, method: 'DELETE' }),
|
|
138
|
+
|
|
139
|
+
close: async () => {
|
|
140
|
+
app.stop();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Run a test routine with automatic harness cleanup.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* await withTestApp(async (harness) => {
|
|
151
|
+
* const response = await harness.get('/health');
|
|
152
|
+
* expect(response.status).toBe(200);
|
|
153
|
+
* }, { controllers: [HealthController], listen: true });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export async function withTestApp(
|
|
157
|
+
routine: (harness: TestHarness) => Promise<void>,
|
|
158
|
+
options: TestOptions = {}
|
|
159
|
+
): Promise<void> {
|
|
160
|
+
const harness = await createTestHarness(options);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
await routine(harness);
|
|
164
|
+
} finally {
|
|
165
|
+
await harness.close();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function shouldListen(value: TestOptions['listen']): boolean {
|
|
170
|
+
return typeof value === 'number' || Boolean(value);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function resolvePort(options: TestOptions): number {
|
|
174
|
+
if (typeof options.listen === 'number') return options.listen;
|
|
175
|
+
if (typeof options.port === 'number') return options.port;
|
|
176
|
+
return 0; // Random port
|
|
177
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility class for handling metadata operations.
|
|
3
|
+
* Wraps Reflect.getMetadata and Reflect.defineMetadata.
|
|
4
|
+
*/
|
|
5
|
+
export class Metadata {
|
|
6
|
+
static get<T = any>(key: string | symbol, target: any): T | undefined {
|
|
7
|
+
return Reflect.getMetadata(key, target);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static set(key: string | symbol, value: any, target: any): void {
|
|
11
|
+
Reflect.defineMetadata(key, value, target);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static has(key: string | symbol, target: any): boolean {
|
|
15
|
+
return Reflect.hasMetadata(key, target);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static delete(key: string | symbol, target: any): boolean {
|
|
19
|
+
return Reflect.deleteMetadata(key, target);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static keys(target: any): (string | symbol)[] {
|
|
23
|
+
return Reflect.getMetadataKeys(target);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static getType(target: any, propertyKey: string | symbol): any {
|
|
27
|
+
return Reflect.getMetadata('design:type', target, propertyKey);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Type guard for checking if value is an object.
|
|
33
|
+
*/
|
|
34
|
+
export function isObject(value: unknown): value is Record<string, any> {
|
|
35
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Type guard for checking if value is a string.
|
|
40
|
+
*/
|
|
41
|
+
export function isString(value: unknown): value is string {
|
|
42
|
+
return typeof value === 'string';
|
|
43
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ValidatorAdapter, ValidationResult, ValidationError } from './ValidatorAdapter';
|
|
2
|
+
import { VALIDATION_SCHEMA, getSchema } from './ValidatorAdapter';
|
|
3
|
+
import { ValidationException } from './ZodAdapter';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Valibot Adapter for Turbo validation.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import * as v from 'valibot';
|
|
11
|
+
*
|
|
12
|
+
* @Schema(v.object({
|
|
13
|
+
* name: v.pipe(v.string(), v.minLength(1)),
|
|
14
|
+
* email: v.pipe(v.string(), v.email())
|
|
15
|
+
* }))
|
|
16
|
+
* class CreateUserDto {
|
|
17
|
+
* name: string;
|
|
18
|
+
* email: string;
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class ValibotAdapter implements ValidatorAdapter {
|
|
23
|
+
readonly name = 'ValibotAdapter';
|
|
24
|
+
|
|
25
|
+
private schemaCache = new Map<any, any>();
|
|
26
|
+
private valibot: any = null;
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
// Lazy load valibot
|
|
30
|
+
try {
|
|
31
|
+
this.valibot = require('valibot');
|
|
32
|
+
} catch {
|
|
33
|
+
// Will be loaded on first use
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private ensureValibot(): any {
|
|
38
|
+
if (!this.valibot) {
|
|
39
|
+
this.valibot = require('valibot');
|
|
40
|
+
}
|
|
41
|
+
return this.valibot;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
hasValidation(target: any): boolean {
|
|
45
|
+
return getSchema(target) !== undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
validate<T>(target: any, value: unknown): ValidationResult<T> {
|
|
49
|
+
const schema = this.getOrCacheSchema(target);
|
|
50
|
+
|
|
51
|
+
if (!schema) {
|
|
52
|
+
return { success: true, data: value as T };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const v = this.ensureValibot();
|
|
56
|
+
const result = v.safeParse(schema, value);
|
|
57
|
+
|
|
58
|
+
if (result.success) {
|
|
59
|
+
return { success: true, data: result.output };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
errors: this.formatErrors(result.issues)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
validateOrThrow<T>(target: any, value: unknown): T {
|
|
69
|
+
const result = this.validate<T>(target, value);
|
|
70
|
+
|
|
71
|
+
if (result.success) {
|
|
72
|
+
return result.data!;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
throw new ValidationException(result.errors!);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private getOrCacheSchema(target: any): any {
|
|
79
|
+
let schema = this.schemaCache.get(target);
|
|
80
|
+
|
|
81
|
+
if (schema === undefined) {
|
|
82
|
+
schema = getSchema(target) ?? null;
|
|
83
|
+
this.schemaCache.set(target, schema);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return schema;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private formatErrors(issues: any[]): ValidationError[] {
|
|
90
|
+
return issues.map((issue: any) => ({
|
|
91
|
+
path: issue.path?.map((p: any) => p.key).join('.') || '',
|
|
92
|
+
message: issue.message
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
}
|