@lzehrung/codegraph 2.3.19 → 2.3.20

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.
@@ -309,7 +309,9 @@ function createIndexBuildRunState(projectRoot, opts, graphOptions = normalizeGra
309
309
  normalizedProjectRoot: normalizePath(path.resolve(projectRoot)),
310
310
  report,
311
311
  timings: report?.timings,
312
- totalStart: performance.now(),
312
+ // Delegated rebuilds mint a fresh run state after the preamble; reuse the caller's
313
+ // origin so totalMs covers the whole wait, matching progress completion.
314
+ totalStart: opts?.progressStartedAt ?? performance.now(),
313
315
  cacheMode,
314
316
  cacheEnabled: cacheMode !== "off",
315
317
  graphOptions,
@@ -397,6 +399,16 @@ const TRANSIENT_BUILD_TIMING_STEP_NAMES = {
397
399
  "persist-cache": true,
398
400
  "workspace-manifests": true,
399
401
  "index-manifest": true,
402
+ "file-identity": true,
403
+ "clear-resolution-caches": true,
404
+ "load-manifest": true,
405
+ "diff-build-options": true,
406
+ "git-head": true,
407
+ "config-hash": true,
408
+ "manifest-transform": true,
409
+ "manifest-write": true,
410
+ "cache-prune": true,
411
+ "metadata-discovery": true,
400
412
  finalize: true,
401
413
  "snapshot-write": true,
402
414
  };
@@ -432,6 +444,12 @@ function recordBuildTimingStep(timings, step) {
432
444
  if (step.name === "filesystem-scan")
433
445
  timings.filesystemScanMs = step.ms;
434
446
  }
447
+ async function recordTimedBuildStep(timings, name, fn) {
448
+ const startedAt = performance.now();
449
+ const result = await fn();
450
+ recordBuildTimingStep(timings, { name, ms: Math.round(performance.now() - startedAt) });
451
+ return result;
452
+ }
435
453
  function buildConcurrency(opts) {
436
454
  return resolveWorkerThreadCount({ requested: Number(opts?.threads || 0), defaultCount: 8, max: 64 });
437
455
  }
@@ -891,6 +909,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
891
909
  files: manifestEntries,
892
910
  timings,
893
911
  manifestReport: report?.manifest,
912
+ ...(helperOpts?.configHash ? { configHash: helperOpts.configHash } : {}),
894
913
  ...(helperOpts?.transientFiles !== undefined ? { transientFiles: helperOpts.transientFiles } : {}),
895
914
  ...(helperOpts?.symlinkDirectories !== undefined
896
915
  ? { symlinkDirectories: helperOpts.symlinkDirectories }
@@ -904,29 +923,31 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
904
923
  return [file, { ...entry, ...(cacheSig ? { cacheSig } : {}) }];
905
924
  }))
906
925
  : manifestEntriesForIndex;
