@type-crafter/mcp 0.4.0 → 0.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/README.md CHANGED
@@ -268,20 +268,6 @@ npm run build
268
268
  npm run dev
269
269
  ```
270
270
 
271
- ## Development
272
-
273
- ### Building from Source
274
-
275
- ```bash
276
- npm run build
277
- ```
278
-
279
- ### Watch Mode
280
-
281
- ```bash
282
- npm run dev
283
- ```
284
-
285
271
  ### Linting
286
272
 
287
273
  ```bash
package/dist/index.js CHANGED
@@ -32,6 +32,9 @@ const getSpecInfoSchema = z.object({
32
32
  specFilePath: z.string().describe('Path to the YAML specification file'),
33
33
  });
34
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
+ });
35
38
  // Type guards
36
39
  function isRecord(value) {
37
40
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -387,6 +390,187 @@ server.registerTool('get-spec-rules', {
387
390
  };
388
391
  }
389
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
+ });
390
574
  // Start the server
391
575
  async function main() {
392
576
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type-crafter/mcp",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "MCP server for Type Crafter - generate types from YAML specifications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
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, etc.)
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
- ### 2. `validate-spec`
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
- **When to use:** After creating or modifying a YAML spec
59
+ ### 2. `check-spec` 🔍
60
60
 
