adorn-api 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -323,7 +323,7 @@ function unwrapPromiseTypeNode(typeNode) {
323
323
  }
324
324
 
325
325
  // src/compiler/schema/openapi.ts
326
- var import_typescript9 = __toESM(require("typescript"), 1);
326
+ var import_typescript11 = __toESM(require("typescript"), 1);
327
327
 
328
328
  // src/compiler/schema/typeToJsonSchema.ts
329
329
  var import_typescript7 = __toESM(require("typescript"), 1);
@@ -718,6 +718,48 @@ function getExplicitTypeNameFromNode3(typeNode) {
718
718
 
719
719
  // src/compiler/schema/objectHandler.ts
720
720
  var import_typescript6 = __toESM(require("typescript"), 1);
721
+ function getTypeParameterName(type) {
722
+ if (type.flags & import_typescript6.default.TypeFlags.TypeParameter) {
723
+ const typeParam = type;
724
+ return typeParam.symbol?.getName() ?? null;
725
+ }
726
+ return null;
727
+ }
728
+ function getTypeArguments(type) {
729
+ const typeRef = type;
730
+ const args = typeRef.typeArguments;
731
+ if (!args) return void 0;
732
+ return Array.from(args);
733
+ }
734
+ function createTypeParameterSubstitutions(type, typeNode, checker) {
735
+ const typeArgs = getTypeArguments(type);
736
+ if (!typeArgs || typeArgs.length === 0) {
737
+ return void 0;
738
+ }
739
+ if (!typeNode || !import_typescript6.default.isTypeReferenceNode(typeNode)) {
740
+ return void 0;
741
+ }
742
+ const typeParams = typeNode.typeArguments;
743
+ if (!typeParams || typeParams.length !== typeArgs.length) {
744
+ return void 0;
745
+ }
746
+ const substitutions = /* @__PURE__ */ new Map();
747
+ for (let i = 0; i < typeParams.length; i++) {
748
+ const typeParamNode = typeParams[i];
749
+ const typeArg = typeArgs[i];
750
+ if (import_typescript6.default.isIdentifier(typeParamNode)) {
751
+ substitutions.set(typeParamNode.text, typeArg);
752
+ }
753
+ }
754
+ return substitutions.size > 0 ? substitutions : void 0;
755
+ }
756
+ function resolveTypeParameter(type, substitutions, _checker) {
757
+ if (!substitutions) return null;
758
+ const paramName = getTypeParameterName(type);
759
+ if (!paramName) return null;
760
+ const resolved = substitutions.get(paramName);
761
+ return resolved ?? null;
762
+ }
721
763
  function handleObjectType(type, ctx, typeNode) {
722
764
  const { checker, components, typeStack } = ctx;
723
765
  const symbol = type.getSymbol();
@@ -737,7 +779,9 @@ function handleObjectType(type, ctx, typeNode) {
737
779
  }
738
780
  typeStack.add(type);
739
781
  }
740
- const schema = buildObjectSchema(type, ctx, typeNode);
782
+ const typeParamSubstitutions = createTypeParameterSubstitutions(type, typeNode, checker);
783
+ const buildCtx = typeParamSubstitutions ? { ...ctx, typeParameterSubstitutions: typeParamSubstitutions } : ctx;
784
+ const schema = buildObjectSchema(type, buildCtx, typeNode);
741
785
  if (typeName && typeName !== "__type") {
742
786
  typeStack.delete(type);
743
787
  const existing = components.get(typeName);
@@ -755,7 +799,7 @@ function handleObjectType(type, ctx, typeNode) {
755
799
  return schema;
756
800
  }
757
801
  function buildObjectSchema(type, ctx, _typeNode) {
758
- const { checker, mode } = ctx;
802
+ const { checker, mode, typeParameterSubstitutions } = ctx;
759
803
  const properties = {};
760
804
  const required = [];
761
805
  const props = checker.getPropertiesOfType(type);
@@ -764,10 +808,14 @@ function buildObjectSchema(type, ctx, _typeNode) {
764
808
  if (isIteratorOrSymbolProperty(propName)) {
765
809
  continue;
766
810
  }
767
- const propType = checker.getTypeOfSymbol(prop);
811
+ let propType = checker.getTypeOfSymbol(prop);
768
812
  if (isMethodLike(propType)) {
769
813
  continue;
770
814
  }
815
+ const resolvedType = resolveTypeParameter(propType, typeParameterSubstitutions, checker);
816
+ if (resolvedType) {
817
+ propType = resolvedType;
818
+ }
771
819
  const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
772
820
  const isRelation = isMetalOrmWrapperType(propType, checker);
773
821
  const propCtx = { ...ctx, propertyName: propName };
@@ -787,7 +835,8 @@ function buildObjectSchema(type, ctx, _typeNode) {
787
835
  if (isRecordType(type, checker)) {
788
836
  const valueType = getRecordValueType(type, checker);
789
837
  if (valueType) {
790
- schema.additionalProperties = typeToJsonSchema(valueType, ctx);
838
+ const resolvedValueType = resolveTypeParameter(valueType, typeParameterSubstitutions, checker);
839
+ schema.additionalProperties = typeToJsonSchema(resolvedValueType ?? valueType, ctx);
791
840
  }
792
841
  }
793
842
  return schema;
@@ -1579,6 +1628,376 @@ function resolveAndCollectObjectProps(schema, components) {
1579
1628
  return { properties, required };
1580
1629
  }
1581
1630
 
1631
+ // src/compiler/schema/queryBuilderAnalyzer.ts
1632
+ var import_typescript9 = __toESM(require("typescript"), 1);
1633
+ function analyzeQueryBuilderForSchema(methodDeclaration, checker, options = {}) {
1634
+ const body = methodDeclaration.body;
1635
+ if (!body) {
1636
+ return null;
1637
+ }
1638
+ const trackedSchema = analyzeWithVariableTracking(body, checker, options);
1639
+ if (trackedSchema) {
1640
+ return trackedSchema;
1641
+ }
1642
+ const returnStatement = findReturnStatement(body);
1643
+ if (!returnStatement) {
1644
+ return null;
1645
+ }
1646
+ const callChain = analyzeReturnExpression(returnStatement.expression);
1647
+ if (!callChain) {
1648
+ return null;
1649
+ }
1650
+ return parseQueryBuilderChain(callChain, checker, options);
1651
+ }
1652
+ function analyzeWithVariableTracking(body, checker, options) {
1653
+ let queryBuilderVar = null;
1654
+ let entityName = null;
1655
+ const selectedFields = /* @__PURE__ */ new Set();
1656
+ const includes = {};
1657
+ let isPaged = false;
1658
+ let hasReturn = false;
1659
+ for (const statement of body.statements) {
1660
+ if (import_typescript9.default.isReturnStatement(statement)) {
1661
+ hasReturn = true;
1662
+ const returnExpr = statement.expression;
1663
+ if (returnExpr && import_typescript9.default.isCallExpression(returnExpr)) {
1664
+ const callExpr = returnExpr;
1665
+ if (import_typescript9.default.isIdentifier(callExpr.expression) && queryBuilderVar) {
1666
+ const varName = callExpr.expression.text;
1667
+ if (varName === queryBuilderVar) {
1668
+ const methodName = callExpr.expression.text;
1669
+ if (methodName === "executePaged") {
1670
+ isPaged = true;
1671
+ }
1672
+ }
1673
+ }
1674
+ if (import_typescript9.default.isPropertyAccessExpression(callExpr.expression) && queryBuilderVar) {
1675
+ const propAccess = callExpr.expression;
1676
+ if (import_typescript9.default.isIdentifier(propAccess.expression) && propAccess.expression.text === queryBuilderVar) {
1677
+ const methodName = propAccess.name.text;
1678
+ if (methodName === "executePaged") {
1679
+ isPaged = true;
1680
+ }
1681
+ }
1682
+ }
1683
+ }
1684
+ continue;
1685
+ }
1686
+ if (!import_typescript9.default.isExpressionStatement(statement)) {
1687
+ if (import_typescript9.default.isVariableStatement(statement)) {
1688
+ for (const declaration of statement.declarationList.declarations) {
1689
+ if (!import_typescript9.default.isIdentifier(declaration.name)) continue;
1690
+ const varName = declaration.name.text;
1691
+ const initializer = declaration.initializer;
1692
+ if (!initializer || !import_typescript9.default.isCallExpression(initializer)) continue;
1693
+ const opInfo = extractChainedOperation(initializer);
1694
+ if (opInfo && (opInfo.operation === "selectFromEntity" || opInfo.operation === "selectFrom")) {
1695
+ queryBuilderVar = varName;
1696
+ if (opInfo.entityName) {
1697
+ entityName = opInfo.entityName;
1698
+ }
1699
+ }
1700
+ }
1701
+ }
1702
+ continue;
1703
+ }
1704
+ const expr = statement.expression;
1705
+ if (import_typescript9.default.isBinaryExpression(expr) && expr.operatorToken.kind === import_typescript9.default.SyntaxKind.EqualsToken) {
1706
+ if (!import_typescript9.default.isIdentifier(expr.left)) {
1707
+ continue;
1708
+ }
1709
+ const varName = expr.left.text;
1710
+ const rightSide = expr.right;
1711
+ if (import_typescript9.default.isCallExpression(rightSide)) {
1712
+ const opInfo = extractChainedOperation(rightSide);
1713
+ if (opInfo) {
1714
+ if (opInfo.operation === "selectFromEntity" || opInfo.operation === "selectFrom") {
1715
+ queryBuilderVar = varName;
1716
+ if (opInfo.entityName) {
1717
+ entityName = opInfo.entityName;
1718
+ }
1719
+ }
1720
+ if ((opInfo.operation === "select" || opInfo.operation === "include") && queryBuilderVar === varName) {
1721
+ if (opInfo.operation === "select") {
1722
+ for (const field of opInfo.fields || []) {
1723
+ selectedFields.add(field);
1724
+ }
1725
+ } else if (opInfo.operation === "include" && opInfo.includeArg) {
1726
+ const parsedIncludes = parseIncludeObjectLiteral(opInfo.includeArg);
1727
+ if (parsedIncludes) {
1728
+ for (const [relName, relSchema] of Object.entries(parsedIncludes)) {
1729
+ includes[relName] = relSchema;
1730
+ }
1731
+ }
1732
+ }
1733
+ }
1734
+ }
1735
+ }
1736
+ }
1737
+ }
1738
+ if (!hasReturn || !queryBuilderVar || !entityName) {
1739
+ return null;
1740
+ }
1741
+ return {
1742
+ entityName,
1743
+ selectedFields: Array.from(selectedFields),
1744
+ includes,
1745
+ isPaged
1746
+ };
1747
+ }
1748
+ function extractChainedOperation(callExpr) {
1749
+ if (import_typescript9.default.isIdentifier(callExpr.expression)) {
1750
+ const methodName2 = callExpr.expression.text;
1751
+ if (methodName2 === "selectFromEntity" || methodName2 === "selectFrom") {
1752
+ const entityArg = callExpr.arguments[0];
1753
+ let entityName = null;
1754
+ if (import_typescript9.default.isIdentifier(entityArg)) {
1755
+ entityName = entityArg.text;
1756
+ } else if (import_typescript9.default.isPropertyAccessExpression(entityArg)) {
1757
+ entityName = entityArg.name.text;
1758
+ }
1759
+ return {
1760
+ operation: methodName2 === "selectFromEntity" ? "selectFromEntity" : "selectFrom",
1761
+ fields: null,
1762
+ includeArg: null,
1763
+ entityName
1764
+ };
1765
+ }
1766
+ }
1767
+ if (!import_typescript9.default.isPropertyAccessExpression(callExpr.expression)) {
1768
+ return null;
1769
+ }
1770
+ const propAccess = callExpr.expression;
1771
+ const methodName = propAccess.name.text;
1772
+ if (methodName === "select") {
1773
+ const fields = [];
1774
+ for (const arg of callExpr.arguments) {
1775
+ if (import_typescript9.default.isStringLiteral(arg)) {
1776
+ fields.push(arg.text);
1777
+ }
1778
+ }
1779
+ return {
1780
+ operation: "select",
1781
+ fields,
1782
+ includeArg: null,
1783
+ entityName: null
1784
+ };
1785
+ }
1786
+ if (methodName === "include") {
1787
+ return {
1788
+ operation: "include",
1789
+ fields: null,
1790
+ includeArg: callExpr.arguments[0] || null,
1791
+ entityName: null
1792
+ };
1793
+ }
1794
+ return null;
1795
+ }
1796
+ function parseIncludeObjectLiteral(arg) {
1797
+ if (!import_typescript9.default.isObjectLiteralExpression(arg)) {
1798
+ return null;
1799
+ }
1800
+ const includes = {};
1801
+ for (const prop of arg.properties) {
1802
+ if (!import_typescript9.default.isPropertyAssignment(prop) || !import_typescript9.default.isIdentifier(prop.name)) {
1803
+ continue;
1804
+ }
1805
+ const relationName = prop.name.text;
1806
+ const value = prop.initializer;
1807
+ if (value.kind === import_typescript9.default.SyntaxKind.TrueKeyword) {
1808
+ includes[relationName] = true;
1809
+ } else if (import_typescript9.default.isObjectLiteralExpression(value)) {
1810
+ const nestedSchema = parseNestedInclude(value, 0);
1811
+ if (nestedSchema) {
1812
+ includes[relationName] = nestedSchema;
1813
+ }
1814
+ }
1815
+ }
1816
+ return includes;
1817
+ }
1818
+ function parseNestedInclude(obj, depth) {
1819
+ const selectedFields = [];
1820
+ const includes = {};
1821
+ for (const prop of obj.properties) {
1822
+ if (!import_typescript9.default.isPropertyAssignment(prop) || !import_typescript9.default.isIdentifier(prop.name)) {
1823
+ continue;
1824
+ }
1825
+ const propName = prop.name.text;
1826
+ const value = prop.initializer;
1827
+ if (propName === "select" && import_typescript9.default.isArrayLiteralExpression(value)) {
1828
+ for (const element of value.elements) {
1829
+ if (import_typescript9.default.isStringLiteral(element)) {
1830
+ selectedFields.push(element.text);
1831
+ }
1832
+ }
1833
+ } else if (propName === "include" && import_typescript9.default.isObjectLiteralExpression(value)) {
1834
+ const nestedIncludes = parseIncludeObjectLiteral(value);
1835
+ if (nestedIncludes) {
1836
+ for (const [relName, relSchema] of Object.entries(nestedIncludes)) {
1837
+ includes[relName] = relSchema;
1838
+ }
1839
+ }
1840
+ }
1841
+ }
1842
+ return {
1843
+ entityName: "",
1844
+ selectedFields,
1845
+ includes,
1846
+ isPaged: false
1847
+ };
1848
+ }
1849
+ function getMethodName(expression) {
1850
+ if (import_typescript9.default.isIdentifier(expression)) {
1851
+ return expression.text;
1852
+ }
1853
+ if (import_typescript9.default.isPropertyAccessExpression(expression)) {
1854
+ return expression.name.text;
1855
+ }
1856
+ return null;
1857
+ }
1858
+ function findReturnStatement(body) {
1859
+ let returnStatement = null;
1860
+ for (const statement of body.statements) {
1861
+ if (import_typescript9.default.isReturnStatement(statement)) {
1862
+ if (returnStatement !== null) {
1863
+ return null;
1864
+ }
1865
+ returnStatement = statement;
1866
+ }
1867
+ }
1868
+ return returnStatement;
1869
+ }
1870
+ function analyzeReturnExpression(expression) {
1871
+ if (!expression) {
1872
+ return null;
1873
+ }
1874
+ if (import_typescript9.default.isCallExpression(expression)) {
1875
+ return buildCallChain(expression, null);
1876
+ }
1877
+ return null;
1878
+ }
1879
+ function buildCallChain(node, parent) {
1880
+ if (import_typescript9.default.isCallExpression(node)) {
1881
+ const callNode = {
1882
+ expression: node.expression,
1883
+ methodName: getMethodName(node.expression),
1884
+ arguments: node.arguments,
1885
+ parent
1886
+ };
1887
+ if (import_typescript9.default.isPropertyAccessExpression(node.expression)) {
1888
+ return buildCallChain(node.expression.expression, callNode);
1889
+ }
1890
+ return callNode;
1891
+ }
1892
+ return parent;
1893
+ }
1894
+ function parseQueryBuilderChain(chain, checker, options) {
1895
+ if (!chain) {
1896
+ return null;
1897
+ }
1898
+ const rootNode = findSelectFromEntityCall(chain);
1899
+ if (!rootNode) {
1900
+ return null;
1901
+ }
1902
+ const entityName = extractEntityName(rootNode, checker);
1903
+ if (!entityName) {
1904
+ return null;
1905
+ }
1906
+ const selectedFields = /* @__PURE__ */ new Set();
1907
+ const includes = {};
1908
+ let isPaged = false;
1909
+ let currentNode = chain;
1910
+ while (currentNode) {
1911
+ const methodName = currentNode.methodName;
1912
+ if (methodName === "select") {
1913
+ for (const arg of currentNode.arguments) {
1914
+ if (import_typescript9.default.isStringLiteral(arg)) {
1915
+ selectedFields.add(arg.text);
1916
+ }
1917
+ }
1918
+ } else if (methodName === "include") {
1919
+ parseIncludeArgument(currentNode.arguments[0], includes, checker, options, 0);
1920
+ } else if (methodName === "executePaged") {
1921
+ isPaged = true;
1922
+ }
1923
+ currentNode = currentNode.parent;
1924
+ }
1925
+ return {
1926
+ entityName,
1927
+ selectedFields: Array.from(selectedFields),
1928
+ includes,
1929
+ isPaged
1930
+ };
1931
+ }
1932
+ function findSelectFromEntityCall(chain) {
1933
+ let currentNode = chain;
1934
+ let lastNode = null;
1935
+ while (currentNode) {
1936
+ if (currentNode.methodName === "selectFromEntity" || currentNode.methodName === "selectFrom") {
1937
+ return currentNode;
1938
+ }
1939
+ lastNode = currentNode;
1940
+ currentNode = currentNode.parent;
1941
+ }
1942
+ return lastNode;
1943
+ }
1944
+ function extractEntityName(callNode, checker) {
1945
+ if (callNode.arguments.length === 0) {
1946
+ return null;
1947
+ }
1948
+ const entityArg = callNode.arguments[0];
1949
+ if (import_typescript9.default.isIdentifier(entityArg)) {
1950
+ return entityArg.text;
1951
+ }
1952
+ if (import_typescript9.default.isPropertyAccessExpression(entityArg)) {
1953
+ return entityArg.name.text;
1954
+ }
1955
+ return null;
1956
+ }
1957
+ function parseIncludeArgument(arg, includes, checker, options, depth) {
1958
+ if (!arg) {
1959
+ return;
1960
+ }
1961
+ if (import_typescript9.default.isObjectLiteralExpression(arg)) {
1962
+ for (const prop of arg.properties) {
1963
+ if (!import_typescript9.default.isPropertyAssignment(prop) || !import_typescript9.default.isIdentifier(prop.name)) {
1964
+ continue;
1965
+ }
1966
+ const relationName = prop.name.text;
1967
+ const value = prop.initializer;
1968
+ if (value.kind === import_typescript9.default.SyntaxKind.TrueKeyword) {
1969
+ includes[relationName] = true;
1970
+ } else if (import_typescript9.default.isObjectLiteralExpression(value)) {
1971
+ const maxDepth = options.maxDepth ?? 5;
1972
+ if (depth < maxDepth) {
1973
+ const nestedSchema = parseNestedInclude(value, depth + 1);
1974
+ if (nestedSchema) {
1975
+ includes[relationName] = nestedSchema;
1976
+ }
1977
+ }
1978
+ }
1979
+ }
1980
+ }
1981
+ }
1982
+
1983
+ // src/compiler/schema/queryBuilderSchemaBuilder.ts
1984
+ var import_typescript10 = require("typescript");
1985
+ function wrapInPaginatedResult(schema) {
1986
+ return {
1987
+ type: "object",
1988
+ properties: {
1989
+ items: {
1990
+ type: "array",
1991
+ items: schema
1992
+ },
1993
+ page: { type: "integer" },
1994
+ pageSize: { type: "integer" },
1995
+ totalItems: { type: "integer" }
1996
+ },
1997
+ required: ["items", "page", "pageSize", "totalItems"]
1998
+ };
1999
+ }
2000
+
1582
2001
  // src/compiler/schema/openapi.ts
1583
2002
  var METAL_ORM_WRAPPER_NAMES2 = ["BelongsToReference", "HasOneReference", "HasManyCollection", "ManyToManyCollection"];
1584
2003
  function generateOpenAPI(controllers, checker, options = {}) {
@@ -1733,6 +2152,88 @@ function convertToOpenApiPath(basePath, path4) {
1733
2152
  }
1734
2153
  return fullPath;
1735
2154
  }
2155
+ function tryInferQueryBuilderSchema(operation, checker) {
2156
+ return analyzeQueryBuilderForSchema(operation.methodDeclaration, checker) ?? null;
2157
+ }
2158
+ function getEntityTypeFromReturnType(operation, checker) {
2159
+ const returnType = operation.returnType;
2160
+ const unwrapPromise2 = (type) => {
2161
+ const symbol2 = type.getSymbol();
2162
+ if (symbol2?.getName() === "Promise") {
2163
+ const typeArgs = type.typeArguments;
2164
+ if (typeArgs && typeArgs.length > 0) {
2165
+ return typeArgs[0];
2166
+ }
2167
+ }
2168
+ return type;
2169
+ };
2170
+ const innerType = unwrapPromise2(returnType);
2171
+ const symbol = innerType.getSymbol();
2172
+ if (symbol?.getName() === "PaginatedResult") {
2173
+ const typeArgs = innerType.typeArguments;
2174
+ if (typeArgs && typeArgs.length > 0) {
2175
+ return typeArgs[0];
2176
+ }
2177
+ }
2178
+ return null;
2179
+ }
2180
+ function filterSchemaByQueryBuilder(querySchema, operation, ctx) {
2181
+ const entityType = getEntityTypeFromReturnType(operation, ctx.checker);
2182
+ if (!entityType) {
2183
+ return {};
2184
+ }
2185
+ const entitySchema = typeToJsonSchema(entityType, ctx);
2186
+ let baseSchema = entitySchema;
2187
+ if (entitySchema.$ref && entitySchema.$ref.startsWith("#/components/schemas/")) {
2188
+ const schemaName = entitySchema.$ref.replace("#/components/schemas/", "");
2189
+ const componentSchema = ctx.components.get(schemaName);
2190
+ if (componentSchema) {
2191
+ baseSchema = componentSchema;
2192
+ }
2193
+ }
2194
+ if (!baseSchema.properties || Object.keys(baseSchema.properties).length === 0) {
2195
+ return {};
2196
+ }
2197
+ const filteredSchema = buildFilteredSchema(querySchema, baseSchema);
2198
+ if (querySchema.isPaged) {
2199
+ return wrapInPaginatedResult(filteredSchema);
2200
+ }
2201
+ return filteredSchema;
2202
+ }
2203
+ function buildFilteredSchema(querySchema, entitySchema) {
2204
+ const properties = {};
2205
+ const required = [];
2206
+ for (const field of querySchema.selectedFields) {
2207
+ if (entitySchema.properties?.[field]) {
2208
+ properties[field] = entitySchema.properties[field];
2209
+ if (entitySchema.required && entitySchema.required.includes(field)) {
2210
+ required.push(field);
2211
+ }
2212
+ }
2213
+ }
2214
+ for (const [relationName, includeSpec] of Object.entries(querySchema.includes)) {
2215
+ if (entitySchema.properties?.[relationName]) {
2216
+ properties[relationName] = {
2217
+ type: "object",
2218
+ properties: {
2219
+ id: { type: "integer" }
2220
+ },
2221
+ required: ["id"]
2222
+ };
2223
+ if (entitySchema.required && entitySchema.required.includes(relationName)) {
2224
+ required.push(relationName);
2225
+ }
2226
+ }
2227
+ }
2228
+ const schema = {
2229
+ type: "object",
2230
+ properties
2231
+ };
2232
+ if (required.length > 0) {
2233
+ schema.required = required;
2234
+ }
2235
+ return schema;
2236
+ }
1736
2237
  function buildOperation(operation, ctx, controllerConsumes) {
1737
2238
  const op = {
1738
2239
  operationId: operation.operationId,
@@ -1747,7 +2248,18 @@ function buildOperation(operation, ctx, controllerConsumes) {
1747
2248
  op.parameters = parameters;
1748
2249
  }
1749
2250
  const responseCtx = { ...ctx, mode: "response" };
1750
- const responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
2251
+ let responseSchema;
2252
+ const querySchema = tryInferQueryBuilderSchema(operation, ctx.checker);
2253
+ if (querySchema) {
2254
+ const entityType = getEntityTypeFromReturnType(operation, ctx.checker);
2255
+ if (entityType) {
2256
+ responseSchema = filterSchemaByQueryBuilder(querySchema, operation, responseCtx);
2257
+ } else {
2258
+ responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
2259
+ }
2260
+ } else {
2261
+ responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
2262
+ }
1751
2263
  const status = operation.httpMethod === "POST" ? 201 : 200;
1752
2264
  op.responses[status] = {
1753
2265
  description: status === 201 ? "Created" : "OK",
@@ -1789,12 +2301,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
1789
2301
  const declarations = typeSymbol.getDeclarations();
1790
2302
  if (!declarations || declarations.length === 0) return schema;
1791
2303
  const classDecl = declarations[0];
1792
- if (!import_typescript9.default.isClassDeclaration(classDecl)) return schema;
2304
+ if (!import_typescript11.default.isClassDeclaration(classDecl)) return schema;
1793
2305
  const result = { ...schema };
1794
2306
  const props = { ...result.properties };
1795
2307
  for (const member of classDecl.members) {
1796
- if (!import_typescript9.default.isPropertyDeclaration(member) || !member.name) continue;
1797
- const propName = import_typescript9.default.isIdentifier(member.name) ? member.name.text : null;
2308
+ if (!import_typescript11.default.isPropertyDeclaration(member) || !member.name) continue;
2309
+ const propName = import_typescript11.default.isIdentifier(member.name) ? member.name.text : null;
1798
2310
  if (!propName) continue;
1799
2311
  if (!props[propName]) continue;
1800
2312
  const frags = extractPropertySchemaFragments(ctx.checker, member);
@@ -1807,7 +2319,7 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
1807
2319
  }
1808
2320
 
1809
2321
  // src/compiler/manifest/emit.ts
1810
- var import_typescript10 = __toESM(require("typescript"), 1);
2322
+ var import_typescript12 = __toESM(require("typescript"), 1);
1811
2323
  function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
1812
2324
  const components = /* @__PURE__ */ new Map();
1813
2325
  const ctx = {
@@ -1829,7 +2341,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
1829
2341
  generator: {
1830
2342
  name: "adorn-api",
1831
2343
  version,
1832
- typescript: import_typescript10.default.version
2344
+ typescript: import_typescript12.default.version
1833
2345
  },
1834
2346
  schemas: {
1835
2347
  kind: "openapi-3.1",
@@ -2315,7 +2827,7 @@ async function isStale(params) {
2315
2827
  // src/compiler/cache/writeCache.ts
2316
2828
  var import_node_fs4 = __toESM(require("fs"), 1);
2317
2829
  var import_node_path4 = __toESM(require("path"), 1);
2318
- var import_typescript11 = __toESM(require("typescript"), 1);
2830
+ var import_typescript13 = __toESM(require("typescript"), 1);
2319
2831
  function statMtimeMs2(p) {
2320
2832
  return import_node_fs4.default.statSync(p).mtimeMs;
2321
2833
  }
@@ -2348,7 +2860,7 @@ function writeCache(params) {
2348
2860
  generator: {
2349
2861
  name: "adorn-api",
2350
2862
  version: params.adornVersion,
2351
- typescript: import_typescript11.default.version
2863
+ typescript: import_typescript13.default.version
2352
2864
  },
2353
2865
  project: {
2354
2866
  tsconfigPath: params.tsconfigAbs,
@@ -2868,7 +3380,7 @@ function partitionSchemas(schemas, graph, schemaGraph, config = {}) {
2868
3380
  complexity: totalComplexity,
2869
3381
  dependencies: []
2870
3382
  }];
2871
- recommendation = recommendation || "Single file mode (--no-split)";
3383
+ recommendation = recommendation || "Single file mode (--split not specified)";
2872
3384
  } else if (strategy === "controller") {
2873
3385
  groups = partitionByController(schemas, graph, finalConfig);
2874
3386
  } else if (strategy === "dependency") {
@@ -3101,7 +3613,7 @@ function getEdgesByRelation(graph, relation) {
3101
3613
  }
3102
3614
 
3103
3615
  // src/compiler/graph/builder.ts
3104
- var import_typescript12 = __toESM(require("typescript"), 1);
3616
+ var import_typescript14 = __toESM(require("typescript"), 1);
3105
3617
 
3106
3618
  // src/compiler/graph/schemaGraph.ts
3107
3619
  var SchemaGraph = class {
@@ -3353,7 +3865,7 @@ var SchemaGraph = class {
3353
3865
  };
3354
3866
 
3355
3867
  // src/cli.ts
3356
- var import_typescript13 = __toESM(require("typescript"), 1);
3868
+ var import_typescript15 = __toESM(require("typescript"), 1);
3357
3869
  var import_node_process2 = __toESM(require("process"), 1);
3358
3870
  var import_meta2 = {};
3359
3871
  var ADORN_VERSION = (() => {
@@ -3432,7 +3944,7 @@ function sanitizeForJson(obj) {
3432
3944
  return result;
3433
3945
  }
3434
3946
  function buildControllerGraph(controllers) {
3435
- const graph = createGraph(import_typescript13.default.version);
3947
+ const graph = createGraph(import_typescript15.default.version);
3436
3948
  const nodeMap = /* @__PURE__ */ new Map();
3437
3949
  for (const ctrl of controllers) {
3438
3950
  const nodeId = `Controller:${ctrl.className}`;
@@ -3523,7 +4035,7 @@ async function buildCommand(args) {
3523
4035
  const validationMode = validationModeIndex !== -1 ? args[validationModeIndex + 1] : "ajv-runtime";
3524
4036
  const verbose = args.includes("--verbose");
3525
4037
  const quiet = args.includes("--quiet");
3526
- const noSplit = args.includes("--no-split");
4038
+ const split = args.includes("--split");
3527
4039
  const splitStrategyIndex = args.indexOf("--split-strategy");
3528
4040
  const splitStrategy = splitStrategyIndex !== -1 ? args[splitStrategyIndex + 1] : void 0;
3529
4041
  const splitThresholdIndex = args.indexOf("--split-threshold");
@@ -3543,7 +4055,7 @@ async function buildCommand(args) {
3543
4055
  outDir: outputDir,
3544
4056
  project: projectPath,
3545
4057
  adornVersion: ADORN_VERSION,
3546
- typescriptVersion: import_typescript13.default.version
4058
+ typescriptVersion: import_typescript15.default.version
3547
4059
  });
3548
4060
  if (!stale.stale) {
3549
4061
  progress.completePhase("staleness-check");
@@ -3601,7 +4113,7 @@ async function buildCommand(args) {
3601
4113
  if (!quiet) openapiSpinner.stop();
3602
4114
  const schemaCount = Object.keys(openapi.components?.schemas || {}).length;
3603
4115
  let splitEnabled = false;
3604
- if (!noSplit && schemaCount >= splitThreshold) {
4116
+ if (split && schemaCount >= splitThreshold) {
3605
4117
  progress.verboseLog(`Schema count (${schemaCount}) >= threshold (${splitThreshold}), analyzing for auto-split...`);
3606
4118
  const graph = buildControllerGraph(controllers);
3607
4119
  const schemaGraph = new SchemaGraph(graph);
@@ -3641,9 +4153,9 @@ async function buildCommand(args) {
3641
4153
  log(` Auto-split not needed: ${partitioning.recommendation}`);
3642
4154
  }
3643
4155
  }
3644
- } else if (noSplit) {
4156
+ } else if (!split) {
3645
4157
  if (!quiet) {
3646
- log(` Splitting disabled (--no-split)`);
4158
+ log(` Splitting disabled (--split not specified)`);
3647
4159
  }
3648
4160
  } else {
3649
4161
  if (!quiet) {
@@ -3764,12 +4276,12 @@ Commands:
3764
4276
  build Generate OpenAPI and manifest from TypeScript source
3765
4277
  clean Remove generated artifacts
3766
4278
 
3767
- Options:
4279
+ Options:
3768
4280
  -p <path> Path to tsconfig.json (default: ./tsconfig.json)
3769
4281
  --output <dir> Output directory (default: .adorn)
3770
4282
  --if-stale Only rebuild if artifacts are stale
3771
4283
  --validation-mode <mode> Validation mode: none, ajv-runtime, precompiled (default: ajv-runtime)
3772
- --no-split Disable automatic schema splitting (default: auto-split enabled)
4284
+ --split Enable automatic schema splitting (default: disabled)
3773
4285
  --split-strategy <mode> Override splitting strategy: controller, dependency, size, auto (default: auto)
3774
4286
  --split-threshold <num> Schema count threshold for auto-split (default: 50)
3775
4287
  --verbose Show detailed progress information
@@ -3780,7 +4292,7 @@ Examples:
3780
4292
  adorn-api build --if-stale
3781
4293
  adorn-api build --validation-mode precompiled
3782
4294
  adorn-api build --verbose
3783
- adorn-api build --no-split # Force single file mode
4295
+ adorn-api build --split # Enable split mode
3784
4296
  adorn-api build --split-strategy controller # Force controller-based splitting
3785
4297
  adorn-api build --split-threshold 100 # Increase threshold to 100
3786
4298
  adorn-api clean