1+ import type { AbstractType } from '../type/classes/AbstractType' ;
2+ import type { AnyType } from '../type/classes/AnyType' ;
3+ import type { ArrayType } from '../type/classes/ArrayType' ;
4+ import type { BinaryType } from '../type/classes/BinaryType' ;
5+ import type { BooleanType } from '../type/classes/BooleanType' ;
6+ import type { ConstType } from '../type/classes/ConstType' ;
7+ import type { MapType } from '../type/classes/MapType' ;
8+ import type { NumberType } from '../type/classes/NumberType' ;
9+ import type { ObjectType } from '../type/classes/ObjectType' ;
10+ import type { OrType } from '../type/classes/OrType' ;
11+ import type { RefType } from '../type/classes/RefType' ;
12+ import type { StringType } from '../type/classes/StringType' ;
13+ import type { TupleType } from '../type/classes/TupleType' ;
14+ import type { TypeExportContext } from '../system/TypeExportContext' ;
15+ import type * as schema from '../schema' ;
16+ import type {
17+ JsonSchemaNode ,
18+ JsonSchemaGenericKeywords ,
19+ JsonSchemaAny ,
20+ JsonSchemaArray ,
21+ JsonSchemaBinary ,
22+ JsonSchemaBoolean ,
23+ JsonSchemaString ,
24+ JsonSchemaNumber ,
25+ JsonSchemaObject ,
26+ JsonSchemaRef ,
27+ JsonSchemaOr ,
28+ JsonSchemaNull
29+ } from './types' ;
30+
31+ /**
32+ * Extracts the base JSON Schema properties that are common to all types.
33+ * This replaces the logic from AbstractType.toJsonSchema().
34+ */
35+ function getBaseJsonSchema ( type : AbstractType < any > , ctx ?: TypeExportContext ) : JsonSchemaGenericKeywords {
36+ const typeSchema = type . getSchema ( ) ;
37+ const jsonSchema : JsonSchemaGenericKeywords = { } ;
38+
39+ if ( typeSchema . title ) jsonSchema . title = typeSchema . title ;
40+ if ( typeSchema . description ) jsonSchema . description = typeSchema . description ;
41+ if ( typeSchema . examples ) {
42+ jsonSchema . examples = typeSchema . examples . map ( ( example : schema . TExample ) => example . value ) ;
43+ }
44+
45+ return jsonSchema ;
46+ }
47+
48+ /**
49+ * Main router function that converts a type to JSON Schema using a switch statement.
50+ * This replaces the individual toJsonSchema() methods on each type class.
51+ */
52+ export function typeToJsonSchema ( type : AbstractType < any > , ctx ?: TypeExportContext ) : JsonSchemaNode {
53+ const typeName = type . getTypeName ( ) ;
54+
55+ switch ( typeName ) {
56+ case 'any' :
57+ return anyToJsonSchema ( type as AnyType , ctx ) ;
58+ case 'arr' :
59+ return arrayToJsonSchema ( type as ArrayType < any > , ctx ) ;
60+ case 'bin' :
61+ return binaryToJsonSchema ( type as BinaryType < any > , ctx ) ;
62+ case 'bool' :
63+ return booleanToJsonSchema ( type as BooleanType , ctx ) ;
64+ case 'const' :
65+ return constToJsonSchema ( type as ConstType < any > , ctx ) ;
66+ case 'map' :
67+ return mapToJsonSchema ( type as MapType < any > , ctx ) ;
68+ case 'num' :
69+ return numberToJsonSchema ( type as NumberType , ctx ) ;
70+ case 'obj' :
71+ return objectToJsonSchema ( type as ObjectType < any > , ctx ) ;
72+ case 'or' :
73+ return orToJsonSchema ( type as OrType < any > , ctx ) ;
74+ case 'ref' :
75+ return refToJsonSchema ( type as RefType < any > , ctx ) ;
76+ case 'str' :
77+ return stringToJsonSchema ( type as StringType , ctx ) ;
78+ case 'tup' :
79+ return tupleToJsonSchema ( type as TupleType < any > , ctx ) ;
80+ default :
81+ // Fallback to base implementation for unknown types
82+ return getBaseJsonSchema ( type , ctx ) ;
83+ }
84+ }
85+
86+ // Individual converter functions for each type
87+
88+ function anyToJsonSchema ( type : AnyType , ctx ?: TypeExportContext ) : JsonSchemaAny {
89+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
90+ const result : JsonSchemaAny = {
91+ type : [ 'string' , 'number' , 'boolean' , 'null' , 'array' , 'object' ] as const ,
92+ } ;
93+
94+ // Add base properties
95+ Object . assign ( result , baseSchema ) ;
96+
97+ return result ;
98+ }
99+
100+ function arrayToJsonSchema ( type : ArrayType < any > , ctx ?: TypeExportContext ) : JsonSchemaArray {
101+ const schema = type . getSchema ( ) ;
102+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
103+ const result : JsonSchemaArray = {
104+ type : 'array' ,
105+ items : typeToJsonSchema ( ( type as any ) . type , ctx ) ,
106+ } ;
107+
108+ // Add base properties
109+ Object . assign ( result , baseSchema ) ;
110+
111+ if ( schema . min !== undefined ) result . minItems = schema . min ;
112+ if ( schema . max !== undefined ) result . maxItems = schema . max ;
113+
114+ return result ;
115+ }
116+
117+ function binaryToJsonSchema ( type : BinaryType < any > , ctx ?: TypeExportContext ) : JsonSchemaBinary {
118+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
119+ const result : JsonSchemaBinary = {
120+ type : 'binary' as any ,
121+ } ;
122+
123+ // Add base properties
124+ Object . assign ( result , baseSchema ) ;
125+
126+ return result ;
127+ }
128+
129+ function booleanToJsonSchema ( type : BooleanType , ctx ?: TypeExportContext ) : JsonSchemaBoolean {
130+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
131+ const result : JsonSchemaBoolean = {
132+ type : 'boolean' ,
133+ } ;
134+
135+ // Add base properties
136+ Object . assign ( result , baseSchema ) ;
137+
138+ return result ;
139+ }
140+
141+ function constToJsonSchema ( type : ConstType < any > , ctx ?: TypeExportContext ) : JsonSchemaNode {
142+ const schema = type . getSchema ( ) ;
143+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
144+ const value = schema . value ;
145+
146+ if ( typeof value === 'string' ) {
147+ const result : JsonSchemaString = {
148+ type : 'string' ,
149+ const : value ,
150+ } ;
151+ Object . assign ( result , baseSchema ) ;
152+ return result ;
153+ } else if ( typeof value === 'number' ) {
154+ const result : JsonSchemaNumber = {
155+ type : 'number' ,
156+ const : value ,
157+ } ;
158+ Object . assign ( result , baseSchema ) ;
159+ return result ;
160+ } else if ( typeof value === 'boolean' ) {
161+ const result : JsonSchemaBoolean = {
162+ type : 'boolean' ,
163+ const : value ,
164+ } ;
165+ Object . assign ( result , baseSchema ) ;
166+ return result ;
167+ } else if ( value === null ) {
168+ const result : any = {
169+ type : 'null' ,
170+ const : null ,
171+ } ;
172+ Object . assign ( result , baseSchema ) ;
173+ return result ;
174+ } else if ( typeof value === 'undefined' ) {
175+ // For undefined values, we return a special schema
176+ const result : any = {
177+ type : 'undefined' ,
178+ const : undefined ,
179+ } ;
180+ Object . assign ( result , baseSchema ) ;
181+ return result ;
182+ } else if ( Array . isArray ( value ) ) {
183+ const result : JsonSchemaArray = {
184+ type : 'array' ,
185+ const : value ,
186+ items : false ,
187+ } ;
188+ Object . assign ( result , baseSchema ) ;
189+ return result ;
190+ } else if ( typeof value === 'object' ) {
191+ const result : JsonSchemaObject = {
192+ type : 'object' ,
193+ const : value ,
194+ } ;
195+ Object . assign ( result , baseSchema ) ;
196+ return result ;
197+ }
198+
199+ return baseSchema ;
200+ }
201+
202+ function mapToJsonSchema ( type : MapType < any > , ctx ?: TypeExportContext ) : JsonSchemaObject {
203+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
204+ const result : JsonSchemaObject = {
205+ type : 'object' ,
206+ patternProperties : {
207+ '.*' : typeToJsonSchema ( ( type as any ) . type , ctx ) ,
208+ } ,
209+ } ;
210+
211+ // Add base properties
212+ Object . assign ( result , baseSchema ) ;
213+
214+ return result ;
215+ }
216+
217+ function numberToJsonSchema ( type : NumberType , ctx ?: TypeExportContext ) : JsonSchemaNumber {
218+ const schema = type . getSchema ( ) ;
219+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
220+ const result : JsonSchemaNumber = {
221+ type : 'number' ,
222+ } ;
223+
224+ // Check if it's an integer format
225+ const ints = new Set ( [ 'i8' , 'i16' , 'i32' , 'u8' , 'u16' , 'u32' ] ) ;
226+ if ( schema . format && ints . has ( schema . format ) ) {
227+ result . type = 'integer' ;
228+ }
229+
230+ // Add base properties
231+ Object . assign ( result , baseSchema ) ;
232+
233+ if ( schema . gt !== undefined ) result . exclusiveMinimum = schema . gt ;
234+ if ( schema . gte !== undefined ) result . minimum = schema . gte ;
235+ if ( schema . lt !== undefined ) result . exclusiveMaximum = schema . lt ;
236+ if ( schema . lte !== undefined ) result . maximum = schema . lte ;
237+
238+ return result ;
239+ }
240+
241+ function objectToJsonSchema ( type : ObjectType < any > , ctx ?: TypeExportContext ) : JsonSchemaObject {
242+ const schema = type . getSchema ( ) ;
243+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
244+ const result : JsonSchemaObject = {
245+ type : 'object' ,
246+ properties : { } ,
247+ } ;
248+
249+ const required = [ ] ;
250+ const fields = ( type as any ) . fields ;
251+ for ( const field of fields ) {
252+ result . properties ! [ field . key ] = typeToJsonSchema ( field . value , ctx ) ;
253+ if ( ! ( field . constructor . name . includes ( 'Optional' ) ) ) {
254+ required . push ( field . key ) ;
255+ }
256+ }
257+
258+ if ( required . length ) result . required = required ;
259+ if ( schema . unknownFields === false ) result . additionalProperties = false ;
260+
261+ // Add base properties
262+ Object . assign ( result , baseSchema ) ;
263+
264+ return result ;
265+ }
266+
267+ function orToJsonSchema ( type : OrType < any > , ctx ?: TypeExportContext ) : JsonSchemaOr {
268+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
269+ const types = ( type as any ) . types ;
270+ const result : JsonSchemaOr = {
271+ anyOf : types . map ( ( t : any ) => typeToJsonSchema ( t , ctx ) ) ,
272+ } ;
273+
274+ // Add base properties
275+ Object . assign ( result , baseSchema ) ;
276+
277+ return result ;
278+ }
279+
280+ function refToJsonSchema ( type : RefType < any > , ctx ?: TypeExportContext ) : JsonSchemaRef {
281+ const schema = type . getSchema ( ) ;
282+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
283+ const ref = schema . ref ;
284+
285+ if ( ctx ) ctx . mentionRef ( ref ) ;
286+
287+ const result : JsonSchemaRef = {
288+ $ref : `#/$defs/${ ref } ` ,
289+ } ;
290+
291+ // Add base properties
292+ Object . assign ( result , baseSchema ) ;
293+
294+ return result ;
295+ }
296+
297+ function stringToJsonSchema ( type : StringType , ctx ?: TypeExportContext ) : JsonSchemaString {
298+ const schema = type . getSchema ( ) ;
299+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
300+ const result : JsonSchemaString = {
301+ type : 'string' ,
302+ } ;
303+
304+ if ( schema . min !== undefined ) result . minLength = schema . min ;
305+ if ( schema . max !== undefined ) result . maxLength = schema . max ;
306+
307+ // Add format to JSON Schema if specified
308+ if ( schema . format ) {
309+ if ( schema . format === 'ascii' ) {
310+ // JSON Schema doesn't have an "ascii" format, but we can use a pattern
311+ // ASCII characters are from 0x00 to 0x7F (0-127)
312+ result . pattern = '^[\\x00-\\x7F]*$' ;
313+ }
314+ // UTF-8 is the default for JSON Schema strings, so we don't need to add anything special
315+ } else if ( schema . ascii ) {
316+ // Backward compatibility: if ascii=true, add pattern
317+ result . pattern = '^[\\x00-\\x7F]*$' ;
318+ }
319+
320+ // Add base properties
321+ Object . assign ( result , baseSchema ) ;
322+
323+ return result ;
324+ }
325+
326+ function tupleToJsonSchema ( type : TupleType < any > , ctx ?: TypeExportContext ) : JsonSchemaArray {
327+ const baseSchema = getBaseJsonSchema ( type , ctx ) ;
328+ const types = ( type as any ) . types ;
329+ const result : JsonSchemaArray = {
330+ type : 'array' ,
331+ items : false ,
332+ prefixItems : types . map ( ( t : any ) => typeToJsonSchema ( t , ctx ) ) ,
333+ } ;
334+
335+ // Add base properties
336+ Object . assign ( result , baseSchema ) ;
337+
338+ return result ;
339+ }
0 commit comments