@morojs/moro 1.3.0 → 1.5.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.
- package/README.md +61 -7
- package/dist/core/config/index.d.ts +0 -1
- package/dist/core/config/index.js +0 -4
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/loader.js +219 -226
- package/dist/core/config/loader.js.map +1 -1
- package/dist/core/config/schema.d.ts +30 -335
- package/dist/core/config/schema.js +133 -224
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/config/utils.d.ts +3 -2
- package/dist/core/config/utils.js.map +1 -1
- package/dist/core/config/validation.d.ts +17 -0
- package/dist/core/config/validation.js +129 -0
- package/dist/core/config/validation.js.map +1 -0
- package/dist/core/docs/index.js +1 -1
- package/dist/core/docs/index.js.map +1 -1
- package/dist/core/docs/openapi-generator.js +6 -6
- package/dist/core/docs/openapi-generator.js.map +1 -1
- package/dist/core/docs/schema-to-openapi.d.ts +7 -0
- package/dist/core/docs/schema-to-openapi.js +124 -0
- package/dist/core/docs/schema-to-openapi.js.map +1 -0
- package/dist/core/docs/simple-docs.js +5 -5
- package/dist/core/docs/zod-to-openapi.d.ts +4 -3
- package/dist/core/docs/zod-to-openapi.js +28 -0
- package/dist/core/docs/zod-to-openapi.js.map +1 -1
- package/dist/core/framework.d.ts +29 -6
- package/dist/core/framework.js +117 -18
- package/dist/core/framework.js.map +1 -1
- package/dist/core/networking/adapters/index.d.ts +3 -0
- package/dist/core/networking/adapters/index.js +10 -0
- package/dist/core/networking/adapters/index.js.map +1 -0
- package/dist/core/networking/adapters/socketio-adapter.d.ts +16 -0
- package/dist/core/networking/adapters/socketio-adapter.js +244 -0
- package/dist/core/networking/adapters/socketio-adapter.js.map +1 -0
- package/dist/core/networking/adapters/ws-adapter.d.ts +54 -0
- package/dist/core/networking/adapters/ws-adapter.js +383 -0
- package/dist/core/networking/adapters/ws-adapter.js.map +1 -0
- package/dist/core/networking/websocket-adapter.d.ts +171 -0
- package/dist/core/networking/websocket-adapter.js +5 -0
- package/dist/core/networking/websocket-adapter.js.map +1 -0
- package/dist/core/networking/websocket-manager.d.ts +53 -17
- package/dist/core/networking/websocket-manager.js +184 -126
- package/dist/core/networking/websocket-manager.js.map +1 -1
- package/dist/core/routing/index.d.ts +13 -13
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/validation/adapters.d.ts +51 -0
- package/dist/core/validation/adapters.js +135 -0
- package/dist/core/validation/adapters.js.map +1 -0
- package/dist/core/validation/index.d.ts +12 -11
- package/dist/core/validation/index.js +32 -26
- package/dist/core/validation/index.js.map +1 -1
- package/dist/core/validation/schema-interface.d.ts +36 -0
- package/dist/core/validation/schema-interface.js +68 -0
- package/dist/core/validation/schema-interface.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +23 -4
- package/dist/index.js.map +1 -1
- package/dist/moro.js +24 -17
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +146 -0
- package/dist/types/config.js +4 -0
- package/dist/types/config.js.map +1 -0
- package/package.json +30 -7
- package/src/core/config/index.ts +0 -3
- package/src/core/config/loader.ts +571 -247
- package/src/core/config/schema.ts +146 -279
- package/src/core/config/utils.ts +1 -2
- package/src/core/config/validation.ts +140 -0
- package/src/core/docs/index.ts +1 -1
- package/src/core/docs/openapi-generator.ts +7 -6
- package/src/core/docs/schema-to-openapi.ts +148 -0
- package/src/core/docs/simple-docs.ts +5 -5
- package/src/core/docs/zod-to-openapi.ts +52 -20
- package/src/core/framework.ts +121 -28
- package/src/core/networking/adapters/index.ts +16 -0
- package/src/core/networking/adapters/socketio-adapter.ts +252 -0
- package/src/core/networking/adapters/ws-adapter.ts +425 -0
- package/src/core/networking/websocket-adapter.ts +217 -0
- package/src/core/networking/websocket-manager.ts +201 -143
- package/src/core/routing/index.ts +13 -13
- package/src/core/validation/adapters.ts +147 -0
- package/src/core/validation/index.ts +54 -38
- package/src/core/validation/schema-interface.ts +100 -0
- package/src/index.ts +36 -3
- package/src/moro.ts +27 -17
- package/src/types/config.ts +157 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Intelligent Routing System for Moro Framework
|
|
2
2
|
// Schema-first with automatic middleware ordering and chainable API
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { ValidationSchema } from '../validation/schema-interface';
|
|
5
5
|
import { HttpRequest, HttpResponse } from '../http';
|
|
6
6
|
import { createFrameworkLogger } from '../logger';
|
|
7
7
|
|
|
@@ -18,10 +18,10 @@ export type Middleware = (
|
|
|
18
18
|
|
|
19
19
|
// Configuration interfaces
|
|
20
20
|
export interface ValidationConfig {
|
|
21
|
-
body?:
|
|
22
|
-
query?:
|
|
23
|
-
params?:
|
|
24
|
-
headers?:
|
|
21
|
+
body?: ValidationSchema;
|
|
22
|
+
query?: ValidationSchema;
|
|
23
|
+
params?: ValidationSchema;
|
|
24
|
+
headers?: ValidationSchema;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface AuthConfig {
|
|
@@ -98,10 +98,10 @@ export interface ValidatedRequest<T = any> extends HttpRequest {
|
|
|
98
98
|
export interface RouteBuilder {
|
|
99
99
|
// Validation methods
|
|
100
100
|
validate(config: ValidationConfig): RouteBuilder;
|
|
101
|
-
body<T>(schema:
|
|
102
|
-
query<T>(schema:
|
|
103
|
-
params<T>(schema:
|
|
104
|
-
headers<T>(schema:
|
|
101
|
+
body<T>(schema: ValidationSchema<T>): RouteBuilder;
|
|
102
|
+
query<T>(schema: ValidationSchema<T>): RouteBuilder;
|
|
103
|
+
params<T>(schema: ValidationSchema<T>): RouteBuilder;
|
|
104
|
+
headers<T>(schema: ValidationSchema<T>): RouteBuilder;
|
|
105
105
|
|
|
106
106
|
// Security/Auth methods
|
|
107
107
|
auth(config: AuthConfig): RouteBuilder;
|
|
@@ -148,25 +148,25 @@ export class IntelligentRouteBuilder implements RouteBuilder {
|
|
|
148
148
|
return this;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
body<T>(schema:
|
|
151
|
+
body<T>(schema: ValidationSchema<T>): RouteBuilder {
|
|
152
152
|
if (!this.schema.validation) this.schema.validation = {};
|
|
153
153
|
this.schema.validation.body = schema;
|
|
154
154
|
return this;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
query<T>(schema:
|
|
157
|
+
query<T>(schema: ValidationSchema<T>): RouteBuilder {
|
|
158
158
|
if (!this.schema.validation) this.schema.validation = {};
|
|
159
159
|
this.schema.validation.query = schema;
|
|
160
160
|
return this;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
params<T>(schema:
|
|
163
|
+
params<T>(schema: ValidationSchema<T>): RouteBuilder {
|
|
164
164
|
if (!this.schema.validation) this.schema.validation = {};
|
|
165
165
|
this.schema.validation.params = schema;
|
|
166
166
|
return this;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
headers<T>(schema:
|
|
169
|
+
headers<T>(schema: ValidationSchema<T>): RouteBuilder {
|
|
170
170
|
if (!this.schema.validation) this.schema.validation = {};
|
|
171
171
|
this.schema.validation.headers = schema;
|
|
172
172
|
return this;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Validation Library Adapters for Moro Framework
|
|
2
|
+
// Makes Joi, Yup, and other libraries compatible with ValidationSchema interface
|
|
3
|
+
|
|
4
|
+
import { ValidationSchema, ValidationError, normalizeValidationError } from './schema-interface';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Zod Compatibility Check
|
|
8
|
+
* Zod ALREADY implements ValidationSchema interface natively!
|
|
9
|
+
* No adapter needed - it just works.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Joi Adapter - makes Joi schemas compatible with ValidationSchema
|
|
14
|
+
*/
|
|
15
|
+
export class JoiAdapter<T = any> implements ValidationSchema<T> {
|
|
16
|
+
constructor(private joiSchema: any) {
|
|
17
|
+
if (!joiSchema || typeof joiSchema.validateAsync !== 'function') {
|
|
18
|
+
throw new Error('Invalid Joi schema provided to JoiAdapter');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async parseAsync(data: unknown): Promise<T> {
|
|
23
|
+
try {
|
|
24
|
+
const result = await this.joiSchema.validateAsync(data, { abortEarly: false });
|
|
25
|
+
return result as T;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw normalizeValidationError(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Yup Adapter - makes Yup schemas compatible with ValidationSchema
|
|
34
|
+
*/
|
|
35
|
+
export class YupAdapter<T = any> implements ValidationSchema<T> {
|
|
36
|
+
constructor(private yupSchema: any) {
|
|
37
|
+
if (!yupSchema || typeof yupSchema.validate !== 'function') {
|
|
38
|
+
throw new Error('Invalid Yup schema provided to YupAdapter');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async parseAsync(data: unknown): Promise<T> {
|
|
43
|
+
try {
|
|
44
|
+
const result = await this.yupSchema.validate(data, { abortEarly: false });
|
|
45
|
+
return result as T;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw normalizeValidationError(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Custom Validation Function Adapter
|
|
54
|
+
* Allows users to use simple validation functions
|
|
55
|
+
*/
|
|
56
|
+
export class FunctionAdapter<T = any> implements ValidationSchema<T> {
|
|
57
|
+
constructor(
|
|
58
|
+
private validateFn: (data: unknown) => T | Promise<T>,
|
|
59
|
+
private name: string = 'custom'
|
|
60
|
+
) {
|
|
61
|
+
if (typeof validateFn !== 'function') {
|
|
62
|
+
throw new Error('Validation function is required for FunctionAdapter');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async parseAsync(data: unknown): Promise<T> {
|
|
67
|
+
try {
|
|
68
|
+
return await this.validateFn(data);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new ValidationError([
|
|
71
|
+
{
|
|
72
|
+
path: [],
|
|
73
|
+
message: error instanceof Error ? error.message : String(error),
|
|
74
|
+
code: this.name,
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Class Validator Adapter (for TypeScript decorators)
|
|
83
|
+
*/
|
|
84
|
+
export class ClassValidatorAdapter<T extends object = any> implements ValidationSchema<T> {
|
|
85
|
+
constructor(
|
|
86
|
+
private ClassType: new () => T,
|
|
87
|
+
private validate?: (obj: any) => Promise<any[]>
|
|
88
|
+
) {
|
|
89
|
+
if (typeof ClassType !== 'function') {
|
|
90
|
+
throw new Error('Class constructor is required for ClassValidatorAdapter');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async parseAsync(data: unknown): Promise<T> {
|
|
95
|
+
try {
|
|
96
|
+
const instance = Object.assign(new this.ClassType(), data as Record<string, any>);
|
|
97
|
+
|
|
98
|
+
if (this.validate) {
|
|
99
|
+
const errors = await this.validate(instance);
|
|
100
|
+
if (errors && errors.length > 0) {
|
|
101
|
+
throw new ValidationError(
|
|
102
|
+
errors.map((error: any, index: number) => ({
|
|
103
|
+
path: error.property ? [error.property] : [index],
|
|
104
|
+
message: Object.values(error.constraints || {}).join(', ') || 'Validation failed',
|
|
105
|
+
code: 'class_validator',
|
|
106
|
+
}))
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return instance as T;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error instanceof ValidationError) throw error;
|
|
114
|
+
throw normalizeValidationError(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Utility functions for creating adapters
|
|
121
|
+
*/
|
|
122
|
+
export function joi<T = any>(joiSchema: any): ValidationSchema<T> {
|
|
123
|
+
return new JoiAdapter<T>(joiSchema);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function yup<T = any>(yupSchema: any): ValidationSchema<T> {
|
|
127
|
+
return new YupAdapter<T>(yupSchema);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function fn<T = any>(
|
|
131
|
+
validateFn: (data: unknown) => T | Promise<T>,
|
|
132
|
+
name?: string
|
|
133
|
+
): ValidationSchema<T> {
|
|
134
|
+
return new FunctionAdapter<T>(validateFn, name);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function classValidator<T extends object = any>(
|
|
138
|
+
ClassType: new () => T,
|
|
139
|
+
validate?: (obj: any) => Promise<any[]>
|
|
140
|
+
): ValidationSchema<T> {
|
|
141
|
+
return new ClassValidatorAdapter<T>(ClassType, validate);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Type helpers
|
|
145
|
+
export type JoiSchema<T> = ValidationSchema<T>;
|
|
146
|
+
export type YupSchema<T> = ValidationSchema<T>;
|
|
147
|
+
export type CustomValidator<T> = ValidationSchema<T>;
|
|
@@ -1,28 +1,36 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Universal Validation System for Moro Framework
|
|
2
|
+
// Works with Zod, Joi, Yup, and any validation library via adapters
|
|
3
3
|
|
|
4
|
-
import { z, ZodSchema, ZodError } from 'zod';
|
|
5
4
|
import { HttpRequest, HttpResponse } from '../http';
|
|
6
5
|
import { createFrameworkLogger } from '../logger';
|
|
6
|
+
import {
|
|
7
|
+
ValidationSchema,
|
|
8
|
+
ValidationError,
|
|
9
|
+
normalizeValidationError,
|
|
10
|
+
InferSchemaType,
|
|
11
|
+
} from './schema-interface';
|
|
12
|
+
|
|
13
|
+
// Re-export zod if available (for backward compatibility)
|
|
14
|
+
// The dynamic import is handled in the main index.ts
|
|
7
15
|
|
|
8
16
|
const logger = createFrameworkLogger('Validation');
|
|
9
17
|
|
|
10
|
-
//
|
|
18
|
+
// Universal validation configuration interface
|
|
11
19
|
export interface ValidationConfig {
|
|
12
|
-
body?:
|
|
13
|
-
query?:
|
|
14
|
-
params?:
|
|
15
|
-
headers?:
|
|
20
|
+
body?: ValidationSchema;
|
|
21
|
+
query?: ValidationSchema;
|
|
22
|
+
params?: ValidationSchema;
|
|
23
|
+
headers?: ValidationSchema;
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
// Validation result types
|
|
19
27
|
export interface ValidationResult<T = any> {
|
|
20
28
|
success: boolean;
|
|
21
29
|
data?: T;
|
|
22
|
-
errors?:
|
|
30
|
+
errors?: ValidationErrorDetail[];
|
|
23
31
|
}
|
|
24
32
|
|
|
25
|
-
export interface
|
|
33
|
+
export interface ValidationErrorDetail {
|
|
26
34
|
field: string;
|
|
27
35
|
message: string;
|
|
28
36
|
code: string;
|
|
@@ -110,9 +118,9 @@ export function validate<TBody = any, TQuery = any, TParams = any>(
|
|
|
110
118
|
};
|
|
111
119
|
}
|
|
112
120
|
|
|
113
|
-
// Validate individual field
|
|
121
|
+
// Validate individual field using universal schema interface
|
|
114
122
|
async function validateField(
|
|
115
|
-
schema:
|
|
123
|
+
schema: ValidationSchema,
|
|
116
124
|
data: any,
|
|
117
125
|
fieldName: string
|
|
118
126
|
): Promise<ValidationResult> {
|
|
@@ -123,32 +131,32 @@ async function validateField(
|
|
|
123
131
|
data: validated,
|
|
124
132
|
};
|
|
125
133
|
} catch (error) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
success: false,
|
|
141
|
-
errors,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
134
|
+
const normalizedError = normalizeValidationError(error);
|
|
135
|
+
const errors: ValidationErrorDetail[] = normalizedError.issues.map(issue => ({
|
|
136
|
+
field: issue.path.length > 0 ? issue.path.join('.') : fieldName,
|
|
137
|
+
message: issue.message,
|
|
138
|
+
code: issue.code,
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
logger.debug('Field validation failed', 'ValidationFailed', {
|
|
142
|
+
field: fieldName,
|
|
143
|
+
errors: errors.length,
|
|
144
|
+
details: errors,
|
|
145
|
+
});
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
errors,
|
|
150
|
+
};
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
// Send validation error response
|
|
151
|
-
function sendValidationError(
|
|
155
|
+
function sendValidationError(
|
|
156
|
+
res: HttpResponse,
|
|
157
|
+
errors: ValidationErrorDetail[],
|
|
158
|
+
field: string
|
|
159
|
+
): void {
|
|
152
160
|
logger.debug('Sending validation error response', 'ValidationResponse', {
|
|
153
161
|
field,
|
|
154
162
|
errorCount: errors.length,
|
|
@@ -163,19 +171,19 @@ function sendValidationError(res: HttpResponse, errors: ValidationError[], field
|
|
|
163
171
|
}
|
|
164
172
|
|
|
165
173
|
// Convenience functions for single-field validation
|
|
166
|
-
export function body<T>(schema:
|
|
174
|
+
export function body<T>(schema: ValidationSchema<T>) {
|
|
167
175
|
return (handler: (req: ValidatedRequest<T>, res: HttpResponse) => any | Promise<any>) => {
|
|
168
176
|
return validate({ body: schema }, handler);
|
|
169
177
|
};
|
|
170
178
|
}
|
|
171
179
|
|
|
172
|
-
export function query<T>(schema:
|
|
180
|
+
export function query<T>(schema: ValidationSchema<T>) {
|
|
173
181
|
return (handler: (req: ValidatedRequest<any>, res: HttpResponse) => any | Promise<any>) => {
|
|
174
182
|
return validate({ query: schema }, handler);
|
|
175
183
|
};
|
|
176
184
|
}
|
|
177
185
|
|
|
178
|
-
export function params<T>(schema:
|
|
186
|
+
export function params<T>(schema: ValidationSchema<T>) {
|
|
179
187
|
return (handler: (req: ValidatedRequest<any>, res: HttpResponse) => any | Promise<any>) => {
|
|
180
188
|
return validate({ params: schema }, handler);
|
|
181
189
|
};
|
|
@@ -186,5 +194,13 @@ export function combineSchemas(schemas: ValidationConfig): ValidationConfig {
|
|
|
186
194
|
return schemas;
|
|
187
195
|
}
|
|
188
196
|
|
|
189
|
-
// Re-export
|
|
190
|
-
export {
|
|
197
|
+
// Re-export common validation tools
|
|
198
|
+
export {
|
|
199
|
+
ValidationSchema,
|
|
200
|
+
ValidationError,
|
|
201
|
+
normalizeValidationError,
|
|
202
|
+
InferSchemaType,
|
|
203
|
+
} from './schema-interface';
|
|
204
|
+
export { joi, yup, fn as customValidator, classValidator } from './adapters';
|
|
205
|
+
|
|
206
|
+
// Note: z is re-exported from main index.ts with dynamic import
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Universal Validation Schema Interface for Moro Framework
|
|
2
|
+
// Allows Zod, Joi, Yup, and other validation libraries to work seamlessly
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Standard validation error structure
|
|
6
|
+
*/
|
|
7
|
+
export interface ValidationIssue {
|
|
8
|
+
path: (string | number)[];
|
|
9
|
+
message: string;
|
|
10
|
+
code: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Standard validation error class
|
|
15
|
+
* Compatible with ZodError structure
|
|
16
|
+
*/
|
|
17
|
+
export class ValidationError extends Error {
|
|
18
|
+
public readonly issues: ValidationIssue[];
|
|
19
|
+
|
|
20
|
+
constructor(issues: ValidationIssue[]) {
|
|
21
|
+
const message = `Validation failed: ${issues.map(i => i.message).join(', ')}`;
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'ValidationError';
|
|
24
|
+
this.issues = issues;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Universal validation schema interface
|
|
30
|
+
* This is what Zod naturally implements! No breaking changes needed.
|
|
31
|
+
*/
|
|
32
|
+
export interface ValidationSchema<T = any> {
|
|
33
|
+
/**
|
|
34
|
+
* Parse data asynchronously and return validated result
|
|
35
|
+
* Throws ValidationError on validation failure
|
|
36
|
+
*/
|
|
37
|
+
parseAsync(data: unknown): Promise<T>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if an object implements the ValidationSchema interface
|
|
42
|
+
*/
|
|
43
|
+
export function isValidationSchema(obj: any): obj is ValidationSchema {
|
|
44
|
+
return obj && typeof obj.parseAsync === 'function';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Convert various error formats to our standard ValidationError
|
|
49
|
+
*/
|
|
50
|
+
export function normalizeValidationError(error: any): ValidationError {
|
|
51
|
+
// Already our format
|
|
52
|
+
if (error instanceof ValidationError) {
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Zod error format
|
|
57
|
+
if (error && error.issues && Array.isArray(error.issues)) {
|
|
58
|
+
return new ValidationError(
|
|
59
|
+
error.issues.map((issue: any) => ({
|
|
60
|
+
path: issue.path || [],
|
|
61
|
+
message: issue.message || 'Validation failed',
|
|
62
|
+
code: issue.code || 'invalid',
|
|
63
|
+
}))
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Joi error format
|
|
68
|
+
if (error && error.details && Array.isArray(error.details)) {
|
|
69
|
+
return new ValidationError(
|
|
70
|
+
error.details.map((detail: any) => ({
|
|
71
|
+
path: detail.path || [],
|
|
72
|
+
message: detail.message || 'Validation failed',
|
|
73
|
+
code: detail.type || 'invalid',
|
|
74
|
+
}))
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Yup error format
|
|
79
|
+
if (error && error.errors && Array.isArray(error.errors)) {
|
|
80
|
+
return new ValidationError(
|
|
81
|
+
error.errors.map((msg: string, index: number) => ({
|
|
82
|
+
path: error.path ? [error.path] : [index],
|
|
83
|
+
message: msg,
|
|
84
|
+
code: error.type || 'invalid',
|
|
85
|
+
}))
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Generic error
|
|
90
|
+
return new ValidationError([
|
|
91
|
+
{
|
|
92
|
+
path: [],
|
|
93
|
+
message: error.message || String(error),
|
|
94
|
+
code: 'unknown',
|
|
95
|
+
},
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Type helper for inferring types from validation schemas
|
|
100
|
+
export type InferSchemaType<T> = T extends ValidationSchema<infer U> ? U : never;
|
package/src/index.ts
CHANGED
|
@@ -105,15 +105,34 @@ export type {
|
|
|
105
105
|
// Logger System
|
|
106
106
|
export { createFrameworkLogger, logger } from './core/logger';
|
|
107
107
|
|
|
108
|
-
// Validation System
|
|
109
|
-
export { validate, body, query, params, combineSchemas
|
|
108
|
+
// Universal Validation System
|
|
109
|
+
export { validate, body, query, params, combineSchemas } from './core/validation';
|
|
110
|
+
|
|
111
|
+
// Dynamic Zod export (optional dependency)
|
|
112
|
+
let z: any;
|
|
113
|
+
try {
|
|
114
|
+
z = require('zod').z;
|
|
115
|
+
} catch {
|
|
116
|
+
// Zod not available - that's fine!
|
|
117
|
+
z = undefined;
|
|
118
|
+
}
|
|
119
|
+
export { z };
|
|
110
120
|
export type {
|
|
111
121
|
ValidationConfig,
|
|
112
122
|
ValidationResult,
|
|
113
|
-
|
|
123
|
+
ValidationErrorDetail,
|
|
114
124
|
ValidatedRequest,
|
|
115
125
|
} from './core/validation';
|
|
116
126
|
|
|
127
|
+
// Universal Validation Interfaces and Adapters
|
|
128
|
+
export type {
|
|
129
|
+
ValidationSchema,
|
|
130
|
+
ValidationError,
|
|
131
|
+
InferSchemaType,
|
|
132
|
+
} from './core/validation/schema-interface';
|
|
133
|
+
export { normalizeValidationError } from './core/validation/schema-interface';
|
|
134
|
+
export { joi, yup, fn as customValidator, classValidator } from './core/validation/adapters';
|
|
135
|
+
|
|
117
136
|
// Module System
|
|
118
137
|
export {
|
|
119
138
|
defineModule,
|
|
@@ -123,6 +142,20 @@ export {
|
|
|
123
142
|
} from './core/modules';
|
|
124
143
|
export type { ModuleDefinition, ModuleRoute, ModuleSocket, ModuleConfig } from './types/module';
|
|
125
144
|
|
|
145
|
+
// WebSocket Adapter System
|
|
146
|
+
export type {
|
|
147
|
+
WebSocketAdapter,
|
|
148
|
+
WebSocketAdapterOptions,
|
|
149
|
+
WebSocketNamespace,
|
|
150
|
+
WebSocketConnection,
|
|
151
|
+
WebSocketEmitter,
|
|
152
|
+
WebSocketMiddleware,
|
|
153
|
+
WebSocketEventHandler,
|
|
154
|
+
} from './core/networking/websocket-adapter';
|
|
155
|
+
|
|
156
|
+
// Built-in WebSocket Adapters
|
|
157
|
+
export { SocketIOAdapter, WSAdapter } from './core/networking/adapters';
|
|
158
|
+
|
|
126
159
|
// Intelligent Routing System
|
|
127
160
|
export { createRoute, defineRoute, EXECUTION_PHASES } from './core/routing';
|
|
128
161
|
export { IntelligentRoutingManager, RouteRegistry } from './core/routing/app-integration';
|
package/src/moro.ts
CHANGED
|
@@ -387,10 +387,19 @@ export class Moro extends EventEmitter {
|
|
|
387
387
|
|
|
388
388
|
// WebSocket helper with events
|
|
389
389
|
websocket(namespace: string, handlers: Record<string, Function>) {
|
|
390
|
+
const adapter = this.coreFramework.getWebSocketAdapter();
|
|
391
|
+
if (!adapter) {
|
|
392
|
+
throw new Error(
|
|
393
|
+
'WebSocket features require a WebSocket adapter. Install socket.io or configure an adapter:\n' +
|
|
394
|
+
'npm install socket.io\n' +
|
|
395
|
+
'or\n' +
|
|
396
|
+
'new Moro({ websocket: { adapter: new SocketIOAdapter() } })'
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
390
400
|
this.emit('websocket:registering', { namespace, handlers });
|
|
391
401
|
|
|
392
|
-
const
|
|
393
|
-
const ns = io.of(namespace);
|
|
402
|
+
const ns = adapter.createNamespace(namespace);
|
|
394
403
|
|
|
395
404
|
Object.entries(handlers).forEach(([event, handler]) => {
|
|
396
405
|
ns.on('connection', socket => {
|
|
@@ -761,25 +770,26 @@ export class Moro extends EventEmitter {
|
|
|
761
770
|
// Enhance request with events property for direct routes
|
|
762
771
|
req.events = this.eventBus;
|
|
763
772
|
|
|
764
|
-
//
|
|
773
|
+
// Universal validation middleware (works with any ValidationSchema)
|
|
765
774
|
if (route.validation) {
|
|
766
775
|
try {
|
|
767
|
-
const validated = route.validation.
|
|
776
|
+
const validated = await route.validation.parseAsync(req.body);
|
|
768
777
|
req.body = validated;
|
|
769
778
|
} catch (error: any) {
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
779
|
+
// Handle universal validation errors
|
|
780
|
+
const { normalizeValidationError } = require('./core/validation/schema-interface');
|
|
781
|
+
const normalizedError = normalizeValidationError(error);
|
|
782
|
+
res.status(400).json({
|
|
783
|
+
success: false,
|
|
784
|
+
error: 'Validation failed',
|
|
785
|
+
details: normalizedError.issues.map((issue: any) => ({
|
|
786
|
+
field: issue.path.length > 0 ? issue.path.join('.') : 'body',
|
|
787
|
+
message: issue.message,
|
|
788
|
+
code: issue.code,
|
|
789
|
+
})),
|
|
790
|
+
requestId: req.requestId,
|
|
791
|
+
});
|
|
792
|
+
return;
|
|
783
793
|
}
|
|
784
794
|
}
|
|
785
795
|
|