@@ -57,6 +57,10 @@ export class Input {
5757 constructor (
5858 readonly loader : OpenapiLoader ,
5959 readonly config : InputConfig ,
60+ private readonly schemaNormalizer = new SchemaNormalizer ( config ) ,
61+ private readonly parameterNormalizer = new ParameterNormalizer (
62+ schemaNormalizer ,
63+ ) ,
6064 ) { }
6165
6266 name ( ) : string {
@@ -84,7 +88,10 @@ export class Input {
8488 return Object . fromEntries (
8589 Object . entries ( schemas ) . map ( ( [ name , maybeSchema ] ) => {
8690 // TODO: double normalization?
87- return [ name , this . schema ( this . normalizeSchemaObject ( maybeSchema ) ) ]
91+ return [
92+ name ,
93+ this . schema ( this . schemaNormalizer . normalizeSchemaObject ( maybeSchema ) ) ,
94+ ]
8895 } ) ,
8996 )
9097 }
@@ -231,7 +238,7 @@ export class Input {
231238
232239 schema ( maybeRef : Reference | Schema ) : IRModel {
233240 const schema = this . loader . schema ( maybeRef )
234- return this . normalizeSchemaObject ( schema )
241+ return this . schemaNormalizer . normalizeSchemaObject ( schema )
235242 }
236243
237244 preprocess ( maybePreprocess : Reference | xInternalPreproccess ) : IRPreprocess {
@@ -338,141 +345,7 @@ export class Input {
338345 ) : IRParameter [ ] {
339346 return parameters
340347 . map ( ( it ) => this . loader . parameter ( it ) )
341- . map ( ( it : Parameter ) : IRParameter => {
342- const base = {
343- name : it . name ,
344- schema : this . normalizeSchemaObject ( it . schema ) ,
345- description : it . description ,
346- required : it . required ?? false ,
347- deprecated : it . deprecated ?? false ,
348- } satisfies Omit < IRParameterBase , "explode" >
349-
350- function throwUnsupportedStyle ( style : Style ) : never {
351- throw new Error (
352- `unsupported parameter style: '${ style } ' for in: '${ it . in } '` ,
353- )
354- }
355-
356- switch ( it . in ) {
357- case "path" : {
358- const style = it . style ?? "simple"
359- const explode = this . explodeForParameter ( it , style )
360-
361- if ( ! this . isStyleForPathParameter ( style ) ) {
362- throwUnsupportedStyle ( style )
363- }
364-
365- return {
366- ...base ,
367- in : "path" ,
368- style,
369- explode,
370- } satisfies IRParameterPath
371- }
372-
373- case "query" : {
374- const style = it . style ?? "form"
375- const explode = this . explodeForParameter ( it , style )
376-
377- if ( ! this . isStyleForQueryParameter ( style ) ) {
378- throwUnsupportedStyle ( style )
379- }
380-
381- return {
382- ...base ,
383- in : "query" ,
384- style,
385- explode,
386- allowEmptyValue : it . allowEmptyValue ?? false ,
387- } satisfies IRParameterQuery
388- }
389-
390- case "header" : {
391- const style = it . style ?? "simple"
392- const explode = this . explodeForParameter ( it , style )
393-
394- if ( ! this . isStyleForHeaderParameter ( style ) ) {
395- throwUnsupportedStyle ( style )
396- }
397-
398- return {
399- ...base ,
400- in : "header" ,
401- style,
402- explode,
403- } satisfies IRParameterHeader
404- }
405-
406- case "cookie" : {
407- const style = it . style ?? "form"
408- const explode = this . explodeForParameter ( it , style )
409-
410- if ( ! this . isStyleForCookieParameter ( style ) ) {
411- throwUnsupportedStyle ( style )
412- }
413-
414- return {
415- ...base ,
416- in : "cookie" ,
417- style,
418- explode,
419- } satisfies IRParameterCookie
420- }
421-
422- default : {
423- throw new Error (
424- `unsupported parameter location: '${ it . in satisfies never } '` ,
425- )
426- }
427- }
428- } )
429- }
430-
431- private isStyleForPathParameter (
432- style : Style ,
433- ) : style is IRParameterPath [ "style" ] {
434- return [ "simple" , "label" , "matrix" , "template" ] . includes ( style )
435- }
436-
437- private isStyleForQueryParameter (
438- style : Style ,
439- ) : style is IRParameterQuery [ "style" ] {
440- return [ "form" , "spaceDelimited" , "pipeDelimited" , "deepObject" ] . includes (
441- style ,
442- )
443- }
444-
445- private isStyleForHeaderParameter (
446- style : Style ,
447- ) : style is IRParameterHeader [ "style" ] {
448- return [ "simple" ] . includes ( style )
449- }
450-
451- private isStyleForCookieParameter (
452- style : Style ,
453- ) : style is IRParameterCookie [ "style" ] {
454- if ( style === "cookie" ) {
455- // todo: openapi v3.2.0
456- throw new Error ( "support for style: cookie not implemented." )
457- }
458-
459- return [ "form" ] . includes ( style )
460- }
461-
462- private explodeForParameter ( parameter : Parameter , style : Style ) : boolean {
463- if ( typeof parameter . explode === "boolean" ) {
464- return parameter . explode
465- }
466-
467- /**
468- * "When style is "form" or "cookie", the default value is true. For all other styles, the default value is false."
469- * ref: {@link https://spec.openapis.org/oas/v3.2.0.html#parameter-explode}
470- */
471- if ( style === "form" || style === "cookie" ) {
472- return true
473- }
474-
475- return false
348+ . map ( ( it ) => this . parameterNormalizer . normalizeParameter ( it ) )
476349 }
477350
478351 private normalizeOperationId (
@@ -525,7 +398,7 @@ export class Input {
525398 const syntheticName = `${ operationId } ${ mediaTypeToIdentifier (
526399 mediaType ,
527400 ) } ${ suffix } `
528- const result = this . normalizeSchemaObject ( schema )
401+ const result = this . schemaNormalizer . normalizeSchemaObject ( schema )
529402
530403 const shouldCreateVirtualType =
531404 this . config . extractInlineSchemas &&
@@ -539,13 +412,157 @@ export class Input {
539412 ? this . loader . addVirtualType ( operationId , syntheticName , result )
540413 : result
541414 }
415+ }
416+
417+ export class ParameterNormalizer {
418+ constructor ( private readonly schemaNormalizer : SchemaNormalizer ) { }
419+
420+ public normalizeParameter ( it : Parameter ) : IRParameter {
421+ const base = {
422+ name : it . name ,
423+ schema : this . schemaNormalizer . normalizeSchemaObject ( it . schema ) ,
424+ description : it . description ,
425+ required : it . required ?? false ,
426+ deprecated : it . deprecated ?? false ,
427+ } satisfies Omit < IRParameterBase , "explode" >
428+
429+ function throwUnsupportedStyle ( style : Style ) : never {
430+ throw new Error (
431+ `unsupported parameter style: '${ style } ' for in: '${ it . in } '` ,
432+ )
433+ }
434+
435+ switch ( it . in ) {
436+ case "path" : {
437+ const style = it . style ?? "simple"
438+ const explode = this . explodeForParameter ( it , style )
439+
440+ if ( ! this . isStyleForPathParameter ( style ) ) {
441+ throwUnsupportedStyle ( style )
442+ }
443+
444+ return {
445+ ...base ,
446+ in : "path" ,
447+ style,
448+ explode,
449+ } satisfies IRParameterPath
450+ }
451+
452+ case "query" : {
453+ const style = it . style ?? "form"
454+ const explode = this . explodeForParameter ( it , style )
455+
456+ if ( ! this . isStyleForQueryParameter ( style ) ) {
457+ throwUnsupportedStyle ( style )
458+ }
459+
460+ return {
461+ ...base ,
462+ in : "query" ,
463+ style,
464+ explode,
465+ allowEmptyValue : it . allowEmptyValue ?? false ,
466+ } satisfies IRParameterQuery
467+ }
468+
469+ case "header" : {
470+ const style = it . style ?? "simple"
471+ const explode = this . explodeForParameter ( it , style )
472+
473+ if ( ! this . isStyleForHeaderParameter ( style ) ) {
474+ throwUnsupportedStyle ( style )
475+ }
476+
477+ return {
478+ ...base ,
479+ in : "header" ,
480+ style,
481+ explode,
482+ } satisfies IRParameterHeader
483+ }
484+
485+ case "cookie" : {
486+ const style = it . style ?? "form"
487+ const explode = this . explodeForParameter ( it , style )
488+
489+ if ( ! this . isStyleForCookieParameter ( style ) ) {
490+ throwUnsupportedStyle ( style )
491+ }
492+
493+ return {
494+ ...base ,
495+ in : "cookie" ,
496+ style,
497+ explode,
498+ } satisfies IRParameterCookie
499+ }
500+
501+ default : {
502+ throw new Error (
503+ `unsupported parameter location: '${ it . in satisfies never } '` ,
504+ )
505+ }
506+ }
507+ }
508+
509+ private isStyleForPathParameter (
510+ style : Style ,
511+ ) : style is IRParameterPath [ "style" ] {
512+ return [ "simple" , "label" , "matrix" , "template" ] . includes ( style )
513+ }
514+
515+ private isStyleForQueryParameter (
516+ style : Style ,
517+ ) : style is IRParameterQuery [ "style" ] {
518+ return [ "form" , "spaceDelimited" , "pipeDelimited" , "deepObject" ] . includes (
519+ style ,
520+ )
521+ }
522+
523+ private isStyleForHeaderParameter (
524+ style : Style ,
525+ ) : style is IRParameterHeader [ "style" ] {
526+ return [ "simple" ] . includes ( style )
527+ }
528+
529+ private isStyleForCookieParameter (
530+ style : Style ,
531+ ) : style is IRParameterCookie [ "style" ] {
532+ if ( style === "cookie" ) {
533+ // todo: openapi v3.2.0
534+ throw new Error ( "support for style: cookie not implemented." )
535+ }
536+
537+ return [ "form" ] . includes ( style )
538+ }
539+
540+ private explodeForParameter ( parameter : Parameter , style : Style ) : boolean {
541+ if ( typeof parameter . explode === "boolean" ) {
542+ return parameter . explode
543+ }
544+
545+ /**
546+ * "When style is "form" or "cookie", the default value is true. For all other styles, the default value is false."
547+ * ref: {@link https://spec.openapis.org/oas/v3.2.0.html#parameter-explode}
548+ */
549+ if ( style === "form" || style === "cookie" ) {
550+ return true
551+ }
552+
553+ return false
554+ }
555+ }
556+
557+ export class SchemaNormalizer {
558+ constructor ( readonly config : InputConfig ) { }
542559
543- private normalizeSchemaObject ( schemaObject : Schema ) : IRModel
544- private normalizeSchemaObject ( schemaObject : Reference ) : IRRef
545- private normalizeSchemaObject (
560+ public normalizeSchemaObject ( schemaObject : Schema ) : IRModel
561+ public normalizeSchemaObject ( schemaObject : Reference ) : IRRef
562+ public normalizeSchemaObject (
546563 schemaObject : Schema | Reference ,
547564 ) : IRModel | IRRef
548- private normalizeSchemaObject (
565+ public normalizeSchemaObject (
549566 schemaObject : Schema | Reference ,
550567 ) : IRModel | IRRef {
551568 const self = this
0 commit comments