@dudousxd/nestjs-codegen 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/main.cjs CHANGED
@@ -219,7 +219,7 @@ Run \`nestjs-codegen init\` to create a starter config.`
219
219
 
220
220
  // src/generate.ts
221
221
  var import_promises12 = require("fs/promises");
222
- var import_node_path13 = require("path");
222
+ var import_node_path14 = require("path");
223
223
 
224
224
  // src/discovery/pages.ts
225
225
  var import_promises2 = require("fs/promises");
@@ -548,9 +548,10 @@ function inferExpressionType(node) {
548
548
 
549
549
  // src/emit/emit-api.ts
550
550
  var import_promises3 = require("fs/promises");
551
- var import_node_path4 = require("path");
551
+ var import_node_path5 = require("path");
552
552
 
553
553
  // src/extension/registry.ts
554
+ var import_node_path4 = require("path");
554
555
  var import_ts_morph2 = require("ts-morph");
555
556
  function resolveApiSlots(extensions) {
556
557
  let layer;
@@ -583,7 +584,7 @@ function mergeExclusive(target, incoming, {
583
584
  target.set(key, { value, owner });
584
585
  }
585
586
  }
586
- function createExtensionContext(config, getRoutes) {
587
+ function createExtensionContext(config, getRoutes, trackedInputs) {
587
588
  let project;
588
589
  return {
589
590
  cwd: config.codegen.cwd,
@@ -592,6 +593,13 @@ function createExtensionContext(config, getRoutes) {
592
593
  get routes() {
593
594
  return getRoutes();
594
595
  },
596
+ trackInput(...paths) {
597
+ if (!trackedInputs) return;
598
+ for (const path of paths) {
599
+ if (!path) continue;
600
+ trackedInputs.add((0, import_node_path4.relative)(config.codegen.cwd, (0, import_node_path4.resolve)(config.codegen.cwd, path)));
601
+ }
602
+ },
595
603
  project() {
596
604
  if (!project) {
597
605
  project = new import_ts_morph2.Project({
@@ -651,7 +659,7 @@ function requestShape(route) {
651
659
  async function emitApi(routes, outDir, opts = {}) {
652
660
  await (0, import_promises3.mkdir)(outDir, { recursive: true });
653
661
  const content = buildApiFile(routes, outDir, opts);
654
- await (0, import_promises3.writeFile)((0, import_node_path4.join)(outDir, "api.ts"), content, "utf8");
662
+ await (0, import_promises3.writeFile)((0, import_node_path5.join)(outDir, "api.ts"), content, "utf8");
655
663
  }
656
664
  function splitName(name) {
657
665
  return name.split(".");
@@ -759,7 +767,7 @@ function rawResponseType(c, outDir) {
759
767
  return c.contractSource.response;
760
768
  }
761
769
  if (c.controllerRef) {
762
- let relPath = (0, import_node_path4.relative)(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
770
+ let relPath = (0, import_node_path5.relative)(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
763
771
  if (!relPath.startsWith(".")) relPath = `./${relPath}`;
764
772
  return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
765
773
  }
@@ -1085,6 +1093,11 @@ function buildApiFile(routes, outDir, opts = {}) {
1085
1093
  config: {},
1086
1094
  project: () => {
1087
1095
  throw new Error("ExtensionContext.project() is unavailable in standalone emitApi.");
1096
+ },
1097
+ // No manifest is written on the standalone path, so there is nothing for
1098
+ // tracked dependencies to be recorded into — accept and discard them
1099
+ // rather than making every caller supply a sink.
1100
+ trackInput: () => {
1088
1101
  }
1089
1102
  };
1090
1103
  const importsByFile = /* @__PURE__ */ new Map();
@@ -1140,8 +1153,8 @@ function buildApiFile(routes, outDir, opts = {}) {
1140
1153
  const emittedNames = /* @__PURE__ */ new Set();
1141
1154
  for (const [filePath, names] of importsByFile) {
1142
1155
  let relPath;
1143
- if ((0, import_node_path4.isAbsolute)(filePath)) {
1144
- relPath = (0, import_node_path4.relative)(outDir, filePath).replace(/\.ts$/, "");
1156
+ if ((0, import_node_path5.isAbsolute)(filePath)) {
1157
+ relPath = (0, import_node_path5.relative)(outDir, filePath).replace(/\.ts$/, "");
1145
1158
  if (!relPath.startsWith(".")) relPath = `./${relPath}`;
1146
1159
  } else {
1147
1160
  relPath = filePath;
@@ -1235,7 +1248,7 @@ function buildApiFile(routes, outDir, opts = {}) {
1235
1248
 
1236
1249
  // src/emit/emit-cache.ts
1237
1250
  var import_promises4 = require("fs/promises");
1238
- var import_node_path5 = require("path");
1251
+ var import_node_path6 = require("path");
1239
1252
  async function emitCache(pages, outDir) {
1240
1253
  await (0, import_promises4.mkdir)(outDir, { recursive: true });
1241
1254
  const entries = await Promise.all(
@@ -1249,19 +1262,19 @@ async function emitCache(pages, outDir) {
1249
1262
  })
1250
1263
  );
1251
1264
  const cache = { pages: entries };
1252
- await (0, import_promises4.writeFile)((0, import_node_path5.join)(outDir, "components.json"), `${JSON.stringify(cache, null, 2)}
1265
+ await (0, import_promises4.writeFile)((0, import_node_path6.join)(outDir, "components.json"), `${JSON.stringify(cache, null, 2)}
1253
1266
  `, "utf8");
1254
1267
  }
1255
1268
 
1256
1269
  // src/emit/emit-forms.ts
1257
1270
  var import_promises5 = require("fs/promises");
1258
- var import_node_path6 = require("path");
1271
+ var import_node_path7 = require("path");
1259
1272
  async function emitForms(routes, outDir, config, adapter) {
1260
1273
  if (config && config.enabled === false) return false;
1261
1274
  const content = buildFormsFileWithAdapter(routes, outDir, adapter, config);
1262
1275
  if (content === null) return false;
1263
1276
  await (0, import_promises5.mkdir)(outDir, { recursive: true });
1264
- await (0, import_promises5.writeFile)((0, import_node_path6.join)(outDir, "forms.ts"), content, "utf8");
1277
+ await (0, import_promises5.writeFile)((0, import_node_path7.join)(outDir, "forms.ts"), content, "utf8");
1265
1278
  return true;
1266
1279
  }
1267
1280
  function pascal(segment) {
@@ -1274,7 +1287,7 @@ function deriveBaseName(routeName) {
1274
1287
  return { method, full };
1275
1288
  }
1276
1289
  function relImport(outDir, filePath) {
1277
- let relPath = (0, import_node_path6.relative)(outDir, filePath).replace(/\.ts$/, "");
1290
+ let relPath = (0, import_node_path7.relative)(outDir, filePath).replace(/\.ts$/, "");
1278
1291
  if (!relPath.startsWith(".")) relPath = `./${relPath}`;
1279
1292
  return relPath;
1280
1293
  }
@@ -1548,7 +1561,7 @@ function buildFormsFileWithAdapter(routes, outDir, adapter, config) {
1548
1561
 
1549
1562
  // src/emit/emit-index.ts
1550
1563
  var import_promises6 = require("fs/promises");
1551
- var import_node_path7 = require("path");
1564
+ var import_node_path8 = require("path");
1552
1565
  async function emitIndex(outDir, hasContracts = false, hasForms = false) {
1553
1566
  await (0, import_promises6.mkdir)(outDir, { recursive: true });
1554
1567
  const exports2 = ["export * from './pages.js';", "export * from './routes.js';"];
@@ -1561,12 +1574,12 @@ async function emitIndex(outDir, hasContracts = false, hasForms = false) {
1561
1574
  const content = ["// Generated by @dudousxd/nestjs-codegen. Do not edit.", ...exports2, ""].join(
1562
1575
  "\n"
1563
1576
  );
1564
- await (0, import_promises6.writeFile)((0, import_node_path7.join)(outDir, "index.d.ts"), content, "utf8");
1577
+ await (0, import_promises6.writeFile)((0, import_node_path8.join)(outDir, "index.d.ts"), content, "utf8");
1565
1578
  }
1566
1579
 
1567
1580
  // src/emit/emit-mocks.ts
1568
1581
  var import_promises7 = require("fs/promises");
1569
- var import_node_path8 = require("path");
1582
+ var import_node_path9 = require("path");
1570
1583
 
1571
1584
  // src/ir/schema-node-to-json-schema.ts
1572
1585
  var DEFAULT_CTX = { refPrefix: "#/components/schemas/" };
@@ -1890,12 +1903,12 @@ async function emitMocks(routes, outDir, opts = {}) {
1890
1903
  await (0, import_promises7.mkdir)(outDir, { recursive: true });
1891
1904
  const content = buildMocksFile(routes, opts);
1892
1905
  const fileName = opts.fileName ?? "mocks.ts";
1893
- await (0, import_promises7.writeFile)((0, import_node_path8.join)(outDir, fileName), content, "utf8");
1906
+ await (0, import_promises7.writeFile)((0, import_node_path9.join)(outDir, fileName), content, "utf8");
1894
1907
  }
1895
1908
 
1896
1909
  // src/emit/emit-openapi.ts
1897
1910
  var import_promises8 = require("fs/promises");
1898
- var import_node_path9 = require("path");
1911
+ var import_node_path10 = require("path");
1899
1912
  var REF_PREFIX2 = "#/components/schemas/";
1900
1913
  function toOpenApiPath(path) {
1901
1914
  return path.replace(/:([^/]+)/g, "{$1}");
@@ -2016,13 +2029,13 @@ async function emitOpenApi(routes, outDir, opts = {}) {
2016
2029
  await (0, import_promises8.mkdir)(outDir, { recursive: true });
2017
2030
  const doc = buildOpenApiSpec(routes, opts);
2018
2031
  const fileName = opts.fileName ?? "openapi.json";
2019
- await (0, import_promises8.writeFile)((0, import_node_path9.join)(outDir, fileName), `${JSON.stringify(doc, null, 2)}
2032
+ await (0, import_promises8.writeFile)((0, import_node_path10.join)(outDir, fileName), `${JSON.stringify(doc, null, 2)}
2020
2033
  `, "utf8");
2021
2034
  }
2022
2035
 
2023
2036
  // src/emit/emit-pages.ts
2024
2037
  var import_promises9 = require("fs/promises");
2025
- var import_node_path10 = require("path");
2038
+ var import_node_path11 = require("path");
2026
2039
  async function emitPages(pages, outDir, _options = {}) {
2027
2040
  await (0, import_promises9.mkdir)(outDir, { recursive: true });
2028
2041
  const pageNameUnion = pages.length > 0 ? pages.map((p) => JSON.stringify(p.name)).join(" | ") : "never";
@@ -2043,7 +2056,7 @@ ${augBody}
2043
2056
  }
2044
2057
  ${sharedPropsBlock}}
2045
2058
  `;
2046
- await (0, import_promises9.writeFile)((0, import_node_path10.join)(outDir, "pages.d.ts"), content, "utf8");
2059
+ await (0, import_promises9.writeFile)((0, import_node_path11.join)(outDir, "pages.d.ts"), content, "utf8");
2047
2060
  }
2048
2061
  function buildSharedPropsBlock(sharedProps) {
2049
2062
  if (!sharedProps) return "";
@@ -2062,7 +2075,7 @@ ${propsBody}
2062
2075
  `;
2063
2076
  }
2064
2077
  function buildAugmentationType(page, outDir) {
2065
- let importPath = (0, import_node_path10.relative)(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
2078
+ let importPath = (0, import_node_path11.relative)(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
2066
2079
  if (!importPath.startsWith(".")) {
2067
2080
  importPath = `./${importPath}`;
2068
2081
  }
@@ -2074,11 +2087,11 @@ function needsQuotes(name) {
2074
2087
 
2075
2088
  // src/emit/emit-routes.ts
2076
2089
  var import_promises10 = require("fs/promises");
2077
- var import_node_path11 = require("path");
2090
+ var import_node_path12 = require("path");
2078
2091
  async function emitRoutes(routes, outDir) {
2079
2092
  await (0, import_promises10.mkdir)(outDir, { recursive: true });
2080
2093
  const content = buildRoutesFile(routes);
2081
- await (0, import_promises10.writeFile)((0, import_node_path11.join)(outDir, "routes.ts"), content, "utf8");
2094
+ await (0, import_promises10.writeFile)((0, import_node_path12.join)(outDir, "routes.ts"), content, "utf8");
2082
2095
  }
2083
2096
  function buildRoutesFile(routes) {
2084
2097
  if (routes.length === 0) {
@@ -2193,7 +2206,7 @@ function buildEmpty() {
2193
2206
  // src/generate-manifest.ts
2194
2207
  var import_node_crypto = require("crypto");
2195
2208
  var import_promises11 = require("fs/promises");
2196
- var import_node_path12 = require("path");
2209
+ var import_node_path13 = require("path");
2197
2210
  var import_fast_glob2 = __toESM(require("fast-glob"), 1);
2198
2211
  var MANIFEST_FILE = ".codegen-manifest.json";
2199
2212
  var LOCK_FILE = ".watcher.lock";
@@ -2216,6 +2229,9 @@ function isManifestShape(value) {
2216
2229
  if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
2217
2230
  return false;
2218
2231
  }
2232
+ if (candidate.extraInputs !== void 0 && (!Array.isArray(candidate.extraInputs) || !candidate.extraInputs.every((entry) => typeof entry === "string"))) {
2233
+ return false;
2234
+ }
2219
2235
  if (!Array.isArray(candidate.files)) return false;
2220
2236
  return candidate.files.every((entry) => typeof entry === "string");
2221
2237
  }
@@ -2255,26 +2271,35 @@ async function discoverInputFiles(config) {
2255
2271
  const matched = await (0, import_fast_glob2.default)(globs, { cwd, absolute: true, onlyFiles: true });
2256
2272
  return [...new Set(matched)].sort();
2257
2273
  }
2258
- async function computeInputsHash(config) {
2274
+ async function computeInputsHash(config, extraInputs = []) {
2259
2275
  const hash = (0, import_node_crypto.createHash)("sha256");
2260
2276
  hash.update(`version:${VERSION}
2261
2277
  `);
2262
2278
  hash.update(`config:${serializeConfig(config)}
2263
2279
  `);
2264
- const inputFiles = await discoverInputFiles(config);
2265
2280
  const cwd = config.codegen.cwd;
2266
- for (const file of inputFiles) {
2281
+ const globbed = await discoverInputFiles(config);
2282
+ const globbedRelative = new Set(globbed.map((file) => (0, import_node_path13.relative)(cwd, file)));
2283
+ const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
2284
+ for (const file of globbed) {
2267
2285
  const contents = await (0, import_promises11.readFile)(file, "utf8");
2268
- hash.update(`file:${(0, import_node_path12.relative)(cwd, file)}
2286
+ hash.update(`file:${(0, import_node_path13.relative)(cwd, file)}
2269
2287
  `);
2270
2288
  hash.update(contents);
2271
2289
  hash.update("\n");
2272
2290
  }
2291
+ for (const file of extra) {
2292
+ const contents = await (0, import_promises11.readFile)((0, import_node_path13.join)(cwd, file), "utf8").catch(() => null);
2293
+ hash.update(`extra:${file}
2294
+ `);
2295
+ hash.update(contents ?? "\0missing");
2296
+ hash.update("\n");
2297
+ }
2273
2298
  return hash.digest("hex");
2274
2299
  }
2275
2300
  async function readManifest(outDir) {
2276
2301
  try {
2277
- const raw = await (0, import_promises11.readFile)((0, import_node_path12.join)(outDir, MANIFEST_FILE), "utf8");
2302
+ const raw = await (0, import_promises11.readFile)((0, import_node_path13.join)(outDir, MANIFEST_FILE), "utf8");
2278
2303
  const parsed = JSON.parse(raw);
2279
2304
  if (!isManifestShape(parsed)) return null;
2280
2305
  return {
@@ -2283,6 +2308,7 @@ async function readManifest(outDir) {
2283
2308
  ...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
2284
2309
  ...parsed.configHash ? { configHash: parsed.configHash } : {},
2285
2310
  ...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
2311
+ ...parsed.extraInputs ? { extraInputs: parsed.extraInputs } : {},
2286
2312
  files: parsed.files
2287
2313
  };
2288
2314
  } catch {
@@ -2293,7 +2319,7 @@ function computeConfigHash(config) {
2293
2319
  return (0, import_node_crypto.createHash)("sha256").update(serializeConfig(config)).digest("hex");
2294
2320
  }
2295
2321
  async function writeManifest(outDir, manifest) {
2296
- await (0, import_promises11.writeFile)((0, import_node_path12.join)(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
2322
+ await (0, import_promises11.writeFile)((0, import_node_path13.join)(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
2297
2323
  `, "utf8");
2298
2324
  }
2299
2325
  async function listOutputFiles(outDir) {
@@ -2301,11 +2327,11 @@ async function listOutputFiles(outDir) {
2301
2327
  async function walk(dir) {
2302
2328
  const entries = await (0, import_promises11.readdir)(dir, { withFileTypes: true }).catch(() => []);
2303
2329
  for (const entry of entries) {
2304
- const abs = (0, import_node_path12.join)(dir, entry.name);
2330
+ const abs = (0, import_node_path13.join)(dir, entry.name);
2305
2331
  if (entry.isDirectory()) {
2306
2332
  await walk(abs);
2307
2333
  } else if (entry.isFile()) {
2308
- const rel = (0, import_node_path12.relative)(outDir, abs);
2334
+ const rel = (0, import_node_path13.relative)(outDir, abs);
2309
2335
  if (rel === MANIFEST_FILE || rel === LOCK_FILE) continue;
2310
2336
  found.push(rel);
2311
2337
  }
@@ -2342,8 +2368,8 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
2342
2368
  }
2343
2369
  async function generate(config, inputRoutes = [], entryPoint = "cli") {
2344
2370
  setCodegenDebug(config.debug);
2345
- const inputsHash = await computeInputsHash(config);
2346
2371
  const manifest = await readManifest(config.codegen.outDir);
2372
+ const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? []);
2347
2373
  if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
2348
2374
  console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
2349
2375
  return;
@@ -2364,7 +2390,8 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
2364
2390
  }
2365
2391
  const extensions = config.extensions ?? [];
2366
2392
  let routes = inputRoutes;
2367
- const ctx = createExtensionContext(config, () => routes);
2393
+ const trackedInputs = /* @__PURE__ */ new Set();
2394
+ const ctx = createExtensionContext(config, () => routes, trackedInputs);
2368
2395
  if (extensions.length > 0) {
2369
2396
  routes = await applyTransformRoutes(routes, extensions, ctx);
2370
2397
  }
@@ -2418,31 +2445,34 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
2418
2445
  if (extensions.length > 0) {
2419
2446
  const extraFiles = await collectEmittedFiles(extensions, ctx);
2420
2447
  for (const file of extraFiles) {
2421
- const dest = (0, import_node_path13.join)(config.codegen.outDir, file.path);
2422
- await (0, import_promises12.mkdir)((0, import_node_path13.dirname)(dest), { recursive: true });
2448
+ const dest = (0, import_node_path14.join)(config.codegen.outDir, file.path);
2449
+ await (0, import_promises12.mkdir)((0, import_node_path14.dirname)(dest), { recursive: true });
2423
2450
  await (0, import_promises12.writeFile)(dest, file.contents, "utf8");
2424
2451
  }
2425
2452
  }
2426
2453
  const outputFiles = await listOutputFiles(config.codegen.outDir);
2454
+ const extraInputs = [...trackedInputs].sort();
2455
+ const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
2427
2456
  await writeManifest(config.codegen.outDir, {
2428
2457
  version: VERSION,
2429
- hash: inputsHash,
2458
+ hash: recordedHash,
2430
2459
  entryPoint,
2431
2460
  configHash,
2432
2461
  configKeyHashes,
2433
- files: outputFiles
2462
+ files: outputFiles,
2463
+ ...extraInputs.length > 0 ? { extraInputs } : {}
2434
2464
  });
2435
2465
  }
2436
2466
 
2437
2467
  // src/watch/watcher.ts
2438
2468
  var import_promises15 = require("fs/promises");
2439
- var import_node_path17 = require("path");
2469
+ var import_node_path18 = require("path");
2440
2470
  var import_chokidar = __toESM(require("chokidar"), 1);
2441
2471
 
2442
2472
  // src/discovery/contracts-fast.ts
2443
- var import_node_path15 = require("path");
2473
+ var import_node_path16 = require("path");
2444
2474
  var import_fast_glob3 = __toESM(require("fast-glob"), 1);
2445
- var import_ts_morph9 = require("ts-morph");
2475
+ var import_ts_morph10 = require("ts-morph");
2446
2476
 
2447
2477
  // src/discovery/dto-type-resolver.ts
2448
2478
  var import_ts_morph7 = require("ts-morph");
@@ -2452,7 +2482,7 @@ var import_ts_morph4 = require("ts-morph");
2452
2482
 
2453
2483
  // src/discovery/type-ref-resolution.ts
2454
2484
  var import_node_fs = require("fs");
2455
- var import_node_path14 = require("path");
2485
+ var import_node_path15 = require("path");
2456
2486
  var import_ts_morph3 = require("ts-morph");
2457
2487
  var _EMPTY_CTX = { projectRoot: "", tsconfigPaths: null };
2458
2488
  var _ctxByProject = /* @__PURE__ */ new WeakMap();
@@ -2504,12 +2534,12 @@ function findTypeInFile(name, file) {
2504
2534
  }
2505
2535
  function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
2506
2536
  if (moduleSpecifier.startsWith(".")) {
2507
- const dir = (0, import_node_path14.dirname)(sourceFile.getFilePath());
2537
+ const dir = (0, import_node_path15.dirname)(sourceFile.getFilePath());
2508
2538
  const noExt = moduleSpecifier.replace(/\.(js|ts)$/, "");
2509
2539
  return [
2510
- (0, import_node_path14.resolve)(dir, `${noExt}.ts`),
2511
- (0, import_node_path14.resolve)(dir, `${moduleSpecifier}.ts`),
2512
- (0, import_node_path14.resolve)(dir, moduleSpecifier, "index.ts")
2540
+ (0, import_node_path15.resolve)(dir, `${noExt}.ts`),
2541
+ (0, import_node_path15.resolve)(dir, `${moduleSpecifier}.ts`),
2542
+ (0, import_node_path15.resolve)(dir, moduleSpecifier, "index.ts")
2513
2543
  ];
2514
2544
  }
2515
2545
  const ctx = _ctxFor(project);
@@ -2530,8 +2560,8 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
2530
2560
  const rest = moduleSpecifier.slice(prefix.length);
2531
2561
  const candidates = [];
2532
2562
  for (const mapping of mappings) {
2533
- const resolved = (0, import_node_path14.resolve)(baseUrl, mapping.replace("*", rest));
2534
- candidates.push(`${resolved}.ts`, (0, import_node_path14.resolve)(resolved, "index.ts"));
2563
+ const resolved = (0, import_node_path15.resolve)(baseUrl, mapping.replace("*", rest));
2564
+ candidates.push(`${resolved}.ts`, (0, import_node_path15.resolve)(resolved, "index.ts"));
2535
2565
  }
2536
2566
  dbg(" resolved candidates:", candidates);
2537
2567
  return candidates;
@@ -3421,6 +3451,12 @@ function toFilterFieldType(name, r) {
3421
3451
  }
3422
3452
 
3423
3453
  // src/discovery/filter-for.ts
3454
+ function resolveMixinEntityClass(mixin, project) {
3455
+ const entityArg = mixin?.classArgs[0];
3456
+ if (!entityArg) return void 0;
3457
+ const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
3458
+ return file?.getClass(entityArg.name);
3459
+ }
3424
3460
  function classifyFilterForHint(typeInit) {
3425
3461
  if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
3426
3462
  switch (typeInit.getLiteralValue()) {
@@ -3494,7 +3530,9 @@ function extractFilterForHints(classDecl, project) {
3494
3530
  }
3495
3531
  return hints;
3496
3532
  }
3497
- function extractApplyFilterInfo(method, sourceFile, project) {
3533
+ function extractApplyFilterInfo(method, sourceFile, project, mixin) {
3534
+ const declFile = method.getSourceFile();
3535
+ const mixinEntity = resolveMixinEntityClass(mixin, project);
3498
3536
  for (const param of method.getParameters()) {
3499
3537
  const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
3500
3538
  if (!filterDecorator) continue;
@@ -3514,12 +3552,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
3514
3552
  }
3515
3553
  }
3516
3554
  const filterClassName = filterClassArg.getText();
3517
- const resolved = findType(filterClassName, sourceFile, project);
3518
- if (resolved && resolved.kind === "class") {
3519
- const classDecl = resolved.decl;
3555
+ const resolved = findType(filterClassName, declFile, project);
3556
+ const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
3557
+ if (classDecl) {
3520
3558
  let fieldTypes = extractClassPropertyTypes(classDecl, project);
3521
3559
  if (fieldTypes.length === 0) {
3522
- fieldTypes = extractFilterableEntityFields(classDecl, project);
3560
+ fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
3523
3561
  }
3524
3562
  const filterForHints = extractFilterForHints(classDecl, project);
3525
3563
  if (filterForHints.size > 0) {
@@ -3604,22 +3642,29 @@ function extractClassPropertyTypes(classDecl, project) {
3604
3642
  }
3605
3643
  return fields;
3606
3644
  }
3607
- function extractFilterableEntityFields(filterClass, project) {
3645
+ function resolveLocalClassDeclaration(identifier) {
3646
+ if (!import_ts_morph6.Node.isIdentifier(identifier)) return void 0;
3647
+ return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
3648
+ }
3649
+ function resolveDeclaredEntity(optionsArg, filterClass, project) {
3650
+ if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
3651
+ const entityProp = optionsArg.getProperty("entity");
3652
+ if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return void 0;
3653
+ const entityInit = entityProp.getInitializer();
3654
+ if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return void 0;
3655
+ const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
3656
+ if (!resolved || resolved.kind !== "class") return void 0;
3657
+ return resolved.decl;
3658
+ }
3659
+ function extractFilterableEntityFields(filterClass, project, entityOverride) {
3608
3660
  const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
3609
3661
  if (!filterableDecorator) return [];
3610
3662
  const args = filterableDecorator.getArguments();
3611
3663
  if (args.length === 0) return [];
3612
3664
  const optionsArg = args[0];
3613
3665
  if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
3614
- const entityProp = optionsArg.getProperty("entity");
3615
- if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return [];
3616
- const entityInit = entityProp.getInitializer();
3617
- if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
3618
- const entityName = entityInit.getText();
3619
- const filterSourceFile = filterClass.getSourceFile();
3620
- const resolvedEntity = findType(entityName, filterSourceFile, project);
3621
- if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
3622
- const entityDecl = resolvedEntity.decl;
3666
+ const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
3667
+ if (!entityDecl) return [];
3623
3668
  const fields = collectEntityFields(
3624
3669
  entityDecl,
3625
3670
  entityDecl.getSourceFile(),
@@ -4044,9 +4089,9 @@ function unwrapNamedContainer(node, names) {
4044
4089
  }
4045
4090
  return node;
4046
4091
  }
4047
- function extractDtoContract(method, sourceFile, project) {
4092
+ function extractDtoContract(method, sourceFile, project, mixin) {
4048
4093
  let body = extractBodyType(method, sourceFile, project);
4049
- const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
4094
+ const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
4050
4095
  const query = extractQueryType(method, sourceFile, project);
4051
4096
  const uploads = extractUploadedFiles(method);
4052
4097
  const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
@@ -4156,12 +4201,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
4156
4201
  return null;
4157
4202
  }
4158
4203
 
4159
- // src/discovery/zod-ast-to-ts.ts
4204
+ // src/discovery/heritage.ts
4160
4205
  var import_ts_morph8 = require("ts-morph");
4206
+ function unwrapExpression(node) {
4207
+ let current = node;
4208
+ while (import_ts_morph8.Node.isAsExpression(current) || import_ts_morph8.Node.isSatisfiesExpression(current) || import_ts_morph8.Node.isParenthesizedExpression(current) || import_ts_morph8.Node.isTypeAssertion(current)) {
4209
+ current = current.getExpression();
4210
+ }
4211
+ return current;
4212
+ }
4213
+ function resolveReturnedClass(factoryDecl) {
4214
+ const body = factoryDecl.getBody();
4215
+ if (!body) return void 0;
4216
+ const returns = body.getDescendants().filter((n) => import_ts_morph8.Node.isReturnStatement(n));
4217
+ for (const ret of returns) {
4218
+ const expr = ret.getExpression();
4219
+ if (!expr) continue;
4220
+ const inner = unwrapExpression(expr);
4221
+ if (import_ts_morph8.Node.isClassExpression(inner)) {
4222
+ return inner;
4223
+ }
4224
+ if (import_ts_morph8.Node.isIdentifier(inner)) {
4225
+ const name = inner.getText();
4226
+ const decl = body.getDescendants().find((n) => import_ts_morph8.Node.isClassDeclaration(n) && n.getName() === name);
4227
+ if (decl) return decl;
4228
+ }
4229
+ }
4230
+ return void 0;
4231
+ }
4232
+ function resolveInheritedMethods(cls) {
4233
+ const expr = cls.getExtends()?.getExpression();
4234
+ if (!expr || !import_ts_morph8.Node.isCallExpression(expr)) return void 0;
4235
+ const callee = expr.getExpression();
4236
+ if (!import_ts_morph8.Node.isIdentifier(callee)) return void 0;
4237
+ const factoryDecl = callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isFunctionDeclaration(n));
4238
+ if (!factoryDecl) return void 0;
4239
+ const returnedClass = resolveReturnedClass(factoryDecl);
4240
+ if (!returnedClass) return void 0;
4241
+ const classArgs = [];
4242
+ for (const arg of expr.getArguments()) {
4243
+ if (!import_ts_morph8.Node.isIdentifier(arg)) continue;
4244
+ const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isClassDeclaration(n));
4245
+ if (decl) {
4246
+ classArgs.push({
4247
+ name: decl.getName() ?? arg.getText(),
4248
+ filePath: decl.getSourceFile().getFilePath()
4249
+ });
4250
+ }
4251
+ }
4252
+ return {
4253
+ methods: returnedClass.getMethods(),
4254
+ factoryName: callee.getText(),
4255
+ factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
4256
+ classArgs
4257
+ };
4258
+ }
4259
+ var mixinTypeProject;
4260
+ function getMixinTypeProject() {
4261
+ mixinTypeProject ??= new import_ts_morph8.Project({
4262
+ skipAddingFilesFromTsConfig: true,
4263
+ compilerOptions: { strict: true }
4264
+ });
4265
+ return mixinTypeProject;
4266
+ }
4267
+ function clearMixinTypeProject() {
4268
+ mixinTypeProject = void 0;
4269
+ }
4270
+ function resolveInstantiatedReturnType(cls, methodName) {
4271
+ const filePath = cls.getSourceFile().getFilePath();
4272
+ const className = cls.getName();
4273
+ if (!className) return void 0;
4274
+ const project = getMixinTypeProject();
4275
+ const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPathIfExists(filePath);
4276
+ const typedCls = sourceFile?.getClass(className);
4277
+ if (!typedCls) return void 0;
4278
+ const prop = typedCls.getType().getProperty(methodName);
4279
+ if (!prop) return void 0;
4280
+ const returnType = prop.getTypeAtLocation(typedCls).getCallSignatures()[0]?.getReturnType();
4281
+ if (!returnType) return void 0;
4282
+ const unwrapped = returnType.getSymbol()?.getName() === "Promise" ? returnType.getTypeArguments()[0] ?? returnType : returnType;
4283
+ return unwrapped.getText(typedCls);
4284
+ }
4285
+
4286
+ // src/discovery/zod-ast-to-ts.ts
4287
+ var import_ts_morph9 = require("ts-morph");
4161
4288
  function zodAstToTs(node) {
4162
- if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
4289
+ if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
4163
4290
  const expr = node.getExpression();
4164
- if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
4291
+ if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
4165
4292
  const methodName = expr.getName();
4166
4293
  const receiver = expr.getExpression();
4167
4294
  if (methodName === "optional") {
@@ -4185,17 +4312,17 @@ function zodAstToTs(node) {
4185
4312
  case "literal": {
4186
4313
  const lit = args[0];
4187
4314
  if (!lit) return "unknown";
4188
- if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
4189
- if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
4190
- if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
4191
- if (lit.getKind() === import_ts_morph8.SyntaxKind.FalseKeyword) return "false";
4315
+ if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
4316
+ if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
4317
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
4318
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
4192
4319
  return "unknown";
4193
4320
  }
4194
4321
  case "enum": {
4195
4322
  const arrArg = args[0];
4196
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4323
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4197
4324
  const members = arrArg.getElements().map(
4198
- (el) => import_ts_morph8.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
4325
+ (el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
4199
4326
  );
4200
4327
  return members.join(" | ");
4201
4328
  }
@@ -4206,10 +4333,10 @@ function zodAstToTs(node) {
4206
4333
  }
4207
4334
  case "object": {
4208
4335
  const objArg = args[0];
4209
- if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
4336
+ if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
4210
4337
  const lines = [];
4211
4338
  for (const prop of objArg.getProperties()) {
4212
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
4339
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
4213
4340
  const key = prop.getName();
4214
4341
  const valNode = prop.getInitializer();
4215
4342
  if (!valNode) continue;
@@ -4221,7 +4348,7 @@ function zodAstToTs(node) {
4221
4348
  }
4222
4349
  case "union": {
4223
4350
  const arrArg = args[0];
4224
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4351
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4225
4352
  return arrArg.getElements().map(zodAstToTs).join(" | ");
4226
4353
  }
4227
4354
  case "record": {
@@ -4231,7 +4358,7 @@ function zodAstToTs(node) {
4231
4358
  }
4232
4359
  case "tuple": {
4233
4360
  const arrArg = args[0];
4234
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4361
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4235
4362
  return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
4236
4363
  }
4237
4364
  default:
@@ -4241,18 +4368,18 @@ function zodAstToTs(node) {
4241
4368
  return "unknown";
4242
4369
  }
4243
4370
  function isOptionalChain(node) {
4244
- if (!import_ts_morph8.Node.isCallExpression(node)) return false;
4371
+ if (!import_ts_morph9.Node.isCallExpression(node)) return false;
4245
4372
  const expr = node.getExpression();
4246
- return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
4373
+ return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
4247
4374
  }
4248
4375
  function parseDefineContractCall(callExpr) {
4249
- if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
4376
+ if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
4250
4377
  const callee = callExpr.getExpression();
4251
- const calleeName = import_ts_morph8.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph8.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
4378
+ const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
4252
4379
  if (calleeName !== "defineContract") return null;
4253
4380
  const args = callExpr.getArguments();
4254
4381
  const optsArg = args[0];
4255
- if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
4382
+ if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
4256
4383
  let query = null;
4257
4384
  let body = null;
4258
4385
  let response = "unknown";
@@ -4260,7 +4387,7 @@ function parseDefineContractCall(callExpr) {
4260
4387
  let bodyZodText = null;
4261
4388
  let queryZodText = null;
4262
4389
  for (const prop of optsArg.getProperties()) {
4263
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
4390
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
4264
4391
  const propName = prop.getName();
4265
4392
  const val = prop.getInitializer();
4266
4393
  if (!val) continue;
@@ -4289,21 +4416,22 @@ async function discoverContractsFast(opts) {
4289
4416
  project.addSourceFileAtPath(f);
4290
4417
  }
4291
4418
  bindDiscoveryContext(project, cwd, tsconfigPath);
4419
+ clearMixinTypeProject();
4292
4420
  return extractAllRoutes(project);
4293
4421
  }
4294
4422
  function resolveTsconfigPath(cwd, tsconfig) {
4295
- return tsconfig ? (0, import_node_path15.resolve)(tsconfig) : (0, import_node_path15.join)(cwd, "tsconfig.json");
4423
+ return tsconfig ? (0, import_node_path16.resolve)(tsconfig) : (0, import_node_path16.join)(cwd, "tsconfig.json");
4296
4424
  }
4297
4425
  function createDiscoveryProject(tsconfigPath) {
4298
4426
  try {
4299
- return new import_ts_morph9.Project({
4427
+ return new import_ts_morph10.Project({
4300
4428
  tsConfigFilePath: tsconfigPath,
4301
4429
  skipAddingFilesFromTsConfig: true,
4302
4430
  skipLoadingLibFiles: true,
4303
4431
  skipFileDependencyResolution: true
4304
4432
  });
4305
4433
  } catch {
4306
- return new import_ts_morph9.Project({
4434
+ return new import_ts_morph10.Project({
4307
4435
  skipAddingFilesFromTsConfig: true,
4308
4436
  skipLoadingLibFiles: true,
4309
4437
  skipFileDependencyResolution: true,
@@ -4378,7 +4506,7 @@ var PersistentDiscovery = class _PersistentDiscovery {
4378
4506
  async rediscover(changedPaths) {
4379
4507
  if (changedPaths) {
4380
4508
  for (const p of changedPaths) {
4381
- const abs = (0, import_node_path15.resolve)(p);
4509
+ const abs = (0, import_node_path16.resolve)(p);
4382
4510
  const sf = this.project.getSourceFile(abs);
4383
4511
  if (sf) {
4384
4512
  await sf.refreshFromFileSystem();
@@ -4410,15 +4538,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
4410
4538
  runExtraction() {
4411
4539
  clearTypeResolutionCaches(this.project);
4412
4540
  clearEnumCache(this.project);
4541
+ clearMixinTypeProject();
4413
4542
  return extractRoutesFrom(this.project, this.controllerPaths);
4414
4543
  }
4415
4544
  };
4416
4545
  function decoratorStringArg(decoratorExpr) {
4417
4546
  if (!decoratorExpr) return void 0;
4418
- if (import_ts_morph9.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
4419
- if (import_ts_morph9.Node.isArrayLiteralExpression(decoratorExpr)) {
4547
+ if (import_ts_morph10.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
4548
+ if (import_ts_morph10.Node.isArrayLiteralExpression(decoratorExpr)) {
4420
4549
  const first = decoratorExpr.getElements()[0];
4421
- if (first && import_ts_morph9.Node.isStringLiteral(first)) return first.getLiteralValue();
4550
+ if (first && import_ts_morph10.Node.isStringLiteral(first)) return first.getLiteralValue();
4422
4551
  }
4423
4552
  return void 0;
4424
4553
  }
@@ -4440,7 +4569,8 @@ function joinPaths(prefix, suffix) {
4440
4569
  if (!prefix && !suffix) return "/";
4441
4570
  if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
4442
4571
  if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
4443
- const p = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
4572
+ const withLeadingSlash = prefix.startsWith("/") ? prefix : `/${prefix}`;
4573
+ const p = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
4444
4574
  const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
4445
4575
  const combined = p + s;
4446
4576
  return combined === "" ? "/" : combined;
@@ -4494,7 +4624,8 @@ function buildRoute(args) {
4494
4624
  methodAs,
4495
4625
  sourceFile,
4496
4626
  seenNames,
4497
- contractSource
4627
+ contractSource,
4628
+ mixin
4498
4629
  } = args;
4499
4630
  const routeName = resolveRouteName(className, methodName, classAs, methodAs);
4500
4631
  const qualifiedRef = `${className}.${methodName}`;
@@ -4510,7 +4641,12 @@ function buildRoute(args) {
4510
4641
  path: combinedPath,
4511
4642
  name: routeName,
4512
4643
  params: extractParams(combinedPath),
4513
- controllerRef: { className, methodName, filePath: sourceFile.getFilePath() },
4644
+ controllerRef: {
4645
+ className,
4646
+ methodName,
4647
+ filePath: sourceFile.getFilePath(),
4648
+ ...mixin ? { mixin } : {}
4649
+ },
4514
4650
  contract: { contractSource }
4515
4651
  };
4516
4652
  }
@@ -4524,16 +4660,17 @@ function extractContractRoute(args) {
4524
4660
  className,
4525
4661
  sourceFile,
4526
4662
  project,
4527
- seenNames
4663
+ seenNames,
4664
+ mixin
4528
4665
  } = args;
4529
4666
  const firstDecoratorArg = applyContractDecorator.getArguments()[0];
4530
4667
  if (!firstDecoratorArg) return null;
4531
4668
  let contractDef = null;
4532
4669
  let bodyZodRef = null;
4533
4670
  let queryZodRef = null;
4534
- if (import_ts_morph9.Node.isCallExpression(firstDecoratorArg)) {
4671
+ if (import_ts_morph10.Node.isCallExpression(firstDecoratorArg)) {
4535
4672
  contractDef = parseDefineContractCall(firstDecoratorArg);
4536
- } else if (import_ts_morph9.Node.isIdentifier(firstDecoratorArg)) {
4673
+ } else if (import_ts_morph10.Node.isIdentifier(firstDecoratorArg)) {
4537
4674
  const identName = firstDecoratorArg.getText();
4538
4675
  const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
4539
4676
  if (!resolvedVar) {
@@ -4576,6 +4713,7 @@ function extractContractRoute(args) {
4576
4713
  methodAs,
4577
4714
  sourceFile,
4578
4715
  seenNames,
4716
+ mixin,
4579
4717
  contractSource: {
4580
4718
  query: contractDef.query,
4581
4719
  body: contractDef.body,
@@ -4592,13 +4730,14 @@ function extractContractRoute(args) {
4592
4730
  });
4593
4731
  }
4594
4732
  function extractDtoRoute(args) {
4595
- const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
4733
+ const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
4596
4734
  if (!verb) return null;
4597
4735
  const combined = joinPaths(prefix, verb.handlerPath);
4598
4736
  const methodName = method.getName();
4599
4737
  const classAs = readAsDecorator(cls, `class ${className}`);
4600
4738
  const methodAs = readAsDecorator(method, `${className}.${methodName}`);
4601
- const dtoContract = extractDtoContract(method, sourceFile, project);
4739
+ const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
4740
+ const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
4602
4741
  return buildRoute({
4603
4742
  className,
4604
4743
  methodName,
@@ -4608,10 +4747,11 @@ function extractDtoRoute(args) {
4608
4747
  methodAs,
4609
4748
  sourceFile,
4610
4749
  seenNames,
4750
+ mixin,
4611
4751
  contractSource: {
4612
4752
  query: dtoContract?.query ?? null,
4613
4753
  body: dtoContract?.body ?? null,
4614
- response: dtoContract?.response ?? "unknown",
4754
+ response: mixinResponse ?? dtoContract?.response ?? "unknown",
4615
4755
  error: dtoContract?.error ?? null,
4616
4756
  queryRef: dtoContract?.queryRef ?? null,
4617
4757
  bodyRef: dtoContract?.bodyRef ?? null,
@@ -4640,7 +4780,17 @@ function extractFromSourceFile(sourceFile, project) {
4640
4780
  const firstArg2 = controllerDecorator.getArguments()[0];
4641
4781
  const prefix = decoratorStringArg(firstArg2) ?? "";
4642
4782
  const className = cls.getName() ?? "Unknown";
4643
- for (const method of cls.getMethods()) {
4783
+ const inherited = resolveInheritedMethods(cls);
4784
+ const mixinBinding = inherited ? {
4785
+ factoryName: inherited.factoryName,
4786
+ factoryFilePath: inherited.factoryFilePath,
4787
+ classArgs: inherited.classArgs
4788
+ } : void 0;
4789
+ const methods = [
4790
+ ...cls.getMethods().map((method) => ({ method })),
4791
+ ...(inherited?.methods ?? []).map((method) => ({ method, mixin: mixinBinding }))
4792
+ ];
4793
+ for (const { method, mixin } of methods) {
4644
4794
  const verb = resolveVerb(method);
4645
4795
  const applyContractDecorator = method.getDecorator("ApplyContract");
4646
4796
  const route = applyContractDecorator ? extractContractRoute({
@@ -4652,7 +4802,8 @@ function extractFromSourceFile(sourceFile, project) {
4652
4802
  className,
4653
4803
  sourceFile,
4654
4804
  project,
4655
- seenNames
4805
+ seenNames,
4806
+ mixin
4656
4807
  }) : extractDtoRoute({
4657
4808
  cls,
4658
4809
  method,
@@ -4661,7 +4812,8 @@ function extractFromSourceFile(sourceFile, project) {
4661
4812
  className,
4662
4813
  sourceFile,
4663
4814
  project,
4664
- seenNames
4815
+ seenNames,
4816
+ mixin
4665
4817
  });
4666
4818
  if (route) routes.push(route);
4667
4819
  }
@@ -4672,7 +4824,7 @@ function extractFromSourceFile(sourceFile, project) {
4672
4824
  // src/watch/lock-file.ts
4673
4825
  var import_promises13 = require("fs/promises");
4674
4826
  var import_promises14 = require("fs/promises");
4675
- var import_node_path16 = require("path");
4827
+ var import_node_path17 = require("path");
4676
4828
  var LOCK_FILE2 = ".watcher.lock";
4677
4829
  function isProcessAlive(pid) {
4678
4830
  try {
@@ -4684,7 +4836,7 @@ function isProcessAlive(pid) {
4684
4836
  }
4685
4837
  async function acquireLock(outDir) {
4686
4838
  await (0, import_promises14.mkdir)(outDir, { recursive: true });
4687
- const lockPath = (0, import_node_path16.join)(outDir, LOCK_FILE2);
4839
+ const lockPath = (0, import_node_path17.join)(outDir, LOCK_FILE2);
4688
4840
  const lockData = { pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
4689
4841
  try {
4690
4842
  const fd = await (0, import_promises13.open)(lockPath, "wx");
@@ -4725,7 +4877,7 @@ async function watch(config, onChange, options = {}) {
4725
4877
  if (lock === null) {
4726
4878
  let holderPid = "unknown";
4727
4879
  try {
4728
- const raw = await (0, import_promises15.readFile)((0, import_node_path17.join)(config.codegen.outDir, ".watcher.lock"), "utf8");
4880
+ const raw = await (0, import_promises15.readFile)((0, import_node_path18.join)(config.codegen.outDir, ".watcher.lock"), "utf8");
4729
4881
  const data = JSON.parse(raw);
4730
4882
  if (data.pid !== void 0) holderPid = String(data.pid);
4731
4883
  } catch {
@@ -4778,7 +4930,7 @@ async function watch(config, onChange, options = {}) {
4778
4930
  }
4779
4931
  let pagesDebounceTimer;
4780
4932
  const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
4781
- const pagesWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, pagesGlob), {
4933
+ const pagesWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, pagesGlob), {
4782
4934
  ignoreInitial: true,
4783
4935
  persistent: true,
4784
4936
  awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
@@ -4805,7 +4957,7 @@ async function watch(config, onChange, options = {}) {
4805
4957
  pagesWatcher.on("unlink", schedulePagesRegenerate);
4806
4958
  let contractsDebounceTimer;
4807
4959
  const pendingChangedPaths = /* @__PURE__ */ new Set();
4808
- const contractsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.contracts.glob), {
4960
+ const contractsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.contracts.glob), {
4809
4961
  ignoreInitial: true,
4810
4962
  persistent: true,
4811
4963
  awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
@@ -4835,7 +4987,7 @@ async function watch(config, onChange, options = {}) {
4835
4987
  contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
4836
4988
  contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
4837
4989
  contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
4838
- const formsWatcher = import_chokidar.default.watch((0, import_node_path17.join)(config.codegen.cwd, config.forms.watch), {
4990
+ const formsWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, config.forms.watch), {
4839
4991
  ignoreInitial: true,
4840
4992
  persistent: true,
4841
4993
  awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
@@ -4862,7 +5014,7 @@ async function watch(config, onChange, options = {}) {
4862
5014
  }
4863
5015
 
4864
5016
  // src/index.ts
4865
- var VERSION = "0.15.0";
5017
+ var VERSION = "0.17.0";
4866
5018
 
4867
5019
  // src/cli/codegen.ts
4868
5020
  async function runCodegen(opts = {}) {
@@ -4870,9 +5022,9 @@ async function runCodegen(opts = {}) {
4870
5022
  const config = await loadConfig(cwd);
4871
5023
  if (opts.watch) {
4872
5024
  const watcher = await watch(config, void 0, { entryPoint: "cli" });
4873
- await new Promise((resolve4) => {
5025
+ await new Promise((resolve5) => {
4874
5026
  function onSignal() {
4875
- watcher.close().then(resolve4).catch(resolve4);
5027
+ watcher.close().then(resolve5).catch(resolve5);
4876
5028
  }
4877
5029
  process.once("SIGINT", onSignal);
4878
5030
  process.once("SIGTERM", onSignal);
@@ -4891,13 +5043,13 @@ async function runCodegen(opts = {}) {
4891
5043
  // src/cli/doctor.ts
4892
5044
  var import_node_child_process2 = require("child_process");
4893
5045
  var import_node_fs4 = require("fs");
4894
- var import_node_path19 = require("path");
5046
+ var import_node_path20 = require("path");
4895
5047
 
4896
5048
  // src/cli/init.ts
4897
5049
  var import_node_child_process = require("child_process");
4898
5050
  var import_node_fs3 = require("fs");
4899
5051
  var import_promises16 = require("fs/promises");
4900
- var import_node_path18 = require("path");
5052
+ var import_node_path19 = require("path");
4901
5053
  var import_node_readline = require("readline");
4902
5054
 
4903
5055
  // src/cli/patch-utils.ts
@@ -4959,7 +5111,7 @@ ${bold(title)}`);
4959
5111
  }
4960
5112
  async function readPackageJson(cwd) {
4961
5113
  try {
4962
- const raw = await (0, import_promises16.readFile)((0, import_node_path18.join)(cwd, "package.json"), "utf8");
5114
+ const raw = await (0, import_promises16.readFile)((0, import_node_path19.join)(cwd, "package.json"), "utf8");
4963
5115
  return JSON.parse(raw);
4964
5116
  } catch {
4965
5117
  return {};
@@ -4990,7 +5142,7 @@ async function detectTemplateEngine(cwd) {
4990
5142
  async function detectPackageManager(cwd) {
4991
5143
  async function exists(file) {
4992
5144
  try {
4993
- await (0, import_promises16.access)((0, import_node_path18.join)(cwd, file));
5145
+ await (0, import_promises16.access)((0, import_node_path19.join)(cwd, file));
4994
5146
  return true;
4995
5147
  } catch {
4996
5148
  return false;
@@ -5002,16 +5154,16 @@ async function detectPackageManager(cwd) {
5002
5154
  }
5003
5155
  async function promptFramework() {
5004
5156
  if (!process.stdin.isTTY) return "react";
5005
- return new Promise((resolve4) => {
5157
+ return new Promise((resolve5) => {
5006
5158
  const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
5007
5159
  rl.question(
5008
5160
  "[nestjs-codegen] Which frontend framework? (react/vue/svelte) [react]: ",
5009
5161
  (answer) => {
5010
5162
  rl.close();
5011
5163
  const trimmed = answer.trim().toLowerCase();
5012
- if (trimmed === "vue") resolve4("vue");
5013
- else if (trimmed === "svelte") resolve4("svelte");
5014
- else resolve4("react");
5164
+ if (trimmed === "vue") resolve5("vue");
5165
+ else if (trimmed === "svelte") resolve5("svelte");
5166
+ else resolve5("react");
5015
5167
  }
5016
5168
  );
5017
5169
  });
@@ -5037,7 +5189,7 @@ async function writeIfNotExists(filePath, content, label) {
5037
5189
  logCreated(label);
5038
5190
  }
5039
5191
  async function handleViteConfig(cwd, framework) {
5040
- const filePath = (0, import_node_path18.join)(cwd, "vite.config.ts");
5192
+ const filePath = (0, import_node_path19.join)(cwd, "vite.config.ts");
5041
5193
  if (await fileExists2(filePath)) {
5042
5194
  const existing = await (0, import_promises16.readFile)(filePath, "utf8");
5043
5195
  const hasPlugin = existing.includes("nestInertia") || existing.includes("nestjs-inertia-vite/plugin");
@@ -5095,7 +5247,7 @@ function installDeps(pkgManager, deps, dev) {
5095
5247
  }
5096
5248
  }
5097
5249
  async function patchPackageJsonScripts(cwd, scripts) {
5098
- const pkgPath = (0, import_node_path18.join)(cwd, "package.json");
5250
+ const pkgPath = (0, import_node_path19.join)(cwd, "package.json");
5099
5251
  let pkg = {};
5100
5252
  try {
5101
5253
  pkg = JSON.parse(await (0, import_promises16.readFile)(pkgPath, "utf8"));
@@ -5412,7 +5564,7 @@ export class HomeController {
5412
5564
  }
5413
5565
  `;
5414
5566
  function patchTsconfigExclude(cwd, dir, filename = "tsconfig.json") {
5415
- const filePath = (0, import_node_path18.join)(cwd, filename);
5567
+ const filePath = (0, import_node_path19.join)(cwd, filename);
5416
5568
  return patchJsonFile(
5417
5569
  filePath,
5418
5570
  (json) => {
@@ -5427,7 +5579,7 @@ function patchTsconfigExclude(cwd, dir, filename = "tsconfig.json") {
5427
5579
  );
5428
5580
  }
5429
5581
  function patchNestCliJson(cwd, shellDir) {
5430
- const filePath = (0, import_node_path18.join)(cwd, "nest-cli.json");
5582
+ const filePath = (0, import_node_path19.join)(cwd, "nest-cli.json");
5431
5583
  return patchJsonFile(filePath, (json) => {
5432
5584
  const compiler = json.compilerOptions ?? {};
5433
5585
  const assets = compiler.assets ?? [];
@@ -5450,46 +5602,46 @@ async function scaffoldFiles(ctx) {
5450
5602
  const { cwd, framework, engine, shellFileName, entryExt, pageExt } = ctx;
5451
5603
  logSection("Scaffold files");
5452
5604
  await writeIfNotExists(
5453
- (0, import_node_path18.join)(cwd, "nestjs-inertia.config.ts"),
5605
+ (0, import_node_path19.join)(cwd, "nestjs-inertia.config.ts"),
5454
5606
  configTemplate(framework),
5455
5607
  "nestjs-inertia.config.ts"
5456
5608
  );
5457
- await writeIfNotExists((0, import_node_path18.join)(cwd, "nestjs-inertia.d.ts"), DTS_TEMPLATE, "nestjs-inertia.d.ts");
5609
+ await writeIfNotExists((0, import_node_path19.join)(cwd, "nestjs-inertia.d.ts"), DTS_TEMPLATE, "nestjs-inertia.d.ts");
5458
5610
  await writeIfNotExists(
5459
- (0, import_node_path18.join)(cwd, "tsconfig.inertia.json"),
5611
+ (0, import_node_path19.join)(cwd, "tsconfig.inertia.json"),
5460
5612
  TSCONFIG_INERTIA_TEMPLATE,
5461
5613
  "tsconfig.inertia.json"
5462
5614
  );
5463
5615
  await writeIfNotExists(
5464
- (0, import_node_path18.join)(cwd, "inertia", "tsconfig.json"),
5616
+ (0, import_node_path19.join)(cwd, "inertia", "tsconfig.json"),
5465
5617
  INERTIA_TSCONFIG_TEMPLATE,
5466
5618
  "inertia/tsconfig.json"
5467
5619
  );
5468
5620
  await writeIfNotExists(
5469
- (0, import_node_path18.join)(cwd, "inertia", shellFileName),
5621
+ (0, import_node_path19.join)(cwd, "inertia", shellFileName),
5470
5622
  htmlShellTemplate(framework, engine),
5471
5623
  `inertia/${shellFileName}`
5472
5624
  );
5473
5625
  await handleViteConfig(cwd, framework);
5474
5626
  await writeIfNotExists(
5475
- (0, import_node_path18.join)(cwd, "inertia", "app", `client.${entryExt}`),
5627
+ (0, import_node_path19.join)(cwd, "inertia", "app", `client.${entryExt}`),
5476
5628
  entryPointTemplate(framework),
5477
5629
  `inertia/app/client.${entryExt}`
5478
5630
  );
5479
5631
  await writeIfNotExists(
5480
- (0, import_node_path18.join)(cwd, "inertia", "pages", `Home.${pageExt}`),
5632
+ (0, import_node_path19.join)(cwd, "inertia", "pages", `Home.${pageExt}`),
5481
5633
  samplePageTemplate(framework),
5482
5634
  `inertia/pages/Home.${pageExt}`
5483
5635
  );
5484
5636
  await writeIfNotExists(
5485
- (0, import_node_path18.join)(cwd, "src", "home.controller.ts"),
5637
+ (0, import_node_path19.join)(cwd, "src", "home.controller.ts"),
5486
5638
  SAMPLE_CONTROLLER,
5487
5639
  "src/home.controller.ts"
5488
5640
  );
5489
5641
  }
5490
5642
  function patchServerAppModule(ctx) {
5491
5643
  const { cwd, rootView } = ctx;
5492
- const appModulePath = (0, import_node_path18.join)(cwd, "src", "app.module.ts");
5644
+ const appModulePath = (0, import_node_path19.join)(cwd, "src", "app.module.ts");
5493
5645
  const appModuleResult = patchAppModule(appModulePath, rootView);
5494
5646
  if (appModuleResult === "patched") {
5495
5647
  logPatched("src/app.module.ts", "added InertiaModule.forRoot");
@@ -5503,7 +5655,7 @@ function patchServerAppModule(ctx) {
5503
5655
  }
5504
5656
  }
5505
5657
  function patchServerMainTs(ctx) {
5506
- const mainTsPath = (0, import_node_path18.join)(ctx.cwd, "src", "main.ts");
5658
+ const mainTsPath = (0, import_node_path19.join)(ctx.cwd, "src", "main.ts");
5507
5659
  const mainTsResult = patchMainTs(mainTsPath);
5508
5660
  if (mainTsResult === "patched") {
5509
5661
  logPatched("src/main.ts", "added setupInertiaVite after NestFactory.create");
@@ -5538,7 +5690,7 @@ function patchBuildConfigs(ctx) {
5538
5690
  }
5539
5691
  async function patchGitignoreAndDist(ctx) {
5540
5692
  const { cwd } = ctx;
5541
- await patchGitignore((0, import_node_path18.join)(cwd, ".gitignore"));
5693
+ await patchGitignore((0, import_node_path19.join)(cwd, ".gitignore"));
5542
5694
  const tsconfigDistResult = patchTsconfigExclude(cwd, "dist", "tsconfig.json");
5543
5695
  if (tsconfigDistResult === "patched") {
5544
5696
  logPatched("tsconfig.json", "excluded dist/ from server compilation");
@@ -5644,7 +5796,7 @@ ${green("\u2713")} Setup complete! Run: ${bold("nest start --watch")}
5644
5796
 
5645
5797
  // src/cli/doctor.ts
5646
5798
  function checkFileExists(cwd, file) {
5647
- return (0, import_node_fs4.existsSync)((0, import_node_path19.join)(cwd, file));
5799
+ return (0, import_node_fs4.existsSync)((0, import_node_path20.join)(cwd, file));
5648
5800
  }
5649
5801
  function readJson(path) {
5650
5802
  try {
@@ -5680,15 +5832,15 @@ function writeJsonField(filePath, dotPath, value) {
5680
5832
  }
5681
5833
  function getPackageVersion(cwd, pkg) {
5682
5834
  try {
5683
- const pkgJson = readJson((0, import_node_path19.join)(cwd, "node_modules", pkg, "package.json"));
5835
+ const pkgJson = readJson((0, import_node_path20.join)(cwd, "node_modules", pkg, "package.json"));
5684
5836
  return pkgJson?.version ?? null;
5685
5837
  } catch {
5686
5838
  return null;
5687
5839
  }
5688
5840
  }
5689
5841
  function detectPkgManager(cwd) {
5690
- if ((0, import_node_fs4.existsSync)((0, import_node_path19.join)(cwd, "pnpm-lock.yaml"))) return "pnpm";
5691
- if ((0, import_node_fs4.existsSync)((0, import_node_path19.join)(cwd, "yarn.lock"))) return "yarn";
5842
+ if ((0, import_node_fs4.existsSync)((0, import_node_path20.join)(cwd, "pnpm-lock.yaml"))) return "pnpm";
5843
+ if ((0, import_node_fs4.existsSync)((0, import_node_path20.join)(cwd, "yarn.lock"))) return "yarn";
5692
5844
  return "npm";
5693
5845
  }
5694
5846
  async function runDoctor(opts) {
@@ -5726,7 +5878,7 @@ async function runDoctor(opts) {
5726
5878
  autoFix: () => runInit({ cwd })
5727
5879
  });
5728
5880
  if (foundShellDir) {
5729
- const nestCliPath = (0, import_node_path19.join)(cwd, "nest-cli.json");
5881
+ const nestCliPath = (0, import_node_path20.join)(cwd, "nest-cli.json");
5730
5882
  const nestCli = readJson(nestCliPath);
5731
5883
  const compiler = nestCli?.compilerOptions ?? {};
5732
5884
  const assets = compiler.assets ?? [];
@@ -5765,7 +5917,7 @@ async function runDoctor(opts) {
5765
5917
  fix: "Run: nestjs-codegen codegen",
5766
5918
  autoFix: () => runCodegen({ cwd })
5767
5919
  });
5768
- const tsconfigPath = (0, import_node_path19.join)(cwd, "tsconfig.json");
5920
+ const tsconfigPath = (0, import_node_path20.join)(cwd, "tsconfig.json");
5769
5921
  const tsconfig = readJson(tsconfigPath);
5770
5922
  const paths = tsconfig?.compilerOptions?.paths;
5771
5923
  checks.push({
@@ -5776,7 +5928,7 @@ async function runDoctor(opts) {
5776
5928
  });
5777
5929
  const inertiaDir = foundShellDir ?? "inertia";
5778
5930
  for (const tsconfigFile of ["tsconfig.json", "tsconfig.build.json"]) {
5779
- const tsc = readJson((0, import_node_path19.join)(cwd, tsconfigFile));
5931
+ const tsc = readJson((0, import_node_path20.join)(cwd, tsconfigFile));
5780
5932
  if (!tsc) continue;
5781
5933
  const excl = tsc.exclude ?? [];
5782
5934
  const excludesIt = excl.includes(inertiaDir);
@@ -5802,7 +5954,7 @@ async function runDoctor(opts) {
5802
5954
  }
5803
5955
  });
5804
5956
  }
5805
- const inertiaTsconfigPath = (0, import_node_path19.join)(cwd, "tsconfig.inertia.json");
5957
+ const inertiaTsconfigPath = (0, import_node_path20.join)(cwd, "tsconfig.inertia.json");
5806
5958
  const inertiaTsconfig = readJson(inertiaTsconfigPath);
5807
5959
  checks.push({
5808
5960
  name: "tsconfig.inertia.json exists",
@@ -5854,7 +6006,7 @@ async function runDoctor(opts) {
5854
6006
  fix: 'Add "nestjs-inertia.d.ts" to include array (resolves InertiaRegistry augmentation)'
5855
6007
  });
5856
6008
  }
5857
- const innerTsconfigPath = (0, import_node_path19.join)(cwd, "inertia", "tsconfig.json");
6009
+ const innerTsconfigPath = (0, import_node_path20.join)(cwd, "inertia", "tsconfig.json");
5858
6010
  checks.push({
5859
6011
  name: "inertia/tsconfig.json exists (VSCode picks up ~codegen alias)",
5860
6012
  pass: (0, import_node_fs4.existsSync)(innerTsconfigPath),
@@ -5864,7 +6016,7 @@ async function runDoctor(opts) {
5864
6016
  }
5865
6017
  });
5866
6018
  if (checkFileExists(cwd, "vite.config.ts")) {
5867
- const viteContent = (0, import_node_fs4.readFileSync)((0, import_node_path19.join)(cwd, "vite.config.ts"), "utf8");
6019
+ const viteContent = (0, import_node_fs4.readFileSync)((0, import_node_path20.join)(cwd, "vite.config.ts"), "utf8");
5868
6020
  checks.push({
5869
6021
  name: "vite.config.ts has resolve.alias",
5870
6022
  pass: viteContent.includes("resolve") && viteContent.includes("alias"),
@@ -5929,7 +6081,7 @@ async function runDoctor(opts) {
5929
6081
  });
5930
6082
  }
5931
6083
  if (checkFileExists(cwd, ".gitignore")) {
5932
- const gitignorePath = (0, import_node_path19.join)(cwd, ".gitignore");
6084
+ const gitignorePath = (0, import_node_path20.join)(cwd, ".gitignore");
5933
6085
  const gitignore = (0, import_node_fs4.readFileSync)(gitignorePath, "utf8");
5934
6086
  checks.push({
5935
6087
  name: ".gitignore includes .nestjs-inertia/",
@@ -5938,7 +6090,7 @@ async function runDoctor(opts) {
5938
6090
  autoFix: () => (0, import_node_fs4.appendFileSync)(gitignorePath, "\n.nestjs-inertia/\n")
5939
6091
  });
5940
6092
  }
5941
- const pkgJsonPath = (0, import_node_path19.join)(cwd, "package.json");
6093
+ const pkgJsonPath = (0, import_node_path20.join)(cwd, "package.json");
5942
6094
  const pkgJson = readJson(pkgJsonPath);
5943
6095
  const scripts = pkgJson?.scripts ?? {};
5944
6096
  checks.push({