@dudousxd/nestjs-codegen 0.25.2 → 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 +24 -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 +11 -7
package/dist/nest/index.js
CHANGED
|
@@ -149,15 +149,15 @@ import { existsSync } from "fs";
|
|
|
149
149
|
import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
|
|
150
150
|
import fg from "fast-glob";
|
|
151
151
|
import {
|
|
152
|
-
Node as
|
|
153
|
-
Project as
|
|
154
|
-
ts
|
|
152
|
+
Node as Node10,
|
|
153
|
+
Project as Project3,
|
|
154
|
+
ts as ts3
|
|
155
155
|
} from "ts-morph";
|
|
156
156
|
|
|
157
157
|
// src/discovery/dto-type-resolver.ts
|
|
158
158
|
import {
|
|
159
|
-
Node as
|
|
160
|
-
SyntaxKind as
|
|
159
|
+
Node as Node9,
|
|
160
|
+
SyntaxKind as SyntaxKind3
|
|
161
161
|
} from "ts-morph";
|
|
162
162
|
|
|
163
163
|
// src/discovery/dto-to-ir.ts
|
|
@@ -1695,6 +1695,674 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
1695
1695
|
return fields;
|
|
1696
1696
|
}
|
|
1697
1697
|
|
|
1698
|
+
// src/discovery/pipe-schema.ts
|
|
1699
|
+
import { Node as Node8 } from "ts-morph";
|
|
1700
|
+
|
|
1701
|
+
// src/discovery/schema-type-inference.ts
|
|
1702
|
+
import {
|
|
1703
|
+
ts as ts2
|
|
1704
|
+
} from "ts-morph";
|
|
1705
|
+
|
|
1706
|
+
// src/discovery/schema-type-program.ts
|
|
1707
|
+
import { Node as Node6, Project as Project2, ts } from "ts-morph";
|
|
1708
|
+
var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
|
|
1709
|
+
var SchemaTypeProgram = class {
|
|
1710
|
+
constructor(discovery) {
|
|
1711
|
+
this.discovery = discovery;
|
|
1712
|
+
const base = discovery.getCompilerOptions();
|
|
1713
|
+
this.options = {
|
|
1714
|
+
...base,
|
|
1715
|
+
// A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
|
|
1716
|
+
// `Promise` for the schema libraries' declarations to lean on.
|
|
1717
|
+
target: base.target ?? ts.ScriptTarget.ES2022,
|
|
1718
|
+
// With neither set, resolve the way a bundler does: package `exports`
|
|
1719
|
+
// (`zod/v4`) and extensionless or `.js` relative imports all resolve.
|
|
1720
|
+
...base.module === void 0 && base.moduleResolution === void 0 ? { module: ts.ModuleKind.ESNext, moduleResolution: ts.ModuleResolutionKind.Bundler } : {},
|
|
1721
|
+
// Without it `.nullable()` and `.optional()` would erase to the bare type.
|
|
1722
|
+
strictNullChecks: true,
|
|
1723
|
+
// No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
|
|
1724
|
+
// need them, and they are a hundred-odd files of program to build.
|
|
1725
|
+
types: [],
|
|
1726
|
+
noEmit: true
|
|
1727
|
+
};
|
|
1728
|
+
this.project = new Project2({
|
|
1729
|
+
skipAddingFilesFromTsConfig: true,
|
|
1730
|
+
compilerOptions: this.options
|
|
1731
|
+
});
|
|
1732
|
+
for (const sf of discovery.getSourceFiles()) this.request(sf.getFilePath(), { schema: true });
|
|
1733
|
+
this.flush();
|
|
1734
|
+
}
|
|
1735
|
+
project;
|
|
1736
|
+
options;
|
|
1737
|
+
/** Parses a file that is not in discovery's Project, without adding it there. */
|
|
1738
|
+
scratch = new Project2({
|
|
1739
|
+
skipAddingFilesFromTsConfig: true,
|
|
1740
|
+
skipLoadingLibFiles: true,
|
|
1741
|
+
skipFileDependencyResolution: true
|
|
1742
|
+
});
|
|
1743
|
+
needs = /* @__PURE__ */ new Map();
|
|
1744
|
+
/** Pruned text per file, as last written into {@link project}. */
|
|
1745
|
+
written = /* @__PURE__ */ new Map();
|
|
1746
|
+
/**
|
|
1747
|
+
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
1748
|
+
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
1749
|
+
* program once.
|
|
1750
|
+
*/
|
|
1751
|
+
sourceFileFor(sf) {
|
|
1752
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
1753
|
+
this.flush();
|
|
1754
|
+
return this.project.getSourceFile(sf.getFilePath());
|
|
1755
|
+
}
|
|
1756
|
+
// -------------------------------------------------------------------------
|
|
1757
|
+
queue = [];
|
|
1758
|
+
request(path, want) {
|
|
1759
|
+
let needs = this.needs.get(path);
|
|
1760
|
+
const isNew = !needs;
|
|
1761
|
+
needs ??= { schema: false, all: false, names: /* @__PURE__ */ new Set() };
|
|
1762
|
+
let grew = isNew;
|
|
1763
|
+
if (want.schema && !needs.schema) {
|
|
1764
|
+
needs.schema = true;
|
|
1765
|
+
grew = true;
|
|
1766
|
+
}
|
|
1767
|
+
if (want.all && !needs.all) {
|
|
1768
|
+
needs.all = true;
|
|
1769
|
+
grew = true;
|
|
1770
|
+
}
|
|
1771
|
+
for (const name of want.names ?? []) {
|
|
1772
|
+
if (!needs.names.has(name)) {
|
|
1773
|
+
needs.names.add(name);
|
|
1774
|
+
grew = true;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
this.needs.set(path, needs);
|
|
1778
|
+
if (grew) this.queue.push(path);
|
|
1779
|
+
}
|
|
1780
|
+
/** Run the worklist to a fixpoint, then write every changed file into the program. */
|
|
1781
|
+
flush() {
|
|
1782
|
+
const changed = /* @__PURE__ */ new Map();
|
|
1783
|
+
while (this.queue.length > 0) {
|
|
1784
|
+
const path = this.queue.pop();
|
|
1785
|
+
const original = this.original(path);
|
|
1786
|
+
if (!original) continue;
|
|
1787
|
+
const needs = this.needs.get(path);
|
|
1788
|
+
const text = this.prune(original, needs);
|
|
1789
|
+
if (this.written.get(path) !== text) changed.set(path, text);
|
|
1790
|
+
}
|
|
1791
|
+
for (const [path, text] of changed) {
|
|
1792
|
+
this.project.createSourceFile(path, text, { overwrite: true });
|
|
1793
|
+
this.written.set(path, text);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
/** The file as discovery sees it (its in-memory text), else as it is on disk. */
|
|
1797
|
+
original(path) {
|
|
1798
|
+
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
1802
|
+
* requesting from each kept one's target the names it brings in.
|
|
1803
|
+
*/
|
|
1804
|
+
prune(sf, needs) {
|
|
1805
|
+
const locals = topLevelDeclarations(sf);
|
|
1806
|
+
const roots = [];
|
|
1807
|
+
if (needs.all) roots.push(...sf.getStatements());
|
|
1808
|
+
if (needs.schema) roots.push(...schemaPositions(sf));
|
|
1809
|
+
const reexported = /* @__PURE__ */ new Map();
|
|
1810
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
1811
|
+
const spec = exp.getModuleSpecifierValue();
|
|
1812
|
+
if (spec === void 0) {
|
|
1813
|
+
for (const named of exp.getNamedExports()) {
|
|
1814
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
1815
|
+
if (needs.all || needs.names.has(exposed)) roots.push(named.getNameNode());
|
|
1816
|
+
}
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) continue;
|
|
1820
|
+
for (const named of exp.getNamedExports()) {
|
|
1821
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
1822
|
+
const list = reexported.get(exposed) ?? [];
|
|
1823
|
+
list.push({ from: spec, name: named.getNameNode().getText() });
|
|
1824
|
+
reexported.set(exposed, list);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
const unresolvedNames = [];
|
|
1828
|
+
for (const name of needs.names) {
|
|
1829
|
+
const decls = name === "default" ? defaultExport(sf) : locals.get(name) ?? [];
|
|
1830
|
+
if (decls.length > 0) roots.push(...decls);
|
|
1831
|
+
else if (!reexported.has(name)) unresolvedNames.push(name);
|
|
1832
|
+
}
|
|
1833
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
1834
|
+
const queue = [...roots];
|
|
1835
|
+
const seen = new Set(queue);
|
|
1836
|
+
while (queue.length > 0) {
|
|
1837
|
+
const node = queue.pop();
|
|
1838
|
+
const ids = Node6.isIdentifier(node) ? [node] : node.getDescendantsOfKind(ts.SyntaxKind.Identifier);
|
|
1839
|
+
for (const id of ids) {
|
|
1840
|
+
const name = id.getText();
|
|
1841
|
+
if (referenced.has(name)) continue;
|
|
1842
|
+
referenced.add(name);
|
|
1843
|
+
for (const local of locals.get(name) ?? []) {
|
|
1844
|
+
if (seen.has(local)) continue;
|
|
1845
|
+
seen.add(local);
|
|
1846
|
+
queue.push(local);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
const chars = sf.getFullText().split("");
|
|
1851
|
+
const blank = (node) => {
|
|
1852
|
+
for (let i = node.getStart(); i < node.getEnd(); i++) {
|
|
1853
|
+
if (chars[i] !== "\n" && chars[i] !== "\r") chars[i] = " ";
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
1857
|
+
const target = this.resolve(decl.getModuleSpecifierValue(), sf);
|
|
1858
|
+
const namespace = decl.getNamespaceImport()?.getText();
|
|
1859
|
+
const def = decl.getDefaultImport()?.getText();
|
|
1860
|
+
const named = decl.getNamedImports().map((n) => ({
|
|
1861
|
+
local: (n.getAliasNode() ?? n.getNameNode()).getText(),
|
|
1862
|
+
imported: n.getName()
|
|
1863
|
+
})).filter((n) => referenced.has(n.local));
|
|
1864
|
+
const wantsNamespace = namespace !== void 0 && referenced.has(namespace);
|
|
1865
|
+
const wantsDefault = def !== void 0 && referenced.has(def);
|
|
1866
|
+
if (!wantsNamespace && !wantsDefault && named.length === 0) {
|
|
1867
|
+
blank(decl);
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
if (target) {
|
|
1871
|
+
this.request(target, {
|
|
1872
|
+
all: wantsNamespace,
|
|
1873
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
1878
|
+
const spec = exp.getModuleSpecifierValue();
|
|
1879
|
+
if (spec === void 0) continue;
|
|
1880
|
+
const target = this.resolve(spec, sf);
|
|
1881
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
1882
|
+
if (!needs.all && unresolvedNames.length === 0) {
|
|
1883
|
+
blank(exp);
|
|
1884
|
+
} else if (target) {
|
|
1885
|
+
this.request(target, { all: needs.all, names: unresolvedNames });
|
|
1886
|
+
}
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
1889
|
+
const wanted = exp.getNamedExports().filter(
|
|
1890
|
+
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
1891
|
+
).map((n) => n.getNameNode().getText());
|
|
1892
|
+
if (wanted.length === 0) blank(exp);
|
|
1893
|
+
else if (target) this.request(target, { names: wanted });
|
|
1894
|
+
}
|
|
1895
|
+
return chars.join("");
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* The project source file a module specifier resolves to, or undefined for a
|
|
1899
|
+
* package (read unpruned by the program itself) or an unresolvable one.
|
|
1900
|
+
*/
|
|
1901
|
+
resolve(specifier, from) {
|
|
1902
|
+
const resolved = ts.resolveModuleName(
|
|
1903
|
+
specifier,
|
|
1904
|
+
from.getFilePath(),
|
|
1905
|
+
this.options,
|
|
1906
|
+
ts.sys
|
|
1907
|
+
).resolvedModule;
|
|
1908
|
+
if (!resolved || resolved.isExternalLibraryImport) return void 0;
|
|
1909
|
+
const file = resolved.resolvedFileName;
|
|
1910
|
+
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
1911
|
+
return file;
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
function schemaPositions(sf) {
|
|
1915
|
+
const roots = [];
|
|
1916
|
+
sf.forEachDescendant((node) => {
|
|
1917
|
+
if (Node6.isDecorator(node)) {
|
|
1918
|
+
const name = node.getName();
|
|
1919
|
+
if (name === "ApplyContract") {
|
|
1920
|
+
roots.push(...node.getArguments());
|
|
1921
|
+
} else if (PARAM_DECORATORS.has(name)) {
|
|
1922
|
+
for (const arg of node.getArguments()) {
|
|
1923
|
+
if (Node6.isNewExpression(arg) || Node6.isCallExpression(arg)) {
|
|
1924
|
+
roots.push(...arg.getArguments());
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
} else if (Node6.isCallExpression(node)) {
|
|
1929
|
+
const callee = node.getExpression();
|
|
1930
|
+
const name = Node6.isIdentifier(callee) ? callee.getText() : Node6.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
|
|
1931
|
+
if (name === "defineContract") roots.push(...node.getArguments());
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1934
|
+
return roots;
|
|
1935
|
+
}
|
|
1936
|
+
function topLevelDeclarations(sf) {
|
|
1937
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1938
|
+
const add = (name, node) => {
|
|
1939
|
+
if (!name) return;
|
|
1940
|
+
const list = byName.get(name);
|
|
1941
|
+
if (list) list.push(node);
|
|
1942
|
+
else byName.set(name, [node]);
|
|
1943
|
+
};
|
|
1944
|
+
for (const decl of sf.getVariableDeclarations()) add(decl.getName(), decl);
|
|
1945
|
+
for (const decl of sf.getFunctions()) add(decl.getName(), decl);
|
|
1946
|
+
for (const decl of sf.getClasses()) add(decl.getName(), decl);
|
|
1947
|
+
for (const decl of sf.getEnums()) add(decl.getName(), decl);
|
|
1948
|
+
for (const decl of sf.getInterfaces()) add(decl.getName(), decl);
|
|
1949
|
+
for (const decl of sf.getTypeAliases()) add(decl.getName(), decl);
|
|
1950
|
+
for (const decl of sf.getModules()) add(decl.getName(), decl);
|
|
1951
|
+
return byName;
|
|
1952
|
+
}
|
|
1953
|
+
function defaultExport(sf) {
|
|
1954
|
+
const nodes = [];
|
|
1955
|
+
for (const assignment of sf.getExportAssignments()) nodes.push(assignment);
|
|
1956
|
+
for (const fn of sf.getFunctions()) if (fn.isDefaultExport()) nodes.push(fn);
|
|
1957
|
+
for (const cls of sf.getClasses()) if (cls.isDefaultExport()) nodes.push(cls);
|
|
1958
|
+
return nodes;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// src/discovery/schema-type-inference.ts
|
|
1962
|
+
var programs = /* @__PURE__ */ new WeakMap();
|
|
1963
|
+
function programFor(discovery) {
|
|
1964
|
+
let program = programs.get(discovery);
|
|
1965
|
+
if (!program) {
|
|
1966
|
+
program = new SchemaTypeProgram(discovery);
|
|
1967
|
+
programs.set(discovery, program);
|
|
1968
|
+
}
|
|
1969
|
+
return program;
|
|
1970
|
+
}
|
|
1971
|
+
function clearSchemaTypeProject(discovery) {
|
|
1972
|
+
programs.delete(discovery);
|
|
1973
|
+
}
|
|
1974
|
+
function counterpart(typedSf, node) {
|
|
1975
|
+
const start = node.getStart();
|
|
1976
|
+
const end = node.getEnd();
|
|
1977
|
+
const kind = node.getKind();
|
|
1978
|
+
let candidate = typedSf.getDescendantAtPos(start);
|
|
1979
|
+
while (candidate) {
|
|
1980
|
+
if (candidate.getStart() === start && candidate.getEnd() === end && candidate.getKind() === kind) {
|
|
1981
|
+
return candidate;
|
|
1982
|
+
}
|
|
1983
|
+
if (candidate.getStart() < start || candidate.getEnd() > end) return void 0;
|
|
1984
|
+
candidate = candidate.getParent();
|
|
1985
|
+
}
|
|
1986
|
+
return void 0;
|
|
1987
|
+
}
|
|
1988
|
+
function propType(type, name, at) {
|
|
1989
|
+
return type.getProperty(name)?.getTypeAtLocation(at);
|
|
1990
|
+
}
|
|
1991
|
+
function isAnyOrUnknown(type) {
|
|
1992
|
+
return type.isAny() || type.isUnknown();
|
|
1993
|
+
}
|
|
1994
|
+
function inferSchema(expr, discovery) {
|
|
1995
|
+
try {
|
|
1996
|
+
const typedSf = programFor(discovery).sourceFileFor(expr.getSourceFile());
|
|
1997
|
+
const at = typedSf && counterpart(typedSf, expr);
|
|
1998
|
+
if (!at) return { kind: "unresolved" };
|
|
1999
|
+
const schemaType = at.getType();
|
|
2000
|
+
if (isAnyOrUnknown(schemaType)) return { kind: "unresolved" };
|
|
2001
|
+
const standard = propType(schemaType, "~standard", at);
|
|
2002
|
+
const stdTypes = standard ? propType(standard, "types", at)?.getNonNullableType() : void 0;
|
|
2003
|
+
const stdInput = stdTypes ? propType(stdTypes, "input", at) : void 0;
|
|
2004
|
+
const stdOutput = stdTypes ? propType(stdTypes, "output", at) : void 0;
|
|
2005
|
+
if (stdInput && stdOutput) {
|
|
2006
|
+
return { kind: "schema", types: { input: stdInput, output: stdOutput, at } };
|
|
2007
|
+
}
|
|
2008
|
+
const zodInput = propType(schemaType, "_input", at);
|
|
2009
|
+
const zodOutput = propType(schemaType, "_output", at);
|
|
2010
|
+
if (zodInput && zodOutput) {
|
|
2011
|
+
return { kind: "schema", types: { input: zodInput, output: zodOutput, at } };
|
|
2012
|
+
}
|
|
2013
|
+
return { kind: "not-schema" };
|
|
2014
|
+
} catch (err) {
|
|
2015
|
+
dbg("schema inference failed:", err);
|
|
2016
|
+
return { kind: "unresolved" };
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
var GLOBAL_NAMED_TYPES = /* @__PURE__ */ new Set([
|
|
2020
|
+
"Date",
|
|
2021
|
+
"File",
|
|
2022
|
+
"Blob",
|
|
2023
|
+
"FormData",
|
|
2024
|
+
"URL",
|
|
2025
|
+
"RegExp",
|
|
2026
|
+
"Map",
|
|
2027
|
+
"Set",
|
|
2028
|
+
"ArrayBuffer",
|
|
2029
|
+
"Uint8Array"
|
|
2030
|
+
]);
|
|
2031
|
+
var MAX_DEPTH = 12;
|
|
2032
|
+
function objectKey(name) {
|
|
2033
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2034
|
+
}
|
|
2035
|
+
function isOptionalSymbol(sym) {
|
|
2036
|
+
return (sym.getFlags() & ts2.SymbolFlags.Optional) !== 0;
|
|
2037
|
+
}
|
|
2038
|
+
function isSymbolKeyed(sym) {
|
|
2039
|
+
return sym.getName().startsWith("__@");
|
|
2040
|
+
}
|
|
2041
|
+
function orderedProperties(type) {
|
|
2042
|
+
const position = (sym) => {
|
|
2043
|
+
const decl = sym.getDeclarations()[0];
|
|
2044
|
+
return decl ? [decl.getSourceFile().getFilePath(), decl.getStart()] : void 0;
|
|
2045
|
+
};
|
|
2046
|
+
return type.getProperties().map((sym, index) => ({ sym, index, at: position(sym) })).sort((a, b) => {
|
|
2047
|
+
if (a.at && b.at) {
|
|
2048
|
+
if (a.at[0] !== b.at[0]) return a.at[0] < b.at[0] ? -1 : 1;
|
|
2049
|
+
if (a.at[1] !== b.at[1]) return a.at[1] - b.at[1];
|
|
2050
|
+
} else if (a.at || b.at) {
|
|
2051
|
+
return a.at ? -1 : 1;
|
|
2052
|
+
}
|
|
2053
|
+
return a.index - b.index;
|
|
2054
|
+
}).map((entry) => entry.sym);
|
|
2055
|
+
}
|
|
2056
|
+
function isFromDeclarationFile(sym) {
|
|
2057
|
+
const decl = sym?.getDeclarations()[0];
|
|
2058
|
+
return !!decl && decl.getSourceFile().isDeclarationFile();
|
|
2059
|
+
}
|
|
2060
|
+
function render(type, state, paired) {
|
|
2061
|
+
if (state.depth > MAX_DEPTH) return "unknown";
|
|
2062
|
+
if (isAnyOrUnknown(type)) {
|
|
2063
|
+
return paired && !isAnyOrUnknown(paired) ? render(paired, state) : "unknown";
|
|
2064
|
+
}
|
|
2065
|
+
if (type.isNever()) return "never";
|
|
2066
|
+
if (type.isString() || type.isTemplateLiteral()) return "string";
|
|
2067
|
+
if (type.isNumber()) return "number";
|
|
2068
|
+
if (type.isBoolean()) return "boolean";
|
|
2069
|
+
if (type.isBigInt()) return "bigint";
|
|
2070
|
+
if (type.isNull()) return "null";
|
|
2071
|
+
if (type.isUndefined() || type.isVoid()) return "undefined";
|
|
2072
|
+
if (type.isBooleanLiteral()) return type.getText();
|
|
2073
|
+
if (type.isStringLiteral() || type.isNumberLiteral() || type.isEnumLiteral()) {
|
|
2074
|
+
const value = type.getLiteralValue();
|
|
2075
|
+
return typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
2076
|
+
}
|
|
2077
|
+
if (type.isBigIntLiteral()) return type.getText();
|
|
2078
|
+
if (type.isTypeParameter()) return "unknown";
|
|
2079
|
+
if (type.isUnion()) return renderUnion(type, state, paired);
|
|
2080
|
+
if (type.isIntersection()) return renderIntersection(type, state);
|
|
2081
|
+
const next = { ...state, depth: state.depth + 1 };
|
|
2082
|
+
if (type.isArray() || type.isReadonlyArray()) {
|
|
2083
|
+
const element = type.getArrayElementType() ?? type.getTypeArguments()[0];
|
|
2084
|
+
const pairedElement = paired?.getArrayElementType() ?? paired?.getTypeArguments()[0];
|
|
2085
|
+
return element ? `Array<${render(element, next, pairedElement)}>` : "Array<unknown>";
|
|
2086
|
+
}
|
|
2087
|
+
if (type.isTuple()) {
|
|
2088
|
+
const elements = type.getTupleElements();
|
|
2089
|
+
const pairedElements = paired?.isTuple() ? paired.getTupleElements() : [];
|
|
2090
|
+
return `[${elements.map((e, i) => render(e, next, pairedElements[i])).join(", ")}]`;
|
|
2091
|
+
}
|
|
2092
|
+
if (type.isObject()) return renderObject(type, next, paired);
|
|
2093
|
+
return "unknown";
|
|
2094
|
+
}
|
|
2095
|
+
function renderUnion(type, state, paired) {
|
|
2096
|
+
const members = type.getUnionTypes();
|
|
2097
|
+
const hasTrue = members.some((m) => m.isBooleanLiteral() && m.getText() === "true");
|
|
2098
|
+
const hasFalse = members.some((m) => m.isBooleanLiteral() && m.getText() === "false");
|
|
2099
|
+
const pairedNonNull = paired?.getNonNullableType();
|
|
2100
|
+
const arms = [];
|
|
2101
|
+
let sawBoolean = false;
|
|
2102
|
+
let hasNull = false;
|
|
2103
|
+
let hasUndefined = false;
|
|
2104
|
+
for (const member of members) {
|
|
2105
|
+
if (member.isNull()) {
|
|
2106
|
+
hasNull = true;
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
if (member.isUndefined()) {
|
|
2110
|
+
hasUndefined = true;
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
if (member.isBooleanLiteral() && hasTrue && hasFalse) {
|
|
2114
|
+
if (!sawBoolean) arms.push("boolean");
|
|
2115
|
+
sawBoolean = true;
|
|
2116
|
+
continue;
|
|
2117
|
+
}
|
|
2118
|
+
arms.push(render(member, state, members.length === 1 ? paired : pairedNonNull));
|
|
2119
|
+
}
|
|
2120
|
+
if (hasNull) arms.push("null");
|
|
2121
|
+
if (hasUndefined) arms.push("undefined");
|
|
2122
|
+
const unique = [...new Set(arms)];
|
|
2123
|
+
if (unique.includes("unknown")) return "unknown";
|
|
2124
|
+
return unique.join(" | ");
|
|
2125
|
+
}
|
|
2126
|
+
function renderIntersection(type, state) {
|
|
2127
|
+
const members = type.getIntersectionTypes();
|
|
2128
|
+
const primitive = members.find((m) => !m.isObject());
|
|
2129
|
+
if (primitive) return render(primitive, state);
|
|
2130
|
+
return renderObject(type, { ...state, depth: state.depth + 1 }, void 0);
|
|
2131
|
+
}
|
|
2132
|
+
function renderObject(type, state, paired) {
|
|
2133
|
+
const symbol = type.getSymbol() ?? type.getAliasSymbol();
|
|
2134
|
+
const name = symbol?.getName();
|
|
2135
|
+
if (name && GLOBAL_NAMED_TYPES.has(name) && isFromDeclarationFile(symbol)) {
|
|
2136
|
+
const args = type.getTypeArguments();
|
|
2137
|
+
return args.length ? `${name}<${args.map((a) => render(a, state)).join(", ")}>` : name;
|
|
2138
|
+
}
|
|
2139
|
+
if (type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0) {
|
|
2140
|
+
return "unknown";
|
|
2141
|
+
}
|
|
2142
|
+
if (type.isClassOrInterface() && isFromDeclarationFile(symbol)) return "unknown";
|
|
2143
|
+
if (state.stack.has(type.compilerType)) return "unknown";
|
|
2144
|
+
const stack = new Set(state.stack).add(type.compilerType);
|
|
2145
|
+
const inner = { ...state, stack };
|
|
2146
|
+
const fields = [];
|
|
2147
|
+
for (const prop of orderedProperties(type)) {
|
|
2148
|
+
if (isSymbolKeyed(prop)) continue;
|
|
2149
|
+
const propName = prop.getName();
|
|
2150
|
+
const propT = prop.getTypeAtLocation(state.at);
|
|
2151
|
+
if (propT.getCallSignatures().length > 0 && !propT.isUnion()) continue;
|
|
2152
|
+
const pairedProp = paired?.getProperty(propName)?.getTypeAtLocation(state.at);
|
|
2153
|
+
const optional = isOptionalSymbol(prop) ? "?" : "";
|
|
2154
|
+
fields.push(`${objectKey(propName)}${optional}: ${render(propT, inner, pairedProp)}`);
|
|
2155
|
+
}
|
|
2156
|
+
const stringIndex = type.getStringIndexType();
|
|
2157
|
+
if (stringIndex) {
|
|
2158
|
+
const pairedIndex = paired?.getStringIndexType();
|
|
2159
|
+
const value = render(stringIndex, inner, pairedIndex);
|
|
2160
|
+
if (fields.length === 0) return `Record<string, ${value}>`;
|
|
2161
|
+
fields.push(`[key: string]: ${value}`);
|
|
2162
|
+
}
|
|
2163
|
+
return fields.length ? `{ ${fields.join("; ")} }` : "{}";
|
|
2164
|
+
}
|
|
2165
|
+
function stateAt(at) {
|
|
2166
|
+
return { at, stack: /* @__PURE__ */ new Set(), depth: 0 };
|
|
2167
|
+
}
|
|
2168
|
+
function requestTypeOf(types) {
|
|
2169
|
+
return render(types.input, stateAt(types.at), types.output);
|
|
2170
|
+
}
|
|
2171
|
+
function responseTypeOf(types) {
|
|
2172
|
+
return render(types.output, stateAt(types.at));
|
|
2173
|
+
}
|
|
2174
|
+
function requestFieldsOf(types) {
|
|
2175
|
+
const { input, output, at } = types;
|
|
2176
|
+
const objectInput = input.getNonNullableType();
|
|
2177
|
+
if (!objectInput.isObject() || objectInput.isArray() || objectInput.isTuple()) return null;
|
|
2178
|
+
const state = stateAt(at);
|
|
2179
|
+
return orderedProperties(objectInput).filter((prop) => !isSymbolKeyed(prop)).map((prop) => ({
|
|
2180
|
+
name: prop.getName(),
|
|
2181
|
+
type: render(
|
|
2182
|
+
prop.getTypeAtLocation(at),
|
|
2183
|
+
state,
|
|
2184
|
+
output.getProperty(prop.getName())?.getTypeAtLocation(at)
|
|
2185
|
+
),
|
|
2186
|
+
optional: isOptionalSymbol(prop)
|
|
2187
|
+
}));
|
|
2188
|
+
}
|
|
2189
|
+
function acceptsUndefined(types) {
|
|
2190
|
+
const { input, output } = types;
|
|
2191
|
+
const accepted = isAnyOrUnknown(input) ? output : input;
|
|
2192
|
+
if (isAnyOrUnknown(accepted) || accepted.isUndefined()) return true;
|
|
2193
|
+
return accepted.isUnion() && accepted.getUnionTypes().some((t) => t.isUndefined());
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
2197
|
+
import { Node as Node7, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
2198
|
+
function zodAstToTs(node) {
|
|
2199
|
+
if (!Node7.isCallExpression(node)) return "unknown";
|
|
2200
|
+
const expr = node.getExpression();
|
|
2201
|
+
if (Node7.isPropertyAccessExpression(expr)) {
|
|
2202
|
+
const methodName = expr.getName();
|
|
2203
|
+
const receiver = expr.getExpression();
|
|
2204
|
+
if (methodName === "optional") {
|
|
2205
|
+
return `${zodAstToTs(receiver)} | undefined`;
|
|
2206
|
+
}
|
|
2207
|
+
if (methodName === "nullable") {
|
|
2208
|
+
return `${zodAstToTs(receiver)} | null`;
|
|
2209
|
+
}
|
|
2210
|
+
const args = node.getArguments();
|
|
2211
|
+
switch (methodName) {
|
|
2212
|
+
case "string":
|
|
2213
|
+
return "string";
|
|
2214
|
+
case "number":
|
|
2215
|
+
return "number";
|
|
2216
|
+
case "boolean":
|
|
2217
|
+
return "boolean";
|
|
2218
|
+
case "unknown":
|
|
2219
|
+
return "unknown";
|
|
2220
|
+
case "any":
|
|
2221
|
+
return "unknown";
|
|
2222
|
+
case "literal": {
|
|
2223
|
+
const lit = args[0];
|
|
2224
|
+
if (!lit) return "unknown";
|
|
2225
|
+
if (Node7.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2226
|
+
if (Node7.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2227
|
+
if (lit.getKind() === SyntaxKind2.TrueKeyword) return "true";
|
|
2228
|
+
if (lit.getKind() === SyntaxKind2.FalseKeyword) return "false";
|
|
2229
|
+
return "unknown";
|
|
2230
|
+
}
|
|
2231
|
+
case "enum": {
|
|
2232
|
+
const arrArg = args[0];
|
|
2233
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2234
|
+
const members = arrArg.getElements().map(
|
|
2235
|
+
(el) => Node7.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2236
|
+
);
|
|
2237
|
+
return members.join(" | ");
|
|
2238
|
+
}
|
|
2239
|
+
case "array": {
|
|
2240
|
+
const inner = args[0];
|
|
2241
|
+
if (!inner) return "unknown";
|
|
2242
|
+
return `Array<${zodAstToTs(inner)}>`;
|
|
2243
|
+
}
|
|
2244
|
+
case "object": {
|
|
2245
|
+
const objArg = args[0];
|
|
2246
|
+
if (!objArg || !Node7.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2247
|
+
const lines = [];
|
|
2248
|
+
for (const prop of objArg.getProperties()) {
|
|
2249
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2250
|
+
const key = prop.getName();
|
|
2251
|
+
const valNode = prop.getInitializer();
|
|
2252
|
+
if (!valNode) continue;
|
|
2253
|
+
const tsType = zodAstToTs(valNode);
|
|
2254
|
+
const isOpt = isOptionalChain(valNode);
|
|
2255
|
+
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2256
|
+
}
|
|
2257
|
+
return `{ ${lines.join("; ")} }`;
|
|
2258
|
+
}
|
|
2259
|
+
case "union": {
|
|
2260
|
+
const arrArg = args[0];
|
|
2261
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2262
|
+
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2263
|
+
}
|
|
2264
|
+
case "record": {
|
|
2265
|
+
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2266
|
+
if (!valArg) return "unknown";
|
|
2267
|
+
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2268
|
+
}
|
|
2269
|
+
case "tuple": {
|
|
2270
|
+
const arrArg = args[0];
|
|
2271
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2272
|
+
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2273
|
+
}
|
|
2274
|
+
default:
|
|
2275
|
+
return "unknown";
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
return "unknown";
|
|
2279
|
+
}
|
|
2280
|
+
function isOptionalChain(node) {
|
|
2281
|
+
if (!Node7.isCallExpression(node)) return false;
|
|
2282
|
+
const expr = node.getExpression();
|
|
2283
|
+
return Node7.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2284
|
+
}
|
|
2285
|
+
function memberType(val, position, project) {
|
|
2286
|
+
if (project) {
|
|
2287
|
+
const inferred = inferSchema(val, project);
|
|
2288
|
+
if (inferred.kind === "schema") {
|
|
2289
|
+
return position === "request" ? requestTypeOf(inferred.types) : responseTypeOf(inferred.types);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
return zodAstToTs(val);
|
|
2293
|
+
}
|
|
2294
|
+
function parseDefineContractCall(callExpr, project) {
|
|
2295
|
+
if (!Node7.isCallExpression(callExpr)) return null;
|
|
2296
|
+
const callee = callExpr.getExpression();
|
|
2297
|
+
const calleeName = Node7.isIdentifier(callee) ? callee.getText() : Node7.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2298
|
+
if (calleeName !== "defineContract") return null;
|
|
2299
|
+
const args = callExpr.getArguments();
|
|
2300
|
+
const optsArg = args[0];
|
|
2301
|
+
if (!optsArg || !Node7.isObjectLiteralExpression(optsArg)) return null;
|
|
2302
|
+
let query = null;
|
|
2303
|
+
let body = null;
|
|
2304
|
+
let response = "unknown";
|
|
2305
|
+
let error = null;
|
|
2306
|
+
let bodyZodText = null;
|
|
2307
|
+
let queryZodText = null;
|
|
2308
|
+
for (const prop of optsArg.getProperties()) {
|
|
2309
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2310
|
+
const propName = prop.getName();
|
|
2311
|
+
const val = prop.getInitializer();
|
|
2312
|
+
if (!val) continue;
|
|
2313
|
+
if (propName === "query") {
|
|
2314
|
+
query = memberType(val, "request", project);
|
|
2315
|
+
queryZodText = val.getText();
|
|
2316
|
+
} else if (propName === "body") {
|
|
2317
|
+
body = memberType(val, "request", project);
|
|
2318
|
+
bodyZodText = val.getText();
|
|
2319
|
+
} else if (propName === "response") {
|
|
2320
|
+
response = memberType(val, "response", project);
|
|
2321
|
+
} else if (propName === "error") {
|
|
2322
|
+
error = memberType(val, "response", project);
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// src/discovery/pipe-schema.ts
|
|
2329
|
+
function pipeSchemaOf(decorator, project) {
|
|
2330
|
+
for (const pipe of decorator.getArguments()) {
|
|
2331
|
+
if (!Node8.isNewExpression(pipe) && !Node8.isCallExpression(pipe)) continue;
|
|
2332
|
+
for (const arg of pipe.getArguments()) {
|
|
2333
|
+
if (!couldBeSchema(arg)) continue;
|
|
2334
|
+
const inferred = inferSchema(arg, project);
|
|
2335
|
+
if (inferred.kind === "schema") {
|
|
2336
|
+
return {
|
|
2337
|
+
type: requestTypeOf(inferred.types),
|
|
2338
|
+
optional: acceptsUndefined(inferred.types),
|
|
2339
|
+
fields: requestFieldsOf(inferred.types)
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
if (inferred.kind === "unresolved") {
|
|
2343
|
+
const fallback = syntacticZodType(arg, project);
|
|
2344
|
+
if (fallback) {
|
|
2345
|
+
return { type: fallback, optional: fallback.endsWith("| undefined"), fields: null };
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
function couldBeSchema(arg) {
|
|
2353
|
+
return Node8.isIdentifier(arg) || Node8.isPropertyAccessExpression(arg) || Node8.isCallExpression(arg) || Node8.isAsExpression(arg) || Node8.isSatisfiesExpression(arg) || Node8.isParenthesizedExpression(arg);
|
|
2354
|
+
}
|
|
2355
|
+
function syntacticZodType(arg, project) {
|
|
2356
|
+
let expr = arg;
|
|
2357
|
+
if (Node8.isIdentifier(arg)) {
|
|
2358
|
+
const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
|
|
2359
|
+
expr = resolved?.decl.getInitializer();
|
|
2360
|
+
}
|
|
2361
|
+
if (!expr || !Node8.isCallExpression(expr)) return null;
|
|
2362
|
+
const type = zodAstToTs(expr);
|
|
2363
|
+
return type === "unknown" ? null : type;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
1698
2366
|
// src/discovery/dto-type-resolver.ts
|
|
1699
2367
|
var WRAPPER_TYPES = {
|
|
1700
2368
|
// MikroORM Ref/Reference/LoadedReference/IdentifiedReference are server-side
|
|
@@ -1726,22 +2394,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
|
|
|
1726
2394
|
]);
|
|
1727
2395
|
function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
|
|
1728
2396
|
if (depth <= 0) return "unknown";
|
|
1729
|
-
if (
|
|
2397
|
+
if (Node9.isArrayTypeNode(typeNode)) {
|
|
1730
2398
|
const elementType = typeNode.getElementTypeNode();
|
|
1731
2399
|
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
|
|
1732
2400
|
}
|
|
1733
|
-
if (
|
|
2401
|
+
if (Node9.isUnionTypeNode(typeNode)) {
|
|
1734
2402
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
|
|
1735
2403
|
}
|
|
1736
|
-
if (
|
|
2404
|
+
if (Node9.isIntersectionTypeNode(typeNode)) {
|
|
1737
2405
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
|
|
1738
2406
|
}
|
|
1739
|
-
if (
|
|
2407
|
+
if (Node9.isParenthesizedTypeNode(typeNode)) {
|
|
1740
2408
|
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
|
|
1741
2409
|
}
|
|
1742
|
-
if (
|
|
2410
|
+
if (Node9.isTypeReference(typeNode)) {
|
|
1743
2411
|
const typeName = typeNode.getTypeName();
|
|
1744
|
-
const name =
|
|
2412
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
1745
2413
|
const bound = subst.get(name);
|
|
1746
2414
|
if (bound !== void 0) return bound;
|
|
1747
2415
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
@@ -1764,13 +2432,13 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
1764
2432
|
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1765
2433
|
return "unknown";
|
|
1766
2434
|
}
|
|
1767
|
-
if (
|
|
2435
|
+
if (Node9.isTypeLiteral(typeNode)) {
|
|
1768
2436
|
const members = [];
|
|
1769
2437
|
for (const member of typeNode.getMembers()) {
|
|
1770
|
-
if (
|
|
2438
|
+
if (Node9.isPropertySignature(member)) {
|
|
1771
2439
|
const memberTypeNode = member.getTypeNode();
|
|
1772
|
-
const
|
|
1773
|
-
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${
|
|
2440
|
+
const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
|
|
2441
|
+
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
|
|
1774
2442
|
} else {
|
|
1775
2443
|
members.push(member.getText());
|
|
1776
2444
|
}
|
|
@@ -1778,11 +2446,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
1778
2446
|
return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
|
|
1779
2447
|
}
|
|
1780
2448
|
const kind = typeNode.getKind();
|
|
1781
|
-
if (kind ===
|
|
1782
|
-
if (kind ===
|
|
1783
|
-
if (kind ===
|
|
1784
|
-
if (kind ===
|
|
1785
|
-
if (kind ===
|
|
2449
|
+
if (kind === SyntaxKind3.StringKeyword) return "string";
|
|
2450
|
+
if (kind === SyntaxKind3.NumberKeyword) return "number";
|
|
2451
|
+
if (kind === SyntaxKind3.BooleanKeyword) return "boolean";
|
|
2452
|
+
if (kind === SyntaxKind3.UnknownKeyword) return "unknown";
|
|
2453
|
+
if (kind === SyntaxKind3.AnyKeyword) return "unknown";
|
|
1786
2454
|
return typeNode.getText();
|
|
1787
2455
|
}
|
|
1788
2456
|
function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
|
|
@@ -1830,20 +2498,24 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
|
|
|
1830
2498
|
const propName = prop.getName();
|
|
1831
2499
|
const isOptional = prop.hasQuestionToken();
|
|
1832
2500
|
const propTypeNode = prop.getTypeNode();
|
|
1833
|
-
let
|
|
2501
|
+
let propType2 = "unknown";
|
|
1834
2502
|
if (propTypeNode) {
|
|
1835
|
-
|
|
2503
|
+
propType2 = resolveTypeNodeToString(propTypeNode, sourceFile, project, depth, subst);
|
|
1836
2504
|
}
|
|
1837
|
-
lines.push(`${propName}${isOptional ? "?" : ""}: ${
|
|
2505
|
+
lines.push(`${propName}${isOptional ? "?" : ""}: ${propType2}`);
|
|
1838
2506
|
}
|
|
1839
2507
|
return `{ ${lines.join("; ")} }`;
|
|
1840
2508
|
}
|
|
2509
|
+
function namedField(decorator) {
|
|
2510
|
+
const first = decorator.getArguments()[0];
|
|
2511
|
+
return first && Node9.isStringLiteral(first) ? first.getLiteralValue() : null;
|
|
2512
|
+
}
|
|
1841
2513
|
function extractBodyType(method, sourceFile, project) {
|
|
1842
2514
|
for (const param of method.getParameters()) {
|
|
1843
2515
|
const bodyDecorator = param.getDecorators().find((d) => d.getName() === "Body");
|
|
1844
|
-
if (!bodyDecorator) continue;
|
|
1845
|
-
const
|
|
1846
|
-
if (
|
|
2516
|
+
if (!bodyDecorator || namedField(bodyDecorator) !== null) continue;
|
|
2517
|
+
const piped = pipeSchemaOf(bodyDecorator, project);
|
|
2518
|
+
if (piped) return piped.type;
|
|
1847
2519
|
const typeNode = param.getTypeNode();
|
|
1848
2520
|
if (typeNode) {
|
|
1849
2521
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -1851,11 +2523,16 @@ function extractBodyType(method, sourceFile, project) {
|
|
|
1851
2523
|
}
|
|
1852
2524
|
return null;
|
|
1853
2525
|
}
|
|
2526
|
+
function hasPipeSchema(param, decoratorName, project) {
|
|
2527
|
+
const decorator = param.getDecorators().find((d) => d.getName() === decoratorName);
|
|
2528
|
+
return !!decorator && pipeSchemaOf(decorator, project) !== null;
|
|
2529
|
+
}
|
|
1854
2530
|
function extractQueryType(method, sourceFile, project) {
|
|
1855
2531
|
for (const param of method.getParameters()) {
|
|
1856
2532
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
1857
|
-
if (!queryDecorator) continue;
|
|
1858
|
-
|
|
2533
|
+
if (!queryDecorator || namedField(queryDecorator) !== null) continue;
|
|
2534
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2535
|
+
if (piped) return piped.type;
|
|
1859
2536
|
const typeNode = param.getTypeNode();
|
|
1860
2537
|
if (typeNode) {
|
|
1861
2538
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -1865,10 +2542,13 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
1865
2542
|
for (const param of method.getParameters()) {
|
|
1866
2543
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
1867
2544
|
if (!queryDecorator) continue;
|
|
1868
|
-
const
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
2545
|
+
const queryName = namedField(queryDecorator);
|
|
2546
|
+
if (queryName === null) continue;
|
|
2547
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2548
|
+
if (piped) {
|
|
2549
|
+
entries.push(`${queryName}${piped.optional ? "?" : ""}: ${piped.type}`);
|
|
2550
|
+
continue;
|
|
2551
|
+
}
|
|
1872
2552
|
const typeNode = param.getTypeNode();
|
|
1873
2553
|
const queryType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
1874
2554
|
entries.push(`${queryName}${isParamOptional(param) ? "?" : ""}: ${queryType}`);
|
|
@@ -1878,8 +2558,8 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
1878
2558
|
function isParamOptional(param) {
|
|
1879
2559
|
if (param.hasQuestionToken() || param.hasInitializer()) return true;
|
|
1880
2560
|
const typeNode = param.getTypeNode();
|
|
1881
|
-
if (typeNode &&
|
|
1882
|
-
return typeNode.getTypeNodes().some((t) => t.getKind() ===
|
|
2561
|
+
if (typeNode && Node9.isUnionTypeNode(typeNode)) {
|
|
2562
|
+
return typeNode.getTypeNodes().some((t) => t.getKind() === SyntaxKind3.UndefinedKeyword);
|
|
1883
2563
|
}
|
|
1884
2564
|
return false;
|
|
1885
2565
|
}
|
|
@@ -1891,7 +2571,7 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
1891
2571
|
const paramArgs = paramDecorator.getArguments();
|
|
1892
2572
|
if (paramArgs.length === 0) continue;
|
|
1893
2573
|
const nameArg = paramArgs[0];
|
|
1894
|
-
if (!
|
|
2574
|
+
if (!Node9.isStringLiteral(nameArg)) continue;
|
|
1895
2575
|
const paramName = nameArg.getLiteralValue();
|
|
1896
2576
|
const typeNode = param.getTypeNode();
|
|
1897
2577
|
const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
@@ -1899,6 +2579,22 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
1899
2579
|
}
|
|
1900
2580
|
return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
|
|
1901
2581
|
}
|
|
2582
|
+
function extractPipedParamTypes(method, project) {
|
|
2583
|
+
const types = {};
|
|
2584
|
+
for (const param of method.getParameters()) {
|
|
2585
|
+
const paramDecorator = param.getDecorators().find((d) => d.getName() === "Param");
|
|
2586
|
+
if (!paramDecorator) continue;
|
|
2587
|
+
const piped = pipeSchemaOf(paramDecorator, project);
|
|
2588
|
+
if (!piped) continue;
|
|
2589
|
+
const name = namedField(paramDecorator);
|
|
2590
|
+
if (name !== null) {
|
|
2591
|
+
types[name] = piped.type;
|
|
2592
|
+
} else {
|
|
2593
|
+
for (const field of piped.fields ?? []) types[field.name] = field.type;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
return Object.keys(types).length > 0 ? types : null;
|
|
2597
|
+
}
|
|
1902
2598
|
function extractUploadedFiles(method) {
|
|
1903
2599
|
const FILE = "File | Blob";
|
|
1904
2600
|
const entries = [];
|
|
@@ -1912,28 +2608,28 @@ function extractUploadedFiles(method) {
|
|
|
1912
2608
|
for (const decorator of method.getDecorators()) {
|
|
1913
2609
|
if (decorator.getName() !== "UseInterceptors") continue;
|
|
1914
2610
|
for (const arg of decorator.getArguments()) {
|
|
1915
|
-
if (!
|
|
2611
|
+
if (!Node9.isCallExpression(arg)) continue;
|
|
1916
2612
|
const interceptor = arg.getExpression().getText();
|
|
1917
2613
|
const callArgs = arg.getArguments();
|
|
1918
2614
|
const firstArg2 = callArgs[0];
|
|
1919
2615
|
if (interceptor === "FileInterceptor") {
|
|
1920
|
-
if (firstArg2 &&
|
|
2616
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
1921
2617
|
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
1922
2618
|
multipart = true;
|
|
1923
2619
|
}
|
|
1924
2620
|
} else if (interceptor === "FilesInterceptor") {
|
|
1925
|
-
if (firstArg2 &&
|
|
2621
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
1926
2622
|
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
1927
2623
|
multipart = true;
|
|
1928
2624
|
}
|
|
1929
2625
|
} else if (interceptor === "FileFieldsInterceptor") {
|
|
1930
|
-
if (firstArg2 &&
|
|
2626
|
+
if (firstArg2 && Node9.isArrayLiteralExpression(firstArg2)) {
|
|
1931
2627
|
for (const el of firstArg2.getElements()) {
|
|
1932
|
-
if (!
|
|
2628
|
+
if (!Node9.isObjectLiteralExpression(el)) continue;
|
|
1933
2629
|
const nameProp = el.getProperty("name");
|
|
1934
|
-
if (nameProp &&
|
|
2630
|
+
if (nameProp && Node9.isPropertyAssignment(nameProp)) {
|
|
1935
2631
|
const init = nameProp.getInitializer();
|
|
1936
|
-
if (init &&
|
|
2632
|
+
if (init && Node9.isStringLiteral(init)) {
|
|
1937
2633
|
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
1938
2634
|
}
|
|
1939
2635
|
}
|
|
@@ -1953,13 +2649,13 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
1953
2649
|
if (apiResponseDecorator) {
|
|
1954
2650
|
const args = apiResponseDecorator.getArguments();
|
|
1955
2651
|
const optsArg = args[0];
|
|
1956
|
-
if (optsArg &&
|
|
2652
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
1957
2653
|
for (const prop of optsArg.getProperties()) {
|
|
1958
|
-
if (!
|
|
2654
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
1959
2655
|
if (prop.getName() !== "type") continue;
|
|
1960
2656
|
const val = prop.getInitializer();
|
|
1961
2657
|
if (!val) continue;
|
|
1962
|
-
if (
|
|
2658
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
1963
2659
|
const elements = val.getElements();
|
|
1964
2660
|
const firstEl = elements[0];
|
|
1965
2661
|
if (elements.length > 0 && firstEl !== void 0) {
|
|
@@ -1980,24 +2676,24 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
1980
2676
|
}
|
|
1981
2677
|
function apiResponseStatus(decorator) {
|
|
1982
2678
|
const optsArg = decorator.getArguments()[0];
|
|
1983
|
-
if (!optsArg || !
|
|
2679
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
1984
2680
|
for (const prop of optsArg.getProperties()) {
|
|
1985
|
-
if (!
|
|
2681
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
1986
2682
|
if (prop.getName() !== "status") continue;
|
|
1987
2683
|
const val = prop.getInitializer();
|
|
1988
|
-
if (val &&
|
|
2684
|
+
if (val && Node9.isNumericLiteral(val)) return Number(val.getLiteralValue());
|
|
1989
2685
|
}
|
|
1990
2686
|
return null;
|
|
1991
2687
|
}
|
|
1992
2688
|
function apiResponseTypeNode(decorator) {
|
|
1993
2689
|
const optsArg = decorator.getArguments()[0];
|
|
1994
|
-
if (!optsArg || !
|
|
2690
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
1995
2691
|
for (const prop of optsArg.getProperties()) {
|
|
1996
|
-
if (!
|
|
2692
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
1997
2693
|
if (prop.getName() !== "type") continue;
|
|
1998
2694
|
const val = prop.getInitializer();
|
|
1999
2695
|
if (!val) return null;
|
|
2000
|
-
if (
|
|
2696
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
2001
2697
|
const first = val.getElements()[0];
|
|
2002
2698
|
return first ? { node: first, isArray: true } : null;
|
|
2003
2699
|
}
|
|
@@ -2015,7 +2711,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2015
2711
|
const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
|
|
2016
2712
|
const type = typeInfo.isArray ? `Array<${inner}>` : inner;
|
|
2017
2713
|
let ref = null;
|
|
2018
|
-
if (
|
|
2714
|
+
if (Node9.isIdentifier(typeInfo.node)) {
|
|
2019
2715
|
const name = typeInfo.node.getText();
|
|
2020
2716
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2021
2717
|
if (localDecl?.isExported()) {
|
|
@@ -2032,7 +2728,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2032
2728
|
return null;
|
|
2033
2729
|
}
|
|
2034
2730
|
function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
2035
|
-
if (!
|
|
2731
|
+
if (!Node9.isIdentifier(node)) return "unknown";
|
|
2036
2732
|
const name = node.getText();
|
|
2037
2733
|
const resolved = findType(name, sourceFile, project);
|
|
2038
2734
|
if (resolved) {
|
|
@@ -2052,9 +2748,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
|
|
|
2052
2748
|
var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
|
|
2053
2749
|
function detectBinaryResponse(method) {
|
|
2054
2750
|
const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
|
|
2055
|
-
if (!node || !
|
|
2751
|
+
if (!node || !Node9.isTypeReference(node)) return false;
|
|
2056
2752
|
const typeName = node.getTypeName();
|
|
2057
|
-
const name =
|
|
2753
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2058
2754
|
return BINARY_RESPONSE_TYPES.has(name);
|
|
2059
2755
|
}
|
|
2060
2756
|
function detectStreamElement(method) {
|
|
@@ -2069,9 +2765,9 @@ function detectStreamElement(method) {
|
|
|
2069
2765
|
return null;
|
|
2070
2766
|
}
|
|
2071
2767
|
function streamContainerElement(node) {
|
|
2072
|
-
if (!node || !
|
|
2768
|
+
if (!node || !Node9.isTypeReference(node)) return null;
|
|
2073
2769
|
const typeName = node.getTypeName();
|
|
2074
|
-
const name =
|
|
2770
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2075
2771
|
if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
|
|
2076
2772
|
return node.getTypeArguments()[0] ?? null;
|
|
2077
2773
|
}
|
|
@@ -2081,9 +2777,9 @@ function hasAsQueryDecorator(method) {
|
|
|
2081
2777
|
return method.getDecorators().some((d) => d.getName() === "AsQuery");
|
|
2082
2778
|
}
|
|
2083
2779
|
function unwrapNamedContainer(node, names) {
|
|
2084
|
-
if (!node || !
|
|
2780
|
+
if (!node || !Node9.isTypeReference(node)) return node;
|
|
2085
2781
|
const typeName = node.getTypeName();
|
|
2086
|
-
const name =
|
|
2782
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2087
2783
|
if (names.has(name)) {
|
|
2088
2784
|
return node.getTypeArguments()[0] ?? node;
|
|
2089
2785
|
}
|
|
@@ -2104,19 +2800,20 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2104
2800
|
body = body ?? bodyType;
|
|
2105
2801
|
}
|
|
2106
2802
|
const paramsType = extractParamsType(method, sourceFile, project);
|
|
2803
|
+
const paramTypes = extractPipedParamTypes(method, project);
|
|
2107
2804
|
const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
|
|
2108
2805
|
const errorInfo = extractErrorType(method, sourceFile, project);
|
|
2109
|
-
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
2806
|
+
if (body === null && query === null && paramsType === null && paramTypes === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
2110
2807
|
return null;
|
|
2111
2808
|
}
|
|
2112
2809
|
let bodyRef = null;
|
|
2113
2810
|
let queryRef = null;
|
|
2114
2811
|
let responseRef = null;
|
|
2115
2812
|
for (const param of method.getParameters()) {
|
|
2116
|
-
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
2813
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode() && !hasPipeSchema(param, "Body", project)) {
|
|
2117
2814
|
bodyRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2118
2815
|
}
|
|
2119
|
-
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
2816
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode() && !hasPipeSchema(param, "Query", project)) {
|
|
2120
2817
|
queryRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2121
2818
|
}
|
|
2122
2819
|
}
|
|
@@ -2129,11 +2826,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2129
2826
|
if (apiResp) {
|
|
2130
2827
|
const args = apiResp.getArguments();
|
|
2131
2828
|
const optsArg = args[0];
|
|
2132
|
-
if (optsArg &&
|
|
2829
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
2133
2830
|
for (const prop of optsArg.getProperties()) {
|
|
2134
|
-
if (
|
|
2831
|
+
if (Node9.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
2135
2832
|
const val = prop.getInitializer();
|
|
2136
|
-
if (val &&
|
|
2833
|
+
if (val && Node9.isIdentifier(val)) {
|
|
2137
2834
|
const name = val.getText();
|
|
2138
2835
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2139
2836
|
if (localDecl?.isExported()) {
|
|
@@ -2169,6 +2866,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2169
2866
|
response,
|
|
2170
2867
|
error: errorInfo?.type ?? null,
|
|
2171
2868
|
params: paramsType,
|
|
2869
|
+
paramTypes,
|
|
2172
2870
|
queryRef,
|
|
2173
2871
|
bodyRef,
|
|
2174
2872
|
responseRef,
|
|
@@ -2189,6 +2887,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2189
2887
|
function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
2190
2888
|
for (const param of method.getParameters()) {
|
|
2191
2889
|
if (!param.getDecorators().some((d) => d.getName() === decoratorName)) continue;
|
|
2890
|
+
if (hasPipeSchema(param, decoratorName, project)) continue;
|
|
2192
2891
|
const typeNode = param.getTypeNode();
|
|
2193
2892
|
if (!typeNode) continue;
|
|
2194
2893
|
const text = typeNode.getText().replace(/\[\]$/, "");
|
|
@@ -2201,129 +2900,6 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
2201
2900
|
return null;
|
|
2202
2901
|
}
|
|
2203
2902
|
|
|
2204
|
-
// src/discovery/zod-ast-to-ts.ts
|
|
2205
|
-
import { Node as Node7, SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
2206
|
-
function zodAstToTs(node) {
|
|
2207
|
-
if (!Node7.isCallExpression(node)) return "unknown";
|
|
2208
|
-
const expr = node.getExpression();
|
|
2209
|
-
if (Node7.isPropertyAccessExpression(expr)) {
|
|
2210
|
-
const methodName = expr.getName();
|
|
2211
|
-
const receiver = expr.getExpression();
|
|
2212
|
-
if (methodName === "optional") {
|
|
2213
|
-
return `${zodAstToTs(receiver)} | undefined`;
|
|
2214
|
-
}
|
|
2215
|
-
if (methodName === "nullable") {
|
|
2216
|
-
return `${zodAstToTs(receiver)} | null`;
|
|
2217
|
-
}
|
|
2218
|
-
const args = node.getArguments();
|
|
2219
|
-
switch (methodName) {
|
|
2220
|
-
case "string":
|
|
2221
|
-
return "string";
|
|
2222
|
-
case "number":
|
|
2223
|
-
return "number";
|
|
2224
|
-
case "boolean":
|
|
2225
|
-
return "boolean";
|
|
2226
|
-
case "unknown":
|
|
2227
|
-
return "unknown";
|
|
2228
|
-
case "any":
|
|
2229
|
-
return "unknown";
|
|
2230
|
-
case "literal": {
|
|
2231
|
-
const lit = args[0];
|
|
2232
|
-
if (!lit) return "unknown";
|
|
2233
|
-
if (Node7.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2234
|
-
if (Node7.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2235
|
-
if (lit.getKind() === SyntaxKind3.TrueKeyword) return "true";
|
|
2236
|
-
if (lit.getKind() === SyntaxKind3.FalseKeyword) return "false";
|
|
2237
|
-
return "unknown";
|
|
2238
|
-
}
|
|
2239
|
-
case "enum": {
|
|
2240
|
-
const arrArg = args[0];
|
|
2241
|
-
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2242
|
-
const members = arrArg.getElements().map(
|
|
2243
|
-
(el) => Node7.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2244
|
-
);
|
|
2245
|
-
return members.join(" | ");
|
|
2246
|
-
}
|
|
2247
|
-
case "array": {
|
|
2248
|
-
const inner = args[0];
|
|
2249
|
-
if (!inner) return "unknown";
|
|
2250
|
-
return `Array<${zodAstToTs(inner)}>`;
|
|
2251
|
-
}
|
|
2252
|
-
case "object": {
|
|
2253
|
-
const objArg = args[0];
|
|
2254
|
-
if (!objArg || !Node7.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2255
|
-
const lines = [];
|
|
2256
|
-
for (const prop of objArg.getProperties()) {
|
|
2257
|
-
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2258
|
-
const key = prop.getName();
|
|
2259
|
-
const valNode = prop.getInitializer();
|
|
2260
|
-
if (!valNode) continue;
|
|
2261
|
-
const tsType = zodAstToTs(valNode);
|
|
2262
|
-
const isOpt = isOptionalChain(valNode);
|
|
2263
|
-
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2264
|
-
}
|
|
2265
|
-
return `{ ${lines.join("; ")} }`;
|
|
2266
|
-
}
|
|
2267
|
-
case "union": {
|
|
2268
|
-
const arrArg = args[0];
|
|
2269
|
-
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2270
|
-
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2271
|
-
}
|
|
2272
|
-
case "record": {
|
|
2273
|
-
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2274
|
-
if (!valArg) return "unknown";
|
|
2275
|
-
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2276
|
-
}
|
|
2277
|
-
case "tuple": {
|
|
2278
|
-
const arrArg = args[0];
|
|
2279
|
-
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2280
|
-
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2281
|
-
}
|
|
2282
|
-
default:
|
|
2283
|
-
return "unknown";
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
return "unknown";
|
|
2287
|
-
}
|
|
2288
|
-
function isOptionalChain(node) {
|
|
2289
|
-
if (!Node7.isCallExpression(node)) return false;
|
|
2290
|
-
const expr = node.getExpression();
|
|
2291
|
-
return Node7.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2292
|
-
}
|
|
2293
|
-
function parseDefineContractCall(callExpr) {
|
|
2294
|
-
if (!Node7.isCallExpression(callExpr)) return null;
|
|
2295
|
-
const callee = callExpr.getExpression();
|
|
2296
|
-
const calleeName = Node7.isIdentifier(callee) ? callee.getText() : Node7.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2297
|
-
if (calleeName !== "defineContract") return null;
|
|
2298
|
-
const args = callExpr.getArguments();
|
|
2299
|
-
const optsArg = args[0];
|
|
2300
|
-
if (!optsArg || !Node7.isObjectLiteralExpression(optsArg)) return null;
|
|
2301
|
-
let query = null;
|
|
2302
|
-
let body = null;
|
|
2303
|
-
let response = "unknown";
|
|
2304
|
-
let error = null;
|
|
2305
|
-
let bodyZodText = null;
|
|
2306
|
-
let queryZodText = null;
|
|
2307
|
-
for (const prop of optsArg.getProperties()) {
|
|
2308
|
-
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2309
|
-
const propName = prop.getName();
|
|
2310
|
-
const val = prop.getInitializer();
|
|
2311
|
-
if (!val) continue;
|
|
2312
|
-
if (propName === "query") {
|
|
2313
|
-
query = zodAstToTs(val);
|
|
2314
|
-
queryZodText = val.getText();
|
|
2315
|
-
} else if (propName === "body") {
|
|
2316
|
-
body = zodAstToTs(val);
|
|
2317
|
-
bodyZodText = val.getText();
|
|
2318
|
-
} else if (propName === "response") {
|
|
2319
|
-
response = zodAstToTs(val);
|
|
2320
|
-
} else if (propName === "error") {
|
|
2321
|
-
error = zodAstToTs(val);
|
|
2322
|
-
}
|
|
2323
|
-
}
|
|
2324
|
-
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2325
|
-
}
|
|
2326
|
-
|
|
2327
2903
|
// src/discovery/contracts-fast.ts
|
|
2328
2904
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
2329
2905
|
return tsconfig ? resolve3(tsconfig) : join2(cwd, "tsconfig.json");
|
|
@@ -2335,25 +2911,25 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
2335
2911
|
};
|
|
2336
2912
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
2337
2913
|
if (!existsSync(tsconfigPath)) return { options: null, files: [] };
|
|
2338
|
-
const read =
|
|
2914
|
+
const read = ts3.readConfigFile(tsconfigPath, (path) => ts3.sys.readFile(path));
|
|
2339
2915
|
if (read.error) {
|
|
2340
2916
|
return {
|
|
2341
2917
|
options: null,
|
|
2342
2918
|
files: [tsconfigPath],
|
|
2343
|
-
error:
|
|
2919
|
+
error: ts3.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
2344
2920
|
};
|
|
2345
2921
|
}
|
|
2346
2922
|
const host = {
|
|
2347
|
-
useCaseSensitiveFileNames:
|
|
2923
|
+
useCaseSensitiveFileNames: ts3.sys.useCaseSensitiveFileNames,
|
|
2348
2924
|
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
2349
2925
|
readDirectory: () => [],
|
|
2350
|
-
fileExists: (path) =>
|
|
2926
|
+
fileExists: (path) => ts3.sys.fileExists(path),
|
|
2351
2927
|
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
2352
2928
|
// in a base tsconfig keeps working.
|
|
2353
|
-
readFile: (path) =>
|
|
2929
|
+
readFile: (path) => ts3.sys.readFile(path)
|
|
2354
2930
|
};
|
|
2355
|
-
const sourceFile =
|
|
2356
|
-
const parsed =
|
|
2931
|
+
const sourceFile = ts3.readJsonConfigFile(tsconfigPath, (path) => ts3.sys.readFile(path));
|
|
2932
|
+
const parsed = ts3.parseJsonSourceFileConfigFileContent(
|
|
2357
2933
|
sourceFile,
|
|
2358
2934
|
host,
|
|
2359
2935
|
dirname2(tsconfigPath),
|
|
@@ -2376,7 +2952,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
|
|
|
2376
2952
|
`[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.`
|
|
2377
2953
|
);
|
|
2378
2954
|
}
|
|
2379
|
-
return new
|
|
2955
|
+
return new Project3({
|
|
2380
2956
|
compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
|
|
2381
2957
|
skipAddingFilesFromTsConfig: true,
|
|
2382
2958
|
skipLoadingLibFiles: true,
|
|
@@ -2481,15 +3057,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
2481
3057
|
clearTypeResolutionCaches(this.project);
|
|
2482
3058
|
clearEnumCache(this.project);
|
|
2483
3059
|
clearMixinTypeProject();
|
|
3060
|
+
clearSchemaTypeProject(this.project);
|
|
2484
3061
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
2485
3062
|
}
|
|
2486
3063
|
};
|
|
2487
3064
|
function decoratorStringArg(decoratorExpr) {
|
|
2488
3065
|
if (!decoratorExpr) return void 0;
|
|
2489
|
-
if (
|
|
2490
|
-
if (
|
|
3066
|
+
if (Node10.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3067
|
+
if (Node10.isArrayLiteralExpression(decoratorExpr)) {
|
|
2491
3068
|
const first = decoratorExpr.getElements()[0];
|
|
2492
|
-
if (first &&
|
|
3069
|
+
if (first && Node10.isStringLiteral(first)) return first.getLiteralValue();
|
|
2493
3070
|
}
|
|
2494
3071
|
return void 0;
|
|
2495
3072
|
}
|
|
@@ -2610,9 +3187,9 @@ function extractContractRoute(args) {
|
|
|
2610
3187
|
let contractDef = null;
|
|
2611
3188
|
let bodyZodRef = null;
|
|
2612
3189
|
let queryZodRef = null;
|
|
2613
|
-
if (
|
|
2614
|
-
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
2615
|
-
} else if (
|
|
3190
|
+
if (Node10.isCallExpression(firstDecoratorArg)) {
|
|
3191
|
+
contractDef = parseDefineContractCall(firstDecoratorArg, project);
|
|
3192
|
+
} else if (Node10.isIdentifier(firstDecoratorArg)) {
|
|
2616
3193
|
const identName = firstDecoratorArg.getText();
|
|
2617
3194
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
2618
3195
|
if (!resolvedVar) {
|
|
@@ -2624,7 +3201,7 @@ function extractContractRoute(args) {
|
|
|
2624
3201
|
const { decl: varDecl, file: declFile } = resolvedVar;
|
|
2625
3202
|
const initializer = varDecl.getInitializer();
|
|
2626
3203
|
if (!initializer) return null;
|
|
2627
|
-
contractDef = parseDefineContractCall(initializer);
|
|
3204
|
+
contractDef = parseDefineContractCall(initializer, project);
|
|
2628
3205
|
if (contractDef && varDecl.isExported()) {
|
|
2629
3206
|
const filePath = declFile.getFilePath();
|
|
2630
3207
|
if (contractDef.body !== null) {
|
|
@@ -2695,6 +3272,7 @@ function extractDtoRoute(args) {
|
|
|
2695
3272
|
body: dtoContract?.body ?? null,
|
|
2696
3273
|
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
2697
3274
|
error: dtoContract?.error ?? null,
|
|
3275
|
+
paramTypes: dtoContract?.paramTypes ?? null,
|
|
2698
3276
|
queryRef: dtoContract?.queryRef ?? null,
|
|
2699
3277
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
2700
3278
|
responseRef: dtoContract?.responseRef ?? null,
|
|
@@ -2824,7 +3402,7 @@ function extractPropsSource(source, exportName) {
|
|
|
2824
3402
|
|
|
2825
3403
|
// src/discovery/shared-props.ts
|
|
2826
3404
|
import { join as join4 } from "path";
|
|
2827
|
-
import { Node as
|
|
3405
|
+
import { Node as Node11, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
2828
3406
|
function discoverSharedProps(project, moduleEntry) {
|
|
2829
3407
|
try {
|
|
2830
3408
|
let sourceFile = project.getSourceFile(moduleEntry);
|
|
@@ -2850,14 +3428,14 @@ function discoverSharedPropsFromConfig(config) {
|
|
|
2850
3428
|
const tsconfigPath = config.app.tsconfig ?? join4(config.codegen.cwd, "tsconfig.json");
|
|
2851
3429
|
let project;
|
|
2852
3430
|
try {
|
|
2853
|
-
project = new
|
|
3431
|
+
project = new Project4({
|
|
2854
3432
|
tsConfigFilePath: tsconfigPath,
|
|
2855
3433
|
skipAddingFilesFromTsConfig: true,
|
|
2856
3434
|
skipLoadingLibFiles: true,
|
|
2857
3435
|
skipFileDependencyResolution: true
|
|
2858
3436
|
});
|
|
2859
3437
|
} catch {
|
|
2860
|
-
project = new
|
|
3438
|
+
project = new Project4({
|
|
2861
3439
|
skipAddingFilesFromTsConfig: true,
|
|
2862
3440
|
skipLoadingLibFiles: true,
|
|
2863
3441
|
skipFileDependencyResolution: true,
|
|
@@ -2873,10 +3451,10 @@ function findForRootCall(sourceFile) {
|
|
|
2873
3451
|
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression);
|
|
2874
3452
|
for (const call of callExpressions) {
|
|
2875
3453
|
const expr = call.getExpression();
|
|
2876
|
-
if (!
|
|
3454
|
+
if (!Node11.isPropertyAccessExpression(expr)) continue;
|
|
2877
3455
|
const methodName = expr.getName();
|
|
2878
3456
|
const objectExpr = expr.getExpression();
|
|
2879
|
-
if (methodName === "forRoot" &&
|
|
3457
|
+
if (methodName === "forRoot" && Node11.isIdentifier(objectExpr)) {
|
|
2880
3458
|
const name = objectExpr.getText();
|
|
2881
3459
|
if (name === "InertiaModule") {
|
|
2882
3460
|
return call;
|
|
@@ -2886,19 +3464,19 @@ function findForRootCall(sourceFile) {
|
|
|
2886
3464
|
return null;
|
|
2887
3465
|
}
|
|
2888
3466
|
function findShareInitializer(forRootCall) {
|
|
2889
|
-
if (!
|
|
3467
|
+
if (!Node11.isCallExpression(forRootCall)) return null;
|
|
2890
3468
|
const args = forRootCall.getArguments();
|
|
2891
3469
|
const firstArg2 = args[0];
|
|
2892
|
-
if (!firstArg2 || !
|
|
3470
|
+
if (!firstArg2 || !Node11.isObjectLiteralExpression(firstArg2)) return null;
|
|
2893
3471
|
for (const prop of firstArg2.getProperties()) {
|
|
2894
|
-
if (
|
|
3472
|
+
if (Node11.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
2895
3473
|
return prop.getInitializer() ?? null;
|
|
2896
3474
|
}
|
|
2897
3475
|
}
|
|
2898
3476
|
return null;
|
|
2899
3477
|
}
|
|
2900
3478
|
function extractShareType(node, sourceFile, project) {
|
|
2901
|
-
if (
|
|
3479
|
+
if (Node11.isIdentifier(node)) {
|
|
2902
3480
|
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
2903
3481
|
if (ref) {
|
|
2904
3482
|
return {
|
|
@@ -2908,22 +3486,22 @@ function extractShareType(node, sourceFile, project) {
|
|
|
2908
3486
|
};
|
|
2909
3487
|
}
|
|
2910
3488
|
}
|
|
2911
|
-
if (
|
|
3489
|
+
if (Node11.isArrowFunction(node)) {
|
|
2912
3490
|
const result = extractFromFunctionLike(node, sourceFile);
|
|
2913
3491
|
return result ? { ...result, isImportRef: false } : null;
|
|
2914
3492
|
}
|
|
2915
|
-
if (
|
|
3493
|
+
if (Node11.isFunctionExpression(node)) {
|
|
2916
3494
|
const result = extractFromFunctionLike(node, sourceFile);
|
|
2917
3495
|
return result ? { ...result, isImportRef: false } : null;
|
|
2918
3496
|
}
|
|
2919
|
-
if (
|
|
3497
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
2920
3498
|
const result = extractFromObjectLiteral(node);
|
|
2921
3499
|
return result ? { ...result, isImportRef: false } : null;
|
|
2922
3500
|
}
|
|
2923
3501
|
return null;
|
|
2924
3502
|
}
|
|
2925
3503
|
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
2926
|
-
if (!
|
|
3504
|
+
if (!Node11.isIdentifier(id)) return null;
|
|
2927
3505
|
const name = id.getText();
|
|
2928
3506
|
const localFunc = sourceFile.getFunction(name);
|
|
2929
3507
|
if (localFunc?.isExported()) {
|
|
@@ -2957,37 +3535,37 @@ function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
|
2957
3535
|
return null;
|
|
2958
3536
|
}
|
|
2959
3537
|
function extractFromFunctionLike(node, _sourceFile) {
|
|
2960
|
-
const returnTypeNode =
|
|
3538
|
+
const returnTypeNode = Node11.isArrowFunction(node) || Node11.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
2961
3539
|
if (returnTypeNode) {
|
|
2962
3540
|
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
2963
3541
|
}
|
|
2964
|
-
if (
|
|
3542
|
+
if (Node11.isArrowFunction(node)) {
|
|
2965
3543
|
const body = node.getBody();
|
|
2966
|
-
if (
|
|
3544
|
+
if (Node11.isParenthesizedExpression(body)) {
|
|
2967
3545
|
const inner = body.getExpression();
|
|
2968
|
-
if (
|
|
3546
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
2969
3547
|
return extractFromObjectLiteral(inner);
|
|
2970
3548
|
}
|
|
2971
3549
|
}
|
|
2972
|
-
if (
|
|
3550
|
+
if (Node11.isObjectLiteralExpression(body)) {
|
|
2973
3551
|
return extractFromObjectLiteral(body);
|
|
2974
3552
|
}
|
|
2975
|
-
if (
|
|
3553
|
+
if (Node11.isBlock(body)) {
|
|
2976
3554
|
return extractFromBlockReturn(body);
|
|
2977
3555
|
}
|
|
2978
3556
|
}
|
|
2979
|
-
if (
|
|
3557
|
+
if (Node11.isFunctionExpression(node)) {
|
|
2980
3558
|
const body = node.getBody();
|
|
2981
|
-
if (
|
|
3559
|
+
if (Node11.isBlock(body)) {
|
|
2982
3560
|
return extractFromBlockReturn(body);
|
|
2983
3561
|
}
|
|
2984
3562
|
}
|
|
2985
3563
|
return null;
|
|
2986
3564
|
}
|
|
2987
3565
|
function extractFromReturnTypeAnnotation(typeNode) {
|
|
2988
|
-
if (
|
|
3566
|
+
if (Node11.isTypeReference(typeNode)) {
|
|
2989
3567
|
const typeName = typeNode.getTypeName();
|
|
2990
|
-
if (
|
|
3568
|
+
if (Node11.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
2991
3569
|
const typeArgs = typeNode.getTypeArguments();
|
|
2992
3570
|
const firstArg2 = typeArgs[0];
|
|
2993
3571
|
if (firstArg2) {
|
|
@@ -2996,10 +3574,10 @@ function extractFromReturnTypeAnnotation(typeNode) {
|
|
|
2996
3574
|
return null;
|
|
2997
3575
|
}
|
|
2998
3576
|
}
|
|
2999
|
-
if (
|
|
3577
|
+
if (Node11.isTypeLiteral(typeNode)) {
|
|
3000
3578
|
const properties = [];
|
|
3001
3579
|
for (const member of typeNode.getMembers()) {
|
|
3002
|
-
if (
|
|
3580
|
+
if (Node11.isPropertySignature(member)) {
|
|
3003
3581
|
const name = member.getName();
|
|
3004
3582
|
const memberTypeNode = member.getTypeNode();
|
|
3005
3583
|
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
@@ -3013,19 +3591,19 @@ function extractFromReturnTypeAnnotation(typeNode) {
|
|
|
3013
3591
|
return null;
|
|
3014
3592
|
}
|
|
3015
3593
|
function extractFromBlockReturn(block) {
|
|
3016
|
-
if (!
|
|
3594
|
+
if (!Node11.isBlock(block)) return null;
|
|
3017
3595
|
const statements = block.getStatements();
|
|
3018
3596
|
for (let i = statements.length - 1; i >= 0; i--) {
|
|
3019
3597
|
const stmt = statements[i];
|
|
3020
|
-
if (!
|
|
3598
|
+
if (!Node11.isReturnStatement(stmt)) continue;
|
|
3021
3599
|
const expr = stmt.getExpression();
|
|
3022
3600
|
if (!expr) continue;
|
|
3023
|
-
if (
|
|
3601
|
+
if (Node11.isObjectLiteralExpression(expr)) {
|
|
3024
3602
|
return extractFromObjectLiteral(expr);
|
|
3025
3603
|
}
|
|
3026
|
-
if (
|
|
3604
|
+
if (Node11.isParenthesizedExpression(expr)) {
|
|
3027
3605
|
const inner = expr.getExpression();
|
|
3028
|
-
if (
|
|
3606
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
3029
3607
|
return extractFromObjectLiteral(inner);
|
|
3030
3608
|
}
|
|
3031
3609
|
}
|
|
@@ -3034,10 +3612,10 @@ function extractFromBlockReturn(block) {
|
|
|
3034
3612
|
return null;
|
|
3035
3613
|
}
|
|
3036
3614
|
function extractFromObjectLiteral(objLiteral) {
|
|
3037
|
-
if (!
|
|
3615
|
+
if (!Node11.isObjectLiteralExpression(objLiteral)) return null;
|
|
3038
3616
|
const properties = [];
|
|
3039
3617
|
for (const prop of objLiteral.getProperties()) {
|
|
3040
|
-
if (!
|
|
3618
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
3041
3619
|
const name = prop.getName();
|
|
3042
3620
|
const initializer = prop.getInitializer();
|
|
3043
3621
|
if (!initializer) continue;
|
|
@@ -3049,21 +3627,21 @@ function extractFromObjectLiteral(objLiteral) {
|
|
|
3049
3627
|
return { typeString, properties, isImportRef: false };
|
|
3050
3628
|
}
|
|
3051
3629
|
function inferExpressionType(node) {
|
|
3052
|
-
if (
|
|
3053
|
-
if (
|
|
3630
|
+
if (Node11.isStringLiteral(node)) return "string";
|
|
3631
|
+
if (Node11.isTemplateExpression(node) || Node11.isNoSubstitutionTemplateLiteral(node))
|
|
3054
3632
|
return "string";
|
|
3055
|
-
if (
|
|
3633
|
+
if (Node11.isNumericLiteral(node)) return "number";
|
|
3056
3634
|
if (node.getKind() === SyntaxKind4.TrueKeyword || node.getKind() === SyntaxKind4.FalseKeyword) {
|
|
3057
3635
|
return "boolean";
|
|
3058
3636
|
}
|
|
3059
3637
|
if (node.getKind() === SyntaxKind4.NullKeyword) return "null";
|
|
3060
|
-
if (
|
|
3061
|
-
if (
|
|
3638
|
+
if (Node11.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
3639
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
3062
3640
|
const props = node.getProperties();
|
|
3063
3641
|
if (props.length === 0) return "Record<string, unknown>";
|
|
3064
3642
|
const entries = [];
|
|
3065
3643
|
for (const prop of props) {
|
|
3066
|
-
if (!
|
|
3644
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
3067
3645
|
const key = prop.getName();
|
|
3068
3646
|
const init = prop.getInitializer();
|
|
3069
3647
|
if (!init) continue;
|
|
@@ -3072,23 +3650,23 @@ function inferExpressionType(node) {
|
|
|
3072
3650
|
if (entries.length === 0) return "Record<string, unknown>";
|
|
3073
3651
|
return `{ ${entries.join("; ")} }`;
|
|
3074
3652
|
}
|
|
3075
|
-
if (
|
|
3653
|
+
if (Node11.isArrayLiteralExpression(node)) {
|
|
3076
3654
|
const elements = node.getElements();
|
|
3077
3655
|
if (elements.length === 0) return "Array<unknown>";
|
|
3078
3656
|
const first = elements[0];
|
|
3079
3657
|
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
3080
3658
|
return "Array<unknown>";
|
|
3081
3659
|
}
|
|
3082
|
-
if (
|
|
3660
|
+
if (Node11.isConditionalExpression(node)) {
|
|
3083
3661
|
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
3084
3662
|
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
3085
3663
|
if (whenTrue === whenFalse) return whenTrue;
|
|
3086
3664
|
return `${whenTrue} | ${whenFalse}`;
|
|
3087
3665
|
}
|
|
3088
|
-
if (
|
|
3666
|
+
if (Node11.isParenthesizedExpression(node)) {
|
|
3089
3667
|
return inferExpressionType(node.getExpression());
|
|
3090
3668
|
}
|
|
3091
|
-
if (
|
|
3669
|
+
if (Node11.isAsExpression(node)) {
|
|
3092
3670
|
const typeNode = node.getTypeNode();
|
|
3093
3671
|
if (typeNode) return typeNode.getText();
|
|
3094
3672
|
}
|
|
@@ -3101,7 +3679,7 @@ import { isAbsolute as isAbsolute2, join as join5, relative as relative4 } from
|
|
|
3101
3679
|
|
|
3102
3680
|
// src/extension/registry.ts
|
|
3103
3681
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
3104
|
-
import { Project as
|
|
3682
|
+
import { Project as Project5 } from "ts-morph";
|
|
3105
3683
|
function resolveApiSlots(extensions) {
|
|
3106
3684
|
let layer;
|
|
3107
3685
|
let layerOwner;
|
|
@@ -3152,7 +3730,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
|
|
|
3152
3730
|
},
|
|
3153
3731
|
project() {
|
|
3154
3732
|
if (!project) {
|
|
3155
|
-
project = new
|
|
3733
|
+
project = new Project5({
|
|
3156
3734
|
skipAddingFilesFromTsConfig: true,
|
|
3157
3735
|
skipLoadingLibFiles: true,
|
|
3158
3736
|
skipFileDependencyResolution: true,
|
|
@@ -3270,10 +3848,11 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
3270
3848
|
insertIntoTree(branch.children, rest, leaf, fullName);
|
|
3271
3849
|
}
|
|
3272
3850
|
}
|
|
3273
|
-
function buildParamsType(params) {
|
|
3851
|
+
function buildParamsType(params, paramTypes) {
|
|
3274
3852
|
const pathParams = params.filter((p) => p.source === "path");
|
|
3275
3853
|
if (pathParams.length === 0) return "never";
|
|
3276
|
-
|
|
3854
|
+
const typeOf = (name) => paramTypes && Object.hasOwn(paramTypes, name) && paramTypes[name] || "string";
|
|
3855
|
+
return `{ ${pathParams.map((p) => `${p.name}: ${typeOf(p.name)}`).join("; ")} }`;
|
|
3277
3856
|
}
|
|
3278
3857
|
function hasPathParams(params) {
|
|
3279
3858
|
return params.some((p) => p.source === "path");
|
|
@@ -3367,7 +3946,7 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
|
3367
3946
|
}
|
|
3368
3947
|
const response = buildResponseType(c, outDir, serialization);
|
|
3369
3948
|
const error = buildErrorType(c);
|
|
3370
|
-
const params = buildParamsType(c.params);
|
|
3949
|
+
const params = buildParamsType(c.params, c.contractSource.paramTypes);
|
|
3371
3950
|
const safeMethod = JSON.stringify(method);
|
|
3372
3951
|
const safeUrl = JSON.stringify(c.path);
|
|
3373
3952
|
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
@@ -3636,6 +4215,9 @@ var EMPTY_PATH_NAMESPACE = [
|
|
|
3636
4215
|
"}",
|
|
3637
4216
|
""
|
|
3638
4217
|
];
|
|
4218
|
+
function isClientCallable(route) {
|
|
4219
|
+
return route.method.toUpperCase() !== "ALL";
|
|
4220
|
+
}
|
|
3639
4221
|
function buildApiFile(routes, outDir, opts = {}) {
|
|
3640
4222
|
const fetcherImportPath = opts.fetcherImportPath;
|
|
3641
4223
|
const serialization = opts.serialization ?? "json";
|
|
@@ -3643,7 +4225,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
3643
4225
|
const { layer } = resolveApiSlots(extensions);
|
|
3644
4226
|
const memberExts = extensions.filter((e) => e.apiMembers);
|
|
3645
4227
|
const headerExts = extensions.filter((e) => e.apiHeader);
|
|
3646
|
-
const contracted = routes.filter((r) => r.contract);
|
|
4228
|
+
const contracted = routes.filter((r) => r.contract && isClientCallable(r));
|
|
3647
4229
|
const ctx = opts.ctx ?? {
|
|
3648
4230
|
cwd: outDir ?? "",
|
|
3649
4231
|
outDir: outDir ?? "",
|
|
@@ -4561,6 +5143,7 @@ function buildOpenApiSpec(routes, opts = {}) {
|
|
|
4561
5143
|
const paths = {};
|
|
4562
5144
|
for (const route of routes) {
|
|
4563
5145
|
if (!route.contract) continue;
|
|
5146
|
+
if (route.method.toUpperCase() === "ALL") continue;
|
|
4564
5147
|
const oaPath = toOpenApiPath(route.path);
|
|
4565
5148
|
const method = route.method.toLowerCase();
|
|
4566
5149
|
let pathItem = paths[oaPath];
|
|
@@ -5062,7 +5645,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
5062
5645
|
}
|
|
5063
5646
|
|
|
5064
5647
|
// src/index.ts
|
|
5065
|
-
var VERSION = "0.
|
|
5648
|
+
var VERSION = "0.26.0";
|
|
5066
5649
|
|
|
5067
5650
|
// src/generate-manifest.ts
|
|
5068
5651
|
var MANIFEST_FILE = ".codegen-manifest.json";
|