@mhalder/qdrant-mcp-server 1.4.0 → 1.5.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/.codecov.yml +16 -0
- package/CHANGELOG.md +18 -0
- package/README.md +236 -9
- package/build/code/chunker/base.d.ts +19 -0
- package/build/code/chunker/base.d.ts.map +1 -0
- package/build/code/chunker/base.js +5 -0
- package/build/code/chunker/base.js.map +1 -0
- package/build/code/chunker/character-chunker.d.ts +22 -0
- package/build/code/chunker/character-chunker.d.ts.map +1 -0
- package/build/code/chunker/character-chunker.js +111 -0
- package/build/code/chunker/character-chunker.js.map +1 -0
- package/build/code/chunker/tree-sitter-chunker.d.ts +29 -0
- package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -0
- package/build/code/chunker/tree-sitter-chunker.js +213 -0
- package/build/code/chunker/tree-sitter-chunker.js.map +1 -0
- package/build/code/config.d.ts +11 -0
- package/build/code/config.d.ts.map +1 -0
- package/build/code/config.js +145 -0
- package/build/code/config.js.map +1 -0
- package/build/code/indexer.d.ts +42 -0
- package/build/code/indexer.d.ts.map +1 -0
- package/build/code/indexer.js +508 -0
- package/build/code/indexer.js.map +1 -0
- package/build/code/metadata.d.ts +32 -0
- package/build/code/metadata.d.ts.map +1 -0
- package/build/code/metadata.js +128 -0
- package/build/code/metadata.js.map +1 -0
- package/build/code/scanner.d.ts +35 -0
- package/build/code/scanner.d.ts.map +1 -0
- package/build/code/scanner.js +108 -0
- package/build/code/scanner.js.map +1 -0
- package/build/code/sync/merkle.d.ts +45 -0
- package/build/code/sync/merkle.d.ts.map +1 -0
- package/build/code/sync/merkle.js +116 -0
- package/build/code/sync/merkle.js.map +1 -0
- package/build/code/sync/snapshot.d.ts +41 -0
- package/build/code/sync/snapshot.d.ts.map +1 -0
- package/build/code/sync/snapshot.js +91 -0
- package/build/code/sync/snapshot.js.map +1 -0
- package/build/code/sync/synchronizer.d.ts +53 -0
- package/build/code/sync/synchronizer.d.ts.map +1 -0
- package/build/code/sync/synchronizer.js +132 -0
- package/build/code/sync/synchronizer.js.map +1 -0
- package/build/code/types.d.ts +98 -0
- package/build/code/types.d.ts.map +1 -0
- package/build/code/types.js +5 -0
- package/build/code/types.js.map +1 -0
- package/build/index.js +250 -0
- package/build/index.js.map +1 -1
- package/examples/code-search/README.md +271 -0
- package/package.json +13 -1
- package/src/code/chunker/base.ts +22 -0
- package/src/code/chunker/character-chunker.ts +131 -0
- package/src/code/chunker/tree-sitter-chunker.ts +250 -0
- package/src/code/config.ts +156 -0
- package/src/code/indexer.ts +613 -0
- package/src/code/metadata.ts +153 -0
- package/src/code/scanner.ts +124 -0
- package/src/code/sync/merkle.ts +136 -0
- package/src/code/sync/snapshot.ts +110 -0
- package/src/code/sync/synchronizer.ts +154 -0
- package/src/code/types.ts +117 -0
- package/src/index.ts +296 -0
- package/tests/code/chunker/character-chunker.test.ts +141 -0
- package/tests/code/chunker/tree-sitter-chunker.test.ts +275 -0
- package/tests/code/fixtures/sample-py/calculator.py +32 -0
- package/tests/code/fixtures/sample-ts/async-operations.ts +120 -0
- package/tests/code/fixtures/sample-ts/auth.ts +31 -0
- package/tests/code/fixtures/sample-ts/config.ts +52 -0
- package/tests/code/fixtures/sample-ts/database.ts +50 -0
- package/tests/code/fixtures/sample-ts/index.ts +39 -0
- package/tests/code/fixtures/sample-ts/types-advanced.ts +132 -0
- package/tests/code/fixtures/sample-ts/utils.ts +105 -0
- package/tests/code/fixtures/sample-ts/validator.ts +169 -0
- package/tests/code/indexer.test.ts +828 -0
- package/tests/code/integration.test.ts +708 -0
- package/tests/code/metadata.test.ts +457 -0
- package/tests/code/scanner.test.ts +131 -0
- package/tests/code/sync/merkle.test.ts +406 -0
- package/tests/code/sync/snapshot.test.ts +360 -0
- package/tests/code/sync/synchronizer.test.ts +501 -0
- package/vitest.config.ts +1 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetadataExtractor - Extracts metadata from code chunks
|
|
3
|
+
*/
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { extname } from "node:path";
|
|
6
|
+
import { LANGUAGE_MAP } from "./config.js";
|
|
7
|
+
export class MetadataExtractor {
|
|
8
|
+
/**
|
|
9
|
+
* Extract programming language from file path
|
|
10
|
+
*/
|
|
11
|
+
extractLanguage(filePath) {
|
|
12
|
+
const ext = extname(filePath);
|
|
13
|
+
return LANGUAGE_MAP[ext] || "unknown";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate deterministic chunk ID based on content and location
|
|
17
|
+
* Format: chunk_{sha256(path:start:end:content)[:16]}
|
|
18
|
+
*/
|
|
19
|
+
generateChunkId(chunk) {
|
|
20
|
+
const { metadata, startLine, endLine, content } = chunk;
|
|
21
|
+
const combined = `${metadata.filePath}:${startLine}:${endLine}:${content}`;
|
|
22
|
+
const hash = createHash("sha256").update(combined).digest("hex");
|
|
23
|
+
return `chunk_${hash.substring(0, 16)}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calculate simple code complexity score (optional)
|
|
27
|
+
* Based on: cyclomatic complexity indicators
|
|
28
|
+
*/
|
|
29
|
+
calculateComplexity(code) {
|
|
30
|
+
if (!code || code.trim().length === 0) {
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
let complexity = 0;
|
|
34
|
+
// Count control flow statements
|
|
35
|
+
const controlFlowPatterns = [
|
|
36
|
+
/\bif\b/g,
|
|
37
|
+
/\belse\b/g,
|
|
38
|
+
/\bfor\b/g,
|
|
39
|
+
/\bwhile\b/g,
|
|
40
|
+
/\bswitch\b/g,
|
|
41
|
+
/\bcase\b/g,
|
|
42
|
+
/\bcatch\b/g,
|
|
43
|
+
/&&/g,
|
|
44
|
+
/\|\|/g,
|
|
45
|
+
/\?[^?]/g, // Ternary operator
|
|
46
|
+
];
|
|
47
|
+
for (const pattern of controlFlowPatterns) {
|
|
48
|
+
const matches = code.match(pattern);
|
|
49
|
+
if (matches) {
|
|
50
|
+
complexity += matches.length;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// If code contains function/method/class, add base complexity of 1
|
|
54
|
+
if (complexity > 0 || /\b(function|class|def|fn)\b/.test(code)) {
|
|
55
|
+
complexity = Math.max(1, complexity);
|
|
56
|
+
}
|
|
57
|
+
return complexity;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Detect potential secrets in code (basic pattern matching)
|
|
61
|
+
*/
|
|
62
|
+
containsSecrets(code) {
|
|
63
|
+
const secretPatterns = [
|
|
64
|
+
/(?:api[-_]?key|apikey)\s*=\s*['"][^'"]{20,}['"]/i,
|
|
65
|
+
/(?:secret|SECRET)\s*=\s*['"][^'"]{20,}['"]/i,
|
|
66
|
+
/(?:password|PASSWORD)\s*=\s*['"][^'"]{8,}['"]/i,
|
|
67
|
+
/(?:token|TOKEN)\s*=\s*['"][^'"]{20,}['"]/i,
|
|
68
|
+
/-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/,
|
|
69
|
+
/sk_live_[a-zA-Z0-9]{24,}/, // Stripe secret key
|
|
70
|
+
/ghp_[a-zA-Z0-9]{36,}/, // GitHub personal access token
|
|
71
|
+
/AIza[0-9A-Za-z\\-_]{35}/, // Google API key
|
|
72
|
+
/AKIA[0-9A-Z]{16}/, // AWS access key
|
|
73
|
+
];
|
|
74
|
+
for (const pattern of secretPatterns) {
|
|
75
|
+
if (pattern.test(code)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extract imports/exports from code (basic regex-based)
|
|
83
|
+
*/
|
|
84
|
+
extractImportsExports(code, language) {
|
|
85
|
+
const imports = [];
|
|
86
|
+
const exports = [];
|
|
87
|
+
if (language === "typescript" || language === "javascript") {
|
|
88
|
+
// Extract imports
|
|
89
|
+
const importMatches = code.matchAll(/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g);
|
|
90
|
+
for (const match of importMatches) {
|
|
91
|
+
imports.push(match[1]);
|
|
92
|
+
}
|
|
93
|
+
// Extract require statements
|
|
94
|
+
const requireMatches = code.matchAll(/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g);
|
|
95
|
+
for (const match of requireMatches) {
|
|
96
|
+
imports.push(match[1]);
|
|
97
|
+
}
|
|
98
|
+
// Extract exports - regular declarations
|
|
99
|
+
const exportMatches = code.matchAll(/export\s+(?:class|function|const|let|var)\s+(\w+)/g);
|
|
100
|
+
for (const match of exportMatches) {
|
|
101
|
+
exports.push(match[1]);
|
|
102
|
+
}
|
|
103
|
+
// Extract export default
|
|
104
|
+
if (/export\s+default\b/.test(code)) {
|
|
105
|
+
exports.push("default");
|
|
106
|
+
}
|
|
107
|
+
// Extract named exports from other modules: export { name } from 'module'
|
|
108
|
+
const reExportMatches = code.matchAll(/export\s+\{\s*(\w+)\s*\}/g);
|
|
109
|
+
for (const match of reExportMatches) {
|
|
110
|
+
exports.push(match[1]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (language === "python") {
|
|
114
|
+
// Extract imports
|
|
115
|
+
const importMatches = code.matchAll(/(?:from\s+(\S+)\s+)?import\s+([^;\n]+)/g);
|
|
116
|
+
for (const match of importMatches) {
|
|
117
|
+
imports.push(match[1] || match[2]);
|
|
118
|
+
}
|
|
119
|
+
// Extract functions/classes (rough)
|
|
120
|
+
const defMatches = code.matchAll(/^(?:def|class)\s+(\w+)/gm);
|
|
121
|
+
for (const match of defMatches) {
|
|
122
|
+
exports.push(match[1]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { imports, exports };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/code/metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileScanner - Discovers code files in a directory while respecting ignore patterns
|
|
3
|
+
*/
|
|
4
|
+
import type { ScannerConfig } from "./types.js";
|
|
5
|
+
export declare class FileScanner {
|
|
6
|
+
private config;
|
|
7
|
+
private ig;
|
|
8
|
+
private supportedExts;
|
|
9
|
+
constructor(config: ScannerConfig);
|
|
10
|
+
/**
|
|
11
|
+
* Load ignore patterns from .gitignore, .dockerignore, .npmignore, and .contextignore
|
|
12
|
+
*/
|
|
13
|
+
loadIgnorePatterns(rootPath: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Scan directory recursively and return all code files
|
|
16
|
+
*/
|
|
17
|
+
scanDirectory(rootPath: string): Promise<string[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a file should be ignored based on patterns
|
|
20
|
+
*/
|
|
21
|
+
shouldIgnore(filePath: string, rootPath: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Get list of supported file extensions
|
|
24
|
+
*/
|
|
25
|
+
getSupportedExtensions(): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Recursively walk directory tree
|
|
28
|
+
*/
|
|
29
|
+
private walkDirectory;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a file exists
|
|
32
|
+
*/
|
|
33
|
+
private fileExists;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/code/scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,WAAW;IAIV,OAAO,CAAC,MAAM;IAH1B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,aAAa,CAAc;gBAEf,MAAM,EAAE,aAAa;IAIzC;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BzD;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IASxD;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKzD;;OAEG;IACH,sBAAsB,IAAI,MAAM,EAAE;IAIlC;;OAEG;YACW,aAAa;IAoC3B;;OAEG;YACW,UAAU;CAQzB"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileScanner - Discovers code files in a directory while respecting ignore patterns
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from "node:fs";
|
|
5
|
+
import { extname, join, relative, resolve } from "node:path";
|
|
6
|
+
import ignore from "ignore";
|
|
7
|
+
export class FileScanner {
|
|
8
|
+
config;
|
|
9
|
+
ig = ignore();
|
|
10
|
+
supportedExts;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.supportedExts = new Set(config.supportedExtensions);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load ignore patterns from .gitignore, .dockerignore, .npmignore, and .contextignore
|
|
17
|
+
*/
|
|
18
|
+
async loadIgnorePatterns(rootPath) {
|
|
19
|
+
const ignoreFiles = [".gitignore", ".dockerignore", ".npmignore", ".contextignore"];
|
|
20
|
+
for (const ignoreFile of ignoreFiles) {
|
|
21
|
+
const ignorePath = join(rootPath, ignoreFile);
|
|
22
|
+
if (await this.fileExists(ignorePath)) {
|
|
23
|
+
try {
|
|
24
|
+
const content = await fs.readFile(ignorePath, "utf-8");
|
|
25
|
+
this.ig.add(content);
|
|
26
|
+
}
|
|
27
|
+
catch (_error) {
|
|
28
|
+
// Silently skip if file can't be read
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Add default patterns from config
|
|
33
|
+
if (this.config.ignorePatterns && this.config.ignorePatterns.length > 0) {
|
|
34
|
+
this.ig.add(this.config.ignorePatterns);
|
|
35
|
+
}
|
|
36
|
+
// Add custom patterns
|
|
37
|
+
if (this.config.customIgnorePatterns && this.config.customIgnorePatterns.length > 0) {
|
|
38
|
+
this.ig.add(this.config.customIgnorePatterns);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Scan directory recursively and return all code files
|
|
43
|
+
*/
|
|
44
|
+
async scanDirectory(rootPath) {
|
|
45
|
+
const absoluteRoot = resolve(rootPath);
|
|
46
|
+
const files = [];
|
|
47
|
+
await this.walkDirectory(absoluteRoot, absoluteRoot, files);
|
|
48
|
+
return files;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if a file should be ignored based on patterns
|
|
52
|
+
*/
|
|
53
|
+
shouldIgnore(filePath, rootPath) {
|
|
54
|
+
const relativePath = relative(rootPath, filePath);
|
|
55
|
+
return this.ig.ignores(relativePath);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get list of supported file extensions
|
|
59
|
+
*/
|
|
60
|
+
getSupportedExtensions() {
|
|
61
|
+
return Array.from(this.supportedExts);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Recursively walk directory tree
|
|
65
|
+
*/
|
|
66
|
+
async walkDirectory(currentPath, rootPath, files) {
|
|
67
|
+
try {
|
|
68
|
+
const entries = await fs.readdir(currentPath, { withFileTypes: true });
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const fullPath = join(currentPath, entry.name);
|
|
71
|
+
const relativePath = relative(rootPath, fullPath);
|
|
72
|
+
// Skip ignored paths
|
|
73
|
+
if (this.ig.ignores(relativePath)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Handle symbolic links safely to avoid infinite loops
|
|
77
|
+
if (entry.isSymbolicLink()) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (entry.isDirectory()) {
|
|
81
|
+
await this.walkDirectory(fullPath, rootPath, files);
|
|
82
|
+
}
|
|
83
|
+
else if (entry.isFile()) {
|
|
84
|
+
const ext = extname(entry.name);
|
|
85
|
+
if (this.supportedExts.has(ext)) {
|
|
86
|
+
files.push(fullPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (_error) {
|
|
92
|
+
// Skip directories that can't be read (permission errors, etc.)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check if a file exists
|
|
97
|
+
*/
|
|
98
|
+
async fileExists(path) {
|
|
99
|
+
try {
|
|
100
|
+
await fs.access(path);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/code/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MerkleTree - Efficient change detection using Merkle trees
|
|
3
|
+
* Enables incremental updates by comparing file hashes
|
|
4
|
+
*/
|
|
5
|
+
export declare class MerkleNode {
|
|
6
|
+
hash: string;
|
|
7
|
+
left?: MerkleNode | undefined;
|
|
8
|
+
right?: MerkleNode | undefined;
|
|
9
|
+
constructor(hash: string, left?: MerkleNode | undefined, right?: MerkleNode | undefined);
|
|
10
|
+
}
|
|
11
|
+
export declare class MerkleTree {
|
|
12
|
+
root: MerkleNode | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Build Merkle tree from file hashes
|
|
15
|
+
* @param fileHashes Map of file path to content hash
|
|
16
|
+
*/
|
|
17
|
+
build(fileHashes: Map<string, string>): void;
|
|
18
|
+
/**
|
|
19
|
+
* Recursively build tree from leaf nodes
|
|
20
|
+
*/
|
|
21
|
+
private buildRecursive;
|
|
22
|
+
/**
|
|
23
|
+
* Compare two trees and return file differences
|
|
24
|
+
*/
|
|
25
|
+
static compare(oldHashes: Map<string, string>, newHashes: Map<string, string>): {
|
|
26
|
+
added: string[];
|
|
27
|
+
modified: string[];
|
|
28
|
+
deleted: string[];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Get root hash (quick comparison)
|
|
32
|
+
*/
|
|
33
|
+
getRootHash(): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Serialize tree for storage
|
|
36
|
+
*/
|
|
37
|
+
serialize(): string;
|
|
38
|
+
private serializeNode;
|
|
39
|
+
/**
|
|
40
|
+
* Deserialize tree from storage
|
|
41
|
+
*/
|
|
42
|
+
static deserialize(data: string): MerkleTree;
|
|
43
|
+
private deserializeNode;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=merkle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merkle.d.ts","sourceRoot":"","sources":["../../../src/code/sync/merkle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,qBAAa,UAAU;IAEZ,IAAI,EAAE,MAAM;IACZ,IAAI,CAAC,EAAE,UAAU;IACjB,KAAK,CAAC,EAAE,UAAU;gBAFlB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,UAAU,YAAA,EACjB,KAAK,CAAC,EAAE,UAAU,YAAA;CAE5B;AAED,qBAAa,UAAU;IACrB,IAAI,EAAE,UAAU,GAAG,SAAS,CAAa;IAEzC;;;OAGG;IACH,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAkB5C;;OAEG;IACH,OAAO,CAAC,cAAc;IAmBtB;;OAEG;IACH,MAAM,CAAC,OAAO,CACZ,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IAwB7D;;OAEG;IACH,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC;;OAEG;IACH,SAAS,IAAI,MAAM;IAMnB,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;IAO5C,OAAO,CAAC,eAAe;CAQxB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MerkleTree - Efficient change detection using Merkle trees
|
|
3
|
+
* Enables incremental updates by comparing file hashes
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
export class MerkleNode {
|
|
7
|
+
hash;
|
|
8
|
+
left;
|
|
9
|
+
right;
|
|
10
|
+
constructor(hash, left, right) {
|
|
11
|
+
this.hash = hash;
|
|
12
|
+
this.left = left;
|
|
13
|
+
this.right = right;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class MerkleTree {
|
|
17
|
+
root = undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Build Merkle tree from file hashes
|
|
20
|
+
* @param fileHashes Map of file path to content hash
|
|
21
|
+
*/
|
|
22
|
+
build(fileHashes) {
|
|
23
|
+
if (fileHashes.size === 0) {
|
|
24
|
+
this.root = undefined;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Sort files for deterministic tree structure
|
|
28
|
+
const leaves = Array.from(fileHashes.entries())
|
|
29
|
+
.sort(([pathA], [pathB]) => pathA.localeCompare(pathB))
|
|
30
|
+
.map(([path, hash]) => {
|
|
31
|
+
const combined = `${path}:${hash}`;
|
|
32
|
+
const leafHash = createHash("sha256").update(combined).digest("hex");
|
|
33
|
+
return new MerkleNode(leafHash);
|
|
34
|
+
});
|
|
35
|
+
this.root = this.buildRecursive(leaves);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Recursively build tree from leaf nodes
|
|
39
|
+
*/
|
|
40
|
+
buildRecursive(nodes) {
|
|
41
|
+
if (nodes.length === 1) {
|
|
42
|
+
return nodes[0];
|
|
43
|
+
}
|
|
44
|
+
const parents = [];
|
|
45
|
+
for (let i = 0; i < nodes.length; i += 2) {
|
|
46
|
+
const left = nodes[i];
|
|
47
|
+
const right = nodes[i + 1] || left; // Duplicate if odd number
|
|
48
|
+
const combined = left.hash + right.hash;
|
|
49
|
+
const parentHash = createHash("sha256").update(combined).digest("hex");
|
|
50
|
+
parents.push(new MerkleNode(parentHash, left, right));
|
|
51
|
+
}
|
|
52
|
+
return this.buildRecursive(parents);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Compare two trees and return file differences
|
|
56
|
+
*/
|
|
57
|
+
static compare(oldHashes, newHashes) {
|
|
58
|
+
const added = [];
|
|
59
|
+
const modified = [];
|
|
60
|
+
const deleted = [];
|
|
61
|
+
// Find added and modified files
|
|
62
|
+
for (const [path, hash] of newHashes) {
|
|
63
|
+
if (!oldHashes.has(path)) {
|
|
64
|
+
added.push(path);
|
|
65
|
+
}
|
|
66
|
+
else if (oldHashes.get(path) !== hash) {
|
|
67
|
+
modified.push(path);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Find deleted files
|
|
71
|
+
for (const [path] of oldHashes) {
|
|
72
|
+
if (!newHashes.has(path)) {
|
|
73
|
+
deleted.push(path);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { added, modified, deleted };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get root hash (quick comparison)
|
|
80
|
+
*/
|
|
81
|
+
getRootHash() {
|
|
82
|
+
return this.root?.hash;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Serialize tree for storage
|
|
86
|
+
*/
|
|
87
|
+
serialize() {
|
|
88
|
+
return JSON.stringify({
|
|
89
|
+
root: this.serializeNode(this.root),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
serializeNode(node) {
|
|
93
|
+
if (!node)
|
|
94
|
+
return null;
|
|
95
|
+
return {
|
|
96
|
+
hash: node.hash,
|
|
97
|
+
left: this.serializeNode(node.left),
|
|
98
|
+
right: this.serializeNode(node.right),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Deserialize tree from storage
|
|
103
|
+
*/
|
|
104
|
+
static deserialize(data) {
|
|
105
|
+
const tree = new MerkleTree();
|
|
106
|
+
const obj = JSON.parse(data);
|
|
107
|
+
tree.root = tree.deserializeNode(obj.root);
|
|
108
|
+
return tree;
|
|
109
|
+
}
|
|
110
|
+
deserializeNode(obj) {
|
|
111
|
+
if (!obj)
|
|
112
|
+
return undefined;
|
|
113
|
+
return new MerkleNode(obj.hash, this.deserializeNode(obj.left), this.deserializeNode(obj.right));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=merkle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merkle.js","sourceRoot":"","sources":["../../../src/code/sync/merkle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot - Persistence layer for Merkle tree snapshots
|
|
3
|
+
* Stores file hashes and tree structure for incremental updates
|
|
4
|
+
*/
|
|
5
|
+
import { MerkleTree } from "./merkle.js";
|
|
6
|
+
export interface Snapshot {
|
|
7
|
+
codebasePath: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
fileHashes: Record<string, string>;
|
|
10
|
+
merkleTree: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class SnapshotManager {
|
|
13
|
+
private snapshotPath;
|
|
14
|
+
constructor(snapshotPath: string);
|
|
15
|
+
/**
|
|
16
|
+
* Save snapshot to disk
|
|
17
|
+
*/
|
|
18
|
+
save(codebasePath: string, fileHashes: Map<string, string>, tree: MerkleTree): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Load snapshot from disk
|
|
21
|
+
*/
|
|
22
|
+
load(): Promise<{
|
|
23
|
+
codebasePath: string;
|
|
24
|
+
fileHashes: Map<string, string>;
|
|
25
|
+
merkleTree: MerkleTree;
|
|
26
|
+
timestamp: number;
|
|
27
|
+
} | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Check if snapshot exists
|
|
30
|
+
*/
|
|
31
|
+
exists(): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Delete snapshot
|
|
34
|
+
*/
|
|
35
|
+
delete(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Validate snapshot (check for corruption)
|
|
38
|
+
*/
|
|
39
|
+
validate(): Promise<boolean>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/code/sync/snapshot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,QAAQ;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,YAAY;gBAAZ,YAAY,EAAE,MAAM;IAExC;;OAEG;IACG,IAAI,CACR,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAkBhB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,UAAU,EAAE,UAAU,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IAoBT;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAShC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;CAWnC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot - Persistence layer for Merkle tree snapshots
|
|
3
|
+
* Stores file hashes and tree structure for incremental updates
|
|
4
|
+
*/
|
|
5
|
+
import { promises as fs } from "node:fs";
|
|
6
|
+
import { dirname } from "node:path";
|
|
7
|
+
import { MerkleTree } from "./merkle.js";
|
|
8
|
+
export class SnapshotManager {
|
|
9
|
+
snapshotPath;
|
|
10
|
+
constructor(snapshotPath) {
|
|
11
|
+
this.snapshotPath = snapshotPath;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Save snapshot to disk
|
|
15
|
+
*/
|
|
16
|
+
async save(codebasePath, fileHashes, tree) {
|
|
17
|
+
const snapshot = {
|
|
18
|
+
codebasePath,
|
|
19
|
+
timestamp: Date.now(),
|
|
20
|
+
fileHashes: Object.fromEntries(fileHashes),
|
|
21
|
+
merkleTree: tree.serialize(),
|
|
22
|
+
};
|
|
23
|
+
// Ensure directory exists
|
|
24
|
+
await fs.mkdir(dirname(this.snapshotPath), { recursive: true });
|
|
25
|
+
// Write snapshot atomically (write to temp file, then rename)
|
|
26
|
+
// Use unique temp file name to avoid race conditions
|
|
27
|
+
const tempPath = `${this.snapshotPath}.tmp.${Date.now()}.${Math.random().toString(36).substring(2, 9)}`;
|
|
28
|
+
await fs.writeFile(tempPath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
29
|
+
await fs.rename(tempPath, this.snapshotPath);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Load snapshot from disk
|
|
33
|
+
*/
|
|
34
|
+
async load() {
|
|
35
|
+
try {
|
|
36
|
+
const data = await fs.readFile(this.snapshotPath, "utf-8");
|
|
37
|
+
const snapshot = JSON.parse(data);
|
|
38
|
+
const fileHashes = new Map(Object.entries(snapshot.fileHashes));
|
|
39
|
+
const tree = MerkleTree.deserialize(snapshot.merkleTree);
|
|
40
|
+
return {
|
|
41
|
+
codebasePath: snapshot.codebasePath,
|
|
42
|
+
fileHashes,
|
|
43
|
+
merkleTree: tree,
|
|
44
|
+
timestamp: snapshot.timestamp,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (_error) {
|
|
48
|
+
// Snapshot doesn't exist or is corrupted
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if snapshot exists
|
|
54
|
+
*/
|
|
55
|
+
async exists() {
|
|
56
|
+
try {
|
|
57
|
+
await fs.access(this.snapshotPath);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Delete snapshot
|
|
66
|
+
*/
|
|
67
|
+
async delete() {
|
|
68
|
+
try {
|
|
69
|
+
await fs.unlink(this.snapshotPath);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Ignore if doesn't exist
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate snapshot (check for corruption)
|
|
77
|
+
*/
|
|
78
|
+
async validate() {
|
|
79
|
+
try {
|
|
80
|
+
const snapshot = await this.load();
|
|
81
|
+
if (!snapshot)
|
|
82
|
+
return false;
|
|
83
|
+
// Basic validation: check if tree can be deserialized
|
|
84
|
+
return snapshot.merkleTree.getRootHash() !== undefined || snapshot.fileHashes.size === 0;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/code/sync/snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileSynchronizer - Manages incremental updates using Merkle trees
|
|
3
|
+
* Detects file changes and updates snapshots
|
|
4
|
+
*/
|
|
5
|
+
import type { FileChanges } from "../types.js";
|
|
6
|
+
export declare class FileSynchronizer {
|
|
7
|
+
private codebasePath;
|
|
8
|
+
private snapshotManager;
|
|
9
|
+
private previousHashes;
|
|
10
|
+
private previousTree;
|
|
11
|
+
constructor(codebasePath: string, collectionName: string);
|
|
12
|
+
/**
|
|
13
|
+
* Initialize synchronizer by loading previous snapshot
|
|
14
|
+
*/
|
|
15
|
+
initialize(): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Compute hash for a file's content
|
|
18
|
+
*/
|
|
19
|
+
private hashFile;
|
|
20
|
+
/**
|
|
21
|
+
* Compute hashes for all files
|
|
22
|
+
*/
|
|
23
|
+
computeFileHashes(filePaths: string[]): Promise<Map<string, string>>;
|
|
24
|
+
/**
|
|
25
|
+
* Detect changes since last snapshot
|
|
26
|
+
*/
|
|
27
|
+
detectChanges(currentFiles: string[]): Promise<FileChanges>;
|
|
28
|
+
/**
|
|
29
|
+
* Update snapshot with current state
|
|
30
|
+
*/
|
|
31
|
+
updateSnapshot(files: string[]): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Delete snapshot
|
|
34
|
+
*/
|
|
35
|
+
deleteSnapshot(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if snapshot exists
|
|
38
|
+
*/
|
|
39
|
+
hasSnapshot(): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Validate snapshot integrity
|
|
42
|
+
*/
|
|
43
|
+
validateSnapshot(): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Get snapshot age in milliseconds
|
|
46
|
+
*/
|
|
47
|
+
getSnapshotAge(): Promise<number | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Quick check if re-indexing is needed (compare root hashes)
|
|
50
|
+
*/
|
|
51
|
+
needsReindex(currentFiles: string[]): Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=synchronizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synchronizer.d.ts","sourceRoot":"","sources":["../../../src/code/sync/synchronizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,qBAAa,gBAAgB;IAMzB,OAAO,CAAC,YAAY;IALtB,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,YAAY,CAA2B;gBAGrC,YAAY,EAAE,MAAM,EAC5B,cAAc,EAAE,MAAM;IAQxB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAYpC;;OAEG;YACW,QAAQ;IActB;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAiB1E;;OAEG;IACG,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAUjE;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpD;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAI1C;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAO9C;;OAEG;IACG,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAS7D"}
|