@dudousxd/nestjs-codegen 0.25.1 → 0.26.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.
- package/CHANGELOG.md +30 -0
- package/dist/cli/main.cjs +791 -208
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +796 -211
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.cjs.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/{index-Ce3YnfwS.d.cts → index-Bbw73Kkb.d.cts} +8 -1
- package/dist/{index-Ce3YnfwS.d.ts → index-Bbw73Kkb.d.ts} +8 -1
- package/dist/index.cjs +794 -211
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +799 -214
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +833 -252
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +835 -252
- package/dist/nest/index.js.map +1 -1
- package/package.json +13 -9
package/dist/nest/index.cjs
CHANGED
|
@@ -187,10 +187,10 @@ var import_fast_glob3 = __toESM(require("fast-glob"), 1);
|
|
|
187
187
|
var import_node_fs = require("fs");
|
|
188
188
|
var import_node_path3 = require("path");
|
|
189
189
|
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
190
|
-
var
|
|
190
|
+
var import_ts_morph11 = require("ts-morph");
|
|
191
191
|
|
|
192
192
|
// src/discovery/dto-type-resolver.ts
|
|
193
|
-
var
|
|
193
|
+
var import_ts_morph10 = require("ts-morph");
|
|
194
194
|
|
|
195
195
|
// src/discovery/dto-to-ir.ts
|
|
196
196
|
var import_ts_morph2 = require("ts-morph");
|
|
@@ -1718,6 +1718,672 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
1718
1718
|
return fields;
|
|
1719
1719
|
}
|
|
1720
1720
|
|
|
1721
|
+
// src/discovery/pipe-schema.ts
|
|
1722
|
+
var import_ts_morph9 = require("ts-morph");
|
|
1723
|
+
|
|
1724
|
+
// src/discovery/schema-type-inference.ts
|
|
1725
|
+
var import_ts_morph7 = require("ts-morph");
|
|
1726
|
+
|
|
1727
|
+
// src/discovery/schema-type-program.ts
|
|
1728
|
+
var import_ts_morph6 = require("ts-morph");
|
|
1729
|
+
var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
|
|
1730
|
+
var SchemaTypeProgram = class {
|
|
1731
|
+
constructor(discovery) {
|
|
1732
|
+
this.discovery = discovery;
|
|
1733
|
+
const base = discovery.getCompilerOptions();
|
|
1734
|
+
this.options = {
|
|
1735
|
+
...base,
|
|
1736
|
+
// A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
|
|
1737
|
+
// `Promise` for the schema libraries' declarations to lean on.
|
|
1738
|
+
target: base.target ?? import_ts_morph6.ts.ScriptTarget.ES2022,
|
|
1739
|
+
// With neither set, resolve the way a bundler does: package `exports`
|
|
1740
|
+
// (`zod/v4`) and extensionless or `.js` relative imports all resolve.
|
|
1741
|
+
...base.module === void 0 && base.moduleResolution === void 0 ? { module: import_ts_morph6.ts.ModuleKind.ESNext, moduleResolution: import_ts_morph6.ts.ModuleResolutionKind.Bundler } : {},
|
|
1742
|
+
// Without it `.nullable()` and `.optional()` would erase to the bare type.
|
|
1743
|
+
strictNullChecks: true,
|
|
1744
|
+
// No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
|
|
1745
|
+
// need them, and they are a hundred-odd files of program to build.
|
|
1746
|
+
types: [],
|
|
1747
|
+
noEmit: true
|
|
1748
|
+
};
|
|
1749
|
+
this.project = new import_ts_morph6.Project({
|
|
1750
|
+
skipAddingFilesFromTsConfig: true,
|
|
1751
|
+
compilerOptions: this.options
|
|
1752
|
+
});
|
|
1753
|
+
for (const sf of discovery.getSourceFiles()) this.request(sf.getFilePath(), { schema: true });
|
|
1754
|
+
this.flush();
|
|
1755
|
+
}
|
|
1756
|
+
project;
|
|
1757
|
+
options;
|
|
1758
|
+
/** Parses a file that is not in discovery's Project, without adding it there. */
|
|
1759
|
+
scratch = new import_ts_morph6.Project({
|
|
1760
|
+
skipAddingFilesFromTsConfig: true,
|
|
1761
|
+
skipLoadingLibFiles: true,
|
|
1762
|
+
skipFileDependencyResolution: true
|
|
1763
|
+
});
|
|
1764
|
+
needs = /* @__PURE__ */ new Map();
|
|
1765
|
+
/** Pruned text per file, as last written into {@link project}. */
|
|
1766
|
+
written = /* @__PURE__ */ new Map();
|
|
1767
|
+
/**
|
|
1768
|
+
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
1769
|
+
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
1770
|
+
* program once.
|
|
1771
|
+
*/
|
|
1772
|
+
sourceFileFor(sf) {
|
|
1773
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
1774
|
+
this.flush();
|
|
1775
|
+
return this.project.getSourceFile(sf.getFilePath());
|
|
1776
|
+
}
|
|
1777
|
+
// -------------------------------------------------------------------------
|
|
1778
|
+
queue = [];
|
|
1779
|
+
request(path, want) {
|
|
1780
|
+
let needs = this.needs.get(path);
|
|
1781
|
+
const isNew = !needs;
|
|
1782
|
+
needs ??= { schema: false, all: false, names: /* @__PURE__ */ new Set() };
|
|
1783
|
+
let grew = isNew;
|
|
1784
|
+
if (want.schema && !needs.schema) {
|
|
1785
|
+
needs.schema = true;
|
|
1786
|
+
grew = true;
|
|
1787
|
+
}
|
|
1788
|
+
if (want.all && !needs.all) {
|
|
1789
|
+
needs.all = true;
|
|
1790
|
+
grew = true;
|
|
1791
|
+
}
|
|
1792
|
+
for (const name of want.names ?? []) {
|
|
1793
|
+
if (!needs.names.has(name)) {
|
|
1794
|
+
needs.names.add(name);
|
|
1795
|
+
grew = true;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
this.needs.set(path, needs);
|
|
1799
|
+
if (grew) this.queue.push(path);
|
|
1800
|
+
}
|
|
1801
|
+
/** Run the worklist to a fixpoint, then write every changed file into the program. */
|
|
1802
|
+
flush() {
|
|
1803
|
+
const changed = /* @__PURE__ */ new Map();
|
|
1804
|
+
while (this.queue.length > 0) {
|
|
1805
|
+
const path = this.queue.pop();
|
|
1806
|
+
const original = this.original(path);
|
|
1807
|
+
if (!original) continue;
|
|
1808
|
+
const needs = this.needs.get(path);
|
|
1809
|
+
const text = this.prune(original, needs);
|
|
1810
|
+
if (this.written.get(path) !== text) changed.set(path, text);
|
|
1811
|
+
}
|
|
1812
|
+
for (const [path, text] of changed) {
|
|
1813
|
+
this.project.createSourceFile(path, text, { overwrite: true });
|
|
1814
|
+
this.written.set(path, text);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
/** The file as discovery sees it (its in-memory text), else as it is on disk. */
|
|
1818
|
+
original(path) {
|
|
1819
|
+
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
1823
|
+
* requesting from each kept one's target the names it brings in.
|
|
1824
|
+
*/
|
|
1825
|
+
prune(sf, needs) {
|
|
1826
|
+
const locals = topLevelDeclarations(sf);
|
|
1827
|
+
const roots = [];
|
|
1828
|
+
if (needs.all) roots.push(...sf.getStatements());
|
|
1829
|
+
if (needs.schema) roots.push(...schemaPositions(sf));
|
|
1830
|
+
const reexported = /* @__PURE__ */ new Map();
|
|
1831
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
1832
|
+
const spec = exp.getModuleSpecifierValue();
|
|
1833
|
+
if (spec === void 0) {
|
|
1834
|
+
for (const named of exp.getNamedExports()) {
|
|
1835
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
1836
|
+
if (needs.all || needs.names.has(exposed)) roots.push(named.getNameNode());
|
|
1837
|
+
}
|
|
1838
|
+
continue;
|
|
1839
|
+
}
|
|
1840
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) continue;
|
|
1841
|
+
for (const named of exp.getNamedExports()) {
|
|
1842
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
1843
|
+
const list = reexported.get(exposed) ?? [];
|
|
1844
|
+
list.push({ from: spec, name: named.getNameNode().getText() });
|
|
1845
|
+
reexported.set(exposed, list);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
const unresolvedNames = [];
|
|
1849
|
+
for (const name of needs.names) {
|
|
1850
|
+
const decls = name === "default" ? defaultExport(sf) : locals.get(name) ?? [];
|
|
1851
|
+
if (decls.length > 0) roots.push(...decls);
|
|
1852
|
+
else if (!reexported.has(name)) unresolvedNames.push(name);
|
|
1853
|
+
}
|
|
1854
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
1855
|
+
const queue = [...roots];
|
|
1856
|
+
const seen = new Set(queue);
|
|
1857
|
+
while (queue.length > 0) {
|
|
1858
|
+
const node = queue.pop();
|
|
1859
|
+
const ids = import_ts_morph6.Node.isIdentifier(node) ? [node] : node.getDescendantsOfKind(import_ts_morph6.ts.SyntaxKind.Identifier);
|
|
1860
|
+
for (const id of ids) {
|
|
1861
|
+
const name = id.getText();
|
|
1862
|
+
if (referenced.has(name)) continue;
|
|
1863
|
+
referenced.add(name);
|
|
1864
|
+
for (const local of locals.get(name) ?? []) {
|
|
1865
|
+
if (seen.has(local)) continue;
|
|
1866
|
+
seen.add(local);
|
|
1867
|
+
queue.push(local);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
const chars = sf.getFullText().split("");
|
|
1872
|
+
const blank = (node) => {
|
|
1873
|
+
for (let i = node.getStart(); i < node.getEnd(); i++) {
|
|
1874
|
+
if (chars[i] !== "\n" && chars[i] !== "\r") chars[i] = " ";
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
1878
|
+
const target = this.resolve(decl.getModuleSpecifierValue(), sf);
|
|
1879
|
+
const namespace = decl.getNamespaceImport()?.getText();
|
|
1880
|
+
const def = decl.getDefaultImport()?.getText();
|
|
1881
|
+
const named = decl.getNamedImports().map((n) => ({
|
|
1882
|
+
local: (n.getAliasNode() ?? n.getNameNode()).getText(),
|
|
1883
|
+
imported: n.getName()
|
|
1884
|
+
})).filter((n) => referenced.has(n.local));
|
|
1885
|
+
const wantsNamespace = namespace !== void 0 && referenced.has(namespace);
|
|
1886
|
+
const wantsDefault = def !== void 0 && referenced.has(def);
|
|
1887
|
+
if (!wantsNamespace && !wantsDefault && named.length === 0) {
|
|
1888
|
+
blank(decl);
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
if (target) {
|
|
1892
|
+
this.request(target, {
|
|
1893
|
+
all: wantsNamespace,
|
|
1894
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
1899
|
+
const spec = exp.getModuleSpecifierValue();
|
|
1900
|
+
if (spec === void 0) continue;
|
|
1901
|
+
const target = this.resolve(spec, sf);
|
|
1902
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
1903
|
+
if (!needs.all && unresolvedNames.length === 0) {
|
|
1904
|
+
blank(exp);
|
|
1905
|
+
} else if (target) {
|
|
1906
|
+
this.request(target, { all: needs.all, names: unresolvedNames });
|
|
1907
|
+
}
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
const wanted = exp.getNamedExports().filter(
|
|
1911
|
+
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
1912
|
+
).map((n) => n.getNameNode().getText());
|
|
1913
|
+
if (wanted.length === 0) blank(exp);
|
|
1914
|
+
else if (target) this.request(target, { names: wanted });
|
|
1915
|
+
}
|
|
1916
|
+
return chars.join("");
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* The project source file a module specifier resolves to, or undefined for a
|
|
1920
|
+
* package (read unpruned by the program itself) or an unresolvable one.
|
|
1921
|
+
*/
|
|
1922
|
+
resolve(specifier, from) {
|
|
1923
|
+
const resolved = import_ts_morph6.ts.resolveModuleName(
|
|
1924
|
+
specifier,
|
|
1925
|
+
from.getFilePath(),
|
|
1926
|
+
this.options,
|
|
1927
|
+
import_ts_morph6.ts.sys
|
|
1928
|
+
).resolvedModule;
|
|
1929
|
+
if (!resolved || resolved.isExternalLibraryImport) return void 0;
|
|
1930
|
+
const file = resolved.resolvedFileName;
|
|
1931
|
+
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
1932
|
+
return file;
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
function schemaPositions(sf) {
|
|
1936
|
+
const roots = [];
|
|
1937
|
+
sf.forEachDescendant((node) => {
|
|
1938
|
+
if (import_ts_morph6.Node.isDecorator(node)) {
|
|
1939
|
+
const name = node.getName();
|
|
1940
|
+
if (name === "ApplyContract") {
|
|
1941
|
+
roots.push(...node.getArguments());
|
|
1942
|
+
} else if (PARAM_DECORATORS.has(name)) {
|
|
1943
|
+
for (const arg of node.getArguments()) {
|
|
1944
|
+
if (import_ts_morph6.Node.isNewExpression(arg) || import_ts_morph6.Node.isCallExpression(arg)) {
|
|
1945
|
+
roots.push(...arg.getArguments());
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
} else if (import_ts_morph6.Node.isCallExpression(node)) {
|
|
1950
|
+
const callee = node.getExpression();
|
|
1951
|
+
const name = import_ts_morph6.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph6.Node.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
|
|
1952
|
+
if (name === "defineContract") roots.push(...node.getArguments());
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
return roots;
|
|
1956
|
+
}
|
|
1957
|
+
function topLevelDeclarations(sf) {
|
|
1958
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1959
|
+
const add = (name, node) => {
|
|
1960
|
+
if (!name) return;
|
|
1961
|
+
const list = byName.get(name);
|
|
1962
|
+
if (list) list.push(node);
|
|
1963
|
+
else byName.set(name, [node]);
|
|
1964
|
+
};
|
|
1965
|
+
for (const decl of sf.getVariableDeclarations()) add(decl.getName(), decl);
|
|
1966
|
+
for (const decl of sf.getFunctions()) add(decl.getName(), decl);
|
|
1967
|
+
for (const decl of sf.getClasses()) add(decl.getName(), decl);
|
|
1968
|
+
for (const decl of sf.getEnums()) add(decl.getName(), decl);
|
|
1969
|
+
for (const decl of sf.getInterfaces()) add(decl.getName(), decl);
|
|
1970
|
+
for (const decl of sf.getTypeAliases()) add(decl.getName(), decl);
|
|
1971
|
+
for (const decl of sf.getModules()) add(decl.getName(), decl);
|
|
1972
|
+
return byName;
|
|
1973
|
+
}
|
|
1974
|
+
function defaultExport(sf) {
|
|
1975
|
+
const nodes = [];
|
|
1976
|
+
for (const assignment of sf.getExportAssignments()) nodes.push(assignment);
|
|
1977
|
+
for (const fn of sf.getFunctions()) if (fn.isDefaultExport()) nodes.push(fn);
|
|
1978
|
+
for (const cls of sf.getClasses()) if (cls.isDefaultExport()) nodes.push(cls);
|
|
1979
|
+
return nodes;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// src/discovery/schema-type-inference.ts
|
|
1983
|
+
var programs = /* @__PURE__ */ new WeakMap();
|
|
1984
|
+
function programFor(discovery) {
|
|
1985
|
+
let program = programs.get(discovery);
|
|
1986
|
+
if (!program) {
|
|
1987
|
+
program = new SchemaTypeProgram(discovery);
|
|
1988
|
+
programs.set(discovery, program);
|
|
1989
|
+
}
|
|
1990
|
+
return program;
|
|
1991
|
+
}
|
|
1992
|
+
function clearSchemaTypeProject(discovery) {
|
|
1993
|
+
programs.delete(discovery);
|
|
1994
|
+
}
|
|
1995
|
+
function counterpart(typedSf, node) {
|
|
1996
|
+
const start = node.getStart();
|
|
1997
|
+
const end = node.getEnd();
|
|
1998
|
+
const kind = node.getKind();
|
|
1999
|
+
let candidate = typedSf.getDescendantAtPos(start);
|
|
2000
|
+
while (candidate) {
|
|
2001
|
+
if (candidate.getStart() === start && candidate.getEnd() === end && candidate.getKind() === kind) {
|
|
2002
|
+
return candidate;
|
|
2003
|
+
}
|
|
2004
|
+
if (candidate.getStart() < start || candidate.getEnd() > end) return void 0;
|
|
2005
|
+
candidate = candidate.getParent();
|
|
2006
|
+
}
|
|
2007
|
+
return void 0;
|
|
2008
|
+
}
|
|
2009
|
+
function propType(type, name, at) {
|
|
2010
|
+
return type.getProperty(name)?.getTypeAtLocation(at);
|
|
2011
|
+
}
|
|
2012
|
+
function isAnyOrUnknown(type) {
|
|
2013
|
+
return type.isAny() || type.isUnknown();
|
|
2014
|
+
}
|
|
2015
|
+
function inferSchema(expr, discovery) {
|
|
2016
|
+
try {
|
|
2017
|
+
const typedSf = programFor(discovery).sourceFileFor(expr.getSourceFile());
|
|
2018
|
+
const at = typedSf && counterpart(typedSf, expr);
|
|
2019
|
+
if (!at) return { kind: "unresolved" };
|
|
2020
|
+
const schemaType = at.getType();
|
|
2021
|
+
if (isAnyOrUnknown(schemaType)) return { kind: "unresolved" };
|
|
2022
|
+
const standard = propType(schemaType, "~standard", at);
|
|
2023
|
+
const stdTypes = standard ? propType(standard, "types", at)?.getNonNullableType() : void 0;
|
|
2024
|
+
const stdInput = stdTypes ? propType(stdTypes, "input", at) : void 0;
|
|
2025
|
+
const stdOutput = stdTypes ? propType(stdTypes, "output", at) : void 0;
|
|
2026
|
+
if (stdInput && stdOutput) {
|
|
2027
|
+
return { kind: "schema", types: { input: stdInput, output: stdOutput, at } };
|
|
2028
|
+
}
|
|
2029
|
+
const zodInput = propType(schemaType, "_input", at);
|
|
2030
|
+
const zodOutput = propType(schemaType, "_output", at);
|
|
2031
|
+
if (zodInput && zodOutput) {
|
|
2032
|
+
return { kind: "schema", types: { input: zodInput, output: zodOutput, at } };
|
|
2033
|
+
}
|
|
2034
|
+
return { kind: "not-schema" };
|
|
2035
|
+
} catch (err) {
|
|
2036
|
+
dbg("schema inference failed:", err);
|
|
2037
|
+
return { kind: "unresolved" };
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
var GLOBAL_NAMED_TYPES = /* @__PURE__ */ new Set([
|
|
2041
|
+
"Date",
|
|
2042
|
+
"File",
|
|
2043
|
+
"Blob",
|
|
2044
|
+
"FormData",
|
|
2045
|
+
"URL",
|
|
2046
|
+
"RegExp",
|
|
2047
|
+
"Map",
|
|
2048
|
+
"Set",
|
|
2049
|
+
"ArrayBuffer",
|
|
2050
|
+
"Uint8Array"
|
|
2051
|
+
]);
|
|
2052
|
+
var MAX_DEPTH = 12;
|
|
2053
|
+
function objectKey(name) {
|
|
2054
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2055
|
+
}
|
|
2056
|
+
function isOptionalSymbol(sym) {
|
|
2057
|
+
return (sym.getFlags() & import_ts_morph7.ts.SymbolFlags.Optional) !== 0;
|
|
2058
|
+
}
|
|
2059
|
+
function isSymbolKeyed(sym) {
|
|
2060
|
+
return sym.getName().startsWith("__@");
|
|
2061
|
+
}
|
|
2062
|
+
function orderedProperties(type) {
|
|
2063
|
+
const position = (sym) => {
|
|
2064
|
+
const decl = sym.getDeclarations()[0];
|
|
2065
|
+
return decl ? [decl.getSourceFile().getFilePath(), decl.getStart()] : void 0;
|
|
2066
|
+
};
|
|
2067
|
+
return type.getProperties().map((sym, index) => ({ sym, index, at: position(sym) })).sort((a, b) => {
|
|
2068
|
+
if (a.at && b.at) {
|
|
2069
|
+
if (a.at[0] !== b.at[0]) return a.at[0] < b.at[0] ? -1 : 1;
|
|
2070
|
+
if (a.at[1] !== b.at[1]) return a.at[1] - b.at[1];
|
|
2071
|
+
} else if (a.at || b.at) {
|
|
2072
|
+
return a.at ? -1 : 1;
|
|
2073
|
+
}
|
|
2074
|
+
return a.index - b.index;
|
|
2075
|
+
}).map((entry) => entry.sym);
|
|
2076
|
+
}
|
|
2077
|
+
function isFromDeclarationFile(sym) {
|
|
2078
|
+
const decl = sym?.getDeclarations()[0];
|
|
2079
|
+
return !!decl && decl.getSourceFile().isDeclarationFile();
|
|
2080
|
+
}
|
|
2081
|
+
function render(type, state, paired) {
|
|
2082
|
+
if (state.depth > MAX_DEPTH) return "unknown";
|
|
2083
|
+
if (isAnyOrUnknown(type)) {
|
|
2084
|
+
return paired && !isAnyOrUnknown(paired) ? render(paired, state) : "unknown";
|
|
2085
|
+
}
|
|
2086
|
+
if (type.isNever()) return "never";
|
|
2087
|
+
if (type.isString() || type.isTemplateLiteral()) return "string";
|
|
2088
|
+
if (type.isNumber()) return "number";
|
|
2089
|
+
if (type.isBoolean()) return "boolean";
|
|
2090
|
+
if (type.isBigInt()) return "bigint";
|
|
2091
|
+
if (type.isNull()) return "null";
|
|
2092
|
+
if (type.isUndefined() || type.isVoid()) return "undefined";
|
|
2093
|
+
if (type.isBooleanLiteral()) return type.getText();
|
|
2094
|
+
if (type.isStringLiteral() || type.isNumberLiteral() || type.isEnumLiteral()) {
|
|
2095
|
+
const value = type.getLiteralValue();
|
|
2096
|
+
return typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
2097
|
+
}
|
|
2098
|
+
if (type.isBigIntLiteral()) return type.getText();
|
|
2099
|
+
if (type.isTypeParameter()) return "unknown";
|
|
2100
|
+
if (type.isUnion()) return renderUnion(type, state, paired);
|
|
2101
|
+
if (type.isIntersection()) return renderIntersection(type, state);
|
|
2102
|
+
const next = { ...state, depth: state.depth + 1 };
|
|
2103
|
+
if (type.isArray() || type.isReadonlyArray()) {
|
|
2104
|
+
const element = type.getArrayElementType() ?? type.getTypeArguments()[0];
|
|
2105
|
+
const pairedElement = paired?.getArrayElementType() ?? paired?.getTypeArguments()[0];
|
|
2106
|
+
return element ? `Array<${render(element, next, pairedElement)}>` : "Array<unknown>";
|
|
2107
|
+
}
|
|
2108
|
+
if (type.isTuple()) {
|
|
2109
|
+
const elements = type.getTupleElements();
|
|
2110
|
+
const pairedElements = paired?.isTuple() ? paired.getTupleElements() : [];
|
|
2111
|
+
return `[${elements.map((e, i) => render(e, next, pairedElements[i])).join(", ")}]`;
|
|
2112
|
+
}
|
|
2113
|
+
if (type.isObject()) return renderObject(type, next, paired);
|
|
2114
|
+
return "unknown";
|
|
2115
|
+
}
|
|
2116
|
+
function renderUnion(type, state, paired) {
|
|
2117
|
+
const members = type.getUnionTypes();
|
|
2118
|
+
const hasTrue = members.some((m) => m.isBooleanLiteral() && m.getText() === "true");
|
|
2119
|
+
const hasFalse = members.some((m) => m.isBooleanLiteral() && m.getText() === "false");
|
|
2120
|
+
const pairedNonNull = paired?.getNonNullableType();
|
|
2121
|
+
const arms = [];
|
|
2122
|
+
let sawBoolean = false;
|
|
2123
|
+
let hasNull = false;
|
|
2124
|
+
let hasUndefined = false;
|
|
2125
|
+
for (const member of members) {
|
|
2126
|
+
if (member.isNull()) {
|
|
2127
|
+
hasNull = true;
|
|
2128
|
+
continue;
|
|
2129
|
+
}
|
|
2130
|
+
if (member.isUndefined()) {
|
|
2131
|
+
hasUndefined = true;
|
|
2132
|
+
continue;
|
|
2133
|
+
}
|
|
2134
|
+
if (member.isBooleanLiteral() && hasTrue && hasFalse) {
|
|
2135
|
+
if (!sawBoolean) arms.push("boolean");
|
|
2136
|
+
sawBoolean = true;
|
|
2137
|
+
continue;
|
|
2138
|
+
}
|
|
2139
|
+
arms.push(render(member, state, members.length === 1 ? paired : pairedNonNull));
|
|
2140
|
+
}
|
|
2141
|
+
if (hasNull) arms.push("null");
|
|
2142
|
+
if (hasUndefined) arms.push("undefined");
|
|
2143
|
+
const unique = [...new Set(arms)];
|
|
2144
|
+
if (unique.includes("unknown")) return "unknown";
|
|
2145
|
+
return unique.join(" | ");
|
|
2146
|
+
}
|
|
2147
|
+
function renderIntersection(type, state) {
|
|
2148
|
+
const members = type.getIntersectionTypes();
|
|
2149
|
+
const primitive = members.find((m) => !m.isObject());
|
|
2150
|
+
if (primitive) return render(primitive, state);
|
|
2151
|
+
return renderObject(type, { ...state, depth: state.depth + 1 }, void 0);
|
|
2152
|
+
}
|
|
2153
|
+
function renderObject(type, state, paired) {
|
|
2154
|
+
const symbol = type.getSymbol() ?? type.getAliasSymbol();
|
|
2155
|
+
const name = symbol?.getName();
|
|
2156
|
+
if (name && GLOBAL_NAMED_TYPES.has(name) && isFromDeclarationFile(symbol)) {
|
|
2157
|
+
const args = type.getTypeArguments();
|
|
2158
|
+
return args.length ? `${name}<${args.map((a) => render(a, state)).join(", ")}>` : name;
|
|
2159
|
+
}
|
|
2160
|
+
if (type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0) {
|
|
2161
|
+
return "unknown";
|
|
2162
|
+
}
|
|
2163
|
+
if (type.isClassOrInterface() && isFromDeclarationFile(symbol)) return "unknown";
|
|
2164
|
+
if (state.stack.has(type.compilerType)) return "unknown";
|
|
2165
|
+
const stack = new Set(state.stack).add(type.compilerType);
|
|
2166
|
+
const inner = { ...state, stack };
|
|
2167
|
+
const fields = [];
|
|
2168
|
+
for (const prop of orderedProperties(type)) {
|
|
2169
|
+
if (isSymbolKeyed(prop)) continue;
|
|
2170
|
+
const propName = prop.getName();
|
|
2171
|
+
const propT = prop.getTypeAtLocation(state.at);
|
|
2172
|
+
if (propT.getCallSignatures().length > 0 && !propT.isUnion()) continue;
|
|
2173
|
+
const pairedProp = paired?.getProperty(propName)?.getTypeAtLocation(state.at);
|
|
2174
|
+
const optional = isOptionalSymbol(prop) ? "?" : "";
|
|
2175
|
+
fields.push(`${objectKey(propName)}${optional}: ${render(propT, inner, pairedProp)}`);
|
|
2176
|
+
}
|
|
2177
|
+
const stringIndex = type.getStringIndexType();
|
|
2178
|
+
if (stringIndex) {
|
|
2179
|
+
const pairedIndex = paired?.getStringIndexType();
|
|
2180
|
+
const value = render(stringIndex, inner, pairedIndex);
|
|
2181
|
+
if (fields.length === 0) return `Record<string, ${value}>`;
|
|
2182
|
+
fields.push(`[key: string]: ${value}`);
|
|
2183
|
+
}
|
|
2184
|
+
return fields.length ? `{ ${fields.join("; ")} }` : "{}";
|
|
2185
|
+
}
|
|
2186
|
+
function stateAt(at) {
|
|
2187
|
+
return { at, stack: /* @__PURE__ */ new Set(), depth: 0 };
|
|
2188
|
+
}
|
|
2189
|
+
function requestTypeOf(types) {
|
|
2190
|
+
return render(types.input, stateAt(types.at), types.output);
|
|
2191
|
+
}
|
|
2192
|
+
function responseTypeOf(types) {
|
|
2193
|
+
return render(types.output, stateAt(types.at));
|
|
2194
|
+
}
|
|
2195
|
+
function requestFieldsOf(types) {
|
|
2196
|
+
const { input, output, at } = types;
|
|
2197
|
+
const objectInput = input.getNonNullableType();
|
|
2198
|
+
if (!objectInput.isObject() || objectInput.isArray() || objectInput.isTuple()) return null;
|
|
2199
|
+
const state = stateAt(at);
|
|
2200
|
+
return orderedProperties(objectInput).filter((prop) => !isSymbolKeyed(prop)).map((prop) => ({
|
|
2201
|
+
name: prop.getName(),
|
|
2202
|
+
type: render(
|
|
2203
|
+
prop.getTypeAtLocation(at),
|
|
2204
|
+
state,
|
|
2205
|
+
output.getProperty(prop.getName())?.getTypeAtLocation(at)
|
|
2206
|
+
),
|
|
2207
|
+
optional: isOptionalSymbol(prop)
|
|
2208
|
+
}));
|
|
2209
|
+
}
|
|
2210
|
+
function acceptsUndefined(types) {
|
|
2211
|
+
const { input, output } = types;
|
|
2212
|
+
const accepted = isAnyOrUnknown(input) ? output : input;
|
|
2213
|
+
if (isAnyOrUnknown(accepted) || accepted.isUndefined()) return true;
|
|
2214
|
+
return accepted.isUnion() && accepted.getUnionTypes().some((t) => t.isUndefined());
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
2218
|
+
var import_ts_morph8 = require("ts-morph");
|
|
2219
|
+
function zodAstToTs(node) {
|
|
2220
|
+
if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
|
|
2221
|
+
const expr = node.getExpression();
|
|
2222
|
+
if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
|
|
2223
|
+
const methodName = expr.getName();
|
|
2224
|
+
const receiver = expr.getExpression();
|
|
2225
|
+
if (methodName === "optional") {
|
|
2226
|
+
return `${zodAstToTs(receiver)} | undefined`;
|
|
2227
|
+
}
|
|
2228
|
+
if (methodName === "nullable") {
|
|
2229
|
+
return `${zodAstToTs(receiver)} | null`;
|
|
2230
|
+
}
|
|
2231
|
+
const args = node.getArguments();
|
|
2232
|
+
switch (methodName) {
|
|
2233
|
+
case "string":
|
|
2234
|
+
return "string";
|
|
2235
|
+
case "number":
|
|
2236
|
+
return "number";
|
|
2237
|
+
case "boolean":
|
|
2238
|
+
return "boolean";
|
|
2239
|
+
case "unknown":
|
|
2240
|
+
return "unknown";
|
|
2241
|
+
case "any":
|
|
2242
|
+
return "unknown";
|
|
2243
|
+
case "literal": {
|
|
2244
|
+
const lit = args[0];
|
|
2245
|
+
if (!lit) return "unknown";
|
|
2246
|
+
if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2247
|
+
if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2248
|
+
if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
|
|
2249
|
+
if (lit.getKind() === import_ts_morph8.SyntaxKind.FalseKeyword) return "false";
|
|
2250
|
+
return "unknown";
|
|
2251
|
+
}
|
|
2252
|
+
case "enum": {
|
|
2253
|
+
const arrArg = args[0];
|
|
2254
|
+
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2255
|
+
const members = arrArg.getElements().map(
|
|
2256
|
+
(el) => import_ts_morph8.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2257
|
+
);
|
|
2258
|
+
return members.join(" | ");
|
|
2259
|
+
}
|
|
2260
|
+
case "array": {
|
|
2261
|
+
const inner = args[0];
|
|
2262
|
+
if (!inner) return "unknown";
|
|
2263
|
+
return `Array<${zodAstToTs(inner)}>`;
|
|
2264
|
+
}
|
|
2265
|
+
case "object": {
|
|
2266
|
+
const objArg = args[0];
|
|
2267
|
+
if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2268
|
+
const lines = [];
|
|
2269
|
+
for (const prop of objArg.getProperties()) {
|
|
2270
|
+
if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
|
|
2271
|
+
const key = prop.getName();
|
|
2272
|
+
const valNode = prop.getInitializer();
|
|
2273
|
+
if (!valNode) continue;
|
|
2274
|
+
const tsType = zodAstToTs(valNode);
|
|
2275
|
+
const isOpt = isOptionalChain(valNode);
|
|
2276
|
+
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2277
|
+
}
|
|
2278
|
+
return `{ ${lines.join("; ")} }`;
|
|
2279
|
+
}
|
|
2280
|
+
case "union": {
|
|
2281
|
+
const arrArg = args[0];
|
|
2282
|
+
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2283
|
+
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2284
|
+
}
|
|
2285
|
+
case "record": {
|
|
2286
|
+
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2287
|
+
if (!valArg) return "unknown";
|
|
2288
|
+
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2289
|
+
}
|
|
2290
|
+
case "tuple": {
|
|
2291
|
+
const arrArg = args[0];
|
|
2292
|
+
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2293
|
+
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2294
|
+
}
|
|
2295
|
+
default:
|
|
2296
|
+
return "unknown";
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
return "unknown";
|
|
2300
|
+
}
|
|
2301
|
+
function isOptionalChain(node) {
|
|
2302
|
+
if (!import_ts_morph8.Node.isCallExpression(node)) return false;
|
|
2303
|
+
const expr = node.getExpression();
|
|
2304
|
+
return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2305
|
+
}
|
|
2306
|
+
function memberType(val, position, project) {
|
|
2307
|
+
if (project) {
|
|
2308
|
+
const inferred = inferSchema(val, project);
|
|
2309
|
+
if (inferred.kind === "schema") {
|
|
2310
|
+
return position === "request" ? requestTypeOf(inferred.types) : responseTypeOf(inferred.types);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
return zodAstToTs(val);
|
|
2314
|
+
}
|
|
2315
|
+
function parseDefineContractCall(callExpr, project) {
|
|
2316
|
+
if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
|
|
2317
|
+
const callee = callExpr.getExpression();
|
|
2318
|
+
const calleeName = import_ts_morph8.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph8.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2319
|
+
if (calleeName !== "defineContract") return null;
|
|
2320
|
+
const args = callExpr.getArguments();
|
|
2321
|
+
const optsArg = args[0];
|
|
2322
|
+
if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2323
|
+
let query = null;
|
|
2324
|
+
let body = null;
|
|
2325
|
+
let response = "unknown";
|
|
2326
|
+
let error = null;
|
|
2327
|
+
let bodyZodText = null;
|
|
2328
|
+
let queryZodText = null;
|
|
2329
|
+
for (const prop of optsArg.getProperties()) {
|
|
2330
|
+
if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
|
|
2331
|
+
const propName = prop.getName();
|
|
2332
|
+
const val = prop.getInitializer();
|
|
2333
|
+
if (!val) continue;
|
|
2334
|
+
if (propName === "query") {
|
|
2335
|
+
query = memberType(val, "request", project);
|
|
2336
|
+
queryZodText = val.getText();
|
|
2337
|
+
} else if (propName === "body") {
|
|
2338
|
+
body = memberType(val, "request", project);
|
|
2339
|
+
bodyZodText = val.getText();
|
|
2340
|
+
} else if (propName === "response") {
|
|
2341
|
+
response = memberType(val, "response", project);
|
|
2342
|
+
} else if (propName === "error") {
|
|
2343
|
+
error = memberType(val, "response", project);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
// src/discovery/pipe-schema.ts
|
|
2350
|
+
function pipeSchemaOf(decorator, project) {
|
|
2351
|
+
for (const pipe of decorator.getArguments()) {
|
|
2352
|
+
if (!import_ts_morph9.Node.isNewExpression(pipe) && !import_ts_morph9.Node.isCallExpression(pipe)) continue;
|
|
2353
|
+
for (const arg of pipe.getArguments()) {
|
|
2354
|
+
if (!couldBeSchema(arg)) continue;
|
|
2355
|
+
const inferred = inferSchema(arg, project);
|
|
2356
|
+
if (inferred.kind === "schema") {
|
|
2357
|
+
return {
|
|
2358
|
+
type: requestTypeOf(inferred.types),
|
|
2359
|
+
optional: acceptsUndefined(inferred.types),
|
|
2360
|
+
fields: requestFieldsOf(inferred.types)
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
if (inferred.kind === "unresolved") {
|
|
2364
|
+
const fallback = syntacticZodType(arg, project);
|
|
2365
|
+
if (fallback) {
|
|
2366
|
+
return { type: fallback, optional: fallback.endsWith("| undefined"), fields: null };
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
return null;
|
|
2372
|
+
}
|
|
2373
|
+
function couldBeSchema(arg) {
|
|
2374
|
+
return import_ts_morph9.Node.isIdentifier(arg) || import_ts_morph9.Node.isPropertyAccessExpression(arg) || import_ts_morph9.Node.isCallExpression(arg) || import_ts_morph9.Node.isAsExpression(arg) || import_ts_morph9.Node.isSatisfiesExpression(arg) || import_ts_morph9.Node.isParenthesizedExpression(arg);
|
|
2375
|
+
}
|
|
2376
|
+
function syntacticZodType(arg, project) {
|
|
2377
|
+
let expr = arg;
|
|
2378
|
+
if (import_ts_morph9.Node.isIdentifier(arg)) {
|
|
2379
|
+
const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
|
|
2380
|
+
expr = resolved?.decl.getInitializer();
|
|
2381
|
+
}
|
|
2382
|
+
if (!expr || !import_ts_morph9.Node.isCallExpression(expr)) return null;
|
|
2383
|
+
const type = zodAstToTs(expr);
|
|
2384
|
+
return type === "unknown" ? null : type;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
1721
2387
|
// src/discovery/dto-type-resolver.ts
|
|
1722
2388
|
var WRAPPER_TYPES = {
|
|
1723
2389
|
// MikroORM Ref/Reference/LoadedReference/IdentifiedReference are server-side
|
|
@@ -1749,22 +2415,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
|
|
|
1749
2415
|
]);
|
|
1750
2416
|
function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
|
|
1751
2417
|
if (depth <= 0) return "unknown";
|
|
1752
|
-
if (
|
|
2418
|
+
if (import_ts_morph10.Node.isArrayTypeNode(typeNode)) {
|
|
1753
2419
|
const elementType = typeNode.getElementTypeNode();
|
|
1754
2420
|
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
|
|
1755
2421
|
}
|
|
1756
|
-
if (
|
|
2422
|
+
if (import_ts_morph10.Node.isUnionTypeNode(typeNode)) {
|
|
1757
2423
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
|
|
1758
2424
|
}
|
|
1759
|
-
if (
|
|
2425
|
+
if (import_ts_morph10.Node.isIntersectionTypeNode(typeNode)) {
|
|
1760
2426
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
|
|
1761
2427
|
}
|
|
1762
|
-
if (
|
|
2428
|
+
if (import_ts_morph10.Node.isParenthesizedTypeNode(typeNode)) {
|
|
1763
2429
|
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
|
|
1764
2430
|
}
|
|
1765
|
-
if (
|
|
2431
|
+
if (import_ts_morph10.Node.isTypeReference(typeNode)) {
|
|
1766
2432
|
const typeName = typeNode.getTypeName();
|
|
1767
|
-
const name =
|
|
2433
|
+
const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
1768
2434
|
const bound = subst.get(name);
|
|
1769
2435
|
if (bound !== void 0) return bound;
|
|
1770
2436
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
@@ -1787,13 +2453,13 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
1787
2453
|
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1788
2454
|
return "unknown";
|
|
1789
2455
|
}
|
|
1790
|
-
if (
|
|
2456
|
+
if (import_ts_morph10.Node.isTypeLiteral(typeNode)) {
|
|
1791
2457
|
const members = [];
|
|
1792
2458
|
for (const member of typeNode.getMembers()) {
|
|
1793
|
-
if (
|
|
2459
|
+
if (import_ts_morph10.Node.isPropertySignature(member)) {
|
|
1794
2460
|
const memberTypeNode = member.getTypeNode();
|
|
1795
|
-
const
|
|
1796
|
-
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${
|
|
2461
|
+
const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
|
|
2462
|
+
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
|
|
1797
2463
|
} else {
|
|
1798
2464
|
members.push(member.getText());
|
|
1799
2465
|
}
|
|
@@ -1801,11 +2467,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
1801
2467
|
return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
|
|
1802
2468
|
}
|
|
1803
2469
|
const kind = typeNode.getKind();
|
|
1804
|
-
if (kind ===
|
|
1805
|
-
if (kind ===
|
|
1806
|
-
if (kind ===
|
|
1807
|
-
if (kind ===
|
|
1808
|
-
if (kind ===
|
|
2470
|
+
if (kind === import_ts_morph10.SyntaxKind.StringKeyword) return "string";
|
|
2471
|
+
if (kind === import_ts_morph10.SyntaxKind.NumberKeyword) return "number";
|
|
2472
|
+
if (kind === import_ts_morph10.SyntaxKind.BooleanKeyword) return "boolean";
|
|
2473
|
+
if (kind === import_ts_morph10.SyntaxKind.UnknownKeyword) return "unknown";
|
|
2474
|
+
if (kind === import_ts_morph10.SyntaxKind.AnyKeyword) return "unknown";
|
|
1809
2475
|
return typeNode.getText();
|
|
1810
2476
|
}
|
|
1811
2477
|
function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
|
|
@@ -1853,20 +2519,24 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
|
|
|
1853
2519
|
const propName = prop.getName();
|
|
1854
2520
|
const isOptional = prop.hasQuestionToken();
|
|
1855
2521
|
const propTypeNode = prop.getTypeNode();
|
|
1856
|
-
let
|
|
2522
|
+
let propType2 = "unknown";
|
|
1857
2523
|
if (propTypeNode) {
|
|
1858
|
-
|
|
2524
|
+
propType2 = resolveTypeNodeToString(propTypeNode, sourceFile, project, depth, subst);
|
|
1859
2525
|
}
|
|
1860
|
-
lines.push(`${propName}${isOptional ? "?" : ""}: ${
|
|
2526
|
+
lines.push(`${propName}${isOptional ? "?" : ""}: ${propType2}`);
|
|
1861
2527
|
}
|
|
1862
2528
|
return `{ ${lines.join("; ")} }`;
|
|
1863
2529
|
}
|
|
2530
|
+
function namedField(decorator) {
|
|
2531
|
+
const first = decorator.getArguments()[0];
|
|
2532
|
+
return first && import_ts_morph10.Node.isStringLiteral(first) ? first.getLiteralValue() : null;
|
|
2533
|
+
}
|
|
1864
2534
|
function extractBodyType(method, sourceFile, project) {
|
|
1865
2535
|
for (const param of method.getParameters()) {
|
|
1866
2536
|
const bodyDecorator = param.getDecorators().find((d) => d.getName() === "Body");
|
|
1867
|
-
if (!bodyDecorator) continue;
|
|
1868
|
-
const
|
|
1869
|
-
if (
|
|
2537
|
+
if (!bodyDecorator || namedField(bodyDecorator) !== null) continue;
|
|
2538
|
+
const piped = pipeSchemaOf(bodyDecorator, project);
|
|
2539
|
+
if (piped) return piped.type;
|
|
1870
2540
|
const typeNode = param.getTypeNode();
|
|
1871
2541
|
if (typeNode) {
|
|
1872
2542
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -1874,11 +2544,16 @@ function extractBodyType(method, sourceFile, project) {
|
|
|
1874
2544
|
}
|
|
1875
2545
|
return null;
|
|
1876
2546
|
}
|
|
2547
|
+
function hasPipeSchema(param, decoratorName, project) {
|
|
2548
|
+
const decorator = param.getDecorators().find((d) => d.getName() === decoratorName);
|
|
2549
|
+
return !!decorator && pipeSchemaOf(decorator, project) !== null;
|
|
2550
|
+
}
|
|
1877
2551
|
function extractQueryType(method, sourceFile, project) {
|
|
1878
2552
|
for (const param of method.getParameters()) {
|
|
1879
2553
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
1880
|
-
if (!queryDecorator) continue;
|
|
1881
|
-
|
|
2554
|
+
if (!queryDecorator || namedField(queryDecorator) !== null) continue;
|
|
2555
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2556
|
+
if (piped) return piped.type;
|
|
1882
2557
|
const typeNode = param.getTypeNode();
|
|
1883
2558
|
if (typeNode) {
|
|
1884
2559
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -1888,10 +2563,13 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
1888
2563
|
for (const param of method.getParameters()) {
|
|
1889
2564
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
1890
2565
|
if (!queryDecorator) continue;
|
|
1891
|
-
const
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
2566
|
+
const queryName = namedField(queryDecorator);
|
|
2567
|
+
if (queryName === null) continue;
|
|
2568
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2569
|
+
if (piped) {
|
|
2570
|
+
entries.push(`${queryName}${piped.optional ? "?" : ""}: ${piped.type}`);
|
|
2571
|
+
continue;
|
|
2572
|
+
}
|
|
1895
2573
|
const typeNode = param.getTypeNode();
|
|
1896
2574
|
const queryType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
1897
2575
|
entries.push(`${queryName}${isParamOptional(param) ? "?" : ""}: ${queryType}`);
|
|
@@ -1901,8 +2579,8 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
1901
2579
|
function isParamOptional(param) {
|
|
1902
2580
|
if (param.hasQuestionToken() || param.hasInitializer()) return true;
|
|
1903
2581
|
const typeNode = param.getTypeNode();
|
|
1904
|
-
if (typeNode &&
|
|
1905
|
-
return typeNode.getTypeNodes().some((t) => t.getKind() ===
|
|
2582
|
+
if (typeNode && import_ts_morph10.Node.isUnionTypeNode(typeNode)) {
|
|
2583
|
+
return typeNode.getTypeNodes().some((t) => t.getKind() === import_ts_morph10.SyntaxKind.UndefinedKeyword);
|
|
1906
2584
|
}
|
|
1907
2585
|
return false;
|
|
1908
2586
|
}
|
|
@@ -1914,7 +2592,7 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
1914
2592
|
const paramArgs = paramDecorator.getArguments();
|
|
1915
2593
|
if (paramArgs.length === 0) continue;
|
|
1916
2594
|
const nameArg = paramArgs[0];
|
|
1917
|
-
if (!
|
|
2595
|
+
if (!import_ts_morph10.Node.isStringLiteral(nameArg)) continue;
|
|
1918
2596
|
const paramName = nameArg.getLiteralValue();
|
|
1919
2597
|
const typeNode = param.getTypeNode();
|
|
1920
2598
|
const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
@@ -1922,6 +2600,22 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
1922
2600
|
}
|
|
1923
2601
|
return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
|
|
1924
2602
|
}
|
|
2603
|
+
function extractPipedParamTypes(method, project) {
|
|
2604
|
+
const types = {};
|
|
2605
|
+
for (const param of method.getParameters()) {
|
|
2606
|
+
const paramDecorator = param.getDecorators().find((d) => d.getName() === "Param");
|
|
2607
|
+
if (!paramDecorator) continue;
|
|
2608
|
+
const piped = pipeSchemaOf(paramDecorator, project);
|
|
2609
|
+
if (!piped) continue;
|
|
2610
|
+
const name = namedField(paramDecorator);
|
|
2611
|
+
if (name !== null) {
|
|
2612
|
+
types[name] = piped.type;
|
|
2613
|
+
} else {
|
|
2614
|
+
for (const field of piped.fields ?? []) types[field.name] = field.type;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
return Object.keys(types).length > 0 ? types : null;
|
|
2618
|
+
}
|
|
1925
2619
|
function extractUploadedFiles(method) {
|
|
1926
2620
|
const FILE = "File | Blob";
|
|
1927
2621
|
const entries = [];
|
|
@@ -1935,28 +2629,28 @@ function extractUploadedFiles(method) {
|
|
|
1935
2629
|
for (const decorator of method.getDecorators()) {
|
|
1936
2630
|
if (decorator.getName() !== "UseInterceptors") continue;
|
|
1937
2631
|
for (const arg of decorator.getArguments()) {
|
|
1938
|
-
if (!
|
|
2632
|
+
if (!import_ts_morph10.Node.isCallExpression(arg)) continue;
|
|
1939
2633
|
const interceptor = arg.getExpression().getText();
|
|
1940
2634
|
const callArgs = arg.getArguments();
|
|
1941
2635
|
const firstArg2 = callArgs[0];
|
|
1942
2636
|
if (interceptor === "FileInterceptor") {
|
|
1943
|
-
if (firstArg2 &&
|
|
2637
|
+
if (firstArg2 && import_ts_morph10.Node.isStringLiteral(firstArg2)) {
|
|
1944
2638
|
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
1945
2639
|
multipart = true;
|
|
1946
2640
|
}
|
|
1947
2641
|
} else if (interceptor === "FilesInterceptor") {
|
|
1948
|
-
if (firstArg2 &&
|
|
2642
|
+
if (firstArg2 && import_ts_morph10.Node.isStringLiteral(firstArg2)) {
|
|
1949
2643
|
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
1950
2644
|
multipart = true;
|
|
1951
2645
|
}
|
|
1952
2646
|
} else if (interceptor === "FileFieldsInterceptor") {
|
|
1953
|
-
if (firstArg2 &&
|
|
2647
|
+
if (firstArg2 && import_ts_morph10.Node.isArrayLiteralExpression(firstArg2)) {
|
|
1954
2648
|
for (const el of firstArg2.getElements()) {
|
|
1955
|
-
if (!
|
|
2649
|
+
if (!import_ts_morph10.Node.isObjectLiteralExpression(el)) continue;
|
|
1956
2650
|
const nameProp = el.getProperty("name");
|
|
1957
|
-
if (nameProp &&
|
|
2651
|
+
if (nameProp && import_ts_morph10.Node.isPropertyAssignment(nameProp)) {
|
|
1958
2652
|
const init = nameProp.getInitializer();
|
|
1959
|
-
if (init &&
|
|
2653
|
+
if (init && import_ts_morph10.Node.isStringLiteral(init)) {
|
|
1960
2654
|
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
1961
2655
|
}
|
|
1962
2656
|
}
|
|
@@ -1976,13 +2670,13 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
1976
2670
|
if (apiResponseDecorator) {
|
|
1977
2671
|
const args = apiResponseDecorator.getArguments();
|
|
1978
2672
|
const optsArg = args[0];
|
|
1979
|
-
if (optsArg &&
|
|
2673
|
+
if (optsArg && import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) {
|
|
1980
2674
|
for (const prop of optsArg.getProperties()) {
|
|
1981
|
-
if (!
|
|
2675
|
+
if (!import_ts_morph10.Node.isPropertyAssignment(prop)) continue;
|
|
1982
2676
|
if (prop.getName() !== "type") continue;
|
|
1983
2677
|
const val = prop.getInitializer();
|
|
1984
2678
|
if (!val) continue;
|
|
1985
|
-
if (
|
|
2679
|
+
if (import_ts_morph10.Node.isArrayLiteralExpression(val)) {
|
|
1986
2680
|
const elements = val.getElements();
|
|
1987
2681
|
const firstEl = elements[0];
|
|
1988
2682
|
if (elements.length > 0 && firstEl !== void 0) {
|
|
@@ -2003,24 +2697,24 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
2003
2697
|
}
|
|
2004
2698
|
function apiResponseStatus(decorator) {
|
|
2005
2699
|
const optsArg = decorator.getArguments()[0];
|
|
2006
|
-
if (!optsArg || !
|
|
2700
|
+
if (!optsArg || !import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2007
2701
|
for (const prop of optsArg.getProperties()) {
|
|
2008
|
-
if (!
|
|
2702
|
+
if (!import_ts_morph10.Node.isPropertyAssignment(prop)) continue;
|
|
2009
2703
|
if (prop.getName() !== "status") continue;
|
|
2010
2704
|
const val = prop.getInitializer();
|
|
2011
|
-
if (val &&
|
|
2705
|
+
if (val && import_ts_morph10.Node.isNumericLiteral(val)) return Number(val.getLiteralValue());
|
|
2012
2706
|
}
|
|
2013
2707
|
return null;
|
|
2014
2708
|
}
|
|
2015
2709
|
function apiResponseTypeNode(decorator) {
|
|
2016
2710
|
const optsArg = decorator.getArguments()[0];
|
|
2017
|
-
if (!optsArg || !
|
|
2711
|
+
if (!optsArg || !import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2018
2712
|
for (const prop of optsArg.getProperties()) {
|
|
2019
|
-
if (!
|
|
2713
|
+
if (!import_ts_morph10.Node.isPropertyAssignment(prop)) continue;
|
|
2020
2714
|
if (prop.getName() !== "type") continue;
|
|
2021
2715
|
const val = prop.getInitializer();
|
|
2022
2716
|
if (!val) return null;
|
|
2023
|
-
if (
|
|
2717
|
+
if (import_ts_morph10.Node.isArrayLiteralExpression(val)) {
|
|
2024
2718
|
const first = val.getElements()[0];
|
|
2025
2719
|
return first ? { node: first, isArray: true } : null;
|
|
2026
2720
|
}
|
|
@@ -2038,7 +2732,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2038
2732
|
const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
|
|
2039
2733
|
const type = typeInfo.isArray ? `Array<${inner}>` : inner;
|
|
2040
2734
|
let ref = null;
|
|
2041
|
-
if (
|
|
2735
|
+
if (import_ts_morph10.Node.isIdentifier(typeInfo.node)) {
|
|
2042
2736
|
const name = typeInfo.node.getText();
|
|
2043
2737
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2044
2738
|
if (localDecl?.isExported()) {
|
|
@@ -2055,7 +2749,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2055
2749
|
return null;
|
|
2056
2750
|
}
|
|
2057
2751
|
function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
2058
|
-
if (!
|
|
2752
|
+
if (!import_ts_morph10.Node.isIdentifier(node)) return "unknown";
|
|
2059
2753
|
const name = node.getText();
|
|
2060
2754
|
const resolved = findType(name, sourceFile, project);
|
|
2061
2755
|
if (resolved) {
|
|
@@ -2075,9 +2769,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
|
|
|
2075
2769
|
var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
|
|
2076
2770
|
function detectBinaryResponse(method) {
|
|
2077
2771
|
const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
|
|
2078
|
-
if (!node || !
|
|
2772
|
+
if (!node || !import_ts_morph10.Node.isTypeReference(node)) return false;
|
|
2079
2773
|
const typeName = node.getTypeName();
|
|
2080
|
-
const name =
|
|
2774
|
+
const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2081
2775
|
return BINARY_RESPONSE_TYPES.has(name);
|
|
2082
2776
|
}
|
|
2083
2777
|
function detectStreamElement(method) {
|
|
@@ -2092,9 +2786,9 @@ function detectStreamElement(method) {
|
|
|
2092
2786
|
return null;
|
|
2093
2787
|
}
|
|
2094
2788
|
function streamContainerElement(node) {
|
|
2095
|
-
if (!node || !
|
|
2789
|
+
if (!node || !import_ts_morph10.Node.isTypeReference(node)) return null;
|
|
2096
2790
|
const typeName = node.getTypeName();
|
|
2097
|
-
const name =
|
|
2791
|
+
const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2098
2792
|
if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
|
|
2099
2793
|
return node.getTypeArguments()[0] ?? null;
|
|
2100
2794
|
}
|
|
@@ -2104,9 +2798,9 @@ function hasAsQueryDecorator(method) {
|
|
|
2104
2798
|
return method.getDecorators().some((d) => d.getName() === "AsQuery");
|
|
2105
2799
|
}
|
|
2106
2800
|
function unwrapNamedContainer(node, names) {
|
|
2107
|
-
if (!node || !
|
|
2801
|
+
if (!node || !import_ts_morph10.Node.isTypeReference(node)) return node;
|
|
2108
2802
|
const typeName = node.getTypeName();
|
|
2109
|
-
const name =
|
|
2803
|
+
const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2110
2804
|
if (names.has(name)) {
|
|
2111
2805
|
return node.getTypeArguments()[0] ?? node;
|
|
2112
2806
|
}
|
|
@@ -2127,19 +2821,20 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2127
2821
|
body = body ?? bodyType;
|
|
2128
2822
|
}
|
|
2129
2823
|
const paramsType = extractParamsType(method, sourceFile, project);
|
|
2824
|
+
const paramTypes = extractPipedParamTypes(method, project);
|
|
2130
2825
|
const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
|
|
2131
2826
|
const errorInfo = extractErrorType(method, sourceFile, project);
|
|
2132
|
-
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
2827
|
+
if (body === null && query === null && paramsType === null && paramTypes === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
2133
2828
|
return null;
|
|
2134
2829
|
}
|
|
2135
2830
|
let bodyRef = null;
|
|
2136
2831
|
let queryRef = null;
|
|
2137
2832
|
let responseRef = null;
|
|
2138
2833
|
for (const param of method.getParameters()) {
|
|
2139
|
-
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
2834
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode() && !hasPipeSchema(param, "Body", project)) {
|
|
2140
2835
|
bodyRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2141
2836
|
}
|
|
2142
|
-
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
2837
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode() && !hasPipeSchema(param, "Query", project)) {
|
|
2143
2838
|
queryRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2144
2839
|
}
|
|
2145
2840
|
}
|
|
@@ -2152,11 +2847,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2152
2847
|
if (apiResp) {
|
|
2153
2848
|
const args = apiResp.getArguments();
|
|
2154
2849
|
const optsArg = args[0];
|
|
2155
|
-
if (optsArg &&
|
|
2850
|
+
if (optsArg && import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) {
|
|
2156
2851
|
for (const prop of optsArg.getProperties()) {
|
|
2157
|
-
if (
|
|
2852
|
+
if (import_ts_morph10.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
2158
2853
|
const val = prop.getInitializer();
|
|
2159
|
-
if (val &&
|
|
2854
|
+
if (val && import_ts_morph10.Node.isIdentifier(val)) {
|
|
2160
2855
|
const name = val.getText();
|
|
2161
2856
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2162
2857
|
if (localDecl?.isExported()) {
|
|
@@ -2192,6 +2887,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2192
2887
|
response,
|
|
2193
2888
|
error: errorInfo?.type ?? null,
|
|
2194
2889
|
params: paramsType,
|
|
2890
|
+
paramTypes,
|
|
2195
2891
|
queryRef,
|
|
2196
2892
|
bodyRef,
|
|
2197
2893
|
responseRef,
|
|
@@ -2212,6 +2908,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2212
2908
|
function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
2213
2909
|
for (const param of method.getParameters()) {
|
|
2214
2910
|
if (!param.getDecorators().some((d) => d.getName() === decoratorName)) continue;
|
|
2911
|
+
if (hasPipeSchema(param, decoratorName, project)) continue;
|
|
2215
2912
|
const typeNode = param.getTypeNode();
|
|
2216
2913
|
if (!typeNode) continue;
|
|
2217
2914
|
const text = typeNode.getText().replace(/\[\]$/, "");
|
|
@@ -2224,129 +2921,6 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
2224
2921
|
return null;
|
|
2225
2922
|
}
|
|
2226
2923
|
|
|
2227
|
-
// src/discovery/zod-ast-to-ts.ts
|
|
2228
|
-
var import_ts_morph7 = require("ts-morph");
|
|
2229
|
-
function zodAstToTs(node) {
|
|
2230
|
-
if (!import_ts_morph7.Node.isCallExpression(node)) return "unknown";
|
|
2231
|
-
const expr = node.getExpression();
|
|
2232
|
-
if (import_ts_morph7.Node.isPropertyAccessExpression(expr)) {
|
|
2233
|
-
const methodName = expr.getName();
|
|
2234
|
-
const receiver = expr.getExpression();
|
|
2235
|
-
if (methodName === "optional") {
|
|
2236
|
-
return `${zodAstToTs(receiver)} | undefined`;
|
|
2237
|
-
}
|
|
2238
|
-
if (methodName === "nullable") {
|
|
2239
|
-
return `${zodAstToTs(receiver)} | null`;
|
|
2240
|
-
}
|
|
2241
|
-
const args = node.getArguments();
|
|
2242
|
-
switch (methodName) {
|
|
2243
|
-
case "string":
|
|
2244
|
-
return "string";
|
|
2245
|
-
case "number":
|
|
2246
|
-
return "number";
|
|
2247
|
-
case "boolean":
|
|
2248
|
-
return "boolean";
|
|
2249
|
-
case "unknown":
|
|
2250
|
-
return "unknown";
|
|
2251
|
-
case "any":
|
|
2252
|
-
return "unknown";
|
|
2253
|
-
case "literal": {
|
|
2254
|
-
const lit = args[0];
|
|
2255
|
-
if (!lit) return "unknown";
|
|
2256
|
-
if (import_ts_morph7.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2257
|
-
if (import_ts_morph7.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2258
|
-
if (lit.getKind() === import_ts_morph7.SyntaxKind.TrueKeyword) return "true";
|
|
2259
|
-
if (lit.getKind() === import_ts_morph7.SyntaxKind.FalseKeyword) return "false";
|
|
2260
|
-
return "unknown";
|
|
2261
|
-
}
|
|
2262
|
-
case "enum": {
|
|
2263
|
-
const arrArg = args[0];
|
|
2264
|
-
if (!arrArg || !import_ts_morph7.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2265
|
-
const members = arrArg.getElements().map(
|
|
2266
|
-
(el) => import_ts_morph7.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2267
|
-
);
|
|
2268
|
-
return members.join(" | ");
|
|
2269
|
-
}
|
|
2270
|
-
case "array": {
|
|
2271
|
-
const inner = args[0];
|
|
2272
|
-
if (!inner) return "unknown";
|
|
2273
|
-
return `Array<${zodAstToTs(inner)}>`;
|
|
2274
|
-
}
|
|
2275
|
-
case "object": {
|
|
2276
|
-
const objArg = args[0];
|
|
2277
|
-
if (!objArg || !import_ts_morph7.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2278
|
-
const lines = [];
|
|
2279
|
-
for (const prop of objArg.getProperties()) {
|
|
2280
|
-
if (!import_ts_morph7.Node.isPropertyAssignment(prop)) continue;
|
|
2281
|
-
const key = prop.getName();
|
|
2282
|
-
const valNode = prop.getInitializer();
|
|
2283
|
-
if (!valNode) continue;
|
|
2284
|
-
const tsType = zodAstToTs(valNode);
|
|
2285
|
-
const isOpt = isOptionalChain(valNode);
|
|
2286
|
-
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2287
|
-
}
|
|
2288
|
-
return `{ ${lines.join("; ")} }`;
|
|
2289
|
-
}
|
|
2290
|
-
case "union": {
|
|
2291
|
-
const arrArg = args[0];
|
|
2292
|
-
if (!arrArg || !import_ts_morph7.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2293
|
-
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2294
|
-
}
|
|
2295
|
-
case "record": {
|
|
2296
|
-
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2297
|
-
if (!valArg) return "unknown";
|
|
2298
|
-
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2299
|
-
}
|
|
2300
|
-
case "tuple": {
|
|
2301
|
-
const arrArg = args[0];
|
|
2302
|
-
if (!arrArg || !import_ts_morph7.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2303
|
-
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2304
|
-
}
|
|
2305
|
-
default:
|
|
2306
|
-
return "unknown";
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
|
-
return "unknown";
|
|
2310
|
-
}
|
|
2311
|
-
function isOptionalChain(node) {
|
|
2312
|
-
if (!import_ts_morph7.Node.isCallExpression(node)) return false;
|
|
2313
|
-
const expr = node.getExpression();
|
|
2314
|
-
return import_ts_morph7.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2315
|
-
}
|
|
2316
|
-
function parseDefineContractCall(callExpr) {
|
|
2317
|
-
if (!import_ts_morph7.Node.isCallExpression(callExpr)) return null;
|
|
2318
|
-
const callee = callExpr.getExpression();
|
|
2319
|
-
const calleeName = import_ts_morph7.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph7.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2320
|
-
if (calleeName !== "defineContract") return null;
|
|
2321
|
-
const args = callExpr.getArguments();
|
|
2322
|
-
const optsArg = args[0];
|
|
2323
|
-
if (!optsArg || !import_ts_morph7.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2324
|
-
let query = null;
|
|
2325
|
-
let body = null;
|
|
2326
|
-
let response = "unknown";
|
|
2327
|
-
let error = null;
|
|
2328
|
-
let bodyZodText = null;
|
|
2329
|
-
let queryZodText = null;
|
|
2330
|
-
for (const prop of optsArg.getProperties()) {
|
|
2331
|
-
if (!import_ts_morph7.Node.isPropertyAssignment(prop)) continue;
|
|
2332
|
-
const propName = prop.getName();
|
|
2333
|
-
const val = prop.getInitializer();
|
|
2334
|
-
if (!val) continue;
|
|
2335
|
-
if (propName === "query") {
|
|
2336
|
-
query = zodAstToTs(val);
|
|
2337
|
-
queryZodText = val.getText();
|
|
2338
|
-
} else if (propName === "body") {
|
|
2339
|
-
body = zodAstToTs(val);
|
|
2340
|
-
bodyZodText = val.getText();
|
|
2341
|
-
} else if (propName === "response") {
|
|
2342
|
-
response = zodAstToTs(val);
|
|
2343
|
-
} else if (propName === "error") {
|
|
2344
|
-
error = zodAstToTs(val);
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
2924
|
// src/discovery/contracts-fast.ts
|
|
2351
2925
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
2352
2926
|
return tsconfig ? (0, import_node_path3.resolve)(tsconfig) : (0, import_node_path3.join)(cwd, "tsconfig.json");
|
|
@@ -2358,25 +2932,25 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
2358
2932
|
};
|
|
2359
2933
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
2360
2934
|
if (!(0, import_node_fs.existsSync)(tsconfigPath)) return { options: null, files: [] };
|
|
2361
|
-
const read =
|
|
2935
|
+
const read = import_ts_morph11.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph11.ts.sys.readFile(path));
|
|
2362
2936
|
if (read.error) {
|
|
2363
2937
|
return {
|
|
2364
2938
|
options: null,
|
|
2365
2939
|
files: [tsconfigPath],
|
|
2366
|
-
error:
|
|
2940
|
+
error: import_ts_morph11.ts.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
2367
2941
|
};
|
|
2368
2942
|
}
|
|
2369
2943
|
const host = {
|
|
2370
|
-
useCaseSensitiveFileNames:
|
|
2944
|
+
useCaseSensitiveFileNames: import_ts_morph11.ts.sys.useCaseSensitiveFileNames,
|
|
2371
2945
|
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
2372
2946
|
readDirectory: () => [],
|
|
2373
|
-
fileExists: (path) =>
|
|
2947
|
+
fileExists: (path) => import_ts_morph11.ts.sys.fileExists(path),
|
|
2374
2948
|
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
2375
2949
|
// in a base tsconfig keeps working.
|
|
2376
|
-
readFile: (path) =>
|
|
2950
|
+
readFile: (path) => import_ts_morph11.ts.sys.readFile(path)
|
|
2377
2951
|
};
|
|
2378
|
-
const sourceFile =
|
|
2379
|
-
const parsed =
|
|
2952
|
+
const sourceFile = import_ts_morph11.ts.readJsonConfigFile(tsconfigPath, (path) => import_ts_morph11.ts.sys.readFile(path));
|
|
2953
|
+
const parsed = import_ts_morph11.ts.parseJsonSourceFileConfigFileContent(
|
|
2380
2954
|
sourceFile,
|
|
2381
2955
|
host,
|
|
2382
2956
|
(0, import_node_path3.dirname)(tsconfigPath),
|
|
@@ -2399,7 +2973,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
|
|
|
2399
2973
|
`[nestjs-codegen/fast] Could not load ${tsconfigPath}: ${tsconfig.error} \u2014 continuing WITHOUT its compiler options, so path alias imports (e.g. '@/...') will not resolve and controllers extending an alias-imported factory will contribute NO routes to the generated client.`
|
|
2400
2974
|
);
|
|
2401
2975
|
}
|
|
2402
|
-
return new
|
|
2976
|
+
return new import_ts_morph11.Project({
|
|
2403
2977
|
compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
|
|
2404
2978
|
skipAddingFilesFromTsConfig: true,
|
|
2405
2979
|
skipLoadingLibFiles: true,
|
|
@@ -2504,15 +3078,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
2504
3078
|
clearTypeResolutionCaches(this.project);
|
|
2505
3079
|
clearEnumCache(this.project);
|
|
2506
3080
|
clearMixinTypeProject();
|
|
3081
|
+
clearSchemaTypeProject(this.project);
|
|
2507
3082
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
2508
3083
|
}
|
|
2509
3084
|
};
|
|
2510
3085
|
function decoratorStringArg(decoratorExpr) {
|
|
2511
3086
|
if (!decoratorExpr) return void 0;
|
|
2512
|
-
if (
|
|
2513
|
-
if (
|
|
3087
|
+
if (import_ts_morph11.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3088
|
+
if (import_ts_morph11.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
2514
3089
|
const first = decoratorExpr.getElements()[0];
|
|
2515
|
-
if (first &&
|
|
3090
|
+
if (first && import_ts_morph11.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
2516
3091
|
}
|
|
2517
3092
|
return void 0;
|
|
2518
3093
|
}
|
|
@@ -2633,9 +3208,9 @@ function extractContractRoute(args) {
|
|
|
2633
3208
|
let contractDef = null;
|
|
2634
3209
|
let bodyZodRef = null;
|
|
2635
3210
|
let queryZodRef = null;
|
|
2636
|
-
if (
|
|
2637
|
-
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
2638
|
-
} else if (
|
|
3211
|
+
if (import_ts_morph11.Node.isCallExpression(firstDecoratorArg)) {
|
|
3212
|
+
contractDef = parseDefineContractCall(firstDecoratorArg, project);
|
|
3213
|
+
} else if (import_ts_morph11.Node.isIdentifier(firstDecoratorArg)) {
|
|
2639
3214
|
const identName = firstDecoratorArg.getText();
|
|
2640
3215
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
2641
3216
|
if (!resolvedVar) {
|
|
@@ -2647,7 +3222,7 @@ function extractContractRoute(args) {
|
|
|
2647
3222
|
const { decl: varDecl, file: declFile } = resolvedVar;
|
|
2648
3223
|
const initializer = varDecl.getInitializer();
|
|
2649
3224
|
if (!initializer) return null;
|
|
2650
|
-
contractDef = parseDefineContractCall(initializer);
|
|
3225
|
+
contractDef = parseDefineContractCall(initializer, project);
|
|
2651
3226
|
if (contractDef && varDecl.isExported()) {
|
|
2652
3227
|
const filePath = declFile.getFilePath();
|
|
2653
3228
|
if (contractDef.body !== null) {
|
|
@@ -2718,6 +3293,7 @@ function extractDtoRoute(args) {
|
|
|
2718
3293
|
body: dtoContract?.body ?? null,
|
|
2719
3294
|
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
2720
3295
|
error: dtoContract?.error ?? null,
|
|
3296
|
+
paramTypes: dtoContract?.paramTypes ?? null,
|
|
2721
3297
|
queryRef: dtoContract?.queryRef ?? null,
|
|
2722
3298
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
2723
3299
|
responseRef: dtoContract?.responseRef ?? null,
|
|
@@ -2847,7 +3423,7 @@ function extractPropsSource(source, exportName) {
|
|
|
2847
3423
|
|
|
2848
3424
|
// src/discovery/shared-props.ts
|
|
2849
3425
|
var import_node_path5 = require("path");
|
|
2850
|
-
var
|
|
3426
|
+
var import_ts_morph12 = require("ts-morph");
|
|
2851
3427
|
function discoverSharedProps(project, moduleEntry) {
|
|
2852
3428
|
try {
|
|
2853
3429
|
let sourceFile = project.getSourceFile(moduleEntry);
|
|
@@ -2873,14 +3449,14 @@ function discoverSharedPropsFromConfig(config) {
|
|
|
2873
3449
|
const tsconfigPath = config.app.tsconfig ?? (0, import_node_path5.join)(config.codegen.cwd, "tsconfig.json");
|
|
2874
3450
|
let project;
|
|
2875
3451
|
try {
|
|
2876
|
-
project = new
|
|
3452
|
+
project = new import_ts_morph12.Project({
|
|
2877
3453
|
tsConfigFilePath: tsconfigPath,
|
|
2878
3454
|
skipAddingFilesFromTsConfig: true,
|
|
2879
3455
|
skipLoadingLibFiles: true,
|
|
2880
3456
|
skipFileDependencyResolution: true
|
|
2881
3457
|
});
|
|
2882
3458
|
} catch {
|
|
2883
|
-
project = new
|
|
3459
|
+
project = new import_ts_morph12.Project({
|
|
2884
3460
|
skipAddingFilesFromTsConfig: true,
|
|
2885
3461
|
skipLoadingLibFiles: true,
|
|
2886
3462
|
skipFileDependencyResolution: true,
|
|
@@ -2893,13 +3469,13 @@ function discoverSharedPropsFromConfig(config) {
|
|
|
2893
3469
|
}
|
|
2894
3470
|
}
|
|
2895
3471
|
function findForRootCall(sourceFile) {
|
|
2896
|
-
const callExpressions = sourceFile.getDescendantsOfKind(
|
|
3472
|
+
const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph12.SyntaxKind.CallExpression);
|
|
2897
3473
|
for (const call of callExpressions) {
|
|
2898
3474
|
const expr = call.getExpression();
|
|
2899
|
-
if (!
|
|
3475
|
+
if (!import_ts_morph12.Node.isPropertyAccessExpression(expr)) continue;
|
|
2900
3476
|
const methodName = expr.getName();
|
|
2901
3477
|
const objectExpr = expr.getExpression();
|
|
2902
|
-
if (methodName === "forRoot" &&
|
|
3478
|
+
if (methodName === "forRoot" && import_ts_morph12.Node.isIdentifier(objectExpr)) {
|
|
2903
3479
|
const name = objectExpr.getText();
|
|
2904
3480
|
if (name === "InertiaModule") {
|
|
2905
3481
|
return call;
|
|
@@ -2909,19 +3485,19 @@ function findForRootCall(sourceFile) {
|
|
|
2909
3485
|
return null;
|
|
2910
3486
|
}
|
|
2911
3487
|
function findShareInitializer(forRootCall) {
|
|
2912
|
-
if (!
|
|
3488
|
+
if (!import_ts_morph12.Node.isCallExpression(forRootCall)) return null;
|
|
2913
3489
|
const args = forRootCall.getArguments();
|
|
2914
3490
|
const firstArg2 = args[0];
|
|
2915
|
-
if (!firstArg2 || !
|
|
3491
|
+
if (!firstArg2 || !import_ts_morph12.Node.isObjectLiteralExpression(firstArg2)) return null;
|
|
2916
3492
|
for (const prop of firstArg2.getProperties()) {
|
|
2917
|
-
if (
|
|
3493
|
+
if (import_ts_morph12.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
2918
3494
|
return prop.getInitializer() ?? null;
|
|
2919
3495
|
}
|
|
2920
3496
|
}
|
|
2921
3497
|
return null;
|
|
2922
3498
|
}
|
|
2923
3499
|
function extractShareType(node, sourceFile, project) {
|
|
2924
|
-
if (
|
|
3500
|
+
if (import_ts_morph12.Node.isIdentifier(node)) {
|
|
2925
3501
|
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
2926
3502
|
if (ref) {
|
|
2927
3503
|
return {
|
|
@@ -2931,22 +3507,22 @@ function extractShareType(node, sourceFile, project) {
|
|
|
2931
3507
|
};
|
|
2932
3508
|
}
|
|
2933
3509
|
}
|
|
2934
|
-
if (
|
|
3510
|
+
if (import_ts_morph12.Node.isArrowFunction(node)) {
|
|
2935
3511
|
const result = extractFromFunctionLike(node, sourceFile);
|
|
2936
3512
|
return result ? { ...result, isImportRef: false } : null;
|
|
2937
3513
|
}
|
|
2938
|
-
if (
|
|
3514
|
+
if (import_ts_morph12.Node.isFunctionExpression(node)) {
|
|
2939
3515
|
const result = extractFromFunctionLike(node, sourceFile);
|
|
2940
3516
|
return result ? { ...result, isImportRef: false } : null;
|
|
2941
3517
|
}
|
|
2942
|
-
if (
|
|
3518
|
+
if (import_ts_morph12.Node.isObjectLiteralExpression(node)) {
|
|
2943
3519
|
const result = extractFromObjectLiteral(node);
|
|
2944
3520
|
return result ? { ...result, isImportRef: false } : null;
|
|
2945
3521
|
}
|
|
2946
3522
|
return null;
|
|
2947
3523
|
}
|
|
2948
3524
|
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
2949
|
-
if (!
|
|
3525
|
+
if (!import_ts_morph12.Node.isIdentifier(id)) return null;
|
|
2950
3526
|
const name = id.getText();
|
|
2951
3527
|
const localFunc = sourceFile.getFunction(name);
|
|
2952
3528
|
if (localFunc?.isExported()) {
|
|
@@ -2980,37 +3556,37 @@ function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
|
2980
3556
|
return null;
|
|
2981
3557
|
}
|
|
2982
3558
|
function extractFromFunctionLike(node, _sourceFile) {
|
|
2983
|
-
const returnTypeNode =
|
|
3559
|
+
const returnTypeNode = import_ts_morph12.Node.isArrowFunction(node) || import_ts_morph12.Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
2984
3560
|
if (returnTypeNode) {
|
|
2985
3561
|
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
2986
3562
|
}
|
|
2987
|
-
if (
|
|
3563
|
+
if (import_ts_morph12.Node.isArrowFunction(node)) {
|
|
2988
3564
|
const body = node.getBody();
|
|
2989
|
-
if (
|
|
3565
|
+
if (import_ts_morph12.Node.isParenthesizedExpression(body)) {
|
|
2990
3566
|
const inner = body.getExpression();
|
|
2991
|
-
if (
|
|
3567
|
+
if (import_ts_morph12.Node.isObjectLiteralExpression(inner)) {
|
|
2992
3568
|
return extractFromObjectLiteral(inner);
|
|
2993
3569
|
}
|
|
2994
3570
|
}
|
|
2995
|
-
if (
|
|
3571
|
+
if (import_ts_morph12.Node.isObjectLiteralExpression(body)) {
|
|
2996
3572
|
return extractFromObjectLiteral(body);
|
|
2997
3573
|
}
|
|
2998
|
-
if (
|
|
3574
|
+
if (import_ts_morph12.Node.isBlock(body)) {
|
|
2999
3575
|
return extractFromBlockReturn(body);
|
|
3000
3576
|
}
|
|
3001
3577
|
}
|
|
3002
|
-
if (
|
|
3578
|
+
if (import_ts_morph12.Node.isFunctionExpression(node)) {
|
|
3003
3579
|
const body = node.getBody();
|
|
3004
|
-
if (
|
|
3580
|
+
if (import_ts_morph12.Node.isBlock(body)) {
|
|
3005
3581
|
return extractFromBlockReturn(body);
|
|
3006
3582
|
}
|
|
3007
3583
|
}
|
|
3008
3584
|
return null;
|
|
3009
3585
|
}
|
|
3010
3586
|
function extractFromReturnTypeAnnotation(typeNode) {
|
|
3011
|
-
if (
|
|
3587
|
+
if (import_ts_morph12.Node.isTypeReference(typeNode)) {
|
|
3012
3588
|
const typeName = typeNode.getTypeName();
|
|
3013
|
-
if (
|
|
3589
|
+
if (import_ts_morph12.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
3014
3590
|
const typeArgs = typeNode.getTypeArguments();
|
|
3015
3591
|
const firstArg2 = typeArgs[0];
|
|
3016
3592
|
if (firstArg2) {
|
|
@@ -3019,10 +3595,10 @@ function extractFromReturnTypeAnnotation(typeNode) {
|
|
|
3019
3595
|
return null;
|
|
3020
3596
|
}
|
|
3021
3597
|
}
|
|
3022
|
-
if (
|
|
3598
|
+
if (import_ts_morph12.Node.isTypeLiteral(typeNode)) {
|
|
3023
3599
|
const properties = [];
|
|
3024
3600
|
for (const member of typeNode.getMembers()) {
|
|
3025
|
-
if (
|
|
3601
|
+
if (import_ts_morph12.Node.isPropertySignature(member)) {
|
|
3026
3602
|
const name = member.getName();
|
|
3027
3603
|
const memberTypeNode = member.getTypeNode();
|
|
3028
3604
|
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
@@ -3036,19 +3612,19 @@ function extractFromReturnTypeAnnotation(typeNode) {
|
|
|
3036
3612
|
return null;
|
|
3037
3613
|
}
|
|
3038
3614
|
function extractFromBlockReturn(block) {
|
|
3039
|
-
if (!
|
|
3615
|
+
if (!import_ts_morph12.Node.isBlock(block)) return null;
|
|
3040
3616
|
const statements = block.getStatements();
|
|
3041
3617
|
for (let i = statements.length - 1; i >= 0; i--) {
|
|
3042
3618
|
const stmt = statements[i];
|
|
3043
|
-
if (!
|
|
3619
|
+
if (!import_ts_morph12.Node.isReturnStatement(stmt)) continue;
|
|
3044
3620
|
const expr = stmt.getExpression();
|
|
3045
3621
|
if (!expr) continue;
|
|
3046
|
-
if (
|
|
3622
|
+
if (import_ts_morph12.Node.isObjectLiteralExpression(expr)) {
|
|
3047
3623
|
return extractFromObjectLiteral(expr);
|
|
3048
3624
|
}
|
|
3049
|
-
if (
|
|
3625
|
+
if (import_ts_morph12.Node.isParenthesizedExpression(expr)) {
|
|
3050
3626
|
const inner = expr.getExpression();
|
|
3051
|
-
if (
|
|
3627
|
+
if (import_ts_morph12.Node.isObjectLiteralExpression(inner)) {
|
|
3052
3628
|
return extractFromObjectLiteral(inner);
|
|
3053
3629
|
}
|
|
3054
3630
|
}
|
|
@@ -3057,10 +3633,10 @@ function extractFromBlockReturn(block) {
|
|
|
3057
3633
|
return null;
|
|
3058
3634
|
}
|
|
3059
3635
|
function extractFromObjectLiteral(objLiteral) {
|
|
3060
|
-
if (!
|
|
3636
|
+
if (!import_ts_morph12.Node.isObjectLiteralExpression(objLiteral)) return null;
|
|
3061
3637
|
const properties = [];
|
|
3062
3638
|
for (const prop of objLiteral.getProperties()) {
|
|
3063
|
-
if (!
|
|
3639
|
+
if (!import_ts_morph12.Node.isPropertyAssignment(prop)) continue;
|
|
3064
3640
|
const name = prop.getName();
|
|
3065
3641
|
const initializer = prop.getInitializer();
|
|
3066
3642
|
if (!initializer) continue;
|
|
@@ -3072,21 +3648,21 @@ function extractFromObjectLiteral(objLiteral) {
|
|
|
3072
3648
|
return { typeString, properties, isImportRef: false };
|
|
3073
3649
|
}
|
|
3074
3650
|
function inferExpressionType(node) {
|
|
3075
|
-
if (
|
|
3076
|
-
if (
|
|
3651
|
+
if (import_ts_morph12.Node.isStringLiteral(node)) return "string";
|
|
3652
|
+
if (import_ts_morph12.Node.isTemplateExpression(node) || import_ts_morph12.Node.isNoSubstitutionTemplateLiteral(node))
|
|
3077
3653
|
return "string";
|
|
3078
|
-
if (
|
|
3079
|
-
if (node.getKind() ===
|
|
3654
|
+
if (import_ts_morph12.Node.isNumericLiteral(node)) return "number";
|
|
3655
|
+
if (node.getKind() === import_ts_morph12.SyntaxKind.TrueKeyword || node.getKind() === import_ts_morph12.SyntaxKind.FalseKeyword) {
|
|
3080
3656
|
return "boolean";
|
|
3081
3657
|
}
|
|
3082
|
-
if (node.getKind() ===
|
|
3083
|
-
if (
|
|
3084
|
-
if (
|
|
3658
|
+
if (node.getKind() === import_ts_morph12.SyntaxKind.NullKeyword) return "null";
|
|
3659
|
+
if (import_ts_morph12.Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
3660
|
+
if (import_ts_morph12.Node.isObjectLiteralExpression(node)) {
|
|
3085
3661
|
const props = node.getProperties();
|
|
3086
3662
|
if (props.length === 0) return "Record<string, unknown>";
|
|
3087
3663
|
const entries = [];
|
|
3088
3664
|
for (const prop of props) {
|
|
3089
|
-
if (!
|
|
3665
|
+
if (!import_ts_morph12.Node.isPropertyAssignment(prop)) continue;
|
|
3090
3666
|
const key = prop.getName();
|
|
3091
3667
|
const init = prop.getInitializer();
|
|
3092
3668
|
if (!init) continue;
|
|
@@ -3095,23 +3671,23 @@ function inferExpressionType(node) {
|
|
|
3095
3671
|
if (entries.length === 0) return "Record<string, unknown>";
|
|
3096
3672
|
return `{ ${entries.join("; ")} }`;
|
|
3097
3673
|
}
|
|
3098
|
-
if (
|
|
3674
|
+
if (import_ts_morph12.Node.isArrayLiteralExpression(node)) {
|
|
3099
3675
|
const elements = node.getElements();
|
|
3100
3676
|
if (elements.length === 0) return "Array<unknown>";
|
|
3101
3677
|
const first = elements[0];
|
|
3102
3678
|
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
3103
3679
|
return "Array<unknown>";
|
|
3104
3680
|
}
|
|
3105
|
-
if (
|
|
3681
|
+
if (import_ts_morph12.Node.isConditionalExpression(node)) {
|
|
3106
3682
|
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
3107
3683
|
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
3108
3684
|
if (whenTrue === whenFalse) return whenTrue;
|
|
3109
3685
|
return `${whenTrue} | ${whenFalse}`;
|
|
3110
3686
|
}
|
|
3111
|
-
if (
|
|
3687
|
+
if (import_ts_morph12.Node.isParenthesizedExpression(node)) {
|
|
3112
3688
|
return inferExpressionType(node.getExpression());
|
|
3113
3689
|
}
|
|
3114
|
-
if (
|
|
3690
|
+
if (import_ts_morph12.Node.isAsExpression(node)) {
|
|
3115
3691
|
const typeNode = node.getTypeNode();
|
|
3116
3692
|
if (typeNode) return typeNode.getText();
|
|
3117
3693
|
}
|
|
@@ -3124,7 +3700,7 @@ var import_node_path7 = require("path");
|
|
|
3124
3700
|
|
|
3125
3701
|
// src/extension/registry.ts
|
|
3126
3702
|
var import_node_path6 = require("path");
|
|
3127
|
-
var
|
|
3703
|
+
var import_ts_morph13 = require("ts-morph");
|
|
3128
3704
|
function resolveApiSlots(extensions) {
|
|
3129
3705
|
let layer;
|
|
3130
3706
|
let layerOwner;
|
|
@@ -3175,7 +3751,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
|
|
|
3175
3751
|
},
|
|
3176
3752
|
project() {
|
|
3177
3753
|
if (!project) {
|
|
3178
|
-
project = new
|
|
3754
|
+
project = new import_ts_morph13.Project({
|
|
3179
3755
|
skipAddingFilesFromTsConfig: true,
|
|
3180
3756
|
skipLoadingLibFiles: true,
|
|
3181
3757
|
skipFileDependencyResolution: true,
|
|
@@ -3293,10 +3869,11 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
3293
3869
|
insertIntoTree(branch.children, rest, leaf, fullName);
|
|
3294
3870
|
}
|
|
3295
3871
|
}
|
|
3296
|
-
function buildParamsType(params) {
|
|
3872
|
+
function buildParamsType(params, paramTypes) {
|
|
3297
3873
|
const pathParams = params.filter((p) => p.source === "path");
|
|
3298
3874
|
if (pathParams.length === 0) return "never";
|
|
3299
|
-
|
|
3875
|
+
const typeOf = (name) => paramTypes && Object.hasOwn(paramTypes, name) && paramTypes[name] || "string";
|
|
3876
|
+
return `{ ${pathParams.map((p) => `${p.name}: ${typeOf(p.name)}`).join("; ")} }`;
|
|
3300
3877
|
}
|
|
3301
3878
|
function hasPathParams(params) {
|
|
3302
3879
|
return params.some((p) => p.source === "path");
|
|
@@ -3390,7 +3967,7 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
|
3390
3967
|
}
|
|
3391
3968
|
const response = buildResponseType(c, outDir, serialization);
|
|
3392
3969
|
const error = buildErrorType(c);
|
|
3393
|
-
const params = buildParamsType(c.params);
|
|
3970
|
+
const params = buildParamsType(c.params, c.contractSource.paramTypes);
|
|
3394
3971
|
const safeMethod = JSON.stringify(method);
|
|
3395
3972
|
const safeUrl = JSON.stringify(c.path);
|
|
3396
3973
|
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
@@ -3659,6 +4236,9 @@ var EMPTY_PATH_NAMESPACE = [
|
|
|
3659
4236
|
"}",
|
|
3660
4237
|
""
|
|
3661
4238
|
];
|
|
4239
|
+
function isClientCallable(route) {
|
|
4240
|
+
return route.method.toUpperCase() !== "ALL";
|
|
4241
|
+
}
|
|
3662
4242
|
function buildApiFile(routes, outDir, opts = {}) {
|
|
3663
4243
|
const fetcherImportPath = opts.fetcherImportPath;
|
|
3664
4244
|
const serialization = opts.serialization ?? "json";
|
|
@@ -3666,7 +4246,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
3666
4246
|
const { layer } = resolveApiSlots(extensions);
|
|
3667
4247
|
const memberExts = extensions.filter((e) => e.apiMembers);
|
|
3668
4248
|
const headerExts = extensions.filter((e) => e.apiHeader);
|
|
3669
|
-
const contracted = routes.filter((r) => r.contract);
|
|
4249
|
+
const contracted = routes.filter((r) => r.contract && isClientCallable(r));
|
|
3670
4250
|
const ctx = opts.ctx ?? {
|
|
3671
4251
|
cwd: outDir ?? "",
|
|
3672
4252
|
outDir: outDir ?? "",
|
|
@@ -4584,6 +5164,7 @@ function buildOpenApiSpec(routes, opts = {}) {
|
|
|
4584
5164
|
const paths = {};
|
|
4585
5165
|
for (const route of routes) {
|
|
4586
5166
|
if (!route.contract) continue;
|
|
5167
|
+
if (route.method.toUpperCase() === "ALL") continue;
|
|
4587
5168
|
const oaPath = toOpenApiPath(route.path);
|
|
4588
5169
|
const method = route.method.toLowerCase();
|
|
4589
5170
|
let pathItem = paths[oaPath];
|
|
@@ -5085,7 +5666,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
5085
5666
|
}
|
|
5086
5667
|
|
|
5087
5668
|
// src/index.ts
|
|
5088
|
-
var VERSION = "0.
|
|
5669
|
+
var VERSION = "0.26.0";
|
|
5089
5670
|
|
|
5090
5671
|
// src/generate-manifest.ts
|
|
5091
5672
|
var MANIFEST_FILE = ".codegen-manifest.json";
|