@@ -10,16 +10,26 @@ import { type OpenAPIV3_1 as OpenApi } from 'openapi-types';
10
10
import { OpenApiSchemaId , type OpenApiRef , type OpenApiSchema } from '../../util/openapi.ts' ;
11
11
12
12
import * as GenSpec from '../generationSpec.ts' ;
13
- import { isObjectSchema } from '../../analysis/GraphAnalyzer.ts' ;
13
+ import { isObjectSchema , schemaIdFromRef } from '../../analysis/GraphAnalyzer.ts' ;
14
14
import { type GenResult , GenResultUtil } from './genUtil.ts' ;
15
15
16
16
17
+ const id = GenResultUtil . encodeIdentifier ;
18
+
17
19
export type Context = {
18
20
schemas : Record < string , OpenApiSchema > ,
19
21
hooks : GenSpec . GenerationHooks ,
20
22
isSchemaIdBefore : ( schemaId : OpenApiSchemaId ) => boolean ,
21
23
} ;
22
24
25
+ export const generateForUnknownSchema = ( ctx : Context , schema : OpenApi . NonArraySchemaObject ) : GenResult => {
26
+ return {
27
+ code : `S.Unknown` ,
28
+ refs : [ ] ,
29
+ comments : GenResultUtil . commentsFromSchemaObject ( schema ) ,
30
+ } ;
31
+ } ;
32
+
23
33
export const generateForNullSchema = ( ctx : Context , schema : OpenApi . NonArraySchemaObject ) : GenResult => {
24
34
return {
25
35
code : `S.Null` ,
@@ -32,11 +42,11 @@ export const generateForStringSchema = (ctx: Context, schema: OpenApi.NonArraySc
32
42
let refs : GenResult [ 'refs' ] = [ ] ;
33
43
const code = ( ( ) : string => {
34
44
if ( Array . isArray ( schema . enum ) ) {
35
- if ( ! schema . enum . every ( value => typeof value === 'string' ) ) {
45
+ if ( ! schema . enum . every ( value => typeof value === 'string' || typeof value === 'number' ) ) {
36
46
throw new TypeError ( `Unknown enum value, expected string array: ${ JSON . stringify ( schema . enum ) } ` ) ;
37
47
}
38
48
return dedent `S.Literal(
39
- ${ schema . enum . map ( ( value : string ) => JSON . stringify ( value ) + ',' ) . join ( '\n' ) }
49
+ ${ schema . enum . map ( ( value : string | number ) => JSON . stringify ( String ( value ) ) + ',' ) . join ( '\n' ) }
40
50
)` ;
41
51
}
42
52
@@ -293,17 +303,13 @@ export const generateForArraySchema = (ctx: Context, schema: OpenApi.ArraySchema
293
303
294
304
export const generateForReferenceObject = ( ctx : Context , schema : OpenApi . ReferenceObject ) : GenResult => {
295
305
// FIXME: make this logic customizable (allow a callback to resolve a `$ref` string to a `Ref` instance?)
296
- const matches = schema . $ref . match ( / ^ # \/ c o m p o n e n t s \/ s c h e m a s \/ ( [ a - z A - Z 0 - 9 _ $ ] + ) / ) ;
297
- if ( ! matches ) {
298
- throw new Error ( `Reference format not supported: ${ schema . $ref } ` ) ;
299
- }
300
-
301
- const schemaId = matches [ 1 ] ;
302
- if ( typeof schemaId === 'undefined' ) { throw new Error ( 'Should not happen' ) ; }
306
+ const schemaId = schemaIdFromRef ( schema . $ref ) ;
303
307
304
308
// If the referenced schema ID is topologically after the current one, wrap it in `S.suspend` for lazy eval
305
309
const shouldSuspend = ! ctx . isSchemaIdBefore ( schemaId ) ;
306
- const code = shouldSuspend ? `S.suspend((): S.Schema<_${ schemaId } , _${ schemaId } Encoded> => ${ schemaId } )` : schemaId ;
310
+ const code = shouldSuspend
311
+ ? `S.suspend((): S.Schema<_${ id ( schemaId ) } , _${ id ( schemaId ) } Encoded> => ${ id ( schemaId ) } )`
312
+ : id ( schemaId ) ;
307
313
308
314
return { code, refs : [ `./${ schemaId } .ts` ] , comments : GenResultUtil . initComments ( ) } ;
309
315
} ;
@@ -317,8 +323,8 @@ export const generateForSchema = (ctx: Context, schema: OpenApiSchema): GenResul
317
323
if ( '$ref' in schema ) { // Case: OpenApi.ReferenceObject
318
324
return generateForReferenceObject ( ctx , schema ) ;
319
325
} else { // Case: OpenApi.SchemaObject
320
- if ( 'items' in schema && schema . type === 'array' ) { // Case: OpenApi.ArraySchemaObject
321
- return generateForArraySchema ( ctx , schema ) ;
326
+ if ( schema . type === 'array' || 'items' in schema ) { // Case: OpenApi.ArraySchemaObject
327
+ return generateForArraySchema ( ctx , { items : { } , ... schema } as OpenApi . ArraySchemaObject ) ;
322
328
} else if ( isNonArraySchemaType ( schema ) ) { // Case: OpenApi.NonArraySchemaObject
323
329
if ( 'allOf' in schema && typeof schema . allOf !== 'undefined' ) {
324
330
const schemasHead : undefined | OpenApiSchema = schema . allOf [ 0 ] ;
@@ -483,7 +489,7 @@ export const generateForSchema = (ctx: Context, schema: OpenApiSchema): GenResul
483
489
} )
484
490
. join ( '\n' )
485
491
}
486
- );
492
+ )
487
493
` ;
488
494
return {
489
495
code,
@@ -496,17 +502,14 @@ export const generateForSchema = (ctx: Context, schema: OpenApiSchema): GenResul
496
502
type SchemaType = 'array' | OpenApi . NonArraySchemaObjectType ;
497
503
const type : undefined | OpenApi . NonArraySchemaObjectType | Array < SchemaType > = schema . type ;
498
504
499
- if ( typeof type === 'undefined' ) {
500
- throw new TypeError ( `Missing 'type' in schema` ) ;
501
- }
502
-
503
505
const hookResult : null | GenResult = ctx . hooks . generateSchema ?.( schema ) ?? null ;
504
506
505
507
let result : GenResult ;
506
508
if ( hookResult !== null ) {
507
509
result = hookResult ;
508
510
} else {
509
511
switch ( type ) {
512
+ case undefined : result = generateForUnknownSchema ( ctx , schema ) ; break ;
510
513
case 'null' : result = generateForNullSchema ( ctx , schema ) ; break ;
511
514
case 'string' : result = generateForStringSchema ( ctx , schema ) ; break ;
512
515
case 'number' : result = generateForNumberSchema ( ctx , schema ) ; break ;
0 commit comments