61
- **What it does:** Validates the spec structure without generating types
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. Check if valid or see error messages
70
- 3. Fix errors if needed and validate again
115
+ 2. Get confirmation with type counts
116
+ 3. Proceed to generation
71
117
  ```
72
118
 
73
- ### 3. `generate-types` 🔨
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
- ### 4. `get-spec-info` 📊
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
- ### 5. `list-languages` 📝
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 4: Validate
228
+ Step 5: Validate structure
145
229
  → Call: validate-spec with the spec path
146
- Fix any errors
230
+ Confirm type counts are correct
147
231
 
148
- Step 5: Generate
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 6: Confirm
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: Validate
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 5: Regenerate
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: Validate
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 2: Report results
282
+ Step 3: Report results
189
283
  → If valid: Confirm and show summary
190
- → If invalid: Explain errors and suggest fixes
284
+ → If issues: Explain errors and suggest fixes
191
285
 
192
- Step 3: Optionally generate
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,418 @@ 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
+ # ❌ WRONG: Using # reference in non-top file (file without info section)
68
+ # File: docs/cart.yaml (NON-TOP FILE - no info section)
69
+ Cart:
70
+ SCart:
71
+ type: object
72
+ properties:
73
+ items:
74
+ type: array
75
+ items:
76
+ $ref: '#/Cart/SCartItem' # ❌ WRONG! Must use full path in non-top files
77
+
78
+ # ❌ WRONG: Using full path in top file for same-file reference
79
+ # File: types.yaml (TOP FILE - has info section)
80
+ info:
81
+ version: '1.0.0'
82
+ title: 'Types'
83
+ types:
84
+ User:
85
+ type: object
86
+ properties:
87
+ profile:
88
+ $ref: './types.yaml#/types/Profile' # ❌ WRONG! Use # in top files for same-file refs
89
+ ```
90
+
91
+ ### ✅ CORRECT Ways
92
+
93
+ ```yaml
94
+ # ✅ CORRECT: Use 'required' array to control nullable
95
+ User:
96
+ type: object
97
+ required:
98
+ - id # id is required (NOT nullable)
99
+ # name is NOT in required, so it's nullable
100
+ properties:
101
+ id:
102
+ type: string # Generates: string
103
+ name:
104
+ type: string # Generates: string | null (because not in required)
105
+
106
+ # ✅ CORRECT: Arrays must be properties within objects
107
+ Post:
108
+ type: object
109
+ properties:
110
+ tags:
111
+ type: array
112
+ items:
113
+ type: string
114
+
115
+ # ✅ CORRECT: Use paths from project root
116
+ # File: src/api/users.yaml (running type-crafter from project root)
117
+ User:
118
+ type: object
119
+ properties:
120
+ profile:
121
+ $ref: './src/api/profile.yaml#/Profile' # ✅ From project root
122
+
123
+ # ✅ CORRECT: Top file using # for same-file references
124
+ # File: types.yaml (TOP FILE - has info section)
125
+ info:
126
+ version: '1.0.0'
127
+ title: 'API Types'
128
+ types:
129
+ User:
130
+ type: object
131
+ properties:
132
+ profile:
133
+ $ref: '#/types/Profile' # ✅ Use # in top files for same-file refs
134
+ Profile:
135
+ type: object
136
+ properties:
137
+ bio: { type: string }
138
+
139
+ # ✅ CORRECT: Non-top file using full path for all references
140
+ # File: docs/cart.yaml (NON-TOP FILE - no info section)
141
+ Cart:
142
+ SCart:
143
+ type: object
144
+ properties:
145
+ items:
146
+ type: array
147
+ items:
148
+ $ref: './docs/cart.yaml#/Cart/SCartItem' # ✅ Full path in non-top files
149
+ SCartItem:
150
+ type: object
151
+ properties:
152
+ id: { type: number }
153
+ ```
154
+
155
+ ### 🔑 Key Rules to Remember
156
+
157
+ 1. **Nullable is controlled by `required` array ONLY**
158
+ - In `required` → generates `Type`
159
+ - NOT in `required` → generates `Type | null`
160
+ - No properties like `nullable`, `optional`, `?` exist
161
+
162
+ 2. **Arrays cannot be top-level types**
163
+ - Arrays MUST be properties within objects
164
+ - Use `type: array` with `items` specification
165
+
166
+ 3. **File paths are from project root**
167
+ - NOT relative to current YAML file
168
+ - Always use `./path/from/root/file.yaml#/Type`
169
+
170
+ 4. **Referencing depends on file type**
171
+ - **Top file** (has `info:` at root): Use `#/types/TypeName` for same-file refs
172
+ - **Non-top file** (no `info:` at root): MUST use `'./path/from/root/file.yaml#/TypeName'` for ALL refs
173
+ - Identify top files by presence of `info` section at root level
174
+
175
+ 5. **Top file is required for generation**
176
+ - Only files with `info:` section can be used with `type-crafter generate`
177
+ - Non-top files must be referenced from a top file
178
+
179
+ ---
180
+
181
+ ## Multi-File Specifications
182
+
183
+ ### File Organization Patterns
184
+
185
+ #### Pattern 1: Single Top-Level File
186
+
187
+ **Best for:** Small to medium projects
188
+
189
+ ```
190
+ project/
191
+ ├── types.yaml # Single file with all types
192
+ └── src/
193
+ └── index.ts
194
+ ```
195
+
196
+ **types.yaml:**
197
+ ```yaml
198
+ info:
199
+ version: '1.0.0'
200
+ title: 'Project Types'
201
+
202
+ types:
203
+ User:
204
+ type: object
205
+ properties:
206
+ id: { type: string }
207
+
208
+ Post:
209
+ type: object
210
+ properties:
211
+ author: { $ref: '#/types/User' }
212
+ ```
213
+
214
+ #### Pattern 2: Multiple Files with Main Entry Point
215
+
216
+ **Best for:** Large projects with logical domain separation
217
+
218
+ ```
219
+ project/
220
+ ├── types/
221
+ │ ├── index.yaml # Main entry point (top-level file)
222
+ │ ├── user.yaml # User-related types (sub-file)
223
+ │ ├── post.yaml # Post-related types (sub-file)
224
+ │ └── comment.yaml # Comment-related types (sub-file)
225
+ └── src/
226
+ └── index.ts
227
+ ```
228
+
229
+ **types/index.yaml (Top-level file):**
230
+ ```yaml
231
+ info:
232
+ version: '1.0.0'
233
+ title: 'Main API Types'
234
+
235
+ # Import types from other files
236
+ types:
237
+ # Reference to external file (from project root)
238
+ User:
239
+ $ref: './types/user.yaml#/User'
240
+
241
+ Post:
242
+ $ref: './types/post.yaml#/Post'
243
+
244
+ Comment:
245
+ $ref: './types/comment.yaml#/Comment'
246
+ ```
247
+
248
+ **types/user.yaml (Sub-file):**
249
+ ```yaml
250
+ info:
251
+ version: '1.0.0'
252
+ title: 'User Types'
253
+
254
+ types:
255
+ User:
256
+ type: object
257
+ required:
258
+ - id
259
+ - email
260
+ properties:
261
+ id:
262
+ type: string
263
+ email:
264
+ type: string
265
+ name:
266
+ type: string # nullable (not in required)
267
+ ```
268
+
269
+ **types/post.yaml (Sub-file):**
270
+ ```yaml
271
+ info:
272
+ version: '1.0.0'
273
+ title: 'Post Types'
274
+
275
+ types:
276
+ Post:
277
+ type: object
278
+ required:
279
+ - id
280
+ - title
281
+ - author
282
+ properties:
283
+ id:
284
+ type: string
285
+ title:
286
+ type: string
287
+ author:
288
+ # Reference to user.yaml from project root
289
+ $ref: './types/user.yaml#/User'
290
+ ```
291
+
292
+ #### Pattern 3: Grouped Types with External References
293
+
294
+ **Best for:** Complex domains with many related types
295
+
296
+ ```
297
+ project/
298
+ ├── specs/
299
+ │ ├── api.yaml # Main entry point
300
+ │ ├── auth/
301
+ │ │ └── types.yaml # Auth domain types
302
+ │ └── shop/
303
+ │ ├── cart.yaml # Shopping cart types
304
+ │ └── product.yaml # Product types
305
+ └── src/
306
+ ```
307
+
308
+ **specs/api.yaml (Top-level file):**
309
+ ```yaml
310
+ info:
311
+ version: '1.0.0'
312
+ title: 'API Specification'
313
+
314
+ groupedTypes:
315
+ # Import entire groups from external files
316
+ Auth:
317
+ $ref: './specs/auth/types.yaml#/AuthTypes'
318
+
319
+ Shop:
320
+ Cart:
321
+ $ref: './specs/shop/cart.yaml#/ShopTypes/Cart'
322
+ Product:
323
+ $ref: './specs/shop/product.yaml#/ShopTypes/Product'
324
+ ```
325
+
326
+ **specs/auth/types.yaml:**
327
+ ```yaml
328
+ info:
329
+ version: '1.0.0'
330
+ title: 'Authentication Types'
331
+
332
+ groupedTypes:
333
+ AuthTypes:
334
+ LoginRequest:
335
+ type: object
336
+ required:
337
+ - email
338
+ - password
339
+ properties:
340
+ email: { type: string }
341
+ password: { type: string }
342
+
343
+ LoginResponse:
344
+ type: object
345
+ required:
346
+ - token
347
+ - user
348
+ properties:
349
+ token: { type: string }
350
+ user:
351
+ $ref: './specs/user.yaml#/User' # Cross-file reference
352
+ ```
353
+
354
+ ### Multi-File Reference Rules
355
+
356
+ #### 1. Every File Needs `info` Section
357
+
358
+ ```yaml
359
+ # ✅ CORRECT: All files need info section
360
+ info:
361
+ version: '1.0.0'
362
+ title: 'File Title'
363
+
364
+ types:
365
+ # ... your types
366
+ ```
367
+
368
+ #### 2. Reference Format
369
+
370
+ ```yaml
371
+ # Local reference (same file)
372
+ $ref: '#/types/TypeName'
373
+ $ref: '#/groupedTypes/GroupName/TypeName'
374
+
375
+ # External reference (from project root)
376
+ $ref: './path/from/root/file.yaml#/TypeName'
377
+ $ref: './path/from/root/file.yaml#/GroupName/TypeName'
378
+ ```
379
+
380
+ #### 3. Path Resolution
381
+
382
+ **CRITICAL:** All paths are from **project root** (where you run `type-crafter` command)
383
+
384
+ ```bash
385
+ # If you run this command from /project
386
+ cd /project
387
+ type-crafter generate typescript ./specs/api.yaml ./output
388
+
389
+ # Then all $ref paths in ANY file are relative to /project
390
+ ```
391
+
392
+ **Example:**
393
+ ```
394
+ /project/
395
+ ├── specs/
396
+ │ ├── api.yaml # File A
397
+ │ └── user/
398
+ │ └── types.yaml # File B
399
+ ```
400
+
401
+ **In specs/api.yaml:**
402
+ ```yaml
403
+ # ✅ CORRECT: Path from project root
404
+ User:
405
+ $ref: './specs/user/types.yaml#/User'
406
+
407
+ # ❌ WRONG: Don't use relative path from current file
408
+ User:
409
+ $ref: './user/types.yaml#/User'
410
+ ```
411
+
412
+ **In specs/user/types.yaml:**
413
+ ```yaml
414
+ # ✅ CORRECT: Path from project root
415
+ Profile:
416
+ $ref: './specs/api.yaml#/ApiTypes/Profile'
417
+
418
+ # ❌ WRONG: Don't use relative path
419
+ Profile:
420
+ $ref: '../api.yaml#/ApiTypes/Profile'
421
+ ```
422
+
423
+ ### Generating Types from Multi-File Specs
424
+
425
+ ```bash
426
+ # Generate from top-level file (references will be resolved automatically)
427
+ type-crafter generate typescript ./types/index.yaml ./output
428
+
429
+ # Or from any entry point
430
+ type-crafter generate typescript ./specs/api.yaml ./src/types
431
+ ```
432
+
433
+ All referenced files will be automatically processed and types generated.
434
+
435
+ ---
436
+
23
437
  ## Root Structure
