opencode-codebase-index 0.1.11 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -9
- package/dist/index.cjs +575 -222
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +569 -216
- package/dist/index.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
|
|
|
491
491
|
// path matching.
|
|
492
492
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
493
493
|
// @returns {TestResult} true if a file is ignored
|
|
494
|
-
test(
|
|
494
|
+
test(path8, checkUnignored, mode) {
|
|
495
495
|
let ignored = false;
|
|
496
496
|
let unignored = false;
|
|
497
497
|
let matchedRule;
|
|
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
|
|
|
500
500
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
|
-
const matched = rule[mode].test(
|
|
503
|
+
const matched = rule[mode].test(path8);
|
|
504
504
|
if (!matched) {
|
|
505
505
|
return;
|
|
506
506
|
}
|
|
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
|
|
|
521
521
|
var throwError = (message, Ctor) => {
|
|
522
522
|
throw new Ctor(message);
|
|
523
523
|
};
|
|
524
|
-
var checkPath = (
|
|
525
|
-
if (!isString(
|
|
524
|
+
var checkPath = (path8, originalPath, doThrow) => {
|
|
525
|
+
if (!isString(path8)) {
|
|
526
526
|
return doThrow(
|
|
527
527
|
`path must be a string, but got \`${originalPath}\``,
|
|
528
528
|
TypeError
|
|
529
529
|
);
|
|
530
530
|
}
|
|
531
|
-
if (!
|
|
531
|
+
if (!path8) {
|
|
532
532
|
return doThrow(`path must not be empty`, TypeError);
|
|
533
533
|
}
|
|
534
|
-
if (checkPath.isNotRelative(
|
|
534
|
+
if (checkPath.isNotRelative(path8)) {
|
|
535
535
|
const r = "`path.relative()`d";
|
|
536
536
|
return doThrow(
|
|
537
537
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
|
|
|
540
540
|
}
|
|
541
541
|
return true;
|
|
542
542
|
};
|
|
543
|
-
var isNotRelative = (
|
|
543
|
+
var isNotRelative = (path8) => REGEX_TEST_INVALID_PATH.test(path8);
|
|
544
544
|
checkPath.isNotRelative = isNotRelative;
|
|
545
545
|
checkPath.convert = (p) => p;
|
|
546
546
|
var Ignore2 = class {
|
|
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
|
|
|
570
570
|
}
|
|
571
571
|
// @returns {TestResult}
|
|
572
572
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
573
|
-
const
|
|
573
|
+
const path8 = originalPath && checkPath.convert(originalPath);
|
|
574
574
|
checkPath(
|
|
575
|
-
|
|
575
|
+
path8,
|
|
576
576
|
originalPath,
|
|
577
577
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
578
578
|
);
|
|
579
|
-
return this._t(
|
|
579
|
+
return this._t(path8, cache, checkUnignored, slices);
|
|
580
580
|
}
|
|
581
|
-
checkIgnore(
|
|
582
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
583
|
-
return this.test(
|
|
581
|
+
checkIgnore(path8) {
|
|
582
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path8)) {
|
|
583
|
+
return this.test(path8);
|
|
584
584
|
}
|
|
585
|
-
const slices =
|
|
585
|
+
const slices = path8.split(SLASH2).filter(Boolean);
|
|
586
586
|
slices.pop();
|
|
587
587
|
if (slices.length) {
|
|
588
588
|
const parent = this._t(
|
|
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
|
|
|
595
595
|
return parent;
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
|
-
return this._rules.test(
|
|
598
|
+
return this._rules.test(path8, false, MODE_CHECK_IGNORE);
|
|
599
599
|
}
|
|
600
|
-
_t(
|
|
601
|
-
if (
|
|
602
|
-
return cache[
|
|
600
|
+
_t(path8, cache, checkUnignored, slices) {
|
|
601
|
+
if (path8 in cache) {
|
|
602
|
+
return cache[path8];
|
|
603
603
|
}
|
|
604
604
|
if (!slices) {
|
|
605
|
-
slices =
|
|
605
|
+
slices = path8.split(SLASH2).filter(Boolean);
|
|
606
606
|
}
|
|
607
607
|
slices.pop();
|
|
608
608
|
if (!slices.length) {
|
|
609
|
-
return cache[
|
|
609
|
+
return cache[path8] = this._rules.test(path8, checkUnignored, MODE_IGNORE);
|
|
610
610
|
}
|
|
611
611
|
const parent = this._t(
|
|
612
612
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
|
|
|
614
614
|
checkUnignored,
|
|
615
615
|
slices
|
|
616
616
|
);
|
|
617
|
-
return cache[
|
|
617
|
+
return cache[path8] = parent.ignored ? parent : this._rules.test(path8, checkUnignored, MODE_IGNORE);
|
|
618
618
|
}
|
|
619
|
-
ignores(
|
|
620
|
-
return this._test(
|
|
619
|
+
ignores(path8) {
|
|
620
|
+
return this._test(path8, this._ignoreCache, false).ignored;
|
|
621
621
|
}
|
|
622
622
|
createFilter() {
|
|
623
|
-
return (
|
|
623
|
+
return (path8) => !this.ignores(path8);
|
|
624
624
|
}
|
|
625
625
|
filter(paths) {
|
|
626
626
|
return makeArray(paths).filter(this.createFilter());
|
|
627
627
|
}
|
|
628
628
|
// @returns {TestResult}
|
|
629
|
-
test(
|
|
630
|
-
return this._test(
|
|
629
|
+
test(path8) {
|
|
630
|
+
return this._test(path8, this._testCache, true);
|
|
631
631
|
}
|
|
632
632
|
};
|
|
633
633
|
var factory = (options) => new Ignore2(options);
|
|
634
|
-
var isPathValid = (
|
|
634
|
+
var isPathValid = (path8) => checkPath(path8 && checkPath.convert(path8), path8, RETURN_FALSE);
|
|
635
635
|
var setupWindows = () => {
|
|
636
636
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
637
637
|
checkPath.convert = makePosix;
|
|
638
638
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
639
|
-
checkPath.isNotRelative = (
|
|
639
|
+
checkPath.isNotRelative = (path8) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path8) || isNotRelative(path8);
|
|
640
640
|
};
|
|
641
641
|
if (
|
|
642
642
|
// Detect `process` so that it can run in browsers.
|
|
@@ -652,8 +652,8 @@ var require_ignore = __commonJS({
|
|
|
652
652
|
});
|
|
653
653
|
|
|
654
654
|
// src/index.ts
|
|
655
|
-
import { existsSync as
|
|
656
|
-
import * as
|
|
655
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
656
|
+
import * as path7 from "path";
|
|
657
657
|
|
|
658
658
|
// src/config/schema.ts
|
|
659
659
|
var DEFAULT_INCLUDE = [
|
|
@@ -806,8 +806,8 @@ function getDefaultModelForProvider(provider) {
|
|
|
806
806
|
}
|
|
807
807
|
|
|
808
808
|
// src/indexer/index.ts
|
|
809
|
-
import { existsSync as
|
|
810
|
-
import * as
|
|
809
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync, promises as fsPromises2 } from "fs";
|
|
810
|
+
import * as path5 from "path";
|
|
811
811
|
|
|
812
812
|
// node_modules/eventemitter3/index.mjs
|
|
813
813
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -2188,7 +2188,10 @@ function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatter
|
|
|
2188
2188
|
return false;
|
|
2189
2189
|
}
|
|
2190
2190
|
function matchGlob(filePath, pattern) {
|
|
2191
|
-
|
|
2191
|
+
let regexPattern = pattern.replace(/\*\*/g, "<<<DOUBLESTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<DOUBLESTAR>>>/g, ".*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, p1) => `(${p1.split(",").join("|")})`);
|
|
2192
|
+
if (regexPattern.startsWith(".*/")) {
|
|
2193
|
+
regexPattern = `(.*\\/)?${regexPattern.slice(3)}`;
|
|
2194
|
+
}
|
|
2192
2195
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
2193
2196
|
return regex.test(filePath);
|
|
2194
2197
|
}
|
|
@@ -2659,37 +2662,181 @@ var InvertedIndex = class {
|
|
|
2659
2662
|
return this.inner.documentCount();
|
|
2660
2663
|
}
|
|
2661
2664
|
};
|
|
2665
|
+
var Database = class {
|
|
2666
|
+
inner;
|
|
2667
|
+
constructor(dbPath) {
|
|
2668
|
+
this.inner = new native.Database(dbPath);
|
|
2669
|
+
}
|
|
2670
|
+
embeddingExists(contentHash) {
|
|
2671
|
+
return this.inner.embeddingExists(contentHash);
|
|
2672
|
+
}
|
|
2673
|
+
getEmbedding(contentHash) {
|
|
2674
|
+
return this.inner.getEmbedding(contentHash) ?? null;
|
|
2675
|
+
}
|
|
2676
|
+
upsertEmbedding(contentHash, embedding, chunkText, model) {
|
|
2677
|
+
this.inner.upsertEmbedding(contentHash, embedding, chunkText, model);
|
|
2678
|
+
}
|
|
2679
|
+
getMissingEmbeddings(contentHashes) {
|
|
2680
|
+
return this.inner.getMissingEmbeddings(contentHashes);
|
|
2681
|
+
}
|
|
2682
|
+
upsertChunk(chunk) {
|
|
2683
|
+
this.inner.upsertChunk(chunk);
|
|
2684
|
+
}
|
|
2685
|
+
getChunk(chunkId) {
|
|
2686
|
+
return this.inner.getChunk(chunkId) ?? null;
|
|
2687
|
+
}
|
|
2688
|
+
getChunksByFile(filePath) {
|
|
2689
|
+
return this.inner.getChunksByFile(filePath);
|
|
2690
|
+
}
|
|
2691
|
+
deleteChunksByFile(filePath) {
|
|
2692
|
+
return this.inner.deleteChunksByFile(filePath);
|
|
2693
|
+
}
|
|
2694
|
+
addChunksToBranch(branch, chunkIds) {
|
|
2695
|
+
this.inner.addChunksToBranch(branch, chunkIds);
|
|
2696
|
+
}
|
|
2697
|
+
clearBranch(branch) {
|
|
2698
|
+
return this.inner.clearBranch(branch);
|
|
2699
|
+
}
|
|
2700
|
+
getBranchChunkIds(branch) {
|
|
2701
|
+
return this.inner.getBranchChunkIds(branch);
|
|
2702
|
+
}
|
|
2703
|
+
getBranchDelta(branch, baseBranch) {
|
|
2704
|
+
return this.inner.getBranchDelta(branch, baseBranch);
|
|
2705
|
+
}
|
|
2706
|
+
chunkExistsOnBranch(branch, chunkId) {
|
|
2707
|
+
return this.inner.chunkExistsOnBranch(branch, chunkId);
|
|
2708
|
+
}
|
|
2709
|
+
getAllBranches() {
|
|
2710
|
+
return this.inner.getAllBranches();
|
|
2711
|
+
}
|
|
2712
|
+
getMetadata(key) {
|
|
2713
|
+
return this.inner.getMetadata(key) ?? null;
|
|
2714
|
+
}
|
|
2715
|
+
setMetadata(key, value) {
|
|
2716
|
+
this.inner.setMetadata(key, value);
|
|
2717
|
+
}
|
|
2718
|
+
deleteMetadata(key) {
|
|
2719
|
+
return this.inner.deleteMetadata(key);
|
|
2720
|
+
}
|
|
2721
|
+
gcOrphanEmbeddings() {
|
|
2722
|
+
return this.inner.gcOrphanEmbeddings();
|
|
2723
|
+
}
|
|
2724
|
+
gcOrphanChunks() {
|
|
2725
|
+
return this.inner.gcOrphanChunks();
|
|
2726
|
+
}
|
|
2727
|
+
getStats() {
|
|
2728
|
+
return this.inner.getStats();
|
|
2729
|
+
}
|
|
2730
|
+
};
|
|
2731
|
+
|
|
2732
|
+
// src/git/index.ts
|
|
2733
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
2734
|
+
import * as path4 from "path";
|
|
2735
|
+
import { execSync } from "child_process";
|
|
2736
|
+
function isGitRepo(dir) {
|
|
2737
|
+
return existsSync3(path4.join(dir, ".git"));
|
|
2738
|
+
}
|
|
2739
|
+
function getCurrentBranch(repoRoot) {
|
|
2740
|
+
const headPath = path4.join(repoRoot, ".git", "HEAD");
|
|
2741
|
+
if (!existsSync3(headPath)) {
|
|
2742
|
+
return null;
|
|
2743
|
+
}
|
|
2744
|
+
try {
|
|
2745
|
+
const headContent = readFileSync3(headPath, "utf-8").trim();
|
|
2746
|
+
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
2747
|
+
if (match) {
|
|
2748
|
+
return match[1];
|
|
2749
|
+
}
|
|
2750
|
+
if (/^[0-9a-f]{40}$/i.test(headContent)) {
|
|
2751
|
+
return headContent.slice(0, 7);
|
|
2752
|
+
}
|
|
2753
|
+
return null;
|
|
2754
|
+
} catch {
|
|
2755
|
+
return null;
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
function getBaseBranch(repoRoot) {
|
|
2759
|
+
const candidates = ["main", "master", "develop", "trunk"];
|
|
2760
|
+
for (const candidate of candidates) {
|
|
2761
|
+
const refPath = path4.join(repoRoot, ".git", "refs", "heads", candidate);
|
|
2762
|
+
if (existsSync3(refPath)) {
|
|
2763
|
+
return candidate;
|
|
2764
|
+
}
|
|
2765
|
+
const packedRefsPath = path4.join(repoRoot, ".git", "packed-refs");
|
|
2766
|
+
if (existsSync3(packedRefsPath)) {
|
|
2767
|
+
try {
|
|
2768
|
+
const content = readFileSync3(packedRefsPath, "utf-8");
|
|
2769
|
+
if (content.includes(`refs/heads/${candidate}`)) {
|
|
2770
|
+
return candidate;
|
|
2771
|
+
}
|
|
2772
|
+
} catch {
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
try {
|
|
2777
|
+
const result = execSync("git remote show origin", {
|
|
2778
|
+
cwd: repoRoot,
|
|
2779
|
+
encoding: "utf-8",
|
|
2780
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2781
|
+
});
|
|
2782
|
+
const match = result.match(/HEAD branch: (.+)/);
|
|
2783
|
+
if (match) {
|
|
2784
|
+
return match[1].trim();
|
|
2785
|
+
}
|
|
2786
|
+
} catch {
|
|
2787
|
+
}
|
|
2788
|
+
return getCurrentBranch(repoRoot) ?? "main";
|
|
2789
|
+
}
|
|
2790
|
+
function getBranchOrDefault(repoRoot) {
|
|
2791
|
+
if (!isGitRepo(repoRoot)) {
|
|
2792
|
+
return "default";
|
|
2793
|
+
}
|
|
2794
|
+
return getCurrentBranch(repoRoot) ?? "default";
|
|
2795
|
+
}
|
|
2796
|
+
function getHeadPath(repoRoot) {
|
|
2797
|
+
return path4.join(repoRoot, ".git", "HEAD");
|
|
2798
|
+
}
|
|
2662
2799
|
|
|
2663
2800
|
// src/indexer/index.ts
|
|
2801
|
+
function float32ArrayToBuffer(arr) {
|
|
2802
|
+
const float32 = new Float32Array(arr);
|
|
2803
|
+
return Buffer.from(float32.buffer);
|
|
2804
|
+
}
|
|
2805
|
+
function bufferToFloat32Array(buf) {
|
|
2806
|
+
return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
2807
|
+
}
|
|
2664
2808
|
var Indexer = class {
|
|
2665
2809
|
config;
|
|
2666
2810
|
projectRoot;
|
|
2667
2811
|
indexPath;
|
|
2668
2812
|
store = null;
|
|
2669
2813
|
invertedIndex = null;
|
|
2814
|
+
database = null;
|
|
2670
2815
|
provider = null;
|
|
2671
2816
|
detectedProvider = null;
|
|
2672
2817
|
fileHashCache = /* @__PURE__ */ new Map();
|
|
2673
2818
|
fileHashCachePath = "";
|
|
2674
2819
|
failedBatchesPath = "";
|
|
2820
|
+
currentBranch = "default";
|
|
2821
|
+
baseBranch = "main";
|
|
2675
2822
|
constructor(projectRoot, config) {
|
|
2676
2823
|
this.projectRoot = projectRoot;
|
|
2677
2824
|
this.config = config;
|
|
2678
2825
|
this.indexPath = this.getIndexPath();
|
|
2679
|
-
this.fileHashCachePath =
|
|
2680
|
-
this.failedBatchesPath =
|
|
2826
|
+
this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
|
|
2827
|
+
this.failedBatchesPath = path5.join(this.indexPath, "failed-batches.json");
|
|
2681
2828
|
}
|
|
2682
2829
|
getIndexPath() {
|
|
2683
2830
|
if (this.config.scope === "global") {
|
|
2684
2831
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
2685
|
-
return
|
|
2832
|
+
return path5.join(homeDir, ".opencode", "global-index");
|
|
2686
2833
|
}
|
|
2687
|
-
return
|
|
2834
|
+
return path5.join(this.projectRoot, ".opencode", "index");
|
|
2688
2835
|
}
|
|
2689
2836
|
loadFileHashCache() {
|
|
2690
2837
|
try {
|
|
2691
|
-
if (
|
|
2692
|
-
const data =
|
|
2838
|
+
if (existsSync4(this.fileHashCachePath)) {
|
|
2839
|
+
const data = readFileSync4(this.fileHashCachePath, "utf-8");
|
|
2693
2840
|
const parsed = JSON.parse(data);
|
|
2694
2841
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
2695
2842
|
}
|
|
@@ -2706,8 +2853,8 @@ var Indexer = class {
|
|
|
2706
2853
|
}
|
|
2707
2854
|
loadFailedBatches() {
|
|
2708
2855
|
try {
|
|
2709
|
-
if (
|
|
2710
|
-
const data =
|
|
2856
|
+
if (existsSync4(this.failedBatchesPath)) {
|
|
2857
|
+
const data = readFileSync4(this.failedBatchesPath, "utf-8");
|
|
2711
2858
|
return JSON.parse(data);
|
|
2712
2859
|
}
|
|
2713
2860
|
} catch {
|
|
@@ -2717,7 +2864,7 @@ var Indexer = class {
|
|
|
2717
2864
|
}
|
|
2718
2865
|
saveFailedBatches(batches) {
|
|
2719
2866
|
if (batches.length === 0) {
|
|
2720
|
-
if (
|
|
2867
|
+
if (existsSync4(this.failedBatchesPath)) {
|
|
2721
2868
|
fsPromises2.unlink(this.failedBatchesPath).catch(() => {
|
|
2722
2869
|
});
|
|
2723
2870
|
}
|
|
@@ -2748,25 +2895,66 @@ var Indexer = class {
|
|
|
2748
2895
|
);
|
|
2749
2896
|
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
2750
2897
|
const dimensions = this.detectedProvider.modelInfo.dimensions;
|
|
2751
|
-
const storePath =
|
|
2898
|
+
const storePath = path5.join(this.indexPath, "vectors");
|
|
2752
2899
|
this.store = new VectorStore(storePath, dimensions);
|
|
2753
|
-
const indexFilePath =
|
|
2754
|
-
if (
|
|
2900
|
+
const indexFilePath = path5.join(this.indexPath, "vectors.usearch");
|
|
2901
|
+
if (existsSync4(indexFilePath)) {
|
|
2755
2902
|
this.store.load();
|
|
2756
2903
|
}
|
|
2757
|
-
const invertedIndexPath =
|
|
2904
|
+
const invertedIndexPath = path5.join(this.indexPath, "inverted-index.json");
|
|
2758
2905
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
2759
|
-
|
|
2906
|
+
try {
|
|
2907
|
+
this.invertedIndex.load();
|
|
2908
|
+
} catch {
|
|
2909
|
+
if (existsSync4(invertedIndexPath)) {
|
|
2910
|
+
await fsPromises2.unlink(invertedIndexPath);
|
|
2911
|
+
}
|
|
2912
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
2913
|
+
}
|
|
2914
|
+
const dbPath = path5.join(this.indexPath, "codebase.db");
|
|
2915
|
+
const dbIsNew = !existsSync4(dbPath);
|
|
2916
|
+
this.database = new Database(dbPath);
|
|
2917
|
+
if (dbIsNew && this.store.count() > 0) {
|
|
2918
|
+
this.migrateFromLegacyIndex();
|
|
2919
|
+
}
|
|
2920
|
+
if (isGitRepo(this.projectRoot)) {
|
|
2921
|
+
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
2922
|
+
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
2923
|
+
} else {
|
|
2924
|
+
this.currentBranch = "default";
|
|
2925
|
+
this.baseBranch = "default";
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
migrateFromLegacyIndex() {
|
|
2929
|
+
if (!this.store || !this.database) return;
|
|
2930
|
+
const allMetadata = this.store.getAllMetadata();
|
|
2931
|
+
const chunkIds = [];
|
|
2932
|
+
for (const { key, metadata } of allMetadata) {
|
|
2933
|
+
const chunkData = {
|
|
2934
|
+
chunkId: key,
|
|
2935
|
+
contentHash: metadata.hash,
|
|
2936
|
+
filePath: metadata.filePath,
|
|
2937
|
+
startLine: metadata.startLine,
|
|
2938
|
+
endLine: metadata.endLine,
|
|
2939
|
+
nodeType: metadata.chunkType,
|
|
2940
|
+
name: metadata.name,
|
|
2941
|
+
language: metadata.language
|
|
2942
|
+
};
|
|
2943
|
+
this.database.upsertChunk(chunkData);
|
|
2944
|
+
chunkIds.push(key);
|
|
2945
|
+
}
|
|
2946
|
+
this.database.addChunksToBranch(this.currentBranch || "default", chunkIds);
|
|
2760
2947
|
}
|
|
2761
2948
|
async ensureInitialized() {
|
|
2762
|
-
if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider) {
|
|
2949
|
+
if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider || !this.database) {
|
|
2763
2950
|
await this.initialize();
|
|
2764
2951
|
}
|
|
2765
2952
|
return {
|
|
2766
2953
|
store: this.store,
|
|
2767
2954
|
provider: this.provider,
|
|
2768
2955
|
invertedIndex: this.invertedIndex,
|
|
2769
|
-
detectedProvider: this.detectedProvider
|
|
2956
|
+
detectedProvider: this.detectedProvider,
|
|
2957
|
+
database: this.database
|
|
2770
2958
|
};
|
|
2771
2959
|
}
|
|
2772
2960
|
async estimateCost() {
|
|
@@ -2780,7 +2968,7 @@ var Indexer = class {
|
|
|
2780
2968
|
return createCostEstimate(files, detectedProvider);
|
|
2781
2969
|
}
|
|
2782
2970
|
async index(onProgress) {
|
|
2783
|
-
const { store, provider, invertedIndex } = await this.ensureInitialized();
|
|
2971
|
+
const { store, provider, invertedIndex, database, detectedProvider } = await this.ensureInitialized();
|
|
2784
2972
|
const startTime = Date.now();
|
|
2785
2973
|
const stats = {
|
|
2786
2974
|
totalFiles: 0,
|
|
@@ -2854,7 +3042,7 @@ var Indexer = class {
|
|
|
2854
3042
|
for (const parsed of parsedFiles) {
|
|
2855
3043
|
currentFilePaths.add(parsed.path);
|
|
2856
3044
|
if (parsed.chunks.length === 0) {
|
|
2857
|
-
const relativePath =
|
|
3045
|
+
const relativePath = path5.relative(this.projectRoot, parsed.path);
|
|
2858
3046
|
stats.parseFailures.push(relativePath);
|
|
2859
3047
|
}
|
|
2860
3048
|
let fileChunkCount = 0;
|
|
@@ -2868,6 +3056,17 @@ var Indexer = class {
|
|
|
2868
3056
|
const id = generateChunkId(parsed.path, chunk);
|
|
2869
3057
|
const contentHash = generateChunkHash(chunk);
|
|
2870
3058
|
currentChunkIds.add(id);
|
|
3059
|
+
const chunkData = {
|
|
3060
|
+
chunkId: id,
|
|
3061
|
+
contentHash,
|
|
3062
|
+
filePath: parsed.path,
|
|
3063
|
+
startLine: chunk.startLine,
|
|
3064
|
+
endLine: chunk.endLine,
|
|
3065
|
+
nodeType: chunk.chunkType,
|
|
3066
|
+
name: chunk.name,
|
|
3067
|
+
language: chunk.language
|
|
3068
|
+
};
|
|
3069
|
+
database.upsertChunk(chunkData);
|
|
2871
3070
|
if (existingChunks.get(id) === contentHash) {
|
|
2872
3071
|
fileChunkCount++;
|
|
2873
3072
|
continue;
|
|
@@ -2882,7 +3081,7 @@ var Indexer = class {
|
|
|
2882
3081
|
language: chunk.language,
|
|
2883
3082
|
hash: contentHash
|
|
2884
3083
|
};
|
|
2885
|
-
pendingChunks.push({ id, text, content: chunk.content, metadata });
|
|
3084
|
+
pendingChunks.push({ id, text, content: chunk.content, contentHash, metadata });
|
|
2886
3085
|
fileChunkCount++;
|
|
2887
3086
|
}
|
|
2888
3087
|
}
|
|
@@ -2898,6 +3097,8 @@ var Indexer = class {
|
|
|
2898
3097
|
stats.existingChunks = currentChunkIds.size - pendingChunks.length;
|
|
2899
3098
|
stats.removedChunks = removedCount;
|
|
2900
3099
|
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
3100
|
+
database.clearBranch(this.currentBranch);
|
|
3101
|
+
database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
|
|
2901
3102
|
this.fileHashCache = currentFileHashes;
|
|
2902
3103
|
this.saveFileHashCache();
|
|
2903
3104
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -2911,6 +3112,8 @@ var Indexer = class {
|
|
|
2911
3112
|
return stats;
|
|
2912
3113
|
}
|
|
2913
3114
|
if (pendingChunks.length === 0) {
|
|
3115
|
+
database.clearBranch(this.currentBranch);
|
|
3116
|
+
database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
|
|
2914
3117
|
store.save();
|
|
2915
3118
|
invertedIndex.save();
|
|
2916
3119
|
this.fileHashCache = currentFileHashes;
|
|
@@ -2932,8 +3135,22 @@ var Indexer = class {
|
|
|
2932
3135
|
chunksProcessed: 0,
|
|
2933
3136
|
totalChunks: pendingChunks.length
|
|
2934
3137
|
});
|
|
3138
|
+
const allContentHashes = pendingChunks.map((c) => c.contentHash);
|
|
3139
|
+
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
3140
|
+
const chunksNeedingEmbedding = pendingChunks.filter((c) => missingHashes.has(c.contentHash));
|
|
3141
|
+
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !missingHashes.has(c.contentHash));
|
|
3142
|
+
for (const chunk of chunksWithExistingEmbedding) {
|
|
3143
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
3144
|
+
if (embeddingBuffer) {
|
|
3145
|
+
const vector = bufferToFloat32Array(embeddingBuffer);
|
|
3146
|
+
store.add(chunk.id, Array.from(vector), chunk.metadata);
|
|
3147
|
+
invertedIndex.removeChunk(chunk.id);
|
|
3148
|
+
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
3149
|
+
stats.indexedChunks++;
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
2935
3152
|
const queue = new PQueue({ concurrency: 3 });
|
|
2936
|
-
const dynamicBatches = createDynamicBatches(
|
|
3153
|
+
const dynamicBatches = createDynamicBatches(chunksNeedingEmbedding);
|
|
2937
3154
|
for (const batch of dynamicBatches) {
|
|
2938
3155
|
queue.add(async () => {
|
|
2939
3156
|
try {
|
|
@@ -2958,7 +3175,15 @@ var Indexer = class {
|
|
|
2958
3175
|
metadata: chunk.metadata
|
|
2959
3176
|
}));
|
|
2960
3177
|
store.addBatch(items);
|
|
2961
|
-
for (
|
|
3178
|
+
for (let i = 0; i < batch.length; i++) {
|
|
3179
|
+
const chunk = batch[i];
|
|
3180
|
+
const embedding = result.embeddings[i];
|
|
3181
|
+
database.upsertEmbedding(
|
|
3182
|
+
chunk.contentHash,
|
|
3183
|
+
float32ArrayToBuffer(embedding),
|
|
3184
|
+
chunk.text,
|
|
3185
|
+
detectedProvider.modelInfo.model
|
|
3186
|
+
);
|
|
2962
3187
|
invertedIndex.removeChunk(chunk.id);
|
|
2963
3188
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
2964
3189
|
}
|
|
@@ -2986,6 +3211,8 @@ var Indexer = class {
|
|
|
2986
3211
|
chunksProcessed: stats.indexedChunks,
|
|
2987
3212
|
totalChunks: pendingChunks.length
|
|
2988
3213
|
});
|
|
3214
|
+
database.clearBranch(this.currentBranch);
|
|
3215
|
+
database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
|
|
2989
3216
|
store.save();
|
|
2990
3217
|
invertedIndex.save();
|
|
2991
3218
|
this.fileHashCache = currentFileHashes;
|
|
@@ -3004,18 +3231,24 @@ var Indexer = class {
|
|
|
3004
3231
|
return stats;
|
|
3005
3232
|
}
|
|
3006
3233
|
async search(query, limit, options) {
|
|
3007
|
-
const { store, provider } = await this.ensureInitialized();
|
|
3234
|
+
const { store, provider, database } = await this.ensureInitialized();
|
|
3008
3235
|
if (store.count() === 0) {
|
|
3009
3236
|
return [];
|
|
3010
3237
|
}
|
|
3011
3238
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
3012
3239
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
3240
|
+
const filterByBranch = options?.filterByBranch ?? true;
|
|
3013
3241
|
const { embedding } = await provider.embed(query);
|
|
3014
3242
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
3015
3243
|
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
3016
3244
|
const combined = this.fuseResults(semanticResults, keywordResults, hybridWeight, maxResults * 4);
|
|
3245
|
+
let branchChunkIds = null;
|
|
3246
|
+
if (filterByBranch && this.currentBranch !== "default") {
|
|
3247
|
+
branchChunkIds = new Set(database.getBranchChunkIds(this.currentBranch));
|
|
3248
|
+
}
|
|
3017
3249
|
const filtered = combined.filter((r) => {
|
|
3018
3250
|
if (r.score < this.config.search.minScore) return false;
|
|
3251
|
+
if (branchChunkIds && !branchChunkIds.has(r.id)) return false;
|
|
3019
3252
|
if (options?.fileType) {
|
|
3020
3253
|
const ext = r.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
3021
3254
|
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
@@ -3117,7 +3350,9 @@ var Indexer = class {
|
|
|
3117
3350
|
vectorCount: store.count(),
|
|
3118
3351
|
provider: detectedProvider.provider,
|
|
3119
3352
|
model: detectedProvider.modelInfo.model,
|
|
3120
|
-
indexPath: this.indexPath
|
|
3353
|
+
indexPath: this.indexPath,
|
|
3354
|
+
currentBranch: this.currentBranch,
|
|
3355
|
+
baseBranch: this.baseBranch
|
|
3121
3356
|
};
|
|
3122
3357
|
}
|
|
3123
3358
|
async clearIndex() {
|
|
@@ -3128,7 +3363,7 @@ var Indexer = class {
|
|
|
3128
3363
|
invertedIndex.save();
|
|
3129
3364
|
}
|
|
3130
3365
|
async healthCheck() {
|
|
3131
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
3366
|
+
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
3132
3367
|
const allMetadata = store.getAllMetadata();
|
|
3133
3368
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
3134
3369
|
for (const { key, metadata } of allMetadata) {
|
|
@@ -3139,12 +3374,13 @@ var Indexer = class {
|
|
|
3139
3374
|
const removedFilePaths = [];
|
|
3140
3375
|
let removedCount = 0;
|
|
3141
3376
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
3142
|
-
if (!
|
|
3377
|
+
if (!existsSync4(filePath)) {
|
|
3143
3378
|
for (const key of chunkKeys) {
|
|
3144
3379
|
store.remove(key);
|
|
3145
3380
|
invertedIndex.removeChunk(key);
|
|
3146
3381
|
removedCount++;
|
|
3147
3382
|
}
|
|
3383
|
+
database.deleteChunksByFile(filePath);
|
|
3148
3384
|
removedFilePaths.push(filePath);
|
|
3149
3385
|
}
|
|
3150
3386
|
}
|
|
@@ -3152,7 +3388,9 @@ var Indexer = class {
|
|
|
3152
3388
|
store.save();
|
|
3153
3389
|
invertedIndex.save();
|
|
3154
3390
|
}
|
|
3155
|
-
|
|
3391
|
+
const gcOrphanEmbeddings = database.gcOrphanEmbeddings();
|
|
3392
|
+
const gcOrphanChunks = database.gcOrphanChunks();
|
|
3393
|
+
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks };
|
|
3156
3394
|
}
|
|
3157
3395
|
async retryFailedBatches() {
|
|
3158
3396
|
const { store, provider, invertedIndex } = await this.ensureInitialized();
|
|
@@ -3206,6 +3444,22 @@ var Indexer = class {
|
|
|
3206
3444
|
getFailedBatchesCount() {
|
|
3207
3445
|
return this.loadFailedBatches().length;
|
|
3208
3446
|
}
|
|
3447
|
+
getCurrentBranch() {
|
|
3448
|
+
return this.currentBranch;
|
|
3449
|
+
}
|
|
3450
|
+
getBaseBranch() {
|
|
3451
|
+
return this.baseBranch;
|
|
3452
|
+
}
|
|
3453
|
+
refreshBranchInfo() {
|
|
3454
|
+
if (isGitRepo(this.projectRoot)) {
|
|
3455
|
+
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
3456
|
+
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
async getDatabaseStats() {
|
|
3460
|
+
const { database } = await this.ensureInitialized();
|
|
3461
|
+
return database.getStats();
|
|
3462
|
+
}
|
|
3209
3463
|
};
|
|
3210
3464
|
|
|
3211
3465
|
// node_modules/chokidar/index.js
|
|
@@ -3298,7 +3552,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
3298
3552
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
3299
3553
|
const statMethod = opts.lstat ? lstat : stat;
|
|
3300
3554
|
if (wantBigintFsStats) {
|
|
3301
|
-
this._stat = (
|
|
3555
|
+
this._stat = (path8) => statMethod(path8, { bigint: true });
|
|
3302
3556
|
} else {
|
|
3303
3557
|
this._stat = statMethod;
|
|
3304
3558
|
}
|
|
@@ -3323,8 +3577,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
3323
3577
|
const par = this.parent;
|
|
3324
3578
|
const fil = par && par.files;
|
|
3325
3579
|
if (fil && fil.length > 0) {
|
|
3326
|
-
const { path:
|
|
3327
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
3580
|
+
const { path: path8, depth } = par;
|
|
3581
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path8));
|
|
3328
3582
|
const awaited = await Promise.all(slice);
|
|
3329
3583
|
for (const entry of awaited) {
|
|
3330
3584
|
if (!entry)
|
|
@@ -3364,20 +3618,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
3364
3618
|
this.reading = false;
|
|
3365
3619
|
}
|
|
3366
3620
|
}
|
|
3367
|
-
async _exploreDir(
|
|
3621
|
+
async _exploreDir(path8, depth) {
|
|
3368
3622
|
let files;
|
|
3369
3623
|
try {
|
|
3370
|
-
files = await readdir(
|
|
3624
|
+
files = await readdir(path8, this._rdOptions);
|
|
3371
3625
|
} catch (error) {
|
|
3372
3626
|
this._onError(error);
|
|
3373
3627
|
}
|
|
3374
|
-
return { files, depth, path:
|
|
3628
|
+
return { files, depth, path: path8 };
|
|
3375
3629
|
}
|
|
3376
|
-
async _formatEntry(dirent,
|
|
3630
|
+
async _formatEntry(dirent, path8) {
|
|
3377
3631
|
let entry;
|
|
3378
3632
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
3379
3633
|
try {
|
|
3380
|
-
const fullPath = presolve(pjoin(
|
|
3634
|
+
const fullPath = presolve(pjoin(path8, basename3));
|
|
3381
3635
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
3382
3636
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
3383
3637
|
} catch (err) {
|
|
@@ -3777,16 +4031,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
3777
4031
|
};
|
|
3778
4032
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
3779
4033
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
3780
|
-
function createFsWatchInstance(
|
|
4034
|
+
function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
|
|
3781
4035
|
const handleEvent = (rawEvent, evPath) => {
|
|
3782
|
-
listener(
|
|
3783
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
3784
|
-
if (evPath &&
|
|
3785
|
-
fsWatchBroadcast(sp.resolve(
|
|
4036
|
+
listener(path8);
|
|
4037
|
+
emitRaw(rawEvent, evPath, { watchedPath: path8 });
|
|
4038
|
+
if (evPath && path8 !== evPath) {
|
|
4039
|
+
fsWatchBroadcast(sp.resolve(path8, evPath), KEY_LISTENERS, sp.join(path8, evPath));
|
|
3786
4040
|
}
|
|
3787
4041
|
};
|
|
3788
4042
|
try {
|
|
3789
|
-
return fs_watch(
|
|
4043
|
+
return fs_watch(path8, {
|
|
3790
4044
|
persistent: options.persistent
|
|
3791
4045
|
}, handleEvent);
|
|
3792
4046
|
} catch (error) {
|
|
@@ -3802,12 +4056,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
3802
4056
|
listener(val1, val2, val3);
|
|
3803
4057
|
});
|
|
3804
4058
|
};
|
|
3805
|
-
var setFsWatchListener = (
|
|
4059
|
+
var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
3806
4060
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
3807
4061
|
let cont = FsWatchInstances.get(fullPath);
|
|
3808
4062
|
let watcher;
|
|
3809
4063
|
if (!options.persistent) {
|
|
3810
|
-
watcher = createFsWatchInstance(
|
|
4064
|
+
watcher = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
|
|
3811
4065
|
if (!watcher)
|
|
3812
4066
|
return;
|
|
3813
4067
|
return watcher.close.bind(watcher);
|
|
@@ -3818,7 +4072,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
3818
4072
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
3819
4073
|
} else {
|
|
3820
4074
|
watcher = createFsWatchInstance(
|
|
3821
|
-
|
|
4075
|
+
path8,
|
|
3822
4076
|
options,
|
|
3823
4077
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
3824
4078
|
errHandler,
|
|
@@ -3833,7 +4087,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
3833
4087
|
cont.watcherUnusable = true;
|
|
3834
4088
|
if (isWindows && error.code === "EPERM") {
|
|
3835
4089
|
try {
|
|
3836
|
-
const fd = await open(
|
|
4090
|
+
const fd = await open(path8, "r");
|
|
3837
4091
|
await fd.close();
|
|
3838
4092
|
broadcastErr(error);
|
|
3839
4093
|
} catch (err) {
|
|
@@ -3864,7 +4118,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
3864
4118
|
};
|
|
3865
4119
|
};
|
|
3866
4120
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
3867
|
-
var setFsWatchFileListener = (
|
|
4121
|
+
var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
|
|
3868
4122
|
const { listener, rawEmitter } = handlers;
|
|
3869
4123
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
3870
4124
|
const copts = cont && cont.options;
|
|
@@ -3886,7 +4140,7 @@ var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
|
|
|
3886
4140
|
});
|
|
3887
4141
|
const currmtime = curr.mtimeMs;
|
|
3888
4142
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
3889
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
4143
|
+
foreach(cont.listeners, (listener2) => listener2(path8, curr));
|
|
3890
4144
|
}
|
|
3891
4145
|
})
|
|
3892
4146
|
};
|
|
@@ -3916,13 +4170,13 @@ var NodeFsHandler = class {
|
|
|
3916
4170
|
* @param listener on fs change
|
|
3917
4171
|
* @returns closer for the watcher instance
|
|
3918
4172
|
*/
|
|
3919
|
-
_watchWithNodeFs(
|
|
4173
|
+
_watchWithNodeFs(path8, listener) {
|
|
3920
4174
|
const opts = this.fsw.options;
|
|
3921
|
-
const directory = sp.dirname(
|
|
3922
|
-
const basename3 = sp.basename(
|
|
4175
|
+
const directory = sp.dirname(path8);
|
|
4176
|
+
const basename3 = sp.basename(path8);
|
|
3923
4177
|
const parent = this.fsw._getWatchedDir(directory);
|
|
3924
4178
|
parent.add(basename3);
|
|
3925
|
-
const absolutePath = sp.resolve(
|
|
4179
|
+
const absolutePath = sp.resolve(path8);
|
|
3926
4180
|
const options = {
|
|
3927
4181
|
persistent: opts.persistent
|
|
3928
4182
|
};
|
|
@@ -3932,12 +4186,12 @@ var NodeFsHandler = class {
|
|
|
3932
4186
|
if (opts.usePolling) {
|
|
3933
4187
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
3934
4188
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
3935
|
-
closer = setFsWatchFileListener(
|
|
4189
|
+
closer = setFsWatchFileListener(path8, absolutePath, options, {
|
|
3936
4190
|
listener,
|
|
3937
4191
|
rawEmitter: this.fsw._emitRaw
|
|
3938
4192
|
});
|
|
3939
4193
|
} else {
|
|
3940
|
-
closer = setFsWatchListener(
|
|
4194
|
+
closer = setFsWatchListener(path8, absolutePath, options, {
|
|
3941
4195
|
listener,
|
|
3942
4196
|
errHandler: this._boundHandleError,
|
|
3943
4197
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -3959,7 +4213,7 @@ var NodeFsHandler = class {
|
|
|
3959
4213
|
let prevStats = stats;
|
|
3960
4214
|
if (parent.has(basename3))
|
|
3961
4215
|
return;
|
|
3962
|
-
const listener = async (
|
|
4216
|
+
const listener = async (path8, newStats) => {
|
|
3963
4217
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
3964
4218
|
return;
|
|
3965
4219
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -3973,11 +4227,11 @@ var NodeFsHandler = class {
|
|
|
3973
4227
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
3974
4228
|
}
|
|
3975
4229
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
3976
|
-
this.fsw._closeFile(
|
|
4230
|
+
this.fsw._closeFile(path8);
|
|
3977
4231
|
prevStats = newStats2;
|
|
3978
4232
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
3979
4233
|
if (closer2)
|
|
3980
|
-
this.fsw._addPathCloser(
|
|
4234
|
+
this.fsw._addPathCloser(path8, closer2);
|
|
3981
4235
|
} else {
|
|
3982
4236
|
prevStats = newStats2;
|
|
3983
4237
|
}
|
|
@@ -4009,7 +4263,7 @@ var NodeFsHandler = class {
|
|
|
4009
4263
|
* @param item basename of this item
|
|
4010
4264
|
* @returns true if no more processing is needed for this entry.
|
|
4011
4265
|
*/
|
|
4012
|
-
async _handleSymlink(entry, directory,
|
|
4266
|
+
async _handleSymlink(entry, directory, path8, item) {
|
|
4013
4267
|
if (this.fsw.closed) {
|
|
4014
4268
|
return;
|
|
4015
4269
|
}
|
|
@@ -4019,7 +4273,7 @@ var NodeFsHandler = class {
|
|
|
4019
4273
|
this.fsw._incrReadyCount();
|
|
4020
4274
|
let linkPath;
|
|
4021
4275
|
try {
|
|
4022
|
-
linkPath = await fsrealpath(
|
|
4276
|
+
linkPath = await fsrealpath(path8);
|
|
4023
4277
|
} catch (e) {
|
|
4024
4278
|
this.fsw._emitReady();
|
|
4025
4279
|
return true;
|
|
@@ -4029,12 +4283,12 @@ var NodeFsHandler = class {
|
|
|
4029
4283
|
if (dir.has(item)) {
|
|
4030
4284
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
4031
4285
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4032
|
-
this.fsw._emit(EV.CHANGE,
|
|
4286
|
+
this.fsw._emit(EV.CHANGE, path8, entry.stats);
|
|
4033
4287
|
}
|
|
4034
4288
|
} else {
|
|
4035
4289
|
dir.add(item);
|
|
4036
4290
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4037
|
-
this.fsw._emit(EV.ADD,
|
|
4291
|
+
this.fsw._emit(EV.ADD, path8, entry.stats);
|
|
4038
4292
|
}
|
|
4039
4293
|
this.fsw._emitReady();
|
|
4040
4294
|
return true;
|
|
@@ -4064,9 +4318,9 @@ var NodeFsHandler = class {
|
|
|
4064
4318
|
return;
|
|
4065
4319
|
}
|
|
4066
4320
|
const item = entry.path;
|
|
4067
|
-
let
|
|
4321
|
+
let path8 = sp.join(directory, item);
|
|
4068
4322
|
current.add(item);
|
|
4069
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
4323
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
|
|
4070
4324
|
return;
|
|
4071
4325
|
}
|
|
4072
4326
|
if (this.fsw.closed) {
|
|
@@ -4075,8 +4329,8 @@ var NodeFsHandler = class {
|
|
|
4075
4329
|
}
|
|
4076
4330
|
if (item === target || !target && !previous.has(item)) {
|
|
4077
4331
|
this.fsw._incrReadyCount();
|
|
4078
|
-
|
|
4079
|
-
this._addToNodeFs(
|
|
4332
|
+
path8 = sp.join(dir, sp.relative(dir, path8));
|
|
4333
|
+
this._addToNodeFs(path8, initialAdd, wh, depth + 1);
|
|
4080
4334
|
}
|
|
4081
4335
|
}).on(EV.ERROR, this._boundHandleError);
|
|
4082
4336
|
return new Promise((resolve4, reject) => {
|
|
@@ -4145,13 +4399,13 @@ var NodeFsHandler = class {
|
|
|
4145
4399
|
* @param depth Child path actually targeted for watch
|
|
4146
4400
|
* @param target Child path actually targeted for watch
|
|
4147
4401
|
*/
|
|
4148
|
-
async _addToNodeFs(
|
|
4402
|
+
async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
|
|
4149
4403
|
const ready = this.fsw._emitReady;
|
|
4150
|
-
if (this.fsw._isIgnored(
|
|
4404
|
+
if (this.fsw._isIgnored(path8) || this.fsw.closed) {
|
|
4151
4405
|
ready();
|
|
4152
4406
|
return false;
|
|
4153
4407
|
}
|
|
4154
|
-
const wh = this.fsw._getWatchHelpers(
|
|
4408
|
+
const wh = this.fsw._getWatchHelpers(path8);
|
|
4155
4409
|
if (priorWh) {
|
|
4156
4410
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
4157
4411
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -4167,8 +4421,8 @@ var NodeFsHandler = class {
|
|
|
4167
4421
|
const follow = this.fsw.options.followSymlinks;
|
|
4168
4422
|
let closer;
|
|
4169
4423
|
if (stats.isDirectory()) {
|
|
4170
|
-
const absPath = sp.resolve(
|
|
4171
|
-
const targetPath = follow ? await fsrealpath(
|
|
4424
|
+
const absPath = sp.resolve(path8);
|
|
4425
|
+
const targetPath = follow ? await fsrealpath(path8) : path8;
|
|
4172
4426
|
if (this.fsw.closed)
|
|
4173
4427
|
return;
|
|
4174
4428
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -4178,29 +4432,29 @@ var NodeFsHandler = class {
|
|
|
4178
4432
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
4179
4433
|
}
|
|
4180
4434
|
} else if (stats.isSymbolicLink()) {
|
|
4181
|
-
const targetPath = follow ? await fsrealpath(
|
|
4435
|
+
const targetPath = follow ? await fsrealpath(path8) : path8;
|
|
4182
4436
|
if (this.fsw.closed)
|
|
4183
4437
|
return;
|
|
4184
4438
|
const parent = sp.dirname(wh.watchPath);
|
|
4185
4439
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
4186
4440
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
4187
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
4441
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
|
|
4188
4442
|
if (this.fsw.closed)
|
|
4189
4443
|
return;
|
|
4190
4444
|
if (targetPath !== void 0) {
|
|
4191
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
4445
|
+
this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
|
|
4192
4446
|
}
|
|
4193
4447
|
} else {
|
|
4194
4448
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
4195
4449
|
}
|
|
4196
4450
|
ready();
|
|
4197
4451
|
if (closer)
|
|
4198
|
-
this.fsw._addPathCloser(
|
|
4452
|
+
this.fsw._addPathCloser(path8, closer);
|
|
4199
4453
|
return false;
|
|
4200
4454
|
} catch (error) {
|
|
4201
4455
|
if (this.fsw._handleError(error)) {
|
|
4202
4456
|
ready();
|
|
4203
|
-
return
|
|
4457
|
+
return path8;
|
|
4204
4458
|
}
|
|
4205
4459
|
}
|
|
4206
4460
|
}
|
|
@@ -4243,24 +4497,24 @@ function createPattern(matcher) {
|
|
|
4243
4497
|
}
|
|
4244
4498
|
return () => false;
|
|
4245
4499
|
}
|
|
4246
|
-
function normalizePath(
|
|
4247
|
-
if (typeof
|
|
4500
|
+
function normalizePath(path8) {
|
|
4501
|
+
if (typeof path8 !== "string")
|
|
4248
4502
|
throw new Error("string expected");
|
|
4249
|
-
|
|
4250
|
-
|
|
4503
|
+
path8 = sp2.normalize(path8);
|
|
4504
|
+
path8 = path8.replace(/\\/g, "/");
|
|
4251
4505
|
let prepend = false;
|
|
4252
|
-
if (
|
|
4506
|
+
if (path8.startsWith("//"))
|
|
4253
4507
|
prepend = true;
|
|
4254
|
-
|
|
4508
|
+
path8 = path8.replace(DOUBLE_SLASH_RE, "/");
|
|
4255
4509
|
if (prepend)
|
|
4256
|
-
|
|
4257
|
-
return
|
|
4510
|
+
path8 = "/" + path8;
|
|
4511
|
+
return path8;
|
|
4258
4512
|
}
|
|
4259
4513
|
function matchPatterns(patterns, testString, stats) {
|
|
4260
|
-
const
|
|
4514
|
+
const path8 = normalizePath(testString);
|
|
4261
4515
|
for (let index = 0; index < patterns.length; index++) {
|
|
4262
4516
|
const pattern = patterns[index];
|
|
4263
|
-
if (pattern(
|
|
4517
|
+
if (pattern(path8, stats)) {
|
|
4264
4518
|
return true;
|
|
4265
4519
|
}
|
|
4266
4520
|
}
|
|
@@ -4298,19 +4552,19 @@ var toUnix = (string) => {
|
|
|
4298
4552
|
}
|
|
4299
4553
|
return str;
|
|
4300
4554
|
};
|
|
4301
|
-
var normalizePathToUnix = (
|
|
4302
|
-
var normalizeIgnored = (cwd = "") => (
|
|
4303
|
-
if (typeof
|
|
4304
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
4555
|
+
var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
|
|
4556
|
+
var normalizeIgnored = (cwd = "") => (path8) => {
|
|
4557
|
+
if (typeof path8 === "string") {
|
|
4558
|
+
return normalizePathToUnix(sp2.isAbsolute(path8) ? path8 : sp2.join(cwd, path8));
|
|
4305
4559
|
} else {
|
|
4306
|
-
return
|
|
4560
|
+
return path8;
|
|
4307
4561
|
}
|
|
4308
4562
|
};
|
|
4309
|
-
var getAbsolutePath = (
|
|
4310
|
-
if (sp2.isAbsolute(
|
|
4311
|
-
return
|
|
4563
|
+
var getAbsolutePath = (path8, cwd) => {
|
|
4564
|
+
if (sp2.isAbsolute(path8)) {
|
|
4565
|
+
return path8;
|
|
4312
4566
|
}
|
|
4313
|
-
return sp2.join(cwd,
|
|
4567
|
+
return sp2.join(cwd, path8);
|
|
4314
4568
|
};
|
|
4315
4569
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
4316
4570
|
var DirEntry = class {
|
|
@@ -4375,10 +4629,10 @@ var WatchHelper = class {
|
|
|
4375
4629
|
dirParts;
|
|
4376
4630
|
followSymlinks;
|
|
4377
4631
|
statMethod;
|
|
4378
|
-
constructor(
|
|
4632
|
+
constructor(path8, follow, fsw) {
|
|
4379
4633
|
this.fsw = fsw;
|
|
4380
|
-
const watchPath =
|
|
4381
|
-
this.path =
|
|
4634
|
+
const watchPath = path8;
|
|
4635
|
+
this.path = path8 = path8.replace(REPLACER_RE, "");
|
|
4382
4636
|
this.watchPath = watchPath;
|
|
4383
4637
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
4384
4638
|
this.dirParts = [];
|
|
@@ -4518,20 +4772,20 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4518
4772
|
this._closePromise = void 0;
|
|
4519
4773
|
let paths = unifyPaths(paths_);
|
|
4520
4774
|
if (cwd) {
|
|
4521
|
-
paths = paths.map((
|
|
4522
|
-
const absPath = getAbsolutePath(
|
|
4775
|
+
paths = paths.map((path8) => {
|
|
4776
|
+
const absPath = getAbsolutePath(path8, cwd);
|
|
4523
4777
|
return absPath;
|
|
4524
4778
|
});
|
|
4525
4779
|
}
|
|
4526
|
-
paths.forEach((
|
|
4527
|
-
this._removeIgnoredPath(
|
|
4780
|
+
paths.forEach((path8) => {
|
|
4781
|
+
this._removeIgnoredPath(path8);
|
|
4528
4782
|
});
|
|
4529
4783
|
this._userIgnored = void 0;
|
|
4530
4784
|
if (!this._readyCount)
|
|
4531
4785
|
this._readyCount = 0;
|
|
4532
4786
|
this._readyCount += paths.length;
|
|
4533
|
-
Promise.all(paths.map(async (
|
|
4534
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
4787
|
+
Promise.all(paths.map(async (path8) => {
|
|
4788
|
+
const res = await this._nodeFsHandler._addToNodeFs(path8, !_internal, void 0, 0, _origAdd);
|
|
4535
4789
|
if (res)
|
|
4536
4790
|
this._emitReady();
|
|
4537
4791
|
return res;
|
|
@@ -4553,17 +4807,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4553
4807
|
return this;
|
|
4554
4808
|
const paths = unifyPaths(paths_);
|
|
4555
4809
|
const { cwd } = this.options;
|
|
4556
|
-
paths.forEach((
|
|
4557
|
-
if (!sp2.isAbsolute(
|
|
4810
|
+
paths.forEach((path8) => {
|
|
4811
|
+
if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
|
|
4558
4812
|
if (cwd)
|
|
4559
|
-
|
|
4560
|
-
|
|
4813
|
+
path8 = sp2.join(cwd, path8);
|
|
4814
|
+
path8 = sp2.resolve(path8);
|
|
4561
4815
|
}
|
|
4562
|
-
this._closePath(
|
|
4563
|
-
this._addIgnoredPath(
|
|
4564
|
-
if (this._watched.has(
|
|
4816
|
+
this._closePath(path8);
|
|
4817
|
+
this._addIgnoredPath(path8);
|
|
4818
|
+
if (this._watched.has(path8)) {
|
|
4565
4819
|
this._addIgnoredPath({
|
|
4566
|
-
path:
|
|
4820
|
+
path: path8,
|
|
4567
4821
|
recursive: true
|
|
4568
4822
|
});
|
|
4569
4823
|
}
|
|
@@ -4627,38 +4881,38 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4627
4881
|
* @param stats arguments to be passed with event
|
|
4628
4882
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
4629
4883
|
*/
|
|
4630
|
-
async _emit(event,
|
|
4884
|
+
async _emit(event, path8, stats) {
|
|
4631
4885
|
if (this.closed)
|
|
4632
4886
|
return;
|
|
4633
4887
|
const opts = this.options;
|
|
4634
4888
|
if (isWindows)
|
|
4635
|
-
|
|
4889
|
+
path8 = sp2.normalize(path8);
|
|
4636
4890
|
if (opts.cwd)
|
|
4637
|
-
|
|
4638
|
-
const args = [
|
|
4891
|
+
path8 = sp2.relative(opts.cwd, path8);
|
|
4892
|
+
const args = [path8];
|
|
4639
4893
|
if (stats != null)
|
|
4640
4894
|
args.push(stats);
|
|
4641
4895
|
const awf = opts.awaitWriteFinish;
|
|
4642
4896
|
let pw;
|
|
4643
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
4897
|
+
if (awf && (pw = this._pendingWrites.get(path8))) {
|
|
4644
4898
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
4645
4899
|
return this;
|
|
4646
4900
|
}
|
|
4647
4901
|
if (opts.atomic) {
|
|
4648
4902
|
if (event === EVENTS.UNLINK) {
|
|
4649
|
-
this._pendingUnlinks.set(
|
|
4903
|
+
this._pendingUnlinks.set(path8, [event, ...args]);
|
|
4650
4904
|
setTimeout(() => {
|
|
4651
|
-
this._pendingUnlinks.forEach((entry,
|
|
4905
|
+
this._pendingUnlinks.forEach((entry, path9) => {
|
|
4652
4906
|
this.emit(...entry);
|
|
4653
4907
|
this.emit(EVENTS.ALL, ...entry);
|
|
4654
|
-
this._pendingUnlinks.delete(
|
|
4908
|
+
this._pendingUnlinks.delete(path9);
|
|
4655
4909
|
});
|
|
4656
4910
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
4657
4911
|
return this;
|
|
4658
4912
|
}
|
|
4659
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
4913
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
|
|
4660
4914
|
event = EVENTS.CHANGE;
|
|
4661
|
-
this._pendingUnlinks.delete(
|
|
4915
|
+
this._pendingUnlinks.delete(path8);
|
|
4662
4916
|
}
|
|
4663
4917
|
}
|
|
4664
4918
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -4676,16 +4930,16 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4676
4930
|
this.emitWithAll(event, args);
|
|
4677
4931
|
}
|
|
4678
4932
|
};
|
|
4679
|
-
this._awaitWriteFinish(
|
|
4933
|
+
this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
|
|
4680
4934
|
return this;
|
|
4681
4935
|
}
|
|
4682
4936
|
if (event === EVENTS.CHANGE) {
|
|
4683
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
4937
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
|
|
4684
4938
|
if (isThrottled)
|
|
4685
4939
|
return this;
|
|
4686
4940
|
}
|
|
4687
4941
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
4688
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
4942
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
|
|
4689
4943
|
let stats2;
|
|
4690
4944
|
try {
|
|
4691
4945
|
stats2 = await stat3(fullPath);
|
|
@@ -4716,23 +4970,23 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4716
4970
|
* @param timeout duration of time to suppress duplicate actions
|
|
4717
4971
|
* @returns tracking object or false if action should be suppressed
|
|
4718
4972
|
*/
|
|
4719
|
-
_throttle(actionType,
|
|
4973
|
+
_throttle(actionType, path8, timeout) {
|
|
4720
4974
|
if (!this._throttled.has(actionType)) {
|
|
4721
4975
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
4722
4976
|
}
|
|
4723
4977
|
const action = this._throttled.get(actionType);
|
|
4724
4978
|
if (!action)
|
|
4725
4979
|
throw new Error("invalid throttle");
|
|
4726
|
-
const actionPath = action.get(
|
|
4980
|
+
const actionPath = action.get(path8);
|
|
4727
4981
|
if (actionPath) {
|
|
4728
4982
|
actionPath.count++;
|
|
4729
4983
|
return false;
|
|
4730
4984
|
}
|
|
4731
4985
|
let timeoutObject;
|
|
4732
4986
|
const clear = () => {
|
|
4733
|
-
const item = action.get(
|
|
4987
|
+
const item = action.get(path8);
|
|
4734
4988
|
const count = item ? item.count : 0;
|
|
4735
|
-
action.delete(
|
|
4989
|
+
action.delete(path8);
|
|
4736
4990
|
clearTimeout(timeoutObject);
|
|
4737
4991
|
if (item)
|
|
4738
4992
|
clearTimeout(item.timeoutObject);
|
|
@@ -4740,7 +4994,7 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4740
4994
|
};
|
|
4741
4995
|
timeoutObject = setTimeout(clear, timeout);
|
|
4742
4996
|
const thr = { timeoutObject, clear, count: 0 };
|
|
4743
|
-
action.set(
|
|
4997
|
+
action.set(path8, thr);
|
|
4744
4998
|
return thr;
|
|
4745
4999
|
}
|
|
4746
5000
|
_incrReadyCount() {
|
|
@@ -4754,44 +5008,44 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4754
5008
|
* @param event
|
|
4755
5009
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
4756
5010
|
*/
|
|
4757
|
-
_awaitWriteFinish(
|
|
5011
|
+
_awaitWriteFinish(path8, threshold, event, awfEmit) {
|
|
4758
5012
|
const awf = this.options.awaitWriteFinish;
|
|
4759
5013
|
if (typeof awf !== "object")
|
|
4760
5014
|
return;
|
|
4761
5015
|
const pollInterval = awf.pollInterval;
|
|
4762
5016
|
let timeoutHandler;
|
|
4763
|
-
let fullPath =
|
|
4764
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
4765
|
-
fullPath = sp2.join(this.options.cwd,
|
|
5017
|
+
let fullPath = path8;
|
|
5018
|
+
if (this.options.cwd && !sp2.isAbsolute(path8)) {
|
|
5019
|
+
fullPath = sp2.join(this.options.cwd, path8);
|
|
4766
5020
|
}
|
|
4767
5021
|
const now = /* @__PURE__ */ new Date();
|
|
4768
5022
|
const writes = this._pendingWrites;
|
|
4769
5023
|
function awaitWriteFinishFn(prevStat) {
|
|
4770
5024
|
statcb(fullPath, (err, curStat) => {
|
|
4771
|
-
if (err || !writes.has(
|
|
5025
|
+
if (err || !writes.has(path8)) {
|
|
4772
5026
|
if (err && err.code !== "ENOENT")
|
|
4773
5027
|
awfEmit(err);
|
|
4774
5028
|
return;
|
|
4775
5029
|
}
|
|
4776
5030
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
4777
5031
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
4778
|
-
writes.get(
|
|
5032
|
+
writes.get(path8).lastChange = now2;
|
|
4779
5033
|
}
|
|
4780
|
-
const pw = writes.get(
|
|
5034
|
+
const pw = writes.get(path8);
|
|
4781
5035
|
const df = now2 - pw.lastChange;
|
|
4782
5036
|
if (df >= threshold) {
|
|
4783
|
-
writes.delete(
|
|
5037
|
+
writes.delete(path8);
|
|
4784
5038
|
awfEmit(void 0, curStat);
|
|
4785
5039
|
} else {
|
|
4786
5040
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
4787
5041
|
}
|
|
4788
5042
|
});
|
|
4789
5043
|
}
|
|
4790
|
-
if (!writes.has(
|
|
4791
|
-
writes.set(
|
|
5044
|
+
if (!writes.has(path8)) {
|
|
5045
|
+
writes.set(path8, {
|
|
4792
5046
|
lastChange: now,
|
|
4793
5047
|
cancelWait: () => {
|
|
4794
|
-
writes.delete(
|
|
5048
|
+
writes.delete(path8);
|
|
4795
5049
|
clearTimeout(timeoutHandler);
|
|
4796
5050
|
return event;
|
|
4797
5051
|
}
|
|
@@ -4802,8 +5056,8 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4802
5056
|
/**
|
|
4803
5057
|
* Determines whether user has asked to ignore this path.
|
|
4804
5058
|
*/
|
|
4805
|
-
_isIgnored(
|
|
4806
|
-
if (this.options.atomic && DOT_RE.test(
|
|
5059
|
+
_isIgnored(path8, stats) {
|
|
5060
|
+
if (this.options.atomic && DOT_RE.test(path8))
|
|
4807
5061
|
return true;
|
|
4808
5062
|
if (!this._userIgnored) {
|
|
4809
5063
|
const { cwd } = this.options;
|
|
@@ -4813,17 +5067,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4813
5067
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
4814
5068
|
this._userIgnored = anymatch(list, void 0);
|
|
4815
5069
|
}
|
|
4816
|
-
return this._userIgnored(
|
|
5070
|
+
return this._userIgnored(path8, stats);
|
|
4817
5071
|
}
|
|
4818
|
-
_isntIgnored(
|
|
4819
|
-
return !this._isIgnored(
|
|
5072
|
+
_isntIgnored(path8, stat4) {
|
|
5073
|
+
return !this._isIgnored(path8, stat4);
|
|
4820
5074
|
}
|
|
4821
5075
|
/**
|
|
4822
5076
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
4823
5077
|
* @param path file or directory pattern being watched
|
|
4824
5078
|
*/
|
|
4825
|
-
_getWatchHelpers(
|
|
4826
|
-
return new WatchHelper(
|
|
5079
|
+
_getWatchHelpers(path8) {
|
|
5080
|
+
return new WatchHelper(path8, this.options.followSymlinks, this);
|
|
4827
5081
|
}
|
|
4828
5082
|
// Directory helpers
|
|
4829
5083
|
// -----------------
|
|
@@ -4855,63 +5109,63 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4855
5109
|
* @param item base path of item/directory
|
|
4856
5110
|
*/
|
|
4857
5111
|
_remove(directory, item, isDirectory) {
|
|
4858
|
-
const
|
|
4859
|
-
const fullPath = sp2.resolve(
|
|
4860
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
4861
|
-
if (!this._throttle("remove",
|
|
5112
|
+
const path8 = sp2.join(directory, item);
|
|
5113
|
+
const fullPath = sp2.resolve(path8);
|
|
5114
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
|
|
5115
|
+
if (!this._throttle("remove", path8, 100))
|
|
4862
5116
|
return;
|
|
4863
5117
|
if (!isDirectory && this._watched.size === 1) {
|
|
4864
5118
|
this.add(directory, item, true);
|
|
4865
5119
|
}
|
|
4866
|
-
const wp = this._getWatchedDir(
|
|
5120
|
+
const wp = this._getWatchedDir(path8);
|
|
4867
5121
|
const nestedDirectoryChildren = wp.getChildren();
|
|
4868
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
5122
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
|
|
4869
5123
|
const parent = this._getWatchedDir(directory);
|
|
4870
5124
|
const wasTracked = parent.has(item);
|
|
4871
5125
|
parent.remove(item);
|
|
4872
5126
|
if (this._symlinkPaths.has(fullPath)) {
|
|
4873
5127
|
this._symlinkPaths.delete(fullPath);
|
|
4874
5128
|
}
|
|
4875
|
-
let relPath =
|
|
5129
|
+
let relPath = path8;
|
|
4876
5130
|
if (this.options.cwd)
|
|
4877
|
-
relPath = sp2.relative(this.options.cwd,
|
|
5131
|
+
relPath = sp2.relative(this.options.cwd, path8);
|
|
4878
5132
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
4879
5133
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
4880
5134
|
if (event === EVENTS.ADD)
|
|
4881
5135
|
return;
|
|
4882
5136
|
}
|
|
4883
|
-
this._watched.delete(
|
|
5137
|
+
this._watched.delete(path8);
|
|
4884
5138
|
this._watched.delete(fullPath);
|
|
4885
5139
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
4886
|
-
if (wasTracked && !this._isIgnored(
|
|
4887
|
-
this._emit(eventName,
|
|
4888
|
-
this._closePath(
|
|
5140
|
+
if (wasTracked && !this._isIgnored(path8))
|
|
5141
|
+
this._emit(eventName, path8);
|
|
5142
|
+
this._closePath(path8);
|
|
4889
5143
|
}
|
|
4890
5144
|
/**
|
|
4891
5145
|
* Closes all watchers for a path
|
|
4892
5146
|
*/
|
|
4893
|
-
_closePath(
|
|
4894
|
-
this._closeFile(
|
|
4895
|
-
const dir = sp2.dirname(
|
|
4896
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
5147
|
+
_closePath(path8) {
|
|
5148
|
+
this._closeFile(path8);
|
|
5149
|
+
const dir = sp2.dirname(path8);
|
|
5150
|
+
this._getWatchedDir(dir).remove(sp2.basename(path8));
|
|
4897
5151
|
}
|
|
4898
5152
|
/**
|
|
4899
5153
|
* Closes only file-specific watchers
|
|
4900
5154
|
*/
|
|
4901
|
-
_closeFile(
|
|
4902
|
-
const closers = this._closers.get(
|
|
5155
|
+
_closeFile(path8) {
|
|
5156
|
+
const closers = this._closers.get(path8);
|
|
4903
5157
|
if (!closers)
|
|
4904
5158
|
return;
|
|
4905
5159
|
closers.forEach((closer) => closer());
|
|
4906
|
-
this._closers.delete(
|
|
5160
|
+
this._closers.delete(path8);
|
|
4907
5161
|
}
|
|
4908
|
-
_addPathCloser(
|
|
5162
|
+
_addPathCloser(path8, closer) {
|
|
4909
5163
|
if (!closer)
|
|
4910
5164
|
return;
|
|
4911
|
-
let list = this._closers.get(
|
|
5165
|
+
let list = this._closers.get(path8);
|
|
4912
5166
|
if (!list) {
|
|
4913
5167
|
list = [];
|
|
4914
|
-
this._closers.set(
|
|
5168
|
+
this._closers.set(path8, list);
|
|
4915
5169
|
}
|
|
4916
5170
|
list.push(closer);
|
|
4917
5171
|
}
|
|
@@ -4941,7 +5195,7 @@ function watch(paths, options = {}) {
|
|
|
4941
5195
|
var chokidar_default = { watch, FSWatcher };
|
|
4942
5196
|
|
|
4943
5197
|
// src/watcher/index.ts
|
|
4944
|
-
import * as
|
|
5198
|
+
import * as path6 from "path";
|
|
4945
5199
|
var FileWatcher = class {
|
|
4946
5200
|
watcher = null;
|
|
4947
5201
|
projectRoot;
|
|
@@ -4962,7 +5216,7 @@ var FileWatcher = class {
|
|
|
4962
5216
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
4963
5217
|
this.watcher = chokidar_default.watch(this.projectRoot, {
|
|
4964
5218
|
ignored: (filePath) => {
|
|
4965
|
-
const relativePath =
|
|
5219
|
+
const relativePath = path6.relative(this.projectRoot, filePath);
|
|
4966
5220
|
if (!relativePath) return false;
|
|
4967
5221
|
if (ignoreFilter.ignores(relativePath)) {
|
|
4968
5222
|
return true;
|
|
@@ -5006,7 +5260,7 @@ var FileWatcher = class {
|
|
|
5006
5260
|
return;
|
|
5007
5261
|
}
|
|
5008
5262
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
5009
|
-
([
|
|
5263
|
+
([path8, type]) => ({ path: path8, type })
|
|
5010
5264
|
);
|
|
5011
5265
|
this.pendingChanges.clear();
|
|
5012
5266
|
try {
|
|
@@ -5031,9 +5285,82 @@ var FileWatcher = class {
|
|
|
5031
5285
|
return this.watcher !== null;
|
|
5032
5286
|
}
|
|
5033
5287
|
};
|
|
5288
|
+
var GitHeadWatcher = class {
|
|
5289
|
+
watcher = null;
|
|
5290
|
+
projectRoot;
|
|
5291
|
+
currentBranch = null;
|
|
5292
|
+
onBranchChange = null;
|
|
5293
|
+
debounceTimer = null;
|
|
5294
|
+
debounceMs = 100;
|
|
5295
|
+
// Short debounce for git operations
|
|
5296
|
+
constructor(projectRoot) {
|
|
5297
|
+
this.projectRoot = projectRoot;
|
|
5298
|
+
}
|
|
5299
|
+
start(handler) {
|
|
5300
|
+
if (this.watcher) {
|
|
5301
|
+
return;
|
|
5302
|
+
}
|
|
5303
|
+
if (!isGitRepo(this.projectRoot)) {
|
|
5304
|
+
return;
|
|
5305
|
+
}
|
|
5306
|
+
this.onBranchChange = handler;
|
|
5307
|
+
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
5308
|
+
const headPath = getHeadPath(this.projectRoot);
|
|
5309
|
+
const refsPath = path6.join(this.projectRoot, ".git", "refs", "heads");
|
|
5310
|
+
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
5311
|
+
persistent: true,
|
|
5312
|
+
ignoreInitial: true,
|
|
5313
|
+
awaitWriteFinish: {
|
|
5314
|
+
stabilityThreshold: 50,
|
|
5315
|
+
pollInterval: 10
|
|
5316
|
+
}
|
|
5317
|
+
});
|
|
5318
|
+
this.watcher.on("change", () => this.handleHeadChange());
|
|
5319
|
+
this.watcher.on("add", () => this.handleHeadChange());
|
|
5320
|
+
}
|
|
5321
|
+
handleHeadChange() {
|
|
5322
|
+
if (this.debounceTimer) {
|
|
5323
|
+
clearTimeout(this.debounceTimer);
|
|
5324
|
+
}
|
|
5325
|
+
this.debounceTimer = setTimeout(() => {
|
|
5326
|
+
this.checkBranchChange();
|
|
5327
|
+
}, this.debounceMs);
|
|
5328
|
+
}
|
|
5329
|
+
async checkBranchChange() {
|
|
5330
|
+
const newBranch = getCurrentBranch(this.projectRoot);
|
|
5331
|
+
if (newBranch && newBranch !== this.currentBranch && this.onBranchChange) {
|
|
5332
|
+
const oldBranch = this.currentBranch;
|
|
5333
|
+
this.currentBranch = newBranch;
|
|
5334
|
+
try {
|
|
5335
|
+
await this.onBranchChange(oldBranch, newBranch);
|
|
5336
|
+
} catch (error) {
|
|
5337
|
+
console.error("Error handling branch change:", error);
|
|
5338
|
+
}
|
|
5339
|
+
} else if (newBranch) {
|
|
5340
|
+
this.currentBranch = newBranch;
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
getCurrentBranch() {
|
|
5344
|
+
return this.currentBranch;
|
|
5345
|
+
}
|
|
5346
|
+
stop() {
|
|
5347
|
+
if (this.debounceTimer) {
|
|
5348
|
+
clearTimeout(this.debounceTimer);
|
|
5349
|
+
this.debounceTimer = null;
|
|
5350
|
+
}
|
|
5351
|
+
if (this.watcher) {
|
|
5352
|
+
this.watcher.close();
|
|
5353
|
+
this.watcher = null;
|
|
5354
|
+
}
|
|
5355
|
+
this.onBranchChange = null;
|
|
5356
|
+
}
|
|
5357
|
+
isRunning() {
|
|
5358
|
+
return this.watcher !== null;
|
|
5359
|
+
}
|
|
5360
|
+
};
|
|
5034
5361
|
function createWatcherWithIndexer(indexer, projectRoot, config) {
|
|
5035
|
-
const
|
|
5036
|
-
|
|
5362
|
+
const fileWatcher = new FileWatcher(projectRoot, config);
|
|
5363
|
+
fileWatcher.start(async (changes) => {
|
|
5037
5364
|
const hasAddOrChange = changes.some(
|
|
5038
5365
|
(c) => c.type === "add" || c.type === "change"
|
|
5039
5366
|
);
|
|
@@ -5042,7 +5369,22 @@ function createWatcherWithIndexer(indexer, projectRoot, config) {
|
|
|
5042
5369
|
await indexer.index();
|
|
5043
5370
|
}
|
|
5044
5371
|
});
|
|
5045
|
-
|
|
5372
|
+
let gitWatcher = null;
|
|
5373
|
+
if (isGitRepo(projectRoot)) {
|
|
5374
|
+
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
5375
|
+
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
5376
|
+
console.log(`Branch changed: ${oldBranch ?? "(none)"} -> ${newBranch}`);
|
|
5377
|
+
await indexer.index();
|
|
5378
|
+
});
|
|
5379
|
+
}
|
|
5380
|
+
return {
|
|
5381
|
+
fileWatcher,
|
|
5382
|
+
gitWatcher,
|
|
5383
|
+
stop() {
|
|
5384
|
+
fileWatcher.stop();
|
|
5385
|
+
gitWatcher?.stop();
|
|
5386
|
+
}
|
|
5387
|
+
};
|
|
5046
5388
|
}
|
|
5047
5389
|
|
|
5048
5390
|
// src/tools/index.ts
|
|
@@ -5126,13 +5468,19 @@ var index_health_check = tool({
|
|
|
5126
5468
|
async execute() {
|
|
5127
5469
|
const indexer = getIndexer();
|
|
5128
5470
|
const result = await indexer.healthCheck();
|
|
5129
|
-
if (result.removed === 0) {
|
|
5471
|
+
if (result.removed === 0 && result.gcOrphanEmbeddings === 0 && result.gcOrphanChunks === 0) {
|
|
5130
5472
|
return "Index is healthy. No stale entries found.";
|
|
5131
5473
|
}
|
|
5132
|
-
const lines = [
|
|
5133
|
-
|
|
5134
|
-
` Removed stale entries: ${result.removed}`
|
|
5135
|
-
|
|
5474
|
+
const lines = [`Health check complete:`];
|
|
5475
|
+
if (result.removed > 0) {
|
|
5476
|
+
lines.push(` Removed stale entries: ${result.removed}`);
|
|
5477
|
+
}
|
|
5478
|
+
if (result.gcOrphanEmbeddings > 0) {
|
|
5479
|
+
lines.push(` Garbage collected orphan embeddings: ${result.gcOrphanEmbeddings}`);
|
|
5480
|
+
}
|
|
5481
|
+
if (result.gcOrphanChunks > 0) {
|
|
5482
|
+
lines.push(` Garbage collected orphan chunks: ${result.gcOrphanChunks}`);
|
|
5483
|
+
}
|
|
5136
5484
|
if (result.filePaths.length > 0) {
|
|
5137
5485
|
lines.push(` Cleaned paths: ${result.filePaths.join(", ")}`);
|
|
5138
5486
|
}
|
|
@@ -5187,21 +5535,26 @@ function formatStatus(status) {
|
|
|
5187
5535
|
if (!status.indexed) {
|
|
5188
5536
|
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
5189
5537
|
}
|
|
5190
|
-
|
|
5538
|
+
const lines = [
|
|
5191
5539
|
`Index status:`,
|
|
5192
5540
|
` Indexed chunks: ${status.vectorCount.toLocaleString()}`,
|
|
5193
5541
|
` Provider: ${status.provider}`,
|
|
5194
5542
|
` Model: ${status.model}`,
|
|
5195
5543
|
` Location: ${status.indexPath}`
|
|
5196
|
-
]
|
|
5544
|
+
];
|
|
5545
|
+
if (status.currentBranch !== "default") {
|
|
5546
|
+
lines.push(` Current branch: ${status.currentBranch}`);
|
|
5547
|
+
lines.push(` Base branch: ${status.baseBranch}`);
|
|
5548
|
+
}
|
|
5549
|
+
return lines.join("\n");
|
|
5197
5550
|
}
|
|
5198
5551
|
|
|
5199
5552
|
// src/index.ts
|
|
5200
5553
|
function loadPluginConfig(projectRoot) {
|
|
5201
|
-
const configPath =
|
|
5554
|
+
const configPath = path7.join(projectRoot, ".opencode", "codebase-index.json");
|
|
5202
5555
|
try {
|
|
5203
|
-
if (
|
|
5204
|
-
const content =
|
|
5556
|
+
if (existsSync5(configPath)) {
|
|
5557
|
+
const content = readFileSync5(configPath, "utf-8");
|
|
5205
5558
|
return JSON.parse(content);
|
|
5206
5559
|
}
|
|
5207
5560
|
} catch {
|