opencode-codebase-index 0.1.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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.cjs
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.
|
|
@@ -657,8 +657,8 @@ __export(index_exports, {
|
|
|
657
657
|
default: () => index_default
|
|
658
658
|
});
|
|
659
659
|
module.exports = __toCommonJS(index_exports);
|
|
660
|
-
var
|
|
661
|
-
var
|
|
660
|
+
var import_fs5 = require("fs");
|
|
661
|
+
var path7 = __toESM(require("path"), 1);
|
|
662
662
|
|
|
663
663
|
// src/config/schema.ts
|
|
664
664
|
var DEFAULT_INCLUDE = [
|
|
@@ -811,8 +811,8 @@ function getDefaultModelForProvider(provider) {
|
|
|
811
811
|
}
|
|
812
812
|
|
|
813
813
|
// src/indexer/index.ts
|
|
814
|
-
var
|
|
815
|
-
var
|
|
814
|
+
var import_fs4 = require("fs");
|
|
815
|
+
var path5 = __toESM(require("path"), 1);
|
|
816
816
|
|
|
817
817
|
// node_modules/eventemitter3/index.mjs
|
|
818
818
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -2193,7 +2193,10 @@ function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatter
|
|
|
2193
2193
|
return false;
|
|
2194
2194
|
}
|
|
2195
2195
|
function matchGlob(filePath, pattern) {
|
|
2196
|
-
|
|
2196
|
+
let regexPattern = pattern.replace(/\*\*/g, "<<<DOUBLESTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<DOUBLESTAR>>>/g, ".*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, p1) => `(${p1.split(",").join("|")})`);
|
|
2197
|
+
if (regexPattern.startsWith(".*/")) {
|
|
2198
|
+
regexPattern = `(.*\\/)?${regexPattern.slice(3)}`;
|
|
2199
|
+
}
|
|
2197
2200
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
2198
2201
|
return regex.test(filePath);
|
|
2199
2202
|
}
|
|
@@ -2665,37 +2668,181 @@ var InvertedIndex = class {
|
|
|
2665
2668
|
return this.inner.documentCount();
|
|
2666
2669
|
}
|
|
2667
2670
|
};
|
|
2671
|
+
var Database = class {
|
|
2672
|
+
inner;
|
|
2673
|
+
constructor(dbPath) {
|
|
2674
|
+
this.inner = new native.Database(dbPath);
|
|
2675
|
+
}
|
|
2676
|
+
embeddingExists(contentHash) {
|
|
2677
|
+
return this.inner.embeddingExists(contentHash);
|
|
2678
|
+
}
|
|
2679
|
+
getEmbedding(contentHash) {
|
|
2680
|
+
return this.inner.getEmbedding(contentHash) ?? null;
|
|
2681
|
+
}
|
|
2682
|
+
upsertEmbedding(contentHash, embedding, chunkText, model) {
|
|
2683
|
+
this.inner.upsertEmbedding(contentHash, embedding, chunkText, model);
|
|
2684
|
+
}
|
|
2685
|
+
getMissingEmbeddings(contentHashes) {
|
|
2686
|
+
return this.inner.getMissingEmbeddings(contentHashes);
|
|
2687
|
+
}
|
|
2688
|
+
upsertChunk(chunk) {
|
|
2689
|
+
this.inner.upsertChunk(chunk);
|
|
2690
|
+
}
|
|
2691
|
+
getChunk(chunkId) {
|
|
2692
|
+
return this.inner.getChunk(chunkId) ?? null;
|
|
2693
|
+
}
|
|
2694
|
+
getChunksByFile(filePath) {
|
|
2695
|
+
return this.inner.getChunksByFile(filePath);
|
|
2696
|
+
}
|
|
2697
|
+
deleteChunksByFile(filePath) {
|
|
2698
|
+
return this.inner.deleteChunksByFile(filePath);
|
|
2699
|
+
}
|
|
2700
|
+
addChunksToBranch(branch, chunkIds) {
|
|
2701
|
+
this.inner.addChunksToBranch(branch, chunkIds);
|
|
2702
|
+
}
|
|
2703
|
+
clearBranch(branch) {
|
|
2704
|
+
return this.inner.clearBranch(branch);
|
|
2705
|
+
}
|
|
2706
|
+
getBranchChunkIds(branch) {
|
|
2707
|
+
return this.inner.getBranchChunkIds(branch);
|
|
2708
|
+
}
|
|
2709
|
+
getBranchDelta(branch, baseBranch) {
|
|
2710
|
+
return this.inner.getBranchDelta(branch, baseBranch);
|
|
2711
|
+
}
|
|
2712
|
+
chunkExistsOnBranch(branch, chunkId) {
|
|
2713
|
+
return this.inner.chunkExistsOnBranch(branch, chunkId);
|
|
2714
|
+
}
|
|
2715
|
+
getAllBranches() {
|
|
2716
|
+
return this.inner.getAllBranches();
|
|
2717
|
+
}
|
|
2718
|
+
getMetadata(key) {
|
|
2719
|
+
return this.inner.getMetadata(key) ?? null;
|
|
2720
|
+
}
|
|
2721
|
+
setMetadata(key, value) {
|
|
2722
|
+
this.inner.setMetadata(key, value);
|
|
2723
|
+
}
|
|
2724
|
+
deleteMetadata(key) {
|
|
2725
|
+
return this.inner.deleteMetadata(key);
|
|
2726
|
+
}
|
|
2727
|
+
gcOrphanEmbeddings() {
|
|
2728
|
+
return this.inner.gcOrphanEmbeddings();
|
|
2729
|
+
}
|
|
2730
|
+
gcOrphanChunks() {
|
|
2731
|
+
return this.inner.gcOrphanChunks();
|
|
2732
|
+
}
|
|
2733
|
+
getStats() {
|
|
2734
|
+
return this.inner.getStats();
|
|
2735
|
+
}
|
|
2736
|
+
};
|
|
2737
|
+
|
|
2738
|
+
// src/git/index.ts
|
|
2739
|
+
var import_fs3 = require("fs");
|
|
2740
|
+
var path4 = __toESM(require("path"), 1);
|
|
2741
|
+
var import_child_process = require("child_process");
|
|
2742
|
+
function isGitRepo(dir) {
|
|
2743
|
+
return (0, import_fs3.existsSync)(path4.join(dir, ".git"));
|
|
2744
|
+
}
|
|
2745
|
+
function getCurrentBranch(repoRoot) {
|
|
2746
|
+
const headPath = path4.join(repoRoot, ".git", "HEAD");
|
|
2747
|
+
if (!(0, import_fs3.existsSync)(headPath)) {
|
|
2748
|
+
return null;
|
|
2749
|
+
}
|
|
2750
|
+
try {
|
|
2751
|
+
const headContent = (0, import_fs3.readFileSync)(headPath, "utf-8").trim();
|
|
2752
|
+
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
2753
|
+
if (match) {
|
|
2754
|
+
return match[1];
|
|
2755
|
+
}
|
|
2756
|
+
if (/^[0-9a-f]{40}$/i.test(headContent)) {
|
|
2757
|
+
return headContent.slice(0, 7);
|
|
2758
|
+
}
|
|
2759
|
+
return null;
|
|
2760
|
+
} catch {
|
|
2761
|
+
return null;
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
function getBaseBranch(repoRoot) {
|
|
2765
|
+
const candidates = ["main", "master", "develop", "trunk"];
|
|
2766
|
+
for (const candidate of candidates) {
|
|
2767
|
+
const refPath = path4.join(repoRoot, ".git", "refs", "heads", candidate);
|
|
2768
|
+
if ((0, import_fs3.existsSync)(refPath)) {
|
|
2769
|
+
return candidate;
|
|
2770
|
+
}
|
|
2771
|
+
const packedRefsPath = path4.join(repoRoot, ".git", "packed-refs");
|
|
2772
|
+
if ((0, import_fs3.existsSync)(packedRefsPath)) {
|
|
2773
|
+
try {
|
|
2774
|
+
const content = (0, import_fs3.readFileSync)(packedRefsPath, "utf-8");
|
|
2775
|
+
if (content.includes(`refs/heads/${candidate}`)) {
|
|
2776
|
+
return candidate;
|
|
2777
|
+
}
|
|
2778
|
+
} catch {
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
try {
|
|
2783
|
+
const result = (0, import_child_process.execSync)("git remote show origin", {
|
|
2784
|
+
cwd: repoRoot,
|
|
2785
|
+
encoding: "utf-8",
|
|
2786
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2787
|
+
});
|
|
2788
|
+
const match = result.match(/HEAD branch: (.+)/);
|
|
2789
|
+
if (match) {
|
|
2790
|
+
return match[1].trim();
|
|
2791
|
+
}
|
|
2792
|
+
} catch {
|
|
2793
|
+
}
|
|
2794
|
+
return getCurrentBranch(repoRoot) ?? "main";
|
|
2795
|
+
}
|
|
2796
|
+
function getBranchOrDefault(repoRoot) {
|
|
2797
|
+
if (!isGitRepo(repoRoot)) {
|
|
2798
|
+
return "default";
|
|
2799
|
+
}
|
|
2800
|
+
return getCurrentBranch(repoRoot) ?? "default";
|
|
2801
|
+
}
|
|
2802
|
+
function getHeadPath(repoRoot) {
|
|
2803
|
+
return path4.join(repoRoot, ".git", "HEAD");
|
|
2804
|
+
}
|
|
2668
2805
|
|
|
2669
2806
|
// src/indexer/index.ts
|
|
2807
|
+
function float32ArrayToBuffer(arr) {
|
|
2808
|
+
const float32 = new Float32Array(arr);
|
|
2809
|
+
return Buffer.from(float32.buffer);
|
|
2810
|
+
}
|
|
2811
|
+
function bufferToFloat32Array(buf) {
|
|
2812
|
+
return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
2813
|
+
}
|
|
2670
2814
|
var Indexer = class {
|
|
2671
2815
|
config;
|
|
2672
2816
|
projectRoot;
|
|
2673
2817
|
indexPath;
|
|
2674
2818
|
store = null;
|
|
2675
2819
|
invertedIndex = null;
|
|
2820
|
+
database = null;
|
|
2676
2821
|
provider = null;
|
|
2677
2822
|
detectedProvider = null;
|
|
2678
2823
|
fileHashCache = /* @__PURE__ */ new Map();
|
|
2679
2824
|
fileHashCachePath = "";
|
|
2680
2825
|
failedBatchesPath = "";
|
|
2826
|
+
currentBranch = "default";
|
|
2827
|
+
baseBranch = "main";
|
|
2681
2828
|
constructor(projectRoot, config) {
|
|
2682
2829
|
this.projectRoot = projectRoot;
|
|
2683
2830
|
this.config = config;
|
|
2684
2831
|
this.indexPath = this.getIndexPath();
|
|
2685
|
-
this.fileHashCachePath =
|
|
2686
|
-
this.failedBatchesPath =
|
|
2832
|
+
this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
|
|
2833
|
+
this.failedBatchesPath = path5.join(this.indexPath, "failed-batches.json");
|
|
2687
2834
|
}
|
|
2688
2835
|
getIndexPath() {
|
|
2689
2836
|
if (this.config.scope === "global") {
|
|
2690
2837
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
2691
|
-
return
|
|
2838
|
+
return path5.join(homeDir, ".opencode", "global-index");
|
|
2692
2839
|
}
|
|
2693
|
-
return
|
|
2840
|
+
return path5.join(this.projectRoot, ".opencode", "index");
|
|
2694
2841
|
}
|
|
2695
2842
|
loadFileHashCache() {
|
|
2696
2843
|
try {
|
|
2697
|
-
if ((0,
|
|
2698
|
-
const data = (0,
|
|
2844
|
+
if ((0, import_fs4.existsSync)(this.fileHashCachePath)) {
|
|
2845
|
+
const data = (0, import_fs4.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
2699
2846
|
const parsed = JSON.parse(data);
|
|
2700
2847
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
2701
2848
|
}
|
|
@@ -2708,12 +2855,12 @@ var Indexer = class {
|
|
|
2708
2855
|
for (const [k, v] of this.fileHashCache) {
|
|
2709
2856
|
obj[k] = v;
|
|
2710
2857
|
}
|
|
2711
|
-
(0,
|
|
2858
|
+
(0, import_fs4.writeFileSync)(this.fileHashCachePath, JSON.stringify(obj));
|
|
2712
2859
|
}
|
|
2713
2860
|
loadFailedBatches() {
|
|
2714
2861
|
try {
|
|
2715
|
-
if ((0,
|
|
2716
|
-
const data = (0,
|
|
2862
|
+
if ((0, import_fs4.existsSync)(this.failedBatchesPath)) {
|
|
2863
|
+
const data = (0, import_fs4.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
2717
2864
|
return JSON.parse(data);
|
|
2718
2865
|
}
|
|
2719
2866
|
} catch {
|
|
@@ -2723,13 +2870,13 @@ var Indexer = class {
|
|
|
2723
2870
|
}
|
|
2724
2871
|
saveFailedBatches(batches) {
|
|
2725
2872
|
if (batches.length === 0) {
|
|
2726
|
-
if ((0,
|
|
2727
|
-
|
|
2873
|
+
if ((0, import_fs4.existsSync)(this.failedBatchesPath)) {
|
|
2874
|
+
import_fs4.promises.unlink(this.failedBatchesPath).catch(() => {
|
|
2728
2875
|
});
|
|
2729
2876
|
}
|
|
2730
2877
|
return;
|
|
2731
2878
|
}
|
|
2732
|
-
(0,
|
|
2879
|
+
(0, import_fs4.writeFileSync)(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
2733
2880
|
}
|
|
2734
2881
|
addFailedBatch(batch, error) {
|
|
2735
2882
|
const existing = this.loadFailedBatches();
|
|
@@ -2752,27 +2899,68 @@ var Indexer = class {
|
|
|
2752
2899
|
this.detectedProvider.credentials,
|
|
2753
2900
|
this.detectedProvider.modelInfo
|
|
2754
2901
|
);
|
|
2755
|
-
await
|
|
2902
|
+
await import_fs4.promises.mkdir(this.indexPath, { recursive: true });
|
|
2756
2903
|
const dimensions = this.detectedProvider.modelInfo.dimensions;
|
|
2757
|
-
const storePath =
|
|
2904
|
+
const storePath = path5.join(this.indexPath, "vectors");
|
|
2758
2905
|
this.store = new VectorStore(storePath, dimensions);
|
|
2759
|
-
const indexFilePath =
|
|
2760
|
-
if ((0,
|
|
2906
|
+
const indexFilePath = path5.join(this.indexPath, "vectors.usearch");
|
|
2907
|
+
if ((0, import_fs4.existsSync)(indexFilePath)) {
|
|
2761
2908
|
this.store.load();
|
|
2762
2909
|
}
|
|
2763
|
-
const invertedIndexPath =
|
|
2910
|
+
const invertedIndexPath = path5.join(this.indexPath, "inverted-index.json");
|
|
2764
2911
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
2765
|
-
|
|
2912
|
+
try {
|
|
2913
|
+
this.invertedIndex.load();
|
|
2914
|
+
} catch {
|
|
2915
|
+
if ((0, import_fs4.existsSync)(invertedIndexPath)) {
|
|
2916
|
+
await import_fs4.promises.unlink(invertedIndexPath);
|
|
2917
|
+
}
|
|
2918
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
2919
|
+
}
|
|
2920
|
+
const dbPath = path5.join(this.indexPath, "codebase.db");
|
|
2921
|
+
const dbIsNew = !(0, import_fs4.existsSync)(dbPath);
|
|
2922
|
+
this.database = new Database(dbPath);
|
|
2923
|
+
if (dbIsNew && this.store.count() > 0) {
|
|
2924
|
+
this.migrateFromLegacyIndex();
|
|
2925
|
+
}
|
|
2926
|
+
if (isGitRepo(this.projectRoot)) {
|
|
2927
|
+
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
2928
|
+
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
2929
|
+
} else {
|
|
2930
|
+
this.currentBranch = "default";
|
|
2931
|
+
this.baseBranch = "default";
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
migrateFromLegacyIndex() {
|
|
2935
|
+
if (!this.store || !this.database) return;
|
|
2936
|
+
const allMetadata = this.store.getAllMetadata();
|
|
2937
|
+
const chunkIds = [];
|
|
2938
|
+
for (const { key, metadata } of allMetadata) {
|
|
2939
|
+
const chunkData = {
|
|
2940
|
+
chunkId: key,
|
|
2941
|
+
contentHash: metadata.hash,
|
|
2942
|
+
filePath: metadata.filePath,
|
|
2943
|
+
startLine: metadata.startLine,
|
|
2944
|
+
endLine: metadata.endLine,
|
|
2945
|
+
nodeType: metadata.chunkType,
|
|
2946
|
+
name: metadata.name,
|
|
2947
|
+
language: metadata.language
|
|
2948
|
+
};
|
|
2949
|
+
this.database.upsertChunk(chunkData);
|
|
2950
|
+
chunkIds.push(key);
|
|
2951
|
+
}
|
|
2952
|
+
this.database.addChunksToBranch(this.currentBranch || "default", chunkIds);
|
|
2766
2953
|
}
|
|
2767
2954
|
async ensureInitialized() {
|
|
2768
|
-
if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider) {
|
|
2955
|
+
if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider || !this.database) {
|
|
2769
2956
|
await this.initialize();
|
|
2770
2957
|
}
|
|
2771
2958
|
return {
|
|
2772
2959
|
store: this.store,
|
|
2773
2960
|
provider: this.provider,
|
|
2774
2961
|
invertedIndex: this.invertedIndex,
|
|
2775
|
-
detectedProvider: this.detectedProvider
|
|
2962
|
+
detectedProvider: this.detectedProvider,
|
|
2963
|
+
database: this.database
|
|
2776
2964
|
};
|
|
2777
2965
|
}
|
|
2778
2966
|
async estimateCost() {
|
|
@@ -2786,7 +2974,7 @@ var Indexer = class {
|
|
|
2786
2974
|
return createCostEstimate(files, detectedProvider);
|
|
2787
2975
|
}
|
|
2788
2976
|
async index(onProgress) {
|
|
2789
|
-
const { store, provider, invertedIndex } = await this.ensureInitialized();
|
|
2977
|
+
const { store, provider, invertedIndex, database, detectedProvider } = await this.ensureInitialized();
|
|
2790
2978
|
const startTime = Date.now();
|
|
2791
2979
|
const stats = {
|
|
2792
2980
|
totalFiles: 0,
|
|
@@ -2825,7 +3013,7 @@ var Indexer = class {
|
|
|
2825
3013
|
if (this.fileHashCache.get(f.path) === currentHash) {
|
|
2826
3014
|
unchangedFilePaths.add(f.path);
|
|
2827
3015
|
} else {
|
|
2828
|
-
const content = await
|
|
3016
|
+
const content = await import_fs4.promises.readFile(f.path, "utf-8");
|
|
2829
3017
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
2830
3018
|
}
|
|
2831
3019
|
}
|
|
@@ -2860,7 +3048,7 @@ var Indexer = class {
|
|
|
2860
3048
|
for (const parsed of parsedFiles) {
|
|
2861
3049
|
currentFilePaths.add(parsed.path);
|
|
2862
3050
|
if (parsed.chunks.length === 0) {
|
|
2863
|
-
const relativePath =
|
|
3051
|
+
const relativePath = path5.relative(this.projectRoot, parsed.path);
|
|
2864
3052
|
stats.parseFailures.push(relativePath);
|
|
2865
3053
|
}
|
|
2866
3054
|
let fileChunkCount = 0;
|
|
@@ -2874,6 +3062,17 @@ var Indexer = class {
|
|
|
2874
3062
|
const id = generateChunkId(parsed.path, chunk);
|
|
2875
3063
|
const contentHash = generateChunkHash(chunk);
|
|
2876
3064
|
currentChunkIds.add(id);
|
|
3065
|
+
const chunkData = {
|
|
3066
|
+
chunkId: id,
|
|
3067
|
+
contentHash,
|
|
3068
|
+
filePath: parsed.path,
|
|
3069
|
+
startLine: chunk.startLine,
|
|
3070
|
+
endLine: chunk.endLine,
|
|
3071
|
+
nodeType: chunk.chunkType,
|
|
3072
|
+
name: chunk.name,
|
|
3073
|
+
language: chunk.language
|
|
3074
|
+
};
|
|
3075
|
+
database.upsertChunk(chunkData);
|
|
2877
3076
|
if (existingChunks.get(id) === contentHash) {
|
|
2878
3077
|
fileChunkCount++;
|
|
2879
3078
|
continue;
|
|
@@ -2888,7 +3087,7 @@ var Indexer = class {
|
|
|
2888
3087
|
language: chunk.language,
|
|
2889
3088
|
hash: contentHash
|
|
2890
3089
|
};
|
|
2891
|
-
pendingChunks.push({ id, text, content: chunk.content, metadata });
|
|
3090
|
+
pendingChunks.push({ id, text, content: chunk.content, contentHash, metadata });
|
|
2892
3091
|
fileChunkCount++;
|
|
2893
3092
|
}
|
|
2894
3093
|
}
|
|
@@ -2904,6 +3103,8 @@ var Indexer = class {
|
|
|
2904
3103
|
stats.existingChunks = currentChunkIds.size - pendingChunks.length;
|
|
2905
3104
|
stats.removedChunks = removedCount;
|
|
2906
3105
|
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
3106
|
+
database.clearBranch(this.currentBranch);
|
|
3107
|
+
database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
|
|
2907
3108
|
this.fileHashCache = currentFileHashes;
|
|
2908
3109
|
this.saveFileHashCache();
|
|
2909
3110
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -2917,6 +3118,8 @@ var Indexer = class {
|
|
|
2917
3118
|
return stats;
|
|
2918
3119
|
}
|
|
2919
3120
|
if (pendingChunks.length === 0) {
|
|
3121
|
+
database.clearBranch(this.currentBranch);
|
|
3122
|
+
database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
|
|
2920
3123
|
store.save();
|
|
2921
3124
|
invertedIndex.save();
|
|
2922
3125
|
this.fileHashCache = currentFileHashes;
|
|
@@ -2938,8 +3141,22 @@ var Indexer = class {
|
|
|
2938
3141
|
chunksProcessed: 0,
|
|
2939
3142
|
totalChunks: pendingChunks.length
|
|
2940
3143
|
});
|
|
3144
|
+
const allContentHashes = pendingChunks.map((c) => c.contentHash);
|
|
3145
|
+
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
3146
|
+
const chunksNeedingEmbedding = pendingChunks.filter((c) => missingHashes.has(c.contentHash));
|
|
3147
|
+
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !missingHashes.has(c.contentHash));
|
|
3148
|
+
for (const chunk of chunksWithExistingEmbedding) {
|
|
3149
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
3150
|
+
if (embeddingBuffer) {
|
|
3151
|
+
const vector = bufferToFloat32Array(embeddingBuffer);
|
|
3152
|
+
store.add(chunk.id, Array.from(vector), chunk.metadata);
|
|
3153
|
+
invertedIndex.removeChunk(chunk.id);
|
|
3154
|
+
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
3155
|
+
stats.indexedChunks++;
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
2941
3158
|
const queue = new PQueue({ concurrency: 3 });
|
|
2942
|
-
const dynamicBatches = createDynamicBatches(
|
|
3159
|
+
const dynamicBatches = createDynamicBatches(chunksNeedingEmbedding);
|
|
2943
3160
|
for (const batch of dynamicBatches) {
|
|
2944
3161
|
queue.add(async () => {
|
|
2945
3162
|
try {
|
|
@@ -2964,7 +3181,15 @@ var Indexer = class {
|
|
|
2964
3181
|
metadata: chunk.metadata
|
|
2965
3182
|
}));
|
|
2966
3183
|
store.addBatch(items);
|
|
2967
|
-
for (
|
|
3184
|
+
for (let i = 0; i < batch.length; i++) {
|
|
3185
|
+
const chunk = batch[i];
|
|
3186
|
+
const embedding = result.embeddings[i];
|
|
3187
|
+
database.upsertEmbedding(
|
|
3188
|
+
chunk.contentHash,
|
|
3189
|
+
float32ArrayToBuffer(embedding),
|
|
3190
|
+
chunk.text,
|
|
3191
|
+
detectedProvider.modelInfo.model
|
|
3192
|
+
);
|
|
2968
3193
|
invertedIndex.removeChunk(chunk.id);
|
|
2969
3194
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
2970
3195
|
}
|
|
@@ -2992,6 +3217,8 @@ var Indexer = class {
|
|
|
2992
3217
|
chunksProcessed: stats.indexedChunks,
|
|
2993
3218
|
totalChunks: pendingChunks.length
|
|
2994
3219
|
});
|
|
3220
|
+
database.clearBranch(this.currentBranch);
|
|
3221
|
+
database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
|
|
2995
3222
|
store.save();
|
|
2996
3223
|
invertedIndex.save();
|
|
2997
3224
|
this.fileHashCache = currentFileHashes;
|
|
@@ -3010,18 +3237,24 @@ var Indexer = class {
|
|
|
3010
3237
|
return stats;
|
|
3011
3238
|
}
|
|
3012
3239
|
async search(query, limit, options) {
|
|
3013
|
-
const { store, provider } = await this.ensureInitialized();
|
|
3240
|
+
const { store, provider, database } = await this.ensureInitialized();
|
|
3014
3241
|
if (store.count() === 0) {
|
|
3015
3242
|
return [];
|
|
3016
3243
|
}
|
|
3017
3244
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
3018
3245
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
3246
|
+
const filterByBranch = options?.filterByBranch ?? true;
|
|
3019
3247
|
const { embedding } = await provider.embed(query);
|
|
3020
3248
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
3021
3249
|
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
3022
3250
|
const combined = this.fuseResults(semanticResults, keywordResults, hybridWeight, maxResults * 4);
|
|
3251
|
+
let branchChunkIds = null;
|
|
3252
|
+
if (filterByBranch && this.currentBranch !== "default") {
|
|
3253
|
+
branchChunkIds = new Set(database.getBranchChunkIds(this.currentBranch));
|
|
3254
|
+
}
|
|
3023
3255
|
const filtered = combined.filter((r) => {
|
|
3024
3256
|
if (r.score < this.config.search.minScore) return false;
|
|
3257
|
+
if (branchChunkIds && !branchChunkIds.has(r.id)) return false;
|
|
3025
3258
|
if (options?.fileType) {
|
|
3026
3259
|
const ext = r.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
3027
3260
|
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
@@ -3042,7 +3275,7 @@ var Indexer = class {
|
|
|
3042
3275
|
let contextEndLine = r.metadata.endLine;
|
|
3043
3276
|
if (this.config.search.includeContext) {
|
|
3044
3277
|
try {
|
|
3045
|
-
const fileContent = await
|
|
3278
|
+
const fileContent = await import_fs4.promises.readFile(
|
|
3046
3279
|
r.metadata.filePath,
|
|
3047
3280
|
"utf-8"
|
|
3048
3281
|
);
|
|
@@ -3123,7 +3356,9 @@ var Indexer = class {
|
|
|
3123
3356
|
vectorCount: store.count(),
|
|
3124
3357
|
provider: detectedProvider.provider,
|
|
3125
3358
|
model: detectedProvider.modelInfo.model,
|
|
3126
|
-
indexPath: this.indexPath
|
|
3359
|
+
indexPath: this.indexPath,
|
|
3360
|
+
currentBranch: this.currentBranch,
|
|
3361
|
+
baseBranch: this.baseBranch
|
|
3127
3362
|
};
|
|
3128
3363
|
}
|
|
3129
3364
|
async clearIndex() {
|
|
@@ -3134,7 +3369,7 @@ var Indexer = class {
|
|
|
3134
3369
|
invertedIndex.save();
|
|
3135
3370
|
}
|
|
3136
3371
|
async healthCheck() {
|
|
3137
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
3372
|
+
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
3138
3373
|
const allMetadata = store.getAllMetadata();
|
|
3139
3374
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
3140
3375
|
for (const { key, metadata } of allMetadata) {
|
|
@@ -3145,12 +3380,13 @@ var Indexer = class {
|
|
|
3145
3380
|
const removedFilePaths = [];
|
|
3146
3381
|
let removedCount = 0;
|
|
3147
3382
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
3148
|
-
if (!(0,
|
|
3383
|
+
if (!(0, import_fs4.existsSync)(filePath)) {
|
|
3149
3384
|
for (const key of chunkKeys) {
|
|
3150
3385
|
store.remove(key);
|
|
3151
3386
|
invertedIndex.removeChunk(key);
|
|
3152
3387
|
removedCount++;
|
|
3153
3388
|
}
|
|
3389
|
+
database.deleteChunksByFile(filePath);
|
|
3154
3390
|
removedFilePaths.push(filePath);
|
|
3155
3391
|
}
|
|
3156
3392
|
}
|
|
@@ -3158,7 +3394,9 @@ var Indexer = class {
|
|
|
3158
3394
|
store.save();
|
|
3159
3395
|
invertedIndex.save();
|
|
3160
3396
|
}
|
|
3161
|
-
|
|
3397
|
+
const gcOrphanEmbeddings = database.gcOrphanEmbeddings();
|
|
3398
|
+
const gcOrphanChunks = database.gcOrphanChunks();
|
|
3399
|
+
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks };
|
|
3162
3400
|
}
|
|
3163
3401
|
async retryFailedBatches() {
|
|
3164
3402
|
const { store, provider, invertedIndex } = await this.ensureInitialized();
|
|
@@ -3212,6 +3450,22 @@ var Indexer = class {
|
|
|
3212
3450
|
getFailedBatchesCount() {
|
|
3213
3451
|
return this.loadFailedBatches().length;
|
|
3214
3452
|
}
|
|
3453
|
+
getCurrentBranch() {
|
|
3454
|
+
return this.currentBranch;
|
|
3455
|
+
}
|
|
3456
|
+
getBaseBranch() {
|
|
3457
|
+
return this.baseBranch;
|
|
3458
|
+
}
|
|
3459
|
+
refreshBranchInfo() {
|
|
3460
|
+
if (isGitRepo(this.projectRoot)) {
|
|
3461
|
+
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
3462
|
+
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
async getDatabaseStats() {
|
|
3466
|
+
const { database } = await this.ensureInitialized();
|
|
3467
|
+
return database.getStats();
|
|
3468
|
+
}
|
|
3215
3469
|
};
|
|
3216
3470
|
|
|
3217
3471
|
// node_modules/chokidar/index.js
|
|
@@ -3304,7 +3558,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
3304
3558
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
3305
3559
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
3306
3560
|
if (wantBigintFsStats) {
|
|
3307
|
-
this._stat = (
|
|
3561
|
+
this._stat = (path8) => statMethod(path8, { bigint: true });
|
|
3308
3562
|
} else {
|
|
3309
3563
|
this._stat = statMethod;
|
|
3310
3564
|
}
|
|
@@ -3329,8 +3583,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
3329
3583
|
const par = this.parent;
|
|
3330
3584
|
const fil = par && par.files;
|
|
3331
3585
|
if (fil && fil.length > 0) {
|
|
3332
|
-
const { path:
|
|
3333
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
3586
|
+
const { path: path8, depth } = par;
|
|
3587
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path8));
|
|
3334
3588
|
const awaited = await Promise.all(slice);
|
|
3335
3589
|
for (const entry of awaited) {
|
|
3336
3590
|
if (!entry)
|
|
@@ -3370,20 +3624,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
3370
3624
|
this.reading = false;
|
|
3371
3625
|
}
|
|
3372
3626
|
}
|
|
3373
|
-
async _exploreDir(
|
|
3627
|
+
async _exploreDir(path8, depth) {
|
|
3374
3628
|
let files;
|
|
3375
3629
|
try {
|
|
3376
|
-
files = await (0, import_promises.readdir)(
|
|
3630
|
+
files = await (0, import_promises.readdir)(path8, this._rdOptions);
|
|
3377
3631
|
} catch (error) {
|
|
3378
3632
|
this._onError(error);
|
|
3379
3633
|
}
|
|
3380
|
-
return { files, depth, path:
|
|
3634
|
+
return { files, depth, path: path8 };
|
|
3381
3635
|
}
|
|
3382
|
-
async _formatEntry(dirent,
|
|
3636
|
+
async _formatEntry(dirent, path8) {
|
|
3383
3637
|
let entry;
|
|
3384
3638
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
3385
3639
|
try {
|
|
3386
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
3640
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path8, basename3));
|
|
3387
3641
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename3 };
|
|
3388
3642
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
3389
3643
|
} catch (err) {
|
|
@@ -3783,16 +4037,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
3783
4037
|
};
|
|
3784
4038
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
3785
4039
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
3786
|
-
function createFsWatchInstance(
|
|
4040
|
+
function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
|
|
3787
4041
|
const handleEvent = (rawEvent, evPath) => {
|
|
3788
|
-
listener(
|
|
3789
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
3790
|
-
if (evPath &&
|
|
3791
|
-
fsWatchBroadcast(sp.resolve(
|
|
4042
|
+
listener(path8);
|
|
4043
|
+
emitRaw(rawEvent, evPath, { watchedPath: path8 });
|
|
4044
|
+
if (evPath && path8 !== evPath) {
|
|
4045
|
+
fsWatchBroadcast(sp.resolve(path8, evPath), KEY_LISTENERS, sp.join(path8, evPath));
|
|
3792
4046
|
}
|
|
3793
4047
|
};
|
|
3794
4048
|
try {
|
|
3795
|
-
return (0, import_node_fs.watch)(
|
|
4049
|
+
return (0, import_node_fs.watch)(path8, {
|
|
3796
4050
|
persistent: options.persistent
|
|
3797
4051
|
}, handleEvent);
|
|
3798
4052
|
} catch (error) {
|
|
@@ -3808,12 +4062,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
3808
4062
|
listener(val1, val2, val3);
|
|
3809
4063
|
});
|
|
3810
4064
|
};
|
|
3811
|
-
var setFsWatchListener = (
|
|
4065
|
+
var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
3812
4066
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
3813
4067
|
let cont = FsWatchInstances.get(fullPath);
|
|
3814
4068
|
let watcher;
|
|
3815
4069
|
if (!options.persistent) {
|
|
3816
|
-
watcher = createFsWatchInstance(
|
|
4070
|
+
watcher = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
|
|
3817
4071
|
if (!watcher)
|
|
3818
4072
|
return;
|
|
3819
4073
|
return watcher.close.bind(watcher);
|
|
@@ -3824,7 +4078,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
3824
4078
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
3825
4079
|
} else {
|
|
3826
4080
|
watcher = createFsWatchInstance(
|
|
3827
|
-
|
|
4081
|
+
path8,
|
|
3828
4082
|
options,
|
|
3829
4083
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
3830
4084
|
errHandler,
|
|
@@ -3839,7 +4093,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
3839
4093
|
cont.watcherUnusable = true;
|
|
3840
4094
|
if (isWindows && error.code === "EPERM") {
|
|
3841
4095
|
try {
|
|
3842
|
-
const fd = await (0, import_promises2.open)(
|
|
4096
|
+
const fd = await (0, import_promises2.open)(path8, "r");
|
|
3843
4097
|
await fd.close();
|
|
3844
4098
|
broadcastErr(error);
|
|
3845
4099
|
} catch (err) {
|
|
@@ -3870,7 +4124,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
3870
4124
|
};
|
|
3871
4125
|
};
|
|
3872
4126
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
3873
|
-
var setFsWatchFileListener = (
|
|
4127
|
+
var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
|
|
3874
4128
|
const { listener, rawEmitter } = handlers;
|
|
3875
4129
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
3876
4130
|
const copts = cont && cont.options;
|
|
@@ -3892,7 +4146,7 @@ var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
|
|
|
3892
4146
|
});
|
|
3893
4147
|
const currmtime = curr.mtimeMs;
|
|
3894
4148
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
3895
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
4149
|
+
foreach(cont.listeners, (listener2) => listener2(path8, curr));
|
|
3896
4150
|
}
|
|
3897
4151
|
})
|
|
3898
4152
|
};
|
|
@@ -3922,13 +4176,13 @@ var NodeFsHandler = class {
|
|
|
3922
4176
|
* @param listener on fs change
|
|
3923
4177
|
* @returns closer for the watcher instance
|
|
3924
4178
|
*/
|
|
3925
|
-
_watchWithNodeFs(
|
|
4179
|
+
_watchWithNodeFs(path8, listener) {
|
|
3926
4180
|
const opts = this.fsw.options;
|
|
3927
|
-
const directory = sp.dirname(
|
|
3928
|
-
const basename3 = sp.basename(
|
|
4181
|
+
const directory = sp.dirname(path8);
|
|
4182
|
+
const basename3 = sp.basename(path8);
|
|
3929
4183
|
const parent = this.fsw._getWatchedDir(directory);
|
|
3930
4184
|
parent.add(basename3);
|
|
3931
|
-
const absolutePath = sp.resolve(
|
|
4185
|
+
const absolutePath = sp.resolve(path8);
|
|
3932
4186
|
const options = {
|
|
3933
4187
|
persistent: opts.persistent
|
|
3934
4188
|
};
|
|
@@ -3938,12 +4192,12 @@ var NodeFsHandler = class {
|
|
|
3938
4192
|
if (opts.usePolling) {
|
|
3939
4193
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
3940
4194
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
3941
|
-
closer = setFsWatchFileListener(
|
|
4195
|
+
closer = setFsWatchFileListener(path8, absolutePath, options, {
|
|
3942
4196
|
listener,
|
|
3943
4197
|
rawEmitter: this.fsw._emitRaw
|
|
3944
4198
|
});
|
|
3945
4199
|
} else {
|
|
3946
|
-
closer = setFsWatchListener(
|
|
4200
|
+
closer = setFsWatchListener(path8, absolutePath, options, {
|
|
3947
4201
|
listener,
|
|
3948
4202
|
errHandler: this._boundHandleError,
|
|
3949
4203
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -3965,7 +4219,7 @@ var NodeFsHandler = class {
|
|
|
3965
4219
|
let prevStats = stats;
|
|
3966
4220
|
if (parent.has(basename3))
|
|
3967
4221
|
return;
|
|
3968
|
-
const listener = async (
|
|
4222
|
+
const listener = async (path8, newStats) => {
|
|
3969
4223
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
3970
4224
|
return;
|
|
3971
4225
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -3979,11 +4233,11 @@ var NodeFsHandler = class {
|
|
|
3979
4233
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
3980
4234
|
}
|
|
3981
4235
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
3982
|
-
this.fsw._closeFile(
|
|
4236
|
+
this.fsw._closeFile(path8);
|
|
3983
4237
|
prevStats = newStats2;
|
|
3984
4238
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
3985
4239
|
if (closer2)
|
|
3986
|
-
this.fsw._addPathCloser(
|
|
4240
|
+
this.fsw._addPathCloser(path8, closer2);
|
|
3987
4241
|
} else {
|
|
3988
4242
|
prevStats = newStats2;
|
|
3989
4243
|
}
|
|
@@ -4015,7 +4269,7 @@ var NodeFsHandler = class {
|
|
|
4015
4269
|
* @param item basename of this item
|
|
4016
4270
|
* @returns true if no more processing is needed for this entry.
|
|
4017
4271
|
*/
|
|
4018
|
-
async _handleSymlink(entry, directory,
|
|
4272
|
+
async _handleSymlink(entry, directory, path8, item) {
|
|
4019
4273
|
if (this.fsw.closed) {
|
|
4020
4274
|
return;
|
|
4021
4275
|
}
|
|
@@ -4025,7 +4279,7 @@ var NodeFsHandler = class {
|
|
|
4025
4279
|
this.fsw._incrReadyCount();
|
|
4026
4280
|
let linkPath;
|
|
4027
4281
|
try {
|
|
4028
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
4282
|
+
linkPath = await (0, import_promises2.realpath)(path8);
|
|
4029
4283
|
} catch (e) {
|
|
4030
4284
|
this.fsw._emitReady();
|
|
4031
4285
|
return true;
|
|
@@ -4035,12 +4289,12 @@ var NodeFsHandler = class {
|
|
|
4035
4289
|
if (dir.has(item)) {
|
|
4036
4290
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
4037
4291
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4038
|
-
this.fsw._emit(EV.CHANGE,
|
|
4292
|
+
this.fsw._emit(EV.CHANGE, path8, entry.stats);
|
|
4039
4293
|
}
|
|
4040
4294
|
} else {
|
|
4041
4295
|
dir.add(item);
|
|
4042
4296
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4043
|
-
this.fsw._emit(EV.ADD,
|
|
4297
|
+
this.fsw._emit(EV.ADD, path8, entry.stats);
|
|
4044
4298
|
}
|
|
4045
4299
|
this.fsw._emitReady();
|
|
4046
4300
|
return true;
|
|
@@ -4070,9 +4324,9 @@ var NodeFsHandler = class {
|
|
|
4070
4324
|
return;
|
|
4071
4325
|
}
|
|
4072
4326
|
const item = entry.path;
|
|
4073
|
-
let
|
|
4327
|
+
let path8 = sp.join(directory, item);
|
|
4074
4328
|
current.add(item);
|
|
4075
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
4329
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
|
|
4076
4330
|
return;
|
|
4077
4331
|
}
|
|
4078
4332
|
if (this.fsw.closed) {
|
|
@@ -4081,8 +4335,8 @@ var NodeFsHandler = class {
|
|
|
4081
4335
|
}
|
|
4082
4336
|
if (item === target || !target && !previous.has(item)) {
|
|
4083
4337
|
this.fsw._incrReadyCount();
|
|
4084
|
-
|
|
4085
|
-
this._addToNodeFs(
|
|
4338
|
+
path8 = sp.join(dir, sp.relative(dir, path8));
|
|
4339
|
+
this._addToNodeFs(path8, initialAdd, wh, depth + 1);
|
|
4086
4340
|
}
|
|
4087
4341
|
}).on(EV.ERROR, this._boundHandleError);
|
|
4088
4342
|
return new Promise((resolve4, reject) => {
|
|
@@ -4151,13 +4405,13 @@ var NodeFsHandler = class {
|
|
|
4151
4405
|
* @param depth Child path actually targeted for watch
|
|
4152
4406
|
* @param target Child path actually targeted for watch
|
|
4153
4407
|
*/
|
|
4154
|
-
async _addToNodeFs(
|
|
4408
|
+
async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
|
|
4155
4409
|
const ready = this.fsw._emitReady;
|
|
4156
|
-
if (this.fsw._isIgnored(
|
|
4410
|
+
if (this.fsw._isIgnored(path8) || this.fsw.closed) {
|
|
4157
4411
|
ready();
|
|
4158
4412
|
return false;
|
|
4159
4413
|
}
|
|
4160
|
-
const wh = this.fsw._getWatchHelpers(
|
|
4414
|
+
const wh = this.fsw._getWatchHelpers(path8);
|
|
4161
4415
|
if (priorWh) {
|
|
4162
4416
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
4163
4417
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -4173,8 +4427,8 @@ var NodeFsHandler = class {
|
|
|
4173
4427
|
const follow = this.fsw.options.followSymlinks;
|
|
4174
4428
|
let closer;
|
|
4175
4429
|
if (stats.isDirectory()) {
|
|
4176
|
-
const absPath = sp.resolve(
|
|
4177
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
4430
|
+
const absPath = sp.resolve(path8);
|
|
4431
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
|
|
4178
4432
|
if (this.fsw.closed)
|
|
4179
4433
|
return;
|
|
4180
4434
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -4184,29 +4438,29 @@ var NodeFsHandler = class {
|
|
|
4184
4438
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
4185
4439
|
}
|
|
4186
4440
|
} else if (stats.isSymbolicLink()) {
|
|
4187
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
4441
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
|
|
4188
4442
|
if (this.fsw.closed)
|
|
4189
4443
|
return;
|
|
4190
4444
|
const parent = sp.dirname(wh.watchPath);
|
|
4191
4445
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
4192
4446
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
4193
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
4447
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
|
|
4194
4448
|
if (this.fsw.closed)
|
|
4195
4449
|
return;
|
|
4196
4450
|
if (targetPath !== void 0) {
|
|
4197
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
4451
|
+
this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
|
|
4198
4452
|
}
|
|
4199
4453
|
} else {
|
|
4200
4454
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
4201
4455
|
}
|
|
4202
4456
|
ready();
|
|
4203
4457
|
if (closer)
|
|
4204
|
-
this.fsw._addPathCloser(
|
|
4458
|
+
this.fsw._addPathCloser(path8, closer);
|
|
4205
4459
|
return false;
|
|
4206
4460
|
} catch (error) {
|
|
4207
4461
|
if (this.fsw._handleError(error)) {
|
|
4208
4462
|
ready();
|
|
4209
|
-
return
|
|
4463
|
+
return path8;
|
|
4210
4464
|
}
|
|
4211
4465
|
}
|
|
4212
4466
|
}
|
|
@@ -4249,24 +4503,24 @@ function createPattern(matcher) {
|
|
|
4249
4503
|
}
|
|
4250
4504
|
return () => false;
|
|
4251
4505
|
}
|
|
4252
|
-
function normalizePath(
|
|
4253
|
-
if (typeof
|
|
4506
|
+
function normalizePath(path8) {
|
|
4507
|
+
if (typeof path8 !== "string")
|
|
4254
4508
|
throw new Error("string expected");
|
|
4255
|
-
|
|
4256
|
-
|
|
4509
|
+
path8 = sp2.normalize(path8);
|
|
4510
|
+
path8 = path8.replace(/\\/g, "/");
|
|
4257
4511
|
let prepend = false;
|
|
4258
|
-
if (
|
|
4512
|
+
if (path8.startsWith("//"))
|
|
4259
4513
|
prepend = true;
|
|
4260
|
-
|
|
4514
|
+
path8 = path8.replace(DOUBLE_SLASH_RE, "/");
|
|
4261
4515
|
if (prepend)
|
|
4262
|
-
|
|
4263
|
-
return
|
|
4516
|
+
path8 = "/" + path8;
|
|
4517
|
+
return path8;
|
|
4264
4518
|
}
|
|
4265
4519
|
function matchPatterns(patterns, testString, stats) {
|
|
4266
|
-
const
|
|
4520
|
+
const path8 = normalizePath(testString);
|
|
4267
4521
|
for (let index = 0; index < patterns.length; index++) {
|
|
4268
4522
|
const pattern = patterns[index];
|
|
4269
|
-
if (pattern(
|
|
4523
|
+
if (pattern(path8, stats)) {
|
|
4270
4524
|
return true;
|
|
4271
4525
|
}
|
|
4272
4526
|
}
|
|
@@ -4304,19 +4558,19 @@ var toUnix = (string) => {
|
|
|
4304
4558
|
}
|
|
4305
4559
|
return str;
|
|
4306
4560
|
};
|
|
4307
|
-
var normalizePathToUnix = (
|
|
4308
|
-
var normalizeIgnored = (cwd = "") => (
|
|
4309
|
-
if (typeof
|
|
4310
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
4561
|
+
var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
|
|
4562
|
+
var normalizeIgnored = (cwd = "") => (path8) => {
|
|
4563
|
+
if (typeof path8 === "string") {
|
|
4564
|
+
return normalizePathToUnix(sp2.isAbsolute(path8) ? path8 : sp2.join(cwd, path8));
|
|
4311
4565
|
} else {
|
|
4312
|
-
return
|
|
4566
|
+
return path8;
|
|
4313
4567
|
}
|
|
4314
4568
|
};
|
|
4315
|
-
var getAbsolutePath = (
|
|
4316
|
-
if (sp2.isAbsolute(
|
|
4317
|
-
return
|
|
4569
|
+
var getAbsolutePath = (path8, cwd) => {
|
|
4570
|
+
if (sp2.isAbsolute(path8)) {
|
|
4571
|
+
return path8;
|
|
4318
4572
|
}
|
|
4319
|
-
return sp2.join(cwd,
|
|
4573
|
+
return sp2.join(cwd, path8);
|
|
4320
4574
|
};
|
|
4321
4575
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
4322
4576
|
var DirEntry = class {
|
|
@@ -4381,10 +4635,10 @@ var WatchHelper = class {
|
|
|
4381
4635
|
dirParts;
|
|
4382
4636
|
followSymlinks;
|
|
4383
4637
|
statMethod;
|
|
4384
|
-
constructor(
|
|
4638
|
+
constructor(path8, follow, fsw) {
|
|
4385
4639
|
this.fsw = fsw;
|
|
4386
|
-
const watchPath =
|
|
4387
|
-
this.path =
|
|
4640
|
+
const watchPath = path8;
|
|
4641
|
+
this.path = path8 = path8.replace(REPLACER_RE, "");
|
|
4388
4642
|
this.watchPath = watchPath;
|
|
4389
4643
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
4390
4644
|
this.dirParts = [];
|
|
@@ -4524,20 +4778,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4524
4778
|
this._closePromise = void 0;
|
|
4525
4779
|
let paths = unifyPaths(paths_);
|
|
4526
4780
|
if (cwd) {
|
|
4527
|
-
paths = paths.map((
|
|
4528
|
-
const absPath = getAbsolutePath(
|
|
4781
|
+
paths = paths.map((path8) => {
|
|
4782
|
+
const absPath = getAbsolutePath(path8, cwd);
|
|
4529
4783
|
return absPath;
|
|
4530
4784
|
});
|
|
4531
4785
|
}
|
|
4532
|
-
paths.forEach((
|
|
4533
|
-
this._removeIgnoredPath(
|
|
4786
|
+
paths.forEach((path8) => {
|
|
4787
|
+
this._removeIgnoredPath(path8);
|
|
4534
4788
|
});
|
|
4535
4789
|
this._userIgnored = void 0;
|
|
4536
4790
|
if (!this._readyCount)
|
|
4537
4791
|
this._readyCount = 0;
|
|
4538
4792
|
this._readyCount += paths.length;
|
|
4539
|
-
Promise.all(paths.map(async (
|
|
4540
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
4793
|
+
Promise.all(paths.map(async (path8) => {
|
|
4794
|
+
const res = await this._nodeFsHandler._addToNodeFs(path8, !_internal, void 0, 0, _origAdd);
|
|
4541
4795
|
if (res)
|
|
4542
4796
|
this._emitReady();
|
|
4543
4797
|
return res;
|
|
@@ -4559,17 +4813,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4559
4813
|
return this;
|
|
4560
4814
|
const paths = unifyPaths(paths_);
|
|
4561
4815
|
const { cwd } = this.options;
|
|
4562
|
-
paths.forEach((
|
|
4563
|
-
if (!sp2.isAbsolute(
|
|
4816
|
+
paths.forEach((path8) => {
|
|
4817
|
+
if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
|
|
4564
4818
|
if (cwd)
|
|
4565
|
-
|
|
4566
|
-
|
|
4819
|
+
path8 = sp2.join(cwd, path8);
|
|
4820
|
+
path8 = sp2.resolve(path8);
|
|
4567
4821
|
}
|
|
4568
|
-
this._closePath(
|
|
4569
|
-
this._addIgnoredPath(
|
|
4570
|
-
if (this._watched.has(
|
|
4822
|
+
this._closePath(path8);
|
|
4823
|
+
this._addIgnoredPath(path8);
|
|
4824
|
+
if (this._watched.has(path8)) {
|
|
4571
4825
|
this._addIgnoredPath({
|
|
4572
|
-
path:
|
|
4826
|
+
path: path8,
|
|
4573
4827
|
recursive: true
|
|
4574
4828
|
});
|
|
4575
4829
|
}
|
|
@@ -4633,38 +4887,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4633
4887
|
* @param stats arguments to be passed with event
|
|
4634
4888
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
4635
4889
|
*/
|
|
4636
|
-
async _emit(event,
|
|
4890
|
+
async _emit(event, path8, stats) {
|
|
4637
4891
|
if (this.closed)
|
|
4638
4892
|
return;
|
|
4639
4893
|
const opts = this.options;
|
|
4640
4894
|
if (isWindows)
|
|
4641
|
-
|
|
4895
|
+
path8 = sp2.normalize(path8);
|
|
4642
4896
|
if (opts.cwd)
|
|
4643
|
-
|
|
4644
|
-
const args = [
|
|
4897
|
+
path8 = sp2.relative(opts.cwd, path8);
|
|
4898
|
+
const args = [path8];
|
|
4645
4899
|
if (stats != null)
|
|
4646
4900
|
args.push(stats);
|
|
4647
4901
|
const awf = opts.awaitWriteFinish;
|
|
4648
4902
|
let pw;
|
|
4649
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
4903
|
+
if (awf && (pw = this._pendingWrites.get(path8))) {
|
|
4650
4904
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
4651
4905
|
return this;
|
|
4652
4906
|
}
|
|
4653
4907
|
if (opts.atomic) {
|
|
4654
4908
|
if (event === EVENTS.UNLINK) {
|
|
4655
|
-
this._pendingUnlinks.set(
|
|
4909
|
+
this._pendingUnlinks.set(path8, [event, ...args]);
|
|
4656
4910
|
setTimeout(() => {
|
|
4657
|
-
this._pendingUnlinks.forEach((entry,
|
|
4911
|
+
this._pendingUnlinks.forEach((entry, path9) => {
|
|
4658
4912
|
this.emit(...entry);
|
|
4659
4913
|
this.emit(EVENTS.ALL, ...entry);
|
|
4660
|
-
this._pendingUnlinks.delete(
|
|
4914
|
+
this._pendingUnlinks.delete(path9);
|
|
4661
4915
|
});
|
|
4662
4916
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
4663
4917
|
return this;
|
|
4664
4918
|
}
|
|
4665
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
4919
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
|
|
4666
4920
|
event = EVENTS.CHANGE;
|
|
4667
|
-
this._pendingUnlinks.delete(
|
|
4921
|
+
this._pendingUnlinks.delete(path8);
|
|
4668
4922
|
}
|
|
4669
4923
|
}
|
|
4670
4924
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -4682,16 +4936,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4682
4936
|
this.emitWithAll(event, args);
|
|
4683
4937
|
}
|
|
4684
4938
|
};
|
|
4685
|
-
this._awaitWriteFinish(
|
|
4939
|
+
this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
|
|
4686
4940
|
return this;
|
|
4687
4941
|
}
|
|
4688
4942
|
if (event === EVENTS.CHANGE) {
|
|
4689
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
4943
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
|
|
4690
4944
|
if (isThrottled)
|
|
4691
4945
|
return this;
|
|
4692
4946
|
}
|
|
4693
4947
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
4694
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
4948
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
|
|
4695
4949
|
let stats2;
|
|
4696
4950
|
try {
|
|
4697
4951
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -4722,23 +4976,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4722
4976
|
* @param timeout duration of time to suppress duplicate actions
|
|
4723
4977
|
* @returns tracking object or false if action should be suppressed
|
|
4724
4978
|
*/
|
|
4725
|
-
_throttle(actionType,
|
|
4979
|
+
_throttle(actionType, path8, timeout) {
|
|
4726
4980
|
if (!this._throttled.has(actionType)) {
|
|
4727
4981
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
4728
4982
|
}
|
|
4729
4983
|
const action = this._throttled.get(actionType);
|
|
4730
4984
|
if (!action)
|
|
4731
4985
|
throw new Error("invalid throttle");
|
|
4732
|
-
const actionPath = action.get(
|
|
4986
|
+
const actionPath = action.get(path8);
|
|
4733
4987
|
if (actionPath) {
|
|
4734
4988
|
actionPath.count++;
|
|
4735
4989
|
return false;
|
|
4736
4990
|
}
|
|
4737
4991
|
let timeoutObject;
|
|
4738
4992
|
const clear = () => {
|
|
4739
|
-
const item = action.get(
|
|
4993
|
+
const item = action.get(path8);
|
|
4740
4994
|
const count = item ? item.count : 0;
|
|
4741
|
-
action.delete(
|
|
4995
|
+
action.delete(path8);
|
|
4742
4996
|
clearTimeout(timeoutObject);
|
|
4743
4997
|
if (item)
|
|
4744
4998
|
clearTimeout(item.timeoutObject);
|
|
@@ -4746,7 +5000,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4746
5000
|
};
|
|
4747
5001
|
timeoutObject = setTimeout(clear, timeout);
|
|
4748
5002
|
const thr = { timeoutObject, clear, count: 0 };
|
|
4749
|
-
action.set(
|
|
5003
|
+
action.set(path8, thr);
|
|
4750
5004
|
return thr;
|
|
4751
5005
|
}
|
|
4752
5006
|
_incrReadyCount() {
|
|
@@ -4760,44 +5014,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4760
5014
|
* @param event
|
|
4761
5015
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
4762
5016
|
*/
|
|
4763
|
-
_awaitWriteFinish(
|
|
5017
|
+
_awaitWriteFinish(path8, threshold, event, awfEmit) {
|
|
4764
5018
|
const awf = this.options.awaitWriteFinish;
|
|
4765
5019
|
if (typeof awf !== "object")
|
|
4766
5020
|
return;
|
|
4767
5021
|
const pollInterval = awf.pollInterval;
|
|
4768
5022
|
let timeoutHandler;
|
|
4769
|
-
let fullPath =
|
|
4770
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
4771
|
-
fullPath = sp2.join(this.options.cwd,
|
|
5023
|
+
let fullPath = path8;
|
|
5024
|
+
if (this.options.cwd && !sp2.isAbsolute(path8)) {
|
|
5025
|
+
fullPath = sp2.join(this.options.cwd, path8);
|
|
4772
5026
|
}
|
|
4773
5027
|
const now = /* @__PURE__ */ new Date();
|
|
4774
5028
|
const writes = this._pendingWrites;
|
|
4775
5029
|
function awaitWriteFinishFn(prevStat) {
|
|
4776
5030
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
4777
|
-
if (err || !writes.has(
|
|
5031
|
+
if (err || !writes.has(path8)) {
|
|
4778
5032
|
if (err && err.code !== "ENOENT")
|
|
4779
5033
|
awfEmit(err);
|
|
4780
5034
|
return;
|
|
4781
5035
|
}
|
|
4782
5036
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
4783
5037
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
4784
|
-
writes.get(
|
|
5038
|
+
writes.get(path8).lastChange = now2;
|
|
4785
5039
|
}
|
|
4786
|
-
const pw = writes.get(
|
|
5040
|
+
const pw = writes.get(path8);
|
|
4787
5041
|
const df = now2 - pw.lastChange;
|
|
4788
5042
|
if (df >= threshold) {
|
|
4789
|
-
writes.delete(
|
|
5043
|
+
writes.delete(path8);
|
|
4790
5044
|
awfEmit(void 0, curStat);
|
|
4791
5045
|
} else {
|
|
4792
5046
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
4793
5047
|
}
|
|
4794
5048
|
});
|
|
4795
5049
|
}
|
|
4796
|
-
if (!writes.has(
|
|
4797
|
-
writes.set(
|
|
5050
|
+
if (!writes.has(path8)) {
|
|
5051
|
+
writes.set(path8, {
|
|
4798
5052
|
lastChange: now,
|
|
4799
5053
|
cancelWait: () => {
|
|
4800
|
-
writes.delete(
|
|
5054
|
+
writes.delete(path8);
|
|
4801
5055
|
clearTimeout(timeoutHandler);
|
|
4802
5056
|
return event;
|
|
4803
5057
|
}
|
|
@@ -4808,8 +5062,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4808
5062
|
/**
|
|
4809
5063
|
* Determines whether user has asked to ignore this path.
|
|
4810
5064
|
*/
|
|
4811
|
-
_isIgnored(
|
|
4812
|
-
if (this.options.atomic && DOT_RE.test(
|
|
5065
|
+
_isIgnored(path8, stats) {
|
|
5066
|
+
if (this.options.atomic && DOT_RE.test(path8))
|
|
4813
5067
|
return true;
|
|
4814
5068
|
if (!this._userIgnored) {
|
|
4815
5069
|
const { cwd } = this.options;
|
|
@@ -4819,17 +5073,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4819
5073
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
4820
5074
|
this._userIgnored = anymatch(list, void 0);
|
|
4821
5075
|
}
|
|
4822
|
-
return this._userIgnored(
|
|
5076
|
+
return this._userIgnored(path8, stats);
|
|
4823
5077
|
}
|
|
4824
|
-
_isntIgnored(
|
|
4825
|
-
return !this._isIgnored(
|
|
5078
|
+
_isntIgnored(path8, stat4) {
|
|
5079
|
+
return !this._isIgnored(path8, stat4);
|
|
4826
5080
|
}
|
|
4827
5081
|
/**
|
|
4828
5082
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
4829
5083
|
* @param path file or directory pattern being watched
|
|
4830
5084
|
*/
|
|
4831
|
-
_getWatchHelpers(
|
|
4832
|
-
return new WatchHelper(
|
|
5085
|
+
_getWatchHelpers(path8) {
|
|
5086
|
+
return new WatchHelper(path8, this.options.followSymlinks, this);
|
|
4833
5087
|
}
|
|
4834
5088
|
// Directory helpers
|
|
4835
5089
|
// -----------------
|
|
@@ -4861,63 +5115,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4861
5115
|
* @param item base path of item/directory
|
|
4862
5116
|
*/
|
|
4863
5117
|
_remove(directory, item, isDirectory) {
|
|
4864
|
-
const
|
|
4865
|
-
const fullPath = sp2.resolve(
|
|
4866
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
4867
|
-
if (!this._throttle("remove",
|
|
5118
|
+
const path8 = sp2.join(directory, item);
|
|
5119
|
+
const fullPath = sp2.resolve(path8);
|
|
5120
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
|
|
5121
|
+
if (!this._throttle("remove", path8, 100))
|
|
4868
5122
|
return;
|
|
4869
5123
|
if (!isDirectory && this._watched.size === 1) {
|
|
4870
5124
|
this.add(directory, item, true);
|
|
4871
5125
|
}
|
|
4872
|
-
const wp = this._getWatchedDir(
|
|
5126
|
+
const wp = this._getWatchedDir(path8);
|
|
4873
5127
|
const nestedDirectoryChildren = wp.getChildren();
|
|
4874
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
5128
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
|
|
4875
5129
|
const parent = this._getWatchedDir(directory);
|
|
4876
5130
|
const wasTracked = parent.has(item);
|
|
4877
5131
|
parent.remove(item);
|
|
4878
5132
|
if (this._symlinkPaths.has(fullPath)) {
|
|
4879
5133
|
this._symlinkPaths.delete(fullPath);
|
|
4880
5134
|
}
|
|
4881
|
-
let relPath =
|
|
5135
|
+
let relPath = path8;
|
|
4882
5136
|
if (this.options.cwd)
|
|
4883
|
-
relPath = sp2.relative(this.options.cwd,
|
|
5137
|
+
relPath = sp2.relative(this.options.cwd, path8);
|
|
4884
5138
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
4885
5139
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
4886
5140
|
if (event === EVENTS.ADD)
|
|
4887
5141
|
return;
|
|
4888
5142
|
}
|
|
4889
|
-
this._watched.delete(
|
|
5143
|
+
this._watched.delete(path8);
|
|
4890
5144
|
this._watched.delete(fullPath);
|
|
4891
5145
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
4892
|
-
if (wasTracked && !this._isIgnored(
|
|
4893
|
-
this._emit(eventName,
|
|
4894
|
-
this._closePath(
|
|
5146
|
+
if (wasTracked && !this._isIgnored(path8))
|
|
5147
|
+
this._emit(eventName, path8);
|
|
5148
|
+
this._closePath(path8);
|
|
4895
5149
|
}
|
|
4896
5150
|
/**
|
|
4897
5151
|
* Closes all watchers for a path
|
|
4898
5152
|
*/
|
|
4899
|
-
_closePath(
|
|
4900
|
-
this._closeFile(
|
|
4901
|
-
const dir = sp2.dirname(
|
|
4902
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
5153
|
+
_closePath(path8) {
|
|
5154
|
+
this._closeFile(path8);
|
|
5155
|
+
const dir = sp2.dirname(path8);
|
|
5156
|
+
this._getWatchedDir(dir).remove(sp2.basename(path8));
|
|
4903
5157
|
}
|
|
4904
5158
|
/**
|
|
4905
5159
|
* Closes only file-specific watchers
|
|
4906
5160
|
*/
|
|
4907
|
-
_closeFile(
|
|
4908
|
-
const closers = this._closers.get(
|
|
5161
|
+
_closeFile(path8) {
|
|
5162
|
+
const closers = this._closers.get(path8);
|
|
4909
5163
|
if (!closers)
|
|
4910
5164
|
return;
|
|
4911
5165
|
closers.forEach((closer) => closer());
|
|
4912
|
-
this._closers.delete(
|
|
5166
|
+
this._closers.delete(path8);
|
|
4913
5167
|
}
|
|
4914
|
-
_addPathCloser(
|
|
5168
|
+
_addPathCloser(path8, closer) {
|
|
4915
5169
|
if (!closer)
|
|
4916
5170
|
return;
|
|
4917
|
-
let list = this._closers.get(
|
|
5171
|
+
let list = this._closers.get(path8);
|
|
4918
5172
|
if (!list) {
|
|
4919
5173
|
list = [];
|
|
4920
|
-
this._closers.set(
|
|
5174
|
+
this._closers.set(path8, list);
|
|
4921
5175
|
}
|
|
4922
5176
|
list.push(closer);
|
|
4923
5177
|
}
|
|
@@ -4947,7 +5201,7 @@ function watch(paths, options = {}) {
|
|
|
4947
5201
|
var chokidar_default = { watch, FSWatcher };
|
|
4948
5202
|
|
|
4949
5203
|
// src/watcher/index.ts
|
|
4950
|
-
var
|
|
5204
|
+
var path6 = __toESM(require("path"), 1);
|
|
4951
5205
|
var FileWatcher = class {
|
|
4952
5206
|
watcher = null;
|
|
4953
5207
|
projectRoot;
|
|
@@ -4968,7 +5222,7 @@ var FileWatcher = class {
|
|
|
4968
5222
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
4969
5223
|
this.watcher = chokidar_default.watch(this.projectRoot, {
|
|
4970
5224
|
ignored: (filePath) => {
|
|
4971
|
-
const relativePath =
|
|
5225
|
+
const relativePath = path6.relative(this.projectRoot, filePath);
|
|
4972
5226
|
if (!relativePath) return false;
|
|
4973
5227
|
if (ignoreFilter.ignores(relativePath)) {
|
|
4974
5228
|
return true;
|
|
@@ -5012,7 +5266,7 @@ var FileWatcher = class {
|
|
|
5012
5266
|
return;
|
|
5013
5267
|
}
|
|
5014
5268
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
5015
|
-
([
|
|
5269
|
+
([path8, type]) => ({ path: path8, type })
|
|
5016
5270
|
);
|
|
5017
5271
|
this.pendingChanges.clear();
|
|
5018
5272
|
try {
|
|
@@ -5037,9 +5291,82 @@ var FileWatcher = class {
|
|
|
5037
5291
|
return this.watcher !== null;
|
|
5038
5292
|
}
|
|
5039
5293
|
};
|
|
5294
|
+
var GitHeadWatcher = class {
|
|
5295
|
+
watcher = null;
|
|
5296
|
+
projectRoot;
|
|
5297
|
+
currentBranch = null;
|
|
5298
|
+
onBranchChange = null;
|
|
5299
|
+
debounceTimer = null;
|
|
5300
|
+
debounceMs = 100;
|
|
5301
|
+
// Short debounce for git operations
|
|
5302
|
+
constructor(projectRoot) {
|
|
5303
|
+
this.projectRoot = projectRoot;
|
|
5304
|
+
}
|
|
5305
|
+
start(handler) {
|
|
5306
|
+
if (this.watcher) {
|
|
5307
|
+
return;
|
|
5308
|
+
}
|
|
5309
|
+
if (!isGitRepo(this.projectRoot)) {
|
|
5310
|
+
return;
|
|
5311
|
+
}
|
|
5312
|
+
this.onBranchChange = handler;
|
|
5313
|
+
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
5314
|
+
const headPath = getHeadPath(this.projectRoot);
|
|
5315
|
+
const refsPath = path6.join(this.projectRoot, ".git", "refs", "heads");
|
|
5316
|
+
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
5317
|
+
persistent: true,
|
|
5318
|
+
ignoreInitial: true,
|
|
5319
|
+
awaitWriteFinish: {
|
|
5320
|
+
stabilityThreshold: 50,
|
|
5321
|
+
pollInterval: 10
|
|
5322
|
+
}
|
|
5323
|
+
});
|
|
5324
|
+
this.watcher.on("change", () => this.handleHeadChange());
|
|
5325
|
+
this.watcher.on("add", () => this.handleHeadChange());
|
|
5326
|
+
}
|
|
5327
|
+
handleHeadChange() {
|
|
5328
|
+
if (this.debounceTimer) {
|
|
5329
|
+
clearTimeout(this.debounceTimer);
|
|
5330
|
+
}
|
|
5331
|
+
this.debounceTimer = setTimeout(() => {
|
|
5332
|
+
this.checkBranchChange();
|
|
5333
|
+
}, this.debounceMs);
|
|
5334
|
+
}
|
|
5335
|
+
async checkBranchChange() {
|
|
5336
|
+
const newBranch = getCurrentBranch(this.projectRoot);
|
|
5337
|
+
if (newBranch && newBranch !== this.currentBranch && this.onBranchChange) {
|
|
5338
|
+
const oldBranch = this.currentBranch;
|
|
5339
|
+
this.currentBranch = newBranch;
|
|
5340
|
+
try {
|
|
5341
|
+
await this.onBranchChange(oldBranch, newBranch);
|
|
5342
|
+
} catch (error) {
|
|
5343
|
+
console.error("Error handling branch change:", error);
|
|
5344
|
+
}
|
|
5345
|
+
} else if (newBranch) {
|
|
5346
|
+
this.currentBranch = newBranch;
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
getCurrentBranch() {
|
|
5350
|
+
return this.currentBranch;
|
|
5351
|
+
}
|
|
5352
|
+
stop() {
|
|
5353
|
+
if (this.debounceTimer) {
|
|
5354
|
+
clearTimeout(this.debounceTimer);
|
|
5355
|
+
this.debounceTimer = null;
|
|
5356
|
+
}
|
|
5357
|
+
if (this.watcher) {
|
|
5358
|
+
this.watcher.close();
|
|
5359
|
+
this.watcher = null;
|
|
5360
|
+
}
|
|
5361
|
+
this.onBranchChange = null;
|
|
5362
|
+
}
|
|
5363
|
+
isRunning() {
|
|
5364
|
+
return this.watcher !== null;
|
|
5365
|
+
}
|
|
5366
|
+
};
|
|
5040
5367
|
function createWatcherWithIndexer(indexer, projectRoot, config) {
|
|
5041
|
-
const
|
|
5042
|
-
|
|
5368
|
+
const fileWatcher = new FileWatcher(projectRoot, config);
|
|
5369
|
+
fileWatcher.start(async (changes) => {
|
|
5043
5370
|
const hasAddOrChange = changes.some(
|
|
5044
5371
|
(c) => c.type === "add" || c.type === "change"
|
|
5045
5372
|
);
|
|
@@ -5048,7 +5375,22 @@ function createWatcherWithIndexer(indexer, projectRoot, config) {
|
|
|
5048
5375
|
await indexer.index();
|
|
5049
5376
|
}
|
|
5050
5377
|
});
|
|
5051
|
-
|
|
5378
|
+
let gitWatcher = null;
|
|
5379
|
+
if (isGitRepo(projectRoot)) {
|
|
5380
|
+
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
5381
|
+
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
5382
|
+
console.log(`Branch changed: ${oldBranch ?? "(none)"} -> ${newBranch}`);
|
|
5383
|
+
await indexer.index();
|
|
5384
|
+
});
|
|
5385
|
+
}
|
|
5386
|
+
return {
|
|
5387
|
+
fileWatcher,
|
|
5388
|
+
gitWatcher,
|
|
5389
|
+
stop() {
|
|
5390
|
+
fileWatcher.stop();
|
|
5391
|
+
gitWatcher?.stop();
|
|
5392
|
+
}
|
|
5393
|
+
};
|
|
5052
5394
|
}
|
|
5053
5395
|
|
|
5054
5396
|
// src/tools/index.ts
|
|
@@ -5132,13 +5474,19 @@ var index_health_check = (0, import_plugin.tool)({
|
|
|
5132
5474
|
async execute() {
|
|
5133
5475
|
const indexer = getIndexer();
|
|
5134
5476
|
const result = await indexer.healthCheck();
|
|
5135
|
-
if (result.removed === 0) {
|
|
5477
|
+
if (result.removed === 0 && result.gcOrphanEmbeddings === 0 && result.gcOrphanChunks === 0) {
|
|
5136
5478
|
return "Index is healthy. No stale entries found.";
|
|
5137
5479
|
}
|
|
5138
|
-
const lines = [
|
|
5139
|
-
|
|
5140
|
-
` Removed stale entries: ${result.removed}`
|
|
5141
|
-
|
|
5480
|
+
const lines = [`Health check complete:`];
|
|
5481
|
+
if (result.removed > 0) {
|
|
5482
|
+
lines.push(` Removed stale entries: ${result.removed}`);
|
|
5483
|
+
}
|
|
5484
|
+
if (result.gcOrphanEmbeddings > 0) {
|
|
5485
|
+
lines.push(` Garbage collected orphan embeddings: ${result.gcOrphanEmbeddings}`);
|
|
5486
|
+
}
|
|
5487
|
+
if (result.gcOrphanChunks > 0) {
|
|
5488
|
+
lines.push(` Garbage collected orphan chunks: ${result.gcOrphanChunks}`);
|
|
5489
|
+
}
|
|
5142
5490
|
if (result.filePaths.length > 0) {
|
|
5143
5491
|
lines.push(` Cleaned paths: ${result.filePaths.join(", ")}`);
|
|
5144
5492
|
}
|
|
@@ -5193,21 +5541,26 @@ function formatStatus(status) {
|
|
|
5193
5541
|
if (!status.indexed) {
|
|
5194
5542
|
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
5195
5543
|
}
|
|
5196
|
-
|
|
5544
|
+
const lines = [
|
|
5197
5545
|
`Index status:`,
|
|
5198
5546
|
` Indexed chunks: ${status.vectorCount.toLocaleString()}`,
|
|
5199
5547
|
` Provider: ${status.provider}`,
|
|
5200
5548
|
` Model: ${status.model}`,
|
|
5201
5549
|
` Location: ${status.indexPath}`
|
|
5202
|
-
]
|
|
5550
|
+
];
|
|
5551
|
+
if (status.currentBranch !== "default") {
|
|
5552
|
+
lines.push(` Current branch: ${status.currentBranch}`);
|
|
5553
|
+
lines.push(` Base branch: ${status.baseBranch}`);
|
|
5554
|
+
}
|
|
5555
|
+
return lines.join("\n");
|
|
5203
5556
|
}
|
|
5204
5557
|
|
|
5205
5558
|
// src/index.ts
|
|
5206
5559
|
function loadPluginConfig(projectRoot) {
|
|
5207
|
-
const configPath =
|
|
5560
|
+
const configPath = path7.join(projectRoot, ".opencode", "codebase-index.json");
|
|
5208
5561
|
try {
|
|
5209
|
-
if ((0,
|
|
5210
|
-
const content = (0,
|
|
5562
|
+
if ((0, import_fs5.existsSync)(configPath)) {
|
|
5563
|
+
const content = (0, import_fs5.readFileSync)(configPath, "utf-8");
|
|
5211
5564
|
return JSON.parse(content);
|
|
5212
5565
|
}
|
|
5213
5566
|
} catch {
|