nestjs-openapi 0.1.1 → 0.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/cli.mjs CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import 'tsx';
3
- import { g as generate, e as formatValidationResult } from './shared/nestjs-openapi.CCvQ3RSW.mjs';
3
+ import { g as generate, e as formatValidationResult } from './shared/nestjs-openapi.D2c4bMP5.mjs';
4
4
  import minimist from 'minimist';
5
5
  import { relative } from 'node:path';
6
6
  import { createRequire } from 'node:module';
7
7
  import 'effect';
8
8
  import 'node:fs';
9
+ import 'node:crypto';
9
10
  import 'ts-morph';
10
11
  import 'glob';
11
12
  import 'js-yaml';
12
- import './shared/nestjs-openapi.CzNt1duF.mjs';
13
+ import './shared/nestjs-openapi.DRcy130f.mjs';
13
14
  import 'ts-json-schema-generator';
14
- import 'node:crypto';
15
15
  import 'node:url';
16
16
  import 'child_process';
17
17
 
@@ -117,7 +117,7 @@ const main = async () => {
117
117
  console.log(` ${formatValidationResult(result.validation)}`);
118
118
  }
119
119
  }
120
- process.exit(0);
120
+ process.exit(result.validation.valid ? 0 : 1);
121
121
  } catch (err) {
122
122
  const duration = performance.now() - startTime;
123
123
  const message = err instanceof Error ? err.message : String(err);
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect, Context, Layer, Option } from 'effect';
2
- import { C as ConfigNotFoundError, O as OpenApiGeneratorConfig, a as ConfigError, R as ResolvedConfig, P as ProjectError, b as ProjectInitError, E as EntryNotFoundError, M as MethodInfo, c as OpenApiPaths$1 } from './shared/nestjs-openapi.BYUrTaMo.mjs';
3
- export { A as AnalysisError, i as ConfigLoadError, j as ConfigValidationError, G as GenerateOptions, k as GeneratorError, H as HttpMethod, I as InvalidMethodError, e as ParameterLocation, f as ResolvedParameter, h as ReturnTypeInfo, d as generateAsync, g as generateEffect } from './shared/nestjs-openapi.BYUrTaMo.mjs';
2
+ import { C as ConfigNotFoundError, O as OpenApiGeneratorConfig, a as ConfigError, R as ResolvedConfig, P as ProjectError, b as ProjectInitError, E as EntryNotFoundError, M as MethodInfo, c as OpenApiPaths$1 } from './shared/nestjs-openapi.t3iwzrrT.mjs';
3
+ export { A as AnalysisError, i as ConfigLoadError, j as ConfigValidationError, G as GenerateOptions, k as GeneratorError, H as HttpMethod, I as InvalidMethodError, e as ParameterLocation, f as ResolvedParameter, h as ReturnTypeInfo, d as generateAsync, g as generateEffect } from './shared/nestjs-openapi.t3iwzrrT.mjs';
4
4
  import { DynamicModule } from '@nestjs/common';
5
5
  import { Project, SourceFile, ClassDeclaration, MethodDeclaration, Decorator, Symbol, ObjectLiteralExpression, Expression } from 'ts-morph';
6
6
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect, Context, Layer, Option } from 'effect';
2
- import { C as ConfigNotFoundError, O as OpenApiGeneratorConfig, a as ConfigError, R as ResolvedConfig, P as ProjectError, b as ProjectInitError, E as EntryNotFoundError, M as MethodInfo, c as OpenApiPaths$1 } from './shared/nestjs-openapi.BYUrTaMo.js';
3
- export { A as AnalysisError, i as ConfigLoadError, j as ConfigValidationError, G as GenerateOptions, k as GeneratorError, H as HttpMethod, I as InvalidMethodError, e as ParameterLocation, f as ResolvedParameter, h as ReturnTypeInfo, d as generateAsync, g as generateEffect } from './shared/nestjs-openapi.BYUrTaMo.js';
2
+ import { C as ConfigNotFoundError, O as OpenApiGeneratorConfig, a as ConfigError, R as ResolvedConfig, P as ProjectError, b as ProjectInitError, E as EntryNotFoundError, M as MethodInfo, c as OpenApiPaths$1 } from './shared/nestjs-openapi.t3iwzrrT.js';
3
+ export { A as AnalysisError, i as ConfigLoadError, j as ConfigValidationError, G as GenerateOptions, k as GeneratorError, H as HttpMethod, I as InvalidMethodError, e as ParameterLocation, f as ResolvedParameter, h as ReturnTypeInfo, d as generateAsync, g as generateEffect } from './shared/nestjs-openapi.t3iwzrrT.js';
4
4
  import { DynamicModule } from '@nestjs/common';
5
5
  import { Project, SourceFile, ClassDeclaration, MethodDeclaration, Decorator, Symbol, ObjectLiteralExpression, Expression } from 'ts-morph';
6
6
 
package/dist/index.mjs CHANGED
@@ -1,16 +1,16 @@
1
- export { c as categorizeBrokenRefs, d as defineConfig, f as findConfigFile, e as formatValidationResult, g as generate, b as loadAndResolveConfig, a as loadConfig, l as loadConfigFromFile, r as resolveConfig, v as validateSpec } from './shared/nestjs-openapi.CCvQ3RSW.mjs';
1
+ export { c as categorizeBrokenRefs, d as defineConfig, f as findConfigFile, e as formatValidationResult, g as generate, b as loadAndResolveConfig, a as loadConfig, l as loadConfigFromFile, r as resolveConfig, v as validateSpec } from './shared/nestjs-openapi.D2c4bMP5.mjs';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { Module } from '@nestjs/common';
5
5
  export { generateAsync, generate as generateEffect } from './internal.mjs';
6
- import { P as ProjectInitError, E as EntryNotFoundError } from './shared/nestjs-openapi.CzNt1duF.mjs';
7
- export { a as ConfigLoadError, C as ConfigNotFoundError, b as ConfigValidationError, I as InvalidMethodError, c as getAllControllers, q as getArrayInitializer, o as getControllerMethodInfos, e as getControllerName, d as getControllerPrefix, j as getControllerTags, h as getDecoratorName, k as getHttpDecorator, f as getHttpMethods, m as getMethodInfo, w as getModuleDecoratorArg, z as getModuleMetadata, g as getModules, s as getStringLiteralValue, u as getSymbolFromIdentifier, l as isHttpDecorator, i as isHttpMethod, v as isModuleClass, n as normalizePath, y as resolveArrayOfClasses, x as resolveClassFromExpression, r as resolveClassFromSymbol, t as transformMethod, p as transformMethods } from './shared/nestjs-openapi.CzNt1duF.mjs';
6
+ import { P as ProjectInitError, E as EntryNotFoundError } from './shared/nestjs-openapi.DRcy130f.mjs';
7
+ export { a as ConfigLoadError, C as ConfigNotFoundError, b as ConfigValidationError, I as InvalidMethodError, c as getAllControllers, q as getArrayInitializer, o as getControllerMethodInfos, e as getControllerName, d as getControllerPrefix, j as getControllerTags, h as getDecoratorName, k as getHttpDecorator, f as getHttpMethods, m as getMethodInfo, w as getModuleDecoratorArg, z as getModuleMetadata, g as getModules, s as getStringLiteralValue, u as getSymbolFromIdentifier, l as isHttpDecorator, i as isHttpMethod, v as isModuleClass, n as normalizePath, y as resolveArrayOfClasses, x as resolveClassFromExpression, r as resolveClassFromSymbol, t as transformMethod, p as transformMethods } from './shared/nestjs-openapi.DRcy130f.mjs';
8
8
  import { Context, Effect, Layer } from 'effect';
