@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,283 +1,150 @@
|
|
|
1
1
|
// Core Configuration Schema for Moro Framework
|
|
2
|
-
import { z } from 'zod';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.default(false)
|
|
125
|
-
.describe('Skip failed requests in rate limit counting'),
|
|
126
|
-
}),
|
|
127
|
-
|
|
128
|
-
validation: z.object({
|
|
129
|
-
enabled: z.coerce.boolean().default(true).describe('Enable validation by default'),
|
|
130
|
-
|
|
131
|
-
stripUnknown: z.coerce
|
|
132
|
-
.boolean()
|
|
133
|
-
.default(true)
|
|
134
|
-
.describe('Strip unknown properties from validated data'),
|
|
135
|
-
|
|
136
|
-
abortEarly: z.coerce.boolean().default(false).describe('Stop validation on first error'),
|
|
137
|
-
}),
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Logging Configuration Schema
|
|
141
|
-
const LoggingConfigSchema = z.object({
|
|
142
|
-
level: z
|
|
143
|
-
.enum(['debug', 'info', 'warn', 'error', 'fatal'])
|
|
144
|
-
.default('info')
|
|
145
|
-
.describe('Minimum log level'),
|
|
146
|
-
|
|
147
|
-
format: z.enum(['pretty', 'json', 'compact']).default('pretty').describe('Log output format'),
|
|
148
|
-
|
|
149
|
-
enableColors: z.coerce.boolean().default(true).describe('Enable colored log output'),
|
|
150
|
-
|
|
151
|
-
enableTimestamp: z.coerce.boolean().default(true).describe('Include timestamp in logs'),
|
|
152
|
-
|
|
153
|
-
enableContext: z.coerce.boolean().default(true).describe('Include context information in logs'),
|
|
154
|
-
|
|
155
|
-
outputs: z.object({
|
|
156
|
-
console: z.coerce.boolean().default(true),
|
|
157
|
-
file: z.object({
|
|
158
|
-
enabled: z.coerce.boolean().default(false),
|
|
159
|
-
path: z.string().default('./logs/moro.log'),
|
|
160
|
-
maxSize: z.string().default('10MB'),
|
|
161
|
-
maxFiles: z.coerce.number().default(5),
|
|
162
|
-
}),
|
|
163
|
-
webhook: z.object({
|
|
164
|
-
enabled: z.coerce.boolean().default(false),
|
|
165
|
-
url: z.string().url().optional(),
|
|
166
|
-
headers: z.record(z.string(), z.string()).default({}),
|
|
167
|
-
}),
|
|
168
|
-
}),
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// Security Configuration Schema
|
|
172
|
-
const SecurityConfigSchema = z.object({
|
|
173
|
-
cors: z.object({
|
|
174
|
-
enabled: z.coerce.boolean().default(true),
|
|
175
|
-
origin: z.union([z.string(), z.array(z.string()), z.boolean()]).default('*'),
|
|
176
|
-
methods: z.array(z.string()).default(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']),
|
|
177
|
-
allowedHeaders: z.array(z.string()).default(['Content-Type', 'Authorization']),
|
|
178
|
-
credentials: z.coerce.boolean().default(false),
|
|
179
|
-
}),
|
|
180
|
-
|
|
181
|
-
helmet: z.object({
|
|
182
|
-
enabled: z.coerce.boolean().default(true),
|
|
183
|
-
contentSecurityPolicy: z.coerce.boolean().default(true),
|
|
184
|
-
hsts: z.coerce.boolean().default(true),
|
|
185
|
-
noSniff: z.coerce.boolean().default(true),
|
|
186
|
-
frameguard: z.coerce.boolean().default(true),
|
|
187
|
-
}),
|
|
188
|
-
|
|
189
|
-
rateLimit: z.object({
|
|
190
|
-
global: z.object({
|
|
191
|
-
enabled: z.coerce.boolean().default(false),
|
|
192
|
-
requests: z.coerce.number().min(1).default(1000),
|
|
193
|
-
window: z.coerce.number().min(1000).default(60000),
|
|
194
|
-
}),
|
|
195
|
-
}),
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// External Services Configuration Schema
|
|
199
|
-
const ExternalServicesConfigSchema = z.object({
|
|
200
|
-
stripe: z
|
|
201
|
-
.object({
|
|
202
|
-
secretKey: z.string().optional(),
|
|
203
|
-
publishableKey: z.string().optional(),
|
|
204
|
-
webhookSecret: z.string().optional(),
|
|
205
|
-
apiVersion: z.string().default('2023-10-16'),
|
|
206
|
-
})
|
|
207
|
-
.optional(),
|
|
208
|
-
|
|
209
|
-
paypal: z
|
|
210
|
-
.object({
|
|
211
|
-
clientId: z.string().optional(),
|
|
212
|
-
clientSecret: z.string().optional(),
|
|
213
|
-
webhookId: z.string().optional(),
|
|
214
|
-
environment: z.enum(['sandbox', 'production']).default('sandbox'),
|
|
215
|
-
})
|
|
216
|
-
.optional(),
|
|
217
|
-
|
|
218
|
-
smtp: z
|
|
219
|
-
.object({
|
|
220
|
-
host: z.string().optional(),
|
|
221
|
-
port: z.coerce.number().min(1).max(65535).default(587),
|
|
222
|
-
secure: z.coerce.boolean().default(false),
|
|
223
|
-
username: z.string().optional(),
|
|
224
|
-
password: z.string().optional(),
|
|
225
|
-
})
|
|
226
|
-
.optional(),
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Performance Configuration Schema
|
|
230
|
-
const PerformanceConfigSchema = z.object({
|
|
231
|
-
compression: z.object({
|
|
232
|
-
enabled: z.coerce.boolean().default(true),
|
|
233
|
-
level: z.coerce.number().min(1).max(9).default(6),
|
|
234
|
-
threshold: z.coerce.number().min(0).default(1024),
|
|
235
|
-
}),
|
|
236
|
-
|
|
237
|
-
circuitBreaker: z.object({
|
|
238
|
-
enabled: z.coerce.boolean().default(true),
|
|
239
|
-
failureThreshold: z.coerce.number().min(1).default(5),
|
|
240
|
-
resetTimeout: z.coerce.number().min(1000).default(60000),
|
|
241
|
-
monitoringPeriod: z.coerce.number().min(1000).default(10000),
|
|
242
|
-
}),
|
|
243
|
-
|
|
244
|
-
clustering: z.object({
|
|
245
|
-
enabled: z.coerce.boolean().default(false),
|
|
246
|
-
workers: z.union([z.coerce.number().min(1), z.literal('auto')]).default(1),
|
|
247
|
-
}),
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// Main Configuration Schema
|
|
251
|
-
export const ConfigSchema = z.object({
|
|
252
|
-
server: ServerConfigSchema,
|
|
253
|
-
serviceDiscovery: ServiceDiscoveryConfigSchema,
|
|
254
|
-
database: DatabaseConfigSchema,
|
|
255
|
-
modules: ModuleDefaultsConfigSchema,
|
|
256
|
-
logging: LoggingConfigSchema,
|
|
257
|
-
security: SecurityConfigSchema,
|
|
258
|
-
external: ExternalServicesConfigSchema,
|
|
259
|
-
performance: PerformanceConfigSchema,
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// Inferred TypeScript types
|
|
263
|
-
export type AppConfig = z.infer<typeof ConfigSchema>;
|
|
264
|
-
export type ServerConfig = z.infer<typeof ServerConfigSchema>;
|
|
265
|
-
export type ServiceDiscoveryConfig = z.infer<typeof ServiceDiscoveryConfigSchema>;
|
|
266
|
-
export type DatabaseConfig = z.infer<typeof DatabaseConfigSchema>;
|
|
267
|
-
export type ModuleDefaultsConfig = z.infer<typeof ModuleDefaultsConfigSchema>;
|
|
268
|
-
export type LoggingConfig = z.infer<typeof LoggingConfigSchema>;
|
|
269
|
-
export type SecurityConfig = z.infer<typeof SecurityConfigSchema>;
|
|
270
|
-
export type ExternalServicesConfig = z.infer<typeof ExternalServicesConfigSchema>;
|
|
271
|
-
export type PerformanceConfig = z.infer<typeof PerformanceConfigSchema>;
|
|
3
|
+
import { AppConfig } from '../../types/config';
|
|
4
|
+
|
|
5
|
+
// Default configuration values
|
|
6
|
+
export const DEFAULT_CONFIG: AppConfig = {
|
|
7
|
+
server: {
|
|
8
|
+
port: 3001,
|
|
9
|
+
host: 'localhost',
|
|
10
|
+
environment: 'development',
|
|
11
|
+
maxConnections: 1000,
|
|
12
|
+
timeout: 30000,
|
|
13
|
+
},
|
|
14
|
+
serviceDiscovery: {
|
|
15
|
+
enabled: false,
|
|
16
|
+
type: 'memory',
|
|
17
|
+
consulUrl: 'http://localhost:8500',
|
|
18
|
+
kubernetesNamespace: 'default',
|
|
19
|
+
healthCheckInterval: 30000,
|
|
20
|
+
retryAttempts: 3,
|
|
21
|
+
},
|
|
22
|
+
database: {
|
|
23
|
+
redis: {
|
|
24
|
+
url: 'redis://localhost:6379',
|
|
25
|
+
maxRetries: 3,
|
|
26
|
+
retryDelay: 1000,
|
|
27
|
+
keyPrefix: 'moro:',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
modules: {
|
|
31
|
+
cache: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
defaultTtl: 300,
|
|
34
|
+
maxSize: 1000,
|
|
35
|
+
strategy: 'lru',
|
|
36
|
+
},
|
|
37
|
+
rateLimit: {
|
|
38
|
+
enabled: true,
|
|
39
|
+
defaultRequests: 100,
|
|
40
|
+
defaultWindow: 60000,
|
|
41
|
+
skipSuccessfulRequests: false,
|
|
42
|
+
skipFailedRequests: false,
|
|
43
|
+
},
|
|
44
|
+
validation: {
|
|
45
|
+
enabled: true,
|
|
46
|
+
stripUnknown: true,
|
|
47
|
+
abortEarly: false,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
logging: {
|
|
51
|
+
level: 'info',
|
|
52
|
+
format: 'pretty',
|
|
53
|
+
enableColors: true,
|
|
54
|
+
enableTimestamp: true,
|
|
55
|
+
enableContext: true,
|
|
56
|
+
outputs: {
|
|
57
|
+
console: true,
|
|
58
|
+
file: {
|
|
59
|
+
enabled: false,
|
|
60
|
+
path: './logs/moro.log',
|
|
61
|
+
maxSize: '10MB',
|
|
62
|
+
maxFiles: 5,
|
|
63
|
+
},
|
|
64
|
+
webhook: {
|
|
65
|
+
enabled: false,
|
|
66
|
+
headers: {},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
security: {
|
|
71
|
+
cors: {
|
|
72
|
+
enabled: true,
|
|
73
|
+
origin: '*',
|
|
74
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
|
75
|
+
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
76
|
+
credentials: false,
|
|
77
|
+
},
|
|
78
|
+
helmet: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
contentSecurityPolicy: true,
|
|
81
|
+
hsts: true,
|
|
82
|
+
noSniff: true,
|
|
83
|
+
frameguard: true,
|
|
84
|
+
},
|
|
85
|
+
rateLimit: {
|
|
86
|
+
global: {
|
|
87
|
+
enabled: false,
|
|
88
|
+
requests: 1000,
|
|
89
|
+
window: 60000,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
external: {
|
|
94
|
+
stripe: {
|
|
95
|
+
apiVersion: '2023-10-16',
|
|
96
|
+
},
|
|
97
|
+
paypal: {
|
|
98
|
+
environment: 'sandbox',
|
|
99
|
+
},
|
|
100
|
+
smtp: {
|
|
101
|
+
port: 587,
|
|
102
|
+
secure: false,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
performance: {
|
|
106
|
+
compression: {
|
|
107
|
+
enabled: true,
|
|
108
|
+
level: 6,
|
|
109
|
+
threshold: 1024,
|
|
110
|
+
},
|
|
111
|
+
circuitBreaker: {
|
|
112
|
+
enabled: true,
|
|
113
|
+
failureThreshold: 5,
|
|
114
|
+
resetTimeout: 60000,
|
|
115
|
+
monitoringPeriod: 10000,
|
|
116
|
+
},
|
|
117
|
+
clustering: {
|
|
118
|
+
enabled: false,
|
|
119
|
+
workers: 1,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
272
123
|
|
|
273
|
-
//
|
|
274
|
-
export {
|
|
275
|
-
|
|
276
|
-
ServiceDiscoveryConfigSchema,
|
|
277
|
-
DatabaseConfigSchema,
|
|
278
|
-
ModuleDefaultsConfigSchema,
|
|
279
|
-
LoggingConfigSchema,
|
|
280
|
-
SecurityConfigSchema,
|
|
281
|
-
ExternalServicesConfigSchema,
|
|
282
|
-
PerformanceConfigSchema,
|
|
124
|
+
// Simple compatibility export - just return the config as-is
|
|
125
|
+
export const ConfigSchema = {
|
|
126
|
+
parse: (data: any): AppConfig => data as AppConfig,
|
|
283
127
|
};
|
|
128
|
+
|
|
129
|
+
// Re-export types for backward compatibility
|
|
130
|
+
export type {
|
|
131
|
+
AppConfig,
|
|
132
|
+
ServerConfig,
|
|
133
|
+
ServiceDiscoveryConfig,
|
|
134
|
+
DatabaseConfig,
|
|
135
|
+
ModuleDefaultsConfig,
|
|
136
|
+
LoggingConfig,
|
|
137
|
+
SecurityConfig,
|
|
138
|
+
ExternalServicesConfig,
|
|
139
|
+
PerformanceConfig,
|
|
140
|
+
} from '../../types/config';
|
|
141
|
+
|
|
142
|
+
// For backward compatibility with modules that expect schema objects
|
|
143
|
+
export const ServerConfigSchema = { parse: (data: any) => data };
|
|
144
|
+
export const ServiceDiscoveryConfigSchema = { parse: (data: any) => data };
|
|
145
|
+
export const DatabaseConfigSchema = { parse: (data: any) => data };
|
|
146
|
+
export const ModuleDefaultsConfigSchema = { parse: (data: any) => data };
|
|
147
|
+
export const LoggingConfigSchema = { parse: (data: any) => data };
|
|
148
|
+
export const SecurityConfigSchema = { parse: (data: any) => data };
|
|
149
|
+
export const ExternalServicesConfigSchema = { parse: (data: any) => data };
|
|
150
|
+
export const PerformanceConfigSchema = { parse: (data: any) => data };
|
package/src/core/config/utils.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Configuration Utilities for Modules and Environment Handling
|
|
2
|
-
import { z } from 'zod';
|
|
3
2
|
import { AppConfig } from './schema';
|
|
4
3
|
import { createFrameworkLogger } from '../logger';
|
|
5
4
|
|
|
@@ -66,7 +65,7 @@ function coerceEnvironmentValue(value: string): any {
|
|
|
66
65
|
* Create module-specific configuration with environment override support
|
|
67
66
|
*/
|
|
68
67
|
export function createModuleConfig<T>(
|
|
69
|
-
schema:
|
|
68
|
+
schema: { parse: (data: any) => T },
|
|
70
69
|
defaultConfig: Partial<T>,
|
|
71
70
|
envPrefix?: string
|
|
72
71
|
): T {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Configuration Validation Functions
|
|
2
|
+
// Validation for config system with simple TypeScript functions
|
|
3
|
+
|
|
4
|
+
export class ConfigValidationError extends Error {
|
|
5
|
+
constructor(
|
|
6
|
+
public field: string,
|
|
7
|
+
public value: unknown,
|
|
8
|
+
message: string
|
|
9
|
+
) {
|
|
10
|
+
super(`Configuration validation failed for '${field}': ${message}`);
|
|
11
|
+
this.name = 'ConfigValidationError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Type-safe validation functions for configuration
|
|
16
|
+
export function validatePort(value: unknown, field = 'port'): number {
|
|
17
|
+
const num = Number(value);
|
|
18
|
+
if (isNaN(num) || num < 1 || num > 65535) {
|
|
19
|
+
throw new ConfigValidationError(field, value, 'Must be a number between 1 and 65535');
|
|
20
|
+
}
|
|
21
|
+
return num;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function validateBoolean(value: unknown, field = 'boolean'): boolean {
|
|
25
|
+
if (value === 'true' || value === true) return true;
|
|
26
|
+
if (value === 'false' || value === false) return false;
|
|
27
|
+
if (value === '1' || value === 1) return true;
|
|
28
|
+
if (value === '0' || value === 0) return false;
|
|
29
|
+
throw new ConfigValidationError(field, value, 'Must be a boolean (true/false) or numeric (1/0)');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function validateNumber(
|
|
33
|
+
value: unknown,
|
|
34
|
+
field = 'number',
|
|
35
|
+
options: { min?: number; max?: number } = {}
|
|
36
|
+
): number {
|
|
37
|
+
const num = Number(value);
|
|
38
|
+
if (isNaN(num)) {
|
|
39
|
+
throw new ConfigValidationError(field, value, 'Must be a valid number');
|
|
40
|
+
}
|
|
41
|
+
if (options.min !== undefined && num < options.min) {
|
|
42
|
+
throw new ConfigValidationError(field, value, `Must be at least ${options.min}`);
|
|
43
|
+
}
|
|
44
|
+
if (options.max !== undefined && num > options.max) {
|
|
45
|
+
throw new ConfigValidationError(field, value, `Must be at most ${options.max}`);
|
|
46
|
+
}
|
|
47
|
+
return num;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function validateString(value: unknown, field = 'string'): string {
|
|
51
|
+
if (typeof value !== 'string') {
|
|
52
|
+
throw new ConfigValidationError(field, value, 'Must be a string');
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function validateUrl(value: unknown, field = 'url'): string {
|
|
58
|
+
const str = validateString(value, field);
|
|
59
|
+
try {
|
|
60
|
+
new URL(str);
|
|
61
|
+
return str;
|
|
62
|
+
} catch {
|
|
63
|
+
throw new ConfigValidationError(field, value, 'Must be a valid URL');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function validateEnum<T extends string>(
|
|
68
|
+
value: unknown,
|
|
69
|
+
validValues: readonly T[],
|
|
70
|
+
field = 'enum'
|
|
71
|
+
): T {
|
|
72
|
+
const str = validateString(value, field);
|
|
73
|
+
if (!validValues.includes(str as T)) {
|
|
74
|
+
throw new ConfigValidationError(field, value, `Must be one of: ${validValues.join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
return str as T;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function validateStringArray(value: unknown, field = 'string array'): string[] {
|
|
80
|
+
if (!Array.isArray(value)) {
|
|
81
|
+
// Try to parse comma-separated string
|
|
82
|
+
if (typeof value === 'string') {
|
|
83
|
+
return value
|
|
84
|
+
.split(',')
|
|
85
|
+
.map(s => s.trim())
|
|
86
|
+
.filter(s => s.length > 0);
|
|
87
|
+
}
|
|
88
|
+
throw new ConfigValidationError(field, value, 'Must be an array or comma-separated string');
|
|
89
|
+
}
|
|
90
|
+
return value.map((item, index) => validateString(item, `${field}[${index}]`));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function validateOptional<T>(
|
|
94
|
+
value: unknown,
|
|
95
|
+
validator: (value: unknown, field: string) => T,
|
|
96
|
+
field: string
|
|
97
|
+
): T | undefined {
|
|
98
|
+
if (value === undefined || value === null || value === '') {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return validator(value, field);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Coercion helpers for environment variables
|
|
105
|
+
export function coerceEnvValue(value: string): unknown {
|
|
106
|
+
// Handle common patterns in environment variables
|
|
107
|
+
|
|
108
|
+
// Null/undefined
|
|
109
|
+
if (value === '' || value === 'null' || value === 'undefined') {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Boolean
|
|
114
|
+
if (value === 'true' || value === 'false') {
|
|
115
|
+
return value === 'true';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Number (but not if it starts with 0 - could be port, zip code, etc.)
|
|
119
|
+
if (/^-?\d+(\.\d+)?$/.test(value) && !value.startsWith('0')) {
|
|
120
|
+
const num = Number(value);
|
|
121
|
+
if (!isNaN(num)) {
|
|
122
|
+
return num;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// JSON (for complex objects/arrays)
|
|
127
|
+
if (
|
|
128
|
+
(value.startsWith('{') && value.endsWith('}')) ||
|
|
129
|
+
(value.startsWith('[') && value.endsWith(']'))
|
|
130
|
+
) {
|
|
131
|
+
try {
|
|
132
|
+
return JSON.parse(value);
|
|
133
|
+
} catch {
|
|
134
|
+
// Not valid JSON, treat as string
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Return as string for all other cases
|
|
139
|
+
return value;
|
|
140
|
+
}
|
package/src/core/docs/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Moro Framework Documentation System
|
|
2
|
-
// Automatic API documentation generation from intelligent routes and
|
|
2
|
+
// Automatic API documentation generation from intelligent routes and Validation schemas
|
|
3
3
|
|
|
4
4
|
import { CompiledRoute } from '../routing';
|
|
5
5
|
import { IntelligentRoutingManager } from '../routing/app-integration';
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Extracts route information from intelligent routing and generates OpenAPI 3.0 specs
|
|
3
3
|
|
|
4
4
|
import { CompiledRoute, RouteSchema } from '../routing';
|
|
5
|
-
import {
|
|
5
|
+
import { OpenAPISchema } from './zod-to-openapi';
|
|
6
|
+
import { schemaToOpenAPI, generateExampleFromValidationSchema } from './schema-to-openapi';
|
|
6
7
|
import { createFrameworkLogger } from '../logger';
|
|
7
8
|
|
|
8
9
|
const logger = createFrameworkLogger('OpenAPIGenerator');
|
|
@@ -241,7 +242,7 @@ export class OpenAPIGenerator {
|
|
|
241
242
|
|
|
242
243
|
// Path parameters
|
|
243
244
|
if (route.validation?.params) {
|
|
244
|
-
const paramSchema =
|
|
245
|
+
const paramSchema = schemaToOpenAPI(route.validation.params, this.options);
|
|
245
246
|
if (paramSchema.properties) {
|
|
246
247
|
for (const [name, schema] of Object.entries(paramSchema.properties)) {
|
|
247
248
|
parameters.push({
|
|
@@ -258,7 +259,7 @@ export class OpenAPIGenerator {
|
|
|
258
259
|
|
|
259
260
|
// Query parameters
|
|
260
261
|
if (route.validation?.query) {
|
|
261
|
-
const querySchema =
|
|
262
|
+
const querySchema = schemaToOpenAPI(route.validation.query, this.options);
|
|
262
263
|
if (querySchema.properties) {
|
|
263
264
|
for (const [name, schema] of Object.entries(querySchema.properties)) {
|
|
264
265
|
const isRequired = querySchema.required?.includes(name) || false;
|
|
@@ -276,7 +277,7 @@ export class OpenAPIGenerator {
|
|
|
276
277
|
|
|
277
278
|
// Header parameters
|
|
278
279
|
if (route.validation?.headers) {
|
|
279
|
-
const headerSchema =
|
|
280
|
+
const headerSchema = schemaToOpenAPI(route.validation.headers, this.options);
|
|
280
281
|
if (headerSchema.properties) {
|
|
281
282
|
for (const [name, schema] of Object.entries(headerSchema.properties)) {
|
|
282
283
|
const isRequired = headerSchema.required?.includes(name) || false;
|
|
@@ -309,9 +310,9 @@ export class OpenAPIGenerator {
|
|
|
309
310
|
};
|
|
310
311
|
}
|
|
311
312
|
|
|
312
|
-
const bodySchema =
|
|
313
|
+
const bodySchema = schemaToOpenAPI(route.validation.body, this.options);
|
|
313
314
|
const example = this.options.includeExamples
|
|
314
|
-
?
|
|
315
|
+
? generateExampleFromValidationSchema(route.validation.body)
|
|
315
316
|
: undefined;
|
|
316
317
|
|
|
317
318
|
return {
|