24
438
 
25
439
  ### Required Fields
@@ -124,40 +538,242 @@ Only these can be top-level types:
124
538
 
125
539
  **Properties NOT in `required` array become nullable (`Type | null`)**
126
540
 
541
+ This is controlled EXCLUSIVELY by the `required` array. There are NO other properties or syntax for controlling nullability.
542
+
543
+ ### ❌ WRONG: What NOT to Do
544
+
127
545
  ```yaml
546
+ # ❌ WRONG: Do NOT use 'nullable' property
547
+ User:
548
+ type: object
549
+ properties:
550
+ name:
551
+ type: string
552
+ nullable: true # ❌ INVALID! This property does NOT exist in Type Crafter
553
+
554
+ # ❌ WRONG: Do NOT use 'optional' property
555
+ User:
556
+ type: object
557
+ properties:
558
+ name:
559
+ type: string
560
+ optional: true # ❌ INVALID! This property does NOT exist
561
+
562
+ # ❌ WRONG: Do NOT use '?' suffix
563
+ User:
564
+ type: object
565
+ properties:
566
+ name?: # ❌ INVALID! Don't use ? in property names
567
+ type: string
568
+
569
+ # ❌ WRONG: Do NOT use null in type
570
+ User:
571
+ type: object
572
+ properties:
573
+ name:
574
+ type: [string, null] # ❌ INVALID! Don't use array of types here
575
+ ```
576
+
577
+ ### ✅ CORRECT: Use `required` Array Only
578
+
579
+ ```yaml
580
+ # ✅ CORRECT: Control nullability with 'required' array
128
581
  User:
129
582
  type: object
130
583
  required:
131
- - id # Will be: id: string
132
- - email # Will be: email: string
584
+ - id # id is required → generates: string
585
+ - email # email is required → generates: string
586
+ # name is NOT in required → generates: string | null
587
+ # age is NOT in required → generates: number | null
133
588
  properties:
134
589
  id:
135
590
  type: string
136
591
  email:
137
592
  type: string
138
593
  name:
139
- type: string # NOT in required → becomes: string | null
594
+ type: string
140
595
  age:
141
- type: number # NOT in required → becomes: number | null
596
+ type: number
142
597
  ```