9
9
  import { Project } from 'ts-morph';
10
+ import 'node:crypto';
10
11
  import 'glob';
11
12
  import 'js-yaml';
12
13
  import 'ts-json-schema-generator';
13
- import 'node:crypto';
14
14
  import 'node:url';
15
15
  import 'child_process';
16
16
 
@@ -1,2 +1,2 @@
1
1
  import 'effect';
2
- export { G as GenerateOptions, g as generate, d as generateAsync } from './shared/nestjs-openapi.BYUrTaMo.mjs';
2
+ export { G as GenerateOptions, g as generate, d as generateAsync } from './shared/nestjs-openapi.t3iwzrrT.mjs';
@@ -1,2 +1,2 @@
1
1
  import 'effect';
2
- export { G as GenerateOptions, g as generate, d as generateAsync } from './shared/nestjs-openapi.BYUrTaMo.js';
2
+ export { G as GenerateOptions, g as generate, d as generateAsync } from './shared/nestjs-openapi.t3iwzrrT.js';
package/dist/internal.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect } from 'effect';
2
2
  import { Project } from 'ts-morph';
3
- import { E as EntryNotFoundError, g as getModules, o as getControllerMethodInfos, p as transformMethods } from './shared/nestjs-openapi.CzNt1duF.mjs';
3
+ import { E as EntryNotFoundError, g as getModules, o as getControllerMethodInfos, p as transformMethods } from './shared/nestjs-openapi.DRcy130f.mjs';
4
4
 
