@carno.js/core 1.1.1 → 1.2.0

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 (124) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +188 -188
  3. package/dist/Carno.js +45 -26
  4. package/dist/Carno.mjs +45 -26
  5. package/dist/bun/index.js +4 -4
  6. package/dist/bun/index.js.map +30 -29
  7. package/dist/compression/CompressionMiddleware.js +110 -0
  8. package/dist/compression/CompressionMiddleware.mjs +90 -0
  9. package/dist/index.js +3 -1
  10. package/dist/index.mjs +2 -0
  11. package/package.json +2 -2
  12. package/src/Carno.ts +728 -673
  13. package/src/DefaultRoutes.ts +34 -34
  14. package/src/cache/CacheDriver.ts +50 -50
  15. package/src/cache/CacheService.ts +139 -139
  16. package/src/cache/MemoryDriver.ts +104 -104
  17. package/src/cache/RedisDriver.ts +116 -116
  18. package/src/compiler/JITCompiler.ts +167 -167
  19. package/src/compression/CompressionMiddleware.ts +221 -0
  20. package/src/container/Container.ts +168 -168
  21. package/src/context/Context.ts +130 -130
  22. package/src/cors/CorsHandler.ts +145 -145
  23. package/src/decorators/Controller.ts +63 -63
  24. package/src/decorators/Inject.ts +16 -16
  25. package/src/decorators/Middleware.ts +22 -22
  26. package/src/decorators/Service.ts +18 -18
  27. package/src/decorators/methods.ts +58 -58
  28. package/src/decorators/params.ts +47 -47
  29. package/src/events/Lifecycle.ts +97 -97
  30. package/src/exceptions/HttpException.ts +99 -99
  31. package/src/index.ts +99 -95
  32. package/src/metadata.ts +46 -46
  33. package/src/middleware/CarnoMiddleware.ts +20 -14
  34. package/src/router/RadixRouter.ts +225 -225
  35. package/src/testing/TestHarness.ts +185 -185
  36. package/src/utils/Metadata.ts +43 -43
  37. package/src/utils/parseQuery.ts +161 -161
  38. package/src/validation/ValibotAdapter.ts +95 -95
  39. package/src/validation/ValidatorAdapter.ts +69 -69
  40. package/src/validation/ZodAdapter.ts +102 -102
  41. package/dist/Carno.d.js +0 -14
  42. package/dist/Carno.d.mjs +0 -1
  43. package/dist/DefaultRoutes.d.js +0 -13
  44. package/dist/DefaultRoutes.d.mjs +0 -0
  45. package/dist/cache/CacheDriver.d.js +0 -13
  46. package/dist/cache/CacheDriver.d.mjs +0 -0
  47. package/dist/cache/CacheService.d.js +0 -13
  48. package/dist/cache/CacheService.d.mjs +0 -0
  49. package/dist/cache/MemoryDriver.d.js +0 -13
  50. package/dist/cache/MemoryDriver.d.mjs +0 -0
  51. package/dist/cache/RedisDriver.d.js +0 -13
  52. package/dist/cache/RedisDriver.d.mjs +0 -0
  53. package/dist/compiler/JITCompiler.d.js +0 -13
  54. package/dist/compiler/JITCompiler.d.mjs +0 -0
  55. package/dist/container/Container.d.js +0 -13
  56. package/dist/container/Container.d.mjs +0 -0
  57. package/dist/context/Context.d.js +0 -13
  58. package/dist/context/Context.d.mjs +0 -0
  59. package/dist/cors/CorsHandler.d.js +0 -13
  60. package/dist/cors/CorsHandler.d.mjs +0 -0
  61. package/dist/decorators/Controller.d.js +0 -13
  62. package/dist/decorators/Controller.d.mjs +0 -0
  63. package/dist/decorators/Inject.d.js +0 -13
  64. package/dist/decorators/Inject.d.mjs +0 -0
  65. package/dist/decorators/Middleware.d.js +0 -13
  66. package/dist/decorators/Middleware.d.mjs +0 -0
  67. package/dist/decorators/Service.d.js +0 -13
  68. package/dist/decorators/Service.d.mjs +0 -0
  69. package/dist/decorators/methods.d.js +0 -13
  70. package/dist/decorators/methods.d.mjs +0 -0
  71. package/dist/decorators/params.d.js +0 -13
  72. package/dist/decorators/params.d.mjs +0 -0
  73. package/dist/events/Lifecycle.d.js +0 -13
  74. package/dist/events/Lifecycle.d.mjs +0 -0
  75. package/dist/exceptions/HttpException.d.js +0 -13
  76. package/dist/exceptions/HttpException.d.mjs +0 -0
  77. package/dist/index.d.js +0 -130
  78. package/dist/index.d.mjs +0 -78
  79. package/dist/metadata.d.js +0 -13
  80. package/dist/metadata.d.mjs +0 -0
  81. package/dist/middleware/CarnoMiddleware.d.js +0 -13
  82. package/dist/middleware/CarnoMiddleware.d.mjs +0 -0
  83. package/dist/router/RadixRouter.d.js +0 -13
  84. package/dist/router/RadixRouter.d.mjs +0 -0
  85. package/dist/testing/TestHarness.d.js +0 -13
  86. package/dist/testing/TestHarness.d.mjs +0 -0
  87. package/dist/utils/Metadata.d.js +0 -13
  88. package/dist/utils/Metadata.d.mjs +0 -0
  89. package/dist/utils/parseQuery.d.js +0 -13
  90. package/dist/utils/parseQuery.d.mjs +0 -0
  91. package/dist/validation/ValibotAdapter.d.js +0 -13
  92. package/dist/validation/ValibotAdapter.d.mjs +0 -0
  93. package/dist/validation/ValidatorAdapter.d.js +0 -13
  94. package/dist/validation/ValidatorAdapter.d.mjs +0 -0
  95. package/dist/validation/ZodAdapter.d.js +0 -13
  96. package/dist/validation/ZodAdapter.d.mjs +0 -0
  97. package/src/Carno.d.ts +0 -135
  98. package/src/DefaultRoutes.d.ts +0 -19
  99. package/src/cache/CacheDriver.d.ts +0 -43
  100. package/src/cache/CacheService.d.ts +0 -89
  101. package/src/cache/MemoryDriver.d.ts +0 -32
  102. package/src/cache/RedisDriver.d.ts +0 -34
  103. package/src/compiler/JITCompiler.d.ts +0 -36
  104. package/src/container/Container.d.ts +0 -38
  105. package/src/context/Context.d.ts +0 -36
  106. package/src/cors/CorsHandler.d.ts +0 -47
  107. package/src/decorators/Controller.d.ts +0 -13
  108. package/src/decorators/Inject.d.ts +0 -6
  109. package/src/decorators/Middleware.d.ts +0 -5
  110. package/src/decorators/Service.d.ts +0 -9
  111. package/src/decorators/methods.d.ts +0 -7
  112. package/src/decorators/params.d.ts +0 -13
  113. package/src/events/Lifecycle.d.ts +0 -54
  114. package/src/exceptions/HttpException.d.ts +0 -43
  115. package/src/index.d.ts +0 -42
  116. package/src/metadata.d.ts +0 -41
  117. package/src/middleware/CarnoMiddleware.d.ts +0 -12
  118. package/src/router/RadixRouter.d.ts +0 -19
  119. package/src/testing/TestHarness.d.ts +0 -71
  120. package/src/utils/Metadata.d.ts +0 -20
  121. package/src/utils/parseQuery.d.ts +0 -23
  122. package/src/validation/ValibotAdapter.d.ts +0 -30
  123. package/src/validation/ValidatorAdapter.d.ts +0 -54
  124. package/src/validation/ZodAdapter.d.ts +0 -35
