@type-crafter/mcp 0.3.0 → 0.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/dist/index.js +209 -68
- package/package.json +1 -1
- package/src/GUIDE.md +116 -22
- package/src/SPEC_RULES.md +570 -13
package/dist/index.js
CHANGED
|
@@ -6,7 +6,35 @@ import path from 'path';
|
|
|
6
6
|
import { parse as parseYaml } from 'yaml';
|
|
7
7
|
import { exec } from 'child_process';
|
|
8
8
|
import { promisify } from 'util';
|
|
9
|
+
import { z } from 'zod';
|
|
9
10
|
const execAsync = promisify(exec);
|
|
11
|
+
// Zod schemas for all tools
|
|
12
|
+
const generateTypesSchema = z.object({
|
|
13
|
+
language: z
|
|
14
|
+
.enum(['typescript', 'typescript-with-decoders'])
|
|
15
|
+
.describe('Target language for type generation'),
|
|
16
|
+
specFilePath: z.string().describe('Path to the YAML specification file'),
|
|
17
|
+
outputDirectory: z.string().describe('Directory where generated types will be written'),
|
|
18
|
+
typesWriterMode: z
|
|
19
|
+
.enum(['SingleFile', 'Files'])
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Writer mode for types: SingleFile or Files'),
|
|
22
|
+
groupedTypesWriterMode: z
|
|
23
|
+
.enum(['FolderWithFiles', 'SingleFile'])
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Writer mode for grouped types: FolderWithFiles or SingleFile'),
|
|
26
|
+
});
|
|
27
|
+
const validateSpecSchema = z.object({
|
|
28
|
+
specFilePath: z.string().describe('Path to the YAML specification file to validate'),
|
|
29
|
+
});
|
|
30
|
+
const listLanguagesSchema = z.object({});
|
|
31
|
+
const getSpecInfoSchema = z.object({
|
|
32
|
+
specFilePath: z.string().describe('Path to the YAML specification file'),
|
|
33
|
+
});
|
|
34
|
+
const getSpecRulesSchema = z.object({});
|
|
35
|
+
const checkSpecSchema = z.object({
|
|
36
|
+
specFilePath: z.string().describe('Path to the YAML specification file to check for common mistakes'),
|
|
37
|
+
});
|
|
10
38
|
// Type guards
|
|
11
39
|
function isRecord(value) {
|
|
12
40
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -67,74 +95,10 @@ const server = new McpServer({
|
|
|
67
95
|
tools: {},
|
|
68
96
|
},
|
|
69
97
|
});
|
|
70
|
-
// Schema definitions for all tools
|
|
71
|
-
const generateTypesSchema = {
|
|
72
|
-
type: 'object',
|
|
73
|
-
properties: {
|
|
74
|
-
language: {
|
|
75
|
-
type: 'string',
|
|
76
|
-
description: 'Target language for type generation',
|
|
77
|
-
enum: ['typescript', 'typescript-with-decoders'],
|
|
78
|
-
},
|
|
79
|
-
specFilePath: {
|
|
80
|
-
type: 'string',
|
|
81
|
-
description: 'Path to the YAML specification file',
|
|
82
|
-
},
|
|
83
|
-
outputDirectory: {
|
|
84
|
-
type: 'string',
|
|
85
|
-
description: 'Directory where generated types will be written',
|
|
86
|
-
},
|
|
87
|
-
typesWriterMode: {
|
|
88
|
-
type: 'string',
|
|
89
|
-
description: 'Writer mode for types: SingleFile or Files',
|
|
90
|
-
enum: ['SingleFile', 'Files'],
|
|
91
|
-
},
|
|
92
|
-
groupedTypesWriterMode: {
|
|
93
|
-
type: 'string',
|
|
94
|
-
description: 'Writer mode for grouped types: FolderWithFiles or SingleFile',
|
|
95
|
-
enum: ['FolderWithFiles', 'SingleFile'],
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
required: ['language', 'specFilePath', 'outputDirectory'],
|
|
99
|
-
additionalProperties: false,
|
|
100
|
-
};
|
|
101
|
-
const validateSpecSchema = {
|
|
102
|
-
type: 'object',
|
|
103
|
-
properties: {
|
|
104
|
-
specFilePath: {
|
|
105
|
-
type: 'string',
|
|
106
|
-
description: 'Path to the YAML specification file to validate',
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
required: ['specFilePath'],
|
|
110
|
-
additionalProperties: false,
|
|
111
|
-
};
|
|
112
|
-
const listLanguagesSchema = {
|
|
113
|
-
type: 'object',
|
|
114
|
-
properties: {},
|
|
115
|
-
additionalProperties: false,
|
|
116
|
-
};
|
|
117
|
-
const getSpecInfoSchema = {
|
|
118
|
-
type: 'object',
|
|
119
|
-
properties: {
|
|
120
|
-
specFilePath: {
|
|
121
|
-
type: 'string',
|
|
122
|
-
description: 'Path to the YAML specification file',
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
required: ['specFilePath'],
|
|
126
|
-
additionalProperties: false,
|
|
127
|
-
};
|
|
128
|
-
const getSpecRulesSchema = {
|
|
129
|
-
type: 'object',
|
|
130
|
-
properties: {},
|
|
131
|
-
additionalProperties: false,
|
|
132
|
-
};
|
|
133
98
|
// Register generate-types tool
|
|
134
99
|
server.registerTool('generate-types', {
|
|
135
100
|
description: 'Generate type definitions from a YAML specification file. Supports TypeScript and TypeScript with decoders. ' +
|
|
136
101
|
'The YAML spec should follow the Type Crafter format with info, types, and/or groupedTypes sections.',
|
|
137
|
-
// @ts-expect-error - MCP SDK schema type mismatch
|
|
138
102
|
inputSchema: generateTypesSchema,
|
|
139
103
|
}, async (args) => {
|
|
140
104
|
if (!isGenerateTypesArgs(args)) {
|
|
@@ -221,7 +185,6 @@ server.registerTool('generate-types', {
|
|
|
221
185
|
server.registerTool('validate-spec', {
|
|
222
186
|
description: 'Validate a YAML specification file without generating types. ' +
|
|
223
187
|
'Checks if the spec file is valid and can be processed by Type Crafter.',
|
|
224
|
-
// @ts-expect-error - MCP SDK schema type mismatch
|
|
225
188
|
inputSchema: validateSpecSchema,
|
|
226
189
|
}, async (args) => {
|
|
227
190
|
if (!isSpecFilePathArgs(args)) {
|
|
@@ -283,7 +246,6 @@ server.registerTool('validate-spec', {
|
|
|
283
246
|
// Register list-languages tool
|
|
284
247
|
server.registerTool('list-languages', {
|
|
285
248
|
description: 'List all supported target languages for type generation',
|
|
286
|
-
// @ts-expect-error - MCP SDK schema type mismatch
|
|
287
249
|
inputSchema: listLanguagesSchema,
|
|
288
250
|
}, async () => {
|
|
289
251
|
return {
|
|
@@ -299,7 +261,6 @@ server.registerTool('list-languages', {
|
|
|
299
261
|
server.registerTool('get-spec-info', {
|
|
300
262
|
description: 'Get information about a YAML specification file including version, title, ' +
|
|
301
263
|
'and counts of types and grouped types defined in the spec.',
|
|
302
|
-
// @ts-expect-error - MCP SDK schema type mismatch
|
|
303
264
|
inputSchema: getSpecInfoSchema,
|
|
304
265
|
}, async (args) => {
|
|
305
266
|
if (!isSpecFilePathArgs(args)) {
|
|
@@ -391,7 +352,6 @@ server.registerTool('get-spec-rules', {
|
|
|
391
352
|
description: 'Get comprehensive rules and guidelines for writing Type Crafter YAML specification files. ' +
|
|
392
353
|
'This provides LLMs with detailed information about the YAML spec format, type mappings, nullable types, ' +
|
|
393
354
|
'references, composition, best practices, and common patterns. Use this before creating or modifying spec files.',
|
|
394
|
-
// @ts-expect-error - MCP SDK schema type mismatch
|
|
395
355
|
inputSchema: getSpecRulesSchema,
|
|
396
356
|
}, async () => {
|
|
397
357
|
try {
|
|
@@ -430,6 +390,187 @@ server.registerTool('get-spec-rules', {
|
|
|
430
390
|
};
|
|
431
391
|
}
|
|
432
392
|
});
|
|
393
|
+
// Register check-spec tool
|
|
394
|
+
server.registerTool('check-spec', {
|
|
395
|
+
description: 'Check a YAML specification file for common mistakes and provide helpful feedback. ' +
|
|
396
|
+
'This tool detects issues like using nullable/optional properties, incorrect array definitions, ' +
|
|
397
|
+
'wrong file paths, and other common errors. Use this before generating types to catch mistakes early.',
|
|
398
|
+
inputSchema: checkSpecSchema,
|
|
399
|
+
}, async (args) => {
|
|
400
|
+
if (!isSpecFilePathArgs(args)) {
|
|
401
|
+
return {
|
|
402
|
+
content: [
|
|
403
|
+
{
|
|
404
|
+
type: 'text',
|
|
405
|
+
text: 'Error: Invalid arguments provided',
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
isError: true,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const { specFilePath } = args;
|
|
412
|
+
// Resolve path
|
|
413
|
+
const resolvedSpecPath = path.resolve(specFilePath);
|
|
414
|
+
// Check if spec file exists
|
|
415
|
+
try {
|
|
416
|
+
await fs.access(resolvedSpecPath);
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
return {
|
|
420
|
+
content: [
|
|
421
|
+
{
|
|
422
|
+
type: 'text',
|
|
423
|
+
text: `Error: Specification file not found at ${resolvedSpecPath}`,
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
isError: true,
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
// Read the spec file
|
|
430
|
+
let specContent;
|
|
431
|
+
try {
|
|
432
|
+
specContent = await fs.readFile(resolvedSpecPath, 'utf-8');
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
if (!isExecError(error)) {
|
|
436
|
+
return {
|
|
437
|
+
content: [
|
|
438
|
+
{
|
|
439
|
+
type: 'text',
|
|
440
|
+
text: 'Error: Unknown error occurred while reading spec file',
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
isError: true,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
content: [
|
|
448
|
+
{
|
|
449
|
+
type: 'text',
|
|
450
|
+
text: `Error reading spec file: ${error.message}`,
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
isError: true,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
const issues = [];
|
|
457
|
+
const warnings = [];
|
|
458
|
+
// Check for common mistakes
|
|
459
|
+
const lines = specContent.split('\n');
|
|
460
|
+
lines.forEach((line, index) => {
|
|
461
|
+
const lineNum = index + 1;
|
|
462
|
+
// Check for 'nullable: true'
|
|
463
|
+
if (line.match(/nullable\s*:\s*true/i)) {
|
|
464
|
+
issues.push(`Line ${lineNum}: Found 'nullable: true' - This property does NOT exist in Type Crafter. ` +
|
|
465
|
+
`Use the 'required' array to control nullability instead.`);
|
|
466
|
+
}
|
|
467
|
+
// Check for 'optional: true'
|
|
468
|
+
if (line.match(/optional\s*:\s*true/i)) {
|
|
469
|
+
issues.push(`Line ${lineNum}: Found 'optional: true' - This property does NOT exist in Type Crafter. ` +
|
|
470
|
+
`Use the 'required' array to control optionality instead.`);
|
|
471
|
+
}
|
|
472
|
+
// Check for property names with '?'
|
|
473
|
+
if (line.match(/^\s+[\w]+\?\s*:/)) {
|
|
474
|
+
issues.push(`Line ${lineNum}: Found property name with '?' suffix - This syntax is NOT supported. ` +
|
|
475
|
+
`Use the 'required' array instead.`);
|
|
476
|
+
}
|
|
477
|
+
// Check for type: [string, null] pattern
|
|
478
|
+
if (line.match(/type\s*:\s*\[.*,\s*null\]/)) {
|
|
479
|
+
issues.push(`Line ${lineNum}: Found 'type: [type, null]' pattern - This is NOT supported. ` +
|
|
480
|
+
`Use the 'required' array to control nullability instead.`);
|
|
481
|
+
}
|
|
482
|
+
// Check for top-level array types (heuristic)
|
|
483
|
+
if (line.match(/^\w+:\s*$/) && lines[index + 1]?.match(/^\s+type\s*:\s*array/)) {
|
|
484
|
+
warnings.push(`Line ${lineNum}: Possible top-level array type - Arrays cannot be top-level types. ` +
|
|
485
|
+
`They must be properties within objects. Verify this is inside an object's properties.`);
|
|
486
|
+
}
|
|
487
|
+
// Check for '../' in $ref paths
|
|
488
|
+
if (line.match(/\$ref\s*:\s*['"].*\.\.\//)) {
|
|
489
|
+
issues.push(`Line ${lineNum}: Found relative path with '../' in $ref - Paths should be from project root, ` +
|
|
490
|
+
`not relative to the current file. Use './path/from/root/file.yaml#/Type' format.`);
|
|
491
|
+
}
|
|
492
|
+
// Check for missing './' prefix in external $ref
|
|
493
|
+
if (line.match(/\$ref\s*:\s*['"][^#'][^/]/)) {
|
|
494
|
+
const match = line.match(/\$ref\s*:\s*['"]([^'"]+)['"]/);
|
|
495
|
+
if (match && match[1] && !match[1].startsWith('#') && !match[1].startsWith('./')) {
|
|
496
|
+
warnings.push(`Line ${lineNum}: External $ref path should start with './' - ` +
|
|
497
|
+
`Use './path/from/root/file.yaml#/Type' format.`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
// Try to parse YAML and check for structural issues
|
|
502
|
+
try {
|
|
503
|
+
const specData = parseYaml(specContent);
|
|
504
|
+
if (!isRecord(specData)) {
|
|
505
|
+
issues.push('Spec file root is not an object. Expected YAML object at root level.');
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
// Check for missing info section
|
|
509
|
+
if (!isRecord(specData.info)) {
|
|
510
|
+
issues.push("Missing 'info' section - Every spec file must have an 'info' section with 'version' and 'title'.");
|
|
511
|
+
}
|
|
512
|
+
else if (!isSpecInfo(specData.info)) {
|
|
513
|
+
if (typeof specData.info.version !== 'string') {
|
|
514
|
+
issues.push("Missing 'info.version' - Must be a string in semver format (e.g., '1.0.0').");
|
|
515
|
+
}
|
|
516
|
+
if (typeof specData.info.title !== 'string') {
|
|
517
|
+
issues.push("Missing 'info.title' - Must be a string describing the spec.");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// Check if at least one of types or groupedTypes exists
|
|
521
|
+
const hasTypes = isRecord(specData.types);
|
|
522
|
+
const hasGroupedTypes = isRecord(specData.groupedTypes);
|
|
523
|
+
if (!hasTypes && !hasGroupedTypes) {
|
|
524
|
+
issues.push("Missing 'types' or 'groupedTypes' section - At least one must be defined.");
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
if (isExecError(error)) {
|
|
530
|
+
issues.push(`YAML parsing error: ${error.message}`);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
issues.push('YAML parsing error: Unable to parse spec file.');
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// Generate response
|
|
537
|
+
if (issues.length === 0 && warnings.length === 0) {
|
|
538
|
+
return {
|
|
539
|
+
content: [
|
|
540
|
+
{
|
|
541
|
+
type: 'text',
|
|
542
|
+
text: '✅ No common mistakes detected!\n\nThe spec file looks good. You can proceed with validation using validate-spec or generation using generate-types.',
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
let response = '';
|
|
548
|
+
if (issues.length > 0) {
|
|
549
|
+
response += '❌ Issues Found:\n\n';
|
|
550
|
+
issues.forEach((issue, idx) => {
|
|
551
|
+
response += `${idx + 1}. ${issue}\n\n`;
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
if (warnings.length > 0) {
|
|
555
|
+
if (issues.length > 0)
|
|
556
|
+
response += '\n';
|
|
557
|
+
response += '⚠️ Warnings:\n\n';
|
|
558
|
+
warnings.forEach((warning, idx) => {
|
|
559
|
+
response += `${idx + 1}. ${warning}\n\n`;
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
response +=
|
|
563
|
+
'\n📖 For detailed rules and examples, use the get-spec-rules tool to see complete documentation.';
|
|
564
|
+
return {
|
|
565
|
+
content: [
|
|
566
|
+
{
|
|
567
|
+
type: 'text',
|
|
568
|
+
text: response,
|
|
569
|
+
},
|
|
570
|
+
],
|
|
571
|
+
isError: issues.length > 0,
|
|
572
|
+
};
|
|
573
|
+
});
|
|
433
574
|
// Start the server
|
|
434
575
|
async function main() {
|
|
435
576
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
package/src/GUIDE.md
CHANGED
|
@@ -42,7 +42,7 @@ types: export type User = {
|
|
|
42
42
|
|
|
43
43
|
**When to use:** ALWAYS use this FIRST before creating or modifying YAML specs
|
|
44
44
|
|
|
45
|
-
**What it does:** Returns comprehensive rules for writing Type Crafter YAML specs
|
|
45
|
+
**What it does:** Returns comprehensive rules for writing Type Crafter YAML specs, including common mistakes section, multi-file spec patterns, and detailed examples
|
|
46
46
|
|
|
47
47
|
**Workflow:**
|
|
48
48
|
|
|
@@ -50,27 +50,75 @@ types: export type User = {
|
|
|
50
50
|
User asks: "Create types for my API"
|
|
51
51
|
↓
|
|
52
52
|
1. Call get-spec-rules to learn the format
|
|
53
|
-
2. Read the returned rules (nullable types, arrays, paths
|
|
53
|
+
2. Read the returned rules (especially common mistakes, nullable types, arrays, paths)
|
|
54
54
|
3. Now you know how to write valid YAML specs
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
**Important:** The rules include a "Common Mistakes" section at the top that shows what NOT to do (like using `nullable: true`, `optional: true`, or `?` suffixes).
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
### 2. `check-spec` 🔍
|
|
60
60
|
|
|
61
|
-
**
|
|
61
|
+
**When to use:** IMMEDIATELY after creating or modifying a YAML spec, BEFORE validation or generation
|
|
62
|
+
|
|
63
|
+
**What it does:** Scans the spec file for common mistakes and provides specific feedback with line numbers
|
|
64
|
+
|
|
65
|
+
**Detects:**
|
|
66
|
+
|
|
67
|
+
- Invalid properties like `nullable: true` or `optional: true`
|
|
68
|
+
- Property names with `?` suffix
|
|
69
|
+
- Incorrect type arrays like `type: [string, null]`
|
|
70
|
+
- Top-level array types (which are invalid)
|
|
71
|
+
- Wrong file paths (using `../` or missing `./` prefix)
|
|
72
|
+
- Missing `info` section
|
|
73
|
+
- Structural issues
|
|
62
74
|
|
|
63
75
|
**Workflow:**
|
|
64
76
|
|
|
65
77
|
```
|
|
66
|
-
You created: my-types.yaml
|
|
78
|
+
You created/modified: my-types.yaml
|
|
79
|
+
↓
|
|
80
|
+
1. Call check-spec with path to my-types.yaml
|
|
81
|
+
2. Review issues and warnings with specific line numbers
|
|
82
|
+
3. Fix all issues before proceeding
|
|
83
|
+
4. Run check-spec again to verify fixes
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Example Output:**
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
❌ Issues Found:
|
|
90
|
+
|
|
91
|
+
1. Line 15: Found 'nullable: true' - This property does NOT exist in Type Crafter.
|
|
92
|
+
Use the 'required' array to control nullability instead.
|
|
93
|
+
|
|
94
|
+
2. Line 42: Found relative path with '../' in $ref - Paths should be from project root.
|
|
95
|
+
Use './path/from/root/file.yaml#/Type' format.
|
|
96
|
+
|
|
97
|
+
⚠️ Warnings:
|
|
98
|
+
|
|
99
|
+
1. Line 28: Possible top-level array type - Arrays cannot be top-level types.
|
|
100
|
+
They must be properties within objects.
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3. `validate-spec` ✅
|
|
104
|
+
|
|
105
|
+
**When to use:** After check-spec passes, to do full structural validation
|
|
106
|
+
|
|
107
|
+
**What it does:** Validates the spec structure and reports counts of types
|
|
108
|
+
|
|
109
|
+
**Workflow:**
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
check-spec passed
|
|
67
113
|
↓
|
|
68
114
|
1. Call validate-spec with path to my-types.yaml
|
|
69
|
-
2.
|
|
70
|
-
3.
|
|
115
|
+
2. Get confirmation with type counts
|
|
116
|
+
3. Proceed to generation
|
|
71
117
|
```
|
|
72
118
|
|
|
73
|
-
|
|
119
|
+
**Note:** Use `check-spec` first to catch common mistakes, then `validate-spec` for final structural validation.
|
|
120
|
+
|
|
121
|
+
### 4. `generate-types` 🔨
|
|
74
122
|
|
|
75
123
|
**When to use:** After spec is validated and user wants TypeScript output
|
|
76
124
|
|
|
@@ -94,7 +142,7 @@ Spec is valid
|
|
|
94
142
|
3. User can now import and use them in TypeScript
|
|
95
143
|
```
|
|
96
144
|
|
|
97
|
-
###
|
|
145
|
+
### 5. `get-spec-info` 📊
|
|
98
146
|
|
|
99
147
|
**When to use:** To understand what's in an existing spec file
|
|
100
148
|
|
|
@@ -110,7 +158,7 @@ User asks: "What types are in my spec?"
|
|
|
110
158
|
3. Understand the structure before modifying
|
|
111
159
|
```
|
|
112
160
|
|
|
113
|
-
###
|
|
161
|
+
### 6. `list-languages` 📝
|
|
114
162
|
|
|
115
163
|
**When to use:** User asks what output formats are supported
|
|
116
164
|
|
|
@@ -121,6 +169,35 @@ User asks: "What types are in my spec?"
|
|
|
121
169
|
- `typescript` - TypeScript type definitions
|
|
122
170
|
- `typescript-with-decoders` - TypeScript + runtime decoders
|
|
123
171
|
|
|
172
|
+
## Quick Decision Tree
|
|
173
|
+
|
|
174
|
+
**What tool should I use?**
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
User asks to create/modify spec file?
|
|
178
|
+
→ get-spec-rules FIRST (learn the format)
|
|
179
|
+
→ Create/modify the file
|
|
180
|
+
→ check-spec (catch common mistakes)
|
|
181
|
+
→ validate-spec (structural validation)
|
|
182
|
+
→ generate-types (create TypeScript)
|
|
183
|
+
|
|
184
|
+
User asks "is my spec valid?"
|
|
185
|
+
→ check-spec (find specific issues)
|
|
186
|
+
→ validate-spec (confirm structure)
|
|
187
|
+
|
|
188
|
+
User asks "what types are in this spec?"
|
|
189
|
+
→ get-spec-info
|
|
190
|
+
|
|
191
|
+
User asks "what languages are supported?"
|
|
192
|
+
→ list-languages
|
|
193
|
+
|
|
194
|
+
User wants to generate types?
|
|
195
|
+
→ Ensure spec is valid first (check-spec + validate-spec)
|
|
196
|
+
→ generate-types
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**IMPORTANT: Always use check-spec IMMEDIATELY after creating or modifying a spec file. It will catch common mistakes like `nullable: true`, `optional: true`, wrong file paths, and more.**
|
|
200
|
+
|
|
124
201
|
## Complete Workflows
|
|
125
202
|
|
|
126
203
|
### Workflow 1: Creating Types from Scratch
|
|
@@ -130,7 +207,8 @@ User: "I need types for my e-commerce cart API"
|
|
|
130
207
|
|
|
131
208
|
Step 1: Learn the rules
|
|
132
209
|
→ Call: get-spec-rules
|
|
133
|
-
→ Read and understand: nullable types, arrays, structure
|
|
210
|
+
→ Read and understand: common mistakes, nullable types, arrays, structure
|
|
211
|
+
→ Pay special attention to the "Common Mistakes" section
|
|
134
212
|
|
|
135
213
|
Step 2: Gather requirements
|
|
136
214
|
→ Ask user: What fields does the cart have?
|
|
@@ -140,16 +218,22 @@ Step 3: Create YAML spec
|
|
|
140
218
|
→ Create spec file following the rules
|
|
141
219
|
→ Remember: Properties not in 'required' → Type | null
|
|
142
220
|
→ Remember: Arrays must be properties, not top-level
|
|
221
|
+
→ Remember: NO nullable/optional properties, use 'required' array
|
|
222
|
+
|
|
223
|
+
Step 4: Check for common mistakes
|
|
224
|
+
→ Call: check-spec with the spec path
|
|
225
|
+
→ Review any issues or warnings with line numbers
|
|
226
|
+
→ Fix all issues before proceeding
|
|
143
227
|
|
|
144
|
-
Step
|
|
228
|
+
Step 5: Validate structure
|
|
145
229
|
→ Call: validate-spec with the spec path
|
|
146
|
-
→
|
|
230
|
+
→ Confirm type counts are correct
|
|
147
231
|
|
|
148
|
-
Step
|
|
232
|
+
Step 6: Generate
|
|
149
233
|
→ Call: generate-types with language and paths
|
|
150
234
|
→ Types are now available in output directory
|
|
151
235
|
|
|
152
|
-
Step
|
|
236
|
+
Step 7: Confirm
|
|
153
237
|
→ Tell user where types were generated
|
|
154
238
|
→ Optionally show a snippet of generated types
|
|
155
239
|
```
|
|
@@ -161,6 +245,7 @@ User: "Add a 'description' field to the Product type"
|
|
|
161
245
|
|
|
162
246
|
Step 1: Understand current spec
|
|
163
247
|
→ Call: get-spec-info to see what types exist
|
|
248
|
+
→ Identify where Product type is defined
|
|
164
249
|
→ Optionally: get-spec-rules to refresh on rules
|
|
165
250
|
|
|
166
251
|
Step 2: Read the spec file
|
|
@@ -169,11 +254,16 @@ Step 2: Read the spec file
|
|
|
169
254
|
Step 3: Modify the spec
|
|
170
255
|
→ Add the new field
|
|
171
256
|
→ Decide if it's required or optional (Type | null)
|
|
257
|
+
→ Use 'required' array ONLY, no nullable/optional properties
|
|
172
258
|
|
|
173
|
-
Step 4:
|
|
259
|
+
Step 4: Check for mistakes
|
|
260
|
+
→ Call: check-spec with the modified spec path
|
|
261
|
+
→ Fix any issues found
|
|
262
|
+
|
|
263
|
+
Step 5: Validate structure
|
|
174
264
|
→ Call: validate-spec
|
|
175
265
|
|
|
176
|
-
Step
|
|
266
|
+
Step 6: Regenerate
|
|
177
267
|
→ Call: generate-types to update TypeScript files
|
|
178
268
|
```
|
|
179
269
|
|
|
@@ -182,14 +272,18 @@ Step 5: Regenerate
|
|
|
182
272
|
```
|
|
183
273
|
User: "Can you check if my spec is valid?"
|
|
184
274
|
|
|
185
|
-
Step 1:
|
|
275
|
+
Step 1: Check for common mistakes
|
|
276
|
+
→ Call: check-spec with their spec path
|
|
277
|
+
→ Report any issues with line numbers
|
|
278
|
+
|
|
279
|
+
Step 2: Validate structure (if no issues)
|
|
186
280
|
→ Call: validate-spec with their spec path
|
|
187
281
|
|
|
188
|
-
Step
|
|
282
|
+
Step 3: Report results
|
|
189
283
|
→ If valid: Confirm and show summary
|
|
190
|
-
→ If
|
|
284
|
+
→ If issues: Explain errors and suggest fixes
|
|
191
285
|
|
|
192
|
-
Step
|
|
286
|
+
Step 4: Optionally generate
|
|
193
287
|
→ Ask if they want to generate types now
|
|
194
288
|
→ Call: generate-types if yes
|
|
195
289
|
```
|
package/src/SPEC_RULES.md
CHANGED
|
@@ -6,6 +6,8 @@ Complete guide for writing Type Crafter YAML specifications and understanding Ty
|
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
9
|
+
- [🚨 Common Mistakes (Read This First)](#-common-mistakes-read-this-first)
|
|
10
|
+
- [Multi-File Specifications](#multi-file-specifications)
|
|
9
11
|
- [Root Structure](#root-structure)
|
|
10
12
|
- [Data Types & Type Mapping](#data-types--type-mapping)
|
|
11
13
|
- [Constraints & Limitations](#constraints--limitations)
|
|
@@ -20,6 +22,359 @@ Complete guide for writing Type Crafter YAML specifications and understanding Ty
|
|
|
20
22
|
|
|
21
23
|
---
|
|
22
24
|
|
|
25
|
+
## 🚨 Common Mistakes (Read This First)
|
|
26
|
+
|
|
27
|
+
### ❌ NEVER Do These
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
# ❌ WRONG: Adding 'nullable: true' property
|
|
31
|
+
User:
|
|
32
|
+
type: object
|
|
33
|
+
properties:
|
|
34
|
+
name:
|
|
35
|
+
type: string
|
|
36
|
+
nullable: true # ❌ INVALID! This property does NOT exist
|
|
37
|
+
|
|
38
|
+
# ❌ WRONG: Using 'optional: true' property
|
|
39
|
+
User:
|
|
40
|
+
type: object
|
|
41
|
+
properties:
|
|
42
|
+
name:
|
|
43
|
+
type: string
|
|
44
|
+
optional: true # ❌ INVALID! This property does NOT exist
|
|
45
|
+
|
|
46
|
+
# ❌ WRONG: Using '?' suffix for optional
|
|
47
|
+
User:
|
|
48
|
+
type: object
|
|
49
|
+
properties:
|
|
50
|
+
name?: # ❌ INVALID! Don't use ? suffix
|
|
51
|
+
type: string
|
|
52
|
+
|
|
53
|
+
# ❌ WRONG: Creating top-level array types
|
|
54
|
+
Tags:
|
|
55
|
+
type: array # ❌ INVALID! Arrays cannot be top-level types
|
|
56
|
+
items:
|
|
57
|
+
type: string
|
|
58
|
+
|
|
59
|
+
# ❌ WRONG: Using relative paths from current file
|
|
60
|
+
# File: src/api/users.yaml
|
|
61
|
+
User:
|
|
62
|
+
type: object
|
|
63
|
+
properties:
|
|
64
|
+
profile:
|
|
65
|
+
$ref: './profile.yaml#/Profile' # ❌ WRONG! Not relative to current file
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### ✅ CORRECT Ways
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
# ✅ CORRECT: Use 'required' array to control nullable
|
|
72
|
+
User:
|
|
73
|
+
type: object
|
|
74
|
+
required:
|
|
75
|
+
- id # id is required (NOT nullable)
|
|
76
|
+
# name is NOT in required, so it's nullable
|
|
77
|
+
properties:
|
|
78
|
+
id:
|
|
79
|
+
type: string # Generates: string
|
|
80
|
+
name:
|
|
81
|
+
type: string # Generates: string | null (because not in required)
|
|
82
|
+
|
|
83
|
+
# ✅ CORRECT: Arrays must be properties within objects
|
|
84
|
+
Post:
|
|
85
|
+
type: object
|
|
86
|
+
properties:
|
|
87
|
+
tags:
|
|
88
|
+
type: array
|
|
89
|
+
items:
|
|
90
|
+
type: string
|
|
91
|
+
|
|
92
|
+
# ✅ CORRECT: Use paths from project root
|
|
93
|
+
# File: src/api/users.yaml (running type-crafter from project root)
|
|
94
|
+
User:
|
|
95
|
+
type: object
|
|
96
|
+
properties:
|
|
97
|
+
profile:
|
|
98
|
+
$ref: './src/api/profile.yaml#/Profile' # ✅ From project root
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 🔑 Key Rules to Remember
|
|
102
|
+
|
|
103
|
+
1. **Nullable is controlled by `required` array ONLY**
|
|
104
|
+
- In `required` → generates `Type`
|
|
105
|
+
- NOT in `required` → generates `Type | null`
|
|
106
|
+
- No properties like `nullable`, `optional`, `?` exist
|
|
107
|
+
|
|
108
|
+
2. **Arrays cannot be top-level types**
|
|
109
|
+
- Arrays MUST be properties within objects
|
|
110
|
+
- Use `type: array` with `items` specification
|
|
111
|
+
|
|
112
|
+
3. **File paths are from project root**
|
|
113
|
+
- NOT relative to current YAML file
|
|
114
|
+
- Always use `./path/from/root/file.yaml#/Type`
|
|
115
|
+
|
|
116
|
+
4. **Every spec file needs `info` section**
|
|
117
|
+
- Even sub-files need `info: { version, title }`
|
|
118
|
+
- Top-level file and all referenced files must have `info`
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Multi-File Specifications
|
|
123
|
+
|
|
124
|
+
### File Organization Patterns
|
|
125
|
+
|
|
126
|
+
#### Pattern 1: Single Top-Level File
|
|
127
|
+
|
|
128
|
+
**Best for:** Small to medium projects
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
project/
|
|
132
|
+
├── types.yaml # Single file with all types
|
|
133
|
+
└── src/
|
|
134
|
+
└── index.ts
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**types.yaml:**
|
|
138
|
+
```yaml
|
|
139
|
+
info:
|
|
140
|
+
version: '1.0.0'
|
|
141
|
+
title: 'Project Types'
|
|
142
|
+
|
|
143
|
+
types:
|
|
144
|
+
User:
|
|
145
|
+
type: object
|
|
146
|
+
properties:
|
|
147
|
+
id: { type: string }
|
|
148
|
+
|
|
149
|
+
Post:
|
|
150
|
+
type: object
|
|
151
|
+
properties:
|
|
152
|
+
author: { $ref: '#/types/User' }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Pattern 2: Multiple Files with Main Entry Point
|
|
156
|
+
|
|
157
|
+
**Best for:** Large projects with logical domain separation
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
project/
|
|
161
|
+
├── types/
|
|
162
|
+
│ ├── index.yaml # Main entry point (top-level file)
|
|
163
|
+
│ ├── user.yaml # User-related types (sub-file)
|
|
164
|
+
│ ├── post.yaml # Post-related types (sub-file)
|
|
165
|
+
│ └── comment.yaml # Comment-related types (sub-file)
|
|
166
|
+
└── src/
|
|
167
|
+
└── index.ts
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**types/index.yaml (Top-level file):**
|
|
171
|
+
```yaml
|
|
172
|
+
info:
|
|
173
|
+
version: '1.0.0'
|
|
174
|
+
title: 'Main API Types'
|
|
175
|
+
|
|
176
|
+
# Import types from other files
|
|
177
|
+
types:
|
|
178
|
+
# Reference to external file (from project root)
|
|
179
|
+
User:
|
|
180
|
+
$ref: './types/user.yaml#/User'
|
|
181
|
+
|
|
182
|
+
Post:
|
|
183
|
+
$ref: './types/post.yaml#/Post'
|
|
184
|
+
|
|
185
|
+
Comment:
|
|
186
|
+
$ref: './types/comment.yaml#/Comment'
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**types/user.yaml (Sub-file):**
|
|
190
|
+
```yaml
|
|
191
|
+
info:
|
|
192
|
+
version: '1.0.0'
|
|
193
|
+
title: 'User Types'
|
|
194
|
+
|
|
195
|
+
types:
|
|
196
|
+
User:
|
|
197
|
+
type: object
|
|
198
|
+
required:
|
|
199
|
+
- id
|
|
200
|
+
- email
|
|
201
|
+
properties:
|
|
202
|
+
id:
|
|
203
|
+
type: string
|
|
204
|
+
email:
|
|
205
|
+
type: string
|
|
206
|
+
name:
|
|
207
|
+
type: string # nullable (not in required)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**types/post.yaml (Sub-file):**
|
|
211
|
+
```yaml
|
|
212
|
+
info:
|
|
213
|
+
version: '1.0.0'
|
|
214
|
+
title: 'Post Types'
|
|
215
|
+
|
|
216
|
+
types:
|
|
217
|
+
Post:
|
|
218
|
+
type: object
|
|
219
|
+
required:
|
|
220
|
+
- id
|
|
221
|
+
- title
|
|
222
|
+
- author
|
|
223
|
+
properties:
|
|
224
|
+
id:
|
|
225
|
+
type: string
|
|
226
|
+
title:
|
|
227
|
+
type: string
|
|
228
|
+
author:
|
|
229
|
+
# Reference to user.yaml from project root
|
|
230
|
+
$ref: './types/user.yaml#/User'
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### Pattern 3: Grouped Types with External References
|
|
234
|
+
|
|
235
|
+
**Best for:** Complex domains with many related types
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
project/
|
|
239
|
+
├── specs/
|
|
240
|
+
│ ├── api.yaml # Main entry point
|
|
241
|
+
│ ├── auth/
|
|
242
|
+
│ │ └── types.yaml # Auth domain types
|
|
243
|
+
│ └── shop/
|
|
244
|
+
│ ├── cart.yaml # Shopping cart types
|
|
245
|
+
│ └── product.yaml # Product types
|
|
246
|
+
└── src/
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**specs/api.yaml (Top-level file):**
|
|
250
|
+
```yaml
|
|
251
|
+
info:
|
|
252
|
+
version: '1.0.0'
|
|
253
|
+
title: 'API Specification'
|
|
254
|
+
|
|
255
|
+
groupedTypes:
|
|
256
|
+
# Import entire groups from external files
|
|
257
|
+
Auth:
|
|
258
|
+
$ref: './specs/auth/types.yaml#/AuthTypes'
|
|
259
|
+
|
|
260
|
+
Shop:
|
|
261
|
+
Cart:
|
|
262
|
+
$ref: './specs/shop/cart.yaml#/ShopTypes/Cart'
|
|
263
|
+
Product:
|
|
264
|
+
$ref: './specs/shop/product.yaml#/ShopTypes/Product'
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**specs/auth/types.yaml:**
|
|
268
|
+
```yaml
|
|
269
|
+
info:
|
|
270
|
+
version: '1.0.0'
|
|
271
|
+
title: 'Authentication Types'
|
|
272
|
+
|
|
273
|
+
groupedTypes:
|
|
274
|
+
AuthTypes:
|
|
275
|
+
LoginRequest:
|
|
276
|
+
type: object
|
|
277
|
+
required:
|
|
278
|
+
- email
|
|
279
|
+
- password
|
|
280
|
+
properties:
|
|
281
|
+
email: { type: string }
|
|
282
|
+
password: { type: string }
|
|
283
|
+
|
|
284
|
+
LoginResponse:
|
|
285
|
+
type: object
|
|
286
|
+
required:
|
|
287
|
+
- token
|
|
288
|
+
- user
|
|
289
|
+
properties:
|
|
290
|
+
token: { type: string }
|
|
291
|
+
user:
|
|
292
|
+
$ref: './specs/user.yaml#/User' # Cross-file reference
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Multi-File Reference Rules
|
|
296
|
+
|
|
297
|
+
#### 1. Every File Needs `info` Section
|
|
298
|
+
|
|
299
|
+
```yaml
|
|
300
|
+
# ✅ CORRECT: All files need info section
|
|
301
|
+
info:
|
|
302
|
+
version: '1.0.0'
|
|
303
|
+
title: 'File Title'
|
|
304
|
+
|
|
305
|
+
types:
|
|
306
|
+
# ... your types
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### 2. Reference Format
|
|
310
|
+
|
|
311
|
+
```yaml
|
|
312
|
+
# Local reference (same file)
|
|
313
|
+
$ref: '#/types/TypeName'
|
|
314
|
+
$ref: '#/groupedTypes/GroupName/TypeName'
|
|
315
|
+
|
|
316
|
+
# External reference (from project root)
|
|
317
|
+
$ref: './path/from/root/file.yaml#/TypeName'
|
|
318
|
+
$ref: './path/from/root/file.yaml#/GroupName/TypeName'
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### 3. Path Resolution
|
|
322
|
+
|
|
323
|
+
**CRITICAL:** All paths are from **project root** (where you run `type-crafter` command)
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# If you run this command from /project
|
|
327
|
+
cd /project
|
|
328
|
+
type-crafter generate typescript ./specs/api.yaml ./output
|
|
329
|
+
|
|
330
|
+
# Then all $ref paths in ANY file are relative to /project
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Example:**
|
|
334
|
+
```
|
|
335
|
+
/project/
|
|
336
|
+
├── specs/
|
|
337
|
+
│ ├── api.yaml # File A
|
|
338
|
+
│ └── user/
|
|
339
|
+
│ └── types.yaml # File B
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**In specs/api.yaml:**
|
|
343
|
+
```yaml
|
|
344
|
+
# ✅ CORRECT: Path from project root
|
|
345
|
+
User:
|
|
346
|
+
$ref: './specs/user/types.yaml#/User'
|
|
347
|
+
|
|
348
|
+
# ❌ WRONG: Don't use relative path from current file
|
|
349
|
+
User:
|
|
350
|
+
$ref: './user/types.yaml#/User'
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**In specs/user/types.yaml:**
|
|
354
|
+
```yaml
|
|
355
|
+
# ✅ CORRECT: Path from project root
|
|
356
|
+
Profile:
|
|
357
|
+
$ref: './specs/api.yaml#/ApiTypes/Profile'
|
|
358
|
+
|
|
359
|
+
# ❌ WRONG: Don't use relative path
|
|
360
|
+
Profile:
|
|
361
|
+
$ref: '../api.yaml#/ApiTypes/Profile'
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Generating Types from Multi-File Specs
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# Generate from top-level file (references will be resolved automatically)
|
|
368
|
+
type-crafter generate typescript ./types/index.yaml ./output
|
|
369
|
+
|
|
370
|
+
# Or from any entry point
|
|
371
|
+
type-crafter generate typescript ./specs/api.yaml ./src/types
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
All referenced files will be automatically processed and types generated.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
23
378
|
## Root Structure
|
|
24
379
|
|
|
25
380
|
### Required Fields
|
|
@@ -124,40 +479,242 @@ Only these can be top-level types:
|
|
|
124
479
|
|
|
125
480
|
**Properties NOT in `required` array become nullable (`Type | null`)**
|
|
126
481
|
|
|
482
|
+
This is controlled EXCLUSIVELY by the `required` array. There are NO other properties or syntax for controlling nullability.
|
|
483
|
+
|
|
484
|
+
### ❌ WRONG: What NOT to Do
|
|
485
|
+
|
|
486
|
+
```yaml
|
|
487
|
+
# ❌ WRONG: Do NOT use 'nullable' property
|
|
488
|
+
User:
|
|
489
|
+
type: object
|
|
490
|
+
properties:
|
|
491
|
+
name:
|
|
492
|
+
type: string
|
|
493
|
+
nullable: true # ❌ INVALID! This property does NOT exist in Type Crafter
|
|
494
|
+
|
|
495
|
+
# ❌ WRONG: Do NOT use 'optional' property
|
|
496
|
+
User:
|
|
497
|
+
type: object
|
|
498
|
+
properties:
|
|
499
|
+
name:
|
|
500
|
+
type: string
|
|
501
|
+
optional: true # ❌ INVALID! This property does NOT exist
|
|
502
|
+
|
|
503
|
+
# ❌ WRONG: Do NOT use '?' suffix
|
|
504
|
+
User:
|
|
505
|
+
type: object
|
|
506
|
+
properties:
|
|
507
|
+
name?: # ❌ INVALID! Don't use ? in property names
|
|
508
|
+
type: string
|
|
509
|
+
|
|
510
|
+
# ❌ WRONG: Do NOT use null in type
|
|
511
|
+
User:
|
|
512
|
+
type: object
|
|
513
|
+
properties:
|
|
514
|
+
name:
|
|
515
|
+
type: [string, null] # ❌ INVALID! Don't use array of types here
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### ✅ CORRECT: Use `required` Array Only
|
|
519
|
+
|
|
127
520
|
```yaml
|
|
521
|
+
# ✅ CORRECT: Control nullability with 'required' array
|
|
128
522
|
User:
|
|
129
523
|
type: object
|
|
130
524
|
required:
|
|
131
|
-
- id
|
|
132
|
-
- email
|
|
525
|
+
- id # id is required → generates: string
|
|
526
|
+
- email # email is required → generates: string
|
|
527
|
+
# name is NOT in required → generates: string | null
|
|
528
|
+
# age is NOT in required → generates: number | null
|
|
133
529
|
properties:
|
|
134
530
|
id:
|
|
135
531
|
type: string
|
|
136
532
|
email:
|
|
137
533
|
type: string
|
|
138
534
|
name:
|
|
139
|
-
type: string
|
|
535
|
+
type: string
|
|
140
536
|
age:
|
|
141
|
-
type: number
|
|
537
|
+
type: number
|
|
142
538
|
```
|
|
143
539
|
|
|
144
540
|
**TypeScript Output:**
|
|
145
541
|
|
|
146
542
|
```typescript
|
|
147
543
|
export type User = {
|
|
148
|
-
id: string; // Required
|
|
149
|
-
email: string; // Required
|
|
150
|
-
name: string | null; //
|
|
151
|
-
age: number | null; //
|
|
544
|
+
id: string; // Required (in required array)
|
|
545
|
+
email: string; // Required (in required array)
|
|
546
|
+
name: string | null; // Nullable (NOT in required array)
|
|
547
|
+
age: number | null; // Nullable (NOT in required array)
|
|
152
548
|
};
|
|
153
549
|
```
|
|
154
550
|
|
|
155
|
-
### Rules
|
|
551
|
+
### Detailed Rules
|
|
552
|
+
|
|
553
|
+
1. ✅ **Property in `required` array** → Generates `Type` (NOT nullable)
|
|
554
|
+
|
|
555
|
+
```yaml
|
|
556
|
+
User:
|
|
557
|
+
type: object
|
|
558
|
+
required:
|
|
559
|
+
- name
|
|
560
|
+
properties:
|
|
561
|
+
name: { type: string }
|
|
562
|
+
# Generates: name: string
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
2. ✅ **Property NOT in `required` array** → Generates `Type | null` (nullable)
|
|
566
|
+
|
|
567
|
+
```yaml
|
|
568
|
+
User:
|
|
569
|
+
type: object
|
|
570
|
+
required:
|
|
571
|
+
- id
|
|
572
|
+
properties:
|
|
573
|
+
id: { type: string }
|
|
574
|
+
name: { type: string } # NOT in required
|
|
575
|
+
# Generates: name: string | null
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
3. ✅ **No `required` array at all** → All properties are `Type | null`
|
|
579
|
+
|
|
580
|
+
```yaml
|
|
581
|
+
User:
|
|
582
|
+
type: object
|
|
583
|
+
# No required array
|
|
584
|
+
properties:
|
|
585
|
+
id: { type: string }
|
|
586
|
+
name: { type: string }
|
|
587
|
+
# Generates: id: string | null, name: string | null
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
4. ✅ **Empty `required: []`** → All properties are `Type | null`
|
|
591
|
+
```yaml
|
|
592
|
+
User:
|
|
593
|
+
type: object
|
|
594
|
+
required: [] # Empty array
|
|
595
|
+
properties:
|
|
596
|
+
id: { type: string }
|
|
597
|
+
# Generates: id: string | null
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Complete Examples
|
|
601
|
+
|
|
602
|
+
#### Example 1: All Properties Required
|
|
156
603
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
604
|
+
```yaml
|
|
605
|
+
User:
|
|
606
|
+
type: object
|
|
607
|
+
required:
|
|
608
|
+
- id
|
|
609
|
+
- email
|
|
610
|
+
- name
|
|
611
|
+
- age
|
|
612
|
+
properties:
|
|
613
|
+
id: { type: string }
|
|
614
|
+
email: { type: string }
|
|
615
|
+
name: { type: string }
|
|
616
|
+
age: { type: number }
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
```typescript
|
|
620
|
+
// Generated TypeScript
|
|
621
|
+
export type User = {
|
|
622
|
+
id: string;
|
|
623
|
+
email: string;
|
|
624
|
+
name: string;
|
|
625
|
+
age: number;
|
|
626
|
+
};
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
#### Example 2: Some Properties Required
|
|
630
|
+
|
|
631
|
+
```yaml
|
|
632
|
+
User:
|
|
633
|
+
type: object
|
|
634
|
+
required:
|
|
635
|
+
- id
|
|
636
|
+
- email
|
|
637
|
+
properties:
|
|
638
|
+
id: { type: string }
|
|
639
|
+
email: { type: string }
|
|
640
|
+
name: { type: string }
|
|
641
|
+
age: { type: number }
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
// Generated TypeScript
|
|
646
|
+
export type User = {
|
|
647
|
+
id: string;
|
|
648
|
+
email: string;
|
|
649
|
+
name: string | null;
|
|
650
|
+
age: number | null;
|
|
651
|
+
};
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
#### Example 3: No Properties Required
|
|
655
|
+
|
|
656
|
+
```yaml
|
|
657
|
+
User:
|
|
658
|
+
type: object
|
|
659
|
+
properties:
|
|
660
|
+
id: { type: string }
|
|
661
|
+
email: { type: string }
|
|
662
|
+
name: { type: string }
|
|
663
|
+
age: { type: number }
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
// Generated TypeScript - All nullable
|
|
668
|
+
export type User = {
|
|
669
|
+
id: string | null;
|
|
670
|
+
email: string | null;
|
|
671
|
+
name: string | null;
|
|
672
|
+
age: number | null;
|
|
673
|
+
};
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### Nested Objects and Arrays
|
|
677
|
+
|
|
678
|
+
```yaml
|
|
679
|
+
User:
|
|
680
|
+
type: object
|
|
681
|
+
required:
|
|
682
|
+
- profile
|
|
683
|
+
- posts
|
|
684
|
+
properties:
|
|
685
|
+
profile:
|
|
686
|
+
type: object
|
|
687
|
+
required:
|
|
688
|
+
- name
|
|
689
|
+
properties:
|
|
690
|
+
name: { type: string }
|
|
691
|
+
bio: { type: string } # NOT in profile.required
|
|
692
|
+
posts:
|
|
693
|
+
type: array
|
|
694
|
+
items:
|
|
695
|
+
type: object
|
|
696
|
+
required:
|
|
697
|
+
- title
|
|
698
|
+
properties:
|
|
699
|
+
title: { type: string }
|
|
700
|
+
content: { type: string } # NOT in items.required
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
// Generated TypeScript
|
|
705
|
+
export type User = {
|
|
706
|
+
profile: {
|
|
707
|
+
// profile is required (in User.required)
|
|
708
|
+
name: string; // required in profile
|
|
709
|
+
bio: string | null; // NOT required in profile
|
|
710
|
+
};
|
|
711
|
+
posts: Array<{
|
|
712
|
+
// posts array is required
|
|
713
|
+
title: string; // required in post item
|
|
714
|
+
content: string | null; // NOT required in post item
|
|
715
|
+
}>;
|
|
716
|
+
};
|
|
717
|
+
```
|
|
161
718
|
|
|
162
719
|
---
|
|
163
720
|
|