nestjs-openapi 0.3.1 → 0.4.0

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.
@@ -850,88 +850,89 @@ const resolveEnumMemberValue = (initializerText, nextValue) => {
850
850
  const numericValue = parseNumber(initializerText);
851
851
  return numericValue === void 0 ? { value: initializerText, nextValue } : { value: numericValue, nextValue: numericValue + 1 };
852
852
  };
853
- const extractEnumValues = (enumDecl) => enumDecl.getMembers().reduce(
854
- (state, member) => {
855
- const initializer = member.getInitializer();
856
- if (!initializer) {
857
- return {
858
- values: [...state.values, state.nextValue],
859
- nextValue: state.nextValue + 1
860
- };
861
- }
862
- const stringLiteral = initializer.asKind?.(ts.SyntaxKind.StringLiteral);
863
- if (stringLiteral) {
864
- return {
865
- values: [...state.values, stringLiteral.getLiteralValue()],
866
- nextValue: state.nextValue
867
- };
868
- }
869
- const numericLiteral = initializer.asKind?.(ts.SyntaxKind.NumericLiteral);
870
- if (numericLiteral) {
871
- const numericValue = numericLiteral.getLiteralValue();
872
- return {
873
- values: [...state.values, numericValue],
874
- nextValue: numericValue + 1
875
- };
876
- }
877
- const resolved = resolveEnumMemberValue(
878
- initializer.getText(),
879
- state.nextValue
880
- );
881
- return {
882
- values: [...state.values, resolved.value],
883
- nextValue: resolved.nextValue
884
- };
885
- },
886
- {
887
- values: [],
888
- nextValue: 0
889
- }
890
- ).values;
891
- const resolveEnumFromIdentifier = (identifier) => {
892
- const symbol = identifier.getSymbol();
893
- if (!symbol) return void 0;
894
- const declarations = symbol.getDeclarations();
895
- if (!declarations || declarations.length === 0) return void 0;
896
- const directEnumDecl = declarations.find(
897
- (decl) => Node.isEnumDeclaration(decl)
898
- );
899
- if (directEnumDecl) {
900
- return extractEnumValues(directEnumDecl);
853
+ const appendEnumValue = (state, value, nextValue) => ({
854
+ values: [...state.values, value],
855
+ nextValue
856
+ });
857
+ const resolveEnumInitializerValue = (initializer, nextValue) => {
858
+ const stringLiteral = initializer.asKind?.(ts.SyntaxKind.StringLiteral);
859
+ if (stringLiteral) {
860
+ return { value: stringLiteral.getLiteralValue(), nextValue };
901
861
  }
902
- const importedEnumDecl = declarations.filter(Node.isImportSpecifier).flatMap(
903
- (decl) => decl.getSymbol()?.getAliasedSymbol()?.getDeclarations() ?? []
904
- ).find((decl) => Node.isEnumDeclaration(decl));
905
- return importedEnumDecl ? extractEnumValues(importedEnumDecl) : void 0;
862
+ const numericLiteral = initializer.asKind?.(ts.SyntaxKind.NumericLiteral);
863
+ if (numericLiteral) {
864
+ const value = numericLiteral.getLiteralValue();
865
+ return { value, nextValue: value + 1 };
866
+ }
867
+ return resolveEnumMemberValue(initializer.getText(), nextValue);
906
868
  };
869
+ const resolveEnumMember = (state, member) => Option.fromNullable(member.getInitializer()).pipe(
870
+ Option.match({
871
+ onNone: () => appendEnumValue(state, state.nextValue, state.nextValue + 1),
872
+ onSome: (initializer) => {
873
+ const resolved = resolveEnumInitializerValue(
874
+ initializer,
875
+ state.nextValue
876
+ );
877
+ return appendEnumValue(state, resolved.value, resolved.nextValue);
878
+ }
879
+ })
880
+ );
881
+ const extractEnumValues = (enumDecl) => enumDecl.getMembers().reduce(resolveEnumMember, {
882
+ values: [],
883
+ nextValue: 0
884
+ }).values;
885
+ const findDirectEnumDeclaration = (declarations) => declarations.find(
886
+ (decl) => Node.isEnumDeclaration(decl)
887
+ );
888
+ const findImportedEnumSpecifierDeclaration = (declarations) => declarations.filter(Node.isImportSpecifier).flatMap(
889
+ (decl) => decl.getSymbol()?.getAliasedSymbol()?.getDeclarations() ?? []
890
+ ).find((decl) => Node.isEnumDeclaration(decl));
891
+ const findEnumDeclaration = (declarations) => findDirectEnumDeclaration(declarations) ?? findImportedEnumSpecifierDeclaration(declarations);
892
+ const resolveEnumFromIdentifier = (identifier) => Option.fromNullable(identifier.getSymbol()).pipe(
893
+ Option.map((symbol) => symbol.getDeclarations()),
894
+ Option.filter((declarations) => declarations.length > 0),
895
+ Option.flatMap(
896
+ (declarations) => Option.fromNullable(findEnumDeclaration(declarations))
897
+ ),
898
+ Option.map(extractEnumValues),
899
+ Option.getOrUndefined
900
+ );
907
901
  const resolveEnumFromDecorator = (decorator) => {
908
902
  const firstArg = decorator.getCallExpression()?.getArguments()?.[0];
909
903
  return firstArg && Node.isIdentifier(firstArg) ? resolveEnumFromIdentifier(firstArg) : void 0;
910
904
  };
911
905
  const getPropertyInitializer = (objLit, name) => objLit.getProperty(name)?.asKind?.(ts.SyntaxKind.PropertyAssignment)?.getInitializer();
912
906
  const getStringProp = (objLit, name) => getPropertyInitializer(objLit, name)?.asKind?.(ts.SyntaxKind.StringLiteral)?.getLiteralValue();
907
+ const readNumericInitializer = (initializer) => {
908
+ const numericLiteral = initializer.asKind?.(ts.SyntaxKind.NumericLiteral);
909
+ if (numericLiteral) {
910
+ return numericLiteral.getLiteralValue();
911
+ }
912
+ return Node.isPrefixUnaryExpression(initializer) ? parseNumber(initializer.getText()) : void 0;
913
+ };
913
914
  const getNumericProp = (objLit, name) => {
914
915
  const initializer = getPropertyInitializer(objLit, name);
915
- const numericLiteral = initializer?.asKind?.(ts.SyntaxKind.NumericLiteral);
916
- if (numericLiteral) return numericLiteral.getLiteralValue();
917
- return initializer && Node.isPrefixUnaryExpression(initializer) ? parseNumber(initializer.getText()) : void 0;
916
+ return initializer ? readNumericInitializer(initializer) : void 0;
918
917
  };
919
918
  const getBooleanProp = (objLit, name) => {
920
919
  const text = getPropertyInitializer(objLit, name)?.getText();
921
920
  return text === "true" ? true : text === "false" ? false : void 0;
922
921
  };
923
- const getPrimitiveValue = (objLit, name) => {
924
- const initializer = getPropertyInitializer(objLit, name);
925
- if (!initializer) return void 0;
922
+ const PRIMITIVE_LITERAL_VALUES = {
923
+ true: true,
924
+ false: false,
925
+ null: null
926
+ };
927
+ const readPrimitiveInitializer = (initializer) => {
926
928
  if (Node.isStringLiteral(initializer) || Node.isNumericLiteral(initializer)) {
927
929
  return initializer.getLiteralValue();
928
930
  }
929
- const primitiveLiterals = {
930
- true: true,
931
- false: false,
932
- null: null
933
- };
934
- return primitiveLiterals[initializer.getText()];
931
+ return PRIMITIVE_LITERAL_VALUES[initializer.getText()];
932
+ };
933
+ const getPrimitiveValue = (objLit, name) => {
934
+ const initializer = getPropertyInitializer(objLit, name);
935
+ return initializer ? readPrimitiveInitializer(initializer) : void 0;
935
936
  };
936
937
  const API_STRING_KEYS = ["description", "title", "format", "pattern"];
937
938
  const API_NUMBER_KEYS = [
@@ -954,65 +955,94 @@ const API_BOOLEAN_KEYS = [
954
955
  "isArray"
955
956
  ];
956
957
  const API_PRIMITIVE_KEYS = ["example", "default"];
958
+ const API_TYPE_IDENTIFIERS = {
959
+ String: { type: "string" },
960
+ Number: { type: "number" },
961
+ Boolean: { type: "boolean" },
962
+ Object: { type: "object" },
963
+ Array: { type: "array" },
964
+ Date: { type: "string", format: "date-time" }
965
+ };
957
966
  const buildConstraintsFromKeys = (objectLiteral, keys, read) => Object.fromEntries(
958
967
  keys.flatMap((key) => {
959
968
  const value = read(objectLiteral, key);
960
969
  return value === void 0 ? [] : [[key, value]];
961
970
  })
962
971
  );
963
- const extractApiPropertyEnum = (objLit) => {
964
- const initializer = getPropertyInitializer(objLit, "enum");
965
- if (!initializer) return void 0;
972
+ const readEnumArrayElement = (element) => {
973
+ if (Node.isStringLiteral(element) || Node.isNumericLiteral(element)) {
974
+ return element.getLiteralValue();
975
+ }
976
+ return Node.isPrefixUnaryExpression(element) ? parseNumber(element.getText()) : void 0;
977
+ };
978
+ const readEnumArrayLiteral = (initializer) => {
966
979
  const arrayLiteral = initializer.asKind?.(
967
980
  ts.SyntaxKind.ArrayLiteralExpression
968
981
  );
969
- if (arrayLiteral) {
970
- const values = arrayLiteral.getElements().reduce((acc, element) => {
971
- if (Node.isStringLiteral(element)) {
972
- return [...acc, element.getLiteralValue()];
973
- }
974
- if (Node.isNumericLiteral(element)) {
975
- return [...acc, element.getLiteralValue()];
976
- }
977
- if (Node.isPrefixUnaryExpression(element)) {
978
- const numericValue = parseNumber(element.getText());
979
- return numericValue === void 0 ? acc : [...acc, numericValue];
980
- }
981
- return acc;
982
- }, []);
983
- return values.length > 0 ? values : void 0;
982
+ if (!arrayLiteral) {
983
+ return void 0;
984
984
  }
985
- return Node.isIdentifier(initializer) ? resolveEnumFromIdentifier(initializer) : void 0;
985
+ const values = arrayLiteral.getElements().map(readEnumArrayElement).filter((value) => value !== void 0);
986
+ return values.length > 0 ? values : void 0;
986
987
  };
987
- const extractApiPropertyConstraints = (decorator) => {
988
- const objectLiteral = decorator.getCallExpression()?.getArguments()[0]?.asKind?.(ts.SyntaxKind.ObjectLiteralExpression);
989
- if (!objectLiteral) return void 0;
990
- const enumValues = extractApiPropertyEnum(objectLiteral);
991
- const result = {
992
- ...enumValues ? { enum: enumValues } : {},
993
- ...buildConstraintsFromKeys(
994
- objectLiteral,
995
- API_STRING_KEYS,
996
- (obj, key) => getStringProp(obj, key)
997
- ),
998
- ...buildConstraintsFromKeys(
999
- objectLiteral,
1000
- API_NUMBER_KEYS,
1001
- (obj, key) => getNumericProp(obj, key)
1002
- ),
1003
- ...buildConstraintsFromKeys(
1004
- objectLiteral,
1005
- API_BOOLEAN_KEYS,
1006
- (obj, key) => getBooleanProp(obj, key)
1007
- ),
1008
- ...buildConstraintsFromKeys(
1009
- objectLiteral,
1010
- API_PRIMITIVE_KEYS,
1011
- (obj, key) => getPrimitiveValue(obj, key)
1012
- )
1013
- };
1014
- return Object.keys(result).length > 0 ? result : void 0;
988
+ const readEnumIdentifier = (initializer) => Node.isIdentifier(initializer) ? resolveEnumFromIdentifier(initializer) : void 0;
989
+ const extractApiPropertyEnum = (objLit) => {
990
+ const initializer = getPropertyInitializer(objLit, "enum");
991
+ if (!initializer) {
992
+ return void 0;
993
+ }
994
+ return readEnumArrayLiteral(initializer) ?? readEnumIdentifier(initializer);
1015
995
  };
996
+ const readApiTypeIdentifier = (initializer) => Node.isIdentifier(initializer) ? API_TYPE_IDENTIFIERS[initializer.getText()] : void 0;
997
+ const readApiTypeArrayElement = (initializer) => {
998
+ const firstArrayElement = initializer.asKind?.(ts.SyntaxKind.ArrayLiteralExpression)?.getElements()[0];
999
+ if (!firstArrayElement || !Node.isIdentifier(firstArrayElement)) {
1000
+ return void 0;
1001
+ }
1002
+ const itemType = API_TYPE_IDENTIFIERS[firstArrayElement.getText()];
1003
+ return itemType ? { ...itemType, isArray: true } : void 0;
1004
+ };
1005
+ const extractApiPropertyType = (objLit) => {
1006
+ const initializer = getPropertyInitializer(objLit, "type");
1007
+ if (!initializer) {
1008
+ return void 0;
1009
+ }
1010
+ return readApiTypeIdentifier(initializer) ?? readApiTypeArrayElement(initializer);
1011
+ };
1012
+ const extractApiPropertyConstraints = (decorator) => Option.fromNullable(
1013
+ decorator.getCallExpression()?.getArguments()[0]?.asKind?.(ts.SyntaxKind.ObjectLiteralExpression)
1014
+ ).pipe(
1015
+ Option.map((objectLiteral) => {
1016
+ const enumValues = extractApiPropertyEnum(objectLiteral);
1017
+ const typeConstraints = extractApiPropertyType(objectLiteral);
1018
+ return {
1019
+ ...enumValues ? { enum: enumValues } : {},
1020
+ ...typeConstraints ?? {},
1021
+ ...buildConstraintsFromKeys(
1022
+ objectLiteral,
1023
+ API_STRING_KEYS,
1024
+ (obj, key) => getStringProp(obj, key)
1025
+ ),
1026
+ ...buildConstraintsFromKeys(
1027
+ objectLiteral,
1028
+ API_NUMBER_KEYS,
1029
+ (obj, key) => getNumericProp(obj, key)
1030
+ ),
1031
+ ...buildConstraintsFromKeys(
1032
+ objectLiteral,
1033
+ API_BOOLEAN_KEYS,
1034
+ (obj, key) => getBooleanProp(obj, key)
1035
+ ),
1036
+ ...buildConstraintsFromKeys(
1037
+ objectLiteral,
1038
+ API_PRIMITIVE_KEYS,
1039
+ (obj, key) => getPrimitiveValue(obj, key)
1040
+ )
1041
+ };
1042
+ }),
1043
+ Option.filter((result) => Object.keys(result).length > 0),
1044
+ Option.getOrUndefined
1045
+ );
1016
1046
  const compactConstraints = (constraints) => Object.fromEntries(
1017
1047
  Object.entries(constraints).filter(([, value]) => value !== void 0)
1018
1048
  );
@@ -1099,21 +1129,93 @@ const collectHiddenProperties = (propertyConstraints) => new Set(
1099
1129
  ([propertyName, constraints]) => constraints.hidden ? [propertyName] : []
1100
1130
  )
1101
1131
  );
1132
+ const cleanPropertyConstraints = (constraints) => {
1133
+ const {
1134
+ hidden: _hidden,
1135
+ isArray: _isArray,
1136
+ ...cleanConstraints
1137
+ } = constraints;
1138
+ return cleanConstraints;
1139
+ };
1140
+ const hasNullableArraySchema = (schema) => schema.anyOf?.some((member) => member.type === "array") === true;
1141
+ const isItemTypeOverride = (typeOverride) => typeof typeOverride === "string" && typeOverride !== "array";
1142
+ const shouldApplyNullableArrayItemConstraints = (propertySchema, isArray, typeOverride, enumValues) => hasNullableArraySchema(propertySchema) && (isArray === true && isItemTypeOverride(typeOverride) || enumValues !== void 0);
1143
+ const applyNullableArrayItemConstraints = (propertySchema, isArray, typeOverride, enumValues, restConstraints) => {
1144
+ const hasItemTypeOverride = isArray === true && isItemTypeOverride(typeOverride);
1145
+ const itemConstraints = {
1146
+ ...hasItemTypeOverride ? { type: typeOverride } : {},
1147
+ ...enumValues ? { enum: enumValues } : {}
1148
+ };
1149
+ return {
1150
+ ...propertySchema,
1151
+ ...restConstraints,
1152
+ anyOf: propertySchema.anyOf?.map(
1153
+ (member) => member.type === "array" ? {
1154
+ ...member,
1155
+ items: {
1156
+ ...hasItemTypeOverride ? {} : member.items,
1157
+ ...itemConstraints
1158
+ }
1159
+ } : member
1160
+ )
1161
+ };
1162
+ };
1163
+ const shouldApplyArrayItemConstraints = (propertySchema, typeOverride, enumValues) => propertySchema.type === "array" && (isItemTypeOverride(typeOverride) || enumValues !== void 0);
1164
+ const applyArrayItemConstraints = (propertySchema, typeOverride, enumValues, restConstraints) => {
1165
+ const hasItemTypeOverride = isItemTypeOverride(typeOverride);
1166
+ return {
1167
+ ...propertySchema,
1168
+ ...restConstraints,
1169
+ items: {
1170
+ ...hasItemTypeOverride ? {} : propertySchema.items,
1171
+ ...hasItemTypeOverride ? { type: typeOverride } : {},
1172
+ ...enumValues ? { enum: enumValues } : {}
1173
+ }
1174
+ };
1175
+ };
1176
+ const applyDirectPropertyConstraints = (propertySchema, typeOverride, enumValues, restConstraints) => ({
1177
+ ...propertySchema,
1178
+ ...typeOverride === void 0 ? {} : { type: typeOverride },
1179
+ ...restConstraints,
1180
+ ...enumValues === void 0 ? {} : { enum: enumValues }
1181
+ });
1102
1182
  const applyPropertyConstraintsToSchema = (propertySchema, constraints) => Option.fromNullable(constraints).pipe(
1103
1183
  Option.map((propertyConstraints) => {
1104
- const { hidden: _hidden, ...cleanConstraints } = propertyConstraints;
1105
- if (propertySchema.type === "array" && cleanConstraints.enum) {
1106
- const { enum: enumValues, ...restConstraints } = cleanConstraints;
1107
- return {
1108
- ...propertySchema,
1109
- ...restConstraints,
1110
- items: { ...propertySchema.items, enum: enumValues }
1111
- };
1184
+ const isArray = propertyConstraints.isArray;
1185
+ const {
1186
+ type: typeOverride,
1187
+ enum: enumValues,
1188
+ ...restConstraints
1189
+ } = cleanPropertyConstraints(propertyConstraints);
1190
+ if (shouldApplyNullableArrayItemConstraints(
1191
+ propertySchema,
1192
+ isArray,
1193
+ typeOverride,
1194
+ enumValues
1195
+ )) {
1196
+ return applyNullableArrayItemConstraints(
1197
+ propertySchema,
1198
+ isArray,
1199
+ typeOverride,
1200
+ enumValues,
1201
+ restConstraints
1202
+ );
1112
1203
  }
1113
- return {
1114
- ...propertySchema,
1115
- ...cleanConstraints
1116
- };
1204
+ return shouldApplyArrayItemConstraints(
1205
+ propertySchema,
1206
+ typeOverride,
1207
+ enumValues
1208
+ ) ? applyArrayItemConstraints(
1209
+ propertySchema,
1210
+ typeOverride,
1211
+ enumValues,
1212
+ restConstraints
1213
+ ) : applyDirectPropertyConstraints(
1214
+ propertySchema,
1215
+ typeOverride,
1216
+ enumValues,
1217
+ restConstraints
1218
+ );
1117
1219
  }),
1118
1220
  Option.getOrElse(() => propertySchema)
1119
1221
  );
@@ -1179,8 +1281,8 @@ const mergeValidationConstraints = (schemas, classConstraints, classRequired) =>
1179
1281
  );
1180
1282
  return { definitions };
1181
1283
  };
1182
- const extractClassValidationInfoEffect = Effect.fn(
1183
- "ValidationMapper.extractClassValidationInfo"
1284
+ const serviceExtractClassValidationInfo = Effect.fn(
1285
+ "ValidationMapperService.extractClassValidationInfo"
1184
1286
  )(function* (classDecl) {
1185
1287
  const className = classDecl.getName() ?? "<anonymous>";
1186
1288
  const filePath = classDecl.getSourceFile().getFilePath();
@@ -1200,8 +1302,8 @@ const extractClassValidationInfoEffect = Effect.fn(
1200
1302
  );
1201
1303
  return info;
1202
1304
  });
1203
- const mergeValidationConstraintsEffect = Effect.fn(
1204
- "ValidationMapper.mergeValidationConstraints"
1305
+ const serviceMergeValidationConstraints = Effect.fn(
1306
+ "ValidationMapperService.mergeValidationConstraints"
1205
1307
  )(function* (schemas, classConstraints, classRequired) {
1206
1308
  const merged = mergeValidationConstraints(
1207
1309
  schemas,
@@ -1223,6 +1325,18 @@ const mergeValidationConstraintsEffect = Effect.fn(
1223
1325
  yield* Effect.annotateCurrentSpan("classRequiredCount", classRequired.size);
1224
1326
  return merged;
1225
1327
  });
1328
+ class ValidationMapperService extends Effect.Service()(
1329
+ "ValidationMapperService",
1330
+ {
1331
+ effect: Effect.succeed({
1332
+ extractClassValidationInfo: serviceExtractClassValidationInfo,
1333
+ mergeValidationConstraints: serviceMergeValidationConstraints
1334
+ })
1335
+ }
1336
+ ) {
1337
+ }
1338
+ const extractClassValidationInfoEffect = serviceExtractClassValidationInfo;
1339
+ const mergeValidationConstraintsEffect = serviceMergeValidationConstraints;
1226
1340
 
1227
1341
  const methodInfoCache = /* @__PURE__ */ new WeakMap();
1228
1342
  const returnTypeInfoCache = /* @__PURE__ */ new WeakMap();
@@ -2021,152 +2135,200 @@ class MethodExtractionService extends Effect.Service()(
2021
2135
  ) {
2022
2136
  }
2023
2137
 
2024
- const buildSecurityRequirements = (requirements) => {
2025
- if (requirements.length === 0) return void 0;
2026
- const combined = {};
2027
- for (const req of requirements) {
2028
- combined[req.schemeName] = [...req.scopes];
2029
- }
2030
- return [combined];
2031
- };
2138
+ const buildSecurityRequirements = (requirements) => requirements.length === 0 ? void 0 : [
2139
+ Object.fromEntries(
2140
+ requirements.map((req) => [req.schemeName, [...req.scopes]])
2141
+ )
2142
+ ];
2032
2143
  const getRequestContentTypes = (methodInfo) => methodInfo.consumes.length > 0 ? methodInfo.consumes : ["application/json"];
2033
2144
  const getResponseContentTypes = (methodInfo) => methodInfo.produces.length > 0 ? methodInfo.produces : ["application/json"];
2034
2145
  const buildContentObject = (contentTypes, schema) => Object.fromEntries(contentTypes.map((type) => [type, { schema }]));
2035
- const parseInlineObjectType = (typeStr) => {
2036
- const trimmed = typeStr.trim();
2037
- if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
2038
- return null;
2039
- }
2040
- const content = trimmed.slice(1, -1).trim();
2041
- if (!content) {
2042
- return { properties: {}, required: [] };
2146
+ const INLINE_OBJECT_BOUNDARY_PATTERN = /^\s*\{[\s\S]*\}\s*$/;
2147
+ const INLINE_OBJECT_SEPARATORS = /* @__PURE__ */ new Set([";", ","]);
2148
+ const BRACE_DEPTH_CHANGE = {
2149
+ "{": 1,
2150
+ "}": -1
2151
+ };
2152
+ const pushInlinePart = (parts, part) => {
2153
+ const trimmed = part.trim();
2154
+ if (trimmed) {
2155
+ parts.push(trimmed);
2043
2156
  }
2044
- const properties = {};
2045
- const required = [];
2157
+ };
2158
+ const splitInlineObjectParts = (content) => {
2046
2159
  const parts = [];
2047
2160
  let current = "";
2048
2161
  let braceDepth = 0;
2049
2162
  for (const char of content) {
2050
- if (char === "{") {
2051
- braceDepth++;
2052
- current += char;
2053
- } else if (char === "}") {
2054
- braceDepth--;
2055
- current += char;
2056
- } else if ((char === ";" || char === ",") && braceDepth === 0) {
2057
- if (current.trim()) {
2058
- parts.push(current.trim());
2059
- }
2163
+ if (INLINE_OBJECT_SEPARATORS.has(char) && braceDepth === 0) {
2164
+ pushInlinePart(parts, current);
2060
2165
  current = "";
2061
- } else {
2062
- current += char;
2166
+ continue;
2063
2167
  }
2168
+ current += char;
2169
+ braceDepth += BRACE_DEPTH_CHANGE[char] ?? 0;
2064
2170
  }
2065
- if (current.trim()) {
2066
- parts.push(current.trim());
2067
- }
2068
- for (const part of parts) {
2069
- const colonIndex = part.indexOf(":");
2070
- if (colonIndex === -1) continue;
2071
- let propName = part.slice(0, colonIndex).trim();
2072
- const propType = part.slice(colonIndex + 1).trim();
2073
- const isOptional = propName.endsWith("?");
2074
- if (isOptional) {
2075
- propName = propName.slice(0, -1).trim();
2076
- }
2077
- if (!propName || !propType) continue;
2078
- properties[propName] = tsTypeToOpenApiSchema(propType);
2079
- if (!isOptional) {
2080
- required.push(propName);
2081
- }
2171
+ pushInlinePart(parts, current);
2172
+ return parts;
2173
+ };
2174
+ const parseInlineProperty = (part) => {
2175
+ const colonIndex = part.indexOf(":");
2176
+ const rawName = colonIndex === -1 ? "" : part.slice(0, colonIndex).trim();
2177
+ const type = colonIndex === -1 ? "" : part.slice(colonIndex + 1).trim();
2178
+ const isOptional = rawName.endsWith("?");
2179
+ const name = (isOptional ? rawName.slice(0, -1) : rawName).trim();
2180
+ return name && type ? { name, type, isOptional } : void 0;
2181
+ };
2182
+ const parseInlineObjectType = (typeStr) => {
2183
+ const trimmed = typeStr.trim();
2184
+ if (!INLINE_OBJECT_BOUNDARY_PATTERN.test(trimmed)) {
2185
+ return null;
2186
+ }
2187
+ const content = trimmed.slice(1, -1).trim();
2188
+ const properties = splitInlineObjectParts(content).map(parseInlineProperty).filter((property) => property !== void 0);
2189
+ return {
2190
+ properties: Object.fromEntries(
2191
+ properties.map((property) => [
2192
+ property.name,
2193
+ tsTypeToOpenApiSchema(property.type)
2194
+ ])
2195
+ ),
2196
+ required: properties.flatMap(
2197
+ (property) => property.isOptional ? [] : [property.name]
2198
+ )
2199
+ };
2200
+ };
2201
+ const OBJECT_SCHEMA = { type: "object" };
2202
+ const PRIMITIVE_TYPE_SCHEMAS = {
2203
+ string: { type: "string" },
2204
+ number: { type: "number" },
2205
+ boolean: { type: "boolean" },
2206
+ date: { type: "string", format: "date-time" },
2207
+ void: OBJECT_SCHEMA,
2208
+ undefined: OBJECT_SCHEMA,
2209
+ never: OBJECT_SCHEMA,
2210
+ null: OBJECT_SCHEMA,
2211
+ unknown: OBJECT_SCHEMA,
2212
+ any: OBJECT_SCHEMA,
2213
+ object: OBJECT_SCHEMA
2214
+ };
2215
+ const BINARY_TYPE_NAMES = /* @__PURE__ */ new Set([
2216
+ "StreamableFile",
2217
+ "Buffer",
2218
+ "Readable",
2219
+ "ReadableStream"
2220
+ ]);
2221
+ const RECORD_TYPE_PATTERN = /^Record<string,\s*(.+)>$/;
2222
+ const TYPE_REFERENCE_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$]*(<[^>]+>)?$/;
2223
+ const buildInlineObjectSchema = (parsed) => ({
2224
+ type: "object",
2225
+ properties: parsed.properties,
2226
+ ...parsed.required.length > 0 ? { required: parsed.required } : {}
2227
+ });
2228
+ const withNullable = (schema) => schema.$ref ? { allOf: [{ $ref: schema.$ref }], nullable: true } : { ...schema, nullable: true };
2229
+ const resolveInlineObjectSchema = (tsType) => Option.fromNullable(parseInlineObjectType(tsType)).pipe(
2230
+ Option.map(buildInlineObjectSchema),
2231
+ Option.getOrUndefined
2232
+ );
2233
+ const parseUnionType = (tsType) => {
2234
+ if (!tsType.includes(" | ")) {
2235
+ return void 0;
2236
+ }
2237
+ const members = tsType.split(" | ").map((member) => member.trim());
2238
+ return {
2239
+ hasNull: members.includes("null"),
2240
+ types: members.filter(
2241
+ (member) => member !== "undefined" && member !== "null"
2242
+ )
2243
+ };
2244
+ };
2245
+ const buildUnionSchema = (types) => {
2246
+ if (types.length === 0) {
2247
+ return { type: "object" };
2248
+ }
2249
+ if (types.length === 1) {
2250
+ return tsTypeToOpenApiSchema(types[0]);
2082
2251
  }
2083
- return { properties, required };
2252
+ return { oneOf: types.map((type) => tsTypeToOpenApiSchema(type)) };
2084
2253
  };
2254
+ const resolveUnionSchema = (tsType) => Option.fromNullable(parseUnionType(tsType)).pipe(
2255
+ Option.map(({ types, hasNull }) => {
2256
+ const schema = buildUnionSchema(types);
2257
+ return hasNull && types.length > 0 ? withNullable(schema) : schema;
2258
+ }),
2259
+ Option.getOrUndefined
2260
+ );
2261
+ const resolvePrimitiveSchema = (tsType) => Option.fromNullable(PRIMITIVE_TYPE_SCHEMAS[tsType.toLowerCase()]).pipe(
2262
+ Option.map((schema) => ({ ...schema })),
2263
+ Option.getOrUndefined
2264
+ );
2265
+ const resolveBinarySchema = (tsType) => BINARY_TYPE_NAMES.has(tsType) ? { type: "string", format: "binary" } : void 0;
2266
+ const resolveArraySchema = (tsType) => tsType.endsWith("[]") ? {
2267
+ type: "array",
2268
+ items: tsTypeToOpenApiSchema(tsType.slice(0, -2))
2269
+ } : void 0;
2270
+ const resolveRecordSchema = (tsType) => RECORD_TYPE_PATTERN.test(tsType) ? { type: "object" } : void 0;
2271
+ const resolveReferenceSchema = (tsType) => TYPE_REFERENCE_PATTERN.test(tsType) ? { $ref: `#/components/schemas/${tsType}` } : void 0;
2272
+ const TYPE_SCHEMA_RESOLVERS = [
2273
+ resolveInlineObjectSchema,
2274
+ resolveUnionSchema,
2275
+ resolvePrimitiveSchema,
2276
+ resolveBinarySchema,
2277
+ resolveArraySchema,
2278
+ resolveRecordSchema,
2279
+ resolveReferenceSchema
2280
+ ];
2085
2281
  const tsTypeToOpenApiSchema = (tsType) => {
2086
2282
  const trimmed = tsType.trim();
2087
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
2088
- const parsed = parseInlineObjectType(trimmed);
2089
- if (parsed) {
2090
- const schema = {
2091
- type: "object",
2092
- properties: parsed.properties
2093
- };
2094
- if (parsed.required.length > 0) {
2095
- return { ...schema, required: parsed.required };
2096
- }
2283
+ for (const resolver of TYPE_SCHEMA_RESOLVERS) {
2284
+ const schema = resolver(trimmed);
2285
+ if (schema !== void 0) {
2097
2286
  return schema;
2098
2287
  }
2099
2288
  }
2100
- if (trimmed.includes(" | ")) {
2101
- const allMembers = trimmed.split(" | ").map((t) => t.trim());
2102
- const hasNull = allMembers.includes("null");
2103
- const types = allMembers.filter((t) => t !== "undefined" && t !== "null");
2104
- if (types.length === 0) return { type: "object" };
2105
- const schema = types.length === 1 ? tsTypeToOpenApiSchema(types[0]) : { oneOf: types.map((type) => tsTypeToOpenApiSchema(type)) };
2106
- if (!hasNull) return schema;
2107
- if (schema.$ref) {
2108
- return { allOf: [{ $ref: schema.$ref }], nullable: true };
2109
- }
2110
- return { ...schema, nullable: true };
2111
- }
2112
- switch (trimmed.toLowerCase()) {
2113
- case "string":
2114
- return { type: "string" };
2115
- case "number":
2116
- return { type: "number" };
2117
- case "boolean":
2118
- return { type: "boolean" };
2119
- case "date":
2120
- return { type: "string", format: "date-time" };
2121
- case "void":
2122
- case "undefined":
2123
- case "never":
2124
- case "null":
2125
- return { type: "object" };
2126
- case "unknown":
2127
- case "any":
2128
- case "object":
2129
- return { type: "object" };
2130
- }
2131
- if (trimmed === "StreamableFile" || trimmed === "Buffer" || trimmed === "Readable" || trimmed === "ReadableStream") {
2132
- return { type: "string", format: "binary" };
2133
- }
2134
- if (trimmed.endsWith("[]")) {
2135
- const itemType = trimmed.slice(0, -2);
2136
- return {
2137
- type: "array",
2138
- items: tsTypeToOpenApiSchema(itemType)
2139
- };
2140
- }
2141
- const recordMatch = trimmed.match(/^Record<string,\s*(.+)>$/);
2142
- if (recordMatch) {
2143
- return {
2144
- type: "object"
2145
- };
2146
- }
2147
- if (trimmed.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*(<[^>]+>)?$/)) {
2148
- return { $ref: `#/components/schemas/${trimmed}` };
2149
- }
2150
2289
  return { type: "object" };
2151
2290
  };
2152
2291
  const isNodeModulesPath = (filePath) => filePath.includes("/node_modules/") || filePath.includes("\\node_modules\\");
2153
2292
  const shouldFallbackExternalReturnRef = (returnType) => Option.isSome(returnType.filePath) && isNodeModulesPath(returnType.filePath.value);
2154
2293
  const responseSchemaOrObjectFallback = (schema) => schema.$ref ? { type: "object" } : schema;
2155
- const getParameterLocation = (location) => {
2156
- switch (location) {
2157
- case "path":
2158
- return "path";
2159
- case "header":
2160
- return "header";
2161
- case "cookie":
2162
- return "cookie";
2163
- default:
2164
- return "query";
2294
+ const PARAMETER_LOCATION_MAP = {
2295
+ path: "path",
2296
+ query: "query",
2297
+ header: "header",
2298
+ cookie: "cookie",
2299
+ body: "query"
2300
+ };
2301
+ const getParameterLocation = (location) => PARAMETER_LOCATION_MAP[location];
2302
+ const applyParameterConstraints = (baseSchema, constraints) => {
2303
+ const {
2304
+ isArray,
2305
+ type: typeOverride,
2306
+ enum: enumValues,
2307
+ ...restConstraints
2308
+ } = constraints;
2309
+ const shouldApplyItemConstraints = (isArray === true || baseSchema.type === "array") && typeof typeOverride === "string" && typeOverride !== "array";
2310
+ if (shouldApplyItemConstraints || baseSchema.type === "array" && enumValues) {
2311
+ return {
2312
+ ...baseSchema,
2313
+ ...restConstraints,
2314
+ type: "array",
2315
+ items: {
2316
+ ...shouldApplyItemConstraints ? {} : baseSchema.items,
2317
+ ...shouldApplyItemConstraints ? { type: typeOverride } : {},
2318
+ ...enumValues ? { enum: enumValues } : {}
2319
+ }
2320
+ };
2165
2321
  }
2322
+ return {
2323
+ ...baseSchema,
2324
+ ...restConstraints,
2325
+ ...typeOverride === void 0 ? {} : { type: typeOverride },
2326
+ ...enumValues === void 0 ? {} : { enum: enumValues }
2327
+ };
2166
2328
  };
2167
2329
  const transformParameter = (param) => {
2168
2330
  const baseSchema = tsTypeToOpenApiSchema(param.tsType);
2169
- const schema = param.constraints ? { ...baseSchema, ...param.constraints } : baseSchema;
2331
+ const schema = param.constraints ? applyParameterConstraints(baseSchema, param.constraints) : baseSchema;
2170
2332
  return {
2171
2333
  name: param.name,
2172
2334
  in: getParameterLocation(param.location),
@@ -2179,21 +2341,7 @@ const isInlineOptionalBodyType = (tsType) => {
2179
2341
  const parsed = parseInlineObjectType(tsType);
2180
2342
  return parsed !== null && parsed.required.length === 0;
2181
2343
  };
2182
- const buildResponseSchema = (returnType) => {
2183
- const shouldFallback = Option.isNone(returnType.container) && shouldFallbackExternalReturnRef(returnType);
2184
- if (Option.isSome(returnType.container) && returnType.container.value === "array") {
2185
- if (Option.isSome(returnType.inline)) {
2186
- return {
2187
- type: "array",
2188
- items: tsTypeToOpenApiSchema(returnType.inline.value)
2189
- };
2190
- }
2191
- const itemSchema = Option.isSome(returnType.type) ? tsTypeToOpenApiSchema(returnType.type.value) : { type: "object" };
2192
- return {
2193
- type: "array",
2194
- items: shouldFallback ? responseSchemaOrObjectFallback(itemSchema) : itemSchema
2195
- };
2196
- }
2344
+ const buildScalarReturnSchema = (returnType, shouldFallback) => {
2197
2345
  if (Option.isSome(returnType.type)) {
2198
2346
  const schema = tsTypeToOpenApiSchema(returnType.type.value);
2199
2347
  return shouldFallback ? responseSchemaOrObjectFallback(schema) : schema;
@@ -2203,81 +2351,77 @@ const buildResponseSchema = (returnType) => {
2203
2351
  }
2204
2352
  return { type: "string" };
2205
2353
  };
2206
- const buildResponseSchemaFromMetadata = (response) => {
2207
- if (Option.isNone(response.type)) return void 0;
2208
- const typeName = response.type.value;
2209
- if (response.isArray) {
2210
- return {
2211
- type: "array",
2212
- items: tsTypeToOpenApiSchema(typeName)
2213
- };
2214
- }
2215
- return tsTypeToOpenApiSchema(typeName);
2216
- };
2217
- const getDefaultSuccessCode = (methodInfo) => {
2218
- if (Option.isSome(methodInfo.httpCode)) {
2219
- return methodInfo.httpCode.value;
2220
- }
2221
- return methodInfo.httpMethod === "POST" ? 201 : 200;
2222
- };
2223
- const hasMeaningfulReturnType = (returnType) => {
2224
- if (Option.isSome(returnType.type)) {
2225
- const typeName = returnType.type.value.toLowerCase();
2226
- if (["void", "undefined", "never"].includes(typeName)) {
2227
- return false;
2228
- }
2229
- return true;
2230
- }
2231
- return Option.isSome(returnType.inline);
2354
+ const buildArrayReturnSchema = (returnType) => ({
2355
+ type: "array",
2356
+ items: Option.isSome(returnType.inline) ? tsTypeToOpenApiSchema(returnType.inline.value) : Option.match(returnType.type, {
2357
+ onNone: () => ({ type: "object" }),
2358
+ onSome: (typeName) => tsTypeToOpenApiSchema(typeName)
2359
+ })
2360
+ });
2361
+ const buildResponseSchema = (returnType) => {
2362
+ const shouldFallback = Option.isNone(returnType.container) && shouldFallbackExternalReturnRef(returnType);
2363
+ return returnType.container.pipe(
2364
+ Option.match({
2365
+ onNone: () => buildScalarReturnSchema(returnType, shouldFallback),
2366
+ onSome: () => buildArrayReturnSchema(returnType)
2367
+ })
2368
+ );
2232
2369
  };
2370
+ const buildResponseSchemaFromMetadata = (response) => response.type.pipe(
2371
+ Option.map((typeName) => {
2372
+ const schema = tsTypeToOpenApiSchema(typeName);
2373
+ return response.isArray ? { type: "array", items: schema } : schema;
2374
+ }),
2375
+ Option.getOrUndefined
2376
+ );
2377
+ const getDefaultSuccessCode = (methodInfo) => Option.getOrElse(
2378
+ methodInfo.httpCode,
2379
+ () => methodInfo.httpMethod === "POST" ? 201 : 200
2380
+ );
2381
+ const hasMeaningfulReturnType = (returnType) => returnType.type.pipe(
2382
+ Option.match({
2383
+ onNone: () => Option.isSome(returnType.inline),
2384
+ onSome: (typeName) => !["void", "undefined", "never"].includes(typeName.toLowerCase())
2385
+ })
2386
+ );
2233
2387
  const isSuccessWithContent = (statusCode) => statusCode >= 200 && statusCode < 300 && statusCode !== 204;
2234
2388
  const buildResponseEntry = (response, returnType, hasReturnType, contentTypes) => {
2235
2389
  const schema = buildResponseSchemaFromMetadata(response) ?? (hasReturnType && isSuccessWithContent(response.statusCode) ? buildResponseSchema(returnType) : void 0);
2236
2390
  const description = Option.getOrElse(response.description, () => "");
2237
- if (!schema) return { description };
2238
- return { description, content: buildContentObject(contentTypes, schema) };
2391
+ return schema ? { description, content: buildContentObject(contentTypes, schema) } : { description };
2239
2392
  };
2393
+ const buildDefaultResponseEntry = (returnType, hasReturnType, contentTypes) => ({
2394
+ description: "",
2395
+ ...hasReturnType ? {
2396
+ content: buildContentObject(
2397
+ contentTypes,
2398
+ buildResponseSchema(returnType)
2399
+ )
2400
+ } : {}
2401
+ });
2402
+ const hasDeclaredSuccessResponse = (responses) => responses.some(
2403
+ (response) => response.statusCode >= 200 && response.statusCode < 300
2404
+ );
2240
2405
  const buildResponses = (methodInfo) => {
2241
2406
  const contentTypes = getResponseContentTypes(methodInfo);
2242
2407
  const returnType = methodInfo.returnType;
2243
2408
  const hasReturnType = hasMeaningfulReturnType(returnType);
2244
2409
  const statusCode = getDefaultSuccessCode(methodInfo);
2245
- if (methodInfo.responses.length === 0) {
2246
- if (!hasReturnType) {
2247
- return { [statusCode.toString()]: { description: "" } };
2248
- }
2249
- return {
2250
- [statusCode.toString()]: {
2251
- description: "",
2252
- content: buildContentObject(
2253
- contentTypes,
2254
- buildResponseSchema(returnType)
2255
- )
2256
- }
2257
- };
2258
- }
2259
- const result = {};
2260
- const hasSuccessResponse = methodInfo.responses.some(
2261
- (r) => r.statusCode >= 200 && r.statusCode < 300
2410
+ const defaultSuccessEntry = buildDefaultResponseEntry(
2411
+ returnType,
2412
+ hasReturnType,
2413
+ contentTypes
2262
2414
  );
2263
- if (!hasSuccessResponse && hasReturnType) {
2264
- result[statusCode.toString()] = {
2265
- description: "",
2266
- content: buildContentObject(
2267
- contentTypes,
2268
- buildResponseSchema(returnType)
2269
- )
2270
- };
2271
- }
2272
- for (const response of methodInfo.responses) {
2273
- result[response.statusCode.toString()] = buildResponseEntry(
2274
- response,
2275
- returnType,
2276
- hasReturnType,
2277
- contentTypes
2278
- );
2279
- }
2280
- return result;
2415
+ const declaredResponses = Object.fromEntries(
2416
+ methodInfo.responses.map((response) => [
2417
+ response.statusCode.toString(),
2418
+ buildResponseEntry(response, returnType, hasReturnType, contentTypes)
2419
+ ])
2420
+ );
2421
+ return {
2422
+ ...methodInfo.responses.length === 0 || !hasDeclaredSuccessResponse(methodInfo.responses) && hasReturnType ? { [statusCode.toString()]: defaultSuccessEntry } : {},
2423
+ ...declaredResponses
2424
+ };
2281
2425
  };
2282
2426
  const buildOpenApiPath = (path) => path.replace(/:([^/]+)/g, "{$1}") || "/";
2283
2427
  const transformMethodInternal = (methodInfo) => {
@@ -2323,33 +2467,49 @@ const transformMethodInternal = (methodInfo) => {
2323
2467
  };
2324
2468
  };
2325
2469
  const transformMethod = (methodInfo) => transformMethodInternal(methodInfo);
2326
- const transformMethodEffect = Effect.fn("Transformer.transformMethod")(
2327
- function* (methodInfo) {
2328
- return yield* Effect.succeed(transformMethodInternal(methodInfo));
2329
- }
2330
- );
2331
- const transformMethods = (methodInfos) => methodInfos.reduce((acc, methodInfo) => {
2332
- const endpoint = transformMethodInternal(methodInfo);
2470
+ const mergeOpenApiPaths = (endpoints) => endpoints.reduce((acc, endpoint) => {
2333
2471
  for (const path in endpoint) {
2334
- if (!acc[path]) acc[path] = {};
2335
- Object.assign(acc[path], endpoint[path]);
2472
+ acc[path] = { ...acc[path] ?? {}, ...endpoint[path] };
2336
2473
  }
2337
2474
  return acc;
2338
2475
  }, {});
2339
- const transformMethodsEffect = Effect.fn(
2340
- "Transformer.transformMethods"
2476
+ const transformMethods = (methodInfos) => mergeOpenApiPaths(methodInfos.map(transformMethodInternal));
2477
+ const serviceTransformMethod = Effect.fn("TransformerService.transformMethod")(
2478
+ function* (methodInfo) {
2479
+ const paths = yield* Effect.sync(() => transformMethodInternal(methodInfo));
2480
+ yield* Effect.annotateCurrentSpan(
2481
+ "controllerName",
2482
+ methodInfo.controllerName
2483
+ );
2484
+ yield* Effect.annotateCurrentSpan("methodName", methodInfo.methodName);
2485
+ yield* Effect.annotateCurrentSpan("httpMethod", methodInfo.httpMethod);
2486
+ yield* Effect.annotateCurrentSpan("path", methodInfo.path);
2487
+ return paths;
2488
+ }
2489
+ );
2490
+ const serviceTransformMethods = Effect.fn(
2491
+ "TransformerService.transformMethods"
2341
2492
  )(function* (methodInfos) {
2342
- const endpoints = yield* Effect.forEach(methodInfos, transformMethodEffect, {
2493
+ const endpoints = yield* Effect.forEach(methodInfos, serviceTransformMethod, {
2343
2494
  concurrency: "unbounded"
2344
2495
  });
2345
- return endpoints.reduce((acc, endpoint) => {
2346
- for (const path in endpoint) {
2347
- if (!acc[path]) acc[path] = {};
2348
- Object.assign(acc[path], endpoint[path]);
2349
- }
2350
- return acc;
2351
- }, {});
2496
+ const paths = mergeOpenApiPaths(endpoints);
2497
+ yield* Effect.annotateCurrentSpan("methodCount", methodInfos.length);
2498
+ yield* Effect.annotateCurrentSpan("pathCount", Object.keys(paths).length);
2499
+ return paths;
2352
2500
  });
2501
+ class TransformerService extends Effect.Service()(
2502
+ "TransformerService",
2503
+ {
2504
+ effect: Effect.succeed({
2505
+ transformMethod: serviceTransformMethod,
2506
+ transformMethods: serviceTransformMethods
2507
+ })
2508
+ }
2509
+ ) {
2510
+ }
2511
+ const transformMethodEffect = serviceTransformMethod;
2512
+ const transformMethodsEffect = serviceTransformMethods;
2353
2513
 
2354
2514
  const UGLY_NAME_PATTERN = /^(.+?)<.*>$/;
2355
2515
  const UGLY_NAME_PATTERN_ENCODED = /^(.+?)%3C.*%3E$/i;
@@ -4682,39 +4842,14 @@ class SchemaService extends Effect.Service()(
4682
4842
  ) {
4683
4843
  }
4684
4844
 
4685
- const serviceExtractClassValidationInfo = Effect.fn(
4686
- "ValidationService.extractClassValidationInfo"
4687
- )(function* (classDecl) {
4688
- return yield* extractClassValidationInfoEffect(classDecl);
4689
- });
4690
- const serviceMergeValidationConstraints = Effect.fn(
4691
- "ValidationService.mergeValidationConstraints"
4692
- )(function* (schemas, classConstraints, classRequired) {
4693
- return yield* mergeValidationConstraintsEffect(
4694
- schemas,
4695
- classConstraints,
4696
- classRequired
4697
- );
4698
- });
4699
- class ValidationService extends Effect.Service()(
4700
- "ValidationService",
4701
- {
4702
- accessors: true,
4703
- effect: Effect.succeed({
4704
- extractClassValidationInfo: serviceExtractClassValidationInfo,
4705
- mergeValidationConstraints: serviceMergeValidationConstraints
4706
- })
4707
- }
4708
- ) {
4709
- }
4710
-
4711
4845
  const generatorServicesLayer = Layer.mergeAll(
4712
4846
  ConfigService.Default,
4713
4847
  ProjectService.Default,
4714
4848
  ModuleTraversalService.Default,
4715
4849
  MethodExtractionService.Default,
4716
4850
  SchemaService.Default,
4717
- ValidationService.Default,
4851
+ TransformerService.Default,
4852
+ ValidationMapperService.Default,
4718
4853
  OutputService.Default
4719
4854
  );
4720
4855
 
@@ -5006,6 +5141,7 @@ const extractValidationConstraintsEffect = Effect.fn(
5006
5141
  if (dtoFiles.length === 0) {
5007
5142
  return schemas;
5008
5143
  }
5144
+ const validation = yield* ValidationMapperService;
5009
5145
  const project = new Project({
5010
5146
  tsConfigFilePath: tsconfig,
5011
5147
  skipAddingFilesFromTsConfig: true,
@@ -5026,7 +5162,7 @@ const extractValidationConstraintsEffect = Effect.fn(
5026
5162
  for (const classDecl of sourceFile.getClasses()) {
5027
5163
  const className = classDecl.getName();
5028
5164
  if (!className) continue;
5029
- const { constraints, required } = yield* ValidationService.extractClassValidationInfo(classDecl);
5165
+ const { constraints, required } = yield* validation.extractClassValidationInfo(classDecl);
5030
5166
  if (Object.keys(constraints).length > 0) {
5031
5167
  classConstraints.set(className, constraints);
5032
5168
  }
@@ -5042,7 +5178,7 @@ const extractValidationConstraintsEffect = Effect.fn(
5042
5178
  classesWithRequired: classRequired.size
5043
5179
  })
5044
5180
  );
5045
- return yield* ValidationService.mergeValidationConstraints(
5181
+ return yield* validation.mergeValidationConstraints(
5046
5182
  schemas,
5047
5183
  classConstraints,
5048
5184
  classRequired
@@ -5212,7 +5348,8 @@ const generateEffect = Effect.fn("Generate.generateEffect")(function* (configPat
5212
5348
  "filteredMethodCount",
5213
5349
  filteredMethodInfos.length
5214
5350
  );
5215
- let paths = yield* transformMethodsEffect(filteredMethodInfos);
5351
+ const transformer = yield* TransformerService;
5352
+ let paths = yield* transformer.transformMethods(filteredMethodInfos);
5216
5353
  yield* Effect.annotateCurrentSpan("initialPathCount", Object.keys(paths).length);
5217
5354
  if (options.basePath) {
5218
5355
  const prefix = options.basePath.startsWith("/") ? options.basePath : `/${options.basePath}`;
@@ -5500,4 +5637,4 @@ const generate = async (configPath, overrides) => {
5500
5637
  return runGeneratorApiPromise(program);
5501
5638
  };
5502
5639
 
5503
- export { generateSchemasFromFiles as $, getControllerMethodInfosEffect as A, MethodExtractionService as B, ConfigNotFoundError as C, DtoGlobResolutionError as D, EntryNotFoundError as E, transformMethod as F, transformMethodEffect as G, transformMethods as H, InvalidMethodError as I, transformMethodsEffect as J, mergeSchemas as K, mergeSchemasEffect as L, MissingGenericSchemaTempFileCleanupError as M, mergeGeneratedSchemas as N, mergeGeneratedSchemasEffect as O, ProjectInitError as P, filterSchemas as Q, filterSchemasEffect as R, SpecFileNotFoundError as S, normalizeSchemas as T, normalizeSchemasEffect as U, filterInternalSchemas as V, filterInternalSchemasEffect as W, normalizeStructureRefs as X, normalizeStructureRefsEffect as Y, toPascalCase as Z, generateSchemas as _, SpecFileParseError as a, SchemaGenerationError as a0, SchemaService as a1, extractPropertyConstraints as a2, isPropertyOptional as a3, extractPropertyValidationInfo as a4, extractClassValidationInfo as a5, extractClassValidationInfoEffect as a6, extractClassConstraints as a7, getRequiredProperties as a8, applyConstraintsToSchema as a9, runGeneratorApiPromise as aA, mergeValidationConstraints as aa, mergeValidationConstraintsEffect as ab, ValidationService as ac, OutputService as ad, ConfigService as ae, findConfigFile as af, loadConfigFromFile as ag, loadConfig as ah, resolveConfig as ai, loadAndResolveConfig as aj, generatorServicesLayer as ak, resolveClassFromSymbol as al, getArrayInitializer as am, getStringLiteralValue as an, getSymbolFromIdentifier as ao, isModuleClass as ap, getModuleDecoratorArg as aq, resolveClassFromExpression as ar, resolveArrayOfClasses as as, getModuleMetadata as at, validateSpec as au, categorizeBrokenRefs as av, formatValidationResult as aw, generateEffect as ax, runProjectApiPromise as ay, runtimeLayerFor as az, SpecFileReadError as b, ConfigLoadError as c, defineConfig as d, ConfigValidationError as e, MissingGenericSchemaTempFileWriteError as f, generate as g, PublicApiError as h, ProjectService as i, ProjectServiceLive as j, getModules as k, getAllControllers as l, makeProjectContext as m, ModuleTraversalService as n, getControllerPrefix as o, getControllerName as p, isHttpMethod as q, getHttpMethods as r, getDecoratorName$1 as s, getControllerTags as t, getHttpDecorator as u, isHttpDecorator as v, normalizePath as w, getMethodInfo as x, getMethodInfoEffect as y, getControllerMethodInfos as z };
5640
+ export { generateSchemas as $, getControllerMethodInfosEffect as A, MethodExtractionService as B, ConfigNotFoundError as C, DtoGlobResolutionError as D, EntryNotFoundError as E, transformMethod as F, transformMethodEffect as G, transformMethods as H, InvalidMethodError as I, transformMethodsEffect as J, mergeSchemas as K, mergeSchemasEffect as L, MissingGenericSchemaTempFileCleanupError as M, mergeGeneratedSchemas as N, mergeGeneratedSchemasEffect as O, ProjectInitError as P, filterSchemas as Q, filterSchemasEffect as R, SpecFileNotFoundError as S, TransformerService as T, normalizeSchemas as U, normalizeSchemasEffect as V, filterInternalSchemas as W, filterInternalSchemasEffect as X, normalizeStructureRefs as Y, normalizeStructureRefsEffect as Z, toPascalCase as _, SpecFileParseError as a, generateSchemasFromFiles as a0, SchemaGenerationError as a1, SchemaService as a2, ValidationMapperService as a3, extractPropertyConstraints as a4, isPropertyOptional as a5, extractPropertyValidationInfo as a6, extractClassValidationInfo as a7, extractClassValidationInfoEffect as a8, extractClassConstraints as a9, runtimeLayerFor as aA, runGeneratorApiPromise as aB, getRequiredProperties as aa, applyConstraintsToSchema as ab, mergeValidationConstraints as ac, mergeValidationConstraintsEffect as ad, OutputService as ae, ConfigService as af, findConfigFile as ag, loadConfigFromFile as ah, loadConfig as ai, resolveConfig as aj, loadAndResolveConfig as ak, generatorServicesLayer as al, resolveClassFromSymbol as am, getArrayInitializer as an, getStringLiteralValue as ao, getSymbolFromIdentifier as ap, isModuleClass as aq, getModuleDecoratorArg as ar, resolveClassFromExpression as as, resolveArrayOfClasses as at, getModuleMetadata as au, validateSpec as av, categorizeBrokenRefs as aw, formatValidationResult as ax, generateEffect as ay, runProjectApiPromise as az, SpecFileReadError as b, ConfigLoadError as c, defineConfig as d, ConfigValidationError as e, MissingGenericSchemaTempFileWriteError as f, generate as g, PublicApiError as h, ProjectService as i, ProjectServiceLive as j, getModules as k, getAllControllers as l, makeProjectContext as m, ModuleTraversalService as n, getControllerPrefix as o, getControllerName as p, isHttpMethod as q, getHttpMethods as r, getDecoratorName$1 as s, getControllerTags as t, getHttpDecorator as u, isHttpDecorator as v, normalizePath as w, getMethodInfo as x, getMethodInfoEffect as y, getControllerMethodInfos as z };