143
598
 
144
599
  **TypeScript Output:**
145
600
 
146
601
  ```typescript
147
602
  export type User = {
148
- id: string; // Required - no null
149
- email: string; // Required - no null
150
- name: string | null; // Optional - nullable
151
- age: number | null; // Optional - nullable
603
+ id: string; // Required (in required array)
604
+ email: string; // Required (in required array)
605
+ name: string | null; // Nullable (NOT in required array)
606
+ age: number | null; // Nullable (NOT in required array)
607
+ };
608
+ ```
609
+
610
+ ### Detailed Rules
611
+
612
+ 1. ✅ **Property in `required` array** → Generates `Type` (NOT nullable)
613
+
614
+ ```yaml
615
+ User:
616
+ type: object
617
+ required:
618
+ - name
619
+ properties:
620
+ name: { type: string }
621
+ # Generates: name: string
622
+ ```
623
+
624
+ 2. ✅ **Property NOT in `required` array** → Generates `Type | null` (nullable)
625
+
626
+ ```yaml
627
+ User:
628
+ type: object
629
+ required:
630
+ - id
631
+ properties:
632
+ id: { type: string }
633
+ name: { type: string } # NOT in required
634
+ # Generates: name: string | null
635
+ ```
636
+
637
+ 3. ✅ **No `required` array at all** → All properties are `Type | null`
638
+
639
+ ```yaml
640
+ User:
641
+ type: object
642
+ # No required array
643
+ properties:
644
+ id: { type: string }
645
+ name: { type: string }
646
+ # Generates: id: string | null, name: string | null
647
+ ```
648
+
649
+ 4. ✅ **Empty `required: []`** → All properties are `Type | null`
650
+ ```yaml
651
+ User:
652
+ type: object
653
+ required: [] # Empty array
654
+ properties:
655
+ id: { type: string }
656
+ # Generates: id: string | null
657
+ ```
658
+
659
+ ### Complete Examples
660
+
661
+ #### Example 1: All Properties Required
662
+
663
+ ```yaml
664
+ User:
665
+ type: object
666
+ required:
667
+ - id
668
+ - email
669
+ - name
670
+ - age
671
+ properties:
672
+ id: { type: string }
673
+ email: { type: string }
674
+ name: { type: string }
675
+ age: { type: number }
676
+ ```
677
+
678
+ ```typescript
679
+ // Generated TypeScript
680
+ export type User = {
681
+ id: string;
682
+ email: string;
683
+ name: string;
684
+ age: number;
685
+ };
686
+ ```
687
+
688
+ #### Example 2: Some Properties Required
689
+
690
+ ```yaml
691
+ User:
692
+ type: object
693
+ required:
694
+ - id
695
+ - email
696
+ properties:
697
+ id: { type: string }
698
+ email: { type: string }
699
+ name: { type: string }
700
+ age: { type: number }
701
+ ```
702
+
703
+ ```typescript
704
+ // Generated TypeScript
705
+ export type User = {
706
+ id: string;
707
+ email: string;
708
+ name: string | null;
709
+ age: number | null;
152
710
  };
153
711
  ```
