@dudousxd/nestjs-codegen 0.29.0 → 0.30.0

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