nestjs-openapi 0.3.2 → 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 };
861
+ }
862
+ const numericLiteral = initializer.asKind?.(ts.SyntaxKind.NumericLiteral);
863
+ if (numericLiteral) {
864
+ const value = numericLiteral.getLiteralValue();
865
+ return { value, nextValue: value + 1 };
901
866
  }
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;
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 = [
@@ -968,77 +969,80 @@ const buildConstraintsFromKeys = (objectLiteral, keys, read) => Object.fromEntri
968
969
  return value === void 0 ? [] : [[key, value]];
969
970
  })
970
971
  );
971
- const extractApiPropertyEnum = (objLit) => {
972
- const initializer = getPropertyInitializer(objLit, "enum");
973
- 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) => {
974
979
  const arrayLiteral = initializer.asKind?.(
975
980
  ts.SyntaxKind.ArrayLiteralExpression
976
981
  );
977
- if (arrayLiteral) {
978
- const values = arrayLiteral.getElements().reduce((acc, element) => {
979
- if (Node.isStringLiteral(element)) {
980
- return [...acc, element.getLiteralValue()];
981
- }
982
- if (Node.isNumericLiteral(element)) {
983
- return [...acc, element.getLiteralValue()];
984
- }
985
- if (Node.isPrefixUnaryExpression(element)) {
986
- const numericValue = parseNumber(element.getText());
987
- return numericValue === void 0 ? acc : [...acc, numericValue];
988
- }
989
- return acc;
990
- }, []);
991
- return values.length > 0 ? values : void 0;
982
+ if (!arrayLiteral) {
983
+ return void 0;
992
984
  }
993
- 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;
994
987
  };
995
- const extractApiPropertyType = (objLit) => {
996
- const initializer = getPropertyInitializer(objLit, "type");
997
- if (!initializer) return void 0;
998
- if (Node.isIdentifier(initializer)) {
999
- return API_TYPE_IDENTIFIERS[initializer.getText()];
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;
1000
993
  }
1001
- const arrayLiteral = initializer.asKind?.(
1002
- ts.SyntaxKind.ArrayLiteralExpression
1003
- );
1004
- const firstArrayElement = arrayLiteral?.getElements()[0];
1005
- if (firstArrayElement && Node.isIdentifier(firstArrayElement)) {
1006
- const itemType = API_TYPE_IDENTIFIERS[firstArrayElement.getText()];
1007
- return itemType ? { ...itemType, isArray: true } : void 0;
994
+ return readEnumArrayLiteral(initializer) ?? readEnumIdentifier(initializer);
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;
1008
1001
  }
1009
- return void 0;
1002
+ const itemType = API_TYPE_IDENTIFIERS[firstArrayElement.getText()];
1003
+ return itemType ? { ...itemType, isArray: true } : void 0;
1010
1004
  };
1011
- const extractApiPropertyConstraints = (decorator) => {
1012
- const objectLiteral = decorator.getCallExpression()?.getArguments()[0]?.asKind?.(ts.SyntaxKind.ObjectLiteralExpression);
1013
- if (!objectLiteral) return void 0;
1014
- const enumValues = extractApiPropertyEnum(objectLiteral);
1015
- const typeConstraints = extractApiPropertyType(objectLiteral);
1016
- const result = {
1017
- ...enumValues ? { enum: enumValues } : {},
1018
- ...typeConstraints ?? {},
1019
- ...buildConstraintsFromKeys(
1020
- objectLiteral,
1021
- API_STRING_KEYS,
1022
- (obj, key) => getStringProp(obj, key)
1023
- ),
1024
- ...buildConstraintsFromKeys(
1025
- objectLiteral,
1026
- API_NUMBER_KEYS,
1027
- (obj, key) => getNumericProp(obj, key)
1028
- ),
1029
- ...buildConstraintsFromKeys(
1030
- objectLiteral,
1031
- API_BOOLEAN_KEYS,
1032
- (obj, key) => getBooleanProp(obj, key)
1033
- ),
1034
- ...buildConstraintsFromKeys(
1035
- objectLiteral,
1036
- API_PRIMITIVE_KEYS,
1037
- (obj, key) => getPrimitiveValue(obj, key)
1038
- )
1039
- };
1040
- return Object.keys(result).length > 0 ? result : void 0;
1005
+ const extractApiPropertyType = (objLit) => {
1006
+ const initializer = getPropertyInitializer(objLit, "type");
1007
+ if (!initializer) {
1008
+ return void 0;
1009
+ }
1010
+ return readApiTypeIdentifier(initializer) ?? readApiTypeArrayElement(initializer);
1041
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
+ );
1042
1046
  const compactConstraints = (constraints) => Object.fromEntries(
1043
1047
  Object.entries(constraints).filter(([, value]) => value !== void 0)
1044
1048
  );
@@ -1125,35 +1129,93 @@ const collectHiddenProperties = (propertyConstraints) => new Set(
1125
1129
  ([propertyName, constraints]) => constraints.hidden ? [propertyName] : []
1126
1130
  )
1127
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
+ });
1128
1182
  const applyPropertyConstraintsToSchema = (propertySchema, constraints) => Option.fromNullable(constraints).pipe(
1129
1183
  Option.map((propertyConstraints) => {
1130
- const { hidden: _hidden, isArray: _isArray, ...cleanConstraints } = propertyConstraints;
1184
+ const isArray = propertyConstraints.isArray;
1131
1185
  const {
1132
1186
  type: typeOverride,
1133
1187
  enum: enumValues,
1134
1188
  ...restConstraints
1135
- } = cleanConstraints;
1136
- const hasItemTypeOverride = propertySchema.type === "array" && typeof typeOverride === "string" && typeOverride !== "array";
1137
- if (propertySchema.type === "array" && (hasItemTypeOverride || enumValues)) {
1138
- const itemConstraints = {
1139
- ...hasItemTypeOverride ? { type: typeOverride } : {},
1140
- ...enumValues ? { enum: enumValues } : {}
1141
- };
1142
- return {
1143
- ...propertySchema,
1144
- ...restConstraints,
1145
- items: {
1146
- ...hasItemTypeOverride ? {} : propertySchema.items,
1147
- ...itemConstraints
1148
- }
1149
- };
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
+ );
1150
1203
  }
1151
- return {
1152
- ...propertySchema,
1153
- ...typeOverride === void 0 ? {} : { type: typeOverride },
1154
- ...restConstraints,
1155
- ...enumValues === void 0 ? {} : { enum: enumValues }
1156
- };
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
+ );
1157
1219
  }),