154
712
 
155
- ### Rules
713
+ #### Example 3: No Properties Required
156
714
 
157
- 1. ✅ **In `required` array** → `Type`
158
- 2. ✅ **NOT in `required` array** → `Type | null`
159
- 3. ✅ **No `required` array** → All properties are `Type | null`
160
- 4. ❌ **Empty `required: []`** → All properties are `Type | null`
715
+ ```yaml
716
+ User:
717
+ type: object
718
+ properties:
719
+ id: { type: string }
720
+ email: { type: string }
721
+ name: { type: string }
722
+ age: { type: number }
723
+ ```
724
+
725
+ ```typescript
726
+ // Generated TypeScript - All nullable
727
+ export type User = {
728
+ id: string | null;
729
+ email: string | null;
730
+ name: string | null;
731
+ age: number | null;
732
+ };
733
+ ```
734
+
735
+ ### Nested Objects and Arrays
736
+
737
+ ```yaml
738
+ User:
739
+ type: object
740
+ required:
741
+ - profile
742
+ - posts
743
+ properties:
744
+ profile:
745
+ type: object
746
+ required:
747
+ - name
748
+ properties:
749
+ name: { type: string }
750
+ bio: { type: string } # NOT in profile.required
751
+ posts:
752
+ type: array
753
+ items:
754
+ type: object
755
+ required:
756
+ - title
757
+ properties:
758
+ title: { type: string }
759
+ content: { type: string } # NOT in items.required
760
+ ```
761
+
762
+ ```typescript
763
+ // Generated TypeScript
764
+ export type User = {
765
+ profile: {
766
+ // profile is required (in User.required)
767
+ name: string; // required in profile
768
+ bio: string | null; // NOT required in profile
769
+ };
770
+ posts: Array<{
771
+ // posts array is required
772
+ title: string; // required in post item
773
+ content: string | null; // NOT required in post item
774
+ }>;
775
+ };
776
+ ```
161
777
 
