reliant-type 1.0.0 → 2.1.3
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/cjs/core/schema/extensions/mods/typescript-generator.js +45 -16
- package/dist/cjs/core/schema/extensions/mods/typescript-generator.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/Interface.js +0 -12
- package/dist/cjs/core/schema/mode/interfaces/Interface.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/InterfaceSchema.js +34 -3
- package/dist/cjs/core/schema/mode/interfaces/InterfaceSchema.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/errors/SchemaValidationError.js +16 -0
- package/dist/cjs/core/schema/mode/interfaces/errors/SchemaValidationError.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js +119 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js +49 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/typescript/TypeInference.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/validators/ConstraintParser.js +6 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/ConstraintParser.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeGuards.js +7 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeGuards.js.map +1 -1
- package/dist/cjs/core/utils/Mod.js +33 -4
- package/dist/cjs/core/utils/Mod.js.map +1 -1
- package/dist/cjs/index.js +74 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/core/schema/extensions/mods/typescript-generator.js +45 -16
- package/dist/esm/core/schema/extensions/mods/typescript-generator.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/Interface.js +1 -12
- package/dist/esm/core/schema/mode/interfaces/Interface.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/InterfaceSchema.js +32 -1
- package/dist/esm/core/schema/mode/interfaces/InterfaceSchema.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/errors/SchemaValidationError.js +14 -0
- package/dist/esm/core/schema/mode/interfaces/errors/SchemaValidationError.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js +119 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js +49 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/typescript/TypeInference.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/validators/ConstraintParser.js +6 -0
- package/dist/esm/core/schema/mode/interfaces/validators/ConstraintParser.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/validators/TypeGuards.js +7 -0
- package/dist/esm/core/schema/mode/interfaces/validators/TypeGuards.js.map +1 -1
- package/dist/esm/core/utils/Mod.js +33 -4
- package/dist/esm/core/utils/Mod.js.map +1 -1
- package/dist/esm/index.js +78 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/schema.d.ts +44 -3
- package/docs/FUNCTION-TYPES.md +120 -0
- package/package.json +4 -4
- package/src/core/schema/extensions/components/AutoDocumentation/Docs.ts +1 -1
- package/src/core/schema/extensions/mods/typescript-generator.ts +342 -295
- package/src/core/schema/mode/interfaces/Interface.ts +1 -13
- package/src/core/schema/mode/interfaces/InterfaceSchema.ts +41 -1
- package/src/core/schema/mode/interfaces/errors/SchemaValidationError.ts +13 -0
- package/src/core/schema/mode/interfaces/precompilation/FieldPrecompilers.ts +146 -0
- package/src/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.ts +79 -0
- package/src/core/schema/mode/interfaces/typescript/TypeInference.ts +67 -16
- package/src/core/schema/mode/interfaces/validators/ConstraintParser.ts +8 -0
- package/src/core/schema/mode/interfaces/validators/TypeGuards.ts +19 -5
- package/src/core/types/SchemaValidator.type.ts +4 -0
- package/src/core/utils/Mod.ts +35 -4
- package/src/index.ts +9 -0
|
@@ -6,237 +6,284 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { SchemaInterface } from "../../mode/interfaces/Interface";
|
|
9
|
+
import { TypeGuards } from "../../mode/interfaces/validators/TypeGuards";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* TypeScript code generator for schema definitions
|
|
12
13
|
*/
|
|
13
14
|
export class TypeScriptGenerator {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} else {
|
|
37
|
-
output += this.generateTypeDefinition(fieldDefinitions, exportName, namespace ? 1 : 0);
|
|
38
|
-
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate TypeScript interface from schema
|
|
17
|
+
*/
|
|
18
|
+
static generateInterface(
|
|
19
|
+
schema: SchemaInterface,
|
|
20
|
+
options: TypeScriptOptions = {}
|
|
21
|
+
): string {
|
|
22
|
+
const {
|
|
23
|
+
exportName = "Schema",
|
|
24
|
+
namespace,
|
|
25
|
+
exportType = "interface",
|
|
26
|
+
} = options;
|
|
27
|
+
|
|
28
|
+
// Extract the actual field definitions from the schema
|
|
29
|
+
const fieldDefinitions = this.extractFieldDefinitions(schema);
|
|
30
|
+
|
|
31
|
+
let output = "";
|
|
32
|
+
|
|
33
|
+
// Add namespace if specified
|
|
34
|
+
if (namespace) {
|
|
35
|
+
output += `export namespace ${namespace} {\n`;
|
|
36
|
+
}
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
// Generate interface or type
|
|
39
|
+
if (exportType === "interface") {
|
|
40
|
+
output += this.generateInterfaceDefinition(
|
|
41
|
+
fieldDefinitions,
|
|
42
|
+
exportName,
|
|
43
|
+
namespace ? 1 : 0
|
|
44
|
+
);
|
|
45
|
+
} else {
|
|
46
|
+
output += this.generateTypeDefinition(
|
|
47
|
+
fieldDefinitions,
|
|
48
|
+
exportName,
|
|
49
|
+
namespace ? 1 : 0
|
|
50
|
+
);
|
|
51
|
+
}
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
// Close namespace if specified
|
|
54
|
+
if (namespace) {
|
|
55
|
+
output += "}\n";
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*/
|
|
51
|
-
private static extractFieldDefinitions(schema: any): Record<string, any> {
|
|
52
|
-
// If schema has a definition property, use that
|
|
53
|
-
if (schema && typeof schema === 'object' && schema.definition) {
|
|
54
|
-
return schema.definition;
|
|
55
|
-
}
|
|
58
|
+
return output;
|
|
59
|
+
}
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Extract field definitions from schema object
|
|
63
|
+
*/
|
|
64
|
+
private static extractFieldDefinitions(schema: any): Record<string, any> {
|
|
65
|
+
// If schema has a definition property, use that
|
|
66
|
+
if (schema && typeof schema === "object" && schema.definition) {
|
|
67
|
+
return schema.definition;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
// Otherwise, assume the schema itself contains the field definitions
|
|
71
|
+
return schema;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate interface definition
|
|
76
|
+
*/
|
|
77
|
+
private static generateInterfaceDefinition(
|
|
78
|
+
schema: SchemaInterface,
|
|
79
|
+
name: string,
|
|
80
|
+
indentLevel: number = 0
|
|
81
|
+
): string {
|
|
82
|
+
const indent = " ".repeat(indentLevel);
|
|
83
|
+
let output = `${indent}export interface ${name} {\n`;
|
|
84
|
+
|
|
85
|
+
Object.entries(schema).forEach(([fieldName, fieldType]) => {
|
|
86
|
+
const tsType = this.convertToTypeScript(fieldType, indentLevel + 1);
|
|
87
|
+
const optional = this.isOptionalField(fieldType) ? "?" : "";
|
|
88
|
+
output += `${indent} ${fieldName}${optional}: ${tsType};\n`;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
output += `${indent}}\n`;
|
|
92
|
+
return output;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Generate type alias definition
|
|
97
|
+
*/
|
|
98
|
+
private static generateTypeDefinition(
|
|
99
|
+
schema: SchemaInterface,
|
|
100
|
+
name: string,
|
|
101
|
+
indentLevel: number = 0
|
|
102
|
+
): string {
|
|
103
|
+
const indent = " ".repeat(indentLevel);
|
|
104
|
+
const objectType = this.convertObjectToTypeScript(schema, indentLevel);
|
|
105
|
+
return `${indent}export type ${name} = ${objectType};\n`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Convert schema field to TypeScript type
|
|
110
|
+
*/
|
|
111
|
+
private static convertToTypeScript(
|
|
112
|
+
fieldType: any,
|
|
113
|
+
indentLevel: number = 0
|
|
114
|
+
): string {
|
|
115
|
+
// Handle OptionalSchemaInterface wrapper
|
|
116
|
+
if (TypeGuards.isOptionalSchemaInterface(fieldType)) {
|
|
117
|
+
return this.convertToTypeScript(fieldType.schema, indentLevel);
|
|
80
118
|
}
|
|
81
119
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
*/
|
|
85
|
-
private static generateTypeDefinition(
|
|
86
|
-
schema: SchemaInterface,
|
|
87
|
-
name: string,
|
|
88
|
-
indentLevel: number = 0
|
|
89
|
-
): string {
|
|
90
|
-
const indent = " ".repeat(indentLevel);
|
|
91
|
-
const objectType = this.convertObjectToTypeScript(schema);
|
|
92
|
-
return `${indent}export type ${name} = ${objectType};\n`;
|
|
120
|
+
if (typeof fieldType === "string") {
|
|
121
|
+
return this.convertStringToTypeScript(fieldType);
|
|
93
122
|
}
|
|
94
123
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return this.convertStringToTypeScript(fieldType);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (typeof fieldType === "object" && !Array.isArray(fieldType)) {
|
|
104
|
-
// Handle Make.union() objects
|
|
105
|
-
if (fieldType.union && Array.isArray(fieldType.union)) {
|
|
106
|
-
return fieldType.union.map((value: any) => `"${value}"`).join(" | ");
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Handle Make.const() objects
|
|
110
|
-
if (fieldType.const !== undefined) {
|
|
111
|
-
return `"${fieldType.const}"`;
|
|
112
|
-
}
|
|
124
|
+
if (typeof fieldType === "object" && !Array.isArray(fieldType)) {
|
|
125
|
+
// Handle Make.union() objects
|
|
126
|
+
if (fieldType.union && Array.isArray(fieldType.union)) {
|
|
127
|
+
return fieldType.union.map((value: any) => `"${value}"`).join(" | ");
|
|
128
|
+
}
|
|
113
129
|
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
// Handle Make.const() objects
|
|
131
|
+
if (fieldType.const !== undefined) {
|
|
132
|
+
return `"${fieldType.const}"`;
|
|
133
|
+
}
|
|
116
134
|
|
|
117
|
-
|
|
135
|
+
return this.convertObjectToTypeScript(fieldType, indentLevel);
|
|
118
136
|
}
|
|
119
137
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
private static convertStringToTypeScript(fieldType: string): string {
|
|
124
|
-
const cleanType = fieldType.replace("?", "");
|
|
125
|
-
|
|
126
|
-
// Handle arrays FIRST - before other checks
|
|
127
|
-
if (cleanType.includes("[]")) {
|
|
128
|
-
const elementType = cleanType.replace("[]", "").replace(/\([^)]*\)/, ""); // Remove constraints
|
|
129
|
-
const tsElementType = this.convertStringToTypeScript(elementType);
|
|
130
|
-
return `${tsElementType}[]`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Handle number types with constraints
|
|
134
|
-
if (cleanType.startsWith("number(") || cleanType.includes("number(")) {
|
|
135
|
-
return "number";
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Basic type mappings
|
|
139
|
-
const typeMappings: Record<string, string> = {
|
|
140
|
-
email: "string",
|
|
141
|
-
url: "string",
|
|
142
|
-
uuid: "string",
|
|
143
|
-
phone: "string",
|
|
144
|
-
date: "Date",
|
|
145
|
-
number: "number",
|
|
146
|
-
positive: "number",
|
|
147
|
-
int: "number",
|
|
148
|
-
boolean: "boolean",
|
|
149
|
-
string: "string"
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// Check for direct mapping
|
|
153
|
-
if (typeMappings[cleanType]) {
|
|
154
|
-
return typeMappings[cleanType];
|
|
155
|
-
}
|
|
138
|
+
if (Array.isArray(fieldType) && fieldType.length === 1) {
|
|
139
|
+
return `${this.convertToTypeScript(fieldType[0], indentLevel)}[]`;
|
|
140
|
+
}
|
|
156
141
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return "string";
|
|
160
|
-
}
|
|
142
|
+
return "any";
|
|
143
|
+
}
|
|
161
144
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Convert string-based field to TypeScript type
|
|
147
|
+
*/
|
|
148
|
+
private static convertStringToTypeScript(fieldType: string): string {
|
|
149
|
+
const cleanType = fieldType.replace("?", "");
|
|
167
150
|
|
|
168
|
-
|
|
151
|
+
// Handle function types
|
|
152
|
+
if (TypeGuards.isFunctionType(fieldType)) {
|
|
153
|
+
const type = fieldType.endsWith("?") ? fieldType.slice(0, -1) : fieldType;
|
|
154
|
+
const signature = type.slice(3, -1); // remove fn( and )
|
|
155
|
+
return signature.replace(/TYPE/g, "any");
|
|
169
156
|
}
|
|
170
157
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
158
|
+
// Handle arrays FIRST - before other checks
|
|
159
|
+
if (cleanType.includes("[]")) {
|
|
160
|
+
const elementType = cleanType.replace("[]", "").replace(/\([^)]*\)/, ""); // Remove constraints
|
|
161
|
+
const tsElementType = this.convertStringToTypeScript(elementType);
|
|
162
|
+
return `${tsElementType}[]`;
|
|
163
|
+
}
|
|
178
164
|
|
|
179
|
-
|
|
165
|
+
// Handle number types with constraints
|
|
166
|
+
if (cleanType.startsWith("number(") || cleanType.includes("number(")) {
|
|
167
|
+
return "number";
|
|
168
|
+
}
|
|
180
169
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
170
|
+
// Basic type mappings
|
|
171
|
+
const typeMappings: Record<string, string> = {
|
|
172
|
+
email: "string",
|
|
173
|
+
url: "string",
|
|
174
|
+
uuid: "string",
|
|
175
|
+
phone: "string",
|
|
176
|
+
date: "Date",
|
|
177
|
+
number: "number",
|
|
178
|
+
positive: "number",
|
|
179
|
+
int: "number",
|
|
180
|
+
boolean: "boolean",
|
|
181
|
+
string: "string",
|
|
182
|
+
object: "Record<string, any>",
|
|
183
|
+
any: "any",
|
|
184
|
+
record: "Record<string, any>",
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Check for direct mapping
|
|
188
|
+
if (typeMappings[cleanType]) {
|
|
189
|
+
return typeMappings[cleanType];
|
|
190
|
+
}
|
|
186
191
|
|
|
187
|
-
|
|
192
|
+
// Handle string with constraints
|
|
193
|
+
if (cleanType.startsWith("string")) {
|
|
194
|
+
return "string";
|
|
188
195
|
}
|
|
189
196
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
// Handle union types (pipe-separated values)
|
|
198
|
+
if (cleanType.includes("|")) {
|
|
199
|
+
const unionTypes = cleanType.split("|").map((t) => {
|
|
200
|
+
const trimmed = t.trim();
|
|
201
|
+
// If it's a known type, use its mapping, otherwise treat as literal
|
|
202
|
+
return typeMappings[trimmed] || `"${trimmed}"`;
|
|
203
|
+
});
|
|
204
|
+
return unionTypes.join(" | ");
|
|
198
205
|
}
|
|
199
206
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
output += `export type Required${baseName} = Required<${baseName}>;\n\n`;
|
|
214
|
-
|
|
215
|
-
// Generate pick types for common field combinations
|
|
216
|
-
const fields = Object.keys(schema);
|
|
217
|
-
if (fields.length > 1) {
|
|
218
|
-
output += `export type ${baseName}Keys = keyof ${baseName};\n\n`;
|
|
219
|
-
|
|
220
|
-
// Generate create type (without optional fields)
|
|
221
|
-
const requiredFields = fields.filter(field =>
|
|
222
|
-
!this.isOptionalField(schema[field])
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
if (requiredFields.length > 0 && requiredFields.length < fields.length) {
|
|
226
|
-
output += `export type Create${baseName} = Pick<${baseName}, ${
|
|
227
|
-
requiredFields.map(f => `"${f}"`).join(" | ")
|
|
228
|
-
}>;\n\n`;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
207
|
+
return "any"; // Changed fallback from "string" to "any"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Convert object to TypeScript type
|
|
212
|
+
*/
|
|
213
|
+
private static convertObjectToTypeScript(
|
|
214
|
+
obj: any,
|
|
215
|
+
indentLevel: number = 0
|
|
216
|
+
): string {
|
|
217
|
+
if (typeof obj !== "object" || obj === null) {
|
|
218
|
+
return "any";
|
|
219
|
+
}
|
|
231
220
|
|
|
232
|
-
|
|
221
|
+
const indent = " ".repeat(indentLevel);
|
|
222
|
+
const nextIndent = " ".repeat(indentLevel + 1);
|
|
223
|
+
const properties: string[] = [];
|
|
224
|
+
|
|
225
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
226
|
+
const tsType = this.convertToTypeScript(value, indentLevel + 1);
|
|
227
|
+
const optional = this.isOptionalField(value) ? "?" : "";
|
|
228
|
+
properties.push(`${nextIndent}${key}${optional}: ${tsType}`);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return `{\n${properties.join(";\n")};\n${indent}}`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Check if field is optional
|
|
236
|
+
*/
|
|
237
|
+
private static isOptionalField(fieldType: any): boolean {
|
|
238
|
+
if (TypeGuards.isOptionalSchemaInterface(fieldType)) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
if (typeof fieldType === "string") {
|
|
242
|
+
return fieldType.includes("?");
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Generate utility types for schema
|
|
249
|
+
*/
|
|
250
|
+
static generateUtilityTypes(
|
|
251
|
+
schema: SchemaInterface,
|
|
252
|
+
baseName: string
|
|
253
|
+
): string {
|
|
254
|
+
let output = "";
|
|
255
|
+
|
|
256
|
+
// Generate partial type
|
|
257
|
+
output += `export type Partial${baseName} = Partial<${baseName}>;\n\n`;
|
|
258
|
+
|
|
259
|
+
// Generate required type
|
|
260
|
+
output += `export type Required${baseName} = Required<${baseName}>;\n\n`;
|
|
261
|
+
|
|
262
|
+
// Generate pick types for common field combinations
|
|
263
|
+
const fields = Object.keys(schema);
|
|
264
|
+
if (fields.length > 1) {
|
|
265
|
+
output += `export type ${baseName}Keys = keyof ${baseName};\n\n`;
|
|
266
|
+
|
|
267
|
+
// Generate create type (without optional fields)
|
|
268
|
+
const requiredFields = fields.filter(
|
|
269
|
+
(field) => !this.isOptionalField(schema[field])
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
if (requiredFields.length > 0 && requiredFields.length < fields.length) {
|
|
273
|
+
output += `export type Create${baseName} = Pick<${baseName}, ${requiredFields
|
|
274
|
+
.map((f) => `"${f}"`)
|
|
275
|
+
.join(" | ")}>;\n\n`;
|
|
276
|
+
}
|
|
233
277
|
}
|
|
234
278
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
279
|
+
return output;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Generate validation function types
|
|
284
|
+
*/
|
|
285
|
+
static generateValidationTypes(baseName: string): string {
|
|
286
|
+
return `
|
|
240
287
|
export interface ${baseName}ValidationResult {
|
|
241
288
|
isValid: boolean;
|
|
242
289
|
data: ${baseName};
|
|
@@ -256,124 +303,124 @@ export interface ${baseName}ParseResult {
|
|
|
256
303
|
};
|
|
257
304
|
}
|
|
258
305
|
`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Generate complete TypeScript module
|
|
310
|
+
*/
|
|
311
|
+
static generateModule(
|
|
312
|
+
schema: SchemaInterface,
|
|
313
|
+
options: ModuleOptions
|
|
314
|
+
): string {
|
|
315
|
+
const {
|
|
316
|
+
moduleName,
|
|
317
|
+
exportName = "Schema",
|
|
318
|
+
includeUtilities = true,
|
|
319
|
+
includeValidation = true,
|
|
320
|
+
header,
|
|
321
|
+
} = options;
|
|
322
|
+
|
|
323
|
+
let output = "";
|
|
324
|
+
|
|
325
|
+
// Add header comment
|
|
326
|
+
if (header) {
|
|
327
|
+
output += `/**\n * ${header}\n */\n\n`;
|
|
259
328
|
}
|
|
260
329
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
static generateModule(
|
|
265
|
-
schema: SchemaInterface,
|
|
266
|
-
options: ModuleOptions
|
|
267
|
-
): string {
|
|
268
|
-
const {
|
|
269
|
-
moduleName,
|
|
270
|
-
exportName = "Schema",
|
|
271
|
-
includeUtilities = true,
|
|
272
|
-
includeValidation = true,
|
|
273
|
-
header
|
|
274
|
-
} = options;
|
|
275
|
-
|
|
276
|
-
let output = "";
|
|
277
|
-
|
|
278
|
-
// Add header comment
|
|
279
|
-
if (header) {
|
|
280
|
-
output += `/**\n * ${header}\n */\n\n`;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Generate main interface
|
|
284
|
-
output += this.generateInterface(schema, { exportName });
|
|
285
|
-
output += "\n";
|
|
286
|
-
|
|
287
|
-
// Generate utility types
|
|
288
|
-
if (includeUtilities) {
|
|
289
|
-
output += this.generateUtilityTypes(schema, exportName);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Generate validation types
|
|
293
|
-
if (includeValidation) {
|
|
294
|
-
output += this.generateValidationTypes(exportName);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Add module export
|
|
298
|
-
if (moduleName) {
|
|
299
|
-
output += `\nexport const ${moduleName} = {\n`;
|
|
300
|
-
output += ` name: "${exportName}",\n`;
|
|
301
|
-
output += ` fields: ${JSON.stringify(Object.keys(schema), null, 2)}\n`;
|
|
302
|
-
output += "};\n";
|
|
303
|
-
}
|
|
330
|
+
// Generate main interface
|
|
331
|
+
output += this.generateInterface(schema, { exportName });
|
|
332
|
+
output += "\n";
|
|
304
333
|
|
|
305
|
-
|
|
334
|
+
// Generate utility types
|
|
335
|
+
if (includeUtilities) {
|
|
336
|
+
output += this.generateUtilityTypes(schema, exportName);
|
|
306
337
|
}
|
|
307
338
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const docs: Record<string, string> = {};
|
|
313
|
-
|
|
314
|
-
Object.entries(schema).forEach(([fieldName, fieldType]) => {
|
|
315
|
-
docs[fieldName] = this.generateFieldJSDoc(fieldName, fieldType);
|
|
316
|
-
});
|
|
339
|
+
// Generate validation types
|
|
340
|
+
if (includeValidation) {
|
|
341
|
+
output += this.generateValidationTypes(exportName);
|
|
342
|
+
}
|
|
317
343
|
|
|
318
|
-
|
|
344
|
+
// Add module export
|
|
345
|
+
if (moduleName) {
|
|
346
|
+
output += `\nexport const ${moduleName} = {\n`;
|
|
347
|
+
output += ` name: "${exportName}",\n`;
|
|
348
|
+
output += ` fields: ${JSON.stringify(Object.keys(schema), null, 2)}\n`;
|
|
349
|
+
output += "};\n";
|
|
319
350
|
}
|
|
320
351
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
352
|
+
return output;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Generate JSDoc comments for schema fields
|
|
357
|
+
*/
|
|
358
|
+
static generateJSDoc(schema: SchemaInterface): Record<string, string> {
|
|
359
|
+
const docs: Record<string, string> = {};
|
|
360
|
+
|
|
361
|
+
Object.entries(schema).forEach(([fieldName, fieldType]) => {
|
|
362
|
+
docs[fieldName] = this.generateFieldJSDoc(fieldName, fieldType);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
return docs;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Generate JSDoc for a single field
|
|
370
|
+
*/
|
|
371
|
+
private static generateFieldJSDoc(fieldName: string, fieldType: any): string {
|
|
372
|
+
if (typeof fieldType === "string") {
|
|
373
|
+
const cleanType = fieldType.replace("?", "");
|
|
374
|
+
const isOptional = fieldType.includes("?");
|
|
375
|
+
|
|
376
|
+
let description = `${fieldName} field`;
|
|
377
|
+
|
|
378
|
+
// Add type-specific descriptions
|
|
379
|
+
if (cleanType === "email") description = "Email address";
|
|
380
|
+
else if (cleanType === "url") description = "URL address";
|
|
381
|
+
else if (cleanType === "uuid") description = "Unique identifier";
|
|
382
|
+
else if (cleanType === "date") description = "Date and time";
|
|
383
|
+
else if (cleanType === "positive") description = "Positive number";
|
|
384
|
+
else if (cleanType.startsWith("string")) description = "Text value";
|
|
385
|
+
else if (cleanType.includes("[]")) description = "Array of values";
|
|
386
|
+
|
|
387
|
+
let jsdoc = `/**\n * ${description}`;
|
|
388
|
+
|
|
389
|
+
if (isOptional) {
|
|
390
|
+
jsdoc += "\n * @optional";
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Add constraints info
|
|
394
|
+
if (cleanType.includes("(")) {
|
|
395
|
+
const match = cleanType.match(/\(([^)]+)\)/);
|
|
396
|
+
if (match) {
|
|
397
|
+
jsdoc += `\n * @constraints ${match[1]}`;
|
|
356
398
|
}
|
|
399
|
+
}
|
|
357
400
|
|
|
358
|
-
|
|
401
|
+
jsdoc += "\n */";
|
|
402
|
+
return jsdoc;
|
|
359
403
|
}
|
|
404
|
+
|
|
405
|
+
return `/**\n * ${fieldName} field\n */`;
|
|
406
|
+
}
|
|
360
407
|
}
|
|
361
408
|
|
|
362
409
|
/**
|
|
363
410
|
* Type definitions
|
|
364
411
|
*/
|
|
365
412
|
export interface TypeScriptOptions {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
413
|
+
exportName?: string;
|
|
414
|
+
namespace?: string;
|
|
415
|
+
exportType?: "interface" | "type";
|
|
369
416
|
}
|
|
370
417
|
|
|
371
418
|
export interface ModuleOptions {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
419
|
+
moduleName?: string;
|
|
420
|
+
exportName?: string;
|
|
421
|
+
includeUtilities?: boolean;
|
|
422
|
+
includeValidation?: boolean;
|
|
423
|
+
header?: string;
|
|
377
424
|
}
|
|
378
425
|
|
|
379
|
-
export {TypeScriptGenerator as TSGenerator}
|
|
426
|
+
export { TypeScriptGenerator as TSGenerator };
|