1158
1220
  Option.getOrElse(() => propertySchema)
1159
1221
  );
@@ -1219,8 +1281,8 @@ const mergeValidationConstraints = (schemas, classConstraints, classRequired) =>
1219
1281
  );
1220
1282
  return { definitions };
1221
1283
  };
1222
- const extractClassValidationInfoEffect = Effect.fn(
1223
- "ValidationMapper.extractClassValidationInfo"
1284
+ const serviceExtractClassValidationInfo = Effect.fn(
1285
+ "ValidationMapperService.extractClassValidationInfo"
1224
1286
  )(function* (classDecl) {
1225
1287
  const className = classDecl.getName() ?? "<anonymous>";
1226
1288
  const filePath = classDecl.getSourceFile().getFilePath();
@@ -1240,8 +1302,8 @@ const extractClassValidationInfoEffect = Effect.fn(
1240
1302
  );
1241
1303
  return info;
1242
1304
  });
1243
- const mergeValidationConstraintsEffect = Effect.fn(
1244
- "ValidationMapper.mergeValidationConstraints"
1305
+ const serviceMergeValidationConstraints = Effect.fn(
1306
+ "ValidationMapperService.mergeValidationConstraints"
1245
1307
  )(function* (schemas, classConstraints, classRequired) {
1246
1308
  const merged = mergeValidationConstraints(
1247
1309
  schemas,
@@ -1263,6 +1325,18 @@ const mergeValidationConstraintsEffect = Effect.fn(
1263
1325
  yield* Effect.annotateCurrentSpan("classRequiredCount", classRequired.size);
1264
1326
  return merged;
1265
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;
1266
1340
 
1267
1341
  const methodInfoCache = /* @__PURE__ */ new WeakMap();
1268
1342
  const returnTypeInfoCache = /* @__PURE__ */ new WeakMap();
@@ -2061,152 +2135,200 @@ class MethodExtractionService extends Effect.Service()(
2061
2135
  ) {
2062
2136
  }
2063
2137
 
2064
- const buildSecurityRequirements = (requirements) => {
2065
- if (requirements.length === 0) return void 0;
2066
- const combined = {};
2067
- for (const req of requirements) {
2068
- combined[req.schemeName] = [...req.scopes];
2069
- }
2070
- return [combined];
2071
- };
2138
+ const buildSecurityRequirements = (requirements) => requirements.length === 0 ? void 0 : [
2139
+ Object.fromEntries(
2140
+ requirements.map((req) => [req.schemeName, [...req.scopes]])
2141
+ )
2142
+ ];
2072
2143
  const getRequestContentTypes = (methodInfo) => methodInfo.consumes.length > 0 ? methodInfo.consumes : ["application/json"];
2073
2144
  const getResponseContentTypes = (methodInfo) => methodInfo.produces.length > 0 ? methodInfo.produces : ["application/json"];
2074
2145
  const buildContentObject = (contentTypes, schema) => Object.fromEntries(contentTypes.map((type) => [type, { schema }]));
2075
- const parseInlineObjectType = (typeStr) => {
2076
- const trimmed = typeStr.trim();
2077
- if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
2078
- return null;
2079
- }
2080
- const content = trimmed.slice(1, -1).trim();
2081
- if (!content) {
2082
- 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);
2083
2156
  }
2084
- const properties = {};
2085
- const required = [];
2157
+ };
2158
+ const splitInlineObjectParts = (content) => {
2086
2159
  const parts = [];
2087
2160
  let current = "";
2088
2161
  let braceDepth = 0;
2089
2162
  for (const char of content) {
2090
- if (char === "{") {
2091
- braceDepth++;
2092
- current += char;
2093
- } else if (char === "}") {
2094
- braceDepth--;
2095
- current += char;
2096
- } else if ((char === ";" || char === ",") && braceDepth === 0) {
2097
- if (current.trim()) {
2098
- parts.push(current.trim());
2099
- }
2163
+ if (INLINE_OBJECT_SEPARATORS.has(char) && braceDepth === 0) {
2164
+ pushInlinePart(parts, current);
2100
2165
  current = "";
2101
- } else {
2102
- current += char;
2166
+ continue;
2103
2167
  }
2168
+ current += char;
2169
+ braceDepth += BRACE_DEPTH_CHANGE[char] ?? 0;
2104
2170
  }
2105
- if (current.trim()) {
2106
- parts.push(current.trim());
2107
- }
2108
- for (const part of parts) {
2109
- const colonIndex = part.indexOf(":");
2110
- if (colonIndex === -1) continue;
2111
- let propName = part.slice(0, colonIndex).trim();
2112
- const propType = part.slice(colonIndex + 1).trim();
2113
- const isOptional = propName.endsWith("?");
2114
- if (isOptional) {
2115
- propName = propName.slice(0, -1).trim();
2116
- }
2117
- if (!propName || !propType) continue;
2118
- properties[propName] = tsTypeToOpenApiSchema(propType);
2119
- if (!isOptional) {
2120
- required.push(propName);
2121
- }
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;
2122
2236
  }
2123
- return { properties, required };
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]);
2251
+ }
2252
+ return { oneOf: types.map((type) => tsTypeToOpenApiSchema(type)) };
2124
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
+ ];
2125
2281
  const tsTypeToOpenApiSchema = (tsType) => {
2126
2282
  const trimmed = tsType.trim();
2127
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
2128
- const parsed = parseInlineObjectType(trimmed);
2129
- if (parsed) {
2130
- const schema = {
2131
- type: "object",
2132
- properties: parsed.properties
2133
- };
2134
- if (parsed.required.length > 0) {
2135
- return { ...schema, required: parsed.required };
2136
- }
2283
+ for (const resolver of TYPE_SCHEMA_RESOLVERS) {
2284
+ const schema = resolver(trimmed);
2285
+ if (schema !== void 0) {
2137
2286
  return schema;
2138
2287
  }
2139
2288
  }
2140
- if (trimmed.includes(" | ")) {
2141
- const allMembers = trimmed.split(" | ").map((t) => t.trim());
2142
- const hasNull = allMembers.includes("null");
2143
- const types = allMembers.filter((t) => t !== "undefined" && t !== "null");
2144
- if (types.length === 0) return { type: "object" };
2145
- const schema = types.length === 1 ? tsTypeToOpenApiSchema(types[0]) : { oneOf: types.map((type) => tsTypeToOpenApiSchema(type)) };
2146
- if (!hasNull) return schema;
2147
- if (schema.$ref) {
2148
- return { allOf: [{ $ref: schema.$ref }], nullable: true };
2149
- }
2150
- return { ...schema, nullable: true };
2151
- }
2152
- switch (trimmed.toLowerCase()) {
2153
- case "string":
2154
- return { type: "string" };
2155
- case "number":
2156
- return { type: "number" };
2157
- case "boolean":
2158
- return { type: "boolean" };
2159
- case "date":
2160
- return { type: "string", format: "date-time" };
2161
- case "void":
2162
- case "undefined":
2163
- case "never":
2164
- case "null":
2165
- return { type: "object" };
2166
- case "unknown":
2167
- case "any":
2168
- case "object":
2169
- return { type: "object" };
2170
- }
2171
- if (trimmed === "StreamableFile" || trimmed === "Buffer" || trimmed === "Readable" || trimmed === "ReadableStream") {
2172
- return { type: "string", format: "binary" };
2173
- }
2174
- if (trimmed.endsWith("[]")) {
2175
- const itemType = trimmed.slice(0, -2);
2176
- return {
2177
- type: "array",
2178
- items: tsTypeToOpenApiSchema(itemType)
2179
- };
2180
- }
2181
- const recordMatch = trimmed.match(/^Record<string,\s*(.+)>$/);
2182
- if (recordMatch) {
2183
- return {
2184
- type: "object"
2185
- };
2186
- }
2187
- if (trimmed.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*(<[^>]+>)?$/)) {
2188
- return { $ref: `#/components/schemas/${trimmed}` };
2189
- }
2190
2289
  return { type: "object" };
2191
2290
  };
2192
2291
  const isNodeModulesPath = (filePath) => filePath.includes("/node_modules/") || filePath.includes("\\node_modules\\");
2193
2292
  const shouldFallbackExternalReturnRef = (returnType) => Option.isSome(returnType.filePath) && isNodeModulesPath(returnType.filePath.value);
2194
2293
  const responseSchemaOrObjectFallback = (schema) => schema.$ref ? { type: "object" } : schema;
2195
- const getParameterLocation = (location) => {
2196
- switch (location) {
2197
- case "path":
2198
- return "path";
2199
- case "header":
2200
- return "header";
2201
- case "cookie":
2202
- return "cookie";
2203
- default:
2204
- 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
+ };
2205
2321
  }
2322
+ return {
2323
+ ...baseSchema,
2324
+ ...restConstraints,
2325
+ ...typeOverride === void 0 ? {} : { type: typeOverride },
2326
+ ...enumValues === void 0 ? {} : { enum: enumValues }
2327
+ };
2206
2328
  };
2207
2329
  const transformParameter = (param) => {
2208
2330
  const baseSchema = tsTypeToOpenApiSchema(param.tsType);
2209
- const schema = param.constraints ? { ...baseSchema, ...param.constraints } : baseSchema;
2331
+ const schema = param.constraints ? applyParameterConstraints(baseSchema, param.constraints) : baseSchema;
2210
2332
  return {
2211
2333
  name: param.name,
2212
2334
  in: getParameterLocation(param.location),
@@ -2219,21 +2341,7 @@ const isInlineOptionalBodyType = (tsType) => {
2219
2341
  const parsed = parseInlineObjectType(tsType);
2220
2342
  return parsed !== null && parsed.required.length === 0;
2221
2343
  };
2222
- const buildResponseSchema = (returnType) => {
2223
- const shouldFallback = Option.isNone(returnType.container) && shouldFallbackExternalReturnRef(returnType);
2224
- if (Option.isSome(returnType.container) && returnType.container.value === "array") {
2225
- if (Option.isSome(returnType.inline)) {
2226
- return {
2227
- type: "array",
2228
- items: tsTypeToOpenApiSchema(returnType.inline.value)
2229
- };
2230
- }
2231
- const itemSchema = Option.isSome(returnType.type) ? tsTypeToOpenApiSchema(returnType.type.value) : { type: "object" };
2232
- return {
2233
- type: "array",
2234
- items: shouldFallback ? responseSchemaOrObjectFallback(itemSchema) : itemSchema
2235
- };
2236
- }
2344
+ const buildScalarReturnSchema = (returnType, shouldFallback) => {
2237
2345
  if (Option.isSome(returnType.type)) {
2238
2346
  const schema = tsTypeToOpenApiSchema(returnType.type.value);
2239
2347
  return shouldFallback ? responseSchemaOrObjectFallback(schema) : schema;
@@ -2243,81 +2351,77 @@ const buildResponseSchema = (returnType) => {
2243
2351
  }
2244
2352
  return { type: "string" };
2245
2353
  };
2246
- const buildResponseSchemaFromMetadata = (response) => {
2247
- if (Option.isNone(response.type)) return void 0;
2248
- const typeName = response.type.value;
2249
- if (response.isArray) {
2250
- return {
2251
- type: "array",
2252
- items: tsTypeToOpenApiSchema(typeName)
2253
- };
2254
- }
2255
- return tsTypeToOpenApiSchema(typeName);
2256
- };
2257
- const getDefaultSuccessCode = (methodInfo) => {
2258
- if (Option.isSome(methodInfo.httpCode)) {
2259
- return methodInfo.httpCode.value;
2260
- }
2261
- return methodInfo.httpMethod === "POST" ? 201 : 200;
2262
- };
2263
- const hasMeaningfulReturnType = (returnType) => {
2264
- if (Option.isSome(returnType.type)) {
2265
- const typeName = returnType.type.value.toLowerCase();
2266
- if (["void", "undefined", "never"].includes(typeName)) {
2267
- return false;
2268
- }
2269
- return true;
2270
- }
2271
- 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
+ );
2272
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
+ );
2273
2387
  const isSuccessWithContent = (statusCode) => statusCode >= 200 && statusCode < 300 && statusCode !== 204;
2274
2388
  const buildResponseEntry = (response, returnType, hasReturnType, contentTypes) => {
2275
2389
  const schema = buildResponseSchemaFromMetadata(response) ?? (hasReturnType && isSuccessWithContent(response.statusCode) ? buildResponseSchema(returnType) : void 0);
2276
2390
  const description = Option.getOrElse(response.description, () => "");
2277
- if (!schema) return { description };
2278
- return { description, content: buildContentObject(contentTypes, schema) };
2391
+ return schema ? { description, content: buildContentObject(contentTypes, schema) } : { description };
2279
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
+ );
2280
2405
  const buildResponses = (methodInfo) => {
2281
2406
  const contentTypes = getResponseContentTypes(methodInfo);
2282
2407
  const returnType = methodInfo.returnType;
2283
2408
  const hasReturnType = hasMeaningfulReturnType(returnType);
2284
2409
  const statusCode = getDefaultSuccessCode(methodInfo);
2285
- if (methodInfo.responses.length === 0) {
2286
- if (!hasReturnType) {
2287
- return { [statusCode.toString()]: { description: "" } };
2288
- }
2289
- return {
2290
- [statusCode.toString()]: {
2291
- description: "",
2292
- content: buildContentObject(
2293
- contentTypes,
2294
- buildResponseSchema(returnType)
2295
- )
2296
- }
2297
- };
2298
- }
2299
- const result = {};
2300
- const hasSuccessResponse = methodInfo.responses.some(
2301
- (r) => r.statusCode >= 200 && r.statusCode < 300
2410
+ const defaultSuccessEntry = buildDefaultResponseEntry(
2411
+ returnType,
2412
+ hasReturnType,
2413
+ contentTypes
2302
2414
  );
2303
- if (!hasSuccessResponse && hasReturnType) {
2304
- result[statusCode.toString()] = {
2305
- description: "",
2306
- content: buildContentObject(
2307
- contentTypes,
2308
- buildResponseSchema(returnType)
2309
- )
2310
- };
2311
- }
2312
- for (const response of methodInfo.responses) {
2313
- result[response.statusCode.toString()] = buildResponseEntry(
2314
- response,
2315
- returnType,
2316
- hasReturnType,
2317
- contentTypes
2318
- );
2319
- }
2320
- 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
+ };
2321
2425
  };
2322
2426
  const buildOpenApiPath = (path) => path.replace(/:([^/]+)/g, "{$1}") || "/";
2323
2427
  const transformMethodInternal = (methodInfo) => {
@@ -2363,33 +2467,49 @@ const transformMethodInternal = (methodInfo) => {
2363
2467
  };
2364
2468
  };
2365
2469
  const transformMethod = (methodInfo) => transformMethodInternal(methodInfo);
2366
- const transformMethodEffect = Effect.fn("Transformer.transformMethod")(
2367
- function* (methodInfo) {
2368
- return yield* Effect.succeed(transformMethodInternal(methodInfo));
2369
- }
2370
- );
2371
- const transformMethods = (methodInfos) => methodInfos.reduce((acc, methodInfo) => {
2372
- const endpoint = transformMethodInternal(methodInfo);
2470
+ const mergeOpenApiPaths = (endpoints) => endpoints.reduce((acc, endpoint) => {
2373
2471
  for (const path in endpoint) {
2374
- if (!acc[path]) acc[path] = {};
2375
- Object.assign(acc[path], endpoint[path]);
2472
+ acc[path] = { ...acc[path] ?? {}, ...endpoint[path] };
2376
2473
  }
2377
2474
  return acc;
2378
2475
  }, {});
2379
- const transformMethodsEffect = Effect.fn(
2380
- "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"
2381
2492
  )(function* (methodInfos) {
2382
- const endpoints = yield* Effect.forEach(methodInfos, transformMethodEffect, {
2493
+ const endpoints = yield* Effect.forEach(methodInfos, serviceTransformMethod, {
2383
2494
  concurrency: "unbounded"
2384
2495
  });
2385
- return endpoints.reduce((acc, endpoint) => {
2386
- for (const path in endpoint) {
2387
- if (!acc[path]) acc[path] = {};
2388
- Object.assign(acc[path], endpoint[path]);
2389
- }
2390
- return acc;
2391
- }, {});
2496
+ const paths = mergeOpenApiPaths(endpoints);
2497
+ yield* Effect.annotateCurrentSpan("methodCount", methodInfos.length);
2498
+ yield* Effect.annotateCurrentSpan("pathCount", Object.keys(paths).length);
2499
+ return paths;
2392
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;
2393
2513
 
2394
2514
  const UGLY_NAME_PATTERN = /^(.+?)<.*>$/;
2395
2515
  const UGLY_NAME_PATTERN_ENCODED = /^(.+?)%3C.*%3E$/i;
@@ -4722,39 +4842,14 @@ class SchemaService extends Effect.Service()(
4722
4842
  ) {
4723
4843
  }
4724
4844
 
4725
- const serviceExtractClassValidationInfo = Effect.fn(
4726
- "ValidationService.extractClassValidationInfo"
4727
- )(function* (classDecl) {
4728
- return yield* extractClassValidationInfoEffect(classDecl);
4729
- });
4730
- const serviceMergeValidationConstraints = Effect.fn(
4731
- "ValidationService.mergeValidationConstraints"
4732
- )(function* (schemas, classConstraints, classRequired) {
4733
- return yield* mergeValidationConstraintsEffect(
4734
- schemas,
4735
- classConstraints,
4736
- classRequired
4737
- );
4738
- });
4739
- class ValidationService extends Effect.Service()(
4740
- "ValidationService",
4741
- {
4742
- accessors: true,
4743
- effect: Effect.succeed({
4744
- extractClassValidationInfo: serviceExtractClassValidationInfo,
4745
- mergeValidationConstraints: serviceMergeValidationConstraints
4746
- })
4747
- }
4748
- ) {
4749
- }
4750
-
4751
4845
  const generatorServicesLayer = Layer.mergeAll(
4752
4846
  ConfigService.Default,
4753
4847
  ProjectService.Default,
4754
4848
  ModuleTraversalService.Default,
4755
4849
  MethodExtractionService.Default,
4756
4850
  SchemaService.Default,
4757
- ValidationService.Default,
4851
+ TransformerService.Default,
4852
+ ValidationMapperService.Default,
4758
4853
  OutputService.Default
4759
4854
  );
4760
4855
 
@@ -5046,6 +5141,7 @@ const extractValidationConstraintsEffect = Effect.fn(
5046
5141
  if (dtoFiles.length === 0) {
5047
5142
  return schemas;
5048
5143
  }
5144
+ const validation = yield* ValidationMapperService;
5049
5145
  const project = new Project({
5050
5146
  tsConfigFilePath: tsconfig,
5051
5147
  skipAddingFilesFromTsConfig: true,
@@ -5066,7 +5162,7 @@ const extractValidationConstraintsEffect = Effect.fn(
5066
5162
  for (const classDecl of sourceFile.getClasses()) {
5067
5163
  const className = classDecl.getName();
5068
5164
  if (!className) continue;
5069
- const { constraints, required } = yield* ValidationService.extractClassValidationInfo(classDecl);
5165
+ const { constraints, required } = yield* validation.extractClassValidationInfo(classDecl);
5070
5166
  if (Object.keys(constraints).length > 0) {
5071
5167
  classConstraints.set(className, constraints);
5072
5168
  }
@@ -5082,7 +5178,7 @@ const extractValidationConstraintsEffect = Effect.fn(
5082
5178
  classesWithRequired: classRequired.size
5083
5179
  })
5084
5180
  );
5085
- return yield* ValidationService.mergeValidationConstraints(
5181
+ return yield* validation.mergeValidationConstraints(
5086
5182
  schemas,
5087
5183
  classConstraints,
5088
5184
  classRequired
@@ -5252,7 +5348,8 @@ const generateEffect = Effect.fn("Generate.generateEffect")(function* (configPat
5252
5348
  "filteredMethodCount",
5253
5349
  filteredMethodInfos.length
5254
5350
  );
5255
- let paths = yield* transformMethodsEffect(filteredMethodInfos);
5351
+ const transformer = yield* TransformerService;
5352
+ let paths = yield* transformer.transformMethods(filteredMethodInfos);
5256
5353
  yield* Effect.annotateCurrentSpan("initialPathCount", Object.keys(paths).length);
5257
5354
  if (options.basePath) {
5258
5355
  const prefix = options.basePath.startsWith("/") ? options.basePath : `/${options.basePath}`;
@@ -5540,4 +5637,4 @@ const generate = async (configPath, overrides) => {
5540
5637
  return runGeneratorApiPromise(program);
5541
5638
  };
5542
5639
 
5543
- 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 };