907
- const index = await timeIndexBuildPhase({
926
+ const discoveredInsideFinalize = projectFiles === undefined;
927
+ const finalizeStartedAt = performance.now();
928
+ if (buildStartedAt !== undefined) {
929
+ emitIndexBuildActivity(opts, "Finalizing project graph", totalFiles, totalFiles);
930
+ }
931
+ const index = await finalizeProjectIndex({
932
+ projectRoot,
933
+ normalizedProjectRoot,
908
934
  opts,
909
935
  timings,
910
- buildStartedAt,
911
- stepName: "finalize",
912
- activity: "Finalizing project graph",
913
- current: totalFiles,
914
- total: totalFiles,
915
- fn: () => finalizeProjectIndex({
916
- projectRoot,
917
- normalizedProjectRoot,
918
- opts,
919
- timings,
920
- totalStart,
921
- graph,
922
- modules,
923
- parsedMap,
924
- bloomFilterCache,
925
- ...(projectFiles !== undefined ? { projectFiles } : {}),
926
- buildReport: report,
927
- manifestEntries: indexManifestEntries,
928
- }),
936
+ totalStart,
937
+ graph,
938
+ modules,
939
+ parsedMap,
940
+ bloomFilterCache,
941
+ ...(projectFiles !== undefined ? { projectFiles } : {}),
942
+ buildReport: report,
943
+ manifestEntries: indexManifestEntries,
929
944
  });
945
+ if (timings) {
946
+ const elapsed = Math.round(performance.now() - finalizeStartedAt);
947
+ const discoveryMs = discoveredInsideFinalize ? (timings.metadataDiscoveryMs ?? 0) : 0;
948
+ const finalizeMs = discoveredInsideFinalize ? Math.max(0, elapsed - discoveryMs) : elapsed;
949
+ recordBuildTimingStep(timings, { name: "finalize", ms: finalizeMs });
950
+ }
930
951
  if (manifestEntries) {
931
952
  await timeIndexBuildPhase({
932
953
  opts,
@@ -955,7 +976,6 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
955
976
  }
956
977
  async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpts) {
957
978
  const timings = opts?.report ? (opts.report.timings ??= {}) : undefined;
958
- resetTransientBuildTimings(timings);
959
979
  await initializeFileIdentityCaseSensitivity(projectRoot);
960
980
  try {
961
981
  const useDiskCache = (opts?.cache ?? "off") === "disk";
@@ -1032,6 +1052,7 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
1032
1052
  warnNoFilesMessage: `Warning: No files found in project root: ${projectRoot}. Check codegraph.config.json globs and CLI --include-glob/--ignore-glob filters. Diagnostic: codegraph doctor`,
1033
1053
  ...(helperOpts?.ignoreExistingManifest ? { ignoreExistingManifest: true } : {}),
1034
1054
  ...(helperOpts?.reportDiscoveryProgress ? { reportDiscoveryProgress: true } : {}),
1055
+ ...(helperOpts?.configHash ? { configHash: helperOpts.configHash } : {}),
1035
1056
  projectFiles,
1036
1057
  transientFiles,
1037
1058
  ...(discoveredSymlinkDirectories !== undefined ? { symlinkDirectories: discoveredSymlinkDirectories } : {}),
@@ -1056,9 +1077,12 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
1056
1077
  * discovery, so a caller rendering progress reports the wait the user observed.
1057
1078
  */
1058
1079
  export async function buildProjectIndex(projectRoot, opts) {
1080
+ const progressStartedAt = opts?.progressStartedAt ?? performance.now();
1081
+ const timings = opts?.report ? (opts.report.timings ??= {}) : undefined;
1082
+ resetTransientBuildTimings(timings);
1059
1083
  return buildProjectIndexWithManifestOptions(projectRoot, {
1060
1084
  ...opts,
1061
- progressStartedAt: opts?.progressStartedAt ?? performance.now(),
1085
+ progressStartedAt,
1062
1086
  });
1063
1087
  }
1064
1088
  /**
@@ -1072,6 +1096,10 @@ export async function buildProjectIndexFromFiles(projectRoot, inputFiles, rawOpt
1072
1096
  // Discovery, manifest loading, and cache checks all precede file processing, so stamp the
1073
1097
  // operation origin here and let the completion event report the caller's whole wait.
1074
1098
  const opts = { ...rawOpts, progressStartedAt: rawOpts?.progressStartedAt ?? performance.now() };
1099
+ // A caller can reuse one BuildReport across repeated calls here, the same way an MCP
1100
+ // session reuses `buildOptions.report`, so clear the prior call's transient steps.
1101
+ const timings = opts.report ? (opts.report.timings ??= {}) : undefined;
1102
+ resetTransientBuildTimings(timings);
1075
1103
  try {
1076
1104
  const useDiskCache = (opts?.cache ?? "off") === "disk";
1077
1105
  const normalizedInputFiles = await normalizeIndexedFileInputsWithinRoot(projectRoot, inputFiles, "Index file");
@@ -1122,15 +1150,17 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1122
1150
  ...rawOpts,
1123
1151
  progressStartedAt: rawOpts?.progressStartedAt ?? performance.now(),
1124
1152
  };
1125
- await initializeFileIdentityCaseSensitivity(projectRoot);
1126
- clearResolutionCaches();
1153
+ const discoveryTimings = opts.report ? (opts.report.timings ??= {}) : undefined;
1154
+ resetTransientBuildTimings(discoveryTimings);
1155
+ await recordTimedBuildStep(discoveryTimings, "file-identity", () => initializeFileIdentityCaseSensitivity(projectRoot));
1156
+ await recordTimedBuildStep(discoveryTimings, "clear-resolution-caches", () => {
1157
+ clearResolutionCaches();
1158
+ });
1127
1159
  const graphOptions = normalizeGraphOptions(opts?.graph);
1128
1160
  const strictIncremental = opts?.incrementalStrict ?? false;
1129
1161
  if (strictIncremental && graphOptions.fast)
1130
1162
  graphOptions.fast = false;
1131
1163
  const { normalizedProjectRoot, report, timings, totalStart, cacheMode, cacheEnabled, onFallbackImportExtraction } = createIndexBuildRunState(projectRoot, opts, graphOptions);
1132
- const discoveryTimings = opts?.report ? (opts.report.timings ??= {}) : undefined;
1133
- resetTransientBuildTimings(discoveryTimings);
1134
1164
  let checkProgressActive = false;
1135
1165
  const startCheckProgress = () => {
1136
1166
  if (checkProgressActive)
@@ -1176,14 +1206,14 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1176
1206
  }
1177
1207
  startCheckProgress();
1178
1208
  const manifestStart = performance.now();
1179
- const manifest = await loadManifest(projectRoot, opts, report);
1209
+ const manifest = await recordTimedBuildStep(timings, "load-manifest", () => loadManifest(projectRoot, opts, report));
1180
1210
  if (timings)
1181
1211
  timings.manifestMs = Math.round(performance.now() - manifestStart);
1182
1212
  const manifestUsed = !!manifest;
1183
1213
  const manifestReport = initManifestReport(report, manifestUsed, false);
1184
1214
  if (manifestReport && !manifestUsed)
1185
1215
  manifestReport.reason = "missing";
1186
- const optionDiffs = diffBuildOptions(manifest?.buildOptions, opts);
1216
+ const optionDiffs = await recordTimedBuildStep(timings, "diff-build-options", () => diffBuildOptions(manifest?.buildOptions, opts));
1187
1217
  const warningOptionDiffs = optionDiffs.filter((diff) => diff !== "cache");
1188
1218
  if (manifest && warningOptionDiffs.length) {
1189
1219
  logWithLevel(opts?.logLevel, "warn", `Warning: Manifest options differ from current build options: ${warningOptionDiffs.join(", ")}`);
@@ -1191,7 +1221,7 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1191
1221
  if (manifestReport && optionDiffs.length) {
1192
1222
  manifestReport.optionsMismatch = optionDiffs;
1193
1223
  }
1194
- const currentConfigHashResult = await computeConfigHash(projectRoot, opts?.logLevel);
1224
+ const currentConfigHashResult = await recordTimedBuildStep(timings, "config-hash", () => computeConfigHash(projectRoot, opts?.logLevel));
1195
1225
  const currentConfigHash = recordConfigHashResult(manifestReport, currentConfigHashResult, opts?.logLevel);
1196
1226
  const configChanged = !!currentConfigHashResult.error || !manifest?.configHash || currentConfigHash !== manifest.configHash;
1197
1227
  let resolverEnvironmentFingerprint;
@@ -1229,6 +1259,7 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1229
1259
  return await buildProjectIndexFromExport(projectRoot, opts, {
1230
1260
  ignoreExistingManifest: true,
1231
1261
  reportDiscoveryProgress: true,
1262
+ configHash: currentConfigHashResult,
1232
1263
  });
1233
1264
  }
1234
1265
  const gitAvailable = await isGitRepo(projectRoot);
@@ -1282,6 +1313,7 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1282
1313
  const rebuiltIndex = await buildProjectIndexFromExport(projectRoot, opts, {
1283
1314
  ignoreExistingManifest: true,
1284
1315
  reportDiscoveryProgress: true,
1316
+ configHash: currentConfigHashResult,
1285
1317
  });
1286
1318
  if (manifestReport) {
1287
1319
  manifestReport.reason = "staleGitCommit";
@@ -1303,6 +1335,7 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1303
1335
  return await buildProjectIndexFromExport(projectRoot, opts, {
1304
1336
  ignoreExistingManifest: true,
1305
1337
  reportDiscoveryProgress: true,
1338
+ configHash: currentConfigHashResult,
1306
1339
  });
1307
1340
  }
1308
1341
  }
@@ -1356,6 +1389,7 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1356
1389
  return await buildProjectIndexFromExport(projectRoot, opts, {
1357
1390
  ignoreExistingManifest: true,
1358
1391
  reportDiscoveryProgress: true,
1392
+ configHash: currentConfigHashResult,
1359
1393
  });
1360
1394
  }
1361
1395
  }
@@ -1417,17 +1451,27 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1417
1451
  const dependentFilesOfDeletedTracked = collectDeletedTrackedFileDependents(trackedEntries, deletedTrackedFiles);
1418
1452
  if (allFiles.size === 0) {
1419
1453
  completeCheckProgress(0);
1420
- await writeIndexManifestSnapshot({
1421
- projectRoot,
1454
+ await timeIndexBuildPhase({
1422
1455
  opts,
1423
- graphOptions,
1424
- ...(resolverEnvironmentFingerprint ? { resolverEnvironmentFingerprint } : {}),
1425
- files: {},
1426
1456
  timings,
1427
- manifestReport,
1428
- allowEmpty: true,
1429
- transientFiles,
1430
- ...(manifest.symlinkDirectories !== undefined ? { symlinkDirectories: manifest.symlinkDirectories } : {}),
1457
+ buildStartedAt: undefined,
1458
+ stepName: "index-manifest",
1459
+ activity: "Writing index manifest",
1460
+ current: 0,
1461
+ total: 0,
1462
+ fn: () => writeIndexManifestSnapshot({
1463
+ projectRoot,
1464
+ opts,
1465
+ graphOptions,
1466
+ ...(resolverEnvironmentFingerprint ? { resolverEnvironmentFingerprint } : {}),
1467
+ files: {},
1468
+ timings,
1469
+ manifestReport,
1470
+ allowEmpty: true,
1471
+ transientFiles,
1472
+ configHash: currentConfigHashResult,
1473
+ ...(manifest.symlinkDirectories !== undefined ? { symlinkDirectories: manifest.symlinkDirectories } : {}),
1474
+ }),
1431
1475
  });
1432
1476
  return {
1433
1477
  graph: { nodes: new Set(), edges: [] },
@@ -1837,16 +1881,27 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1837
1881
  ...(signature.gitSig ? { gitSig: signature.gitSig } : {}),
1838
1882
  });
1839
1883
  }
1840
- await writeIndexManifestSnapshot({
1841
- projectRoot,
1884
+ await timeIndexBuildPhase({
1842
1885
  opts,
1843
- graphOptions,
1844
- ...(resolverEnvironmentFingerprint ? { resolverEnvironmentFingerprint } : {}),
1845
- files: manifestEntries,
1846
1886
  timings,
1847
- manifestReport,
1848
- transientFiles,
1849
- ...(manifest.symlinkDirectories !== undefined ? { symlinkDirectories: manifest.symlinkDirectories } : {}),
1887
+ buildStartedAt: updateStartedAt,
1888
+ stepName: "index-manifest",
1889
+ activity: "Writing index manifest",
1890
+ current: allFiles.size,
1891
+ total: allFiles.size,
1892
+ mode: "update",
1893
+ fn: () => writeIndexManifestSnapshot({
1894
+ projectRoot,
1895
+ opts,
1896
+ graphOptions,
1897
+ ...(resolverEnvironmentFingerprint ? { resolverEnvironmentFingerprint } : {}),
1898
+ files: manifestEntries,
1899
+ timings,
1900
+ manifestReport,
1901
+ transientFiles,
1902
+ configHash: currentConfigHashResult,
1903
+ ...(manifest.symlinkDirectories !== undefined ? { symlinkDirectories: manifest.symlinkDirectories } : {}),
1904
+ }),
1850
1905
  });
1851
1906
  const index = await finalizeProjectIndex({
1852
1907
  projectRoot,