5
5
  const generate = (options) => Effect.gen(function* () {
6
6
  yield* Effect.logInfo("Starting OpenAPI generation").pipe(
@@ -1,12 +1,12 @@
1
1
  import { Schema, Effect, Logger, Layer, LogLevel } from 'effect';
2
2
  import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from 'node:fs';
3
- import { join, dirname, resolve } from 'node:path';
3
+ import { join, dirname, resolve, relative } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
4
5
  import { Project } from 'ts-morph';
5
6
  import { globSync, glob } from 'glob';
6
7
  import yaml from 'js-yaml';
7
- import { C as ConfigNotFoundError, a as ConfigLoadError, b as ConfigValidationError, p as transformMethods, A as extractClassConstraints, B as getRequiredProperties, D as mergeValidationConstraints, E as EntryNotFoundError, g as getModules, o as getControllerMethodInfos } from './nestjs-openapi.CzNt1duF.mjs';
8
+ import { C as ConfigNotFoundError, a as ConfigLoadError, b as ConfigValidationError, p as transformMethods, A as extractClassConstraints, B as getRequiredProperties, D as mergeValidationConstraints, E as EntryNotFoundError, g as getModules, o as getControllerMethodInfos } from './nestjs-openapi.DRcy130f.mjs';
8
9
  import { createGenerator } from 'ts-json-schema-generator';
9
- import { randomUUID } from 'node:crypto';
10
10
  import { pathToFileURL } from 'node:url';
11
11
  import { execSync } from 'child_process';
12
12
 
@@ -466,6 +466,12 @@ const extractReferencedSchemas = (paths) => {
466
466
  if (schema.oneOf) {
467
467
  schema.oneOf.forEach(extractFromSchema);
468
468
  }
469
+ if (schema.allOf) {
470
+ schema.allOf.forEach(extractFromSchema);
471
+ }
472
+ if (schema.anyOf) {
473
+ schema.anyOf.forEach(extractFromSchema);
474
+ }
469
475
  if (schema.properties) {
470
476
  Object.values(schema.properties).forEach(extractFromSchema);
471
477
  }
@@ -529,7 +535,14 @@ const extractNestedReferences = (schemas, knownSchemas) => {
529
535
  };
530
536
  const convertToOpenApiSchema = (schema) => {
531
537
  const result = {};
532
- if (schema.type) result["type"] = schema.type;
538
+ if (Array.isArray(schema.type)) {
539
+ const nonNull = schema.type.filter((t) => t !== "null");
540
+ const isNullable = nonNull.length < schema.type.length;
541
+ result["type"] = nonNull.length === 1 ? nonNull[0] : schema.type;
542
+ if (isNullable && nonNull.length === 1) result["nullable"] = true;
543
+ } else if (schema.type) {
544
+ result["type"] = schema.type;
545
+ }
533
546
  if (schema.format) result["format"] = schema.format;
534
547
  if (schema.$ref) {
535
548
  result["$ref"] = schema.$ref.replace(
@@ -633,7 +646,11 @@ const createPathFilter = (pathFilter) => {
633
646
  if (typeof pathFilter === "function") {
634
647
  return (method) => pathFilter(method.path);
635
648
  }
636
- return (method) => pathFilter.test(method.path);
649
+ const pattern = new RegExp(pathFilter.source, pathFilter.flags);
650
+ return (method) => {
651
+ pattern.lastIndex = 0;
652
+ return pattern.test(method.path);
653
+ };
637
654
  };
638
655
  const combineFilters = (filters) => {
639
656
  if (filters.length === 0) {
@@ -665,6 +682,11 @@ const filterMethods = (methods, options) => {
665
682
  return methods.filter(filter);
666
683
  };
667
684
 
685
+ const NULL_SCHEMA = { type: "null" };
686
+ const schemaIncludesNull = (schema) => {
687
+ if (schema.type === "null") return true;
688
+ return Array.isArray(schema.type) && schema.type.includes("null");
689
+ };
668
690
  const transformSchemaToV31 = (schema) => {
669
691
  const transformedOneOf = schema.oneOf?.map(transformSchemaToV31);
670
692
  const transformedAnyOf = schema.anyOf?.map(transformSchemaToV31);
@@ -676,19 +698,36 @@ const transformSchemaToV31 = (schema) => {
676
698
  transformSchemaToV31(value)
677
699
  ])
678
700
  ) : void 0;
679
- const hasNullable = schema.nullable && schema.type && typeof schema.type === "string";
680
- const transformedType = hasNullable ? [schema.type, "null"] : schema.type;
681
- const { nullable: _nullable, ...restWithoutNullable } = schema;
682
- return {
701
+ const { nullable, ...restWithoutNullable } = schema;
702
+ const transformedType = nullable && typeof schema.type === "string" ? [schema.type, "null"] : schema.type;
703
+ const transformedSchema = {
683
704
  ...restWithoutNullable,
684
- type: transformedType,
705
+ ...transformedType !== void 0 && { type: transformedType },
685
706
  ...transformedOneOf && { oneOf: transformedOneOf },
686
707
  ...transformedAnyOf && { anyOf: transformedAnyOf },
687
708
  ...transformedAllOf && { allOf: transformedAllOf },
688
709
  ...transformedItems && { items: transformedItems },
689
710
  ...transformedProperties && { properties: transformedProperties }
690
711
  };
712
+ if (!nullable) return transformedSchema;
713
+ if (schema.type && typeof schema.type === "string") {
714
+ return transformedSchema;
715
+ }
716
+ if (transformedSchema.oneOf) {
717
+ return {
718
+ ...transformedSchema,
719
+ oneOf: schemaIncludesNullInVariants(transformedSchema.oneOf) ? transformedSchema.oneOf : [...transformedSchema.oneOf, NULL_SCHEMA]
720
+ };
721
+ }
722
+ if (transformedSchema.anyOf) {
723
+ return {
724
+ ...transformedSchema,
725
+ anyOf: schemaIncludesNullInVariants(transformedSchema.anyOf) ? transformedSchema.anyOf : [...transformedSchema.anyOf, NULL_SCHEMA]
726
+ };
727
+ }
728
+ return { anyOf: [transformedSchema, NULL_SCHEMA] };
691
729
  };
730
+ const schemaIncludesNullInVariants = (variants) => variants.some(schemaIncludesNull);
692
731
  const transformSchemasForVersion = (schemas, version) => {
693
732
  if (version === "3.0.3") {
694
733
  return schemas;
@@ -700,14 +739,81 @@ const transformSchemasForVersion = (schemas, version) => {
700
739
  ])
701
740
  );
702
741
  };
742
+ const transformOperationToV31 = (operation) => {
743
+ const parameters = operation.parameters?.map((param) => ({
744
+ ...param,
745
+ schema: transformSchemaToV31(param.schema)
746
+ }));
747
+ const requestBody = operation.requestBody ? {
748
+ ...operation.requestBody,
749
+ content: Object.fromEntries(
750
+ Object.entries(operation.requestBody.content).map(
751
+ ([contentType, mediaType]) => [
752
+ contentType,
753
+ { ...mediaType, schema: transformSchemaToV31(mediaType.schema) }
754
+ ]
755
+ )
756
+ )
757
+ } : void 0;
758
+ const responses = Object.fromEntries(
759
+ Object.entries(operation.responses).map(([code, response]) => [
760
+ code,
761
+ response.content ? {
762
+ ...response,
763
+ content: Object.fromEntries(
764
+ Object.entries(response.content).map(
765
+ ([contentType, mediaType]) => [
766
+ contentType,
767
+ {
768
+ ...mediaType,
769
+ schema: transformSchemaToV31(mediaType.schema)
770
+ }
771
+ ]
772
+ )
773
+ )
774
+ } : response
775
+ ])
776
+ );
777
+ return {
778
+ ...operation,
779
+ ...parameters && { parameters },
780
+ ...requestBody && { requestBody },
781
+ responses
782
+ };
783
+ };
784
+ const HTTP_METHODS = /* @__PURE__ */ new Set([
785
+ "get",
786
+ "put",
787
+ "post",
788
+ "delete",
789
+ "options",
790
+ "head",
791
+ "patch",
792
+ "trace"
793
+ ]);
794
+ const transformPathsForVersion = (paths, version) => {
795
+ if (version === "3.0.3") return paths;
796
+ return Object.fromEntries(
797
+ Object.entries(paths).map(([path, pathItem]) => [
798
+ path,
799
+ Object.fromEntries(
800
+ Object.entries(pathItem).map(
801
+ ([key, value]) => HTTP_METHODS.has(key) ? [key, transformOperationToV31(value)] : [key, value]
802
+ )
803
+ )
804
+ ])
805
+ );
806
+ };
703
807
  const transformSpecForVersion = (spec, version) => {
704
808
  if (version === "3.0.3") {
705
809
  return { ...spec, openapi: version };
706
810
  }
707
811
  const transformedSchemas = spec.components?.schemas ? transformSchemasForVersion(spec.components.schemas, version) : void 0;
812
+ const transformedPaths = transformPathsForVersion(spec.paths, version);
708
813
  return {
709
814
  ...spec,
710
815
  openapi: version,
816
+ paths: transformedPaths,
711
817
  ...transformedSchemas && {
712
818
  components: {
713
819
  ...spec.components,
@@ -886,22 +992,59 @@ const OpenApiTagConfig = Schema.Struct({
886
992
  name: Schema.String,
887
993
  description: Schema.optional(Schema.String)
888
994
  });
889
- const SecuritySchemeType = Schema.Literal(
995
+ Schema.Literal(
890
996
  "apiKey",
891
997
  "http",
892
998
  "oauth2",
893
999
  "openIdConnect"
894
1000
  );
895
1001
  const SecuritySchemeIn = Schema.Literal("query", "header", "cookie");
896
- const SecuritySchemeConfig = Schema.Struct({
1002
+ const OAuth2FlowConfig = Schema.Struct({
1003
+ authorizationUrl: Schema.optional(Schema.String),
1004
+ tokenUrl: Schema.optional(Schema.String),
1005
+ refreshUrl: Schema.optional(Schema.String),
1006
+ scopes: Schema.optional(
1007
+ Schema.Record({ key: Schema.String, value: Schema.String })
1008
+ )
1009
+ });
1010
+ const OAuth2FlowsConfig = Schema.Struct({
1011
+ implicit: Schema.optional(OAuth2FlowConfig),
1012
+ password: Schema.optional(OAuth2FlowConfig),
1013
+ clientCredentials: Schema.optional(OAuth2FlowConfig),
1014
+ authorizationCode: Schema.optional(OAuth2FlowConfig)
1015
+ });
1016
+ const HttpSecuritySchemeConfig = Schema.Struct({
897
1017
  name: Schema.String,
898
- type: SecuritySchemeType,
899
- scheme: Schema.optional(Schema.String),
1018
+ type: Schema.Literal("http"),
1019
+ scheme: Schema.String,
900
1020
  bearerFormat: Schema.optional(Schema.String),
901
- in: Schema.optional(SecuritySchemeIn),
902
- parameterName: Schema.optional(Schema.String),
903
1021
  description: Schema.optional(Schema.String)
904
1022
  });
1023
+ const ApiKeySecuritySchemeConfig = Schema.Struct({
1024
+ name: Schema.String,
1025
+ type: Schema.Literal("apiKey"),
1026
+ in: SecuritySchemeIn,
1027
+ parameterName: Schema.String,
1028
+ description: Schema.optional(Schema.String)
1029
+ });
1030
+ const OAuth2SecuritySchemeConfig = Schema.Struct({
1031
+ name: Schema.String,
1032
+ type: Schema.Literal("oauth2"),
1033
+ flows: OAuth2FlowsConfig,
1034
+ description: Schema.optional(Schema.String)
1035
+ });
1036
+ const OpenIdConnectSecuritySchemeConfig = Schema.Struct({
1037
+ name: Schema.String,
1038
+ type: Schema.Literal("openIdConnect"),
1039
+ openIdConnectUrl: Schema.String,
1040
+ description: Schema.optional(Schema.String)
1041
+ });
1042
+ const SecuritySchemeConfig = Schema.Union(
1043
+ HttpSecuritySchemeConfig,
1044
+ ApiKeySecuritySchemeConfig,
1045
+ OAuth2SecuritySchemeConfig,
1046
+ OpenIdConnectSecuritySchemeConfig
1047
+ );
905
1048
  const SecurityRequirement = Schema.Record({
906
1049
  key: Schema.String,
907
1050
  value: Schema.Array(Schema.String)
@@ -1008,7 +1151,7 @@ const CONFIG_FILE_NAMES = [
1008
1151
  "openapi.config.cjs"
1009
1152
  ];
1010
1153
  const DEFAULT_ENTRY$1 = "src/app.module.ts";
1011
- const DEFAULT_DTO_GLOB = [
1154
+ const DEFAULT_DTO_GLOB$1 = [
1012
1155
  "**/*.dto.ts",
1013
1156
  "**/*.entity.ts",
1014
1157
  "**/*.model.ts",
@@ -1035,8 +1178,7 @@ const DEFAULT_CONFIG = {
1035
1178
  };
1036
1179
  const findConfigFile = (startDir = process.cwd()) => Effect.gen(function* () {
1037
1180
  let currentDir = resolve(startDir);
1038
- const root = dirname(currentDir);
1039
- while (currentDir !== root) {
1181
+ while (true) {
1040
1182
  for (const fileName of CONFIG_FILE_NAMES) {
1041
1183
  const configPath = resolve(currentDir, fileName);
1042
1184
  if (existsSync(configPath)) {
@@ -1125,7 +1267,7 @@ const resolveConfig = (config) => {
1125
1267
  const rawEntry = files.entry ?? DEFAULT_ENTRY$1;
1126
1268
  const entry = Array.isArray(rawEntry) ? rawEntry : [rawEntry];
1127
1269
  const rawDtoGlob = files.dtoGlob;
1128
- const dtoGlob = rawDtoGlob ? Array.isArray(rawDtoGlob) ? rawDtoGlob : [rawDtoGlob] : [...DEFAULT_DTO_GLOB];
1270
+ const dtoGlob = rawDtoGlob ? Array.isArray(rawDtoGlob) ? rawDtoGlob : [rawDtoGlob] : [...DEFAULT_DTO_GLOB$1];
1129
1271
  const tsconfig = files.tsconfig;
1130
1272
  if (!tsconfig) {
1131
1273
  throw new Error("tsconfig is required in files configuration");
@@ -1404,6 +1546,22 @@ function resolveTypeLocationsFast(baseDir, missingTypes) {
1404
1546
  }
1405
1547
 
1406
1548
  const DEFAULT_ENTRY = "src/app.module.ts";
1549
+ const DEFAULT_DTO_GLOB = [
1550
+ "**/*.dto.ts",
1551
+ "**/*.entity.ts",
1552
+ "**/*.model.ts",
1553
+ "**/*.schema.ts"
1554
+ ];
1555
+ const mergeSingleSecurityRequirement = (left, right) => {
1556
+ const merged = {};
1557
+ for (const requirement of [left, right]) {
1558
+ for (const [scheme, scopes] of Object.entries(requirement)) {
1559
+ const existingScopes = merged[scheme] ?? [];
1560
+ merged[scheme] = [.../* @__PURE__ */ new Set([...existingScopes, ...scopes])];
1561
+ }
1562
+ }
1563
+ return merged;
1564
+ };
1407
1565
  const mergeSecurityWithGlobal = (paths, globalSecurity) => {
1408
1566
  if (!globalSecurity || globalSecurity.length === 0) {
1409
1567
  return paths;
@@ -1413,23 +1571,17 @@ const mergeSecurityWithGlobal = (paths, globalSecurity) => {
1413
1571
  const mergedMethods = {};
1414
1572
  for (const [method, operation] of Object.entries(methods)) {
1415
1573
  if (operation.security && operation.security.length > 0) {
1416
- const merged = {};
1574
+ const mergedSecurity = [];
1417
1575
  for (const globalReq of globalSecurity) {
1418
- for (const [scheme, scopes] of Object.entries(globalReq)) {
1419
- merged[scheme] = [...merged[scheme] ?? [], ...scopes];
1576
+ for (const operationReq of operation.security) {
1577
+ mergedSecurity.push(
1578
+ mergeSingleSecurityRequirement(globalReq, operationReq)
1579
+ );
1420
1580
  }
1421
1581
  }
1422
- for (const decoratorReq of operation.security) {
1423
- for (const [scheme, scopes] of Object.entries(decoratorReq)) {
1424
- merged[scheme] = [...merged[scheme] ?? [], ...scopes];
1425
- }
1426
- }
1427
- for (const scheme of Object.keys(merged)) {
1428
- merged[scheme] = [...new Set(merged[scheme])];
1429
- }
1430
1582
  mergedMethods[method] = {
1431
1583
  ...operation,
1432
- security: [merged]
1584
+ security: mergedSecurity
1433
1585
  };
1434
1586
  } else {
1435
1587
  mergedMethods[method] = operation;
@@ -1493,6 +1645,131 @@ const findMissingSchemaRefs = (paths, schemas) => {
1493
1645
  findRefs(paths);
1494
1646
  return missing;
1495
1647
  };
1648
+ const isGenericSchemaRef = (name) => name.includes("<") && name.endsWith(">");
1649
+ const NON_IMPORTABLE_TYPE_NAMES = /* @__PURE__ */ new Set([
1650
+ "string",
1651
+ "number",
1652
+ "boolean",
1653
+ "null",
1654
+ "undefined",
1655
+ "void",
1656
+ "unknown",
1657
+ "any",
1658
+ "never",
1659
+ "object",
1660
+ "true",
1661
+ "false",
1662
+ "Array",
1663
+ "ReadonlyArray",
1664
+ "Record",
1665
+ "Promise",
1666
+ "Partial",
1667
+ "Required",
1668
+ "Pick",
1669
+ "Omit",
1670
+ "Exclude",
1671
+ "Extract",
1672
+ "Readonly",
1673
+ "keyof",
1674
+ "infer",
1675
+ "extends"
1676
+ ]);
1677
+ const extractTypeIdentifiers = (typeRef) => {
1678
+ const withoutStringLiterals = typeRef.replace(
1679
+ /'[^']*'|"[^"]*"|`[^`]*`/g,
1680
+ ""
1681
+ );
1682
+ const matches = withoutStringLiterals.match(/\b[A-Za-z_$][A-Za-z0-9_$]*\b/g) ?? [];
1683
+ return new Set(
1684
+ matches.filter((name) => !NON_IMPORTABLE_TYPE_NAMES.has(name))
1685
+ );
1686
+ };
1687
+ const toModuleImportPath = (fromDir, filePath) => {
1688
+ const importPath = relative(fromDir, filePath).replace(/\\/g, "/");
1689
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
1690
+ };
1691
+ const resolveSymbolLocations = (tsconfig, symbolNames) => {
1692
+ if (symbolNames.size === 0) {
1693
+ return /* @__PURE__ */ new Map();
1694
+ }
1695
+ const tsconfigDir = dirname(tsconfig);
1696
+ const resolved = resolveTypeLocationsFast(tsconfigDir, symbolNames);
1697
+ const unresolved = new Set(
1698
+ [...symbolNames].filter((name) => !resolved.has(name))
1699
+ );
1700
+ if (unresolved.size > 0) {
1701
+ const project = createTypeResolverProject(tsconfig);
1702
+ const morphResolved = resolveTypeLocations(project, unresolved);
1703
+ for (const [name, filePath] of morphResolved) {
1704
+ resolved.set(name, filePath);
1705
+ }
1706
+ }
1707
+ return resolved;
1708
+ };
1709
+ const generateMissingGenericSchemas = async (genericRefs, tsconfig, symbolLocations, runEffect) => {
1710
+ if (genericRefs.length === 0) {
1711
+ return { definitions: {} };
1712
+ }
1713
+ const importGroups = /* @__PURE__ */ new Map();
1714
+ const aliases = [];
1715
+ const aliasLines = [];
1716
+ for (const [index, genericRef] of genericRefs.entries()) {
1717
+ const identifiers = [...extractTypeIdentifiers(genericRef)];
1718
+ if (identifiers.length === 0) {
1719
+ continue;
1720
+ }
1721
+ const unresolved = identifiers.filter((name) => !symbolLocations.has(name));
1722
+ if (unresolved.length > 0) {
1723
+ continue;
1724
+ }
1725
+ for (const identifier of identifiers) {
1726
+ const filePath = symbolLocations.get(identifier);
1727
+ if (!filePath) continue;
1728
+ const existing = importGroups.get(filePath) ?? /* @__PURE__ */ new Set();
1729
+ existing.add(identifier);
1730
+ importGroups.set(filePath, existing);
1731
+ }
1732
+ const aliasName = `__MissingGenericRef${index}`;
1733
+ aliases.push({ aliasName, schemaName: genericRef });
1734
+ aliasLines.push(`export type ${aliasName} = ${genericRef};`);
1735
+ }
1736
+ if (aliases.length === 0) {
1737
+ return { definitions: {} };
1738
+ }
1739
+ const tempDir = dirname(tsconfig);
1740
+ const tempFilePath = join(
1741
+ tempDir,
1742
+ `.openapi.missing-generic.${randomUUID()}.ts`
1743
+ );
1744
+ const importLines = [...importGroups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([filePath, symbols]) => {
1745
+ const importPath = toModuleImportPath(tempDir, filePath);
1746
+ const names = [...symbols].sort().join(", ");
1747
+ return `import type { ${names} } from '${importPath}';`;
1748
+ });
1749
+ writeFileSync(
1750
+ tempFilePath,
1751
+ [...importLines, "", ...aliasLines, ""].join("\n"),
1752
+ "utf-8"
1753
+ );
1754
+ try {
1755
+ const generated = await runEffect(
1756
+ generateSchemasFromFiles([tempFilePath], tsconfig)
1757
+ );
1758
+ const definitions = { ...generated.definitions };
1759
+ for (const { aliasName, schemaName } of aliases) {
1760
+ const resolvedSchema = definitions[schemaName] ?? definitions[aliasName] ?? void 0;
1761
+ if (resolvedSchema) {
1762
+ definitions[schemaName] = resolvedSchema;
1763
+ }
1764
+ delete definitions[aliasName];
1765
+ }
1766
+ return { definitions };
1767
+ } finally {
1768
+ if (existsSync(tempFilePath)) {
1769
+ unlinkSync(tempFilePath);
1770
+ }
1771
+ }
1772
+ };
1496
1773
  const extractValidationConstraints = async (dtoGlobPatterns, basePath, tsconfig, schemas) => {
1497
1774
  const absolutePatterns = dtoGlobPatterns.map(
1498
1775
  (pattern) => pattern.startsWith("/") ? pattern : join(basePath, pattern)
@@ -1539,8 +1816,7 @@ const extractValidationConstraints = async (dtoGlobPatterns, basePath, tsconfig,
1539
1816
  };
1540
1817
  const findTsConfig = (startDir) => {
1541
1818
  let currentDir = resolve(startDir);
1542
- const root = dirname(currentDir);
1543
- while (currentDir !== root) {
1819
+ while (true) {
1544
1820
  const tsconfigPath = join(currentDir, "tsconfig.json");
1545
1821
  if (existsSync(tsconfigPath)) {
1546
1822
  return tsconfigPath;
@@ -1635,19 +1911,30 @@ const generate = async (configPath, overrides) => {
1635
1911
  (e) => resolve(configDir, e)
1636
1912
  );
1637
1913
  const output = resolve(configDir, config.output);
1638
- const tsconfig = files.tsconfig ? resolve(configDir, files.tsconfig) : findTsConfig(dirname(entries[0]));
1639
- if (!tsconfig) {
1640
- throw new Error(
1641
- `Could not find tsconfig.json. Please specify files.tsconfig in your config file.`
1642
- );
1643
- }
1644
- if (!existsSync(tsconfig)) {
1645
- throw new Error(`tsconfig.json not found at: ${tsconfig}`);
1646
- }
1914
+ const tsconfig = await runEffect(
1915
+ Effect.gen(function* () {
1916
+ const discoveredTsconfig = files.tsconfig ? resolve(configDir, files.tsconfig) : findTsConfig(dirname(entries[0]));
1917
+ if (!discoveredTsconfig) {
1918
+ return yield* Effect.fail(
1919
+ ConfigValidationError.fromIssues(absoluteConfigPath, [
1920
+ "Could not find tsconfig.json. Please specify files.tsconfig in your config file."
1921
+ ])
1922
+ );
1923
+ }
1924
+ if (!existsSync(discoveredTsconfig)) {
1925
+ return yield* Effect.fail(
1926
+ ConfigValidationError.fromIssues(absoluteConfigPath, [
1927
+ `tsconfig.json not found at: ${discoveredTsconfig}`
1928
+ ])
1929
+ );
1930
+ }
1931
+ return discoveredTsconfig;
1932
+ }).pipe(Effect.mapError((error) => new Error(error.message)))
1933
+ );
1647
1934
  const extractOptions = {
1648
1935
  query: options.query
1649
1936
  };
1650
- const dtoGlobArray = files.dtoGlob ? Array.isArray(files.dtoGlob) ? files.dtoGlob : [files.dtoGlob] : null;
1937
+ const dtoGlobArray = files.dtoGlob === void 0 ? [...DEFAULT_DTO_GLOB] : Array.isArray(files.dtoGlob) ? files.dtoGlob : [files.dtoGlob];
1651
1938
  const [extractedMethodInfos, initialSchemas] = await Promise.all([
1652
1939
  runEffect(
1653
1940
  extractMethodInfosEffect(tsconfig, entries, extractOptions).pipe(
@@ -1659,7 +1946,7 @@ const generate = async (configPath, overrides) => {
1659
1946
  Effect.mapError((error) => new Error(error.message))
1660
1947
  )
1661
1948
  ),
1662
- dtoGlobArray ? runEffect(
1949
+ runEffect(
1663
1950
  generateSchemas({
1664
1951
  dtoGlob: dtoGlobArray,
1665
1952
  tsconfig,
@@ -1675,7 +1962,7 @@ const generate = async (configPath, overrides) => {
1675
1962
  ),
1676
1963
  Effect.mapError((error) => new Error(error.message))
1677
1964
  )
1678
- ) : Promise.resolve(null)
1965
+ )
1679
1966
  ]);
1680
1967
  const filteredMethodInfos = filterMethods(extractedMethodInfos, {
1681
1968
  excludeDecorators: options.excludeDecorators,
@@ -1696,7 +1983,7 @@ const generate = async (configPath, overrides) => {
1696
1983
  security.global
1697
1984
  );
1698
1985
  let schemas = {};
1699
- if (initialSchemas && dtoGlobArray) {
1986
+ if (initialSchemas) {
1700
1987
  let generatedSchemas = initialSchemas;
1701
1988
  const shouldExtractValidation = options.extractValidation !== false;
1702
1989
  if (shouldExtractValidation) {
@@ -1748,6 +2035,50 @@ const generate = async (configPath, overrides) => {
1748
2035
  ...normalizedAdditional.definitions
1749
2036
  }
1750
2037
  };
2038
+ generatedSchemas = combinedSchemas;
2039
+ mergeResult = mergeSchemas(
2040
+ paths,
2041
+ combinedSchemas
2042
+ );
2043
+ schemas = mergeResult.schemas;
2044
+ }
2045
+ }
2046
+ const unresolvedAfterFileResolution = findMissingSchemaRefs(
2047
+ paths,
2048
+ schemas
2049
+ );
2050
+ const unresolvedGenericRefs = [...unresolvedAfterFileResolution].filter(
2051
+ isGenericSchemaRef
2052
+ );
2053
+ if (unresolvedGenericRefs.length > 0) {
2054
+ const genericSymbols = /* @__PURE__ */ new Set();
2055
+ for (const ref of unresolvedGenericRefs) {
2056
+ for (const symbol of extractTypeIdentifiers(ref)) {
2057
+ genericSymbols.add(symbol);
2058
+ }
2059
+ }
2060
+ const resolvedGenericSymbols = resolveSymbolLocations(
2061
+ tsconfig,
2062
+ genericSymbols
2063
+ );
2064
+ for (const [name, filePath] of resolvedLocations) {
2065
+ resolvedGenericSymbols.set(name, filePath);
2066
+ }
2067
+ const genericSchemas = await generateMissingGenericSchemas(
2068
+ unresolvedGenericRefs,
2069
+ tsconfig,
2070
+ resolvedGenericSymbols,
2071
+ runEffect
2072
+ );
2073
+ if (Object.keys(genericSchemas.definitions).length > 0) {
2074
+ const normalizedGeneric = normalizeStructureRefs(genericSchemas);
2075
+ const combinedSchemas = {
2076
+ definitions: {
2077
+ ...generatedSchemas.definitions,
2078
+ ...normalizedGeneric.definitions
2079
+ }
2080
+ };
2081
+ generatedSchemas = combinedSchemas;
1751
2082
  mergeResult = mergeSchemas(
1752
2083
  paths,
1753
2084
  combinedSchemas
@@ -703,6 +703,22 @@ const parseTypeText = (text) => {
703
703
  const trimmed = text.trim();
704
704
  return trimmed.startsWith("{") && trimmed.endsWith("}") ? { type: Option.none(), inline: Option.some(trimmed) } : { type: Option.some(trimmed), inline: Option.none() };
705
705
  };
706
+ const getGenericBaseType = (text) => {
707
+ const genericStart = text.indexOf("<");
708
+ return genericStart === -1 ? null : text.slice(0, genericStart).trim();
709
+ };
710
+ const hasAliasedImportCollision = (method, exportedName) => {
711
+ const localNames = /* @__PURE__ */ new Set();
712
+ for (const importDecl of method.getSourceFile().getImportDeclarations()) {
713
+ for (const namedImport of importDecl.getNamedImports()) {
714
+ const aliasNode = namedImport.getAliasNode();
715
+ if (!aliasNode) continue;
716
+ if (namedImport.getName() !== exportedName) continue;
717
+ localNames.add(aliasNode.getText());
718
+ }
719
+ }
720
+ return localNames.size > 1;
721
+ };
706
722
  const getReturnTypeInfo = (method) => {
707
723
  const returnType = method.getReturnType();
708
724
  const awaited = returnType.getAwaitedType?.() ?? returnType;
@@ -717,6 +733,25 @@ const getReturnTypeInfo = (method) => {
717
733
  return null;
718
734
  };
719
735
  let text = getOriginalTypeName() ?? awaited.getText(method);
736
+ const compilerType = awaited.compilerType;
737
+ const aliasName = compilerType.aliasSymbol?.escapedName?.toString();
738
+ if (aliasName && !aliasName.startsWith("__")) {
739
+ const genericBase = getGenericBaseType(text);
740
+ const shouldPreserveLocalAlias = genericBase !== null && genericBase !== aliasName && hasAliasedImportCollision(method, aliasName);
741
+ if (!shouldPreserveLocalAlias) {
742
+ text = genericBase === null ? aliasName : `${aliasName}${text.slice(text.indexOf("<"))}`;
743
+ }
744
+ }
745
+ const symbolName = symbol?.getName();
746
+ if (symbolName && !symbolName.startsWith("__")) {
747
+ const genericBase = getGenericBaseType(text);
748
+ if (genericBase !== null) {
749
+ const shouldPreserveLocalAlias = genericBase !== symbolName && hasAliasedImportCollision(method, symbolName);
750
+ if (!shouldPreserveLocalAlias) {
751
+ text = `${symbolName}${text.slice(text.indexOf("<"))}`;
752
+ }
753
+ }
754
+ }
720
755
  const promiseMatch = text.match(/^Promise<(.+)>$/);
721
756
  if (promiseMatch) text = promiseMatch[1].trim();
722
757
  text = text.replace(/\bimport\([^)]*\)\./g, "");
@@ -1306,10 +1341,16 @@ const tsTypeToOpenApiSchema = (tsType) => {
1306
1341
  }
1307
1342
  }
1308
1343
  if (trimmed.includes(" | ")) {
1309
- const types = trimmed.split(" | ").map((t) => t.trim());
1310
- return {
1311
- oneOf: types.map((type) => tsTypeToOpenApiSchema(type))
1312
- };
1344
+ const allMembers = trimmed.split(" | ").map((t) => t.trim());
1345
+ const hasNull = allMembers.includes("null");
1346
+ const types = allMembers.filter((t) => t !== "undefined" && t !== "null");
1347
+ if (types.length === 0) return { type: "object" };
1348
+ const schema = types.length === 1 ? tsTypeToOpenApiSchema(types[0]) : { oneOf: types.map((type) => tsTypeToOpenApiSchema(type)) };
1349
+ if (!hasNull) return schema;
1350
+ if (schema.$ref) {
1351
+ return { allOf: [{ $ref: schema.$ref }], nullable: true };
1352
+ }
1353
+ return { ...schema, nullable: true };
1313
1354
  }
1314
1355
  switch (trimmed.toLowerCase()) {
1315
1356
  case "string":
@@ -173,9 +173,12 @@ interface OpenApiSchema {
173
173
  readonly format?: string;
174
174
  readonly $ref?: string;
175
175
  readonly oneOf?: readonly OpenApiSchema[];
176
+ readonly allOf?: readonly OpenApiSchema[];
176
177
  readonly items?: OpenApiSchema;
177
178
  readonly properties?: Record<string, OpenApiSchema>;
178
179
  readonly required?: readonly string[];
180
+ /** OpenAPI 3.0 nullable flag */
181
+ readonly nullable?: boolean;
179
182
  }
180
183
  declare const OpenApiOperation: Schema.Struct<{
181
184
  operationId: typeof Schema.String;
@@ -249,15 +252,54 @@ declare const OpenApiGeneratorConfig: Schema.Struct<{
249
252
  description: Schema.optional<typeof Schema.String>;
250
253
  }>>>;
251
254
  security: Schema.optional<Schema.Struct<{
252
- schemes: Schema.optional<Schema.Array$<Schema.Struct<{
255
+ schemes: Schema.optional<Schema.Array$<Schema.Union<[Schema.Struct<{
253
256
  name: typeof Schema.String;
254
- type: Schema.Literal<["apiKey", "http", "oauth2", "openIdConnect"]>;
255
- scheme: Schema.optional<typeof Schema.String>;
257
+ type: Schema.Literal<["http"]>;
258
+ scheme: typeof Schema.String;
256
259
  bearerFormat: Schema.optional<typeof Schema.String>;
257
- in: Schema.optional<Schema.Literal<["query", "header", "cookie"]>>;
258
- parameterName: Schema.optional<typeof Schema.String>;
259
260
  description: Schema.optional<typeof Schema.String>;
260
- }>>>;
261
+ }>, Schema.Struct<{
262
+ name: typeof Schema.String;
263
+ type: Schema.Literal<["apiKey"]>;
264
+ in: Schema.Literal<["query", "header", "cookie"]>;
265
+ parameterName: typeof Schema.String;
266
+ description: Schema.optional<typeof Schema.String>;
267
+ }>, Schema.Struct<{
268
+ name: typeof Schema.String;
269
+ type: Schema.Literal<["oauth2"]>;
270
+ flows: Schema.Struct<{
271
+ implicit: Schema.optional<Schema.Struct<{
272
+ authorizationUrl: Schema.optional<typeof Schema.String>;
273
+ tokenUrl: Schema.optional<typeof Schema.String>;
274
+ refreshUrl: Schema.optional<typeof Schema.String>;
275
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
276
+ }>>;
277
+ password: Schema.optional<Schema.Struct<{
278
+ authorizationUrl: Schema.optional<typeof Schema.String>;
279
+ tokenUrl: Schema.optional<typeof Schema.String>;
280
+ refreshUrl: Schema.optional<typeof Schema.String>;
281
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
282
+ }>>;
283
+ clientCredentials: Schema.optional<Schema.Struct<{
284
+ authorizationUrl: Schema.optional<typeof Schema.String>;
285
+ tokenUrl: Schema.optional<typeof Schema.String>;
286
+ refreshUrl: Schema.optional<typeof Schema.String>;
287
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
288
+ }>>;
289
+ authorizationCode: Schema.optional<Schema.Struct<{
290
+ authorizationUrl: Schema.optional<typeof Schema.String>;
291
+ tokenUrl: Schema.optional<typeof Schema.String>;
292
+ refreshUrl: Schema.optional<typeof Schema.String>;
293
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
294
+ }>>;
295
+ }>;
296
+ description: Schema.optional<typeof Schema.String>;
297
+ }>, Schema.Struct<{
298
+ name: typeof Schema.String;
299
+ type: Schema.Literal<["openIdConnect"]>;
300
+ openIdConnectUrl: typeof Schema.String;
301
+ description: Schema.optional<typeof Schema.String>;
302
+ }>]>>>;
261
303
  global: Schema.optional<Schema.Array$<Schema.Record$<typeof Schema.String, Schema.Array$<typeof Schema.String>>>>;
262
304
  }>>;
263
305
  }>;
@@ -301,15 +343,54 @@ declare const ResolvedConfig: Schema.Struct<{
301
343
  url: typeof Schema.String;
302
344
  description: Schema.optional<typeof Schema.String>;
303
345
  }>>;
304
- securitySchemes: Schema.Array$<Schema.Struct<{
346
+ securitySchemes: Schema.Array$<Schema.Union<[Schema.Struct<{
305
347
  name: typeof Schema.String;
306
- type: Schema.Literal<["apiKey", "http", "oauth2", "openIdConnect"]>;
307
- scheme: Schema.optional<typeof Schema.String>;
348
+ type: Schema.Literal<["http"]>;
349
+ scheme: typeof Schema.String;
308
350
  bearerFormat: Schema.optional<typeof Schema.String>;
309
- in: Schema.optional<Schema.Literal<["query", "header", "cookie"]>>;
310
- parameterName: Schema.optional<typeof Schema.String>;
311
351
  description: Schema.optional<typeof Schema.String>;
312
- }>>;
352
+ }>, Schema.Struct<{
353
+ name: typeof Schema.String;
354
+ type: Schema.Literal<["apiKey"]>;
355
+ in: Schema.Literal<["query", "header", "cookie"]>;
356
+ parameterName: typeof Schema.String;
357
+ description: Schema.optional<typeof Schema.String>;
358
+ }>, Schema.Struct<{
359
+ name: typeof Schema.String;
360
+ type: Schema.Literal<["oauth2"]>;
361
+ flows: Schema.Struct<{
362
+ implicit: Schema.optional<Schema.Struct<{
363
+ authorizationUrl: Schema.optional<typeof Schema.String>;
364
+ tokenUrl: Schema.optional<typeof Schema.String>;
365
+ refreshUrl: Schema.optional<typeof Schema.String>;
366
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
367
+ }>>;
368
+ password: Schema.optional<Schema.Struct<{
369
+ authorizationUrl: Schema.optional<typeof Schema.String>;
370
+ tokenUrl: Schema.optional<typeof Schema.String>;
371
+ refreshUrl: Schema.optional<typeof Schema.String>;
372
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
373
+ }>>;
374
+ clientCredentials: Schema.optional<Schema.Struct<{
375
+ authorizationUrl: Schema.optional<typeof Schema.String>;
376
+ tokenUrl: Schema.optional<typeof Schema.String>;
377
+ refreshUrl: Schema.optional<typeof Schema.String>;
378
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
379
+ }>>;
380
+ authorizationCode: Schema.optional<Schema.Struct<{
381
+ authorizationUrl: Schema.optional<typeof Schema.String>;
382
+ tokenUrl: Schema.optional<typeof Schema.String>;
383
+ refreshUrl: Schema.optional<typeof Schema.String>;
384
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
385
+ }>>;
386
+ }>;
387
+ description: Schema.optional<typeof Schema.String>;
388
+ }>, Schema.Struct<{
389
+ name: typeof Schema.String;
390
+ type: Schema.Literal<["openIdConnect"]>;
391
+ openIdConnectUrl: typeof Schema.String;
392
+ description: Schema.optional<typeof Schema.String>;
393
+ }>]>>;
313
394
  securityRequirements: Schema.Array$<Schema.Record$<typeof Schema.String, Schema.Array$<typeof Schema.String>>>;
314
395
  tags: Schema.Array$<Schema.Struct<{
315
396
  name: typeof Schema.String;
@@ -173,9 +173,12 @@ interface OpenApiSchema {
173
173
  readonly format?: string;
174
174
  readonly $ref?: string;
175
175
  readonly oneOf?: readonly OpenApiSchema[];
176
+ readonly allOf?: readonly OpenApiSchema[];
176
177
  readonly items?: OpenApiSchema;
177
178
  readonly properties?: Record<string, OpenApiSchema>;
178
179
  readonly required?: readonly string[];
180
+ /** OpenAPI 3.0 nullable flag */
181
+ readonly nullable?: boolean;
179
182
  }
180
183
  declare const OpenApiOperation: Schema.Struct<{
181
184
  operationId: typeof Schema.String;
@@ -249,15 +252,54 @@ declare const OpenApiGeneratorConfig: Schema.Struct<{
249
252
  description: Schema.optional<typeof Schema.String>;
250
253
  }>>>;
251
254
  security: Schema.optional<Schema.Struct<{
252
- schemes: Schema.optional<Schema.Array$<Schema.Struct<{
255
+ schemes: Schema.optional<Schema.Array$<Schema.Union<[Schema.Struct<{
253
256
  name: typeof Schema.String;
254
- type: Schema.Literal<["apiKey", "http", "oauth2", "openIdConnect"]>;
255
- scheme: Schema.optional<typeof Schema.String>;
257
+ type: Schema.Literal<["http"]>;
258
+ scheme: typeof Schema.String;
256
259
  bearerFormat: Schema.optional<typeof Schema.String>;
257
- in: Schema.optional<Schema.Literal<["query", "header", "cookie"]>>;
258
- parameterName: Schema.optional<typeof Schema.String>;
259
260
  description: Schema.optional<typeof Schema.String>;
260
- }>>>;
261
+ }>, Schema.Struct<{
262
+ name: typeof Schema.String;
263
+ type: Schema.Literal<["apiKey"]>;
264
+ in: Schema.Literal<["query", "header", "cookie"]>;
265
+ parameterName: typeof Schema.String;
266
+ description: Schema.optional<typeof Schema.String>;
267
+ }>, Schema.Struct<{
268
+ name: typeof Schema.String;
269
+ type: Schema.Literal<["oauth2"]>;
270
+ flows: Schema.Struct<{
271
+ implicit: Schema.optional<Schema.Struct<{
272
+ authorizationUrl: Schema.optional<typeof Schema.String>;
273
+ tokenUrl: Schema.optional<typeof Schema.String>;
274
+ refreshUrl: Schema.optional<typeof Schema.String>;
275
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
276
+ }>>;
277
+ password: Schema.optional<Schema.Struct<{
278
+ authorizationUrl: Schema.optional<typeof Schema.String>;
279
+ tokenUrl: Schema.optional<typeof Schema.String>;
280
+ refreshUrl: Schema.optional<typeof Schema.String>;
281
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
282
+ }>>;
283
+ clientCredentials: Schema.optional<Schema.Struct<{
284
+ authorizationUrl: Schema.optional<typeof Schema.String>;
285
+ tokenUrl: Schema.optional<typeof Schema.String>;
286
+ refreshUrl: Schema.optional<typeof Schema.String>;
287
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
288
+ }>>;
289
+ authorizationCode: Schema.optional<Schema.Struct<{
290
+ authorizationUrl: Schema.optional<typeof Schema.String>;
291
+ tokenUrl: Schema.optional<typeof Schema.String>;
292
+ refreshUrl: Schema.optional<typeof Schema.String>;
293
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
294
+ }>>;
295
+ }>;
296
+ description: Schema.optional<typeof Schema.String>;
297
+ }>, Schema.Struct<{
298
+ name: typeof Schema.String;
299
+ type: Schema.Literal<["openIdConnect"]>;
300
+ openIdConnectUrl: typeof Schema.String;
301
+ description: Schema.optional<typeof Schema.String>;
302
+ }>]>>>;
261
303
  global: Schema.optional<Schema.Array$<Schema.Record$<typeof Schema.String, Schema.Array$<typeof Schema.String>>>>;
262
304
  }>>;
263
305
  }>;
@@ -301,15 +343,54 @@ declare const ResolvedConfig: Schema.Struct<{
301
343
  url: typeof Schema.String;
302
344
  description: Schema.optional<typeof Schema.String>;
303
345
  }>>;
304
- securitySchemes: Schema.Array$<Schema.Struct<{
346
+ securitySchemes: Schema.Array$<Schema.Union<[Schema.Struct<{
305
347
  name: typeof Schema.String;
306
- type: Schema.Literal<["apiKey", "http", "oauth2", "openIdConnect"]>;
307
- scheme: Schema.optional<typeof Schema.String>;
348
+ type: Schema.Literal<["http"]>;
349
+ scheme: typeof Schema.String;
308
350
  bearerFormat: Schema.optional<typeof Schema.String>;
309
- in: Schema.optional<Schema.Literal<["query", "header", "cookie"]>>;
310
- parameterName: Schema.optional<typeof Schema.String>;
311
351
  description: Schema.optional<typeof Schema.String>;
312
- }>>;
352
+ }>, Schema.Struct<{
353
+ name: typeof Schema.String;
354
+ type: Schema.Literal<["apiKey"]>;
355
+ in: Schema.Literal<["query", "header", "cookie"]>;
356
+ parameterName: typeof Schema.String;
357
+ description: Schema.optional<typeof Schema.String>;
358
+ }>, Schema.Struct<{
359
+ name: typeof Schema.String;
360
+ type: Schema.Literal<["oauth2"]>;
361
+ flows: Schema.Struct<{
362
+ implicit: Schema.optional<Schema.Struct<{
363
+ authorizationUrl: Schema.optional<typeof Schema.String>;
364
+ tokenUrl: Schema.optional<typeof Schema.String>;
365
+ refreshUrl: Schema.optional<typeof Schema.String>;
366
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
367
+ }>>;
368
+ password: Schema.optional<Schema.Struct<{
369
+ authorizationUrl: Schema.optional<typeof Schema.String>;
370
+ tokenUrl: Schema.optional<typeof Schema.String>;
371
+ refreshUrl: Schema.optional<typeof Schema.String>;
372
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
373
+ }>>;
374
+ clientCredentials: Schema.optional<Schema.Struct<{
375
+ authorizationUrl: Schema.optional<typeof Schema.String>;
376
+ tokenUrl: Schema.optional<typeof Schema.String>;
377
+ refreshUrl: Schema.optional<typeof Schema.String>;
378
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
379
+ }>>;
380
+ authorizationCode: Schema.optional<Schema.Struct<{
381
+ authorizationUrl: Schema.optional<typeof Schema.String>;
382
+ tokenUrl: Schema.optional<typeof Schema.String>;
383
+ refreshUrl: Schema.optional<typeof Schema.String>;
384
+ scopes: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.String>>;
385
+ }>>;
386
+ }>;
387
+ description: Schema.optional<typeof Schema.String>;
388
+ }>, Schema.Struct<{
389
+ name: typeof Schema.String;
390
+ type: Schema.Literal<["openIdConnect"]>;
391
+ openIdConnectUrl: typeof Schema.String;
392
+ description: Schema.optional<typeof Schema.String>;
393
+ }>]>>;
313
394
  securityRequirements: Schema.Array$<Schema.Record$<typeof Schema.String, Schema.Array$<typeof Schema.String>>>;
314
395
  tags: Schema.Array$<Schema.Struct<{
315
396
  name: typeof Schema.String;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-openapi",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Static code analysis tool to generate OpenAPI specifications from NestJS applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",