@dudousxd/nestjs-codegen 0.28.0 → 0.29.1

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.
@@ -215,13 +215,52 @@ var import_node_path18 = require("path");
215
215
  var import_fast_glob3 = __toESM(require("fast-glob"), 1);
216
216
 
217
217
  // src/discovery/contracts-fast.ts
218
- var import_node_fs = require("fs");
218
+ var import_node_fs2 = require("fs");
219
219
  var import_node_path3 = require("path");
220
220
  var import_fast_glob = __toESM(require("fast-glob"), 1);
221
- var import_ts_morph11 = require("ts-morph");
221
+ var import_ts_morph12 = require("ts-morph");
222
+
223
+ // src/discovery/disk-fingerprints.ts
224
+ var import_node_fs = require("fs");
225
+ var DiskFingerprints = class {
226
+ stats = /* @__PURE__ */ new Map();
227
+ /** The paths whose disk content differs from `text`, or that no longer exist. */
228
+ changed(files) {
229
+ const changed = [];
230
+ for (const { path, text } of files) {
231
+ let key;
232
+ try {
233
+ const stat2 = (0, import_node_fs.statSync)(path);
234
+ key = `${stat2.mtimeMs}:${stat2.size}`;
235
+ } catch {
236
+ this.stats.delete(path);
237
+ changed.push(path);
238
+ continue;
239
+ }
240
+ if (this.stats.get(path) === key) continue;
241
+ let disk;
242
+ try {
243
+ disk = (0, import_node_fs.readFileSync)(path, "utf-8");
244
+ } catch {
245
+ disk = void 0;
246
+ }
247
+ if (disk === text) {
248
+ this.stats.set(path, key);
249
+ } else {
250
+ this.stats.delete(path);
251
+ changed.push(path);
252
+ }
253
+ }
254
+ return changed;
255
+ }
256
+ /** Forget `path`, so its next check compares content again. */
257
+ forget(path) {
258
+ this.stats.delete(path);
259
+ }
260
+ };
222
261
 
223
262
  // src/discovery/dto-type-resolver.ts
224
- var import_ts_morph10 = require("ts-morph");
263
+ var import_ts_morph11 = require("ts-morph");
225
264
 
226
265
  // src/discovery/dto-to-ir.ts
227
266
  var import_ts_morph2 = require("ts-morph");
@@ -982,7 +1021,7 @@ function inSchemaFromDecorator(decorator) {
982
1021
  }
983
1022
 
984
1023
  // src/discovery/filter-for.ts
985
- var import_ts_morph5 = require("ts-morph");
1024
+ var import_ts_morph6 = require("ts-morph");
986
1025
 
987
1026
  // src/discovery/filter-field-types.ts
988
1027
  var import_ts_morph3 = require("ts-morph");
@@ -1222,23 +1261,66 @@ function toFilterFieldType(name, r) {
1222
1261
  }
1223
1262
 
1224
1263
  // src/discovery/heritage.ts
1264
+ var import_ts_morph5 = require("ts-morph");
1265
+
1266
+ // src/discovery/unresolved-imports.ts
1225
1267
  var import_ts_morph4 = require("ts-morph");
1268
+ var UnresolvedImports = class {
1269
+ read;
1270
+ /**
1271
+ * The files with an import that failed to resolve in `program` and now resolves
1272
+ * to a TypeScript file the program does not hold.
1273
+ */
1274
+ nowResolving(program) {
1275
+ if (this.read?.program !== program) {
1276
+ this.read = { program, imports: failedImports(program) };
1277
+ }
1278
+ const options = program.getCompilerOptions();
1279
+ const files = /* @__PURE__ */ new Set();
1280
+ for (const { specifier, from } of this.read.imports) {
1281
+ const file = import_ts_morph4.ts.resolveModuleName(specifier, from, options, import_ts_morph4.ts.sys).resolvedModule?.resolvedFileName;
1282
+ if (file !== void 0 && /\.(m|c)?tsx?$/.test(file) && !program.getSourceFile(file)) {
1283
+ files.add(from);
1284
+ }
1285
+ }
1286
+ return [...files];
1287
+ }
1288
+ };
1289
+ function failedImports(program) {
1290
+ const checker = program.getTypeChecker();
1291
+ const imports = [];
1292
+ for (const file of program.getSourceFiles()) {
1293
+ if (program.isSourceFileDefaultLibrary(file) || file.isDeclarationFile) continue;
1294
+ if (program.isSourceFileFromExternalLibrary(file)) continue;
1295
+ for (const statement of file.statements) {
1296
+ if (!import_ts_morph4.ts.isImportDeclaration(statement) && !import_ts_morph4.ts.isExportDeclaration(statement)) continue;
1297
+ const specifier = statement.moduleSpecifier;
1298
+ if (!specifier || !import_ts_morph4.ts.isStringLiteral(specifier)) continue;
1299
+ if (!checker.getSymbolAtLocation(specifier)) {
1300
+ imports.push({ specifier: specifier.text, from: file.fileName });
1301
+ }
1302
+ }
1303
+ }
1304
+ return imports;
1305
+ }
1306
+
1307
+ // src/discovery/heritage.ts
1226
1308
  function resolveHeritageCall(node) {
1227
1309
  if (!node) return void 0;
1228
1310
  const expr = unwrapExpression(node);
1229
- if (import_ts_morph4.Node.isCallExpression(expr)) return expr;
1230
- if (!import_ts_morph4.Node.isIdentifier(expr)) return void 0;
1311
+ if (import_ts_morph5.Node.isCallExpression(expr)) return expr;
1312
+ if (!import_ts_morph5.Node.isIdentifier(expr)) return void 0;
1231
1313
  const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find(
1232
- (n) => n !== void 0 && import_ts_morph4.Node.isVariableDeclaration(n)
1314
+ (n) => n !== void 0 && import_ts_morph5.Node.isVariableDeclaration(n)
1233
1315
  );
1234
1316
  const init = decl?.getInitializer();
1235
1317
  if (!init) return void 0;
1236
1318
  const unwrapped = unwrapExpression(init);
1237
- return import_ts_morph4.Node.isCallExpression(unwrapped) ? unwrapped : void 0;
1319
+ return import_ts_morph5.Node.isCallExpression(unwrapped) ? unwrapped : void 0;
1238
1320
  }