162
778
  ---
163
779
 
@@ -409,69 +1025,238 @@ export type UserWithMeta = {
409
1025
 
410
1026
  ## References
411
1027
 
412
- ### Local References (Same File)
1028
+ ### Understanding Top Files vs Non-Top Files
1029
+
1030
+ **CRITICAL:** Referencing rules differ based on whether you're in a top file or non-top file.
1031
+
1032
+ #### What is a Top File?
413
1033
 
1034
+ A **top file** is the root specification file that contains the `info` section at the root level.
1035
+
1036
+ **Identifying a top file:**
414
1037
  ```yaml
415
- # Reference to top-level type
416
- Post:
417
- type: object
418
- properties:
419
- author:
420
- $ref: '#/types/User'
1038
+ # This is a TOP FILE
1039
+ info:
1040
+ version: '1.0.0' # Has info section at root
1041
+ title: 'My API Types'
421
1042
 
422
- # Reference to grouped type
423
- Comment:
424
- type: object
425
- properties:
426
- author:
427
- $ref: '#/groupedTypes/Auth/UserProfile'
1043
+ types:
1044
+ User: { ... }
428
1045
  ```
429
1046
 
430
- **Reference Format:**
1047
+ **Characteristics:**
1048
+ - Contains `info:` section with `version` and `title` at the root level
1049
+ - Is the root of all schemas
1050
+ - Can be directly used with `type-crafter generate` command
1051
+ - Typically referenced by other files
431
1052
 
432
- - Top-level: `#/types/TypeName`
433
- - Grouped: `#/groupedTypes/GroupName/TypeName`
1053
+ #### What is a Non-Top File?
434
1054
 
435
- ### External File References
1055
+ A **non-top file** is any YAML file that does NOT have `info:` at the root level. These files are used to organize types into domain-specific files.
436
1056
 
437
- **⚠️ IMPORTANT: Paths are from project root, not relative to current file**
1057
+ **Identifying a non-top file:**
1058
+ ```yaml
1059
+ # ✅ This is a NON-TOP FILE (no info section at root)
1060
+ Cart:
1061
+ SCart:
1062
+ type: object
1063
+ properties:
1064
+ items: { ... }
1065
+
1066
+ SCartItem:
1067
+ type: object
1068
+ properties:
1069
+ id: { ... }
1070
+ ```
1071
+
1072
+ **Characteristics:**
1073
+ - Does NOT have `info:` section at the root level
1074
+ - Must be referenced from a top file or another file
1075
+ - Used for better organization of types in domain-specific files
1076
+ - Cannot be used directly with `type-crafter generate` command
1077
+
1078
+ ### Referencing Rules
1079
+
1080
+ #### Rule 1: References in TOP Files
1081
+
1082
+ **In top files, you can reference types using `#` without specifying the file path.**
1083
+
1084
+ **Example:**
438
1085
 
439
1086
  ```yaml
440
- # Reference to type in another file (path from project root)
441
- Post:
442
- type: object
443
- properties:
444
- author:
445
- $ref: './src/types/users.yaml#/User'
1087
+ # File: types.yaml (TOP FILE - has info section)
1088
+ info:
1089
+ version: '0.0.0'
1090
+ title: 'Sample Typing Spec'
446
1091
 
447
- # Reference to grouped type in another file
448
- Comment:
449
- type: object
450
- properties:
451
- metadata:
452
- $ref: './docs/specs/common.yaml#/SharedTypes/Metadata'
1092
+ types:
1093
+ TypeObjectTwo:
1094
+ type: object
1095
+ properties:
1096
+ propOne:
1097
+ $ref: '#/types/TypeObjectOne' # ✅ Direct reference (no file path needed)
453
1098
 
454
- # Real-world example: Shopify cart referencing cart item
455
- SCart:
456
- type: object
457
- properties:
458
- items:
459
- type: array
1099
+ TypeObjectOne:
1100
+ type: object
1101
+ properties:
1102
+ name:
1103
+ type: string
1104
+ ```
1105
+
1106
+ **Valid reference formats in top files:**
1107
+ - Same file, top-level type: `#/types/TypeName`
1108
+ - Same file, grouped type: `#/groupedTypes/GroupName/TypeName`
1109
+ - External file: `'./path/from/root/file.yaml#/TypeName'` (when referencing another file)
1110
+
1111
+ #### Rule 2: References in NON-TOP Files
1112
+
1113
+ **In non-top files, you MUST specify the complete file path (from project root) when referencing ANY type, even types in the same file.**
1114
+
1115
+ **Example:**
1116
+
1117
+ ```yaml
1118
+ # File: docs/cart.yaml (NON-TOP FILE - no info section)
1119
+ Cart:
1120
+ SCart:
1121
+ description: 'Shopify cart object'
1122
+ type: object
1123
+ properties:
460
1124
  items:
461
- $ref: './docs/specs/Cart.yaml#/Cart/SCartItem'
1125
+ type: array
1126
+ items:
1127
+ $ref: './docs/cart.yaml#/Cart/SCartItem' # ✅ MUST include full file path
1128
+
1129
+ SCartItem:
1130
+ description: 'Individual cart item'
1131
+ type: object
1132
+ properties:
1133
+ id:
1134
+ type: number
462
1135
  ```
463
1136
 
464
- **Reference Format:**
1137
+ **Why the full path?** Non-top files don't have an `info` section, so they don't have a root context. You must always specify where the type is located.
1138
+
1139
+ ### Complete Examples
465
1140
 
466
- - **Path from project root:** `./path/from/root/file.yaml#/TypeName`
467
- - External type: `./path/from/root/file.yaml#/TypeName`
468
- - External grouped: `./path/from/root/file.yaml#/GroupName/TypeName`
1141
+ #### Example 1: Top File with Local References
469
1142
 
470
- **Path Rules:**
1143
+ ```yaml
1144
+ # File: types.yaml (TOP FILE)
1145
+ info:
1146
+ version: '1.0.0'
1147
+ title: 'API Types'
1148
+
1149
+ types:
1150
+ Post:
1151
+ type: object
1152
+ properties:
1153
+ author:
1154
+ $ref: '#/types/User' # ✅ Local reference in top file (no path)
1155
+ comments:
1156
+ type: array
1157
+ items:
1158
+ $ref: '#/types/Comment' # ✅ Local reference in top file (no path)
1159
+
1160
+ User:
1161
+ type: object
1162
+ properties:
1163
+ id: { type: string }
1164
+ name: { type: string }
1165
+
1166
+ Comment:
1167
+ type: object
1168
+ properties:
1169
+ text: { type: string }
1170
+ ```
1171
+
1172
+ #### Example 2: Top File Referencing Non-Top Files
1173
+
1174
+ ```yaml
1175
+ # File: types.yaml (TOP FILE)
1176
+ info:
1177
+ version: '0.0.0'
1178
+ title: 'Sample Typing Spec'
1179
+
1180
+ groupedTypes:
1181
+ Cart:
1182
+ $ref: './docs/cart.yaml#/Cart' # ✅ Importing entire Cart group from non-top file
1183
+
1184
+ # Project structure:
1185
+ # .
1186
+ # ├── types.yaml (this file - top file)
1187
+ # └── docs/
1188
+ # └── cart.yaml (non-top file)
1189
+ ```
1190
+
1191
+ ```yaml
1192
+ # File: docs/cart.yaml (NON-TOP FILE)
1193
+ Cart:
1194
+ SCart:
1195
+ description: 'Shopify cart object'
1196
+ type: object
1197
+ required:
1198
+ - items
1199
+ properties:
1200
+ items:
1201
+ type: array
1202
+ items:
1203
+ $ref: './docs/cart.yaml#/Cart/SCartItem' # ✅ Full path required in non-top file
1204
+
1205
+ SCartItem:
1206
+ description: 'Individual item in the cart'
1207
+ type: object
1208
+ required:
1209
+ - id
1210
+ properties:
1211
+ id:
1212
+ type: number
1213
+ description: 'Unique item identifier'
1214
+ quantity:
1215
+ type: number
1216
+ ```
1217
+
1218
+ #### Example 3: Non-Top File Referencing Another Non-Top File
1219
+
1220
+ ```yaml
1221
+ # File: docs/order.yaml (NON-TOP FILE)
1222
+ Order:
1223
+ SOrder:
1224
+ type: object
1225
+ properties:
1226
+ items:
1227
+ type: array
1228
+ items:
1229
+ $ref: './docs/cart.yaml#/Cart/SCartItem' # ✅ Full path to another non-top file
1230
+ customer:
1231
+ $ref: './docs/user.yaml#/User/SCustomer' # ✅ Full path to another non-top file
1232
+ ```
1233
+
1234
+ ### Reference Format Summary
1235
+
1236
+ | File Type | Referencing Same File | Referencing Another File |
1237
+ |-----------|----------------------|--------------------------|
1238
+ | **Top File** | `#/types/TypeName`<br>`#/groupedTypes/Group/TypeName` | `'./path/from/root/file.yaml#/TypeName'` |
1239
+ | **Non-Top File** | `'./path/from/root/current-file.yaml#/TypeName'`<br>(full path required) | `'./path/from/root/other-file.yaml#/TypeName'` |
1240
+
1241
+ ### Path Rules (All Files)
1242
+
1243
+ 1. **Paths are from project root** (where you run `type-crafter` command), NOT relative to current file
1244
+ 2. **Always start with `./`** for file paths
1245
+ 3. **Never use `../`** for relative navigation
1246
+ 4. **Case matters** - file paths are case-sensitive on some systems
1247
+
1248
+ **Example project structure:**
1249
+ ```
1250
+ project-root/
1251
+ ├── types.yaml (top file)
1252
+ └── docs/
1253
+ ├── cart.yaml (non-top file)
1254
+ └── user.yaml (non-top file)
1255
+ ```
471
1256
 
472
- - Paths start with `./` and are relative to **project root** (where you run the command)
473
- - NOT relative to the current YAML file location
474
- - Example: If your spec is at `./src/api.yaml` and references `./src/types/user.yaml`, the path is `./src/types/user.yaml`, not `./types/user.yaml`
1257
+ If running `type-crafter generate typescript ./types.yaml ./output` from `project-root/`:
1258
+ - Path to cart.yaml: `'./docs/cart.yaml#/...'`
1259
+ - Path to user.yaml: `'./docs/user.yaml#/...'`
475
1260
 
476
1261
  ### Group References (Entire Group)
477
1262