adorn-api 1.0.21 → 1.0.23

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.js CHANGED
@@ -306,7 +306,7 @@ function unwrapPromiseTypeNode(typeNode) {
306
306
  }
307
307
 
308
308
  // src/compiler/schema/openapi.ts
309
- import ts11 from "typescript";
309
+ import ts12 from "typescript";
310
310
 
311
311
  // src/compiler/schema/typeToJsonSchema.ts
312
312
  import ts7 from "typescript";
@@ -1612,7 +1612,221 @@ function resolveAndCollectObjectProps(schema, components) {
1612
1612
  }
1613
1613
 
1614
1614
  // src/compiler/schema/queryBuilderAnalyzer.ts
1615
+ import ts10 from "typescript";
1616
+
1617
+ // src/compiler/schema/serviceCallAnalyzer.ts
1615
1618
  import ts9 from "typescript";
1619
+ var ServiceCallAnalyzer = class {
1620
+ checker;
1621
+ program;
1622
+ cache = /* @__PURE__ */ new Map();
1623
+ analyzedMethods = /* @__PURE__ */ new Set();
1624
+ constructor(checker, program) {
1625
+ this.checker = checker;
1626
+ this.program = program;
1627
+ }
1628
+ /**
1629
+ * Analyzes a controller method for query builder patterns, following service calls
1630
+ */
1631
+ analyzeControllerMethod(methodDeclaration, options = {}) {
1632
+ const cacheKey = this.getMethodCacheKey(methodDeclaration);
1633
+ if (this.cache.has(cacheKey)) {
1634
+ return this.cache.get(cacheKey) ?? null;
1635
+ }
1636
+ const maxDepth = options.maxDepth ?? 3;
1637
+ const schema = this.analyzeMethodWithServiceCalls(methodDeclaration, 0, maxDepth, options);
1638
+ this.cache.set(cacheKey, schema);
1639
+ return schema;
1640
+ }
1641
+ /**
1642
+ * Recursively analyzes method with service call traversal
1643
+ */
1644
+ analyzeMethodWithServiceCalls(methodDeclaration, currentDepth, maxDepth, options) {
1645
+ if (currentDepth >= maxDepth) {
1646
+ return null;
1647
+ }
1648
+ const methodKey = this.getMethodCacheKey(methodDeclaration);
1649
+ if (this.analyzedMethods.has(methodKey)) {
1650
+ return null;
1651
+ }
1652
+ this.analyzedMethods.add(methodKey);
1653
+ const directSchema = analyzeQueryBuilderForSchema(methodDeclaration, this.checker);
1654
+ if (directSchema) {
1655
+ return directSchema;
1656
+ }
1657
+ const serviceCalls = this.findServiceCalls(methodDeclaration);
1658
+ for (const serviceCall of serviceCalls) {
1659
+ const serviceSchema = this.analyzeServiceMethod(serviceCall, currentDepth, maxDepth, options);
1660
+ if (serviceSchema) {
1661
+ return serviceSchema;
1662
+ }
1663
+ }
1664
+ return null;
1665
+ }
1666
+ /**
1667
+ * Analyzes a service method for query builder patterns
1668
+ */
1669
+ analyzeServiceMethod(serviceCall, currentDepth, maxDepth, options) {
1670
+ const directSchema = analyzeQueryBuilderForSchema(serviceCall.methodDeclaration, this.checker);
1671
+ if (directSchema) {
1672
+ return directSchema;
1673
+ }
1674
+ if (options.analyzeHelpers) {
1675
+ return this.analyzeMethodWithServiceCalls(
1676
+ serviceCall.methodDeclaration,
1677
+ currentDepth + 1,
1678
+ maxDepth,
1679
+ options
1680
+ );
1681
+ }
1682
+ return null;
1683
+ }
1684
+ /**
1685
+ * Finds service calls in a method body
1686
+ */
1687
+ findServiceCalls(methodDeclaration) {
1688
+ const serviceCalls = [];
1689
+ const body = methodDeclaration.body;
1690
+ if (!body) {
1691
+ return serviceCalls;
1692
+ }
1693
+ const visitor = (node) => {
1694
+ if (ts9.isCallExpression(node)) {
1695
+ const serviceCall = this.resolveServiceCall(node);
1696
+ if (serviceCall) {
1697
+ serviceCalls.push(serviceCall);
1698
+ }
1699
+ }
1700
+ ts9.forEachChild(node, visitor);
1701
+ };
1702
+ ts9.forEachChild(body, visitor);
1703
+ return serviceCalls;
1704
+ }
1705
+ /**
1706
+ * Resolves a call expression to a service method
1707
+ */
1708
+ resolveServiceCall(callExpression) {
1709
+ if (ts9.isPropertyAccessExpression(callExpression.expression)) {
1710
+ const propAccess = callExpression.expression;
1711
+ const methodName = propAccess.name.text;
1712
+ const objectType = this.checker.getTypeAtLocation(propAccess.expression);
1713
+ const objectSymbol = objectType.getSymbol();
1714
+ if (objectSymbol) {
1715
+ const classDeclaration = this.findClassDeclaration(objectSymbol);
1716
+ if (classDeclaration) {
1717
+ const methodDeclaration = this.findMethodDeclaration(classDeclaration, methodName);
1718
+ if (methodDeclaration) {
1719
+ return {
1720
+ serviceName: classDeclaration.name?.text || "Unknown",
1721
+ methodName,
1722
+ filePath: classDeclaration.getSourceFile().fileName,
1723
+ classDeclaration,
1724
+ methodDeclaration
1725
+ };
1726
+ }
1727
+ }
1728
+ }
1729
+ }
1730
+ if (ts9.isPropertyAccessExpression(callExpression.expression)) {
1731
+ const propAccess = callExpression.expression;
1732
+ const methodName = propAccess.name.text;
1733
+ if (ts9.isIdentifier(propAccess.expression)) {
1734
+ const className = propAccess.expression.text;
1735
+ const classSymbol = this.checker.getSymbolAtLocation(propAccess.expression);
1736
+ if (classSymbol) {
1737
+ const classDeclaration = this.findClassDeclaration(classSymbol);
1738
+ if (classDeclaration && classDeclaration.name?.text === className) {
1739
+ const methodDeclaration = this.findMethodDeclaration(classDeclaration, methodName);
1740
+ if (methodDeclaration) {
1741
+ return {
1742
+ serviceName: className,
1743
+ methodName,
1744
+ filePath: classDeclaration.getSourceFile().fileName,
1745
+ classDeclaration,
1746
+ methodDeclaration
1747
+ };
1748
+ }
1749
+ }
1750
+ }
1751
+ }
1752
+ }
1753
+ return null;
1754
+ }
1755
+ /**
1756
+ * Finds class declaration from a symbol
1757
+ */
1758
+ findClassDeclaration(symbol) {
1759
+ const declarations = symbol.getDeclarations();
1760
+ if (!declarations) return null;
1761
+ for (const declaration of declarations) {
1762
+ if (ts9.isClassDeclaration(declaration)) {
1763
+ return declaration;
1764
+ }
1765
+ }
1766
+ return null;
1767
+ }
1768
+ /**
1769
+ * Finds method declaration in a class
1770
+ */
1771
+ findMethodDeclaration(classDeclaration, methodName) {
1772
+ for (const member of classDeclaration.members) {
1773
+ if (ts9.isMethodDeclaration(member) && member.name) {
1774
+ if (ts9.isIdentifier(member.name) && member.name.text === methodName) {
1775
+ return member;
1776
+ }
1777
+ }
1778
+ }
1779
+ return null;
1780
+ }
1781
+ /**
1782
+ * Generates cache key for a method
1783
+ */
1784
+ getMethodCacheKey(methodDeclaration) {
1785
+ const sourceFile = methodDeclaration.getSourceFile();
1786
+ const className = this.getClassName(methodDeclaration);
1787
+ const methodName = methodDeclaration.name?.getText() || "unknown";
1788
+ const line = sourceFile.getLineAndCharacterOfPosition(methodDeclaration.getStart()).line;
1789
+ return `${sourceFile.fileName}:${className}:${methodName}:${line}`;
1790
+ }
1791
+ /**
1792
+ * Gets class name from method declaration
1793
+ */
1794
+ getClassName(methodDeclaration) {
1795
+ let node = methodDeclaration;
1796
+ while (node) {
1797
+ if (ts9.isClassDeclaration(node)) {
1798
+ return node.name?.text || "Unknown";
1799
+ }
1800
+ node = node.parent;
1801
+ }
1802
+ return "Unknown";
1803
+ }
1804
+ /**
1805
+ * Clears the analysis cache
1806
+ */
1807
+ clearCache() {
1808
+ this.cache.clear();
1809
+ this.analyzedMethods.clear();
1810
+ }
1811
+ /**
1812
+ * Gets cache statistics
1813
+ */
1814
+ getCacheStats() {
1815
+ return {
1816
+ cached: this.cache.size,
1817
+ analyzed: this.analyzedMethods.size
1818
+ };
1819
+ }
1820
+ };
1821
+ function analyzeControllerWithServiceCalls(methodDeclaration, checker, program, options = {}) {
1822
+ if (!program) {
1823
+ return null;
1824
+ }
1825
+ const analyzer = new ServiceCallAnalyzer(checker, program);
1826
+ return analyzer.analyzeControllerMethod(methodDeclaration, options);
1827
+ }
1828
+
1829
+ // src/compiler/schema/queryBuilderAnalyzer.ts
1616
1830
  function analyzeQueryBuilderForSchema(methodDeclaration, checker, options = {}) {
1617
1831
  const body = methodDeclaration.body;
1618
1832
  if (!body) {
@@ -1632,6 +1846,24 @@ function analyzeQueryBuilderForSchema(methodDeclaration, checker, options = {})
1632
1846
  }
1633
1847
  return parseQueryBuilderChain(callChain, checker, options);
1634
1848
  }