@@ -1,161 +1,161 @@
1
- /**
2
- * High-performance query string parser.
3
- *
4
- * Based on Elysia's approach - uses manual string parsing with charCodeAt()
5
- * instead of new URL() for significant performance gains.
6
- *
7
- * Benchmark: ~10x faster than new URL().searchParams
8
- */
9
-
10
- // Bit flags for tracking decode requirements
11
- const KEY_HAS_PLUS = 1;
12
- const KEY_NEEDS_DECODE = 2;
13
- const VALUE_HAS_PLUS = 4;
14
- const VALUE_NEEDS_DECODE = 8;
15
-
16
- /**
17
- * Parse query string from a full URL.
18
- * Extracts the query portion and parses key-value pairs.
19
- *
20
- * @param url Full URL string (e.g., "http://localhost/path?foo=bar&baz=123")
21
- * @returns Record<string, string> - parsed query parameters
22
- */
23
- export function parseQueryFromURL(url: string): Record<string, string> {
24
- // Find the start of query string
25
- const queryStart = url.indexOf('?');
26
-
27
- if (queryStart === -1) {
28
- return Object.create(null);
29
- }
30
-
31
- // Find the end of query string (before hash if present)
32
- let queryEnd = url.indexOf('#', queryStart);
33
-
34
- if (queryEnd === -1) {
35
- queryEnd = url.length;
36
- }
37
-
38
- return parseQuery(url, queryStart + 1, queryEnd);
39
- }
40
-
41
- /**
42
- * Parse query string directly.
43
- *
44
- * @param input Query string without leading '?' (e.g., "foo=bar&baz=123")
45
- * @returns Record<string, string> - parsed query parameters
46
- */
47
- export function parseQueryString(input: string): Record<string, string> {
48
- return parseQuery(input, 0, input.length);
49
- }
50
-
51
- /**
52
- * Internal parser - parses query string from startIndex to endIndex.
53
- */
54
- function parseQuery(
55
- input: string,
56
- startIndex: number,
57
- endIndex: number
58
- ): Record<string, string> {
59
- const result: Record<string, string> = Object.create(null);
60
-
61
- let flags = 0;
62
- let startingIndex = startIndex - 1;
63
- let equalityIndex = startingIndex;
64
-
65
- for (let i = startIndex; i < endIndex; i++) {
66
- switch (input.charCodeAt(i)) {
67
- // '&' - separator between key-value pairs
68
- case 38:
69
- processKeyValuePair(i);
70
- startingIndex = i;
71
- equalityIndex = i;
72
- flags = 0;
73
- break;
74
-
75
- // '=' - separator between key and value
76
- case 61:
77
- if (equalityIndex <= startingIndex) {
78
- equalityIndex = i;
79
- } else {
80
- // Multiple '=' means value needs decode
81
- flags |= VALUE_NEEDS_DECODE;
82
- }
83
- break;
84
-
85
- // '+' - space encoding
86
- case 43:
87
- if (equalityIndex > startingIndex) {
88
- flags |= VALUE_HAS_PLUS;
89
- } else {
90
- flags |= KEY_HAS_PLUS;
91
- }
92
- break;
93
-
94
- // '%' - URL encoding
95
- case 37:
96
- if (equalityIndex > startingIndex) {
97
- flags |= VALUE_NEEDS_DECODE;
98
- } else {
99
- flags |= KEY_NEEDS_DECODE;
100
- }
101
- break;
102
- }
103
- }
104
-
105
- // Process the last pair
106
- if (startingIndex < endIndex) {
107
- processKeyValuePair(endIndex);
108
- }
109
-
110
- return result;
111
-
112
- function processKeyValuePair(pairEndIndex: number) {
113
- const hasBothKeyValuePair = equalityIndex > startingIndex;
114
- const effectiveEqualityIndex = hasBothKeyValuePair
115
- ? equalityIndex
116
- : pairEndIndex;
117
-
118
- const keySlice = input.slice(startingIndex + 1, effectiveEqualityIndex);
119
-
120
- // Skip empty keys
121
- if (!hasBothKeyValuePair && keySlice.length === 0) {
122
- return;
123
- }
124
-
125
- let finalKey = keySlice;
126
-
127
- if (flags & KEY_HAS_PLUS) {
128
- finalKey = finalKey.replace(/\+/g, ' ');
129
- }
130
-
131
- if (flags & KEY_NEEDS_DECODE) {
132
- try {
133
- finalKey = decodeURIComponent(finalKey);
134
- } catch {
135
- // Keep original if decode fails
136
- }
137
- }
138
-
139
- let finalValue = '';
140
-
141
- if (hasBothKeyValuePair) {
142
- let valueSlice = input.slice(equalityIndex + 1, pairEndIndex);
143
-
144
- if (flags & VALUE_HAS_PLUS) {
145
- valueSlice = valueSlice.replace(/\+/g, ' ');
146
- }
147
-
148
- if (flags & VALUE_NEEDS_DECODE) {
149
- try {
150
- finalValue = decodeURIComponent(valueSlice);
151
- } catch {
152
- finalValue = valueSlice;
153
- }
154
- } else {
155
- finalValue = valueSlice;
156
- }
157
- }
158
-
159
- result[finalKey] = finalValue;
160
- }
161
- }
1
+ /**
2
+ * High-performance query string parser.
3
+ *
4
+ * Based on Elysia's approach - uses manual string parsing with charCodeAt()
5
+ * instead of new URL() for significant performance gains.
6
+ *
7
+ * Benchmark: ~10x faster than new URL().searchParams
8
+ */
9
+
10
+ // Bit flags for tracking decode requirements
11
+ const KEY_HAS_PLUS = 1;
12
+ const KEY_NEEDS_DECODE = 2;
13
+ const VALUE_HAS_PLUS = 4;
14
+ const VALUE_NEEDS_DECODE = 8;
15
+
16
+ /**
17
+ * Parse query string from a full URL.
18
+ * Extracts the query portion and parses key-value pairs.
19
+ *
20
+ * @param url Full URL string (e.g., "http://localhost/path?foo=bar&baz=123")
21
+ * @returns Record<string, string> - parsed query parameters
22
+ */
23
+ export function parseQueryFromURL(url: string): Record<string, string> {
24
+ // Find the start of query string
25
+ const queryStart = url.indexOf('?');
26
+
27
+ if (queryStart === -1) {
28
+ return Object.create(null);
29
+ }
30
+
31
+ // Find the end of query string (before hash if present)
32
+ let queryEnd = url.indexOf('#', queryStart);
33
+
34
+ if (queryEnd === -1) {
35
+ queryEnd = url.length;
36
+ }
37
+
38
+ return parseQuery(url, queryStart + 1, queryEnd);
39
+ }
40
+
41
+ /**
42
+ * Parse query string directly.
43
+ *
44
+ * @param input Query string without leading '?' (e.g., "foo=bar&baz=123")
45
+ * @returns Record<string, string> - parsed query parameters
46
+ */
47
+ export function parseQueryString(input: string): Record<string, string> {
48
+ return parseQuery(input, 0, input.length);
49
+ }
50
+
51
+ /**
52
+ * Internal parser - parses query string from startIndex to endIndex.
53
+ */
54
+ function parseQuery(
55
+ input: string,
56
+ startIndex: number,
57
+ endIndex: number
58
+ ): Record<string, string> {
59
+ const result: Record<string, string> = Object.create(null);
60
+
61
+ let flags = 0;
62
+ let startingIndex = startIndex - 1;
63
+ let equalityIndex = startingIndex;
64
+
65
+ for (let i = startIndex; i < endIndex; i++) {
66
+ switch (input.charCodeAt(i)) {
67
+ // '&' - separator between key-value pairs
68
+ case 38:
69
+ processKeyValuePair(i);
70
+ startingIndex = i;
71
+ equalityIndex = i;
72
+ flags = 0;
73
+ break;
74
+
75
+ // '=' - separator between key and value
76
+ case 61:
77
+ if (equalityIndex <= startingIndex) {
78
+ equalityIndex = i;
79
+ } else {
80
+ // Multiple '=' means value needs decode
81
+ flags |= VALUE_NEEDS_DECODE;
82
+ }
83
+ break;
84
+
85
+ // '+' - space encoding
86
+ case 43:
87
+ if (equalityIndex > startingIndex) {
88
+ flags |= VALUE_HAS_PLUS;
89
+ } else {
90
+ flags |= KEY_HAS_PLUS;
91
+ }
92
+ break;
93
+
94
+ // '%' - URL encoding
95
+ case 37:
96
+ if (equalityIndex > startingIndex) {
97
+ flags |= VALUE_NEEDS_DECODE;
98
+ } else {
99
+ flags |= KEY_NEEDS_DECODE;
100
+ }
101
+ break;
102
+ }
103
+ }
104
+
105
+ // Process the last pair
106
+ if (startingIndex < endIndex) {
107
+ processKeyValuePair(endIndex);
108
+ }
109
+
110
+ return result;
111
+
112
+ function processKeyValuePair(pairEndIndex: number) {
113
+ const hasBothKeyValuePair = equalityIndex > startingIndex;
114
+ const effectiveEqualityIndex = hasBothKeyValuePair
115
+ ? equalityIndex
116
+ : pairEndIndex;
117
+
118
+ const keySlice = input.slice(startingIndex + 1, effectiveEqualityIndex);
119
+
120
+ // Skip empty keys
121
+ if (!hasBothKeyValuePair && keySlice.length === 0) {
122
+ return;
123
+ }
124
+
125
+ let finalKey = keySlice;
126
+
127
+ if (flags & KEY_HAS_PLUS) {
128
+ finalKey = finalKey.replace(/\+/g, ' ');
129
+ }
130
+
131
+ if (flags & KEY_NEEDS_DECODE) {
132
+ try {
133
+ finalKey = decodeURIComponent(finalKey);
134
+ } catch {
135
+ // Keep original if decode fails
136
+ }
137
+ }
138
+
139
+ let finalValue = '';
140
+
141
+ if (hasBothKeyValuePair) {
142
+ let valueSlice = input.slice(equalityIndex + 1, pairEndIndex);
143
+
144
+ if (flags & VALUE_HAS_PLUS) {
145
+ valueSlice = valueSlice.replace(/\+/g, ' ');
146
+ }
147
+
148
+ if (flags & VALUE_NEEDS_DECODE) {
149
+ try {
150
+ finalValue = decodeURIComponent(valueSlice);
151
+ } catch {
152
+ finalValue = valueSlice;
153
+ }
154
+ } else {
155
+ finalValue = valueSlice;
156
+ }
157
+ }
158
+
159
+ result[finalKey] = finalValue;
160
+ }
161
+ }
@@ -1,95 +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
- }
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
+ }
@@ -1,69 +1,69 @@
1
- /**
2
- * Validation result type.
3
- */
4
- export interface ValidationResult<T = any> {
5
- success: boolean;
6
- data?: T;
7
- errors?: ValidationError[];
8
- }
9
-
10
- export interface ValidationError {
11
- path: string;
12
- message: string;
13
- }
14
-
15
- /**
16
- * Base interface for validation adapters.
17
- * Adapters provide validation capabilities for different libraries.
18
- */
19
- export interface ValidatorAdapter {
20
- /**
21
- * Validator name for debugging.
22
- */
23
- readonly name: string;
24
-
25
- /**
26
- * Check if a target has validation schema.
27
- */
28
- hasValidation(target: any): boolean;
29
-
30
- /**
31
- * Validate and transform a value.
32
- * Returns result object instead of throwing for better performance.
33
- */
34
- validate<T>(target: any, value: unknown): ValidationResult<T>;
35
-
36
- /**
37
- * Validate and transform, throwing on error.
38
- * Used when you want to short-circuit on failure.
39
- */
40
- validateOrThrow<T>(target: any, value: unknown): T;
41
- }
42
-
43
- /**
44
- * Validation configuration for Turbo.
45
- */
46
- export interface ValidationConfig {
47
- adapter: ValidatorAdapter;
48
- }
49
-
50
- /**
51
- * Symbol for storing validation schema on DTOs.
52
- */
53
- export const VALIDATION_SCHEMA = Symbol('turbo:validation');
54
-
55
- /**
56
- * Decorator to attach a validation schema to a DTO class.
57
- */
58
- export function Schema(schema: any): ClassDecorator {
59
- return (target) => {
60
- Reflect.defineMetadata(VALIDATION_SCHEMA, schema, target);
61
- };
62
- }
63
-
64
- /**
65
- * Get the validation schema from a DTO class.
66
- */
67
- export function getSchema(target: any): any | undefined {
68
- return Reflect.getMetadata(VALIDATION_SCHEMA, target);
69
- }
1
+ /**
2
+ * Validation result type.
3
+ */
4
+ export interface ValidationResult<T = any> {
5
+ success: boolean;
6
+ data?: T;
7
+ errors?: ValidationError[];
8
+ }
9
+
10
+ export interface ValidationError {
11
+ path: string;
12
+ message: string;
13
+ }
14
+
15
+ /**
16
+ * Base interface for validation adapters.
17
+ * Adapters provide validation capabilities for different libraries.
18
+ */
19
+ export interface ValidatorAdapter {
20
+ /**
21
+ * Validator name for debugging.
22
+ */
23
+ readonly name: string;
24
+
25
+ /**
26
+ * Check if a target has validation schema.
27
+ */
28
+ hasValidation(target: any): boolean;
29
+
30
+ /**
31
+ * Validate and transform a value.
32
+ * Returns result object instead of throwing for better performance.
33
+ */
34
+ validate<T>(target: any, value: unknown): ValidationResult<T>;
35
+
36
+ /**
37
+ * Validate and transform, throwing on error.
38
+ * Used when you want to short-circuit on failure.
39
+ */
40
+ validateOrThrow<T>(target: any, value: unknown): T;
41
+ }
42
+
43
+ /**
44
+ * Validation configuration for Turbo.
45
+ */
46
+ export interface ValidationConfig {
47
+ adapter: ValidatorAdapter;
48
+ }
49
+
50
+ /**
51
+ * Symbol for storing validation schema on DTOs.
52
+ */
53
+ export const VALIDATION_SCHEMA = Symbol('turbo:validation');
54
+
55
+ /**
56
+ * Decorator to attach a validation schema to a DTO class.
57
+ */
58
+ export function Schema(schema: any): ClassDecorator {
59
+ return (target) => {
60
+ Reflect.defineMetadata(VALIDATION_SCHEMA, schema, target);
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Get the validation schema from a DTO class.
66
+ */
67
+ export function getSchema(target: any): any | undefined {
68
+ return Reflect.getMetadata(VALIDATION_SCHEMA, target);
69
+ }