@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/index.cjs
CHANGED
|
@@ -584,16 +584,16 @@ var import_node_path7 = require("path");
|
|
|
584
584
|
|
|
585
585
|
// src/extension/registry.ts
|
|
586
586
|
var import_node_path6 = require("path");
|
|
587
|
-
var
|
|
587
|
+
var import_ts_morph13 = require("ts-morph");
|
|
588
588
|
|
|
589
589
|
// src/discovery/contracts-fast.ts
|
|
590
590
|
var import_node_fs = require("fs");
|
|
591
591
|
var import_node_path5 = require("path");
|
|
592
592
|
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
593
|
-
var
|
|
593
|
+
var import_ts_morph12 = require("ts-morph");
|
|
594
594
|
|
|
595
595
|
// src/discovery/dto-type-resolver.ts
|
|
596
|
-
var
|
|
596
|
+
var import_ts_morph11 = require("ts-morph");
|
|
597
597
|
|
|
598
598
|
// src/discovery/dto-to-ir.ts
|
|
599
599
|
var import_ts_morph3 = require("ts-morph");
|
|
@@ -2121,6 +2121,672 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
2121
2121
|
return fields;
|
|
2122
2122
|
}
|
|
2123
2123
|
|
|
2124
|
+
// src/discovery/pipe-schema.ts
|
|
2125
|
+
var import_ts_morph10 = require("ts-morph");
|
|
2126
|
+
|
|
2127
|
+
// src/discovery/schema-type-inference.ts
|
|
2128
|
+
var import_ts_morph8 = require("ts-morph");
|
|
2129
|
+
|
|
2130
|
+
// src/discovery/schema-type-program.ts
|
|
2131
|
+
var import_ts_morph7 = require("ts-morph");
|
|
2132
|
+
var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
|
|
2133
|
+
var SchemaTypeProgram = class {
|
|
2134
|
+
constructor(discovery) {
|
|
2135
|
+
this.discovery = discovery;
|
|
2136
|
+
const base = discovery.getCompilerOptions();
|
|
2137
|
+
this.options = {
|
|
2138
|
+
...base,
|
|
2139
|
+
// A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
|
|
2140
|
+
// `Promise` for the schema libraries' declarations to lean on.
|
|
2141
|
+
target: base.target ?? import_ts_morph7.ts.ScriptTarget.ES2022,
|
|
2142
|
+
// With neither set, resolve the way a bundler does: package `exports`
|
|
2143
|
+
// (`zod/v4`) and extensionless or `.js` relative imports all resolve.
|
|
2144
|
+
...base.module === void 0 && base.moduleResolution === void 0 ? { module: import_ts_morph7.ts.ModuleKind.ESNext, moduleResolution: import_ts_morph7.ts.ModuleResolutionKind.Bundler } : {},
|
|
2145
|
+
// Without it `.nullable()` and `.optional()` would erase to the bare type.
|
|
2146
|
+
strictNullChecks: true,
|
|
2147
|
+
// No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
|
|
2148
|
+
// need them, and they are a hundred-odd files of program to build.
|
|
2149
|
+
types: [],
|
|
2150
|
+
noEmit: true
|
|
2151
|
+
};
|
|
2152
|
+
this.project = new import_ts_morph7.Project({
|
|
2153
|
+
skipAddingFilesFromTsConfig: true,
|
|
2154
|
+
compilerOptions: this.options
|
|
2155
|
+
});
|
|
2156
|
+
for (const sf of discovery.getSourceFiles()) this.request(sf.getFilePath(), { schema: true });
|
|
2157
|
+
this.flush();
|
|
2158
|
+
}
|
|
2159
|
+
project;
|
|
2160
|
+
options;
|
|
2161
|
+
/** Parses a file that is not in discovery's Project, without adding it there. */
|
|
2162
|
+
scratch = new import_ts_morph7.Project({
|
|
2163
|
+
skipAddingFilesFromTsConfig: true,
|
|
2164
|
+
skipLoadingLibFiles: true,
|
|
2165
|
+
skipFileDependencyResolution: true
|
|
2166
|
+
});
|
|
2167
|
+
needs = /* @__PURE__ */ new Map();
|
|
2168
|
+
/** Pruned text per file, as last written into {@link project}. */
|
|
2169
|
+
written = /* @__PURE__ */ new Map();
|
|
2170
|
+
/**
|
|
2171
|
+
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
2172
|
+
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
2173
|
+
* program once.
|
|
2174
|
+
*/
|
|
2175
|
+
sourceFileFor(sf) {
|
|
2176
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
2177
|
+
this.flush();
|
|
2178
|
+
return this.project.getSourceFile(sf.getFilePath());
|
|
2179
|
+
}
|
|
2180
|
+
// -------------------------------------------------------------------------
|
|
2181
|
+
queue = [];
|
|
2182
|
+
request(path, want) {
|
|
2183
|
+
let needs = this.needs.get(path);
|
|
2184
|
+
const isNew = !needs;
|
|
2185
|
+
needs ??= { schema: false, all: false, names: /* @__PURE__ */ new Set() };
|
|
2186
|
+
let grew = isNew;
|
|
2187
|
+
if (want.schema && !needs.schema) {
|
|
2188
|
+
needs.schema = true;
|
|
2189
|
+
grew = true;
|
|
2190
|
+
}
|
|
2191
|
+
if (want.all && !needs.all) {
|
|
2192
|
+
needs.all = true;
|
|
2193
|
+
grew = true;
|
|
2194
|
+
}
|
|
2195
|
+
for (const name of want.names ?? []) {
|
|
2196
|
+
if (!needs.names.has(name)) {
|
|
2197
|
+
needs.names.add(name);
|
|
2198
|
+
grew = true;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
this.needs.set(path, needs);
|
|
2202
|
+
if (grew) this.queue.push(path);
|
|
2203
|
+
}
|
|
2204
|
+
/** Run the worklist to a fixpoint, then write every changed file into the program. */
|
|
2205
|
+
flush() {
|
|
2206
|
+
const changed = /* @__PURE__ */ new Map();
|
|
2207
|
+
while (this.queue.length > 0) {
|
|
2208
|
+
const path = this.queue.pop();
|
|
2209
|
+
const original = this.original(path);
|
|
2210
|
+
if (!original) continue;
|
|
2211
|
+
const needs = this.needs.get(path);
|
|
2212
|
+
const text = this.prune(original, needs);
|
|
2213
|
+
if (this.written.get(path) !== text) changed.set(path, text);
|
|
2214
|
+
}
|
|
2215
|
+
for (const [path, text] of changed) {
|
|
2216
|
+
this.project.createSourceFile(path, text, { overwrite: true });
|
|
2217
|
+
this.written.set(path, text);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
/** The file as discovery sees it (its in-memory text), else as it is on disk. */
|
|
2221
|
+
original(path) {
|
|
2222
|
+
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
2226
|
+
* requesting from each kept one's target the names it brings in.
|
|
2227
|
+
*/
|
|
2228
|
+
prune(sf, needs) {
|
|
2229
|
+
const locals = topLevelDeclarations(sf);
|
|
2230
|
+
const roots = [];
|
|
2231
|
+
if (needs.all) roots.push(...sf.getStatements());
|
|
2232
|
+
if (needs.schema) roots.push(...schemaPositions(sf));
|
|
2233
|
+
const reexported = /* @__PURE__ */ new Map();
|
|
2234
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
2235
|
+
const spec = exp.getModuleSpecifierValue();
|
|
2236
|
+
if (spec === void 0) {
|
|
2237
|
+
for (const named of exp.getNamedExports()) {
|
|
2238
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
2239
|
+
if (needs.all || needs.names.has(exposed)) roots.push(named.getNameNode());
|
|
2240
|
+
}
|
|
2241
|
+
continue;
|
|
2242
|
+
}
|
|
2243
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) continue;
|
|
2244
|
+
for (const named of exp.getNamedExports()) {
|
|
2245
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
2246
|
+
const list = reexported.get(exposed) ?? [];
|
|
2247
|
+
list.push({ from: spec, name: named.getNameNode().getText() });
|
|
2248
|
+
reexported.set(exposed, list);
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
const unresolvedNames = [];
|
|
2252
|
+
for (const name of needs.names) {
|
|
2253
|
+
const decls = name === "default" ? defaultExport(sf) : locals.get(name) ?? [];
|
|
2254
|
+
if (decls.length > 0) roots.push(...decls);
|
|
2255
|
+
else if (!reexported.has(name)) unresolvedNames.push(name);
|
|
2256
|
+
}
|
|
2257
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
2258
|
+
const queue = [...roots];
|
|
2259
|
+
const seen = new Set(queue);
|
|
2260
|
+
while (queue.length > 0) {
|
|
2261
|
+
const node = queue.pop();
|
|
2262
|
+
const ids = import_ts_morph7.Node.isIdentifier(node) ? [node] : node.getDescendantsOfKind(import_ts_morph7.ts.SyntaxKind.Identifier);
|
|
2263
|
+
for (const id of ids) {
|
|
2264
|
+
const name = id.getText();
|
|
2265
|
+
if (referenced.has(name)) continue;
|
|
2266
|
+
referenced.add(name);
|
|
2267
|
+
for (const local of locals.get(name) ?? []) {
|
|
2268
|
+
if (seen.has(local)) continue;
|
|
2269
|
+
seen.add(local);
|
|
2270
|
+
queue.push(local);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
const chars = sf.getFullText().split("");
|
|
2275
|
+
const blank = (node) => {
|
|
2276
|
+
for (let i = node.getStart(); i < node.getEnd(); i++) {
|
|
2277
|
+
if (chars[i] !== "\n" && chars[i] !== "\r") chars[i] = " ";
|
|
2278
|
+
}
|
|
2279
|
+
};
|
|
2280
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
2281
|
+
const target = this.resolve(decl.getModuleSpecifierValue(), sf);
|
|
2282
|
+
const namespace = decl.getNamespaceImport()?.getText();
|
|
2283
|
+
const def = decl.getDefaultImport()?.getText();
|
|
2284
|
+
const named = decl.getNamedImports().map((n) => ({
|
|
2285
|
+
local: (n.getAliasNode() ?? n.getNameNode()).getText(),
|
|
2286
|
+
imported: n.getName()
|
|
2287
|
+
})).filter((n) => referenced.has(n.local));
|
|
2288
|
+
const wantsNamespace = namespace !== void 0 && referenced.has(namespace);
|
|
2289
|
+
const wantsDefault = def !== void 0 && referenced.has(def);
|
|
2290
|
+
if (!wantsNamespace && !wantsDefault && named.length === 0) {
|
|
2291
|
+
blank(decl);
|
|
2292
|
+
continue;
|
|
2293
|
+
}
|
|
2294
|
+
if (target) {
|
|
2295
|
+
this.request(target, {
|
|
2296
|
+
all: wantsNamespace,
|
|
2297
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
2302
|
+
const spec = exp.getModuleSpecifierValue();
|
|
2303
|
+
if (spec === void 0) continue;
|
|
2304
|
+
const target = this.resolve(spec, sf);
|
|
2305
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
2306
|
+
if (!needs.all && unresolvedNames.length === 0) {
|
|
2307
|
+
blank(exp);
|
|
2308
|
+
} else if (target) {
|
|
2309
|
+
this.request(target, { all: needs.all, names: unresolvedNames });
|
|
2310
|
+
}
|
|
2311
|
+
continue;
|
|
2312
|
+
}
|
|
2313
|
+
const wanted = exp.getNamedExports().filter(
|
|
2314
|
+
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
2315
|
+
).map((n) => n.getNameNode().getText());
|
|
2316
|
+
if (wanted.length === 0) blank(exp);
|
|
2317
|
+
else if (target) this.request(target, { names: wanted });
|
|
2318
|
+
}
|
|
2319
|
+
return chars.join("");
|
|
2320
|
+
}
|
|
2321
|
+
/**
|
|
2322
|
+
* The project source file a module specifier resolves to, or undefined for a
|
|
2323
|
+
* package (read unpruned by the program itself) or an unresolvable one.
|
|
2324
|
+
*/
|
|
2325
|
+
resolve(specifier, from) {
|
|
2326
|
+
const resolved = import_ts_morph7.ts.resolveModuleName(
|
|
2327
|
+
specifier,
|
|
2328
|
+
from.getFilePath(),
|
|
2329
|
+
this.options,
|
|
2330
|
+
import_ts_morph7.ts.sys
|
|
2331
|
+
).resolvedModule;
|
|
2332
|
+
if (!resolved || resolved.isExternalLibraryImport) return void 0;
|
|
2333
|
+
const file = resolved.resolvedFileName;
|
|
2334
|
+
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
2335
|
+
return file;
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
2338
|
+
function schemaPositions(sf) {
|
|
2339
|
+
const roots = [];
|
|
2340
|
+
sf.forEachDescendant((node) => {
|
|
2341
|
+
if (import_ts_morph7.Node.isDecorator(node)) {
|
|
2342
|
+
const name = node.getName();
|
|
2343
|
+
if (name === "ApplyContract") {
|
|
2344
|
+
roots.push(...node.getArguments());
|
|
2345
|
+
} else if (PARAM_DECORATORS.has(name)) {
|
|
2346
|
+
for (const arg of node.getArguments()) {
|
|
2347
|
+
if (import_ts_morph7.Node.isNewExpression(arg) || import_ts_morph7.Node.isCallExpression(arg)) {
|
|
2348
|
+
roots.push(...arg.getArguments());
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
} else if (import_ts_morph7.Node.isCallExpression(node)) {
|
|
2353
|
+
const callee = node.getExpression();
|
|
2354
|
+
const name = import_ts_morph7.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph7.Node.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
|
|
2355
|
+
if (name === "defineContract") roots.push(...node.getArguments());
|
|
2356
|
+
}
|
|
2357
|
+
});
|
|
2358
|
+
return roots;
|
|
2359
|
+
}
|
|
2360
|
+
function topLevelDeclarations(sf) {
|
|
2361
|
+
const byName = /* @__PURE__ */ new Map();
|
|
2362
|
+
const add = (name, node) => {
|
|
2363
|
+
if (!name) return;
|
|
2364
|
+
const list = byName.get(name);
|
|
2365
|
+
if (list) list.push(node);
|
|
2366
|
+
else byName.set(name, [node]);
|
|
2367
|
+
};
|
|
2368
|
+
for (const decl of sf.getVariableDeclarations()) add(decl.getName(), decl);
|
|
2369
|
+
for (const decl of sf.getFunctions()) add(decl.getName(), decl);
|
|
2370
|
+
for (const decl of sf.getClasses()) add(decl.getName(), decl);
|
|
2371
|
+
for (const decl of sf.getEnums()) add(decl.getName(), decl);
|
|
2372
|
+
for (const decl of sf.getInterfaces()) add(decl.getName(), decl);
|
|
2373
|
+
for (const decl of sf.getTypeAliases()) add(decl.getName(), decl);
|
|
2374
|
+
for (const decl of sf.getModules()) add(decl.getName(), decl);
|
|
2375
|
+
return byName;
|
|
2376
|
+
}
|
|
2377
|
+
function defaultExport(sf) {
|
|
2378
|
+
const nodes = [];
|
|
2379
|
+
for (const assignment of sf.getExportAssignments()) nodes.push(assignment);
|
|
2380
|
+
for (const fn of sf.getFunctions()) if (fn.isDefaultExport()) nodes.push(fn);
|
|
2381
|
+
for (const cls of sf.getClasses()) if (cls.isDefaultExport()) nodes.push(cls);
|
|
2382
|
+
return nodes;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// src/discovery/schema-type-inference.ts
|
|
2386
|
+
var programs = /* @__PURE__ */ new WeakMap();
|
|
2387
|
+
function programFor(discovery) {
|
|
2388
|
+
let program = programs.get(discovery);
|
|
2389
|
+
if (!program) {
|
|
2390
|
+
program = new SchemaTypeProgram(discovery);
|
|
2391
|
+
programs.set(discovery, program);
|
|
2392
|
+
}
|
|
2393
|
+
return program;
|
|
2394
|
+
}
|
|
2395
|
+
function clearSchemaTypeProject(discovery) {
|
|
2396
|
+
programs.delete(discovery);
|
|
2397
|
+
}
|
|
2398
|
+
function counterpart(typedSf, node) {
|
|
2399
|
+
const start = node.getStart();
|
|
2400
|
+
const end = node.getEnd();
|
|
2401
|
+
const kind = node.getKind();
|
|
2402
|
+
let candidate = typedSf.getDescendantAtPos(start);
|
|
2403
|
+
while (candidate) {
|
|
2404
|
+
if (candidate.getStart() === start && candidate.getEnd() === end && candidate.getKind() === kind) {
|
|
2405
|
+
return candidate;
|
|
2406
|
+
}
|
|
2407
|
+
if (candidate.getStart() < start || candidate.getEnd() > end) return void 0;
|
|
2408
|
+
candidate = candidate.getParent();
|
|
2409
|
+
}
|
|
2410
|
+
return void 0;
|
|
2411
|
+
}
|
|
2412
|
+
function propType(type, name, at) {
|
|
2413
|
+
return type.getProperty(name)?.getTypeAtLocation(at);
|
|
2414
|
+
}
|
|
2415
|
+
function isAnyOrUnknown(type) {
|
|
2416
|
+
return type.isAny() || type.isUnknown();
|
|
2417
|
+
}
|
|
2418
|
+
function inferSchema(expr, discovery) {
|
|
2419
|
+
try {
|
|
2420
|
+
const typedSf = programFor(discovery).sourceFileFor(expr.getSourceFile());
|
|
2421
|
+
const at = typedSf && counterpart(typedSf, expr);
|
|
2422
|
+
if (!at) return { kind: "unresolved" };
|
|
2423
|
+
const schemaType = at.getType();
|
|
2424
|
+
if (isAnyOrUnknown(schemaType)) return { kind: "unresolved" };
|
|
2425
|
+
const standard = propType(schemaType, "~standard", at);
|
|
2426
|
+
const stdTypes = standard ? propType(standard, "types", at)?.getNonNullableType() : void 0;
|
|
2427
|
+
const stdInput = stdTypes ? propType(stdTypes, "input", at) : void 0;
|
|
2428
|
+
const stdOutput = stdTypes ? propType(stdTypes, "output", at) : void 0;
|
|
2429
|
+
if (stdInput && stdOutput) {
|
|
2430
|
+
return { kind: "schema", types: { input: stdInput, output: stdOutput, at } };
|
|
2431
|
+
}
|
|
2432
|
+
const zodInput = propType(schemaType, "_input", at);
|
|
2433
|
+
const zodOutput = propType(schemaType, "_output", at);
|
|
2434
|
+
if (zodInput && zodOutput) {
|
|
2435
|
+
return { kind: "schema", types: { input: zodInput, output: zodOutput, at } };
|
|
2436
|
+
}
|
|
2437
|
+
return { kind: "not-schema" };
|
|
2438
|
+
} catch (err) {
|
|
2439
|
+
dbg("schema inference failed:", err);
|
|
2440
|
+
return { kind: "unresolved" };
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
var GLOBAL_NAMED_TYPES = /* @__PURE__ */ new Set([
|
|
2444
|
+
"Date",
|
|
2445
|
+
"File",
|
|
2446
|
+
"Blob",
|
|
2447
|
+
"FormData",
|
|
2448
|
+
"URL",
|
|
2449
|
+
"RegExp",
|
|
2450
|
+
"Map",
|
|
2451
|
+
"Set",
|
|
2452
|
+
"ArrayBuffer",
|
|
2453
|
+
"Uint8Array"
|
|
2454
|
+
]);
|
|
2455
|
+
var MAX_DEPTH = 12;
|
|
2456
|
+
function objectKey(name) {
|
|
2457
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2458
|
+
}
|
|
2459
|
+
function isOptionalSymbol(sym) {
|
|
2460
|
+
return (sym.getFlags() & import_ts_morph8.ts.SymbolFlags.Optional) !== 0;
|
|
2461
|
+
}
|
|
2462
|
+
function isSymbolKeyed(sym) {
|
|
2463
|
+
return sym.getName().startsWith("__@");
|
|
2464
|
+
}
|
|
2465
|
+
function orderedProperties(type) {
|
|
2466
|
+
const position = (sym) => {
|
|
2467
|
+
const decl = sym.getDeclarations()[0];
|
|
2468
|
+
return decl ? [decl.getSourceFile().getFilePath(), decl.getStart()] : void 0;
|
|
2469
|
+
};
|
|
2470
|
+
return type.getProperties().map((sym, index) => ({ sym, index, at: position(sym) })).sort((a, b) => {
|
|
2471
|
+
if (a.at && b.at) {
|
|
2472
|
+
if (a.at[0] !== b.at[0]) return a.at[0] < b.at[0] ? -1 : 1;
|
|
2473
|
+
if (a.at[1] !== b.at[1]) return a.at[1] - b.at[1];
|
|
2474
|
+
} else if (a.at || b.at) {
|
|
2475
|
+
return a.at ? -1 : 1;
|
|
2476
|
+
}
|
|
2477
|
+
return a.index - b.index;
|
|
2478
|
+
}).map((entry) => entry.sym);
|
|
2479
|
+
}
|
|
2480
|
+
function isFromDeclarationFile(sym) {
|
|
2481
|
+
const decl = sym?.getDeclarations()[0];
|
|
2482
|
+
return !!decl && decl.getSourceFile().isDeclarationFile();
|
|
2483
|
+
}
|
|
2484
|
+
function render(type, state, paired) {
|
|
2485
|
+
if (state.depth > MAX_DEPTH) return "unknown";
|
|
2486
|
+
if (isAnyOrUnknown(type)) {
|
|
2487
|
+
return paired && !isAnyOrUnknown(paired) ? render(paired, state) : "unknown";
|
|
2488
|
+
}
|
|
2489
|
+
if (type.isNever()) return "never";
|
|
2490
|
+
if (type.isString() || type.isTemplateLiteral()) return "string";
|
|
2491
|
+
if (type.isNumber()) return "number";
|
|
2492
|
+
if (type.isBoolean()) return "boolean";
|
|
2493
|
+
if (type.isBigInt()) return "bigint";
|
|
2494
|
+
if (type.isNull()) return "null";
|
|
2495
|
+
if (type.isUndefined() || type.isVoid()) return "undefined";
|
|
2496
|
+
if (type.isBooleanLiteral()) return type.getText();
|
|
2497
|
+
if (type.isStringLiteral() || type.isNumberLiteral() || type.isEnumLiteral()) {
|
|
2498
|
+
const value = type.getLiteralValue();
|
|
2499
|
+
return typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
2500
|
+
}
|
|
2501
|
+
if (type.isBigIntLiteral()) return type.getText();
|
|
2502
|
+
if (type.isTypeParameter()) return "unknown";
|
|
2503
|
+
if (type.isUnion()) return renderUnion(type, state, paired);
|
|
2504
|
+
if (type.isIntersection()) return renderIntersection(type, state);
|
|
2505
|
+
const next = { ...state, depth: state.depth + 1 };
|
|
2506
|
+
if (type.isArray() || type.isReadonlyArray()) {
|
|
2507
|
+
const element = type.getArrayElementType() ?? type.getTypeArguments()[0];
|
|
2508
|
+
const pairedElement = paired?.getArrayElementType() ?? paired?.getTypeArguments()[0];
|
|
2509
|
+
return element ? `Array<${render(element, next, pairedElement)}>` : "Array<unknown>";
|
|
2510
|
+
}
|
|
2511
|
+
if (type.isTuple()) {
|
|
2512
|
+
const elements = type.getTupleElements();
|
|
2513
|
+
const pairedElements = paired?.isTuple() ? paired.getTupleElements() : [];
|
|
2514
|
+
return `[${elements.map((e, i) => render(e, next, pairedElements[i])).join(", ")}]`;
|
|
2515
|
+
}
|
|
2516
|
+
if (type.isObject()) return renderObject(type, next, paired);
|
|
2517
|
+
return "unknown";
|
|
2518
|
+
}
|
|
2519
|
+
function renderUnion(type, state, paired) {
|
|
2520
|
+
const members = type.getUnionTypes();
|
|
2521
|
+
const hasTrue = members.some((m) => m.isBooleanLiteral() && m.getText() === "true");
|
|
2522
|
+
const hasFalse = members.some((m) => m.isBooleanLiteral() && m.getText() === "false");
|
|
2523
|
+
const pairedNonNull = paired?.getNonNullableType();
|
|
2524
|
+
const arms = [];
|
|
2525
|
+
let sawBoolean = false;
|
|
2526
|
+
let hasNull = false;
|
|
2527
|
+
let hasUndefined = false;
|
|
2528
|
+
for (const member of members) {
|
|
2529
|
+
if (member.isNull()) {
|
|
2530
|
+
hasNull = true;
|
|
2531
|
+
continue;
|
|
2532
|
+
}
|
|
2533
|
+
if (member.isUndefined()) {
|
|
2534
|
+
hasUndefined = true;
|
|
2535
|
+
continue;
|
|
2536
|
+
}
|
|
2537
|
+
if (member.isBooleanLiteral() && hasTrue && hasFalse) {
|
|
2538
|
+
if (!sawBoolean) arms.push("boolean");
|
|
2539
|
+
sawBoolean = true;
|
|
2540
|
+
continue;
|
|
2541
|
+
}
|
|
2542
|
+
arms.push(render(member, state, members.length === 1 ? paired : pairedNonNull));
|
|
2543
|
+
}
|
|
2544
|
+
if (hasNull) arms.push("null");
|
|
2545
|
+
if (hasUndefined) arms.push("undefined");
|
|
2546
|
+
const unique = [...new Set(arms)];
|
|
2547
|
+
if (unique.includes("unknown")) return "unknown";
|
|
2548
|
+
return unique.join(" | ");
|
|
2549
|
+
}
|
|
2550
|
+
function renderIntersection(type, state) {
|
|
2551
|
+
const members = type.getIntersectionTypes();
|
|
2552
|
+
const primitive = members.find((m) => !m.isObject());
|
|
2553
|
+
if (primitive) return render(primitive, state);
|
|
2554
|
+
return renderObject(type, { ...state, depth: state.depth + 1 }, void 0);
|
|
2555
|
+
}
|
|
2556
|
+
function renderObject(type, state, paired) {
|
|
2557
|
+
const symbol = type.getSymbol() ?? type.getAliasSymbol();
|
|
2558
|
+
const name = symbol?.getName();
|
|
2559
|
+
if (name && GLOBAL_NAMED_TYPES.has(name) && isFromDeclarationFile(symbol)) {
|
|
2560
|
+
const args = type.getTypeArguments();
|
|
2561
|
+
return args.length ? `${name}<${args.map((a) => render(a, state)).join(", ")}>` : name;
|
|
2562
|
+
}
|
|
2563
|
+
if (type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0) {
|
|
2564
|
+
return "unknown";
|
|
2565
|
+
}
|
|
2566
|
+
if (type.isClassOrInterface() && isFromDeclarationFile(symbol)) return "unknown";
|
|
2567
|
+
if (state.stack.has(type.compilerType)) return "unknown";
|
|
2568
|
+
const stack = new Set(state.stack).add(type.compilerType);
|
|
2569
|
+
const inner = { ...state, stack };
|
|
2570
|
+
const fields = [];
|
|
2571
|
+
for (const prop of orderedProperties(type)) {
|
|
2572
|
+
if (isSymbolKeyed(prop)) continue;
|
|
2573
|
+
const propName = prop.getName();
|
|
2574
|
+
const propT = prop.getTypeAtLocation(state.at);
|
|
2575
|
+
if (propT.getCallSignatures().length > 0 && !propT.isUnion()) continue;
|
|
2576
|
+
const pairedProp = paired?.getProperty(propName)?.getTypeAtLocation(state.at);
|
|
2577
|
+
const optional = isOptionalSymbol(prop) ? "?" : "";
|
|
2578
|
+
fields.push(`${objectKey(propName)}${optional}: ${render(propT, inner, pairedProp)}`);
|
|
2579
|
+
}
|
|
2580
|
+
const stringIndex = type.getStringIndexType();
|
|
2581
|
+
if (stringIndex) {
|
|
2582
|
+
const pairedIndex = paired?.getStringIndexType();
|
|
2583
|
+
const value = render(stringIndex, inner, pairedIndex);
|
|
2584
|
+
if (fields.length === 0) return `Record<string, ${value}>`;
|
|
2585
|
+
fields.push(`[key: string]: ${value}`);
|
|
2586
|
+
}
|
|
2587
|
+
return fields.length ? `{ ${fields.join("; ")} }` : "{}";
|
|
2588
|
+
}
|
|
2589
|
+
function stateAt(at) {
|
|
2590
|
+
return { at, stack: /* @__PURE__ */ new Set(), depth: 0 };
|
|
2591
|
+
}
|
|
2592
|
+
function requestTypeOf(types) {
|
|
2593
|
+
return render(types.input, stateAt(types.at), types.output);
|
|
2594
|
+
}
|
|
2595
|
+
function responseTypeOf(types) {
|
|
2596
|
+
return render(types.output, stateAt(types.at));
|
|
2597
|
+
}
|
|
2598
|
+
function requestFieldsOf(types) {
|
|
2599
|
+
const { input, output, at } = types;
|
|
2600
|
+
const objectInput = input.getNonNullableType();
|
|
2601
|
+
if (!objectInput.isObject() || objectInput.isArray() || objectInput.isTuple()) return null;
|
|
2602
|
+
const state = stateAt(at);
|
|
2603
|
+
return orderedProperties(objectInput).filter((prop) => !isSymbolKeyed(prop)).map((prop) => ({
|
|
2604
|
+
name: prop.getName(),
|
|
2605
|
+
type: render(
|
|
2606
|
+
prop.getTypeAtLocation(at),
|
|
2607
|
+
state,
|
|
2608
|
+
output.getProperty(prop.getName())?.getTypeAtLocation(at)
|
|
2609
|
+
),
|
|
2610
|
+
optional: isOptionalSymbol(prop)
|
|
2611
|
+
}));
|
|
2612
|
+
}
|
|
2613
|
+
function acceptsUndefined(types) {
|
|
2614
|
+
const { input, output } = types;
|
|
2615
|
+
const accepted = isAnyOrUnknown(input) ? output : input;
|
|
2616
|
+
if (isAnyOrUnknown(accepted) || accepted.isUndefined()) return true;
|
|
2617
|
+
return accepted.isUnion() && accepted.getUnionTypes().some((t) => t.isUndefined());
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
2621
|
+
var import_ts_morph9 = require("ts-morph");
|
|
2622
|
+
function zodAstToTs(node) {
|
|
2623
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
|
|
2624
|
+
const expr = node.getExpression();
|
|
2625
|
+
if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
|
|
2626
|
+
const methodName = expr.getName();
|
|
2627
|
+
const receiver = expr.getExpression();
|
|
2628
|
+
if (methodName === "optional") {
|
|
2629
|
+
return `${zodAstToTs(receiver)} | undefined`;
|
|
2630
|
+
}
|
|
2631
|
+
if (methodName === "nullable") {
|
|
2632
|
+
return `${zodAstToTs(receiver)} | null`;
|
|
2633
|
+
}
|
|
2634
|
+
const args = node.getArguments();
|
|
2635
|
+
switch (methodName) {
|
|
2636
|
+
case "string":
|
|
2637
|
+
return "string";
|
|
2638
|
+
case "number":
|
|
2639
|
+
return "number";
|
|
2640
|
+
case "boolean":
|
|
2641
|
+
return "boolean";
|
|
2642
|
+
case "unknown":
|
|
2643
|
+
return "unknown";
|
|
2644
|
+
case "any":
|
|
2645
|
+
return "unknown";
|
|
2646
|
+
case "literal": {
|
|
2647
|
+
const lit = args[0];
|
|
2648
|
+
if (!lit) return "unknown";
|
|
2649
|
+
if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2650
|
+
if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2651
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
|
|
2652
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
|
|
2653
|
+
return "unknown";
|
|
2654
|
+
}
|
|
2655
|
+
case "enum": {
|
|
2656
|
+
const arrArg = args[0];
|
|
2657
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2658
|
+
const members = arrArg.getElements().map(
|
|
2659
|
+
(el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2660
|
+
);
|
|
2661
|
+
return members.join(" | ");
|
|
2662
|
+
}
|
|
2663
|
+
case "array": {
|
|
2664
|
+
const inner = args[0];
|
|
2665
|
+
if (!inner) return "unknown";
|
|
2666
|
+
return `Array<${zodAstToTs(inner)}>`;
|
|
2667
|
+
}
|
|
2668
|
+
case "object": {
|
|
2669
|
+
const objArg = args[0];
|
|
2670
|
+
if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2671
|
+
const lines = [];
|
|
2672
|
+
for (const prop of objArg.getProperties()) {
|
|
2673
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
2674
|
+
const key = prop.getName();
|
|
2675
|
+
const valNode = prop.getInitializer();
|
|
2676
|
+
if (!valNode) continue;
|
|
2677
|
+
const tsType = zodAstToTs(valNode);
|
|
2678
|
+
const isOpt = isOptionalChain(valNode);
|
|
2679
|
+
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2680
|
+
}
|
|
2681
|
+
return `{ ${lines.join("; ")} }`;
|
|
2682
|
+
}
|
|
2683
|
+
case "union": {
|
|
2684
|
+
const arrArg = args[0];
|
|
2685
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2686
|
+
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2687
|
+
}
|
|
2688
|
+
case "record": {
|
|
2689
|
+
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2690
|
+
if (!valArg) return "unknown";
|
|
2691
|
+
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2692
|
+
}
|
|
2693
|
+
case "tuple": {
|
|
2694
|
+
const arrArg = args[0];
|
|
2695
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2696
|
+
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2697
|
+
}
|
|
2698
|
+
default:
|
|
2699
|
+
return "unknown";
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
return "unknown";
|
|
2703
|
+
}
|
|
2704
|
+
function isOptionalChain(node) {
|
|
2705
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return false;
|
|
2706
|
+
const expr = node.getExpression();
|
|
2707
|
+
return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2708
|
+
}
|
|
2709
|
+
function memberType(val, position, project) {
|
|
2710
|
+
if (project) {
|
|
2711
|
+
const inferred = inferSchema(val, project);
|
|
2712
|
+
if (inferred.kind === "schema") {
|
|
2713
|
+
return position === "request" ? requestTypeOf(inferred.types) : responseTypeOf(inferred.types);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
return zodAstToTs(val);
|
|
2717
|
+
}
|
|
2718
|
+
function parseDefineContractCall(callExpr, project) {
|
|
2719
|
+
if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
|
|
2720
|
+
const callee = callExpr.getExpression();
|
|
2721
|
+
const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2722
|
+
if (calleeName !== "defineContract") return null;
|
|
2723
|
+
const args = callExpr.getArguments();
|
|
2724
|
+
const optsArg = args[0];
|
|
2725
|
+
if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2726
|
+
let query = null;
|
|
2727
|
+
let body = null;
|
|
2728
|
+
let response = "unknown";
|
|
2729
|
+
let error = null;
|
|
2730
|
+
let bodyZodText = null;
|
|
2731
|
+
let queryZodText = null;
|
|
2732
|
+
for (const prop of optsArg.getProperties()) {
|
|
2733
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
2734
|
+
const propName = prop.getName();
|
|
2735
|
+
const val = prop.getInitializer();
|
|
2736
|
+
if (!val) continue;
|
|
2737
|
+
if (propName === "query") {
|
|
2738
|
+
query = memberType(val, "request", project);
|
|
2739
|
+
queryZodText = val.getText();
|
|
2740
|
+
} else if (propName === "body") {
|
|
2741
|
+
body = memberType(val, "request", project);
|
|
2742
|
+
bodyZodText = val.getText();
|
|
2743
|
+
} else if (propName === "response") {
|
|
2744
|
+
response = memberType(val, "response", project);
|
|
2745
|
+
} else if (propName === "error") {
|
|
2746
|
+
error = memberType(val, "response", project);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// src/discovery/pipe-schema.ts
|
|
2753
|
+
function pipeSchemaOf(decorator, project) {
|
|
2754
|
+
for (const pipe of decorator.getArguments()) {
|
|
2755
|
+
if (!import_ts_morph10.Node.isNewExpression(pipe) && !import_ts_morph10.Node.isCallExpression(pipe)) continue;
|
|
2756
|
+
for (const arg of pipe.getArguments()) {
|
|
2757
|
+
if (!couldBeSchema(arg)) continue;
|
|
2758
|
+
const inferred = inferSchema(arg, project);
|
|
2759
|
+
if (inferred.kind === "schema") {
|
|
2760
|
+
return {
|
|
2761
|
+
type: requestTypeOf(inferred.types),
|
|
2762
|
+
optional: acceptsUndefined(inferred.types),
|
|
2763
|
+
fields: requestFieldsOf(inferred.types)
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
if (inferred.kind === "unresolved") {
|
|
2767
|
+
const fallback = syntacticZodType(arg, project);
|
|
2768
|
+
if (fallback) {
|
|
2769
|
+
return { type: fallback, optional: fallback.endsWith("| undefined"), fields: null };
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
return null;
|
|
2775
|
+
}
|
|
2776
|
+
function couldBeSchema(arg) {
|
|
2777
|
+
return import_ts_morph10.Node.isIdentifier(arg) || import_ts_morph10.Node.isPropertyAccessExpression(arg) || import_ts_morph10.Node.isCallExpression(arg) || import_ts_morph10.Node.isAsExpression(arg) || import_ts_morph10.Node.isSatisfiesExpression(arg) || import_ts_morph10.Node.isParenthesizedExpression(arg);
|
|
2778
|
+
}
|
|
2779
|
+
function syntacticZodType(arg, project) {
|
|
2780
|
+
let expr = arg;
|
|
2781
|
+
if (import_ts_morph10.Node.isIdentifier(arg)) {
|
|
2782
|
+
const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
|
|
2783
|
+
expr = resolved?.decl.getInitializer();
|
|
2784
|
+
}
|
|
2785
|
+
if (!expr || !import_ts_morph10.Node.isCallExpression(expr)) return null;
|
|
2786
|
+
const type = zodAstToTs(expr);
|
|
2787
|
+
return type === "unknown" ? null : type;
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2124
2790
|
// src/discovery/dto-type-resolver.ts
|
|
2125
2791
|
var WRAPPER_TYPES = {
|
|
2126
2792
|
// MikroORM Ref/Reference/LoadedReference/IdentifiedReference are server-side
|
|
@@ -2152,22 +2818,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
|
|
|
2152
2818
|
]);
|
|
2153
2819
|
function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
|
|
2154
2820
|
if (depth <= 0) return "unknown";
|
|
2155
|
-
if (
|
|
2821
|
+
if (import_ts_morph11.Node.isArrayTypeNode(typeNode)) {
|
|
2156
2822
|
const elementType = typeNode.getElementTypeNode();
|
|
2157
2823
|
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
|
|
2158
2824
|
}
|
|
2159
|
-
if (
|
|
2825
|
+
if (import_ts_morph11.Node.isUnionTypeNode(typeNode)) {
|
|
2160
2826
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
|
|
2161
2827
|
}
|
|
2162
|
-
if (
|
|
2828
|
+
if (import_ts_morph11.Node.isIntersectionTypeNode(typeNode)) {
|
|
2163
2829
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
|
|
2164
2830
|
}
|
|
2165
|
-
if (
|
|
2831
|
+
if (import_ts_morph11.Node.isParenthesizedTypeNode(typeNode)) {
|
|
2166
2832
|
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
|
|
2167
2833
|
}
|
|
2168
|
-
if (
|
|
2834
|
+
if (import_ts_morph11.Node.isTypeReference(typeNode)) {
|
|
2169
2835
|
const typeName = typeNode.getTypeName();
|
|
2170
|
-
const name =
|
|
2836
|
+
const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
2171
2837
|
const bound = subst.get(name);
|
|
2172
2838
|
if (bound !== void 0) return bound;
|
|
2173
2839
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
@@ -2190,13 +2856,13 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
2190
2856
|
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
2191
2857
|
return "unknown";
|
|
2192
2858
|
}
|
|
2193
|
-
if (
|
|
2859
|
+
if (import_ts_morph11.Node.isTypeLiteral(typeNode)) {
|
|
2194
2860
|
const members = [];
|
|
2195
2861
|
for (const member of typeNode.getMembers()) {
|
|
2196
|
-
if (
|
|
2862
|
+
if (import_ts_morph11.Node.isPropertySignature(member)) {
|
|
2197
2863
|
const memberTypeNode = member.getTypeNode();
|
|
2198
|
-
const
|
|
2199
|
-
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${
|
|
2864
|
+
const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
|
|
2865
|
+
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
|
|
2200
2866
|
} else {
|
|
2201
2867
|
members.push(member.getText());
|
|
2202
2868
|
}
|
|
@@ -2204,11 +2870,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
2204
2870
|
return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
|
|
2205
2871
|
}
|
|
2206
2872
|
const kind = typeNode.getKind();
|
|
2207
|
-
if (kind ===
|
|
2208
|
-
if (kind ===
|
|
2209
|
-
if (kind ===
|
|
2210
|
-
if (kind ===
|
|
2211
|
-
if (kind ===
|
|
2873
|
+
if (kind === import_ts_morph11.SyntaxKind.StringKeyword) return "string";
|
|
2874
|
+
if (kind === import_ts_morph11.SyntaxKind.NumberKeyword) return "number";
|
|
2875
|
+
if (kind === import_ts_morph11.SyntaxKind.BooleanKeyword) return "boolean";
|
|
2876
|
+
if (kind === import_ts_morph11.SyntaxKind.UnknownKeyword) return "unknown";
|
|
2877
|
+
if (kind === import_ts_morph11.SyntaxKind.AnyKeyword) return "unknown";
|
|
2212
2878
|
return typeNode.getText();
|
|
2213
2879
|
}
|
|
2214
2880
|
function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
|
|
@@ -2256,20 +2922,24 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
|
|
|
2256
2922
|
const propName = prop.getName();
|
|
2257
2923
|
const isOptional = prop.hasQuestionToken();
|
|
2258
2924
|
const propTypeNode = prop.getTypeNode();
|
|
2259
|
-
let
|
|
2925
|
+
let propType2 = "unknown";
|
|
2260
2926
|
if (propTypeNode) {
|
|
2261
|
-
|
|
2927
|
+
propType2 = resolveTypeNodeToString(propTypeNode, sourceFile, project, depth, subst);
|
|
2262
2928
|
}
|
|
2263
|
-
lines.push(`${propName}${isOptional ? "?" : ""}: ${
|
|
2929
|
+
lines.push(`${propName}${isOptional ? "?" : ""}: ${propType2}`);
|
|
2264
2930
|
}
|
|
2265
2931
|
return `{ ${lines.join("; ")} }`;
|
|
2266
2932
|
}
|
|
2933
|
+
function namedField(decorator) {
|
|
2934
|
+
const first = decorator.getArguments()[0];
|
|
2935
|
+
return first && import_ts_morph11.Node.isStringLiteral(first) ? first.getLiteralValue() : null;
|
|
2936
|
+
}
|
|
2267
2937
|
function extractBodyType(method, sourceFile, project) {
|
|
2268
2938
|
for (const param of method.getParameters()) {
|
|
2269
2939
|
const bodyDecorator = param.getDecorators().find((d) => d.getName() === "Body");
|
|
2270
|
-
if (!bodyDecorator) continue;
|
|
2271
|
-
const
|
|
2272
|
-
if (
|
|
2940
|
+
if (!bodyDecorator || namedField(bodyDecorator) !== null) continue;
|
|
2941
|
+
const piped = pipeSchemaOf(bodyDecorator, project);
|
|
2942
|
+
if (piped) return piped.type;
|
|
2273
2943
|
const typeNode = param.getTypeNode();
|
|
2274
2944
|
if (typeNode) {
|
|
2275
2945
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -2277,11 +2947,16 @@ function extractBodyType(method, sourceFile, project) {
|
|
|
2277
2947
|
}
|
|
2278
2948
|
return null;
|
|
2279
2949
|
}
|
|
2950
|
+
function hasPipeSchema(param, decoratorName, project) {
|
|
2951
|
+
const decorator = param.getDecorators().find((d) => d.getName() === decoratorName);
|
|
2952
|
+
return !!decorator && pipeSchemaOf(decorator, project) !== null;
|
|
2953
|
+
}
|
|
2280
2954
|
function extractQueryType(method, sourceFile, project) {
|
|
2281
2955
|
for (const param of method.getParameters()) {
|
|
2282
2956
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
2283
|
-
if (!queryDecorator) continue;
|
|
2284
|
-
|
|
2957
|
+
if (!queryDecorator || namedField(queryDecorator) !== null) continue;
|
|
2958
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2959
|
+
if (piped) return piped.type;
|
|
2285
2960
|
const typeNode = param.getTypeNode();
|
|
2286
2961
|
if (typeNode) {
|
|
2287
2962
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -2291,10 +2966,13 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
2291
2966
|
for (const param of method.getParameters()) {
|
|
2292
2967
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
2293
2968
|
if (!queryDecorator) continue;
|
|
2294
|
-
const
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2969
|
+
const queryName = namedField(queryDecorator);
|
|
2970
|
+
if (queryName === null) continue;
|
|
2971
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2972
|
+
if (piped) {
|
|
2973
|
+
entries.push(`${queryName}${piped.optional ? "?" : ""}: ${piped.type}`);
|
|
2974
|
+
continue;
|
|
2975
|
+
}
|
|
2298
2976
|
const typeNode = param.getTypeNode();
|
|
2299
2977
|
const queryType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
2300
2978
|
entries.push(`${queryName}${isParamOptional(param) ? "?" : ""}: ${queryType}`);
|
|
@@ -2304,8 +2982,8 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
2304
2982
|
function isParamOptional(param) {
|
|
2305
2983
|
if (param.hasQuestionToken() || param.hasInitializer()) return true;
|
|
2306
2984
|
const typeNode = param.getTypeNode();
|
|
2307
|
-
if (typeNode &&
|
|
2308
|
-
return typeNode.getTypeNodes().some((t) => t.getKind() ===
|
|
2985
|
+
if (typeNode && import_ts_morph11.Node.isUnionTypeNode(typeNode)) {
|
|
2986
|
+
return typeNode.getTypeNodes().some((t) => t.getKind() === import_ts_morph11.SyntaxKind.UndefinedKeyword);
|
|
2309
2987
|
}
|
|
2310
2988
|
return false;
|
|
2311
2989
|
}
|
|
@@ -2317,7 +2995,7 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
2317
2995
|
const paramArgs = paramDecorator.getArguments();
|
|
2318
2996
|
if (paramArgs.length === 0) continue;
|
|
2319
2997
|
const nameArg = paramArgs[0];
|
|
2320
|
-
if (!
|
|
2998
|
+
if (!import_ts_morph11.Node.isStringLiteral(nameArg)) continue;
|
|
2321
2999
|
const paramName = nameArg.getLiteralValue();
|
|
2322
3000
|
const typeNode = param.getTypeNode();
|
|
2323
3001
|
const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
@@ -2325,6 +3003,22 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
2325
3003
|
}
|
|
2326
3004
|
return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
|
|
2327
3005
|
}
|
|
3006
|
+
function extractPipedParamTypes(method, project) {
|
|
3007
|
+
const types = {};
|
|
3008
|
+
for (const param of method.getParameters()) {
|
|
3009
|
+
const paramDecorator = param.getDecorators().find((d) => d.getName() === "Param");
|
|
3010
|
+
if (!paramDecorator) continue;
|
|
3011
|
+
const piped = pipeSchemaOf(paramDecorator, project);
|
|
3012
|
+
if (!piped) continue;
|
|
3013
|
+
const name = namedField(paramDecorator);
|
|
3014
|
+
if (name !== null) {
|
|
3015
|
+
types[name] = piped.type;
|
|
3016
|
+
} else {
|
|
3017
|
+
for (const field of piped.fields ?? []) types[field.name] = field.type;
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
return Object.keys(types).length > 0 ? types : null;
|
|
3021
|
+
}
|
|
2328
3022
|
function extractUploadedFiles(method) {
|
|
2329
3023
|
const FILE = "File | Blob";
|
|
2330
3024
|
const entries = [];
|
|
@@ -2338,28 +3032,28 @@ function extractUploadedFiles(method) {
|
|
|
2338
3032
|
for (const decorator of method.getDecorators()) {
|
|
2339
3033
|
if (decorator.getName() !== "UseInterceptors") continue;
|
|
2340
3034
|
for (const arg of decorator.getArguments()) {
|
|
2341
|
-
if (!
|
|
3035
|
+
if (!import_ts_morph11.Node.isCallExpression(arg)) continue;
|
|
2342
3036
|
const interceptor = arg.getExpression().getText();
|
|
2343
3037
|
const callArgs = arg.getArguments();
|
|
2344
3038
|
const firstArg2 = callArgs[0];
|
|
2345
3039
|
if (interceptor === "FileInterceptor") {
|
|
2346
|
-
if (firstArg2 &&
|
|
3040
|
+
if (firstArg2 && import_ts_morph11.Node.isStringLiteral(firstArg2)) {
|
|
2347
3041
|
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
2348
3042
|
multipart = true;
|
|
2349
3043
|
}
|
|
2350
3044
|
} else if (interceptor === "FilesInterceptor") {
|
|
2351
|
-
if (firstArg2 &&
|
|
3045
|
+
if (firstArg2 && import_ts_morph11.Node.isStringLiteral(firstArg2)) {
|
|
2352
3046
|
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
2353
3047
|
multipart = true;
|
|
2354
3048
|
}
|
|
2355
3049
|
} else if (interceptor === "FileFieldsInterceptor") {
|
|
2356
|
-
if (firstArg2 &&
|
|
3050
|
+
if (firstArg2 && import_ts_morph11.Node.isArrayLiteralExpression(firstArg2)) {
|
|
2357
3051
|
for (const el of firstArg2.getElements()) {
|
|
2358
|
-
if (!
|
|
3052
|
+
if (!import_ts_morph11.Node.isObjectLiteralExpression(el)) continue;
|
|
2359
3053
|
const nameProp = el.getProperty("name");
|
|
2360
|
-
if (nameProp &&
|
|
3054
|
+
if (nameProp && import_ts_morph11.Node.isPropertyAssignment(nameProp)) {
|
|
2361
3055
|
const init = nameProp.getInitializer();
|
|
2362
|
-
if (init &&
|
|
3056
|
+
if (init && import_ts_morph11.Node.isStringLiteral(init)) {
|
|
2363
3057
|
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
2364
3058
|
}
|
|
2365
3059
|
}
|
|
@@ -2379,13 +3073,13 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
2379
3073
|
if (apiResponseDecorator) {
|
|
2380
3074
|
const args = apiResponseDecorator.getArguments();
|
|
2381
3075
|
const optsArg = args[0];
|
|
2382
|
-
if (optsArg &&
|
|
3076
|
+
if (optsArg && import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) {
|
|
2383
3077
|
for (const prop of optsArg.getProperties()) {
|
|
2384
|
-
if (!
|
|
3078
|
+
if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
|
|
2385
3079
|
if (prop.getName() !== "type") continue;
|
|
2386
3080
|
const val = prop.getInitializer();
|
|
2387
3081
|
if (!val) continue;
|
|
2388
|
-
if (
|
|
3082
|
+
if (import_ts_morph11.Node.isArrayLiteralExpression(val)) {
|
|
2389
3083
|
const elements = val.getElements();
|
|
2390
3084
|
const firstEl = elements[0];
|
|
2391
3085
|
if (elements.length > 0 && firstEl !== void 0) {
|
|
@@ -2406,24 +3100,24 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
2406
3100
|
}
|
|
2407
3101
|
function apiResponseStatus(decorator) {
|
|
2408
3102
|
const optsArg = decorator.getArguments()[0];
|
|
2409
|
-
if (!optsArg || !
|
|
3103
|
+
if (!optsArg || !import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2410
3104
|
for (const prop of optsArg.getProperties()) {
|
|
2411
|
-
if (!
|
|
3105
|
+
if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
|
|
2412
3106
|
if (prop.getName() !== "status") continue;
|
|
2413
3107
|
const val = prop.getInitializer();
|
|
2414
|
-
if (val &&
|
|
3108
|
+
if (val && import_ts_morph11.Node.isNumericLiteral(val)) return Number(val.getLiteralValue());
|
|
2415
3109
|
}
|
|
2416
3110
|
return null;
|
|
2417
3111
|
}
|
|
2418
3112
|
function apiResponseTypeNode(decorator) {
|
|
2419
3113
|
const optsArg = decorator.getArguments()[0];
|
|
2420
|
-
if (!optsArg || !
|
|
3114
|
+
if (!optsArg || !import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2421
3115
|
for (const prop of optsArg.getProperties()) {
|
|
2422
|
-
if (!
|
|
3116
|
+
if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
|
|
2423
3117
|
if (prop.getName() !== "type") continue;
|
|
2424
3118
|
const val = prop.getInitializer();
|
|
2425
3119
|
if (!val) return null;
|
|
2426
|
-
if (
|
|
3120
|
+
if (import_ts_morph11.Node.isArrayLiteralExpression(val)) {
|
|
2427
3121
|
const first = val.getElements()[0];
|
|
2428
3122
|
return first ? { node: first, isArray: true } : null;
|
|
2429
3123
|
}
|
|
@@ -2441,7 +3135,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2441
3135
|
const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
|
|
2442
3136
|
const type = typeInfo.isArray ? `Array<${inner}>` : inner;
|
|
2443
3137
|
let ref = null;
|
|
2444
|
-
if (
|
|
3138
|
+
if (import_ts_morph11.Node.isIdentifier(typeInfo.node)) {
|
|
2445
3139
|
const name = typeInfo.node.getText();
|
|
2446
3140
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2447
3141
|
if (localDecl?.isExported()) {
|
|
@@ -2458,7 +3152,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2458
3152
|
return null;
|
|
2459
3153
|
}
|
|
2460
3154
|
function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
2461
|
-
if (!
|
|
3155
|
+
if (!import_ts_morph11.Node.isIdentifier(node)) return "unknown";
|
|
2462
3156
|
const name = node.getText();
|
|
2463
3157
|
const resolved = findType(name, sourceFile, project);
|
|
2464
3158
|
if (resolved) {
|
|
@@ -2478,9 +3172,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
|
|
|
2478
3172
|
var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
|
|
2479
3173
|
function detectBinaryResponse(method) {
|
|
2480
3174
|
const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
|
|
2481
|
-
if (!node || !
|
|
3175
|
+
if (!node || !import_ts_morph11.Node.isTypeReference(node)) return false;
|
|
2482
3176
|
const typeName = node.getTypeName();
|
|
2483
|
-
const name =
|
|
3177
|
+
const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2484
3178
|
return BINARY_RESPONSE_TYPES.has(name);
|
|
2485
3179
|
}
|
|
2486
3180
|
function detectStreamElement(method) {
|
|
@@ -2495,9 +3189,9 @@ function detectStreamElement(method) {
|
|
|
2495
3189
|
return null;
|
|
2496
3190
|
}
|
|
2497
3191
|
function streamContainerElement(node) {
|
|
2498
|
-
if (!node || !
|
|
3192
|
+
if (!node || !import_ts_morph11.Node.isTypeReference(node)) return null;
|
|
2499
3193
|
const typeName = node.getTypeName();
|
|
2500
|
-
const name =
|
|
3194
|
+
const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2501
3195
|
if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
|
|
2502
3196
|
return node.getTypeArguments()[0] ?? null;
|
|
2503
3197
|
}
|
|
@@ -2507,9 +3201,9 @@ function hasAsQueryDecorator(method) {
|
|
|
2507
3201
|
return method.getDecorators().some((d) => d.getName() === "AsQuery");
|
|
2508
3202
|
}
|
|
2509
3203
|
function unwrapNamedContainer(node, names) {
|
|
2510
|
-
if (!node || !
|
|
3204
|
+
if (!node || !import_ts_morph11.Node.isTypeReference(node)) return node;
|
|
2511
3205
|
const typeName = node.getTypeName();
|
|
2512
|
-
const name =
|
|
3206
|
+
const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2513
3207
|
if (names.has(name)) {
|
|
2514
3208
|
return node.getTypeArguments()[0] ?? node;
|
|
2515
3209
|
}
|
|
@@ -2530,19 +3224,20 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2530
3224
|
body = body ?? bodyType;
|
|
2531
3225
|
}
|
|
2532
3226
|
const paramsType = extractParamsType(method, sourceFile, project);
|
|
3227
|
+
const paramTypes = extractPipedParamTypes(method, project);
|
|
2533
3228
|
const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
|
|
2534
3229
|
const errorInfo = extractErrorType(method, sourceFile, project);
|
|
2535
|
-
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
3230
|
+
if (body === null && query === null && paramsType === null && paramTypes === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
2536
3231
|
return null;
|
|
2537
3232
|
}
|
|
2538
3233
|
let bodyRef = null;
|
|
2539
3234
|
let queryRef = null;
|
|
2540
3235
|
let responseRef = null;
|
|
2541
3236
|
for (const param of method.getParameters()) {
|
|
2542
|
-
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
3237
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode() && !hasPipeSchema(param, "Body", project)) {
|
|
2543
3238
|
bodyRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2544
3239
|
}
|
|
2545
|
-
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
3240
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode() && !hasPipeSchema(param, "Query", project)) {
|
|
2546
3241
|
queryRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2547
3242
|
}
|
|
2548
3243
|
}
|
|
@@ -2555,11 +3250,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2555
3250
|
if (apiResp) {
|
|
2556
3251
|
const args = apiResp.getArguments();
|
|
2557
3252
|
const optsArg = args[0];
|
|
2558
|
-
if (optsArg &&
|
|
3253
|
+
if (optsArg && import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) {
|
|
2559
3254
|
for (const prop of optsArg.getProperties()) {
|
|
2560
|
-
if (
|
|
3255
|
+
if (import_ts_morph11.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
2561
3256
|
const val = prop.getInitializer();
|
|
2562
|
-
if (val &&
|
|
3257
|
+
if (val && import_ts_morph11.Node.isIdentifier(val)) {
|
|
2563
3258
|
const name = val.getText();
|
|
2564
3259
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2565
3260
|
if (localDecl?.isExported()) {
|
|
@@ -2595,6 +3290,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2595
3290
|
response,
|
|
2596
3291
|
error: errorInfo?.type ?? null,
|
|
2597
3292
|
params: paramsType,
|
|
3293
|
+
paramTypes,
|
|
2598
3294
|
queryRef,
|
|
2599
3295
|
bodyRef,
|
|
2600
3296
|
responseRef,
|
|
@@ -2615,6 +3311,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2615
3311
|
function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
2616
3312
|
for (const param of method.getParameters()) {
|
|
2617
3313
|
if (!param.getDecorators().some((d) => d.getName() === decoratorName)) continue;
|
|
3314
|
+
if (hasPipeSchema(param, decoratorName, project)) continue;
|
|
2618
3315
|
const typeNode = param.getTypeNode();
|
|
2619
3316
|
if (!typeNode) continue;
|
|
2620
3317
|
const text = typeNode.getText().replace(/\[\]$/, "");
|
|
@@ -2627,129 +3324,6 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
2627
3324
|
return null;
|
|
2628
3325
|
}
|
|
2629
3326
|
|
|
2630
|
-
// src/discovery/zod-ast-to-ts.ts
|
|
2631
|
-
var import_ts_morph8 = require("ts-morph");
|
|
2632
|
-
function zodAstToTs(node) {
|
|
2633
|
-
if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
|
|
2634
|
-
const expr = node.getExpression();
|
|
2635
|
-
if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
|
|
2636
|
-
const methodName = expr.getName();
|
|
2637
|
-
const receiver = expr.getExpression();
|
|
2638
|
-
if (methodName === "optional") {
|
|
2639
|
-
return `${zodAstToTs(receiver)} | undefined`;
|
|
2640
|
-
}
|
|
2641
|
-
if (methodName === "nullable") {
|
|
2642
|
-
return `${zodAstToTs(receiver)} | null`;
|
|
2643
|
-
}
|
|
2644
|
-
const args = node.getArguments();
|
|
2645
|
-
switch (methodName) {
|
|
2646
|
-
case "string":
|
|
2647
|
-
return "string";
|
|
2648
|
-
case "number":
|
|
2649
|
-
return "number";
|
|
2650
|
-
case "boolean":
|
|
2651
|
-
return "boolean";
|
|
2652
|
-
case "unknown":
|
|
2653
|
-
return "unknown";
|
|
2654
|
-
case "any":
|
|
2655
|
-
return "unknown";
|
|
2656
|
-
case "literal": {
|
|
2657
|
-
const lit = args[0];
|
|
2658
|
-
if (!lit) return "unknown";
|
|
2659
|
-
if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2660
|
-
if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2661
|
-
if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
|
|
2662
|
-
if (lit.getKind() === import_ts_morph8.SyntaxKind.FalseKeyword) return "false";
|
|
2663
|
-
return "unknown";
|
|
2664
|
-
}
|
|
2665
|
-
case "enum": {
|
|
2666
|
-
const arrArg = args[0];
|
|
2667
|
-
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2668
|
-
const members = arrArg.getElements().map(
|
|
2669
|
-
(el) => import_ts_morph8.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2670
|
-
);
|
|
2671
|
-
return members.join(" | ");
|
|
2672
|
-
}
|
|
2673
|
-
case "array": {
|
|
2674
|
-
const inner = args[0];
|
|
2675
|
-
if (!inner) return "unknown";
|
|
2676
|
-
return `Array<${zodAstToTs(inner)}>`;
|
|
2677
|
-
}
|
|
2678
|
-
case "object": {
|
|
2679
|
-
const objArg = args[0];
|
|
2680
|
-
if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2681
|
-
const lines = [];
|
|
2682
|
-
for (const prop of objArg.getProperties()) {
|
|
2683
|
-
if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
|
|
2684
|
-
const key = prop.getName();
|
|
2685
|
-
const valNode = prop.getInitializer();
|
|
2686
|
-
if (!valNode) continue;
|
|
2687
|
-
const tsType = zodAstToTs(valNode);
|
|
2688
|
-
const isOpt = isOptionalChain(valNode);
|
|
2689
|
-
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2690
|
-
}
|
|
2691
|
-
return `{ ${lines.join("; ")} }`;
|
|
2692
|
-
}
|
|
2693
|
-
case "union": {
|
|
2694
|
-
const arrArg = args[0];
|
|
2695
|
-
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2696
|
-
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2697
|
-
}
|
|
2698
|
-
case "record": {
|
|
2699
|
-
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2700
|
-
if (!valArg) return "unknown";
|
|
2701
|
-
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2702
|
-
}
|
|
2703
|
-
case "tuple": {
|
|
2704
|
-
const arrArg = args[0];
|
|
2705
|
-
if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2706
|
-
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2707
|
-
}
|
|
2708
|
-
default:
|
|
2709
|
-
return "unknown";
|
|
2710
|
-
}
|
|
2711
|
-
}
|
|
2712
|
-
return "unknown";
|
|
2713
|
-
}
|
|
2714
|
-
function isOptionalChain(node) {
|
|
2715
|
-
if (!import_ts_morph8.Node.isCallExpression(node)) return false;
|
|
2716
|
-
const expr = node.getExpression();
|
|
2717
|
-
return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2718
|
-
}
|
|
2719
|
-
function parseDefineContractCall(callExpr) {
|
|
2720
|
-
if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
|
|
2721
|
-
const callee = callExpr.getExpression();
|
|
2722
|
-
const calleeName = import_ts_morph8.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph8.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2723
|
-
if (calleeName !== "defineContract") return null;
|
|
2724
|
-
const args = callExpr.getArguments();
|
|
2725
|
-
const optsArg = args[0];
|
|
2726
|
-
if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
2727
|
-
let query = null;
|
|
2728
|
-
let body = null;
|
|
2729
|
-
let response = "unknown";
|
|
2730
|
-
let error = null;
|
|
2731
|
-
let bodyZodText = null;
|
|
2732
|
-
let queryZodText = null;
|
|
2733
|
-
for (const prop of optsArg.getProperties()) {
|
|
2734
|
-
if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
|
|
2735
|
-
const propName = prop.getName();
|
|
2736
|
-
const val = prop.getInitializer();
|
|
2737
|
-
if (!val) continue;
|
|
2738
|
-
if (propName === "query") {
|
|
2739
|
-
query = zodAstToTs(val);
|
|
2740
|
-
queryZodText = val.getText();
|
|
2741
|
-
} else if (propName === "body") {
|
|
2742
|
-
body = zodAstToTs(val);
|
|
2743
|
-
bodyZodText = val.getText();
|
|
2744
|
-
} else if (propName === "response") {
|
|
2745
|
-
response = zodAstToTs(val);
|
|
2746
|
-
} else if (propName === "error") {
|
|
2747
|
-
error = zodAstToTs(val);
|
|
2748
|
-
}
|
|
2749
|
-
}
|
|
2750
|
-
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
3327
|
// src/discovery/contracts-fast.ts
|
|
2754
3328
|
async function discoverContractsFast(opts) {
|
|
2755
3329
|
const { cwd, glob, tsconfig } = opts;
|
|
@@ -2762,7 +3336,9 @@ async function discoverContractsFast(opts) {
|
|
|
2762
3336
|
}
|
|
2763
3337
|
bindDiscoveryContext(project, cwd, tsconfigPath, loaded);
|
|
2764
3338
|
clearMixinTypeProject();
|
|
2765
|
-
|
|
3339
|
+
const routes = extractAllRoutes(project);
|
|
3340
|
+
clearSchemaTypeProject(project);
|
|
3341
|
+
return routes;
|
|
2766
3342
|
}
|
|
2767
3343
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
2768
3344
|
return tsconfig ? (0, import_node_path5.resolve)(tsconfig) : (0, import_node_path5.join)(cwd, "tsconfig.json");
|
|
@@ -2774,25 +3350,25 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
2774
3350
|
};
|
|
2775
3351
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
2776
3352
|
if (!(0, import_node_fs.existsSync)(tsconfigPath)) return { options: null, files: [] };
|
|
2777
|
-
const read =
|
|
3353
|
+
const read = import_ts_morph12.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
|
|
2778
3354
|
if (read.error) {
|
|
2779
3355
|
return {
|
|
2780
3356
|
options: null,
|
|
2781
3357
|
files: [tsconfigPath],
|
|
2782
|
-
error:
|
|
3358
|
+
error: import_ts_morph12.ts.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
2783
3359
|
};
|
|
2784
3360
|
}
|
|
2785
3361
|
const host = {
|
|
2786
|
-
useCaseSensitiveFileNames:
|
|
3362
|
+
useCaseSensitiveFileNames: import_ts_morph12.ts.sys.useCaseSensitiveFileNames,
|
|
2787
3363
|
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
2788
3364
|
readDirectory: () => [],
|
|
2789
|
-
fileExists: (path) =>
|
|
3365
|
+
fileExists: (path) => import_ts_morph12.ts.sys.fileExists(path),
|
|
2790
3366
|
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
2791
3367
|
// in a base tsconfig keeps working.
|
|
2792
|
-
readFile: (path) =>
|
|
3368
|
+
readFile: (path) => import_ts_morph12.ts.sys.readFile(path)
|
|
2793
3369
|
};
|
|
2794
|
-
const sourceFile =
|
|
2795
|
-
const parsed =
|
|
3370
|
+
const sourceFile = import_ts_morph12.ts.readJsonConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
|
|
3371
|
+
const parsed = import_ts_morph12.ts.parseJsonSourceFileConfigFileContent(
|
|
2796
3372
|
sourceFile,
|
|
2797
3373
|
host,
|
|
2798
3374
|
(0, import_node_path5.dirname)(tsconfigPath),
|
|
@@ -2815,7 +3391,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
|
|
|
2815
3391
|
`[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.`
|
|
2816
3392
|
);
|
|
2817
3393
|
}
|
|
2818
|
-
return new
|
|
3394
|
+
return new import_ts_morph12.Project({
|
|
2819
3395
|
compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
|
|
2820
3396
|
skipAddingFilesFromTsConfig: true,
|
|
2821
3397
|
skipLoadingLibFiles: true,
|
|
@@ -2927,15 +3503,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
2927
3503
|
clearTypeResolutionCaches(this.project);
|
|
2928
3504
|
clearEnumCache(this.project);
|
|
2929
3505
|
clearMixinTypeProject();
|
|
3506
|
+
clearSchemaTypeProject(this.project);
|
|
2930
3507
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
2931
3508
|
}
|
|
2932
3509
|
};
|
|
2933
3510
|
function decoratorStringArg(decoratorExpr) {
|
|
2934
3511
|
if (!decoratorExpr) return void 0;
|
|
2935
|
-
if (
|
|
2936
|
-
if (
|
|
3512
|
+
if (import_ts_morph12.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3513
|
+
if (import_ts_morph12.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
2937
3514
|
const first = decoratorExpr.getElements()[0];
|
|
2938
|
-
if (first &&
|
|
3515
|
+
if (first && import_ts_morph12.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
2939
3516
|
}
|
|
2940
3517
|
return void 0;
|
|
2941
3518
|
}
|
|
@@ -3056,9 +3633,9 @@ function extractContractRoute(args) {
|
|
|
3056
3633
|
let contractDef = null;
|
|
3057
3634
|
let bodyZodRef = null;
|
|
3058
3635
|
let queryZodRef = null;
|
|
3059
|
-
if (
|
|
3060
|
-
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
3061
|
-
} else if (
|
|
3636
|
+
if (import_ts_morph12.Node.isCallExpression(firstDecoratorArg)) {
|
|
3637
|
+
contractDef = parseDefineContractCall(firstDecoratorArg, project);
|
|
3638
|
+
} else if (import_ts_morph12.Node.isIdentifier(firstDecoratorArg)) {
|
|
3062
3639
|
const identName = firstDecoratorArg.getText();
|
|
3063
3640
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
3064
3641
|
if (!resolvedVar) {
|
|
@@ -3070,7 +3647,7 @@ function extractContractRoute(args) {
|
|
|
3070
3647
|
const { decl: varDecl, file: declFile } = resolvedVar;
|
|
3071
3648
|
const initializer = varDecl.getInitializer();
|
|
3072
3649
|
if (!initializer) return null;
|
|
3073
|
-
contractDef = parseDefineContractCall(initializer);
|
|
3650
|
+
contractDef = parseDefineContractCall(initializer, project);
|
|
3074
3651
|
if (contractDef && varDecl.isExported()) {
|
|
3075
3652
|
const filePath = declFile.getFilePath();
|
|
3076
3653
|
if (contractDef.body !== null) {
|
|
@@ -3141,6 +3718,7 @@ function extractDtoRoute(args) {
|
|
|
3141
3718
|
body: dtoContract?.body ?? null,
|
|
3142
3719
|
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
3143
3720
|
error: dtoContract?.error ?? null,
|
|
3721
|
+
paramTypes: dtoContract?.paramTypes ?? null,
|
|
3144
3722
|
queryRef: dtoContract?.queryRef ?? null,
|
|
3145
3723
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
3146
3724
|
responseRef: dtoContract?.responseRef ?? null,
|
|
@@ -3263,7 +3841,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
|
|
|
3263
3841
|
},
|
|
3264
3842
|
project() {
|
|
3265
3843
|
if (!project) {
|
|
3266
|
-
project = new
|
|
3844
|
+
project = new import_ts_morph13.Project({
|
|
3267
3845
|
skipAddingFilesFromTsConfig: true,
|
|
3268
3846
|
skipLoadingLibFiles: true,
|
|
3269
3847
|
skipFileDependencyResolution: true,
|
|
@@ -3381,10 +3959,11 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
3381
3959
|
insertIntoTree(branch.children, rest, leaf, fullName);
|
|
3382
3960
|
}
|
|
3383
3961
|
}
|
|
3384
|
-
function buildParamsType(params) {
|
|
3962
|
+
function buildParamsType(params, paramTypes) {
|
|
3385
3963
|
const pathParams = params.filter((p) => p.source === "path");
|
|
3386
3964
|
if (pathParams.length === 0) return "never";
|
|
3387
|
-
|
|
3965
|
+
const typeOf = (name) => paramTypes && Object.hasOwn(paramTypes, name) && paramTypes[name] || "string";
|
|
3966
|
+
return `{ ${pathParams.map((p) => `${p.name}: ${typeOf(p.name)}`).join("; ")} }`;
|
|
3388
3967
|
}
|
|
3389
3968
|
function hasPathParams(params) {
|
|
3390
3969
|
return params.some((p) => p.source === "path");
|
|
@@ -3478,7 +4057,7 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
|
3478
4057
|
}
|
|
3479
4058
|
const response = buildResponseType(c, outDir, serialization);
|
|
3480
4059
|
const error = buildErrorType(c);
|
|
3481
|
-
const params = buildParamsType(c.params);
|
|
4060
|
+
const params = buildParamsType(c.params, c.contractSource.paramTypes);
|
|
3482
4061
|
const safeMethod = JSON.stringify(method);
|
|
3483
4062
|
const safeUrl = JSON.stringify(c.path);
|
|
3484
4063
|
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
@@ -3747,6 +4326,9 @@ var EMPTY_PATH_NAMESPACE = [
|
|
|
3747
4326
|
"}",
|
|
3748
4327
|
""
|
|
3749
4328
|
];
|
|
4329
|
+
function isClientCallable(route) {
|
|
4330
|
+
return route.method.toUpperCase() !== "ALL";
|
|
4331
|
+
}
|
|
3750
4332
|
function buildApiFile(routes, outDir, opts = {}) {
|
|
3751
4333
|
const fetcherImportPath = opts.fetcherImportPath;
|
|
3752
4334
|
const serialization = opts.serialization ?? "json";
|
|
@@ -3754,7 +4336,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
3754
4336
|
const { layer } = resolveApiSlots(extensions);
|
|
3755
4337
|
const memberExts = extensions.filter((e) => e.apiMembers);
|
|
3756
4338
|
const headerExts = extensions.filter((e) => e.apiHeader);
|
|
3757
|
-
const contracted = routes.filter((r) => r.contract);
|
|
4339
|
+
const contracted = routes.filter((r) => r.contract && isClientCallable(r));
|
|
3758
4340
|
const ctx = opts.ctx ?? {
|
|
3759
4341
|
cwd: outDir ?? "",
|
|
3760
4342
|
outDir: outDir ?? "",
|
|
@@ -4675,6 +5257,7 @@ function buildOpenApiSpec(routes, opts = {}) {
|
|
|
4675
5257
|
const paths = {};
|
|
4676
5258
|
for (const route of routes) {
|
|
4677
5259
|
if (!route.contract) continue;
|
|
5260
|
+
if (route.method.toUpperCase() === "ALL") continue;
|
|
4678
5261
|
const oaPath = toOpenApiPath(route.path);
|
|
4679
5262
|
const method = route.method.toLowerCase();
|
|
4680
5263
|
let pathItem = paths[oaPath];
|
|
@@ -5390,7 +5973,7 @@ function toObjectKey2(name) {
|
|
|
5390
5973
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
5391
5974
|
}
|
|
5392
5975
|
function createChainModuleRenderer(opts) {
|
|
5393
|
-
const { render, recursiveAnnotation } = opts;
|
|
5976
|
+
const { render: render2, recursiveAnnotation } = opts;
|
|
5394
5977
|
return (mod) => {
|
|
5395
5978
|
const ctx = { named: mod.named };
|
|
5396
5979
|
const recursive = mod.recursive ?? /* @__PURE__ */ new Set();
|
|
@@ -5399,7 +5982,7 @@ function createChainModuleRenderer(opts) {
|
|
|
5399
5982
|
const namedTypeAliases = /* @__PURE__ */ new Map();
|
|
5400
5983
|
const namedAnnotations = /* @__PURE__ */ new Map();
|
|
5401
5984
|
for (const [name, node] of mod.named) {
|
|
5402
|
-
namedNestedSchemas.set(name,
|
|
5985
|
+
namedNestedSchemas.set(name, render2(node, ctx));
|
|
5403
5986
|
if (recursive.has(name)) {
|
|
5404
5987
|
const typeName = typeNameFor(name);
|
|
5405
5988
|
namedTypeAliases.set(name, `type ${typeName} = ${renderTsType(node, tctx)}`);
|
|
@@ -5407,7 +5990,7 @@ function createChainModuleRenderer(opts) {
|
|
|
5407
5990
|
}
|
|
5408
5991
|
}
|
|
5409
5992
|
return {
|
|
5410
|
-
schemaText:
|
|
5993
|
+
schemaText: render2(mod.root, ctx),
|
|
5411
5994
|
namedNestedSchemas,
|
|
5412
5995
|
namedTypeAliases,
|
|
5413
5996
|
namedAnnotations,
|
|
@@ -5417,7 +6000,7 @@ function createChainModuleRenderer(opts) {
|
|
|
5417
6000
|
}
|
|
5418
6001
|
|
|
5419
6002
|
// src/index.ts
|
|
5420
|
-
var VERSION = "0.
|
|
6003
|
+
var VERSION = "0.26.0";
|
|
5421
6004
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5422
6005
|
0 && (module.exports = {
|
|
5423
6006
|
CodegenError,
|