gitnexus 1.6.11-rc.2 → 1.6.11-rc.21
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/README.md +43 -1
- package/dist/_shared/impact-risk.d.ts +37 -0
- package/dist/_shared/impact-risk.d.ts.map +1 -0
- package/dist/_shared/impact-risk.js +92 -0
- package/dist/_shared/impact-risk.js.map +1 -0
- package/dist/_shared/index.d.ts +2 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +2 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/cli/ai-context.js +4 -4
- package/dist/cli/analyze-config.d.ts +2 -0
- package/dist/cli/analyze-config.js +16 -0
- package/dist/cli/analyze-options.d.ts +4 -0
- package/dist/cli/analyze.d.ts +17 -0
- package/dist/cli/analyze.js +43 -7
- package/dist/cli/eval-server.js +22 -5
- package/dist/cli/group.js +7 -1
- package/dist/cli/help-i18n.js +2 -0
- package/dist/cli/i18n/en.d.ts +12 -2
- package/dist/cli/i18n/en.js +12 -2
- package/dist/cli/i18n/resources.d.ts +22 -2
- package/dist/cli/i18n/zh-CN.d.ts +10 -0
- package/dist/cli/i18n/zh-CN.js +12 -2
- package/dist/cli/index.js +11 -4
- package/dist/cli/status.js +91 -6
- package/dist/cli/watch-queue.d.ts +41 -0
- package/dist/cli/watch-queue.js +184 -0
- package/dist/cli/watch.d.ts +20 -0
- package/dist/cli/watch.js +372 -0
- package/dist/cli/wiki.js +15 -2
- package/dist/config/ignore-service.d.ts +11 -0
- package/dist/config/ignore-service.js +41 -4
- package/dist/config/repo-control-file.d.ts +3 -0
- package/dist/config/repo-control-file.js +115 -0
- package/dist/core/group/config-parser.js +20 -2
- package/dist/core/group/cross-impact.d.ts +2 -1
- package/dist/core/group/cross-impact.js +33 -2
- package/dist/core/group/extractors/fs-utils.d.ts +2 -0
- package/dist/core/group/extractors/fs-utils.js +86 -0
- package/dist/core/group/extractors/graphql-extractor.d.ts +17 -0
- package/dist/core/group/extractors/graphql-extractor.js +652 -0
- package/dist/core/group/extractors/java-workspace-extractor.js +244 -31
- package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
- package/dist/core/group/extractors/manifest-extractor.js +1 -1
- package/dist/core/group/matching.js +8 -1
- package/dist/core/group/service.js +5 -1
- package/dist/core/group/storage.js +1 -0
- package/dist/core/group/sync.d.ts +3 -1
- package/dist/core/group/sync.js +43 -5
- package/dist/core/group/types.d.ts +19 -5
- package/dist/core/incremental/derived-writeback.d.ts +36 -0
- package/dist/core/incremental/derived-writeback.js +68 -0
- package/dist/core/incremental/subgraph-extract.d.ts +6 -4
- package/dist/core/incremental/subgraph-extract.js +7 -5
- package/dist/core/index-content-drift.d.ts +54 -0
- package/dist/core/index-content-drift.js +127 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +15 -5
- package/dist/core/ingestion/filesystem-walker.js +20 -3
- package/dist/core/ingestion/frameworks/spring/dynamic-lookups.d.ts +22 -0
- package/dist/core/ingestion/frameworks/spring/dynamic-lookups.js +124 -0
- package/dist/core/ingestion/language-provider.d.ts +43 -0
- package/dist/core/ingestion/languages/csharp/razor-view-components.d.ts +62 -0
- package/dist/core/ingestion/languages/csharp/razor-view-components.js +954 -0
- package/dist/core/ingestion/languages/csharp/resolution-config.d.ts +3 -0
- package/dist/core/ingestion/languages/csharp/resolution-config.js +6 -1
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -0
- package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +4 -0
- package/dist/core/ingestion/languages/java/capture-side-channel.js +16 -0
- package/dist/core/ingestion/languages/java/captures.js +14 -1
- package/dist/core/ingestion/languages/java/lombok-synthesizer.d.ts +42 -0
- package/dist/core/ingestion/languages/java/lombok-synthesizer.js +439 -0
- package/dist/core/ingestion/languages/java/scope-resolver.js +2 -0
- package/dist/core/ingestion/languages/java/spring-dynamic-lookup.d.ts +8 -0
- package/dist/core/ingestion/languages/java/spring-dynamic-lookup.js +62 -0
- package/dist/core/ingestion/languages/java.js +2 -0
- package/dist/core/ingestion/languages/jvm/accessor-synthesis.d.ts +99 -0
- package/dist/core/ingestion/languages/jvm/accessor-synthesis.js +173 -0
- package/dist/core/ingestion/languages/jvm/beanspec.d.ts +17 -0
- package/dist/core/ingestion/languages/jvm/beanspec.js +42 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +5 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +16 -0
- package/dist/core/ingestion/languages/kotlin/captures.js +14 -1
- package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.d.ts +26 -0
- package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.js +417 -0
- package/dist/core/ingestion/languages/kotlin/scope-resolver.js +2 -0
- package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.d.ts +8 -0
- package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.js +77 -0
- package/dist/core/ingestion/languages/kotlin.js +2 -0
- package/dist/core/ingestion/pipeline-phases/di.js +47 -14
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +3 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +57 -86
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +4 -1
- package/dist/core/ingestion/pipeline-phases/runner.js +34 -14
- package/dist/core/ingestion/pipeline-phases/scan.js +25 -13
- package/dist/core/ingestion/pipeline.d.ts +6 -0
- package/dist/core/ingestion/pipeline.js +44 -12
- package/dist/core/ingestion/scope-extractor.js +1 -0
- package/dist/core/ingestion/utils/symbol-labels.d.ts +2 -2
- package/dist/core/ingestion/utils/symbol-labels.js +2 -2
- package/dist/core/ingestion/workers/parse-worker.js +28 -2
- package/dist/core/lbug/lbug-adapter.d.ts +59 -0
- package/dist/core/lbug/lbug-adapter.js +154 -1
- package/dist/core/run-analyze.d.ts +17 -0
- package/dist/core/run-analyze.js +231 -23
- package/dist/core/search/fts-indexes.d.ts +19 -1
- package/dist/core/search/fts-indexes.js +28 -1
- package/dist/core/wiki/generator.js +8 -0
- package/dist/core/wiki/grok-client.d.ts +21 -0
- package/dist/core/wiki/grok-client.js +287 -0
- package/dist/core/wiki/llm-client.d.ts +1 -1
- package/dist/core/wiki/llm-client.js +5 -2
- package/dist/core/wiki/local-cli-client.d.ts +11 -0
- package/dist/core/wiki/local-cli-client.js +22 -9
- package/dist/mcp/local/local-backend.d.ts +24 -7
- package/dist/mcp/local/local-backend.js +190 -81
- package/dist/mcp/local/pdg-impact.d.ts +8 -4
- package/dist/mcp/local/pdg-impact.js +7 -2
- package/dist/mcp/repository-policy.d.ts +5 -1
- package/dist/mcp/repository-policy.js +48 -4
- package/dist/mcp/resources.js +2 -1
- package/dist/mcp/server.js +6 -5
- package/dist/mcp/tools.js +31 -19
- package/dist/server/api.js +23 -64
- package/dist/server/grep-params.d.ts +18 -0
- package/dist/server/grep-params.js +83 -0
- package/dist/server/grep-scan.d.ts +23 -0
- package/dist/server/grep-scan.js +107 -0
- package/dist/server/grep-worker.d.ts +1 -0
- package/dist/server/grep-worker.js +12 -0
- package/dist/server/mcp-http.d.ts +8 -0
- package/dist/server/mcp-http.js +16 -1
- package/dist/storage/file-hash.d.ts +5 -0
- package/dist/storage/file-hash.js +16 -6
- package/dist/storage/fs-atomic.d.ts +24 -0
- package/dist/storage/fs-atomic.js +86 -2
- package/dist/storage/git.d.ts +19 -8
- package/dist/storage/git.js +83 -24
- package/dist/storage/gitnexus-managed-paths.d.ts +36 -0
- package/dist/storage/gitnexus-managed-paths.js +46 -0
- package/dist/storage/parse-cache.d.ts +22 -4
- package/dist/storage/parse-cache.js +106 -29
- package/dist/storage/parsedfile-store.d.ts +34 -60
- package/dist/storage/parsedfile-store.js +177 -171
- package/dist/storage/repo-manager.d.ts +11 -1
- package/dist/storage/repo-manager.js +23 -2
- package/dist/storage/repo-meta.d.ts +12 -0
- package/dist/storage/v8-sidecar.d.ts +48 -0
- package/dist/storage/v8-sidecar.js +347 -0
- package/dist/types/pipeline.d.ts +14 -0
- package/package.json +4 -1
- package/scripts/cross-platform-shard.ts +4 -2
- package/scripts/cross-platform-tests.ts +7 -1
- package/skills/gitnexus-cli.md +11 -3
- package/skills/gitnexus-impact-analysis.md +9 -0
- package/web/assets/{agent-Dr4l5EOp.js → agent-CFqT4hjR.js} +108 -104
- package/web/assets/{index-2zdvEdzg.js → index-BMIniRtX.js} +3 -3
- package/web/index.html +1 -1
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* does is skip the tree-sitter worker dispatch when a chunk's contents
|
|
7
7
|
* haven't changed since the last run.
|
|
8
8
|
*
|
|
9
|
-
* Granularity: chunk-level.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Granularity: chunk-level. Files are assigned to a stable
|
|
10
|
+
* `(language, hash(path) mod 128)` bucket, then packed to a 2 MiB (or
|
|
11
|
+
* operator) byte budget *inside* that bucket. Membership does not depend
|
|
12
|
+
* on worker count. The cache key is `sha256(joined(filePath:contentHash
|
|
13
|
+
* for each file in the chunk, sorted))`. A content edit invalidates only
|
|
14
|
+
* that file's pack; add/delete/rename only the affected bucket.
|
|
13
15
|
*
|
|
14
16
|
* Why not per-file:
|
|
15
17
|
* - Workers process sub-batches and emit aggregated `ParseWorkerResult`s.
|
|
@@ -26,6 +28,8 @@ import { createRequire } from 'module';
|
|
|
26
28
|
import fs from 'fs/promises';
|
|
27
29
|
import path from 'path';
|
|
28
30
|
import { fileURLToPath } from 'url';
|
|
31
|
+
import { compareCodeUnits } from '../lib/utils.js';
|
|
32
|
+
import { copyV8CacheIfPresent, tryLoadV8Cache, writeV8CacheFile } from './v8-sidecar.js';
|
|
29
33
|
/**
|
|
30
34
|
* Cache version composed of:
|
|
31
35
|
* - A schema bump knob (`SCHEMA_BUMP`) for hand-controlled invalidation
|
|
@@ -627,6 +631,16 @@ import { fileURLToPath } from 'url';
|
|
|
627
631
|
// therefore takes 79, the next free value above origin/main and every open PR
|
|
628
632
|
// found by the contents-API scan at their exact head SHAs.
|
|
629
633
|
//
|
|
634
|
+
// 79 -> 80 for #3088: parse-cache membership is `(language, sha256(path) mod
|
|
635
|
+
// 128)` then the byte budget *inside* that bucket. Worker count is no longer a
|
|
636
|
+
// membership input, so a warm v79 cache keyed sequential scan-order packs (and
|
|
637
|
+
// on multi-worker hosts, pool×2 MiB mega-chunks) must miss. Sidecar-era
|
|
638
|
+
// ParsedFile stores (#3086/#3087) share PARSE_CACHE_VERSION, so both stores
|
|
639
|
+
// invalidate in lockstep. origin/main at allocation is 79; open PRs that still
|
|
640
|
+
// touch gitnexus/src/storage/parse-cache.ts claim 78 (#3060), 71 (#2840), and
|
|
641
|
+
// 2 (#1616) — none claim 80. RE-CHECK AGAINST origin/main AND OPEN PRs
|
|
642
|
+
// IMMEDIATELY BEFORE MERGING.
|
|
643
|
+
//
|
|
630
644
|
// WHY THIS IS STILL A HAND-PICKED NUMBER, when `SCHEMA_FINGERPRINT` next door
|
|
631
645
|
// is a derived sha256 that cannot collide. The derivation exists and already
|
|
632
646
|
// runs: `resolveAnalyzerRunnerIdentity` computes `build.digest` over the
|
|
@@ -645,7 +659,28 @@ import { fileURLToPath } from 'url';
|
|
|
645
659
|
// `route-extractors/` and `workers/` module content — would close the missing-
|
|
646
660
|
// bump axis without invalidating on unrelated churn, and is the real follow-up.
|
|
647
661
|
// RE-CHECK AGAINST origin/main AND OPEN PRs IMMEDIATELY BEFORE MERGING.
|
|
648
|
-
|
|
662
|
+
// 80 -> 81: ParsedFile and parse-cache shards are one immutable `.v8` envelope
|
|
663
|
+
// each (no JSON/path/generation siblings). A v80 index still names `.json`
|
|
664
|
+
// keys and would skip workers while scope-resolution found nothing — the
|
|
665
|
+
// #1983 main-thread reparse. origin/main at allocation is 80.
|
|
666
|
+
// 81 -> 82: Java and Kotlin ParsedFile capture side channels now carry
|
|
667
|
+
// programmatic Spring lookup facts. A warm v81 cache has no such facts, so it
|
|
668
|
+
// would skip workers and silently omit the new INJECTS edges. origin/main at
|
|
669
|
+
// allocation is 81.
|
|
670
|
+
// 82 -> 83: Java Lombok @Data/@Getter/@Setter accessor synthesis emits
|
|
671
|
+
// synthetic Method nodes, HAS_METHOD edges, and matching scope captures into
|
|
672
|
+
// ParseWorkerResult / ParsedFile. A warm v82 cache replays pre-Lombok worker
|
|
673
|
+
// output and silently omits those callables. origin/main at allocation is 82.
|
|
674
|
+
// 83 -> 84: Kotlin val/var properties synthesize JVM get/set Method nodes
|
|
675
|
+
// (same provider hook as Java Lombok). A warm v83 cache omits those callables.
|
|
676
|
+
// origin/main at allocation is 83 (Java Lombok on this branch).
|
|
677
|
+
// 84 -> 85: JVM synthetic accessor captures now use the declaration
|
|
678
|
+
// qualified_name key consumed by scope extraction, and Kotlin accessor planning
|
|
679
|
+
// follows JvmAbi naming plus conservative @JvmName suppression. A warm v84
|
|
680
|
+
// cache can replay stale names and declaration metadata.
|
|
681
|
+
// 85 -> 86: Kotlin interface property accessors now record isAbstract on the
|
|
682
|
+
// synthetic Method. A warm v85 cache replays them as concrete.
|
|
683
|
+
const SCHEMA_BUMP = 86;
|
|
649
684
|
const GITNEXUS_PKG_VERSION = (() => {
|
|
650
685
|
try {
|
|
651
686
|
// package.json sits at gitnexus/package.json — two levels up from
|
|
@@ -673,16 +708,56 @@ const GITNEXUS_PKG_VERSION = (() => {
|
|
|
673
708
|
return '0.0.0-unknown';
|
|
674
709
|
})();
|
|
675
710
|
export const PARSE_CACHE_VERSION = `${SCHEMA_BUMP}+${GITNEXUS_PKG_VERSION}`;
|
|
711
|
+
/** SHA-256 hex of a string or buffer (paths for bucket ids, contents for cache keys). */
|
|
712
|
+
const sha256Hex = (input) => createHash('sha256')
|
|
713
|
+
.update(typeof input === 'string' ? Buffer.from(input) : input)
|
|
714
|
+
.digest('hex');
|
|
715
|
+
/** Stable parse-cache bucket count (#3088). Changing this requires SCHEMA_BUMP. */
|
|
716
|
+
export const PARSE_CACHE_BUCKET_COUNT = 128;
|
|
717
|
+
/** Bucket id for cache membership: `sha256(path) mod N` without IEEE-754 truncation. */
|
|
718
|
+
export const parseCacheBucketId = (filePath) => Number(BigInt(`0x${sha256Hex(filePath)}`) % BigInt(PARSE_CACHE_BUCKET_COUNT));
|
|
719
|
+
/**
|
|
720
|
+
* Pack files into parse-cache chunks: group by (language, bucket id), sort
|
|
721
|
+
* paths inside the group, then cut at `byteBudget`. Bucket visit order is
|
|
722
|
+
* the lexicographic order of `${language}\\0${bucketId}` keys (deterministic,
|
|
723
|
+
* independent of scan order and worker count).
|
|
724
|
+
*/
|
|
725
|
+
export const packParseCacheChunks = (files, byteBudget) => {
|
|
726
|
+
const buckets = new Map();
|
|
727
|
+
for (const file of files) {
|
|
728
|
+
const key = `${file.language}\0${parseCacheBucketId(file.path)}`;
|
|
729
|
+
const list = buckets.get(key);
|
|
730
|
+
if (list)
|
|
731
|
+
list.push(file);
|
|
732
|
+
else
|
|
733
|
+
buckets.set(key, [file]);
|
|
734
|
+
}
|
|
735
|
+
const chunks = [];
|
|
736
|
+
for (const key of [...buckets.keys()].sort()) {
|
|
737
|
+
const group = buckets.get(key);
|
|
738
|
+
group.sort((a, b) => compareCodeUnits(a.path, b.path));
|
|
739
|
+
let current = [];
|
|
740
|
+
let bytes = 0;
|
|
741
|
+
for (const file of group) {
|
|
742
|
+
if (current.length > 0 && bytes + file.size > byteBudget) {
|
|
743
|
+
chunks.push(current);
|
|
744
|
+
current = [];
|
|
745
|
+
bytes = 0;
|
|
746
|
+
}
|
|
747
|
+
current.push(file.path);
|
|
748
|
+
bytes += file.size;
|
|
749
|
+
}
|
|
750
|
+
if (current.length > 0)
|
|
751
|
+
chunks.push(current);
|
|
752
|
+
}
|
|
753
|
+
return chunks;
|
|
754
|
+
};
|
|
676
755
|
const LEGACY_CACHE_FILENAME = 'parse-cache.json';
|
|
677
756
|
const CACHE_DIRNAME = 'parse-cache';
|
|
678
757
|
const CACHE_INDEX_FILENAME = 'index.json';
|
|
679
758
|
/** Keys on disk always come from `computeChunkHash` — 64-char lowercase hex. */
|
|
680
759
|
const CHUNK_CACHE_KEY_HEX_RE = /^[a-f0-9]{64}$/;
|
|
681
760
|
const isValidChunkCacheKey = (chunkHash) => CHUNK_CACHE_KEY_HEX_RE.test(chunkHash);
|
|
682
|
-
/** SHA-256 hex of a single string or buffer. */
|
|
683
|
-
const sha256Hex = (input) => createHash('sha256')
|
|
684
|
-
.update(typeof input === 'string' ? Buffer.from(input) : input)
|
|
685
|
-
.digest('hex');
|
|
686
761
|
/** Stable hash of a single file's contents — used by callers to compose a chunk hash. */
|
|
687
762
|
export const fileContentHash = (content) => sha256Hex(content);
|
|
688
763
|
export const computeChunkHash = (entries, pdg = false) => {
|
|
@@ -759,7 +834,7 @@ export const mapReviver = (_key, value) => {
|
|
|
759
834
|
const getLegacyCachePath = (storagePath) => path.join(storagePath, LEGACY_CACHE_FILENAME);
|
|
760
835
|
const getCacheDirPath = (storagePath) => path.join(storagePath, CACHE_DIRNAME);
|
|
761
836
|
const getCacheIndexPath = (storagePath) => path.join(getCacheDirPath(storagePath), CACHE_INDEX_FILENAME);
|
|
762
|
-
const getCacheChunkPath = (storagePath, chunkHash) => path.join(getCacheDirPath(storagePath), `${chunkHash}.
|
|
837
|
+
const getCacheChunkPath = (storagePath, chunkHash) => path.join(getCacheDirPath(storagePath), `${chunkHash}.v8`);
|
|
763
838
|
/**
|
|
764
839
|
* Drop fields that are not replayed by `mergeChunkResults` / parse-impl after
|
|
765
840
|
* RING4-1 (#942). Shrinks on-disk shards and peak RSS during cold runs.
|
|
@@ -784,9 +859,12 @@ const readParseCacheChunkFromDisk = async (storagePath, chunkHash) => {
|
|
|
784
859
|
if (!isValidChunkCacheKey(chunkHash))
|
|
785
860
|
return undefined;
|
|
786
861
|
try {
|
|
787
|
-
const
|
|
788
|
-
const
|
|
789
|
-
|
|
862
|
+
const chunkPath = getCacheChunkPath(storagePath, chunkHash);
|
|
863
|
+
const v8Hit = await tryLoadV8Cache(chunkPath);
|
|
864
|
+
if (v8Hit?.kind === 'hit' && Array.isArray(v8Hit.value)) {
|
|
865
|
+
return v8Hit.value;
|
|
866
|
+
}
|
|
867
|
+
return undefined;
|
|
790
868
|
}
|
|
791
869
|
catch {
|
|
792
870
|
return undefined;
|
|
@@ -821,8 +899,17 @@ export const persistParseCacheChunk = async (cache, chunkHash, chunkResults) =>
|
|
|
821
899
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
822
900
|
createdCacheDirs.add(cacheDir);
|
|
823
901
|
}
|
|
824
|
-
const
|
|
825
|
-
await
|
|
902
|
+
const chunkPath = getCacheChunkPath(cache.storagePath, chunkHash);
|
|
903
|
+
let ok = await writeV8CacheFile(chunkPath, slim);
|
|
904
|
+
if (!ok) {
|
|
905
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
906
|
+
createdCacheDirs.add(cacheDir);
|
|
907
|
+
ok = await writeV8CacheFile(chunkPath, slim);
|
|
908
|
+
}
|
|
909
|
+
if (!ok) {
|
|
910
|
+
cache.entries.set(chunkHash, slim);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
826
913
|
cache.onDiskKeys ??= new Set();
|
|
827
914
|
cache.onDiskKeys.add(chunkHash);
|
|
828
915
|
cache.entries.delete(chunkHash);
|
|
@@ -918,28 +1005,18 @@ export const saveParseCache = async (storagePath, cache) => {
|
|
|
918
1005
|
// index from what we persisted, not from the raw usedKeys snapshot.
|
|
919
1006
|
const writtenKeys = [];
|
|
920
1007
|
for (const chunkHash of keys) {
|
|
921
|
-
const chunkPath = path.join(tmpDir, `${chunkHash}.
|
|
1008
|
+
const chunkPath = path.join(tmpDir, `${chunkHash}.v8`);
|
|
922
1009
|
const inMemory = cache.entries.get(chunkHash);
|
|
923
1010
|
if (inMemory !== undefined) {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
payload = JSON.stringify(inMemory, mapReplacer);
|
|
927
|
-
}
|
|
928
|
-
catch {
|
|
929
|
-
continue;
|
|
1011
|
+
if (await writeV8CacheFile(chunkPath, inMemory)) {
|
|
1012
|
+
writtenKeys.push(chunkHash);
|
|
930
1013
|
}
|
|
931
|
-
await fs.writeFile(chunkPath, payload, 'utf-8');
|
|
932
|
-
writtenKeys.push(chunkHash);
|
|
933
1014
|
continue;
|
|
934
1015
|
}
|
|
935
1016
|
const existingPath = getCacheChunkPath(storagePath, chunkHash);
|
|
936
|
-
|
|
937
|
-
await fs.copyFile(existingPath, chunkPath);
|
|
1017
|
+
if (await copyV8CacheIfPresent(existingPath, chunkPath)) {
|
|
938
1018
|
writtenKeys.push(chunkHash);
|
|
939
1019
|
}
|
|
940
|
-
catch {
|
|
941
|
-
/* shard missing — skip; next run treats as cache miss */
|
|
942
|
-
}
|
|
943
1020
|
}
|
|
944
1021
|
const index = {
|
|
945
1022
|
version: cache.version,
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
*
|
|
25
25
|
* ## Shape
|
|
26
26
|
*
|
|
27
|
-
* `<storagePath>/parsedfile-store/<shardId>.
|
|
28
|
-
* a
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* `<storagePath>/parsedfile-store/<shardId>.v8` — one shard per parse chunk,
|
|
28
|
+
* a V8 envelope of `ParsedFile[]` (Scope.bindings / Scope.typeBindings stay
|
|
29
|
+
* `Map`s). The store is cleared at the start of each parse and after
|
|
30
|
+
* scope-resolution consumes it, so it never lingers and never goes stale
|
|
31
|
+
* across runs.
|
|
32
32
|
*
|
|
33
33
|
* ## Durable sibling store (`parsedfile-cache/`, warm-cache coverage)
|
|
34
34
|
*
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
* that gap we ALSO write the worker's ParsedFiles to a second, CONTENT-ADDRESSED
|
|
42
42
|
* store keyed by the parse chunk hash (`getDurableParsedFileDir`), which mirrors
|
|
43
43
|
* the parse cache's lifecycle (persists across runs, pruned by `usedKeys`,
|
|
44
|
-
* version-tied via `PARSE_CACHE_VERSION`). On a warm hit the chunk's
|
|
45
|
-
* shards are
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
44
|
+
* version-tied via `PARSE_CACHE_VERSION`). On a warm hit the chunk's immutable
|
|
45
|
+
* durable shards are hardlinked (or atomically copied) into the run store after
|
|
46
|
+
* their envelope metadata proves complete coverage. That pins a stable snapshot
|
|
47
|
+
* before workers are skipped, even when another branch refreshes the shared
|
|
48
|
+
* durable directory concurrently.
|
|
49
49
|
*/
|
|
50
50
|
import type { ParsedFile, SymbolDefinition } from '../_shared/index.js';
|
|
51
51
|
/**
|
|
@@ -73,30 +73,25 @@ export declare const getParsedFileStoreDir: (storagePath: string) => string;
|
|
|
73
73
|
/** Remove any prior run's shards so a fresh parse starts clean. Idempotent. */
|
|
74
74
|
export declare const clearParsedFileStore: (storagePath: string) => Promise<void>;
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* main-thread no-store-disabled fallback and any non-worker writer; the worker
|
|
79
|
-
* store path uses {@link persistParsedFileShardSync}.
|
|
76
|
+
* Test seam for #3086. Production always calls {@link forceGc}; unit tests
|
|
77
|
+
* replace `run` to count cadence without requiring `--expose-gc`.
|
|
80
78
|
*/
|
|
81
|
-
export declare const
|
|
79
|
+
export declare const parsedFileLoadGc: {
|
|
80
|
+
run: () => void;
|
|
81
|
+
/** V8 envelope bytes visited between GCs (#3086). Tests may lower this. */
|
|
82
|
+
byteBudget: number;
|
|
83
|
+
};
|
|
82
84
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* protects the main thread, and a sync write avoids threading `async`/`await`
|
|
86
|
-
* through the synchronous per-file extract loop. Produces byte-identical shards
|
|
87
|
-
* to {@link persistParsedFileChunk} via the shared {@link serializeParsedFileShard}.
|
|
88
|
-
* No-op for an empty chunk. `shardId` must be globally unique for the run (the
|
|
89
|
-
* worker uses `w<threadId>-<seq>`); a duplicate would silently overwrite.
|
|
85
|
+
* Write one parse chunk's `ParsedFile[]` to the store as a single `.v8` shard.
|
|
86
|
+
* No-op for an empty chunk. `shardId` must be unique within a run.
|
|
90
87
|
*/
|
|
91
|
-
export declare const
|
|
88
|
+
export declare const persistParsedFileChunk: (storagePath: string, shardId: string, parsedFiles: readonly ParsedFile[]) => Promise<boolean>;
|
|
92
89
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* rather than the whole store. Returns an empty map when the store is absent
|
|
97
|
-
* (e.g. tests, or a run with no worker pool) — callers fall back to a fresh
|
|
98
|
-
* extract for the missing files.
|
|
90
|
+
* Synchronous shard writer for use INSIDE a parse worker (#1983 parallel
|
|
91
|
+
* serialization). Returns false on write failure so the worker can keep
|
|
92
|
+
* ParsedFiles in the result instead of dropping them.
|
|
99
93
|
*/
|
|
94
|
+
export declare const persistParsedFileShardSync: (storagePath: string, shardId: string, parsedFiles: readonly ParsedFile[]) => boolean;
|
|
100
95
|
export declare const loadParsedFilesForPaths: (storagePath: string, wantPaths: ReadonlySet<string>) => Promise<Map<string, ParsedFile>>;
|
|
101
96
|
/** Durable store dir — a sibling of `parsedfile-store/`, NEVER cleared per run. */
|
|
102
97
|
export declare const getDurableParsedFileDir: (storagePath: string) => string;
|
|
@@ -108,43 +103,22 @@ export declare const getDurableParsedFileDir: (storagePath: string) => string;
|
|
|
108
103
|
* from accumulating across analyses.
|
|
109
104
|
*/
|
|
110
105
|
export declare const prepareDurableParsedFileChunk: (durableDir: string, chunkHash: string) => Promise<void>;
|
|
106
|
+
export declare const persistDurableParsedFileShardSync: (durableDir: string, chunkHash: string, threadId: number, shardSeq: number, parsedFiles: readonly ParsedFile[]) => boolean;
|
|
111
107
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* N-shards-per-chunk fan-out and across worker-death retries — the same
|
|
117
|
-
* uniqueness that makes the run-scoped `w<tid>-<seq>` name safe, prefixed by
|
|
118
|
-
* content. No-op for an empty chunk.
|
|
119
|
-
*/
|
|
120
|
-
export declare const persistDurableParsedFileShardSync: (durableDir: string, chunkHash: string, threadId: number, shardSeq: number, parsedFiles: readonly ParsedFile[]) => void;
|
|
121
|
-
/**
|
|
122
|
-
* Restore a cached chunk's durable shards into the run-scoped store on a warm
|
|
123
|
-
* hit. A verbatim byte copy (no parse, no re-serialize), so the restored
|
|
124
|
-
* ParsedFiles are byte-identical to a cold run and `loadParsedFilesForPaths`
|
|
125
|
-
* (which keys on `filePath`, not shard name) gives scope-resolution full
|
|
126
|
-
* coverage. The durable shard names already carry the chunk hash, so they never
|
|
127
|
-
* collide with the worker's run-scoped `w<tid>-<seq>` shards. Returns the number
|
|
128
|
-
* of shards restored (0 ⇒ no durable coverage for this chunk; caller treats it
|
|
129
|
-
* as a miss).
|
|
130
|
-
*/
|
|
131
|
-
export declare const restoreDurableParsedFileShard: (durableDir: string, runStoragePath: string, chunkHash: string) => Promise<number>;
|
|
132
|
-
/**
|
|
133
|
-
* Read the durable index and return the set of chunk hashes it vouches for,
|
|
134
|
-
* gated on `expectedVersion` (`PARSE_CACHE_VERSION`). A version mismatch or a
|
|
135
|
-
* missing/corrupt index returns the empty set — the caller then treats every
|
|
136
|
-
* chunk as a durable miss and re-dispatches workers (NEVER the main-thread
|
|
137
|
-
* `extractParsedFile` fallback), which rewrites the durable store under the new
|
|
138
|
-
* version. Mirrors `loadParseCache`'s version-invalidation contract.
|
|
108
|
+
* Validate and snapshot one durable chunk into the run store. Every envelope
|
|
109
|
+
* must be runtime-compatible and integrity-valid, and together they must match
|
|
110
|
+
* the path coverage recorded when the durable index was published. Linking
|
|
111
|
+
* before returning pins the inodes against concurrent branch-cache rotation.
|
|
139
112
|
*/
|
|
140
|
-
export declare const
|
|
113
|
+
export declare const durableChunkHasShards: (runStoragePath: string, chunkHash: string, expectedPaths: ReadonlySet<string>) => Promise<boolean>;
|
|
114
|
+
export declare const loadDurableParsedFileIndex: (durableDir: string, expectedVersion: string) => Promise<Map<string, ReadonlySet<string>>>;
|
|
141
115
|
/**
|
|
142
116
|
* Prune the durable store to `keepKeys` and rewrite its index. `keepKeys` must
|
|
143
117
|
* be the parse cache's surviving on-disk keys (so the two stores stay coherent:
|
|
144
118
|
* a chunk is "cached" iff BOTH its parse-cache shard and its durable shards
|
|
145
119
|
* exist; a quarantined chunk — no parse-cache shard — drops its durable subdir
|
|
146
|
-
* here and re-dispatches next run). Only
|
|
147
|
-
*
|
|
148
|
-
*
|
|
120
|
+
* here and re-dispatches next run). Only chunks whose envelopes all validate
|
|
121
|
+
* are indexed, together with their exact persisted path coverage (never vouch
|
|
122
|
+
* for a missing/corrupt shard). The index write is tmp+rename atomic.
|
|
149
123
|
*/
|
|
150
124
|
export declare const pruneAndSaveDurableParsedFileStore: (durableDir: string, version: string, keepKeys: ReadonlySet<string>) => Promise<void>;
|