@tamyla/clodo-framework 4.5.0 → 4.6.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/CHANGELOG.md +28 -0
- package/README.md +157 -13
- package/dist/cli/clodo-service.js +13 -0
- package/dist/cli/commands/config-schema.js +144 -0
- package/dist/cli/commands/create.js +18 -1
- package/dist/cli/commands/deploy.js +61 -2
- package/dist/cli/commands/doctor.js +124 -0
- package/dist/cli/commands/secrets.js +258 -0
- package/dist/cli/security-cli.js +1 -1
- package/dist/index.js +3 -1
- package/dist/security/SecretsManager.js +398 -0
- package/dist/security/index.js +2 -0
- package/dist/service-management/ConfirmationEngine.js +4 -1
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +5 -1
- package/dist/service-management/handlers/ConfirmationHandler.js +1 -1
- package/dist/service-management/handlers/ValidationHandler.js +696 -0
- package/dist/validation/ConfigSchemaValidator.js +503 -0
- package/dist/validation/configSchemas.js +236 -0
- package/dist/validation/index.js +6 -2
- package/docs/00_START_HERE.md +26 -338
- package/package.json +1 -1
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Schema Validator
|
|
3
|
+
* Validates CLI configuration files against Zod schemas
|
|
4
|
+
*
|
|
5
|
+
* Integrates with existing infrastructure:
|
|
6
|
+
* - ConfigLoader for file loading
|
|
7
|
+
* - configSchemas.js for Zod schema definitions
|
|
8
|
+
* - service-schema-config.js for canonical enums
|
|
9
|
+
* - payloadValidation.js patterns for result format
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const validator = new ConfigSchemaValidator();
|
|
13
|
+
* const result = validator.validateConfig(config, 'create');
|
|
14
|
+
* // result: { valid, errors[], warnings[], schema, commandType }
|
|
15
|
+
*
|
|
16
|
+
* const result = await validator.validateConfigFile('/path/to/config.json', 'deploy');
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync, existsSync } from 'fs';
|
|
20
|
+
import { resolve } from 'path';
|
|
21
|
+
import { CONFIG_SCHEMAS, getConfigSchema, getRegisteredConfigTypes, CreateConfigSchema, DeployConfigSchema, ValidateConfigSchema, UpdateConfigSchema } from './configSchemas.js';
|
|
22
|
+
import { getConfig } from '../config/service-schema-config.js';
|
|
23
|
+
export class ConfigSchemaValidator {
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.verbose = options.verbose || false;
|
|
26
|
+
this.strict = options.strict || false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validate a config object against the schema for a given command type
|
|
31
|
+
* @param {Object} config - Configuration object to validate
|
|
32
|
+
* @param {string} commandType - Command type (create, deploy, validate, update)
|
|
33
|
+
* @returns {Object} Validation result { valid, errors[], warnings[], commandType }
|
|
34
|
+
*/
|
|
35
|
+
validateConfig(config, commandType) {
|
|
36
|
+
const result = {
|
|
37
|
+
valid: true,
|
|
38
|
+
errors: [],
|
|
39
|
+
warnings: [],
|
|
40
|
+
commandType,
|
|
41
|
+
fieldCount: 0
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Get the schema for this command type
|
|
45
|
+
const schema = getConfigSchema(commandType);
|
|
46
|
+
if (!schema) {
|
|
47
|
+
result.valid = false;
|
|
48
|
+
result.errors.push({
|
|
49
|
+
field: 'commandType',
|
|
50
|
+
code: 'UNKNOWN_COMMAND_TYPE',
|
|
51
|
+
message: `Unknown command type: '${commandType}'. Valid types: ${getRegisteredConfigTypes().join(', ')}`
|
|
52
|
+
});
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Basic type check
|
|
57
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
58
|
+
result.valid = false;
|
|
59
|
+
result.errors.push({
|
|
60
|
+
field: '_root',
|
|
61
|
+
code: 'INVALID_CONFIG_TYPE',
|
|
62
|
+
message: 'Configuration must be a non-null object'
|
|
63
|
+
});
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Filter out comment keys (e.g., "// Comment") before validation
|
|
68
|
+
const cleanConfig = this._stripComments(config);
|
|
69
|
+
result.fieldCount = Object.keys(cleanConfig).length;
|
|
70
|
+
|
|
71
|
+
// Run Zod validation (safeParse for non-throwing)
|
|
72
|
+
const zodResult = schema.safeParse(cleanConfig);
|
|
73
|
+
if (!zodResult.success) {
|
|
74
|
+
result.valid = false;
|
|
75
|
+
for (const issue of zodResult.error.issues) {
|
|
76
|
+
const field = issue.path.length > 0 ? issue.path.join('.') : '_root';
|
|
77
|
+
result.errors.push({
|
|
78
|
+
field,
|
|
79
|
+
code: this._mapZodCode(issue.code),
|
|
80
|
+
message: issue.message,
|
|
81
|
+
expected: issue.expected,
|
|
82
|
+
received: issue.received
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Add semantic warnings (non-blocking)
|
|
88
|
+
this._addSemanticWarnings(cleanConfig, commandType, result);
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Load and validate a config file
|
|
94
|
+
* @param {string} filePath - Path to JSON config file
|
|
95
|
+
* @param {string} commandType - Command type (create, deploy, validate, update)
|
|
96
|
+
* @returns {Object} Validation result with parsed config
|
|
97
|
+
*/
|
|
98
|
+
validateConfigFile(filePath, commandType) {
|
|
99
|
+
const result = {
|
|
100
|
+
valid: true,
|
|
101
|
+
errors: [],
|
|
102
|
+
warnings: [],
|
|
103
|
+
commandType,
|
|
104
|
+
filePath,
|
|
105
|
+
config: null,
|
|
106
|
+
fieldCount: 0
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Resolve and check file existence
|
|
110
|
+
const fullPath = resolve(filePath);
|
|
111
|
+
if (!existsSync(fullPath)) {
|
|
112
|
+
result.valid = false;
|
|
113
|
+
result.errors.push({
|
|
114
|
+
field: '_file',
|
|
115
|
+
code: 'FILE_NOT_FOUND',
|
|
116
|
+
message: `Configuration file not found: ${fullPath}`
|
|
117
|
+
});
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Parse JSON
|
|
122
|
+
let config;
|
|
123
|
+
try {
|
|
124
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
125
|
+
config = JSON.parse(content);
|
|
126
|
+
} catch (parseError) {
|
|
127
|
+
result.valid = false;
|
|
128
|
+
result.errors.push({
|
|
129
|
+
field: '_file',
|
|
130
|
+
code: 'INVALID_JSON',
|
|
131
|
+
message: `Invalid JSON in configuration file: ${parseError.message}`
|
|
132
|
+
});
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validate against schema
|
|
137
|
+
const validation = this.validateConfig(config, commandType);
|
|
138
|
+
result.valid = validation.valid;
|
|
139
|
+
result.errors = validation.errors;
|
|
140
|
+
result.warnings = validation.warnings;
|
|
141
|
+
result.fieldCount = validation.fieldCount;
|
|
142
|
+
result.config = config;
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Auto-detect the command type from a config object based on its fields
|
|
148
|
+
* @param {Object} config - Configuration object
|
|
149
|
+
* @returns {Object} Detection result { detected, commandType, confidence, candidates[] }
|
|
150
|
+
*/
|
|
151
|
+
detectConfigType(config) {
|
|
152
|
+
if (!config || typeof config !== 'object') {
|
|
153
|
+
return {
|
|
154
|
+
detected: false,
|
|
155
|
+
commandType: null,
|
|
156
|
+
confidence: 0,
|
|
157
|
+
candidates: []
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const cleanConfig = this._stripComments(config);
|
|
161
|
+
const keys = Object.keys(cleanConfig);
|
|
162
|
+
|
|
163
|
+
// Signature fields for each command type
|
|
164
|
+
const signatures = {
|
|
165
|
+
create: {
|
|
166
|
+
strong: ['projectName', 'serviceName', 'serviceType', 'template', 'typescript'],
|
|
167
|
+
moderate: ['features', 'advanced', 'metadata', 'middlewareStrategy']
|
|
168
|
+
},
|
|
169
|
+
deploy: {
|
|
170
|
+
strong: ['deployment', 'routing', 'monitoring', 'dryRun', 'skipDoctor', 'doctorStrict'],
|
|
171
|
+
moderate: ['security', 'token', 'servicePath']
|
|
172
|
+
},
|
|
173
|
+
validate: {
|
|
174
|
+
strong: ['deepScan', 'checks', 'requirements', 'reporting'],
|
|
175
|
+
moderate: ['validation', 'exportReport']
|
|
176
|
+
},
|
|
177
|
+
update: {
|
|
178
|
+
strong: ['updates', 'migration', 'notification', 'performance'],
|
|
179
|
+
moderate: ['preview', 'interactive']
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const candidates = [];
|
|
183
|
+
for (const [type, sig] of Object.entries(signatures)) {
|
|
184
|
+
let score = 0;
|
|
185
|
+
const matchedFields = [];
|
|
186
|
+
for (const key of keys) {
|
|
187
|
+
if (sig.strong.includes(key)) {
|
|
188
|
+
score += 3;
|
|
189
|
+
matchedFields.push(key);
|
|
190
|
+
} else if (sig.moderate.includes(key)) {
|
|
191
|
+
score += 1;
|
|
192
|
+
matchedFields.push(key);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (score > 0) {
|
|
196
|
+
candidates.push({
|
|
197
|
+
commandType: type,
|
|
198
|
+
score,
|
|
199
|
+
matchedFields
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Sort by score descending
|
|
205
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
206
|
+
if (candidates.length === 0) {
|
|
207
|
+
return {
|
|
208
|
+
detected: false,
|
|
209
|
+
commandType: null,
|
|
210
|
+
confidence: 0,
|
|
211
|
+
candidates: []
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const best = candidates[0];
|
|
215
|
+
const confidence = Math.min(best.score / 6, 1); // Normalize to 0-1
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
detected: true,
|
|
219
|
+
commandType: best.commandType,
|
|
220
|
+
confidence,
|
|
221
|
+
candidates
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get human-readable schema definition for a command type
|
|
227
|
+
* @param {string} commandType - Command type
|
|
228
|
+
* @returns {Object} Schema definition with field descriptions
|
|
229
|
+
*/
|
|
230
|
+
getSchemaDefinition(commandType) {
|
|
231
|
+
const schema = getConfigSchema(commandType);
|
|
232
|
+
if (!schema) return null;
|
|
233
|
+
const schemaConfig = getConfig();
|
|
234
|
+
const definitions = {};
|
|
235
|
+
|
|
236
|
+
// Extract shape from Zod schema
|
|
237
|
+
const shape = schema.shape;
|
|
238
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
239
|
+
definitions[key] = this._describeZodField(key, fieldSchema, schemaConfig);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
commandType,
|
|
243
|
+
description: schema.description || `Configuration schema for '${commandType}' command`,
|
|
244
|
+
fields: definitions,
|
|
245
|
+
fieldCount: Object.keys(definitions).length,
|
|
246
|
+
validServiceTypes: schemaConfig.serviceTypes,
|
|
247
|
+
validFeatures: schemaConfig.features
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Validate all example config files in the config directory
|
|
253
|
+
* @param {string} configDir - Path to config directory
|
|
254
|
+
* @returns {Object} Results for each example file
|
|
255
|
+
*/
|
|
256
|
+
validateExampleConfigs(configDir) {
|
|
257
|
+
const results = {};
|
|
258
|
+
const exampleFiles = {
|
|
259
|
+
'clodo-create.example.json': 'create',
|
|
260
|
+
'clodo-deploy.example.json': 'deploy',
|
|
261
|
+
'clodo-validate.example.json': 'validate',
|
|
262
|
+
'clodo-update.example.json': 'update'
|
|
263
|
+
};
|
|
264
|
+
for (const [filename, commandType] of Object.entries(exampleFiles)) {
|
|
265
|
+
const filePath = resolve(configDir, filename);
|
|
266
|
+
if (existsSync(filePath)) {
|
|
267
|
+
results[filename] = this.validateConfigFile(filePath, commandType);
|
|
268
|
+
} else {
|
|
269
|
+
results[filename] = {
|
|
270
|
+
valid: false,
|
|
271
|
+
errors: [{
|
|
272
|
+
field: '_file',
|
|
273
|
+
code: 'FILE_NOT_FOUND',
|
|
274
|
+
message: `Example file not found: ${filename}`
|
|
275
|
+
}],
|
|
276
|
+
warnings: [],
|
|
277
|
+
commandType
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return results;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get all registered config types
|
|
286
|
+
* @returns {string[]}
|
|
287
|
+
*/
|
|
288
|
+
getRegisteredTypes() {
|
|
289
|
+
return getRegisteredConfigTypes();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ─── Private Helpers ─────────────────────────────────────────────────────────
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Strip comment keys (e.g., "// Comment") from config objects
|
|
296
|
+
*/
|
|
297
|
+
_stripComments(config) {
|
|
298
|
+
const clean = {};
|
|
299
|
+
for (const [key, value] of Object.entries(config)) {
|
|
300
|
+
if (!key.startsWith('//')) {
|
|
301
|
+
clean[key] = value;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return clean;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Map Zod error codes to our error codes
|
|
309
|
+
*/
|
|
310
|
+
_mapZodCode(zodCode) {
|
|
311
|
+
const codeMap = {
|
|
312
|
+
invalid_type: 'INVALID_TYPE',
|
|
313
|
+
invalid_string: 'INVALID_STRING',
|
|
314
|
+
too_small: 'VALUE_TOO_SMALL',
|
|
315
|
+
too_big: 'VALUE_TOO_BIG',
|
|
316
|
+
invalid_enum_value: 'INVALID_ENUM',
|
|
317
|
+
custom: 'CUSTOM_VALIDATION',
|
|
318
|
+
invalid_union: 'INVALID_UNION',
|
|
319
|
+
unrecognized_keys: 'UNRECOGNIZED_KEYS'
|
|
320
|
+
};
|
|
321
|
+
return codeMap[zodCode] || 'SCHEMA_VALIDATION';
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Add semantic warnings (non-blocking checks)
|
|
326
|
+
*/
|
|
327
|
+
_addSemanticWarnings(config, commandType, result) {
|
|
328
|
+
// Warn about env var placeholders that weren't substituted
|
|
329
|
+
this._checkEnvVarPlaceholders(config, result);
|
|
330
|
+
|
|
331
|
+
// Command-specific warnings
|
|
332
|
+
switch (commandType) {
|
|
333
|
+
case 'create':
|
|
334
|
+
this._addCreateWarnings(config, result);
|
|
335
|
+
break;
|
|
336
|
+
case 'deploy':
|
|
337
|
+
this._addDeployWarnings(config, result);
|
|
338
|
+
break;
|
|
339
|
+
case 'update':
|
|
340
|
+
this._addUpdateWarnings(config, result);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Check for unsubstituted environment variable placeholders
|
|
347
|
+
*/
|
|
348
|
+
_checkEnvVarPlaceholders(config, result, prefix = '') {
|
|
349
|
+
for (const [key, value] of Object.entries(config)) {
|
|
350
|
+
const field = prefix ? `${prefix}.${key}` : key;
|
|
351
|
+
if (typeof value === 'string' && /\$\{[^}]+\}/.test(value)) {
|
|
352
|
+
result.warnings.push({
|
|
353
|
+
field,
|
|
354
|
+
code: 'ENV_VAR_PLACEHOLDER',
|
|
355
|
+
message: `Field '${field}' contains environment variable placeholder: ${value}. Ensure env vars are set at runtime.`
|
|
356
|
+
});
|
|
357
|
+
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
358
|
+
this._checkEnvVarPlaceholders(value, result, field);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Warnings specific to create configs
|
|
365
|
+
*/
|
|
366
|
+
_addCreateWarnings(config, result) {
|
|
367
|
+
if (config.features && Array.isArray(config.features)) {
|
|
368
|
+
const duplicates = config.features.filter((v, i, a) => a.indexOf(v) !== i);
|
|
369
|
+
if (duplicates.length) {
|
|
370
|
+
result.warnings.push({
|
|
371
|
+
field: 'features',
|
|
372
|
+
code: 'DUPLICATE_FEATURES',
|
|
373
|
+
message: `Duplicate features: ${[...new Set(duplicates)].join(', ')}`
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Both projectName and serviceName provided — potential confusion
|
|
379
|
+
if (config.projectName && config.serviceName && config.projectName !== config.serviceName) {
|
|
380
|
+
result.warnings.push({
|
|
381
|
+
field: 'serviceName',
|
|
382
|
+
code: 'NAME_MISMATCH',
|
|
383
|
+
message: `projectName ('${config.projectName}') differs from serviceName ('${config.serviceName}'). serviceName is used for the service identifier.`
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Warnings specific to deploy configs
|
|
390
|
+
*/
|
|
391
|
+
_addDeployWarnings(config, result) {
|
|
392
|
+
// Production without security settings
|
|
393
|
+
if (config.environment === 'production') {
|
|
394
|
+
if (!config.security) {
|
|
395
|
+
result.warnings.push({
|
|
396
|
+
field: 'security',
|
|
397
|
+
code: 'MISSING_SECURITY',
|
|
398
|
+
message: 'Production deployment without security configuration. Consider adding security settings.'
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
if (!config.monitoring) {
|
|
402
|
+
result.warnings.push({
|
|
403
|
+
field: 'monitoring',
|
|
404
|
+
code: 'MISSING_MONITORING',
|
|
405
|
+
message: 'Production deployment without monitoring configuration. Consider adding monitoring settings.'
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Dry-run with force is contradictory
|
|
411
|
+
if (config.dryRun && config.force) {
|
|
412
|
+
result.warnings.push({
|
|
413
|
+
field: 'dryRun',
|
|
414
|
+
code: 'CONTRADICTORY_FLAGS',
|
|
415
|
+
message: 'Both dryRun and force are set. dryRun simulates without changes, force skips confirmations — these are contradictory.'
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Warnings specific to update configs
|
|
422
|
+
*/
|
|
423
|
+
_addUpdateWarnings(config, result) {
|
|
424
|
+
// Update without backup
|
|
425
|
+
if (config.migration && config.migration.runMigrations && !config.migration.backupBeforeUpdate) {
|
|
426
|
+
result.warnings.push({
|
|
427
|
+
field: 'migration.backupBeforeUpdate',
|
|
428
|
+
code: 'NO_BACKUP',
|
|
429
|
+
message: 'Running migrations without backupBeforeUpdate enabled. Consider enabling backups for safety.'
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Describe a Zod field for human-readable output
|
|
436
|
+
*/
|
|
437
|
+
_describeZodField(key, fieldSchema, schemaConfig) {
|
|
438
|
+
const def = {
|
|
439
|
+
name: key,
|
|
440
|
+
required: !fieldSchema.isOptional(),
|
|
441
|
+
type: this._getZodType(fieldSchema)
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// Add description if available
|
|
445
|
+
if (fieldSchema.description) {
|
|
446
|
+
def.description = fieldSchema.description;
|
|
447
|
+
}
|
|
448
|
+
return def;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Get human-readable type from Zod schema
|
|
453
|
+
*/
|
|
454
|
+
_getZodType(schema) {
|
|
455
|
+
if (!schema || !schema._def) return 'unknown';
|
|
456
|
+
|
|
457
|
+
// Zod v4 uses _def.type (string), Zod v3 uses _def.typeName
|
|
458
|
+
const typeName = schema._def.type || schema._def.typeName;
|
|
459
|
+
switch (typeName) {
|
|
460
|
+
case 'string':
|
|
461
|
+
case 'ZodString':
|
|
462
|
+
return 'string';
|
|
463
|
+
case 'number':
|
|
464
|
+
case 'ZodNumber':
|
|
465
|
+
return 'number';
|
|
466
|
+
case 'boolean':
|
|
467
|
+
case 'ZodBoolean':
|
|
468
|
+
return 'boolean';
|
|
469
|
+
case 'array':
|
|
470
|
+
case 'ZodArray':
|
|
471
|
+
return 'array';
|
|
472
|
+
case 'object':
|
|
473
|
+
case 'ZodObject':
|
|
474
|
+
return 'object';
|
|
475
|
+
case 'enum':
|
|
476
|
+
case 'ZodEnum':
|
|
477
|
+
{
|
|
478
|
+
const values = schema._def.entries || schema._def.values;
|
|
479
|
+
return values ? `enum(${Array.isArray(values) ? values.join(', ') : Object.keys(values).join(', ')})` : 'enum';
|
|
480
|
+
}
|
|
481
|
+
case 'optional':
|
|
482
|
+
case 'ZodOptional':
|
|
483
|
+
return this._getZodType(schema._def.innerType);
|
|
484
|
+
case 'default':
|
|
485
|
+
case 'ZodDefault':
|
|
486
|
+
return this._getZodType(schema._def.innerType);
|
|
487
|
+
case 'pipe':
|
|
488
|
+
case 'ZodEffects':
|
|
489
|
+
return this._getZodType(schema._def.in || schema._def.schema);
|
|
490
|
+
case 'record':
|
|
491
|
+
case 'ZodRecord':
|
|
492
|
+
return 'record';
|
|
493
|
+
case 'any':
|
|
494
|
+
case 'ZodAny':
|
|
495
|
+
return 'any';
|
|
496
|
+
default:
|
|
497
|
+
{
|
|
498
|
+
const name = String(typeName || '');
|
|
499
|
+
return name.replace('Zod', '').toLowerCase() || 'unknown';
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|