1849
+ function analyzeQueryBuilderWithServiceCalls(methodDeclaration, checker, program, options = {}, operationInfo) {
1850
+ let schema = analyzeQueryBuilderForSchema(methodDeclaration, checker, options);
1851
+ if (!schema && program) {
1852
+ try {
1853
+ schema = analyzeControllerWithServiceCalls(methodDeclaration, checker, program, {
1854
+ maxDepth: options.maxDepth,
1855
+ analyzeHelpers: options.analyzeHelpers
1856
+ });
1857
+ } catch (error) {
1858
+ console.warn("Service call analysis failed:", error);
1859
+ }
1860
+ }
1861
+ return {
1862
+ detected: schema !== null,
1863
+ schema,
1864
+ ...operationInfo
1865
+ };
1866
+ }
1635
1867
  function analyzeWithVariableTracking(body, checker, options) {
1636
1868
  let queryBuilderVar = null;
1637
1869
  let entityName = null;
@@ -1640,12 +1872,12 @@ function analyzeWithVariableTracking(body, checker, options) {
1640
1872
  let isPaged = false;
1641
1873
  let hasReturn = false;
1642
1874
  for (const statement of body.statements) {
1643
- if (ts9.isReturnStatement(statement)) {
1875
+ if (ts10.isReturnStatement(statement)) {
1644
1876
  hasReturn = true;
1645
1877
  const returnExpr = statement.expression;
1646
- if (returnExpr && ts9.isCallExpression(returnExpr)) {
1878
+ if (returnExpr && ts10.isCallExpression(returnExpr)) {
1647
1879
  const callExpr = returnExpr;
1648
- if (ts9.isIdentifier(callExpr.expression) && queryBuilderVar) {
1880
+ if (ts10.isIdentifier(callExpr.expression) && queryBuilderVar) {
1649
1881
  const varName = callExpr.expression.text;
1650
1882
  if (varName === queryBuilderVar) {
1651
1883
  const methodName = callExpr.expression.text;
@@ -1654,9 +1886,9 @@ function analyzeWithVariableTracking(body, checker, options) {
1654
1886
  }
1655
1887
  }
1656
1888
  }
1657
- if (ts9.isPropertyAccessExpression(callExpr.expression) && queryBuilderVar) {
1889
+ if (ts10.isPropertyAccessExpression(callExpr.expression) && queryBuilderVar) {
1658
1890
  const propAccess = callExpr.expression;
1659
- if (ts9.isIdentifier(propAccess.expression) && propAccess.expression.text === queryBuilderVar) {
1891
+ if (ts10.isIdentifier(propAccess.expression) && propAccess.expression.text === queryBuilderVar) {
1660
1892
  const methodName = propAccess.name.text;
1661
1893
  if (methodName === "executePaged") {
1662
1894
  isPaged = true;
@@ -1666,13 +1898,13 @@ function analyzeWithVariableTracking(body, checker, options) {
1666
1898
  }
1667
1899
  continue;
1668
1900
  }
1669
- if (!ts9.isExpressionStatement(statement)) {
1670
- if (ts9.isVariableStatement(statement)) {
1901
+ if (!ts10.isExpressionStatement(statement)) {
1902
+ if (ts10.isVariableStatement(statement)) {
1671
1903
  for (const declaration of statement.declarationList.declarations) {
1672
- if (!ts9.isIdentifier(declaration.name)) continue;
1904
+ if (!ts10.isIdentifier(declaration.name)) continue;
1673
1905
  const varName = declaration.name.text;
1674
1906
  const initializer = declaration.initializer;
1675
- if (!initializer || !ts9.isCallExpression(initializer)) continue;
1907
+ if (!initializer || !ts10.isCallExpression(initializer)) continue;
1676
1908
  const opInfo = extractChainedOperation(initializer);
1677
1909
  if (opInfo && (opInfo.operation === "selectFromEntity" || opInfo.operation === "selectFrom")) {
1678
1910
  queryBuilderVar = varName;
@@ -1685,13 +1917,13 @@ function analyzeWithVariableTracking(body, checker, options) {
1685
1917
  continue;
1686
1918
  }
1687
1919
  const expr = statement.expression;
1688
- if (ts9.isBinaryExpression(expr) && expr.operatorToken.kind === ts9.SyntaxKind.EqualsToken) {
1689
- if (!ts9.isIdentifier(expr.left)) {
1920
+ if (ts10.isBinaryExpression(expr) && expr.operatorToken.kind === ts10.SyntaxKind.EqualsToken) {
1921
+ if (!ts10.isIdentifier(expr.left)) {
1690
1922
  continue;
1691
1923
  }
1692
1924
  const varName = expr.left.text;
1693
1925
  const rightSide = expr.right;
1694
- if (ts9.isCallExpression(rightSide)) {
1926
+ if (ts10.isCallExpression(rightSide)) {
1695
1927
  const opInfo = extractChainedOperation(rightSide);
1696
1928
  if (opInfo) {
1697
1929
  if (opInfo.operation === "selectFromEntity" || opInfo.operation === "selectFrom") {
@@ -1729,14 +1961,14 @@ function analyzeWithVariableTracking(body, checker, options) {
1729
1961
  };
1730
1962
  }
1731
1963
  function extractChainedOperation(callExpr) {
1732
- if (ts9.isIdentifier(callExpr.expression)) {
1964
+ if (ts10.isIdentifier(callExpr.expression)) {
1733
1965
  const methodName2 = callExpr.expression.text;
1734
1966
  if (methodName2 === "selectFromEntity" || methodName2 === "selectFrom") {
1735
1967
  const entityArg = callExpr.arguments[0];
1736
1968
  let entityName = null;
1737
- if (ts9.isIdentifier(entityArg)) {
1969
+ if (ts10.isIdentifier(entityArg)) {
1738
1970
  entityName = entityArg.text;
1739
- } else if (ts9.isPropertyAccessExpression(entityArg)) {
1971
+ } else if (ts10.isPropertyAccessExpression(entityArg)) {
1740
1972
  entityName = entityArg.name.text;
1741
1973
  }
1742
1974
  return {
@@ -1747,7 +1979,7 @@ function extractChainedOperation(callExpr) {
1747
1979
  };
1748
1980
  }
1749
1981
  }
1750
- if (!ts9.isPropertyAccessExpression(callExpr.expression)) {
1982
+ if (!ts10.isPropertyAccessExpression(callExpr.expression)) {
1751
1983
  return null;
1752
1984
  }
1753
1985
  const propAccess = callExpr.expression;
@@ -1755,7 +1987,7 @@ function extractChainedOperation(callExpr) {
1755
1987
  if (methodName === "select") {
1756
1988
  const fields = [];
1757
1989
  for (const arg of callExpr.arguments) {
1758
- if (ts9.isStringLiteral(arg)) {
1990
+ if (ts10.isStringLiteral(arg)) {
1759
1991
  fields.push(arg.text);
1760
1992
  }
1761
1993
  }
@@ -1777,19 +2009,19 @@ function extractChainedOperation(callExpr) {
1777
2009
  return null;
1778
2010
  }
1779
2011
  function parseIncludeObjectLiteral(arg) {
1780
- if (!ts9.isObjectLiteralExpression(arg)) {
2012
+ if (!ts10.isObjectLiteralExpression(arg)) {
1781
2013
  return null;
1782
2014
  }
1783
2015
  const includes = {};
1784
2016
  for (const prop of arg.properties) {
1785
- if (!ts9.isPropertyAssignment(prop) || !ts9.isIdentifier(prop.name)) {
2017
+ if (!ts10.isPropertyAssignment(prop) || !ts10.isIdentifier(prop.name)) {
1786
2018
  continue;
1787
2019
  }
1788
2020
  const relationName = prop.name.text;
1789
2021
  const value = prop.initializer;
1790
- if (value.kind === ts9.SyntaxKind.TrueKeyword) {
2022
+ if (value.kind === ts10.SyntaxKind.TrueKeyword) {
1791
2023
  includes[relationName] = true;
1792
- } else if (ts9.isObjectLiteralExpression(value)) {
2024
+ } else if (ts10.isObjectLiteralExpression(value)) {
1793
2025
  const nestedSchema = parseNestedInclude(value, 0);
1794
2026
  if (nestedSchema) {
1795
2027
  includes[relationName] = nestedSchema;
@@ -1802,18 +2034,18 @@ function parseNestedInclude(obj, depth) {
1802
2034
  const selectedFields = [];
1803
2035
  const includes = {};
1804
2036
  for (const prop of obj.properties) {
1805
- if (!ts9.isPropertyAssignment(prop) || !ts9.isIdentifier(prop.name)) {
2037
+ if (!ts10.isPropertyAssignment(prop) || !ts10.isIdentifier(prop.name)) {
1806
2038
  continue;
1807
2039
  }
1808
2040
  const propName = prop.name.text;
1809
2041
  const value = prop.initializer;
1810
- if (propName === "select" && ts9.isArrayLiteralExpression(value)) {
2042
+ if (propName === "select" && ts10.isArrayLiteralExpression(value)) {
1811
2043
  for (const element of value.elements) {
1812
- if (ts9.isStringLiteral(element)) {
2044
+ if (ts10.isStringLiteral(element)) {
1813
2045
  selectedFields.push(element.text);
1814
2046
  }
1815
2047
  }
1816
- } else if (propName === "include" && ts9.isObjectLiteralExpression(value)) {
2048
+ } else if (propName === "include" && ts10.isObjectLiteralExpression(value)) {
1817
2049
  const nestedIncludes = parseIncludeObjectLiteral(value);
1818
2050
  if (nestedIncludes) {
1819
2051
  for (const [relName, relSchema] of Object.entries(nestedIncludes)) {
@@ -1830,10 +2062,10 @@ function parseNestedInclude(obj, depth) {
1830
2062
  };
1831
2063
  }
1832
2064
  function getMethodName(expression) {
1833
- if (ts9.isIdentifier(expression)) {
2065
+ if (ts10.isIdentifier(expression)) {
1834
2066
  return expression.text;
1835
2067
  }
1836
- if (ts9.isPropertyAccessExpression(expression)) {
2068
+ if (ts10.isPropertyAccessExpression(expression)) {
1837
2069
  return expression.name.text;
1838
2070
  }
1839
2071
  return null;
@@ -1841,7 +2073,7 @@ function getMethodName(expression) {
1841
2073
  function findReturnStatement(body) {
1842
2074
  let returnStatement = null;
1843
2075
  for (const statement of body.statements) {
1844
- if (ts9.isReturnStatement(statement)) {
2076
+ if (ts10.isReturnStatement(statement)) {
1845
2077
  if (returnStatement !== null) {
1846
2078
  return null;
1847
2079
  }
@@ -1854,20 +2086,20 @@ function analyzeReturnExpression(expression) {
1854
2086
  if (!expression) {
1855
2087
  return null;
1856
2088
  }
1857
- if (ts9.isCallExpression(expression)) {
2089
+ if (ts10.isCallExpression(expression)) {
1858
2090
  return buildCallChain(expression, null);
1859
2091
  }
1860
2092
  return null;
1861
2093
  }
1862
2094
  function buildCallChain(node, parent) {
1863
- if (ts9.isCallExpression(node)) {
2095
+ if (ts10.isCallExpression(node)) {
1864
2096
  const callNode = {
1865
2097
  expression: node.expression,
1866
2098
  methodName: getMethodName(node.expression),
1867
2099
  arguments: node.arguments,
1868
2100
  parent
1869
2101
  };
1870
- if (ts9.isPropertyAccessExpression(node.expression)) {
2102
+ if (ts10.isPropertyAccessExpression(node.expression)) {
1871
2103
  return buildCallChain(node.expression.expression, callNode);
1872
2104
  }
1873
2105
  return callNode;
@@ -1894,7 +2126,7 @@ function parseQueryBuilderChain(chain, checker, options) {
1894
2126
  const methodName = currentNode.methodName;
1895
2127
  if (methodName === "select") {
1896
2128
  for (const arg of currentNode.arguments) {
1897
- if (ts9.isStringLiteral(arg)) {
2129
+ if (ts10.isStringLiteral(arg)) {
1898
2130
  selectedFields.add(arg.text);
1899
2131
  }
1900
2132
  }
@@ -1929,10 +2161,10 @@ function extractEntityName(callNode, checker) {
1929
2161
  return null;
1930
2162
  }
1931
2163
  const entityArg = callNode.arguments[0];
1932
- if (ts9.isIdentifier(entityArg)) {
2164
+ if (ts10.isIdentifier(entityArg)) {
1933
2165
  return entityArg.text;
1934
2166
  }
1935
- if (ts9.isPropertyAccessExpression(entityArg)) {
2167
+ if (ts10.isPropertyAccessExpression(entityArg)) {
1936
2168
  return entityArg.name.text;
1937
2169
  }
1938
2170
  return null;
@@ -1941,16 +2173,16 @@ function parseIncludeArgument(arg, includes, checker, options, depth) {
1941
2173
  if (!arg) {
1942
2174
  return;
1943
2175
  }
1944
- if (ts9.isObjectLiteralExpression(arg)) {
2176
+ if (ts10.isObjectLiteralExpression(arg)) {
1945
2177
  for (const prop of arg.properties) {
1946
- if (!ts9.isPropertyAssignment(prop) || !ts9.isIdentifier(prop.name)) {
2178
+ if (!ts10.isPropertyAssignment(prop) || !ts10.isIdentifier(prop.name)) {
1947
2179
  continue;
1948
2180
  }
1949
2181
  const relationName = prop.name.text;
1950
2182
  const value = prop.initializer;
1951
- if (value.kind === ts9.SyntaxKind.TrueKeyword) {
2183
+ if (value.kind === ts10.SyntaxKind.TrueKeyword) {
1952
2184
  includes[relationName] = true;
1953
- } else if (ts9.isObjectLiteralExpression(value)) {
2185
+ } else if (ts10.isObjectLiteralExpression(value)) {
1954
2186
  const maxDepth = options.maxDepth ?? 5;
1955
2187
  if (depth < maxDepth) {
1956
2188
  const nestedSchema = parseNestedInclude(value, depth + 1);
@@ -1993,7 +2225,12 @@ function generateOpenAPI(controllers, checker, options = {}) {
1993
2225
  mode: "response"
1994
2226
  };
1995
2227
  const paths = {};
1996
- const { onProgress } = options;
2228
+ const { onProgress, onQueryBuilderProgress } = options;
2229
+ let totalOperations = 0;
2230
+ for (const controller of controllers) {
2231
+ totalOperations += controller.operations.length;
2232
+ }
2233
+ let currentOperation = 0;
1997
2234
  for (let i = 0; i < controllers.length; i++) {
1998
2235
  const controller = controllers[i];
1999
2236
  if (onProgress) {
@@ -2005,6 +2242,34 @@ function generateOpenAPI(controllers, checker, options = {}) {
2005
2242
  paths[fullPath] = {};
2006
2243
  }
2007
2244
  const method = operation.httpMethod.toLowerCase();
2245
+ const analysisResult = analyzeQueryBuilderWithServiceCalls(
2246
+ operation.methodDeclaration,
2247
+ checker,
2248
+ null,
2249
+ // TODO: Pass program when available
2250
+ {},
2251
+ {
2252
+ methodName: operation.operationId,
2253
+ httpMethod: operation.httpMethod,
2254
+ path: operation.path,
2255
+ operationId: operation.operationId
2256
+ }
2257
+ );
2258
+ if (onQueryBuilderProgress) {
2259
+ currentOperation++;
2260
+ onQueryBuilderProgress({
2261
+ controller: controller.className,
2262
+ operation: operation.operationId,
2263
+ method: operation.httpMethod,
2264
+ path: operation.path,
2265
+ queryBuilderDetected: analysisResult.detected,
2266
+ entityName: analysisResult.schema?.entityName,
2267
+ selectedFields: analysisResult.schema?.selectedFields,
2268
+ isPaged: analysisResult.schema?.isPaged,
2269
+ current: currentOperation,
2270
+ total: totalOperations
2271
+ });
2272
+ }
2008
2273
  paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
2009
2274
  }
2010
2275
  }
@@ -2284,12 +2549,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
2284
2549
  const declarations = typeSymbol.getDeclarations();
2285
2550
  if (!declarations || declarations.length === 0) return schema;
2286
2551
  const classDecl = declarations[0];
2287
- if (!ts11.isClassDeclaration(classDecl)) return schema;
2552
+ if (!ts12.isClassDeclaration(classDecl)) return schema;
2288
2553
  const result = { ...schema };
2289
2554
  const props = { ...result.properties };
2290
2555
  for (const member of classDecl.members) {
2291
- if (!ts11.isPropertyDeclaration(member) || !member.name) continue;
2292
- const propName = ts11.isIdentifier(member.name) ? member.name.text : null;
2556
+ if (!ts12.isPropertyDeclaration(member) || !member.name) continue;
2557
+ const propName = ts12.isIdentifier(member.name) ? member.name.text : null;
2293
2558
  if (!propName) continue;
2294
2559
  if (!props[propName]) continue;
2295
2560
  const frags = extractPropertySchemaFragments(ctx.checker, member);
@@ -2302,7 +2567,7 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
2302
2567
  }
2303
2568
 
2304
2569
  // src/compiler/manifest/emit.ts
2305
- import ts12 from "typescript";
2570
+ import ts13 from "typescript";
2306
2571
  function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
2307
2572
  const components = /* @__PURE__ */ new Map();
2308
2573
  const ctx = {
@@ -2324,7 +2589,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
2324
2589
  generator: {
2325
2590
  name: "adorn-api",
2326
2591
  version,
2327
- typescript: ts12.version
2592
+ typescript: ts13.version
2328
2593
  },
2329
2594
  schemas: {
2330
2595
  kind: "openapi-3.1",
@@ -2809,7 +3074,7 @@ async function isStale(params) {
2809
3074
  // src/compiler/cache/writeCache.ts
2810
3075
  import fs3 from "fs";
2811
3076
  import path3 from "path";
2812
- import ts13 from "typescript";
3077
+ import ts14 from "typescript";
2813
3078
  function statMtimeMs2(p) {
2814
3079
  return fs3.statSync(p).mtimeMs;
2815
3080
  }
@@ -2842,7 +3107,7 @@ function writeCache(params) {
2842
3107
  generator: {
2843
3108
  name: "adorn-api",
2844
3109
  version: params.adornVersion,
2845
- typescript: ts13.version
3110
+ typescript: ts14.version
2846
3111
  },
2847
3112
  project: {
2848
3113
  tsconfigPath: params.tsconfigAbs,
@@ -3004,6 +3269,8 @@ var Spinner = class {
3004
3269
  current = 0;
3005
3270
  total = 0;
3006
3271
  customStatus;
3272
+ frameIndex = 0;
3273
+ lastLineLength = 0;
3007
3274
  constructor(message = "") {
3008
3275
  this.message = message;
3009
3276
  }
@@ -3011,9 +3278,8 @@ var Spinner = class {
3011
3278
  * Start the spinner.
3012
3279
  */
3013
3280
  start() {
3014
- let frameIndex = 0;
3015
3281
  this.interval = setInterval(() => {
3016
- const frame = this.frames[frameIndex];
3282
+ const frame = this.frames[this.frameIndex];
3017
3283
  let output;
3018
3284
  if (this.customStatus) {
3019
3285
  output = `\r${frame} ${this.customStatus}`;
@@ -3022,11 +3288,12 @@ var Spinner = class {
3022
3288
  } else {
3023
3289
  output = `\r${frame} ${this.message}`;
3024
3290
  }
3291
+ this.lastLineLength = output.length - 1;
3025
3292
  process.stdout.write(output);
3026
3293
  if (process.stdout.writable) {
3027
3294
  process.stdout.write("");
3028
3295
  }
3029
- frameIndex = (frameIndex + 1) % this.frames.length;
3296
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
3030
3297
  }, 80);
3031
3298
  }
3032
3299
  /**
@@ -3041,8 +3308,12 @@ var Spinner = class {
3041
3308
  */
3042
3309
  setStatus(status) {
3043
3310
  this.customStatus = status;
3044
- const frame = this.frames[this.current];
3045
- process.stdout.write(`\r${frame} ${status}`);
3311
+ const frame = this.frames[this.frameIndex];
3312
+ const output = `\r${frame} ${status}`;
3313
+ const clearLength = Math.max(this.lastLineLength, output.length - 1);
3314
+ process.stdout.write("\r" + " ".repeat(clearLength) + "\r");
3315
+ this.lastLineLength = output.length - 1;
3316
+ process.stdout.write(output);
3046
3317
  if (process.stdout.writable) {
3047
3318
  process.stdout.write("");
3048
3319
  }
@@ -3061,7 +3332,7 @@ var Spinner = class {
3061
3332
  clearInterval(this.interval);
3062
3333
  this.interval = void 0;
3063
3334
  }
3064
- process.stdout.write("\r" + " ".repeat(60) + "\r");
3335
+ process.stdout.write("\r" + " ".repeat(this.lastLineLength) + "\r");
3065
3336
  if (completedMessage) {
3066
3337
  process.stdout.write(completedMessage + "\n");
3067
3338
  }
@@ -3595,7 +3866,7 @@ function getEdgesByRelation(graph, relation) {
3595
3866
  }
3596
3867
 
3597
3868
  // src/compiler/graph/builder.ts
3598
- import ts14 from "typescript";
3869
+ import ts15 from "typescript";
3599
3870
 
3600
3871
  // src/compiler/graph/schemaGraph.ts
3601
3872
  var SchemaGraph = class {
@@ -3847,7 +4118,7 @@ var SchemaGraph = class {
3847
4118
  };
3848
4119
 
3849
4120
  // src/cli.ts
3850
- import ts15 from "typescript";
4121
+ import ts16 from "typescript";
3851
4122
  import process2 from "process";
3852
4123
  var ADORN_VERSION = (() => {
3853
4124
  const tryReadPackageJson = (filePath) => {
@@ -3925,7 +4196,7 @@ function sanitizeForJson(obj) {
3925
4196
  return result;
3926
4197
  }
3927
4198
  function buildControllerGraph(controllers) {
3928
- const graph = createGraph(ts15.version);
4199
+ const graph = createGraph(ts16.version);
3929
4200
  const nodeMap = /* @__PURE__ */ new Map();
3930
4201
  for (const ctrl of controllers) {
3931
4202
  const nodeId = `Controller:${ctrl.className}`;
@@ -4017,6 +4288,7 @@ async function buildCommand(args) {
4017
4288
  const verbose = args.includes("--verbose");
4018
4289
  const quiet = args.includes("--quiet");
4019
4290
  const split = args.includes("--split");
4291
+ const showQueryBuilder = args.includes("--show-query-builder");
4020
4292
  const splitStrategyIndex = args.indexOf("--split-strategy");
4021
4293
  const splitStrategy = splitStrategyIndex !== -1 ? args[splitStrategyIndex + 1] : void 0;
4022
4294
  const splitThresholdIndex = args.indexOf("--split-threshold");
@@ -4036,7 +4308,7 @@ async function buildCommand(args) {
4036
4308
  outDir: outputDir,
4037
4309
  project: projectPath,
4038
4310
  adornVersion: ADORN_VERSION,
4039
- typescriptVersion: ts15.version
4311
+ typescriptVersion: ts16.version
4040
4312
  });
4041
4313
  if (!stale.stale) {
4042
4314
  progress.completePhase("staleness-check");
@@ -4077,6 +4349,12 @@ async function buildCommand(args) {
4077
4349
  }
4078
4350
  }
4079
4351
  progress.startPhase("openapi", "Generating OpenAPI schema");
4352
+ const queryBuilderStats = {
4353
+ totalOperations,
4354
+ detected: 0,
4355
+ fallback: 0,
4356
+ operations: []
4357
+ };
4080
4358
  const openapiSpinner = new Spinner("Processing schemas");
4081
4359
  if (!quiet) openapiSpinner.start();
4082
4360
  const openapi = generateOpenAPI(controllers, checker, {
@@ -4086,6 +4364,31 @@ async function buildCommand(args) {
4086
4364
  if (!quiet) {
4087
4365
  openapiSpinner.setStatus(`${message} (${current}/${total})`);
4088
4366
  }
4367
+ },
4368
+ onQueryBuilderProgress: (info) => {
4369
+ if (info.queryBuilderDetected) {
4370
+ queryBuilderStats.detected++;
4371
+ } else {
4372
+ queryBuilderStats.fallback++;
4373
+ }
4374
+ queryBuilderStats.operations.push({
4375
+ operationId: info.operation,
4376
+ method: info.method,
4377
+ path: info.path,
4378
+ detected: info.queryBuilderDetected,
4379
+ entityName: info.entityName,
4380
+ selectedFields: info.selectedFields,
4381
+ isPaged: info.isPaged
4382
+ });
4383
+ if (showQueryBuilder || verbose) {
4384
+ if (info.queryBuilderDetected) {
4385
+ const fieldsStr = info.selectedFields && info.selectedFields.length > 0 ? ` [select: ${info.selectedFields.join(",")}]` : "";
4386
+ const pagedStr = info.isPaged ? " (paged)" : "";
4387
+ progress.verboseLog(` \u2713 Query builder: ${info.method} ${info.path} \u2192 ${info.entityName}${fieldsStr}${pagedStr}`);
4388
+ } else {
4389
+ progress.verboseLog(` \u25CB No query builder: ${info.method} ${info.path} \u2192 using full entity schema`);
4390
+ }
4391
+ }
4089
4392
  }
4090
4393
  });
4091
4394
  if (!quiet) {
@@ -4144,6 +4447,17 @@ async function buildCommand(args) {
4144
4447
  }
4145
4448
  }
4146
4449
  progress.completePhase("openapi", `Generated ${schemaCount} schema(s)${splitEnabled ? " (split into groups)" : ""}`);
4450
+ if (showQueryBuilder && totalOperations > 0) {
4451
+ log("");
4452
+ log("Query Builder Analysis:");
4453
+ log(` Operations analyzed: ${totalOperations}`);
4454
+ log(` Patterns detected: ${queryBuilderStats.detected} (${Math.round(queryBuilderStats.detected / totalOperations * 100)}%)`);
4455
+ log(` Full schemas used: ${queryBuilderStats.fallback} (${Math.round(queryBuilderStats.fallback / totalOperations * 100)}%)`);
4456
+ if (queryBuilderStats.detected > 0) {
4457
+ const totalFields = queryBuilderStats.operations.filter((op) => op.detected && op.selectedFields).reduce((sum, op) => sum + (op.selectedFields?.length || 0), 0);
4458
+ log(` Fields selected: ${totalFields} total (avg ${Math.round(totalFields / queryBuilderStats.detected)} per query)`);
4459
+ }
4460
+ }
4147
4461
  progress.startPhase("manifest", "Generating manifest");
4148
4462
  const manifest = generateManifest(controllers, checker, ADORN_VERSION, validationMode);
4149
4463
  progress.completePhase("manifest");
@@ -4225,7 +4539,12 @@ async function buildCommand(args) {
4225
4539
  schemas: schemaCount,
4226
4540
  sourceFiles: projectSourceFiles.length,
4227
4541
  artifactsWritten: artifacts.map((a) => a.name),
4228
- splitEnabled
4542
+ splitEnabled,
4543
+ queryBuilder: {
4544
+ detected: queryBuilderStats.detected,
4545
+ fallback: queryBuilderStats.fallback,
4546
+ total: totalOperations
4547
+ }
4229
4548
  };
4230
4549
  progress.printSummary(stats);
4231
4550
  progress.printArtifacts(artifacts);
@@ -4253,30 +4572,32 @@ if (command === "build") {
4253
4572
  console.log(`
4254
4573
  adorn-api CLI v${ADORN_VERSION}
4255
4574
 
4256
- Commands:
4257
- build Generate OpenAPI and manifest from TypeScript source
4258
- clean Remove generated artifacts
4575
+ Commands:
4576
+ build Generate OpenAPI and manifest from TypeScript source
4577
+ clean Remove generated artifacts
4259
4578
 
4260
- Options:
4261
- -p <path> Path to tsconfig.json (default: ./tsconfig.json)
4262
- --output <dir> Output directory (default: .adorn)
4263
- --if-stale Only rebuild if artifacts are stale
4264
- --validation-mode <mode> Validation mode: none, ajv-runtime, precompiled (default: ajv-runtime)
4265
- --split Enable automatic schema splitting (default: disabled)
4266
- --split-strategy <mode> Override splitting strategy: controller, dependency, size, auto (default: auto)
4267
- --split-threshold <num> Schema count threshold for auto-split (default: 50)
4268
- --verbose Show detailed progress information
4269
- --quiet Suppress non-essential output
4579
+ Options:
4580
+ -p <path> Path to tsconfig.json (default: ./tsconfig.json)
4581
+ --output <dir> Output directory (default: .adorn)
4582
+ --if-stale Only rebuild if artifacts are stale
4583
+ --validation-mode <mode> Validation mode: none, ajv-runtime, precompiled (default: ajv-runtime)
4584
+ --split Enable automatic schema splitting (default: disabled)
4585
+ --split-strategy <mode> Override splitting strategy: controller, dependency, size, auto (default: auto)
4586
+ --split-threshold <num> Schema count threshold for auto-split (default: 50)
4587
+ --verbose Show detailed progress information
4588
+ --quiet Suppress non-essential output
4589
+ --show-query-builder Show query builder inspection details and statistics
4270
4590
 
4271
- Examples:
4272
- adorn-api build -p ./tsconfig.json --output .adorn
4273
- adorn-api build --if-stale
4274
- adorn-api build --validation-mode precompiled
4275
- adorn-api build --verbose
4276
- adorn-api build --split # Enable split mode
4277
- adorn-api build --split-strategy controller # Force controller-based splitting
4278
- adorn-api build --split-threshold 100 # Increase threshold to 100
4279
- adorn-api clean
4280
- `);
4591
+ Examples:
4592
+ adorn-api build -p ./tsconfig.json --output .adorn
4593
+ adorn-api build --if-stale
4594
+ adorn-api build --validation-mode precompiled
4595
+ adorn-api build --verbose
4596
+ adorn-api build --show-query-builder # Show query builder analysis details
4597
+ adorn-api build --split # Enable split mode
4598
+ adorn-api build --split-strategy controller # Force controller-based splitting
4599
+ adorn-api build --split-threshold 100 # Increase threshold to 100
4600
+ adorn-api clean
4601
+ `);
4281
4602
  }
4282
4603
  //# sourceMappingURL=cli.js.map