1239
1321
  function unwrapExpression(node) {
1240
1322
  let current = node;
1241
- while (import_ts_morph4.Node.isAsExpression(current) || import_ts_morph4.Node.isSatisfiesExpression(current) || import_ts_morph4.Node.isParenthesizedExpression(current) || import_ts_morph4.Node.isTypeAssertion(current)) {
1323
+ while (import_ts_morph5.Node.isAsExpression(current) || import_ts_morph5.Node.isSatisfiesExpression(current) || import_ts_morph5.Node.isParenthesizedExpression(current) || import_ts_morph5.Node.isTypeAssertion(current)) {
1242
1324
  current = current.getExpression();
1243
1325
  }
1244
1326
  return current;
@@ -1246,17 +1328,17 @@ function unwrapExpression(node) {
1246
1328
  function resolveReturnedClass(factoryDecl) {
1247
1329
  const body = factoryDecl.getBody();
1248
1330
  if (!body) return void 0;
1249
- const returns = body.getDescendants().filter((n) => import_ts_morph4.Node.isReturnStatement(n));
1331
+ const returns = body.getDescendants().filter((n) => import_ts_morph5.Node.isReturnStatement(n));
1250
1332
  for (const ret of returns) {
1251
1333
  const expr = ret.getExpression();
1252
1334
  if (!expr) continue;
1253
1335
  const inner = unwrapExpression(expr);
1254
- if (import_ts_morph4.Node.isClassExpression(inner)) {
1336
+ if (import_ts_morph5.Node.isClassExpression(inner)) {
1255
1337
  return inner;
1256
1338
  }
1257
- if (import_ts_morph4.Node.isIdentifier(inner)) {
1339
+ if (import_ts_morph5.Node.isIdentifier(inner)) {
1258
1340
  const name = inner.getText();
1259
- const decl = body.getDescendants().find((n) => import_ts_morph4.Node.isClassDeclaration(n) && n.getName() === name);
1341
+ const decl = body.getDescendants().find((n) => import_ts_morph5.Node.isClassDeclaration(n) && n.getName() === name);
1260
1342
  if (decl) return decl;
1261
1343
  }
1262
1344
  }
@@ -1264,10 +1346,10 @@ function resolveReturnedClass(factoryDecl) {
1264
1346
  }
1265
1347
  function factoryCalleeName(call) {
1266
1348
  const callee = unwrapExpression(call.getExpression());
1267
- if (import_ts_morph4.Node.isIdentifier(callee)) return callee;
1268
- if (import_ts_morph4.Node.isPropertyAccessExpression(callee)) {
1349
+ if (import_ts_morph5.Node.isIdentifier(callee)) return callee;
1350
+ if (import_ts_morph5.Node.isPropertyAccessExpression(callee)) {
1269
1351
  const name = callee.getNameNode();
1270
- return import_ts_morph4.Node.isIdentifier(name) ? name : void 0;
1352
+ return import_ts_morph5.Node.isIdentifier(name) ? name : void 0;
1271
1353
  }
1272
1354
  return void 0;
1273
1355
  }
@@ -1280,19 +1362,19 @@ function resolveFactoryFromName(name, seen) {
1280
1362
  seen.add(name);
1281
1363
  const decls = name.getDefinitions().map((d) => d.getDeclarationNode()).filter((n) => n !== void 0);
1282
1364
  for (const decl of decls) {
1283
- if (import_ts_morph4.Node.isFunctionDeclaration(decl) || import_ts_morph4.Node.isMethodDeclaration(decl)) return decl;
1284
- if (import_ts_morph4.Node.isPropertyAssignment(decl)) {
1365
+ if (import_ts_morph5.Node.isFunctionDeclaration(decl) || import_ts_morph5.Node.isMethodDeclaration(decl)) return decl;
1366
+ if (import_ts_morph5.Node.isPropertyAssignment(decl)) {
1285
1367
  const init = decl.getInitializer();
1286
1368
  const inner = init ? unwrapExpression(init) : void 0;
1287
- if (inner && import_ts_morph4.Node.isIdentifier(inner)) {
1369
+ if (inner && import_ts_morph5.Node.isIdentifier(inner)) {
1288
1370
  const resolved = resolveFactoryFromName(inner, seen);
1289
1371
  if (resolved) return resolved;
1290
1372
  }
1291
1373
  continue;
1292
1374
  }
1293
- if (import_ts_morph4.Node.isShorthandPropertyAssignment(decl)) {
1375
+ if (import_ts_morph5.Node.isShorthandPropertyAssignment(decl)) {
1294
1376
  const nameNode = decl.getNameNode();
1295
- const resolved = import_ts_morph4.Node.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
1377
+ const resolved = import_ts_morph5.Node.isIdentifier(nameNode) ? resolveFactoryFromName(nameNode, seen) : void 0;
1296
1378
  if (resolved) return resolved;
1297
1379
  }
1298
1380
  }
@@ -1304,7 +1386,7 @@ function warnUnresolvedFactory(cls, calleeText, reason) {
1304
1386
  );
1305
1387
  }
1306
1388
  function resolveFactoryStaticClass(node) {
1307
- if (!import_ts_morph4.Node.isPropertyAccessExpression(node)) return void 0;
1389
+ if (!import_ts_morph5.Node.isPropertyAccessExpression(node)) return void 0;
1308
1390
  const call = resolveHeritageCall(node.getExpression());
1309
1391
  if (!call) return void 0;
1310
1392
  const factoryDecl = resolveFactoryDeclaration(call);
@@ -1312,13 +1394,13 @@ function resolveFactoryStaticClass(node) {
1312
1394
  const returnedClass = resolveReturnedClass(factoryDecl);
1313
1395
  if (!returnedClass) return void 0;
1314
1396
  const staticProp = returnedClass.getStaticProperties().find((p) => p.getName() === node.getName());
1315
- if (!staticProp || !import_ts_morph4.Node.isPropertyDeclaration(staticProp)) return void 0;
1397
+ if (!staticProp || !import_ts_morph5.Node.isPropertyDeclaration(staticProp)) return void 0;
1316
1398
  const init = staticProp.getInitializer();
1317
1399
  if (!init) return void 0;
1318
1400
  const inner = unwrapExpression(init);
1319
- if (import_ts_morph4.Node.isClassExpression(inner)) return inner;
1320
- if (!import_ts_morph4.Node.isIdentifier(inner)) return void 0;
1321
- return inner.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph4.Node.isClassDeclaration(n));
1401
+ if (import_ts_morph5.Node.isClassExpression(inner)) return inner;
1402
+ if (!import_ts_morph5.Node.isIdentifier(inner)) return void 0;
1403
+ return inner.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph5.Node.isClassDeclaration(n));
1322
1404
  }
1323
1405
  function resolveBaseClass(cls) {
1324
1406
  const heritage = cls.getExtends()?.getExpression();
@@ -1329,8 +1411,8 @@ function resolveBaseClass(cls) {
1329
1411
  return factoryDecl ? resolveReturnedClass(factoryDecl) : void 0;
1330
1412
  }
1331
1413
  const expr = unwrapExpression(heritage);
1332
- if (!import_ts_morph4.Node.isIdentifier(expr)) return void 0;
1333
- return expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph4.Node.isClassDeclaration(n));
1414
+ if (!import_ts_morph5.Node.isIdentifier(expr)) return void 0;
1415
+ return expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph5.Node.isClassDeclaration(n));
1334
1416
  }
1335
1417
  function collectMethodsThroughChain(returnedClass) {
1336
1418
  const byName = /* @__PURE__ */ new Map();
@@ -1377,7 +1459,7 @@ function resolveInheritedMethods(cls) {
1377
1459
  classArgs.push(positional);
1378
1460
  continue;
1379
1461
  }
1380
- if (!import_ts_morph4.Node.isObjectLiteralExpression(arg)) continue;
1462
+ if (!import_ts_morph5.Node.isObjectLiteralExpression(arg)) continue;
1381
1463
  for (const prop of arg.getProperties()) {
1382
1464
  const named = resolvePropertyClassReference(prop);
1383
1465
  if (!named) continue;
@@ -1399,8 +1481,8 @@ function resolveInheritedMethods(cls) {
1399
1481
  }
1400
1482
  function resolveClassReference(node) {
1401
1483
  const expr = unwrapExpression(node);
1402
- if (!import_ts_morph4.Node.isIdentifier(expr)) return void 0;
1403
- const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph4.Node.isClassDeclaration(n));
1484
+ if (!import_ts_morph5.Node.isIdentifier(expr)) return void 0;
1485
+ const decl = expr.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph5.Node.isClassDeclaration(n));
1404
1486
  if (!decl) return void 0;
1405
1487
  return {
1406
1488
  name: decl.getName() ?? expr.getText(),
@@ -1408,29 +1490,56 @@ function resolveClassReference(node) {
1408
1490
  };
1409
1491
  }
1410
1492
  function resolvePropertyClassReference(prop) {
1411
- if (import_ts_morph4.Node.isShorthandPropertyAssignment(prop)) {
1493
+ if (import_ts_morph5.Node.isShorthandPropertyAssignment(prop)) {
1412
1494
  const ref2 = resolveClassReference(prop.getNameNode());
1413
1495
  return ref2 ? { key: prop.getName(), ref: ref2 } : void 0;
1414
1496
  }
1415
- if (!import_ts_morph4.Node.isPropertyAssignment(prop)) return void 0;
1497
+ if (!import_ts_morph5.Node.isPropertyAssignment(prop)) return void 0;
1416
1498
  const init = prop.getInitializer();
1417
1499
  if (!init) return void 0;
1418
1500
  const ref = resolveClassReference(init);
1419
1501
  if (!ref) return void 0;
1420
1502
  const nameNode = prop.getNameNode();
1421
- const key = import_ts_morph4.Node.isStringLiteral(nameNode) ? nameNode.getLiteralValue() : prop.getName();
1503
+ const key = import_ts_morph5.Node.isStringLiteral(nameNode) ? nameNode.getLiteralValue() : prop.getName();
1422
1504
  return { key, ref };
1423
1505
  }
1424
1506
  var mixinTypeProject;
1507
+ var mixinFiles = new DiskFingerprints();
1508
+ var mixinStale = false;
1509
+ var mixinUnresolved = new UnresolvedImports();
1425
1510
  function getMixinTypeProject() {
1426
- mixinTypeProject ??= new import_ts_morph4.Project({
1427
- skipAddingFilesFromTsConfig: true,
1428
- compilerOptions: { strict: true }
1429
- });
1511
+ if (mixinTypeProject && mixinStale && !refreshMixinTypeProject(mixinTypeProject)) {
1512
+ mixinTypeProject = void 0;
1513
+ }
1514
+ mixinStale = false;
1515
+ if (!mixinTypeProject) {
1516
+ mixinTypeProject = new import_ts_morph5.Project({
1517
+ skipAddingFilesFromTsConfig: true,
1518
+ compilerOptions: { strict: true }
1519
+ });
1520
+ mixinFiles = new DiskFingerprints();
1521
+ mixinUnresolved = new UnresolvedImports();
1522
+ }
1430
1523
  return mixinTypeProject;
1431
1524
  }
1432
1525
  function clearMixinTypeProject() {
1433
1526
  mixinTypeProject = void 0;
1527
+ mixinStale = false;
1528
+ }
1529
+ function invalidateMixinTypeProject() {
1530
+ mixinStale = true;
1531
+ }
1532
+ function refreshMixinTypeProject(project) {
1533
+ const program = project.getProgram().compilerObject;
1534
+ const files = program.getSourceFiles().filter((file) => !program.isSourceFileDefaultLibrary(file)).map((file) => ({ path: file.fileName, text: file.text }));
1535
+ const changed = mixinFiles.changed(files);
1536
+ if (changed.length === 0) return mixinUnresolved.nowResolving(program).length === 0;
1537
+ for (const path of changed) {
1538
+ const sf = project.getSourceFile(path);
1539
+ if (sf?.refreshFromFileSystemSync() !== import_ts_morph5.FileSystemRefreshResult.Updated) return false;
1540
+ }
1541
+ project.resolveSourceFileDependencies();
1542
+ return true;
1434
1543
  }
1435
1544
  function resolveInstantiatedReturnType(cls, methodName) {
1436
1545
  const filePath = cls.getSourceFile().getFilePath();
@@ -1461,7 +1570,7 @@ function resolveClassRef(ref, project) {
1461
1570
  return file?.getClass(ref.name);
1462
1571
  }
1463
1572
  function classifyFilterForHint(typeInit) {
1464
- if (import_ts_morph5.Node.isStringLiteral(typeInit)) {
1573
+ if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
1465
1574
  switch (typeInit.getLiteralValue()) {
1466
1575
  case "string":
1467
1576
  return { kind: "string" };
@@ -1475,10 +1584,10 @@ function classifyFilterForHint(typeInit) {
1475
1584
  return null;
1476
1585
  }
1477
1586
  }
1478
- if (import_ts_morph5.Node.isArrayLiteralExpression(typeInit)) {
1587
+ if (import_ts_morph6.Node.isArrayLiteralExpression(typeInit)) {
1479
1588
  const values = [];
1480
1589
  for (const el of typeInit.getElements()) {
1481
- if (!import_ts_morph5.Node.isStringLiteral(el)) return null;
1590
+ if (!import_ts_morph6.Node.isStringLiteral(el)) return null;
1482
1591
  values.push(el.getLiteralValue());
1483
1592
  }
1484
1593
  if (values.length === 0) return null;
@@ -1513,11 +1622,11 @@ function extractFilterForHints(classDecl, project) {
1513
1622
  if (!filterForDec) continue;
1514
1623
  const args = filterForDec.getArguments();
1515
1624
  const keyArg = args[0];
1516
- const inputKey = keyArg && import_ts_morph5.Node.isStringLiteral(keyArg) ? keyArg.getLiteralValue() : method.getName();
1625
+ const inputKey = keyArg && import_ts_morph6.Node.isStringLiteral(keyArg) ? keyArg.getLiteralValue() : method.getName();
1517
1626
  const optsArg = args[1];
1518
- if (optsArg && import_ts_morph5.Node.isObjectLiteralExpression(optsArg)) {
1627
+ if (optsArg && import_ts_morph6.Node.isObjectLiteralExpression(optsArg)) {
1519
1628
  const typeProp = optsArg.getProperty("type");
1520
- if (typeProp && import_ts_morph5.Node.isPropertyAssignment(typeProp)) {
1629
+ if (typeProp && import_ts_morph6.Node.isPropertyAssignment(typeProp)) {
1521
1630
  const typeInit = typeProp.getInitializer();
1522
1631
  if (typeInit) {
1523
1632
  const classified = classifyFilterForHint(typeInit);
@@ -1544,14 +1653,14 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin, controllerCl
1544
1653
  const filterClassArg = args[0];
1545
1654
  if (!filterClassArg) continue;
1546
1655
  const factoryStaticClass = resolveFactoryStaticClass(filterClassArg);
1547
- if (!factoryStaticClass && !import_ts_morph5.Node.isIdentifier(filterClassArg)) continue;
1656
+ if (!factoryStaticClass && !import_ts_morph6.Node.isIdentifier(filterClassArg)) continue;
1548
1657
  let source = "query";
1549
1658
  const optionsArg = args[1];
1550
- if (optionsArg && import_ts_morph5.Node.isObjectLiteralExpression(optionsArg)) {
1659
+ if (optionsArg && import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) {
1551
1660
  const sourceProp = optionsArg.getProperty("source");
1552
- if (sourceProp && import_ts_morph5.Node.isPropertyAssignment(sourceProp)) {
1661
+ if (sourceProp && import_ts_morph6.Node.isPropertyAssignment(sourceProp)) {
1553
1662
  const init = sourceProp.getInitializer();
1554
- if (init && import_ts_morph5.Node.isStringLiteral(init) && init.getLiteralValue() === "body") {
1663
+ if (init && import_ts_morph6.Node.isStringLiteral(init) && init.getLiteralValue() === "body") {
1555
1664
  source = "body";
1556
1665
  }
1557
1666
  }
@@ -1560,7 +1669,7 @@ function extractApplyFilterInfo(method, sourceFile, project, mixin, controllerCl
1560
1669
  const resolved = factoryStaticClass ? void 0 : findType(filterClassName, declFile, project);
1561
1670
  const declaredClass = factoryStaticClass ?? (resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg));
1562
1671
  const ownRoute = !mixin || (controllerClass ? method.getParent() === controllerClass : declFile.getFilePath() !== mixin.factoryFilePath);
1563
- const namedByRoute = ownRoute && import_ts_morph5.Node.isIdentifier(filterClassArg) ? declaredClass : void 0;
1672
+ const namedByRoute = ownRoute && import_ts_morph6.Node.isIdentifier(filterClassArg) ? declaredClass : void 0;
1564
1673
  for (const candidate of orderFilterCandidates({
1565
1674
  namedByRoute,
1566
1675
  supplied: resolveMixinFilterClass(mixin, project),
@@ -1638,22 +1747,22 @@ function resolveRelationEntity(prop, sourceFile, project) {
1638
1747
  const args = dec.getArguments();
1639
1748
  if (args.length === 0) continue;
1640
1749
  const arg = args[0];
1641
- if (import_ts_morph5.Node.isObjectLiteralExpression(arg)) {
1750
+ if (import_ts_morph6.Node.isObjectLiteralExpression(arg)) {
1642
1751
  const entityProp = arg.getProperty("entity");
1643
- if (entityProp && import_ts_morph5.Node.isPropertyAssignment(entityProp)) {
1752
+ if (entityProp && import_ts_morph6.Node.isPropertyAssignment(entityProp)) {
1644
1753
  const init = entityProp.getInitializer();
1645
- if (init && import_ts_morph5.Node.isArrowFunction(init)) {
1754
+ if (init && import_ts_morph6.Node.isArrowFunction(init)) {
1646
1755
  const body = init.getBody();
1647
- if (import_ts_morph5.Node.isIdentifier(body)) {
1756
+ if (import_ts_morph6.Node.isIdentifier(body)) {
1648
1757
  const resolved = findType(body.getText(), prop.getSourceFile(), project);
1649
1758
  if (resolved?.kind === "class") return resolved.decl;
1650
1759
  }
1651
1760
  }
1652
1761
  }
1653
1762
  }
1654
- if (import_ts_morph5.Node.isArrowFunction(arg)) {
1763
+ if (import_ts_morph6.Node.isArrowFunction(arg)) {
1655
1764
  const body = arg.getBody();
1656
- if (import_ts_morph5.Node.isIdentifier(body)) {
1765
+ if (import_ts_morph6.Node.isIdentifier(body)) {
1657
1766
  const resolved = findType(body.getText(), prop.getSourceFile(), project);
1658
1767
  if (resolved?.kind === "class") return resolved.decl;
1659
1768
  }
@@ -1672,36 +1781,36 @@ function extractClassPropertyTypes(classDecl, project) {
1672
1781
  return fields;
1673
1782
  }
1674
1783
  function resolveLocalClassDeclaration(identifier) {
1675
- if (!import_ts_morph5.Node.isIdentifier(identifier)) return void 0;
1676
- return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph5.Node.isClassDeclaration(n));
1784
+ if (!import_ts_morph6.Node.isIdentifier(identifier)) return void 0;
1785
+ return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
1677
1786
  }
1678
1787
  function resolveDeclaredEntity(optionsArg, filterClass, project) {
1679
- if (!import_ts_morph5.Node.isObjectLiteralExpression(optionsArg)) return void 0;
1788
+ if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
1680
1789
  const entityProp = optionsArg.getProperty("entity");
1681
- if (!entityProp || !import_ts_morph5.Node.isPropertyAssignment(entityProp)) return void 0;
1790
+ if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return void 0;
1682
1791
  const entityInit = entityProp.getInitializer();
1683
- if (!entityInit || !import_ts_morph5.Node.isIdentifier(entityInit)) return void 0;
1792
+ if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return void 0;
1684
1793
  const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
1685
1794
  if (!resolved || resolved.kind !== "class") return void 0;
1686
1795
  return resolved.decl;
1687
1796
  }
1688
1797
  function readFieldNameList(optionsArg, key) {
1689
- if (!import_ts_morph5.Node.isObjectLiteralExpression(optionsArg)) return void 0;
1798
+ if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
1690
1799
  const prop = optionsArg.getProperty(key);
1691
- if (!prop || !import_ts_morph5.Node.isPropertyAssignment(prop)) return void 0;
1800
+ if (!prop || !import_ts_morph6.Node.isPropertyAssignment(prop)) return void 0;
1692
1801
  const init = prop.getInitializer();
1693
- if (!init || !import_ts_morph5.Node.isArrayLiteralExpression(init)) return void 0;
1802
+ if (!init || !import_ts_morph6.Node.isArrayLiteralExpression(init)) return void 0;
1694
1803
  const names = /* @__PURE__ */ new Set();
1695
1804
  for (const el of init.getElements()) {
1696
- if (import_ts_morph5.Node.isStringLiteral(el)) {
1805
+ if (import_ts_morph6.Node.isStringLiteral(el)) {
1697
1806
  names.add(el.getLiteralValue());
1698
1807
  continue;
1699
1808
  }
1700
- if (import_ts_morph5.Node.isObjectLiteralExpression(el)) {
1809
+ if (import_ts_morph6.Node.isObjectLiteralExpression(el)) {
1701
1810
  const fieldProp = el.getProperty("field");
1702
- if (fieldProp && import_ts_morph5.Node.isPropertyAssignment(fieldProp)) {
1811
+ if (fieldProp && import_ts_morph6.Node.isPropertyAssignment(fieldProp)) {
1703
1812
  const fieldInit = fieldProp.getInitializer();
1704
- if (fieldInit && import_ts_morph5.Node.isStringLiteral(fieldInit)) {
1813
+ if (fieldInit && import_ts_morph6.Node.isStringLiteral(fieldInit)) {
1705
1814
  names.add(fieldInit.getLiteralValue());
1706
1815
  continue;
1707
1816
  }
@@ -1725,7 +1834,7 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
1725
1834
  const args = filterableDecorator.getArguments();
1726
1835
  if (args.length === 0) return [];
1727
1836
  const optionsArg = args[0];
1728
- if (!import_ts_morph5.Node.isObjectLiteralExpression(optionsArg)) return [];
1837
+ if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
1729
1838
  const declaredEntity = resolveDeclaredEntity(optionsArg, filterClass, project);
1730
1839
  const entityDecl = preferDeclaredEntity ? declaredEntity ?? entityOverride : entityOverride ?? declaredEntity;
1731
1840
  if (!entityDecl) return [];
@@ -1736,17 +1845,17 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
1736
1845
  const relationsDecorator = filterClass.getDecorators().find((d) => d.getName() === "Relations");
1737
1846
  if (relationsDecorator) {
1738
1847
  const relArgs = relationsDecorator.getArguments();
1739
- if (relArgs.length > 0 && import_ts_morph5.Node.isObjectLiteralExpression(relArgs[0])) {
1848
+ if (relArgs.length > 0 && import_ts_morph6.Node.isObjectLiteralExpression(relArgs[0])) {
1740
1849
  for (const relProp of relArgs[0].getProperties()) {
1741
- if (!import_ts_morph5.Node.isPropertyAssignment(relProp)) continue;
1850
+ if (!import_ts_morph6.Node.isPropertyAssignment(relProp)) continue;
1742
1851
  const relInit = relProp.getInitializer();
1743
- if (!relInit || !import_ts_morph5.Node.isObjectLiteralExpression(relInit)) continue;
1852
+ if (!relInit || !import_ts_morph6.Node.isObjectLiteralExpression(relInit)) continue;
1744
1853
  const keysProp = relInit.getProperty("keys");
1745
- if (!keysProp || !import_ts_morph5.Node.isPropertyAssignment(keysProp)) continue;
1854
+ if (!keysProp || !import_ts_morph6.Node.isPropertyAssignment(keysProp)) continue;
1746
1855
  const keysInit = keysProp.getInitializer();
1747
- if (!keysInit || !import_ts_morph5.Node.isArrayLiteralExpression(keysInit)) continue;
1856
+ if (!keysInit || !import_ts_morph6.Node.isArrayLiteralExpression(keysInit)) continue;
1748
1857
  for (const el of keysInit.getElements()) {
1749
- if (import_ts_morph5.Node.isStringLiteral(el)) {
1858
+ if (import_ts_morph6.Node.isStringLiteral(el)) {
1750
1859
  fields.push(toFilterFieldType(el.getLiteralValue(), { kind: "unknown" }));
1751
1860
  }
1752
1861
  }
@@ -1757,13 +1866,13 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
1757
1866
  }
1758
1867
 
1759
1868
  // src/discovery/pipe-schema.ts
1760
- var import_ts_morph9 = require("ts-morph");
1869
+ var import_ts_morph10 = require("ts-morph");
1761
1870
 
1762
1871
  // src/discovery/schema-type-inference.ts
1763
- var import_ts_morph7 = require("ts-morph");
1872
+ var import_ts_morph8 = require("ts-morph");
1764
1873
 
1765
1874
  // src/discovery/schema-type-program.ts
1766
- var import_ts_morph6 = require("ts-morph");
1875
+ var import_ts_morph7 = require("ts-morph");
1767
1876
  var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
1768
1877
  var SchemaTypeProgram = class {
1769
1878
  constructor(discovery) {
@@ -1773,10 +1882,10 @@ var SchemaTypeProgram = class {
1773
1882
  ...base,
1774
1883
  // A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
1775
1884
  // `Promise` for the schema libraries' declarations to lean on.
1776
- target: base.target ?? import_ts_morph6.ts.ScriptTarget.ES2022,
1885
+ target: base.target ?? import_ts_morph7.ts.ScriptTarget.ES2022,
1777
1886
  // With neither set, resolve the way a bundler does: package `exports`
1778
1887
  // (`zod/v4`) and extensionless or `.js` relative imports all resolve.
1779
- ...base.module === void 0 && base.moduleResolution === void 0 ? { module: import_ts_morph6.ts.ModuleKind.ESNext, moduleResolution: import_ts_morph6.ts.ModuleResolutionKind.Bundler } : {},
1888
+ ...base.module === void 0 && base.moduleResolution === void 0 ? { module: import_ts_morph7.ts.ModuleKind.ESNext, moduleResolution: import_ts_morph7.ts.ModuleResolutionKind.Bundler } : {},
1780
1889
  // Without it `.nullable()` and `.optional()` would erase to the bare type.
1781
1890
  strictNullChecks: true,
1782
1891
  // No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
@@ -1784,17 +1893,16 @@ var SchemaTypeProgram = class {
1784
1893
  types: [],
1785
1894
  noEmit: true
1786
1895
  };
1787
- this.project = new import_ts_morph6.Project({
1896
+ this.project = new import_ts_morph7.Project({
1788
1897
  skipAddingFilesFromTsConfig: true,
1789
1898
  compilerOptions: this.options
1790
1899
  });
1791
- for (const sf of discovery.getSourceFiles()) this.request(sf.getFilePath(), { schema: true });
1792
- this.flush();
1900
+ this.requestDiscoveryFiles();
1793
1901
  }
1794
1902
  project;
1795
1903
  options;
1796
1904
  /** Parses a file that is not in discovery's Project, without adding it there. */
1797
- scratch = new import_ts_morph6.Project({
1905
+ scratch = new import_ts_morph7.Project({
1798
1906
  skipAddingFilesFromTsConfig: true,
1799
1907
  skipLoadingLibFiles: true,
1800
1908
  skipFileDependencyResolution: true
@@ -1802,6 +1910,53 @@ var SchemaTypeProgram = class {
1802
1910
  needs = /* @__PURE__ */ new Map();
1803
1911
  /** Pruned text per file, as last written into {@link project}. */
1804
1912
  written = /* @__PURE__ */ new Map();
1913
+ /** What {@link project} read from disk unpruned (packages, declaration files). */
1914
+ externalFiles = new DiskFingerprints();
1915
+ scratchFiles = new DiskFingerprints();
1916
+ /** The last pruning of each file, with the source text and needs it was pruned for. */
1917
+ pruned = /* @__PURE__ */ new Map();
1918
+ /**
1919
+ * Bring the program up to date with discovery's files and the disk, keeping the
1920
+ * lib and package declarations it has already parsed and checked. Leaves it
1921
+ * holding exactly the files a new program built now would hold, with the same
1922
+ * text. Returns false when a file the program read unpruned has changed on disk
1923
+ * (a package was reinstalled, a `.d.ts` was edited): the caller builds a new
1924
+ * program instead.
1925
+ */
1926
+ refresh() {
1927
+ const program = this.project.getProgram().compilerObject;
1928
+ const external = program.getSourceFiles().filter(
1929
+ (file) => !program.isSourceFileDefaultLibrary(file) && !this.written.has(file.fileName)
1930
+ ).map((file) => ({ path: file.fileName, text: file.text }));
1931
+ if (this.externalFiles.changed(external).length > 0) return false;
1932
+ const scratch = this.scratch.getSourceFiles().map((sf) => ({ path: sf.getFilePath(), text: sf.getFullText() }));
1933
+ for (const path of this.scratchFiles.changed(scratch)) {
1934
+ const sf = this.scratch.getSourceFile(path);
1935
+ if (sf?.refreshFromFileSystemSync() === import_ts_morph7.FileSystemRefreshResult.Deleted) {
1936
+ this.scratchFiles.forget(path);
1937
+ }
1938
+ }
1939
+ this.needs.clear();
1940
+ this.requestDiscoveryFiles();
1941
+ for (const path of [...this.written.keys()]) {
1942
+ if (this.needs.has(path)) continue;
1943
+ const sf = this.project.getSourceFile(path);
1944
+ if (sf) this.project.removeSourceFile(sf);
1945
+ this.written.delete(path);
1946
+ }
1947
+ return true;
1948
+ }
1949
+ /**
1950
+ * Everything discovery holds now (the controllers) goes in before the first
1951
+ * query: a file added afterwards discards the program and its checker, and
1952
+ * rebuilding them once per controller is exactly the cost this avoids.
1953
+ */
1954
+ requestDiscoveryFiles() {
1955
+ for (const sf of this.discovery.getSourceFiles()) {
1956
+ this.request(sf.getFilePath(), { schema: true });
1957
+ }
1958
+ this.flush();
1959
+ }
1805
1960
  /**
1806
1961
  * The typed copy of a discovery file, with its schema positions kept. Adding one
1807
1962
  * that was not there yet (a contract file discovery reached lazily) rebuilds the
@@ -1842,12 +1997,21 @@ var SchemaTypeProgram = class {
1842
1997
  while (this.queue.length > 0) {
1843
1998
  const path = this.queue.pop();
1844
1999
  const original = this.original(path);
1845
- if (!original) continue;
1846
- const needs = this.needs.get(path);
1847
- const text = this.prune(original, needs);
2000
+ if (!original) {
2001
+ if (this.written.has(path)) changed.set(path, void 0);
2002
+ continue;
2003
+ }
2004
+ const { text, requests } = this.pruneCached(path, original, this.needs.get(path));
2005
+ for (const { path: target, want } of requests) this.request(target, want);
1848
2006
  if (this.written.get(path) !== text) changed.set(path, text);
1849
2007
  }
1850
2008
  for (const [path, text] of changed) {
2009
+ if (text === void 0) {
2010
+ const sf = this.project.getSourceFile(path);
2011
+ if (sf) this.project.removeSourceFile(sf);
2012
+ this.written.delete(path);
2013
+ continue;
2014
+ }
1851
2015
  this.project.createSourceFile(path, text, { overwrite: true });
1852
2016
  this.written.set(path, text);
1853
2017
  }
@@ -1856,11 +2020,29 @@ var SchemaTypeProgram = class {
1856
2020
  original(path) {
1857
2021
  return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
1858
2022
  }
2023
+ /** {@link prune}, reusing the last result while the file's text and needs are the same. */
2024
+ pruneCached(path, sf, needs) {
2025
+ const source = sf.getFullText();
2026
+ const key = `${needs.schema}:${needs.all}:${[...needs.names].sort().join(",")}`;
2027
+ const last = this.pruned.get(path);
2028
+ if (last && last.source === source && last.needs === key) return last;
2029
+ const result = this.prune(sf, needs);
2030
+ if (result.resolved) this.pruned.set(path, { ...result, source, needs: key });
2031
+ else this.pruned.delete(path);
2032
+ return result;
2033
+ }
1859
2034
  /**
1860
2035
  * `sf`'s text with every import/re-export nothing in `needs` references blanked,
1861
- * requesting from each kept one's target the names it brings in.
2036
+ * and the names each kept one brings in from its target.
1862
2037
  */
1863
2038
  prune(sf, needs) {
2039
+ const requests = [];
2040
+ let resolved = true;
2041
+ const resolveFrom = (specifier) => {
2042
+ const target = this.resolve(specifier, sf);
2043
+ if (target === null) resolved = false;
2044
+ return target ?? void 0;
2045
+ };
1864
2046
  const locals = topLevelDeclarations(sf);
1865
2047
  const roots = [];
1866
2048
  if (needs.all) roots.push(...sf.getStatements());
@@ -1894,7 +2076,7 @@ var SchemaTypeProgram = class {
1894
2076
  const seen = new Set(queue);
1895
2077
  while (queue.length > 0) {
1896
2078
  const node = queue.pop();
1897
- const ids = import_ts_morph6.Node.isIdentifier(node) ? [node] : node.getDescendantsOfKind(import_ts_morph6.ts.SyntaxKind.Identifier);
2079
+ const ids = import_ts_morph7.Node.isIdentifier(node) ? [node] : node.getDescendantsOfKind(import_ts_morph7.ts.SyntaxKind.Identifier);
1898
2080
  for (const id of ids) {
1899
2081
  const name = id.getText();
1900
2082
  if (referenced.has(name)) continue;
@@ -1913,7 +2095,7 @@ var SchemaTypeProgram = class {
1913
2095
  }
1914
2096
  };
1915
2097
  for (const decl of sf.getImportDeclarations()) {
1916
- const target = this.resolve(decl.getModuleSpecifierValue(), sf);
2098
+ const target = resolveFrom(decl.getModuleSpecifierValue());
1917
2099
  const namespace = decl.getNamespaceImport()?.getText();
1918
2100
  const def = decl.getDefaultImport()?.getText();
1919
2101
  const named = decl.getNamedImports().map((n) => ({
@@ -1927,21 +2109,24 @@ var SchemaTypeProgram = class {
1927
2109
  continue;
1928
2110
  }
1929
2111
  if (target) {
1930
- this.request(target, {
1931
- all: wantsNamespace,
1932
- names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
2112
+ requests.push({
2113
+ path: target,
2114
+ want: {
2115
+ all: wantsNamespace,
2116
+ names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
2117
+ }
1933
2118
  });
1934
2119
  }
1935
2120
  }
1936
2121
  for (const exp of sf.getExportDeclarations()) {
1937
2122
  const spec = exp.getModuleSpecifierValue();
1938
2123
  if (spec === void 0) continue;
1939
- const target = this.resolve(spec, sf);
2124
+ const target = resolveFrom(spec);
1940
2125
  if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
1941
2126
  if (!needs.all && unresolvedNames.length === 0) {
1942
2127
  blank(exp);
1943
2128
  } else if (target) {
1944
- this.request(target, { all: needs.all, names: unresolvedNames });
2129
+ requests.push({ path: target, want: { all: needs.all, names: unresolvedNames } });
1945
2130
  }
1946
2131
  continue;
1947
2132
  }
@@ -1949,22 +2134,24 @@ var SchemaTypeProgram = class {
1949
2134
  (n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
1950
2135
  ).map((n) => n.getNameNode().getText());
1951
2136
  if (wanted.length === 0) blank(exp);
1952
- else if (target) this.request(target, { names: wanted });
2137
+ else if (target) requests.push({ path: target, want: { names: wanted } });
1953
2138
  }
1954
- return chars.join("");
2139
+ return { text: chars.join(""), requests, resolved };
1955
2140
  }
1956
2141
  /**
1957
- * The project source file a module specifier resolves to, or undefined for a
1958
- * package (read unpruned by the program itself) or an unresolvable one.
2142
+ * The project source file a module specifier resolves to; undefined for a
2143
+ * package or a declaration file (read unpruned by the program itself), null for
2144
+ * one that does not resolve.
1959
2145
  */
1960
2146
  resolve(specifier, from) {
1961
- const resolved = import_ts_morph6.ts.resolveModuleName(
2147
+ const resolved = import_ts_morph7.ts.resolveModuleName(
1962
2148
  specifier,
1963
2149
  from.getFilePath(),
1964
2150
  this.options,
1965
- import_ts_morph6.ts.sys
2151
+ import_ts_morph7.ts.sys
1966
2152
  ).resolvedModule;
1967
- if (!resolved || resolved.isExternalLibraryImport) return void 0;
2153
+ if (!resolved) return null;
2154
+ if (resolved.isExternalLibraryImport) return void 0;
1968
2155
  const file = resolved.resolvedFileName;
1969
2156
  if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
1970
2157
  return file;
@@ -1973,20 +2160,20 @@ var SchemaTypeProgram = class {
1973
2160
  function schemaPositions(sf) {
1974
2161
  const roots = [];
1975
2162
  sf.forEachDescendant((node) => {
1976
- if (import_ts_morph6.Node.isDecorator(node)) {
2163
+ if (import_ts_morph7.Node.isDecorator(node)) {
1977
2164
  const name = node.getName();
1978
2165
  if (name === "ApplyContract") {
1979
2166
  roots.push(...node.getArguments());
1980
2167
  } else if (PARAM_DECORATORS.has(name)) {
1981
2168
  for (const arg of node.getArguments()) {
1982
- if (import_ts_morph6.Node.isNewExpression(arg) || import_ts_morph6.Node.isCallExpression(arg)) {
2169
+ if (import_ts_morph7.Node.isNewExpression(arg) || import_ts_morph7.Node.isCallExpression(arg)) {
1983
2170
  roots.push(...arg.getArguments());
1984
2171
  }
1985
2172
  }
1986
2173
  }
1987
- } else if (import_ts_morph6.Node.isCallExpression(node)) {
2174
+ } else if (import_ts_morph7.Node.isCallExpression(node)) {
1988
2175
  const callee = node.getExpression();
1989
- const name = import_ts_morph6.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph6.Node.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
2176
+ const name = import_ts_morph7.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph7.Node.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
1990
2177
  if (name === "defineContract") roots.push(...node.getArguments());
1991
2178
  }
1992
2179
  });
@@ -2019,8 +2206,13 @@ function defaultExport(sf) {
2019
2206
 
2020
2207
  // src/discovery/schema-type-inference.ts
2021
2208
  var programs = /* @__PURE__ */ new WeakMap();
2209
+ var stale = /* @__PURE__ */ new WeakSet();
2022
2210
  function programFor(discovery) {
2023
2211
  let program = programs.get(discovery);
2212
+ if (program && stale.has(discovery)) {
2213
+ stale.delete(discovery);
2214
+ if (!program.refresh()) program = void 0;
2215
+ }
2024
2216
  if (!program) {
2025
2217
  program = new SchemaTypeProgram(discovery);
2026
2218
  programs.set(discovery, program);
@@ -2029,6 +2221,10 @@ function programFor(discovery) {
2029
2221
  }
2030
2222
  function clearSchemaTypeProject(discovery) {
2031
2223
  programs.delete(discovery);
2224
+ stale.delete(discovery);
2225
+ }
2226
+ function invalidateSchemaTypeProject(discovery) {
2227
+ if (programs.has(discovery)) stale.add(discovery);
2032
2228
  }
2033
2229
  function counterpart(typedSf, node) {
2034
2230
  const start = node.getStart();
@@ -2092,7 +2288,7 @@ function objectKey(name) {
2092
2288
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
2093
2289
  }
2094
2290
  function isOptionalSymbol(sym) {
2095
- return (sym.getFlags() & import_ts_morph7.ts.SymbolFlags.Optional) !== 0;
2291
+ return (sym.getFlags() & import_ts_morph8.ts.SymbolFlags.Optional) !== 0;
2096
2292
  }
2097
2293
  function isSymbolKeyed(sym) {
2098
2294
  return sym.getName().startsWith("__@");
@@ -2253,11 +2449,11 @@ function acceptsUndefined(types) {
2253
2449
  }
2254
2450
 
2255
2451
  // src/discovery/zod-ast-to-ts.ts
2256
- var import_ts_morph8 = require("ts-morph");
2452
+ var import_ts_morph9 = require("ts-morph");
2257
2453
  function zodAstToTs(node) {
2258
- if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
2454
+ if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
2259
2455
  const expr = node.getExpression();
2260
- if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
2456
+ if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
2261
2457
  const methodName = expr.getName();
2262
2458
  const receiver = expr.getExpression();
2263
2459
  if (methodName === "optional") {
@@ -2281,17 +2477,17 @@ function zodAstToTs(node) {
2281
2477
  case "literal": {
2282
2478
  const lit = args[0];
2283
2479
  if (!lit) return "unknown";
2284
- if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
2285
- if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
2286
- if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
2287
- if (lit.getKind() === import_ts_morph8.SyntaxKind.FalseKeyword) return "false";
2480
+ if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
2481
+ if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
2482
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
2483
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
2288
2484
  return "unknown";
2289
2485
  }
2290
2486
  case "enum": {
2291
2487
  const arrArg = args[0];
2292
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2488
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2293
2489
  const members = arrArg.getElements().map(
2294
- (el) => import_ts_morph8.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
2490
+ (el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
2295
2491
  );
2296
2492
  return members.join(" | ");
2297
2493
  }
@@ -2302,10 +2498,10 @@ function zodAstToTs(node) {
2302
2498
  }
2303
2499
  case "object": {
2304
2500
  const objArg = args[0];
2305
- if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
2501
+ if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
2306
2502
  const lines = [];
2307
2503
  for (const prop of objArg.getProperties()) {
2308
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
2504
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
2309
2505
  const key = prop.getName();
2310
2506
  const valNode = prop.getInitializer();
2311
2507
  if (!valNode) continue;
@@ -2317,7 +2513,7 @@ function zodAstToTs(node) {
2317
2513
  }
2318
2514
  case "union": {
2319
2515
  const arrArg = args[0];
2320
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2516
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2321
2517
  return arrArg.getElements().map(zodAstToTs).join(" | ");
2322
2518
  }
2323
2519
  case "record": {
@@ -2327,7 +2523,7 @@ function zodAstToTs(node) {
2327
2523
  }
2328
2524
  case "tuple": {
2329
2525
  const arrArg = args[0];
2330
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2526
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2331
2527
  return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
2332
2528
  }
2333
2529
  default:
@@ -2337,9 +2533,9 @@ function zodAstToTs(node) {
2337
2533
  return "unknown";
2338
2534
  }
2339
2535
  function isOptionalChain(node) {
2340
- if (!import_ts_morph8.Node.isCallExpression(node)) return false;
2536
+ if (!import_ts_morph9.Node.isCallExpression(node)) return false;
2341
2537
  const expr = node.getExpression();
2342
- return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
2538
+ return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
2343
2539
  }
2344
2540
  function memberType(val, position, project) {
2345
2541
  if (project) {
@@ -2351,13 +2547,13 @@ function memberType(val, position, project) {
2351
2547
  return zodAstToTs(val);
2352
2548
  }
2353
2549
  function parseDefineContractCall(callExpr, project) {
2354
- if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
2550
+ if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
2355
2551
  const callee = callExpr.getExpression();
2356
- const calleeName = import_ts_morph8.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph8.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
2552
+ const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
2357
2553
  if (calleeName !== "defineContract") return null;
2358
2554
  const args = callExpr.getArguments();
2359
2555
  const optsArg = args[0];
2360
- if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
2556
+ if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
2361
2557
  let query = null;
2362
2558
  let body = null;
2363
2559
  let response = "unknown";
@@ -2365,7 +2561,7 @@ function parseDefineContractCall(callExpr, project) {
2365
2561
  let bodyZodText = null;
2366
2562
  let queryZodText = null;
2367
2563
  for (const prop of optsArg.getProperties()) {
2368
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
2564
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
2369
2565
  const propName = prop.getName();
2370
2566
  const val = prop.getInitializer();
2371
2567
  if (!val) continue;
@@ -2387,7 +2583,7 @@ function parseDefineContractCall(callExpr, project) {
2387
2583
  // src/discovery/pipe-schema.ts
2388
2584
  function pipeSchemaOf(decorator, project) {
2389
2585
  for (const pipe of decorator.getArguments()) {
2390
- if (!import_ts_morph9.Node.isNewExpression(pipe) && !import_ts_morph9.Node.isCallExpression(pipe)) continue;
2586
+ if (!import_ts_morph10.Node.isNewExpression(pipe) && !import_ts_morph10.Node.isCallExpression(pipe)) continue;
2391
2587
  for (const arg of pipe.getArguments()) {
2392
2588
  if (!couldBeSchema(arg)) continue;
2393
2589
  const inferred = inferSchema(arg, project);
@@ -2409,15 +2605,15 @@ function pipeSchemaOf(decorator, project) {
2409
2605
  return null;
2410
2606
  }
2411
2607
  function couldBeSchema(arg) {
2412
- return import_ts_morph9.Node.isIdentifier(arg) || import_ts_morph9.Node.isPropertyAccessExpression(arg) || import_ts_morph9.Node.isCallExpression(arg) || import_ts_morph9.Node.isAsExpression(arg) || import_ts_morph9.Node.isSatisfiesExpression(arg) || import_ts_morph9.Node.isParenthesizedExpression(arg);
2608
+ 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);
2413
2609
  }
2414
2610
  function syntacticZodType(arg, project) {
2415
2611
  let expr = arg;
2416
- if (import_ts_morph9.Node.isIdentifier(arg)) {
2612
+ if (import_ts_morph10.Node.isIdentifier(arg)) {
2417
2613
  const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
2418
2614
  expr = resolved?.decl.getInitializer();
2419
2615
  }
2420
- if (!expr || !import_ts_morph9.Node.isCallExpression(expr)) return null;
2616
+ if (!expr || !import_ts_morph10.Node.isCallExpression(expr)) return null;
2421
2617
  const type = zodAstToTs(expr);
2422
2618
  return type === "unknown" ? null : type;
2423
2619
  }
@@ -2453,22 +2649,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
2453
2649
  ]);
2454
2650
  function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
2455
2651
  if (depth <= 0) return "unknown";
2456
- if (import_ts_morph10.Node.isArrayTypeNode(typeNode)) {
2652
+ if (import_ts_morph11.Node.isArrayTypeNode(typeNode)) {
2457
2653
  const elementType = typeNode.getElementTypeNode();
2458
2654
  return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
2459
2655
  }
2460
- if (import_ts_morph10.Node.isUnionTypeNode(typeNode)) {
2656
+ if (import_ts_morph11.Node.isUnionTypeNode(typeNode)) {
2461
2657
  return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
2462
2658
  }
2463
- if (import_ts_morph10.Node.isIntersectionTypeNode(typeNode)) {
2659
+ if (import_ts_morph11.Node.isIntersectionTypeNode(typeNode)) {
2464
2660
  return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
2465
2661
  }
2466
- if (import_ts_morph10.Node.isParenthesizedTypeNode(typeNode)) {
2662
+ if (import_ts_morph11.Node.isParenthesizedTypeNode(typeNode)) {
2467
2663
  return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
2468
2664
  }
2469
- if (import_ts_morph10.Node.isTypeReference(typeNode)) {
2665
+ if (import_ts_morph11.Node.isTypeReference(typeNode)) {
2470
2666
  const typeName = typeNode.getTypeName();
2471
- const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
2667
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
2472
2668
  const bound = subst.get(name);
2473
2669
  if (bound !== void 0) return bound;
2474
2670
  if (name === "string" || name === "number" || name === "boolean") return name;
@@ -2491,10 +2687,10 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
2491
2687
  dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
2492
2688
  return "unknown";
2493
2689
  }
2494
- if (import_ts_morph10.Node.isTypeLiteral(typeNode)) {
2690
+ if (import_ts_morph11.Node.isTypeLiteral(typeNode)) {
2495
2691
  const members = [];
2496
2692
  for (const member of typeNode.getMembers()) {
2497
- if (import_ts_morph10.Node.isPropertySignature(member)) {
2693
+ if (import_ts_morph11.Node.isPropertySignature(member)) {
2498
2694
  const memberTypeNode = member.getTypeNode();
2499
2695
  const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
2500
2696
  members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
@@ -2505,11 +2701,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
2505
2701
  return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
2506
2702
  }
2507
2703
  const kind = typeNode.getKind();
2508
- if (kind === import_ts_morph10.SyntaxKind.StringKeyword) return "string";
2509
- if (kind === import_ts_morph10.SyntaxKind.NumberKeyword) return "number";
2510
- if (kind === import_ts_morph10.SyntaxKind.BooleanKeyword) return "boolean";
2511
- if (kind === import_ts_morph10.SyntaxKind.UnknownKeyword) return "unknown";
2512
- if (kind === import_ts_morph10.SyntaxKind.AnyKeyword) return "unknown";
2704
+ if (kind === import_ts_morph11.SyntaxKind.StringKeyword) return "string";
2705
+ if (kind === import_ts_morph11.SyntaxKind.NumberKeyword) return "number";
2706
+ if (kind === import_ts_morph11.SyntaxKind.BooleanKeyword) return "boolean";
2707
+ if (kind === import_ts_morph11.SyntaxKind.UnknownKeyword) return "unknown";
2708
+ if (kind === import_ts_morph11.SyntaxKind.AnyKeyword) return "unknown";
2513
2709
  return typeNode.getText();
2514
2710
  }
2515
2711
  function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
@@ -2567,7 +2763,7 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
2567
2763
  }
2568
2764
  function namedField(decorator) {
2569
2765
  const first = decorator.getArguments()[0];
2570
- return first && import_ts_morph10.Node.isStringLiteral(first) ? first.getLiteralValue() : null;
2766
+ return first && import_ts_morph11.Node.isStringLiteral(first) ? first.getLiteralValue() : null;
2571
2767
  }
2572
2768
  function extractBodyType(method, sourceFile, project) {
2573
2769
  for (const param of method.getParameters()) {
@@ -2617,8 +2813,8 @@ function extractQueryType(method, sourceFile, project) {
2617
2813
  function isParamOptional(param) {
2618
2814
  if (param.hasQuestionToken() || param.hasInitializer()) return true;
2619
2815
  const typeNode = param.getTypeNode();
2620
- if (typeNode && import_ts_morph10.Node.isUnionTypeNode(typeNode)) {
2621
- return typeNode.getTypeNodes().some((t) => t.getKind() === import_ts_morph10.SyntaxKind.UndefinedKeyword);
2816
+ if (typeNode && import_ts_morph11.Node.isUnionTypeNode(typeNode)) {
2817
+ return typeNode.getTypeNodes().some((t) => t.getKind() === import_ts_morph11.SyntaxKind.UndefinedKeyword);
2622
2818
  }
2623
2819
  return false;
2624
2820
  }
@@ -2630,7 +2826,7 @@ function extractParamsType(method, sourceFile, project) {
2630
2826
  const paramArgs = paramDecorator.getArguments();
2631
2827
  if (paramArgs.length === 0) continue;
2632
2828
  const nameArg = paramArgs[0];
2633
- if (!import_ts_morph10.Node.isStringLiteral(nameArg)) continue;
2829
+ if (!import_ts_morph11.Node.isStringLiteral(nameArg)) continue;
2634
2830
  const paramName = nameArg.getLiteralValue();
2635
2831
  const typeNode = param.getTypeNode();
2636
2832
  const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
@@ -2667,28 +2863,28 @@ function extractUploadedFiles(method) {
2667
2863
  for (const decorator of method.getDecorators()) {
2668
2864
  if (decorator.getName() !== "UseInterceptors") continue;
2669
2865
  for (const arg of decorator.getArguments()) {
2670
- if (!import_ts_morph10.Node.isCallExpression(arg)) continue;
2866
+ if (!import_ts_morph11.Node.isCallExpression(arg)) continue;
2671
2867
  const interceptor = arg.getExpression().getText();
2672
2868
  const callArgs = arg.getArguments();
2673
2869
  const firstArg2 = callArgs[0];
2674
2870
  if (interceptor === "FileInterceptor") {
2675
- if (firstArg2 && import_ts_morph10.Node.isStringLiteral(firstArg2)) {
2871
+ if (firstArg2 && import_ts_morph11.Node.isStringLiteral(firstArg2)) {
2676
2872
  entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
2677
2873
  multipart = true;
2678
2874
  }
2679
2875
  } else if (interceptor === "FilesInterceptor") {
2680
- if (firstArg2 && import_ts_morph10.Node.isStringLiteral(firstArg2)) {
2876
+ if (firstArg2 && import_ts_morph11.Node.isStringLiteral(firstArg2)) {
2681
2877
  entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
2682
2878
  multipart = true;
2683
2879
  }
2684
2880
  } else if (interceptor === "FileFieldsInterceptor") {
2685
- if (firstArg2 && import_ts_morph10.Node.isArrayLiteralExpression(firstArg2)) {
2881
+ if (firstArg2 && import_ts_morph11.Node.isArrayLiteralExpression(firstArg2)) {
2686
2882
  for (const el of firstArg2.getElements()) {
2687
- if (!import_ts_morph10.Node.isObjectLiteralExpression(el)) continue;
2883
+ if (!import_ts_morph11.Node.isObjectLiteralExpression(el)) continue;
2688
2884
  const nameProp = el.getProperty("name");
2689
- if (nameProp && import_ts_morph10.Node.isPropertyAssignment(nameProp)) {
2885
+ if (nameProp && import_ts_morph11.Node.isPropertyAssignment(nameProp)) {
2690
2886
  const init = nameProp.getInitializer();
2691
- if (init && import_ts_morph10.Node.isStringLiteral(init)) {
2887
+ if (init && import_ts_morph11.Node.isStringLiteral(init)) {
2692
2888
  entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
2693
2889
  }
2694
2890
  }
@@ -2708,13 +2904,13 @@ function extractResponseType(method, sourceFile, project) {
2708
2904
  if (apiResponseDecorator) {
2709
2905
  const args = apiResponseDecorator.getArguments();
2710
2906
  const optsArg = args[0];
2711
- if (optsArg && import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) {
2907
+ if (optsArg && import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) {
2712
2908
  for (const prop of optsArg.getProperties()) {
2713
- if (!import_ts_morph10.Node.isPropertyAssignment(prop)) continue;
2909
+ if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
2714
2910
  if (prop.getName() !== "type") continue;
2715
2911
  const val = prop.getInitializer();
2716
2912
  if (!val) continue;
2717
- if (import_ts_morph10.Node.isArrayLiteralExpression(val)) {
2913
+ if (import_ts_morph11.Node.isArrayLiteralExpression(val)) {
2718
2914
  const elements = val.getElements();
2719
2915
  const firstEl = elements[0];
2720
2916
  if (elements.length > 0 && firstEl !== void 0) {
@@ -2735,24 +2931,24 @@ function extractResponseType(method, sourceFile, project) {
2735
2931
  }
2736
2932
  function apiResponseStatus(decorator) {
2737
2933
  const optsArg = decorator.getArguments()[0];
2738
- if (!optsArg || !import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) return null;
2934
+ if (!optsArg || !import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) return null;
2739
2935
  for (const prop of optsArg.getProperties()) {
2740
- if (!import_ts_morph10.Node.isPropertyAssignment(prop)) continue;
2936
+ if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
2741
2937
  if (prop.getName() !== "status") continue;
2742
2938
  const val = prop.getInitializer();
2743
- if (val && import_ts_morph10.Node.isNumericLiteral(val)) return Number(val.getLiteralValue());
2939
+ if (val && import_ts_morph11.Node.isNumericLiteral(val)) return Number(val.getLiteralValue());
2744
2940
  }
2745
2941
  return null;
2746
2942
  }
2747
2943
  function apiResponseTypeNode(decorator) {
2748
2944
  const optsArg = decorator.getArguments()[0];
2749
- if (!optsArg || !import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) return null;
2945
+ if (!optsArg || !import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) return null;
2750
2946
  for (const prop of optsArg.getProperties()) {
2751
- if (!import_ts_morph10.Node.isPropertyAssignment(prop)) continue;
2947
+ if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
2752
2948
  if (prop.getName() !== "type") continue;
2753
2949
  const val = prop.getInitializer();
2754
2950
  if (!val) return null;
2755
- if (import_ts_morph10.Node.isArrayLiteralExpression(val)) {
2951
+ if (import_ts_morph11.Node.isArrayLiteralExpression(val)) {
2756
2952
  const first = val.getElements()[0];
2757
2953
  return first ? { node: first, isArray: true } : null;
2758
2954
  }
@@ -2770,7 +2966,7 @@ function extractErrorType(method, sourceFile, project) {
2770
2966
  const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
2771
2967
  const type = typeInfo.isArray ? `Array<${inner}>` : inner;
2772
2968
  let ref = null;
2773
- if (import_ts_morph10.Node.isIdentifier(typeInfo.node)) {
2969
+ if (import_ts_morph11.Node.isIdentifier(typeInfo.node)) {
2774
2970
  const name = typeInfo.node.getText();
2775
2971
  const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
2776
2972
  if (localDecl?.isExported()) {
@@ -2787,7 +2983,7 @@ function extractErrorType(method, sourceFile, project) {
2787
2983
  return null;
2788
2984
  }
2789
2985
  function resolveIdentifierToClassType(node, sourceFile, project, depth) {
2790
- if (!import_ts_morph10.Node.isIdentifier(node)) return "unknown";
2986
+ if (!import_ts_morph11.Node.isIdentifier(node)) return "unknown";
2791
2987
  const name = node.getText();
2792
2988
  const resolved = findType(name, sourceFile, project);
2793
2989
  if (resolved) {
@@ -2807,9 +3003,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
2807
3003
  var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
2808
3004
  function detectBinaryResponse(method) {
2809
3005
  const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
2810
- if (!node || !import_ts_morph10.Node.isTypeReference(node)) return false;
3006
+ if (!node || !import_ts_morph11.Node.isTypeReference(node)) return false;
2811
3007
  const typeName = node.getTypeName();
2812
- const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : "";
3008
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
2813
3009
  return BINARY_RESPONSE_TYPES.has(name);
2814
3010
  }
2815
3011
  function detectStreamElement(method) {
@@ -2824,9 +3020,9 @@ function detectStreamElement(method) {
2824
3020
  return null;
2825
3021
  }
2826
3022
  function streamContainerElement(node) {
2827
- if (!node || !import_ts_morph10.Node.isTypeReference(node)) return null;
3023
+ if (!node || !import_ts_morph11.Node.isTypeReference(node)) return null;
2828
3024
  const typeName = node.getTypeName();
2829
- const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : "";
3025
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
2830
3026
  if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
2831
3027
  return node.getTypeArguments()[0] ?? null;
2832
3028
  }
@@ -2836,9 +3032,9 @@ function hasAsQueryDecorator(method) {
2836
3032
  return method.getDecorators().some((d) => d.getName() === "AsQuery");
2837
3033
  }
2838
3034
  function unwrapNamedContainer(node, names) {
2839
- if (!node || !import_ts_morph10.Node.isTypeReference(node)) return node;
3035
+ if (!node || !import_ts_morph11.Node.isTypeReference(node)) return node;
2840
3036
  const typeName = node.getTypeName();
2841
- const name = import_ts_morph10.Node.isIdentifier(typeName) ? typeName.getText() : "";
3037
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
2842
3038
  if (names.has(name)) {
2843
3039
  return node.getTypeArguments()[0] ?? node;
2844
3040
  }
@@ -2885,11 +3081,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
2885
3081
  if (apiResp) {
2886
3082
  const args = apiResp.getArguments();
2887
3083
  const optsArg = args[0];
2888
- if (optsArg && import_ts_morph10.Node.isObjectLiteralExpression(optsArg)) {
3084
+ if (optsArg && import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) {
2889
3085
  for (const prop of optsArg.getProperties()) {
2890
- if (import_ts_morph10.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
3086
+ if (import_ts_morph11.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
2891
3087
  const val = prop.getInitializer();
2892
- if (val && import_ts_morph10.Node.isIdentifier(val)) {
3088
+ if (val && import_ts_morph11.Node.isIdentifier(val)) {
2893
3089
  const name = val.getText();
2894
3090
  const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
2895
3091
  if (localDecl?.isExported()) {
@@ -2969,26 +3165,26 @@ var NO_TSCONFIG_OPTIONS = {
2969
3165
  strict: false
2970
3166
  };
2971
3167
  function loadDiscoveryTsconfig(tsconfigPath) {
2972
- if (!(0, import_node_fs.existsSync)(tsconfigPath)) return { options: null, files: [] };
2973
- const read = import_ts_morph11.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph11.ts.sys.readFile(path));
3168
+ if (!(0, import_node_fs2.existsSync)(tsconfigPath)) return { options: null, files: [] };
3169
+ const read = import_ts_morph12.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
2974
3170
  if (read.error) {
2975
3171
  return {
2976
3172
  options: null,
2977
3173
  files: [tsconfigPath],
2978
- error: import_ts_morph11.ts.flattenDiagnosticMessageText(read.error.messageText, " ")
3174
+ error: import_ts_morph12.ts.flattenDiagnosticMessageText(read.error.messageText, " ")
2979
3175
  };
2980
3176
  }
2981
3177
  const host = {
2982
- useCaseSensitiveFileNames: import_ts_morph11.ts.sys.useCaseSensitiveFileNames,
3178
+ useCaseSensitiveFileNames: import_ts_morph12.ts.sys.useCaseSensitiveFileNames,
2983
3179
  // The one line that keeps a tsconfig from being a filesystem walk.
2984
3180
  readDirectory: () => [],
2985
- fileExists: (path) => import_ts_morph11.ts.sys.fileExists(path),
3181
+ fileExists: (path) => import_ts_morph12.ts.sys.fileExists(path),
2986
3182
  // `extends` is resolved through the host, so a consumer whose `paths` live
2987
3183
  // in a base tsconfig keeps working.
2988
- readFile: (path) => import_ts_morph11.ts.sys.readFile(path)
3184
+ readFile: (path) => import_ts_morph12.ts.sys.readFile(path)
2989
3185
  };
2990
- const sourceFile = import_ts_morph11.ts.readJsonConfigFile(tsconfigPath, (path) => import_ts_morph11.ts.sys.readFile(path));
2991
- const parsed = import_ts_morph11.ts.parseJsonSourceFileConfigFileContent(
3186
+ const sourceFile = import_ts_morph12.ts.readJsonConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
3187
+ const parsed = import_ts_morph12.ts.parseJsonSourceFileConfigFileContent(
2992
3188
  sourceFile,
2993
3189
  host,
2994
3190
  (0, import_node_path3.dirname)(tsconfigPath),
@@ -3011,7 +3207,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
3011
3207
  `[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.`
3012
3208
  );
3013
3209
  }
3014
- return new import_ts_morph11.Project({
3210
+ return new import_ts_morph12.Project({
3015
3211
  compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
3016
3212
  skipAddingFilesFromTsConfig: true,
3017
3213
  skipLoadingLibFiles: true,
@@ -3046,6 +3242,9 @@ var PersistentDiscovery = class _PersistentDiscovery {
3046
3242
  glob;
3047
3243
  /** Absolute paths of the controllers currently loaded as extraction roots. */
3048
3244
  controllerPaths = /* @__PURE__ */ new Set();
3245
+ /** Every file in {@link project}, as last read from disk. */
3246
+ files = new DiskFingerprints();
3247
+ unresolved = new UnresolvedImports();
3049
3248
  constructor(project, cwd, glob) {
3050
3249
  this.project = project;
3051
3250
  this.cwd = cwd;
@@ -3071,6 +3270,8 @@ var PersistentDiscovery = class _PersistentDiscovery {
3071
3270
  }
3072
3271
  /** Run the initial extraction (equivalent to a first `discoverContractsFast`). */
3073
3272
  discover() {
3273
+ clearMixinTypeProject();
3274
+ clearSchemaTypeProject(this.project);
3074
3275
  return this.runExtraction();
3075
3276
  }
3076
3277
  /**
@@ -3078,26 +3279,39 @@ var PersistentDiscovery = class _PersistentDiscovery {
3078
3279
  * from disk (controllers AND any lazily-loaded DTO/imported files), re-globs
3079
3280
  * to pick up added/removed controllers, clears the per-Project caches, then
3080
3281
  * re-extracts. `changedPaths` is a hint; correctness does not depend on it
3081
- * being exhaustive because re-globbing + refresh-on-presence covers the set.
3282
+ * being exhaustive because re-globbing + comparing every loaded file with the
3283
+ * disk covers the set.
3082
3284
  */
3083
- async rediscover(changedPaths) {
3084
- if (changedPaths) {
3085
- for (const p of changedPaths) {
3086
- const abs = (0, import_node_path3.resolve)(p);
3087
- const sf = this.project.getSourceFile(abs);
3088
- if (sf) {
3089
- await sf.refreshFromFileSystem();
3090
- }
3285
+ async rediscover(changedPaths, options = {}) {
3286
+ const hinted = new Set([...changedPaths ?? []].map((p) => (0, import_node_path3.resolve)(p)));
3287
+ const program = this.project.getProgram().compilerObject;
3288
+ const sources = program.getSourceFiles().filter(
3289
+ (file) => !program.isSourceFileDefaultLibrary(file) && !program.isSourceFileFromExternalLibrary(file)
3290
+ ).map((file) => ({ path: file.fileName, text: file.text }));
3291
+ for (const path of /* @__PURE__ */ new Set([...hinted, ...this.files.changed(sources)])) {
3292
+ const sf = this.project.getSourceFile(path);
3293
+ if (sf && await sf.refreshFromFileSystem() === import_ts_morph12.FileSystemRefreshResult.Deleted) {
3294
+ this.files.forget(path);
3091
3295
  }
3092
3296
  }
3297
+ const inProject = new Set(this.project.getSourceFiles().map((sf) => sf.getFilePath()));
3298
+ const definitions = this.project.getLanguageService().compilerObject.getProgram();
3299
+ for (const path of definitions ? this.unresolved.nowResolving(definitions) : []) {
3300
+ const sf = this.project.getSourceFile(path);
3301
+ if (!sf) continue;
3302
+ this.project.removeSourceFile(sf);
3303
+ if (inProject.has(path)) this.project.addSourceFileAtPathIfExists(path);
3304
+ }
3093
3305
  const globbed = new Set(
3094
3306
  await (0, import_fast_glob.default)(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
3095
3307
  );
3308
+ let controllersChanged = false;
3096
3309
  for (const f of globbed) {
3097
3310
  if (!this.controllerPaths.has(f)) {
3098
3311
  try {
3099
3312
  this.project.addSourceFileAtPath(f);
3100
3313
  this.controllerPaths.add(f);
3314
+ controllersChanged = true;
3101
3315
  } catch {
3102
3316
  }
3103
3317
  }
@@ -3107,25 +3321,31 @@ var PersistentDiscovery = class _PersistentDiscovery {
3107
3321
  const sf = this.project.getSourceFile(f);
3108
3322
  if (sf) this.project.removeSourceFile(sf);
3109
3323
  this.controllerPaths.delete(f);
3324
+ controllersChanged = true;
3110
3325
  }
3111
3326
  }
3327
+ if (controllersChanged || options.filesAddedOrRemoved) {
3328
+ clearMixinTypeProject();
3329
+ clearSchemaTypeProject(this.project);
3330
+ } else {
3331
+ invalidateMixinTypeProject();
3332
+ invalidateSchemaTypeProject(this.project);
3333
+ }
3112
3334
  return this.runExtraction();
3113
3335
  }
3114
3336
  /** Clear stale per-Project caches, then extract over the controller set. */
3115
3337
  runExtraction() {
3116
3338
  clearTypeResolutionCaches(this.project);
3117
3339
  clearEnumCache(this.project);
3118
- clearMixinTypeProject();
3119
- clearSchemaTypeProject(this.project);
3120
3340
  return extractRoutesFrom(this.project, this.controllerPaths);
3121
3341
  }
3122
3342
  };
3123
3343
  function decoratorStringArg(decoratorExpr) {
3124
3344
  if (!decoratorExpr) return void 0;
3125
- if (import_ts_morph11.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
3126
- if (import_ts_morph11.Node.isArrayLiteralExpression(decoratorExpr)) {
3345
+ if (import_ts_morph12.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
3346
+ if (import_ts_morph12.Node.isArrayLiteralExpression(decoratorExpr)) {
3127
3347
  const first = decoratorExpr.getElements()[0];
3128
- if (first && import_ts_morph11.Node.isStringLiteral(first)) return first.getLiteralValue();
3348
+ if (first && import_ts_morph12.Node.isStringLiteral(first)) return first.getLiteralValue();
3129
3349
  }
3130
3350
  return void 0;
3131
3351
  }
@@ -3246,9 +3466,9 @@ function extractContractRoute(args) {
3246
3466
  let contractDef = null;
3247
3467
  let bodyZodRef = null;
3248
3468
  let queryZodRef = null;
3249
- if (import_ts_morph11.Node.isCallExpression(firstDecoratorArg)) {
3469
+ if (import_ts_morph12.Node.isCallExpression(firstDecoratorArg)) {
3250
3470
  contractDef = parseDefineContractCall(firstDecoratorArg, project);
3251
- } else if (import_ts_morph11.Node.isIdentifier(firstDecoratorArg)) {
3471
+ } else if (import_ts_morph12.Node.isIdentifier(firstDecoratorArg)) {
3252
3472
  const identName = firstDecoratorArg.getText();
3253
3473
  const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
3254
3474
  if (!resolvedVar) {
@@ -3432,7 +3652,7 @@ async function discoverPages(opts) {
3432
3652
  const rel = (0, import_node_path4.relative)(opts.cwd, file);
3433
3653
  const nameRel = (0, import_node_path4.relative)(pagesBase, file).split(import_node_path4.sep).join("/");
3434
3654
  const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
3435
- const source = await (0, import_promises2.readFile)(file, "utf8");
3655
+ const source = await (opts.read ? opts.read(file) : (0, import_promises2.readFile)(file, "utf8"));
3436
3656
  const propsSource = extractPropsSource(source, opts.propsExport);
3437
3657
  out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
3438
3658
  }
@@ -3440,10 +3660,14 @@ async function discoverPages(opts) {
3440
3660
  assertUniqueNames(out);
3441
3661
  return out;
3442
3662
  }
3443
- async function discoverPageScopes(config) {
3663
+ async function discoverPageScopes(config, read) {
3444
3664
  const pages = config.pages;
3445
3665
  if (!pages) return [];
3446
- const common = { cwd: config.codegen.cwd, propsExport: pages.propsExport };
3666
+ const common = {
3667
+ cwd: config.codegen.cwd,
3668
+ propsExport: pages.propsExport,
3669
+ ...read ? { read } : {}
3670
+ };
3447
3671
  const out = await discoverPages({
3448
3672
  ...common,
3449
3673
  glob: pages.glob,
@@ -3518,8 +3742,9 @@ var import_promises11 = require("fs/promises");
3518
3742
  var import_node_path15 = require("path");
3519
3743
 
3520
3744
  // src/discovery/shared-props.ts
3745
+ var import_node_fs3 = require("fs");
3521
3746
  var import_node_path5 = require("path");
3522
- var import_ts_morph12 = require("ts-morph");
3747
+ var import_ts_morph13 = require("ts-morph");
3523
3748
  function discoverSharedProps(project, moduleEntry) {
3524
3749
  try {
3525
3750
  let sourceFile = project.getSourceFile(moduleEntry);
@@ -3545,14 +3770,9 @@ function discoverSharedPropsFromConfig(config) {
3545
3770
  const tsconfigPath = config.app.tsconfig ?? (0, import_node_path5.join)(config.codegen.cwd, "tsconfig.json");
3546
3771
  let project;
3547
3772
  try {
3548
- project = new import_ts_morph12.Project({
3549
- tsConfigFilePath: tsconfigPath,
3550
- skipAddingFilesFromTsConfig: true,
3551
- skipLoadingLibFiles: true,
3552
- skipFileDependencyResolution: true
3553
- });
3773
+ project = sharedPropsProject(tsconfigPath);
3554
3774
  } catch {
3555
- project = new import_ts_morph12.Project({
3775
+ project = new import_ts_morph13.Project({
3556
3776
  skipAddingFilesFromTsConfig: true,
3557
3777
  skipLoadingLibFiles: true,
3558
3778
  skipFileDependencyResolution: true,
@@ -3564,14 +3784,35 @@ function discoverSharedPropsFromConfig(config) {
3564
3784
  return null;
3565
3785
  }
3566
3786
  }
3787
+ var parsedTsconfigs = /* @__PURE__ */ new Map();
3788
+ function readText(file) {
3789
+ try {
3790
+ return (0, import_node_fs3.readFileSync)(file, "utf8");
3791
+ } catch {
3792
+ return "\0missing";
3793
+ }
3794
+ }
3795
+ function sharedPropsProject(tsconfigPath) {
3796
+ const text = loadDiscoveryTsconfig(tsconfigPath).files.map((file) => `${file}\0${readText(file)}`).join("\0");
3797
+ const flags = {
3798
+ skipAddingFilesFromTsConfig: true,
3799
+ skipLoadingLibFiles: true,
3800
+ skipFileDependencyResolution: true
3801
+ };
3802
+ const parsed = parsedTsconfigs.get(tsconfigPath);
3803
+ if (parsed?.text === text) return new import_ts_morph13.Project({ ...flags, compilerOptions: parsed.options });
3804
+ const project = new import_ts_morph13.Project({ ...flags, tsConfigFilePath: tsconfigPath });
3805
+ parsedTsconfigs.set(tsconfigPath, { text, options: project.getCompilerOptions() });
3806
+ return project;
3807
+ }
3567
3808
  function findForRootCall(sourceFile) {
3568
- const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph12.SyntaxKind.CallExpression);
3809
+ const callExpressions = sourceFile.getDescendantsOfKind(import_ts_morph13.SyntaxKind.CallExpression);
3569
3810
  for (const call of callExpressions) {
3570
3811
  const expr = call.getExpression();
3571
- if (!import_ts_morph12.Node.isPropertyAccessExpression(expr)) continue;
3812
+ if (!import_ts_morph13.Node.isPropertyAccessExpression(expr)) continue;
3572
3813
  const methodName = expr.getName();
3573
3814
  const objectExpr = expr.getExpression();
3574
- if (methodName === "forRoot" && import_ts_morph12.Node.isIdentifier(objectExpr)) {
3815
+ if (methodName === "forRoot" && import_ts_morph13.Node.isIdentifier(objectExpr)) {
3575
3816
  const name = objectExpr.getText();
3576
3817
  if (name === "InertiaModule") {
3577
3818
  return call;
@@ -3581,19 +3822,19 @@ function findForRootCall(sourceFile) {
3581
3822
  return null;
3582
3823
  }
3583
3824
  function findShareInitializer(forRootCall) {
3584
- if (!import_ts_morph12.Node.isCallExpression(forRootCall)) return null;
3825
+ if (!import_ts_morph13.Node.isCallExpression(forRootCall)) return null;
3585
3826
  const args = forRootCall.getArguments();
3586
3827
  const firstArg2 = args[0];
3587
- if (!firstArg2 || !import_ts_morph12.Node.isObjectLiteralExpression(firstArg2)) return null;
3828
+ if (!firstArg2 || !import_ts_morph13.Node.isObjectLiteralExpression(firstArg2)) return null;
3588
3829
  for (const prop of firstArg2.getProperties()) {
3589
- if (import_ts_morph12.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
3830
+ if (import_ts_morph13.Node.isPropertyAssignment(prop) && prop.getName() === "share") {
3590
3831
  return prop.getInitializer() ?? null;
3591
3832
  }
3592
3833
  }
3593
3834
  return null;
3594
3835
  }
3595
3836
  function extractShareType(node, sourceFile, project) {
3596
- if (import_ts_morph12.Node.isIdentifier(node)) {
3837
+ if (import_ts_morph13.Node.isIdentifier(node)) {
3597
3838
  const ref = resolveIdentifierToImportRef(node, sourceFile, project);
3598
3839
  if (ref) {
3599
3840
  return {
@@ -3603,22 +3844,22 @@ function extractShareType(node, sourceFile, project) {
3603
3844
  };
3604
3845
  }
3605
3846
  }
3606
- if (import_ts_morph12.Node.isArrowFunction(node)) {
3847
+ if (import_ts_morph13.Node.isArrowFunction(node)) {
3607
3848
  const result = extractFromFunctionLike(node, sourceFile);
3608
3849
  return result ? { ...result, isImportRef: false } : null;
3609
3850
  }
3610
- if (import_ts_morph12.Node.isFunctionExpression(node)) {
3851
+ if (import_ts_morph13.Node.isFunctionExpression(node)) {
3611
3852
  const result = extractFromFunctionLike(node, sourceFile);
3612
3853
  return result ? { ...result, isImportRef: false } : null;
3613
3854
  }
3614
- if (import_ts_morph12.Node.isObjectLiteralExpression(node)) {
3855
+ if (import_ts_morph13.Node.isObjectLiteralExpression(node)) {
3615
3856
  const result = extractFromObjectLiteral(node);
3616
3857
  return result ? { ...result, isImportRef: false } : null;
3617
3858
  }
3618
3859
  return null;
3619
3860
  }
3620
3861
  function resolveIdentifierToImportRef(id, sourceFile, project) {
3621
- if (!import_ts_morph12.Node.isIdentifier(id)) return null;
3862
+ if (!import_ts_morph13.Node.isIdentifier(id)) return null;
3622
3863
  const name = id.getText();
3623
3864
  const localFunc = sourceFile.getFunction(name);
3624
3865
  if (localFunc?.isExported()) {
@@ -3652,37 +3893,37 @@ function resolveIdentifierToImportRef(id, sourceFile, project) {
3652
3893
  return null;
3653
3894
  }
3654
3895
  function extractFromFunctionLike(node, _sourceFile) {
3655
- const returnTypeNode = import_ts_morph12.Node.isArrowFunction(node) || import_ts_morph12.Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
3896
+ const returnTypeNode = import_ts_morph13.Node.isArrowFunction(node) || import_ts_morph13.Node.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
3656
3897
  if (returnTypeNode) {
3657
3898
  return extractFromReturnTypeAnnotation(returnTypeNode);
3658
3899
  }
3659
- if (import_ts_morph12.Node.isArrowFunction(node)) {
3900
+ if (import_ts_morph13.Node.isArrowFunction(node)) {
3660
3901
  const body = node.getBody();
3661
- if (import_ts_morph12.Node.isParenthesizedExpression(body)) {
3902
+ if (import_ts_morph13.Node.isParenthesizedExpression(body)) {
3662
3903
  const inner = body.getExpression();
3663
- if (import_ts_morph12.Node.isObjectLiteralExpression(inner)) {
3904
+ if (import_ts_morph13.Node.isObjectLiteralExpression(inner)) {
3664
3905
  return extractFromObjectLiteral(inner);
3665
3906
  }
3666
3907
  }
3667
- if (import_ts_morph12.Node.isObjectLiteralExpression(body)) {
3908
+ if (import_ts_morph13.Node.isObjectLiteralExpression(body)) {
3668
3909
  return extractFromObjectLiteral(body);
3669
3910
  }
3670
- if (import_ts_morph12.Node.isBlock(body)) {
3911
+ if (import_ts_morph13.Node.isBlock(body)) {
3671
3912
  return extractFromBlockReturn(body);
3672
3913
  }
3673
3914
  }
3674
- if (import_ts_morph12.Node.isFunctionExpression(node)) {
3915
+ if (import_ts_morph13.Node.isFunctionExpression(node)) {
3675
3916
  const body = node.getBody();
3676
- if (import_ts_morph12.Node.isBlock(body)) {
3917
+ if (import_ts_morph13.Node.isBlock(body)) {
3677
3918
  return extractFromBlockReturn(body);
3678
3919
  }
3679
3920
  }
3680
3921
  return null;
3681
3922
  }
3682
3923
  function extractFromReturnTypeAnnotation(typeNode) {
3683
- if (import_ts_morph12.Node.isTypeReference(typeNode)) {
3924
+ if (import_ts_morph13.Node.isTypeReference(typeNode)) {
3684
3925
  const typeName = typeNode.getTypeName();
3685
- if (import_ts_morph12.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
3926
+ if (import_ts_morph13.Node.isIdentifier(typeName) && typeName.getText() === "Promise") {
3686
3927
  const typeArgs = typeNode.getTypeArguments();
3687
3928
  const firstArg2 = typeArgs[0];
3688
3929
  if (firstArg2) {
@@ -3691,10 +3932,10 @@ function extractFromReturnTypeAnnotation(typeNode) {
3691
3932
  return null;
3692
3933
  }
3693
3934
  }
3694
- if (import_ts_morph12.Node.isTypeLiteral(typeNode)) {
3935
+ if (import_ts_morph13.Node.isTypeLiteral(typeNode)) {
3695
3936
  const properties = [];
3696
3937
  for (const member of typeNode.getMembers()) {
3697
- if (import_ts_morph12.Node.isPropertySignature(member)) {
3938
+ if (import_ts_morph13.Node.isPropertySignature(member)) {
3698
3939
  const name = member.getName();
3699
3940
  const memberTypeNode = member.getTypeNode();
3700
3941
  const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
@@ -3708,19 +3949,19 @@ function extractFromReturnTypeAnnotation(typeNode) {
3708
3949
  return null;
3709
3950
  }
3710
3951
  function extractFromBlockReturn(block) {
3711
- if (!import_ts_morph12.Node.isBlock(block)) return null;
3952
+ if (!import_ts_morph13.Node.isBlock(block)) return null;
3712
3953
  const statements = block.getStatements();
3713
3954
  for (let i = statements.length - 1; i >= 0; i--) {
3714
3955
  const stmt = statements[i];
3715
- if (!import_ts_morph12.Node.isReturnStatement(stmt)) continue;
3956
+ if (!import_ts_morph13.Node.isReturnStatement(stmt)) continue;
3716
3957
  const expr = stmt.getExpression();
3717
3958
  if (!expr) continue;
3718
- if (import_ts_morph12.Node.isObjectLiteralExpression(expr)) {
3959
+ if (import_ts_morph13.Node.isObjectLiteralExpression(expr)) {
3719
3960
  return extractFromObjectLiteral(expr);
3720
3961
  }
3721
- if (import_ts_morph12.Node.isParenthesizedExpression(expr)) {
3962
+ if (import_ts_morph13.Node.isParenthesizedExpression(expr)) {
3722
3963
  const inner = expr.getExpression();
3723
- if (import_ts_morph12.Node.isObjectLiteralExpression(inner)) {
3964
+ if (import_ts_morph13.Node.isObjectLiteralExpression(inner)) {
3724
3965
  return extractFromObjectLiteral(inner);
3725
3966
  }
3726
3967
  }
@@ -3729,10 +3970,10 @@ function extractFromBlockReturn(block) {
3729
3970
  return null;
3730
3971
  }
3731
3972
  function extractFromObjectLiteral(objLiteral) {
3732
- if (!import_ts_morph12.Node.isObjectLiteralExpression(objLiteral)) return null;
3973
+ if (!import_ts_morph13.Node.isObjectLiteralExpression(objLiteral)) return null;
3733
3974
  const properties = [];
3734
3975
  for (const prop of objLiteral.getProperties()) {
3735
- if (!import_ts_morph12.Node.isPropertyAssignment(prop)) continue;
3976
+ if (!import_ts_morph13.Node.isPropertyAssignment(prop)) continue;
3736
3977
  const name = prop.getName();
3737
3978
  const initializer = prop.getInitializer();
3738
3979
  if (!initializer) continue;
@@ -3744,21 +3985,21 @@ function extractFromObjectLiteral(objLiteral) {
3744
3985
  return { typeString, properties, isImportRef: false };
3745
3986
  }
3746
3987
  function inferExpressionType(node) {
3747
- if (import_ts_morph12.Node.isStringLiteral(node)) return "string";
3748
- if (import_ts_morph12.Node.isTemplateExpression(node) || import_ts_morph12.Node.isNoSubstitutionTemplateLiteral(node))
3988
+ if (import_ts_morph13.Node.isStringLiteral(node)) return "string";
3989
+ if (import_ts_morph13.Node.isTemplateExpression(node) || import_ts_morph13.Node.isNoSubstitutionTemplateLiteral(node))
3749
3990
  return "string";
3750
- if (import_ts_morph12.Node.isNumericLiteral(node)) return "number";
3751
- if (node.getKind() === import_ts_morph12.SyntaxKind.TrueKeyword || node.getKind() === import_ts_morph12.SyntaxKind.FalseKeyword) {
3991
+ if (import_ts_morph13.Node.isNumericLiteral(node)) return "number";
3992
+ if (node.getKind() === import_ts_morph13.SyntaxKind.TrueKeyword || node.getKind() === import_ts_morph13.SyntaxKind.FalseKeyword) {
3752
3993
  return "boolean";
3753
3994
  }
3754
- if (node.getKind() === import_ts_morph12.SyntaxKind.NullKeyword) return "null";
3755
- if (import_ts_morph12.Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
3756
- if (import_ts_morph12.Node.isObjectLiteralExpression(node)) {
3995
+ if (node.getKind() === import_ts_morph13.SyntaxKind.NullKeyword) return "null";
3996
+ if (import_ts_morph13.Node.isIdentifier(node) && node.getText() === "undefined") return "undefined";
3997
+ if (import_ts_morph13.Node.isObjectLiteralExpression(node)) {
3757
3998
  const props = node.getProperties();
3758
3999
  if (props.length === 0) return "Record<string, unknown>";
3759
4000
  const entries = [];
3760
4001
  for (const prop of props) {
3761
- if (!import_ts_morph12.Node.isPropertyAssignment(prop)) continue;
4002
+ if (!import_ts_morph13.Node.isPropertyAssignment(prop)) continue;
3762
4003
  const key = prop.getName();
3763
4004
  const init = prop.getInitializer();
3764
4005
  if (!init) continue;
@@ -3767,23 +4008,23 @@ function inferExpressionType(node) {
3767
4008
  if (entries.length === 0) return "Record<string, unknown>";
3768
4009
  return `{ ${entries.join("; ")} }`;
3769
4010
  }
3770
- if (import_ts_morph12.Node.isArrayLiteralExpression(node)) {
4011
+ if (import_ts_morph13.Node.isArrayLiteralExpression(node)) {
3771
4012
  const elements = node.getElements();
3772
4013
  if (elements.length === 0) return "Array<unknown>";
3773
4014
  const first = elements[0];
3774
4015
  if (first) return `Array<${inferExpressionType(first)}>`;
3775
4016
  return "Array<unknown>";
3776
4017
  }
3777
- if (import_ts_morph12.Node.isConditionalExpression(node)) {
4018
+ if (import_ts_morph13.Node.isConditionalExpression(node)) {
3778
4019
  const whenTrue = inferExpressionType(node.getWhenTrue());
3779
4020
  const whenFalse = inferExpressionType(node.getWhenFalse());
3780
4021
  if (whenTrue === whenFalse) return whenTrue;
3781
4022
  return `${whenTrue} | ${whenFalse}`;
3782
4023
  }
3783
- if (import_ts_morph12.Node.isParenthesizedExpression(node)) {
4024
+ if (import_ts_morph13.Node.isParenthesizedExpression(node)) {
3784
4025
  return inferExpressionType(node.getExpression());
3785
4026
  }
3786
- if (import_ts_morph12.Node.isAsExpression(node)) {
4027
+ if (import_ts_morph13.Node.isAsExpression(node)) {
3787
4028
  const typeNode = node.getTypeNode();
3788
4029
  if (typeNode) return typeNode.getText();
3789
4030
  }
@@ -3796,7 +4037,7 @@ var import_node_path7 = require("path");
3796
4037
 
3797
4038
  // src/extension/registry.ts
3798
4039
  var import_node_path6 = require("path");
3799
- var import_ts_morph13 = require("ts-morph");
4040
+ var import_ts_morph14 = require("ts-morph");
3800
4041
  function resolveApiSlots(extensions) {
3801
4042
  let layer;
3802
4043
  let layerOwner;
@@ -3847,7 +4088,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
3847
4088
  },
3848
4089
  project() {
3849
4090
  if (!project) {
3850
- project = new import_ts_morph13.Project({
4091
+ project = new import_ts_morph14.Project({
3851
4092
  skipAddingFilesFromTsConfig: true,
3852
4093
  skipLoadingLibFiles: true,
3853
4094
  skipFileDependencyResolution: true,
@@ -5519,7 +5760,8 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
5519
5760
  async function generate(config, inputRoutes = [], entryPoint = "cli") {
5520
5761
  setCodegenDebug(config.debug);
5521
5762
  const manifest = await readManifest(config.codegen.outDir);
5522
- const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
5763
+ const inputs = await readInputs(config);
5764
+ const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? [], inputs);
5523
5765
  if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
5524
5766
  console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
5525
5767
  return;
@@ -5547,7 +5789,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
5547
5789
  }
5548
5790
  if (config.pages) {
5549
5791
  const pagesConfig = config.pages;
5550
- const pages = await discoverPageScopes(config);
5792
+ const pages = await discoverPageScopes(config, (path) => readInput(inputs, path));
5551
5793
  const sharedProps = discoverSharedPropsFromConfig(config);
5552
5794
  await emitPages(pages, config.codegen.outDir, {
5553
5795
  propsExport: pagesConfig.propsExport,
@@ -5597,7 +5839,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
5597
5839
  }
5598
5840
  const outputFiles = await listOutputFiles(config.codegen.outDir);
5599
5841
  const extraInputs = [...trackedInputs].sort();
5600
- const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
5842
+ const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs, inputs) : inputsHash;
5601
5843
  await writeManifest(config.codegen.outDir, {
5602
5844
  version: VERSION,
5603
5845
  hash: recordedHash,
@@ -5780,13 +6022,15 @@ async function watch(config, onChange, options = {}) {
5780
6022
  pagesWatcher.on("unlink", schedulePagesRegenerate);
5781
6023
  let contractsDebounceTimer;
5782
6024
  const pendingChangedPaths = /* @__PURE__ */ new Set();
6025
+ let pendingAddedOrRemoved = false;
5783
6026
  const contractsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.contracts.glob), {
5784
6027
  ignoreInitial: true,
5785
6028
  persistent: true,
5786
6029
  awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5787
6030
  });
5788
- function scheduleContractsRegenerate(changedPath) {
6031
+ function scheduleContractsRegenerate(changedPath, addedOrRemoved = false) {
5789
6032
  if (typeof changedPath === "string") pendingChangedPaths.add(changedPath);
6033
+ if (addedOrRemoved) pendingAddedOrRemoved = true;
5790
6034
  if (contractsDebounceTimer !== void 0) {
5791
6035
  clearTimeout(contractsDebounceTimer);
5792
6036
  }
@@ -5794,8 +6038,12 @@ async function watch(config, onChange, options = {}) {
5794
6038
  contractsDebounceTimer = void 0;
5795
6039
  const changed = [...pendingChangedPaths];
5796
6040
  pendingChangedPaths.clear();
6041
+ const filesAddedOrRemoved = pendingAddedOrRemoved;
6042
+ pendingAddedOrRemoved = false;
5797
6043
  try {
5798
- const routes = await (await getDiscovery()).rediscover(changed);
6044
+ const routes = await (await getDiscovery()).rediscover(changed, {
6045
+ filesAddedOrRemoved
6046
+ });
5799
6047
  lastRoutes = routes;
5800
6048
  await generateAndTrack(routes);
5801
6049
  } catch (err) {
@@ -5807,20 +6055,20 @@ async function watch(config, onChange, options = {}) {
5807
6055
  onChange?.();
5808
6056
  }, config.contracts.debounceMs);
5809
6057
  }
5810
- contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6058
+ contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
5811
6059
  contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
5812
- contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
6060
+ contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
5813
6061
  const formsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.forms.watch), {
5814
6062
  ignoreInitial: true,
5815
6063
  persistent: true,
5816
6064
  awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5817
6065
  });
5818
- formsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6066
+ formsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
5819
6067
  formsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
5820
- formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
5821
- trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p));
6068
+ formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
6069
+ trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
5822
6070
  trackedWatcher.on("change", (p) => scheduleContractsRegenerate(p));
5823
- trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
6071
+ trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
5824
6072
  return {
5825
6073
  close: async () => {
5826
6074
  if (pagesDebounceTimer !== void 0) {
@@ -5841,7 +6089,7 @@ async function watch(config, onChange, options = {}) {
5841
6089
  }
5842
6090
 
5843
6091
  // src/index.ts
5844
- var VERSION = "0.28.0";
6092
+ var VERSION = "0.29.1";
5845
6093
 
5846
6094
  // src/generate-manifest.ts
5847
6095
  var MANIFEST_FILE = ".codegen-manifest.json";
@@ -5906,36 +6154,64 @@ async function discoverInputFiles(config) {
5906
6154
  const matched = await (0, import_fast_glob3.default)(globs, { cwd, absolute: true, onlyFiles: true });
5907
6155
  return [...new Set(matched)].sort();
5908
6156
  }
5909
- async function computeInputsHash(config, extraInputs = []) {
6157
+ var READ_CONCURRENCY = 64;
6158
+ async function readAll(paths, read) {
6159
+ const out = /* @__PURE__ */ new Map();
6160
+ for (let i = 0; i < paths.length; i += READ_CONCURRENCY) {
6161
+ const batch = paths.slice(i, i + READ_CONCURRENCY);
6162
+ const texts = await Promise.all(batch.map(read));
6163
+ batch.forEach((path, j) => out.set(path, texts[j]));
6164
+ }
6165
+ return out;
6166
+ }
6167
+ async function readInputs(config) {
6168
+ const cwd = config.codegen.cwd;
6169
+ const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
6170
+ const extended = loadDiscoveryTsconfig(tsconfigPath).files;
6171
+ const tsconfigFiles = extended.length > 0 ? extended : [tsconfigPath];
6172
+ const files = await discoverInputFiles(config);
6173
+ const contents = /* @__PURE__ */ new Map();
6174
+ const tsconfigs = await readAll(
6175
+ tsconfigFiles,
6176
+ (file) => (0, import_promises15.readFile)(file, "utf8").catch(() => null)
6177
+ );
6178
+ for (const [file, text] of tsconfigs) contents.set(file, text);
6179
+ for (const [file, text] of await readAll(files, (file2) => (0, import_promises15.readFile)(file2, "utf8"))) {
6180
+ contents.set(file, text);
6181
+ }
6182
+ return { tsconfigFiles, files, contents, tracked: /* @__PURE__ */ new Map() };
6183
+ }
6184
+ async function readInput(snapshot, path) {
6185
+ return snapshot.contents.get(path) ?? (0, import_promises15.readFile)(path, "utf8");
6186
+ }
6187
+ async function computeInputsHash(config, extraInputs = [], snapshot) {
6188
+ const inputs = snapshot ?? await readInputs(config);
5910
6189
  const hash = (0, import_node_crypto.createHash)("sha256");
5911
6190
  hash.update(`version:${VERSION}
5912
6191
  `);
5913
6192
  hash.update(`config:${serializeConfig(config)}
5914
6193
  `);
5915
6194
  const cwd = config.codegen.cwd;
5916
- const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
5917
- const tsconfigFiles = loadDiscoveryTsconfig(tsconfigPath).files;
5918
- for (const file of tsconfigFiles.length > 0 ? tsconfigFiles : [tsconfigPath]) {
5919
- const contents = await (0, import_promises15.readFile)(file, "utf8").catch(() => null);
6195
+ for (const file of inputs.tsconfigFiles) {
5920
6196
  hash.update(`tsconfig:${(0, import_node_path18.relative)(cwd, file)}
5921
6197
  `);
5922
- hash.update(contents ?? " missing");
6198
+ hash.update(inputs.contents.get(file) ?? " missing");
5923
6199
  hash.update("\n");
5924
6200
  }
5925
- const globbed = await discoverInputFiles(config);
5926
- const globbedRelative = new Set(globbed.map((file) => (0, import_node_path18.relative)(cwd, file)));
6201
+ const globbedRelative = new Set(inputs.files.map((file) => (0, import_node_path18.relative)(cwd, file)));
5927
6202
  const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
5928
- for (const file of globbed) {
5929
- const contents = await (0, import_promises15.readFile)(file, "utf8");
6203
+ for (const file of inputs.files) {
5930
6204
  hash.update(`file:${(0, import_node_path18.relative)(cwd, file)}
5931
6205
  `);
5932
- hash.update(contents);
6206
+ hash.update(inputs.contents.get(file));
5933
6207
  hash.update("\n");
5934
6208
  }
6209
+ const unread = extra.map((file) => (0, import_node_path18.join)(cwd, file)).filter((abs) => !inputs.tracked.has(abs));
6210
+ for (const [abs, text] of await readAll(unread, readTrackedInput)) inputs.tracked.set(abs, text);
5935
6211
  for (const file of extra) {
5936
6212
  hash.update(`extra:${file}
5937
6213
  `);
5938
- hash.update(await readTrackedInput((0, import_node_path18.join)(cwd, file)));
6214
+ hash.update(inputs.tracked.get((0, import_node_path18.join)(cwd, file)));
5939
6215
  hash.update("\n");
5940
6216
  }
5941
6217
  return hash.digest("hex");