@carno.js/core 1.1.0 → 1.1.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.
Files changed (119) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +188 -188
  3. package/dist/Carno.js +46 -26
  4. package/dist/Carno.mjs +46 -26
  5. package/dist/bun/index.js +4 -4
  6. package/dist/bun/index.js.map +29 -29
  7. package/package.json +2 -2
  8. package/src/Carno.ts +718 -673
  9. package/src/DefaultRoutes.ts +34 -34
  10. package/src/cache/CacheDriver.ts +50 -50
  11. package/src/cache/CacheService.ts +139 -139
  12. package/src/cache/MemoryDriver.ts +104 -104
  13. package/src/cache/RedisDriver.ts +116 -116
  14. package/src/compiler/JITCompiler.ts +167 -167
  15. package/src/container/Container.ts +168 -168
  16. package/src/context/Context.ts +130 -130
  17. package/src/cors/CorsHandler.ts +145 -145
  18. package/src/decorators/Controller.ts +63 -63
  19. package/src/decorators/Inject.ts +16 -16
  20. package/src/decorators/Middleware.ts +22 -22
  21. package/src/decorators/Service.ts +18 -18
  22. package/src/decorators/methods.ts +58 -58
  23. package/src/decorators/params.ts +47 -47
  24. package/src/events/Lifecycle.ts +97 -97
  25. package/src/exceptions/HttpException.ts +99 -99
  26. package/src/index.ts +95 -95
  27. package/src/metadata.ts +46 -46
  28. package/src/middleware/CarnoMiddleware.ts +14 -14
  29. package/src/router/RadixRouter.ts +225 -225
  30. package/src/testing/TestHarness.ts +185 -185
  31. package/src/utils/Metadata.ts +43 -43
  32. package/src/utils/parseQuery.ts +161 -161
  33. package/src/validation/ValibotAdapter.ts +95 -95
  34. package/src/validation/ValidatorAdapter.ts +69 -69
  35. package/src/validation/ZodAdapter.ts +102 -102
  36. package/dist/Carno.d.js +0 -14
  37. package/dist/Carno.d.mjs +0 -1
  38. package/dist/DefaultRoutes.d.js +0 -13
  39. package/dist/DefaultRoutes.d.mjs +0 -0
  40. package/dist/cache/CacheDriver.d.js +0 -13
  41. package/dist/cache/CacheDriver.d.mjs +0 -0
  42. package/dist/cache/CacheService.d.js +0 -13
  43. package/dist/cache/CacheService.d.mjs +0 -0
  44. package/dist/cache/MemoryDriver.d.js +0 -13
  45. package/dist/cache/MemoryDriver.d.mjs +0 -0
  46. package/dist/cache/RedisDriver.d.js +0 -13
  47. package/dist/cache/RedisDriver.d.mjs +0 -0
  48. package/dist/compiler/JITCompiler.d.js +0 -13
  49. package/dist/compiler/JITCompiler.d.mjs +0 -0
  50. package/dist/container/Container.d.js +0 -13
  51. package/dist/container/Container.d.mjs +0 -0
  52. package/dist/context/Context.d.js +0 -13
  53. package/dist/context/Context.d.mjs +0 -0
  54. package/dist/cors/CorsHandler.d.js +0 -13
  55. package/dist/cors/CorsHandler.d.mjs +0 -0
  56. package/dist/decorators/Controller.d.js +0 -13
  57. package/dist/decorators/Controller.d.mjs +0 -0
  58. package/dist/decorators/Inject.d.js +0 -13
  59. package/dist/decorators/Inject.d.mjs +0 -0
  60. package/dist/decorators/Middleware.d.js +0 -13
  61. package/dist/decorators/Middleware.d.mjs +0 -0
  62. package/dist/decorators/Service.d.js +0 -13
  63. package/dist/decorators/Service.d.mjs +0 -0
  64. package/dist/decorators/methods.d.js +0 -13
  65. package/dist/decorators/methods.d.mjs +0 -0
  66. package/dist/decorators/params.d.js +0 -13
  67. package/dist/decorators/params.d.mjs +0 -0
  68. package/dist/events/Lifecycle.d.js +0 -13
  69. package/dist/events/Lifecycle.d.mjs +0 -0
  70. package/dist/exceptions/HttpException.d.js +0 -13
  71. package/dist/exceptions/HttpException.d.mjs +0 -0
  72. package/dist/index.d.js +0 -130
  73. package/dist/index.d.mjs +0 -78
  74. package/dist/metadata.d.js +0 -13
  75. package/dist/metadata.d.mjs +0 -0
  76. package/dist/middleware/CarnoMiddleware.d.js +0 -13
  77. package/dist/middleware/CarnoMiddleware.d.mjs +0 -0
  78. package/dist/router/RadixRouter.d.js +0 -13
  79. package/dist/router/RadixRouter.d.mjs +0 -0
  80. package/dist/testing/TestHarness.d.js +0 -13
  81. package/dist/testing/TestHarness.d.mjs +0 -0
  82. package/dist/utils/Metadata.d.js +0 -13
  83. package/dist/utils/Metadata.d.mjs +0 -0
  84. package/dist/utils/parseQuery.d.js +0 -13
  85. package/dist/utils/parseQuery.d.mjs +0 -0
  86. package/dist/validation/ValibotAdapter.d.js +0 -13
  87. package/dist/validation/ValibotAdapter.d.mjs +0 -0
  88. package/dist/validation/ValidatorAdapter.d.js +0 -13
  89. package/dist/validation/ValidatorAdapter.d.mjs +0 -0
  90. package/dist/validation/ZodAdapter.d.js +0 -13
  91. package/dist/validation/ZodAdapter.d.mjs +0 -0
  92. package/src/Carno.d.ts +0 -135
  93. package/src/DefaultRoutes.d.ts +0 -19
  94. package/src/cache/CacheDriver.d.ts +0 -43
  95. package/src/cache/CacheService.d.ts +0 -89
  96. package/src/cache/MemoryDriver.d.ts +0 -32
  97. package/src/cache/RedisDriver.d.ts +0 -34
  98. package/src/compiler/JITCompiler.d.ts +0 -36
  99. package/src/container/Container.d.ts +0 -38
  100. package/src/context/Context.d.ts +0 -36
  101. package/src/cors/CorsHandler.d.ts +0 -47
  102. package/src/decorators/Controller.d.ts +0 -13
  103. package/src/decorators/Inject.d.ts +0 -6
  104. package/src/decorators/Middleware.d.ts +0 -5
  105. package/src/decorators/Service.d.ts +0 -9
  106. package/src/decorators/methods.d.ts +0 -7
  107. package/src/decorators/params.d.ts +0 -13
  108. package/src/events/Lifecycle.d.ts +0 -54
  109. package/src/exceptions/HttpException.d.ts +0 -43
  110. package/src/index.d.ts +0 -42
  111. package/src/metadata.d.ts +0 -41
  112. package/src/middleware/CarnoMiddleware.d.ts +0 -12
  113. package/src/router/RadixRouter.d.ts +0 -19
  114. package/src/testing/TestHarness.d.ts +0 -71
  115. package/src/utils/Metadata.d.ts +0 -20
  116. package/src/utils/parseQuery.d.ts +0 -23
  117. package/src/validation/ValibotAdapter.d.ts +0 -30
  118. package/src/validation/ValidatorAdapter.d.ts +0 -54
  119. 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
+ }