@diegonogueiradev_/mcp-graph 1.0.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/dist/core/context/compact-context.d.ts +51 -0
- package/dist/core/context/compact-context.d.ts.map +1 -0
- package/dist/core/context/compact-context.js +115 -0
- package/dist/core/context/compact-context.js.map +1 -0
- package/dist/core/context/rag-context.d.ts +36 -0
- package/dist/core/context/rag-context.d.ts.map +1 -0
- package/dist/core/context/rag-context.js +69 -0
- package/dist/core/context/rag-context.js.map +1 -0
- package/dist/core/context/token-estimator.d.ts +7 -0
- package/dist/core/context/token-estimator.d.ts.map +1 -0
- package/dist/core/context/token-estimator.js +9 -0
- package/dist/core/context/token-estimator.js.map +1 -0
- package/dist/core/graph/graph-indexes.d.ts +3 -0
- package/dist/core/graph/graph-indexes.d.ts.map +1 -0
- package/dist/core/graph/graph-indexes.js +28 -0
- package/dist/core/graph/graph-indexes.js.map +1 -0
- package/dist/core/graph/graph-types.d.ts +72 -0
- package/dist/core/graph/graph-types.d.ts.map +1 -0
- package/dist/core/graph/graph-types.js +2 -0
- package/dist/core/graph/graph-types.js.map +1 -0
- package/dist/core/importer/import-prd.d.ts +7 -0
- package/dist/core/importer/import-prd.d.ts.map +1 -0
- package/dist/core/importer/import-prd.js +7 -0
- package/dist/core/importer/import-prd.js.map +1 -0
- package/dist/core/importer/prd-to-graph.d.ts +18 -0
- package/dist/core/importer/prd-to-graph.d.ts.map +1 -0
- package/dist/core/importer/prd-to-graph.js +188 -0
- package/dist/core/importer/prd-to-graph.js.map +1 -0
- package/dist/core/parser/classify.d.ts +31 -0
- package/dist/core/parser/classify.d.ts.map +1 -0
- package/dist/core/parser/classify.js +128 -0
- package/dist/core/parser/classify.js.map +1 -0
- package/dist/core/parser/extract.d.ts +21 -0
- package/dist/core/parser/extract.d.ts.map +1 -0
- package/dist/core/parser/extract.js +66 -0
- package/dist/core/parser/extract.js.map +1 -0
- package/dist/core/parser/normalize.d.ts +9 -0
- package/dist/core/parser/normalize.d.ts.map +1 -0
- package/dist/core/parser/normalize.js +22 -0
- package/dist/core/parser/normalize.js.map +1 -0
- package/dist/core/parser/read-file.d.ts +7 -0
- package/dist/core/parser/read-file.d.ts.map +1 -0
- package/dist/core/parser/read-file.js +17 -0
- package/dist/core/parser/read-file.js.map +1 -0
- package/dist/core/parser/segment.d.ts +13 -0
- package/dist/core/parser/segment.d.ts.map +1 -0
- package/dist/core/parser/segment.js +53 -0
- package/dist/core/parser/segment.js.map +1 -0
- package/dist/core/planner/decompose.d.ts +25 -0
- package/dist/core/planner/decompose.d.ts.map +1 -0
- package/dist/core/planner/decompose.js +100 -0
- package/dist/core/planner/decompose.js.map +1 -0
- package/dist/core/planner/dependency-chain.d.ts +20 -0
- package/dist/core/planner/dependency-chain.d.ts.map +1 -0
- package/dist/core/planner/dependency-chain.js +163 -0
- package/dist/core/planner/dependency-chain.js.map +1 -0
- package/dist/core/planner/next-task.d.ts +16 -0
- package/dist/core/planner/next-task.d.ts.map +1 -0
- package/dist/core/planner/next-task.js +76 -0
- package/dist/core/planner/next-task.js.map +1 -0
- package/dist/core/planner/velocity.d.ts +38 -0
- package/dist/core/planner/velocity.d.ts.map +1 -0
- package/dist/core/planner/velocity.js +92 -0
- package/dist/core/planner/velocity.js.map +1 -0
- package/dist/core/search/fts-search.d.ts +16 -0
- package/dist/core/search/fts-search.d.ts.map +1 -0
- package/dist/core/search/fts-search.js +57 -0
- package/dist/core/search/fts-search.js.map +1 -0
- package/dist/core/search/tfidf.d.ts +32 -0
- package/dist/core/search/tfidf.d.ts.map +1 -0
- package/dist/core/search/tfidf.js +67 -0
- package/dist/core/search/tfidf.js.map +1 -0
- package/dist/core/search/tokenizer.d.ts +9 -0
- package/dist/core/search/tokenizer.d.ts.map +1 -0
- package/dist/core/search/tokenizer.js +37 -0
- package/dist/core/search/tokenizer.js.map +1 -0
- package/dist/core/store/migrations.d.ts +4 -0
- package/dist/core/store/migrations.d.ts.map +1 -0
- package/dist/core/store/migrations.js +137 -0
- package/dist/core/store/migrations.js.map +1 -0
- package/dist/core/store/sqlite-store.d.ts +68 -0
- package/dist/core/store/sqlite-store.d.ts.map +1 -0
- package/dist/core/store/sqlite-store.js +608 -0
- package/dist/core/store/sqlite-store.js.map +1 -0
- package/dist/core/utils/errors.d.ts +20 -0
- package/dist/core/utils/errors.d.ts.map +1 -0
- package/dist/core/utils/errors.js +39 -0
- package/dist/core/utils/errors.js.map +1 -0
- package/dist/core/utils/fs.d.ts +2 -0
- package/dist/core/utils/fs.d.ts.map +1 -0
- package/dist/core/utils/fs.js +11 -0
- package/dist/core/utils/fs.js.map +1 -0
- package/dist/core/utils/id.d.ts +2 -0
- package/dist/core/utils/id.d.ts.map +1 -0
- package/dist/core/utils/id.js +6 -0
- package/dist/core/utils/id.js.map +1 -0
- package/dist/core/utils/logger.d.ts +7 -0
- package/dist/core/utils/logger.d.ts.map +1 -0
- package/dist/core/utils/logger.js +23 -0
- package/dist/core/utils/logger.js.map +1 -0
- package/dist/core/utils/time.d.ts +2 -0
- package/dist/core/utils/time.d.ts.map +1 -0
- package/dist/core/utils/time.js +4 -0
- package/dist/core/utils/time.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/init-project.d.ts +2 -0
- package/dist/mcp/init-project.d.ts.map +1 -0
- package/dist/mcp/init-project.js +104 -0
- package/dist/mcp/init-project.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +30 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +3 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +20 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/mcp/tools/add-edge.d.ts +4 -0
- package/dist/mcp/tools/add-edge.d.ts.map +1 -0
- package/dist/mcp/tools/add-edge.js +62 -0
- package/dist/mcp/tools/add-edge.js.map +1 -0
- package/dist/mcp/tools/add-node.d.ts +4 -0
- package/dist/mcp/tools/add-node.d.ts.map +1 -0
- package/dist/mcp/tools/add-node.js +80 -0
- package/dist/mcp/tools/add-node.js.map +1 -0
- package/dist/mcp/tools/bulk-update-status.d.ts +4 -0
- package/dist/mcp/tools/bulk-update-status.d.ts.map +1 -0
- package/dist/mcp/tools/bulk-update-status.js +19 -0
- package/dist/mcp/tools/bulk-update-status.js.map +1 -0
- package/dist/mcp/tools/clone-node.d.ts +4 -0
- package/dist/mcp/tools/clone-node.d.ts.map +1 -0
- package/dist/mcp/tools/clone-node.js +107 -0
- package/dist/mcp/tools/clone-node.js.map +1 -0
- package/dist/mcp/tools/context.d.ts +4 -0
- package/dist/mcp/tools/context.d.ts.map +1 -0
- package/dist/mcp/tools/context.js +26 -0
- package/dist/mcp/tools/context.js.map +1 -0
- package/dist/mcp/tools/decompose.d.ts +4 -0
- package/dist/mcp/tools/decompose.d.ts.map +1 -0
- package/dist/mcp/tools/decompose.js +22 -0
- package/dist/mcp/tools/decompose.js.map +1 -0
- package/dist/mcp/tools/delete-edge.d.ts +4 -0
- package/dist/mcp/tools/delete-edge.d.ts.map +1 -0
- package/dist/mcp/tools/delete-edge.js +25 -0
- package/dist/mcp/tools/delete-edge.js.map +1 -0
- package/dist/mcp/tools/delete-node.d.ts +4 -0
- package/dist/mcp/tools/delete-node.d.ts.map +1 -0
- package/dist/mcp/tools/delete-node.js +25 -0
- package/dist/mcp/tools/delete-node.js.map +1 -0
- package/dist/mcp/tools/dependencies.d.ts +4 -0
- package/dist/mcp/tools/dependencies.d.ts.map +1 -0
- package/dist/mcp/tools/dependencies.js +42 -0
- package/dist/mcp/tools/dependencies.js.map +1 -0
- package/dist/mcp/tools/export-graph.d.ts +4 -0
- package/dist/mcp/tools/export-graph.d.ts.map +1 -0
- package/dist/mcp/tools/export-graph.js +14 -0
- package/dist/mcp/tools/export-graph.js.map +1 -0
- package/dist/mcp/tools/import-prd.d.ts +4 -0
- package/dist/mcp/tools/import-prd.d.ts.map +1 -0
- package/dist/mcp/tools/import-prd.js +72 -0
- package/dist/mcp/tools/import-prd.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +4 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +53 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/init.d.ts +4 -0
- package/dist/mcp/tools/init.d.ts.map +1 -0
- package/dist/mcp/tools/init.js +15 -0
- package/dist/mcp/tools/init.js.map +1 -0
- package/dist/mcp/tools/list-edges.d.ts +4 -0
- package/dist/mcp/tools/list-edges.d.ts.map +1 -0
- package/dist/mcp/tools/list-edges.js +49 -0
- package/dist/mcp/tools/list-edges.js.map +1 -0
- package/dist/mcp/tools/list.d.ts +4 -0
- package/dist/mcp/tools/list.d.ts.map +1 -0
- package/dist/mcp/tools/list.js +53 -0
- package/dist/mcp/tools/list.js.map +1 -0
- package/dist/mcp/tools/move-node.d.ts +4 -0
- package/dist/mcp/tools/move-node.d.ts.map +1 -0
- package/dist/mcp/tools/move-node.js +107 -0
- package/dist/mcp/tools/move-node.js.map +1 -0
- package/dist/mcp/tools/next.d.ts +4 -0
- package/dist/mcp/tools/next.d.ts.map +1 -0
- package/dist/mcp/tools/next.js +28 -0
- package/dist/mcp/tools/next.js.map +1 -0
- package/dist/mcp/tools/rag-context.d.ts +4 -0
- package/dist/mcp/tools/rag-context.d.ts.map +1 -0
- package/dist/mcp/tools/rag-context.js +25 -0
- package/dist/mcp/tools/rag-context.js.map +1 -0
- package/dist/mcp/tools/search.d.ts +4 -0
- package/dist/mcp/tools/search.d.ts.map +1 -0
- package/dist/mcp/tools/search.js +38 -0
- package/dist/mcp/tools/search.js.map +1 -0
- package/dist/mcp/tools/show.d.ts +4 -0
- package/dist/mcp/tools/show.d.ts.map +1 -0
- package/dist/mcp/tools/show.js +38 -0
- package/dist/mcp/tools/show.js.map +1 -0
- package/dist/mcp/tools/snapshot.d.ts +6 -0
- package/dist/mcp/tools/snapshot.d.ts.map +1 -0
- package/dist/mcp/tools/snapshot.js +43 -0
- package/dist/mcp/tools/snapshot.js.map +1 -0
- package/dist/mcp/tools/stats.d.ts +4 -0
- package/dist/mcp/tools/stats.d.ts.map +1 -0
- package/dist/mcp/tools/stats.js +43 -0
- package/dist/mcp/tools/stats.js.map +1 -0
- package/dist/mcp/tools/update-node.d.ts +4 -0
- package/dist/mcp/tools/update-node.d.ts.map +1 -0
- package/dist/mcp/tools/update-node.js +41 -0
- package/dist/mcp/tools/update-node.js.map +1 -0
- package/dist/mcp/tools/update-status.d.ts +4 -0
- package/dist/mcp/tools/update-status.d.ts.map +1 -0
- package/dist/mcp/tools/update-status.js +29 -0
- package/dist/mcp/tools/update-status.js.map +1 -0
- package/dist/mcp/tools/velocity.d.ts +4 -0
- package/dist/mcp/tools/velocity.d.ts.map +1 -0
- package/dist/mcp/tools/velocity.js +22 -0
- package/dist/mcp/tools/velocity.js.map +1 -0
- package/dist/schemas/edge.schema.d.ts +31 -0
- package/dist/schemas/edge.schema.d.ts.map +1 -0
- package/dist/schemas/edge.schema.js +16 -0
- package/dist/schemas/edge.schema.js.map +1 -0
- package/dist/schemas/graph.schema.d.ts +102 -0
- package/dist/schemas/graph.schema.d.ts.map +1 -0
- package/dist/schemas/graph.schema.js +28 -0
- package/dist/schemas/graph.schema.js.map +1 -0
- package/dist/schemas/node.schema.d.ts +80 -0
- package/dist/schemas/node.schema.d.ts.map +1 -0
- package/dist/schemas/node.schema.js +38 -0
- package/dist/schemas/node.schema.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { logger } from "../utils/logger.js";
|
|
2
|
+
import { rerankWithTfIdf } from "./tfidf.js";
|
|
3
|
+
/**
|
|
4
|
+
* Sanitize user query for FTS5 — escape special characters and
|
|
5
|
+
* convert spaces to implicit AND (FTS5 default).
|
|
6
|
+
*/
|
|
7
|
+
function sanitizeFtsQuery(raw) {
|
|
8
|
+
// Remove FTS5 special operators that could cause syntax errors
|
|
9
|
+
const cleaned = raw
|
|
10
|
+
.replace(/[*"(){}[\]:^~!@#$%&|\\]/g, " ")
|
|
11
|
+
.replace(/\b(AND|OR|NOT|NEAR)\b/gi, " ")
|
|
12
|
+
.trim()
|
|
13
|
+
.replace(/\s+/g, " ");
|
|
14
|
+
if (!cleaned)
|
|
15
|
+
return '""';
|
|
16
|
+
// Wrap each term in double quotes for exact matching, join with space (implicit AND)
|
|
17
|
+
const terms = cleaned.split(" ").filter(Boolean);
|
|
18
|
+
return terms.map((t) => `"${t}"`).join(" ");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Search nodes using FTS5 full-text search with BM25 ranking.
|
|
22
|
+
* Optionally applies TF-IDF reranking for better relevance.
|
|
23
|
+
*/
|
|
24
|
+
export function searchNodes(store, query, options = {}) {
|
|
25
|
+
const { limit = 20, rerank = false } = options;
|
|
26
|
+
const sanitized = sanitizeFtsQuery(query);
|
|
27
|
+
logger.info(`FTS search: "${query}" → ${sanitized}${rerank ? " (with TF-IDF rerank)" : ""}`);
|
|
28
|
+
// Stage 1: FTS5 candidates (fetch extra for reranking)
|
|
29
|
+
const candidateLimit = rerank ? Math.min(limit * 3, 100) : limit;
|
|
30
|
+
const ftsResults = store.searchNodes(sanitized, candidateLimit);
|
|
31
|
+
const resultMap = new Map();
|
|
32
|
+
for (const r of ftsResults) {
|
|
33
|
+
const { score, ...node } = r;
|
|
34
|
+
resultMap.set(node.id, node);
|
|
35
|
+
}
|
|
36
|
+
if (!rerank || ftsResults.length === 0) {
|
|
37
|
+
return ftsResults.map((r) => {
|
|
38
|
+
const { score, ...node } = r;
|
|
39
|
+
return { node: node, score };
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Stage 2: TF-IDF reranking
|
|
43
|
+
const candidates = ftsResults.map((r) => ({
|
|
44
|
+
id: r.id,
|
|
45
|
+
text: [r.title, r.description ?? "", r.tags?.join(" ") ?? ""].join(" "),
|
|
46
|
+
}));
|
|
47
|
+
const reranked = rerankWithTfIdf(candidates, query, limit);
|
|
48
|
+
return reranked
|
|
49
|
+
.map((r) => {
|
|
50
|
+
const node = resultMap.get(r.id);
|
|
51
|
+
if (!node)
|
|
52
|
+
return null;
|
|
53
|
+
return { node, score: r.score };
|
|
54
|
+
})
|
|
55
|
+
.filter((r) => r !== null);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=fts-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fts-search.js","sourceRoot":"","sources":["../../../src/core/search/fts-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight TF-IDF implementation for two-stage search reranking.
|
|
3
|
+
* No external dependencies — pure TypeScript.
|
|
4
|
+
*/
|
|
5
|
+
export declare class TfIdfIndex {
|
|
6
|
+
private docs;
|
|
7
|
+
private docFreq;
|
|
8
|
+
/**
|
|
9
|
+
* Add a document to the index.
|
|
10
|
+
*/
|
|
11
|
+
addDocument(id: string, text: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Compute TF-IDF score for a query against all documents.
|
|
14
|
+
* Returns sorted results (highest score first).
|
|
15
|
+
*/
|
|
16
|
+
search(query: string, limit?: number): Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
score: number;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build a TF-IDF index from search result nodes and rerank them.
|
|
23
|
+
* Two-stage: FTS5 candidates → TF-IDF reranking.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rerankWithTfIdf(candidates: Array<{
|
|
26
|
+
id: string;
|
|
27
|
+
text: string;
|
|
28
|
+
}>, query: string, limit?: number): Array<{
|
|
29
|
+
id: string;
|
|
30
|
+
score: number;
|
|
31
|
+
}>;
|
|
32
|
+
//# sourceMappingURL=tfidf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tfidf.d.ts","sourceRoot":"","sources":["../../../src/core/search/tfidf.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,OAAO,CAAkC;IAEjD;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAgB3C;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CA+BhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC/C,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,GACjB,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAMtC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight TF-IDF implementation for two-stage search reranking.
|
|
3
|
+
* No external dependencies — pure TypeScript.
|
|
4
|
+
*/
|
|
5
|
+
import { tokenize } from "./tokenizer.js";
|
|
6
|
+
export class TfIdfIndex {
|
|
7
|
+
docs = [];
|
|
8
|
+
docFreq = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Add a document to the index.
|
|
11
|
+
*/
|
|
12
|
+
addDocument(id, text) {
|
|
13
|
+
const tokens = tokenize(text);
|
|
14
|
+
const termFreq = new Map();
|
|
15
|
+
for (const token of tokens) {
|
|
16
|
+
termFreq.set(token, (termFreq.get(token) ?? 0) + 1);
|
|
17
|
+
}
|
|
18
|
+
// Update document frequency
|
|
19
|
+
for (const term of termFreq.keys()) {
|
|
20
|
+
this.docFreq.set(term, (this.docFreq.get(term) ?? 0) + 1);
|
|
21
|
+
}
|
|
22
|
+
this.docs.push({ id, tokens, termFreq });
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Compute TF-IDF score for a query against all documents.
|
|
26
|
+
* Returns sorted results (highest score first).
|
|
27
|
+
*/
|
|
28
|
+
search(query, limit = 20) {
|
|
29
|
+
const queryTokens = tokenize(query);
|
|
30
|
+
if (queryTokens.length === 0)
|
|
31
|
+
return [];
|
|
32
|
+
const n = this.docs.length;
|
|
33
|
+
if (n === 0)
|
|
34
|
+
return [];
|
|
35
|
+
const results = [];
|
|
36
|
+
for (const doc of this.docs) {
|
|
37
|
+
let score = 0;
|
|
38
|
+
const docLen = doc.tokens.length || 1;
|
|
39
|
+
for (const qt of queryTokens) {
|
|
40
|
+
const tf = (doc.termFreq.get(qt) ?? 0) / docLen;
|
|
41
|
+
const df = this.docFreq.get(qt) ?? 0;
|
|
42
|
+
if (df === 0)
|
|
43
|
+
continue;
|
|
44
|
+
// IDF with smoothing: log(1 + N/df)
|
|
45
|
+
const idf = Math.log(1 + n / df);
|
|
46
|
+
score += tf * idf;
|
|
47
|
+
}
|
|
48
|
+
if (score > 0) {
|
|
49
|
+
results.push({ id: doc.id, score });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
results.sort((a, b) => b.score - a.score);
|
|
53
|
+
return results.slice(0, limit);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build a TF-IDF index from search result nodes and rerank them.
|
|
58
|
+
* Two-stage: FTS5 candidates → TF-IDF reranking.
|
|
59
|
+
*/
|
|
60
|
+
export function rerankWithTfIdf(candidates, query, limit = 20) {
|
|
61
|
+
const index = new TfIdfIndex();
|
|
62
|
+
for (const c of candidates) {
|
|
63
|
+
index.addDocument(c.id, c.text);
|
|
64
|
+
}
|
|
65
|
+
return index.search(query, limit);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=tfidf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tfidf.js","sourceRoot":"","sources":["../../../src/core/search/tfidf.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight tokenizer for TF-IDF computation.
|
|
3
|
+
* Handles Portuguese and English text, strips accents and punctuation.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Normalize and tokenize text into lowercase terms, stripping accents.
|
|
7
|
+
*/
|
|
8
|
+
export declare function tokenize(text: string): string[];
|
|
9
|
+
//# sourceMappingURL=tokenizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../../src/core/search/tokenizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAc/C"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight tokenizer for TF-IDF computation.
|
|
3
|
+
* Handles Portuguese and English text, strips accents and punctuation.
|
|
4
|
+
*/
|
|
5
|
+
const STOPWORDS = new Set([
|
|
6
|
+
// Portuguese
|
|
7
|
+
"a", "o", "e", "é", "de", "do", "da", "dos", "das", "em", "no", "na",
|
|
8
|
+
"nos", "nas", "um", "uma", "uns", "umas", "por", "para", "com", "sem",
|
|
9
|
+
"que", "se", "ou", "ao", "aos", "como", "mais", "mas", "este", "esta",
|
|
10
|
+
"esse", "essa", "não", "nao", "ser", "ter", "foi", "são", "sao",
|
|
11
|
+
// English
|
|
12
|
+
"the", "a", "an", "is", "are", "was", "were", "be", "been", "being",
|
|
13
|
+
"have", "has", "had", "do", "does", "did", "will", "would", "could",
|
|
14
|
+
"should", "may", "might", "can", "shall", "of", "in", "to", "for",
|
|
15
|
+
"with", "on", "at", "by", "from", "as", "into", "through", "and",
|
|
16
|
+
"but", "or", "not", "no", "if", "it", "its", "this", "that", "they",
|
|
17
|
+
"we", "he", "she", "you", "i", "me", "my", "your", "his", "her",
|
|
18
|
+
"our", "their", "what", "which", "who", "when", "where", "how",
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* Normalize and tokenize text into lowercase terms, stripping accents.
|
|
22
|
+
*/
|
|
23
|
+
export function tokenize(text) {
|
|
24
|
+
const normalized = text
|
|
25
|
+
.normalize("NFD")
|
|
26
|
+
.replace(/[\u0300-\u036f]/g, "") // strip accents
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^a-z0-9_\s]/g, " ") // keep only alphanumeric + underscore
|
|
29
|
+
.replace(/\s+/g, " ")
|
|
30
|
+
.trim();
|
|
31
|
+
if (!normalized)
|
|
32
|
+
return [];
|
|
33
|
+
return normalized
|
|
34
|
+
.split(" ")
|
|
35
|
+
.filter((t) => t.length >= 2 && !STOPWORDS.has(t));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../../src/core/search/tokenizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,aAAa;IACb,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;IACpE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IACrE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;IACrE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;IAC/D,UAAU;IACV,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO;IACnE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IACnE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK;IACjE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK;IAChE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IACnE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IAC/D,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK;CAC/D,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,UAAU,GAAG,IAAI;SACpB,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,gBAAgB;SAChD,WAAW,EAAE;SACb,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAG,sCAAsC;SACtE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IAEV,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAE3B,OAAO,UAAU;SACd,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../src/core/store/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAuH3C,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CA2BzD;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAGvD"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const migrations = [
|
|
2
|
+
{
|
|
3
|
+
version: 1,
|
|
4
|
+
description: "Initial schema — projects, nodes, edges, snapshots, import_history",
|
|
5
|
+
sql: `
|
|
6
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
7
|
+
id TEXT PRIMARY KEY,
|
|
8
|
+
name TEXT NOT NULL,
|
|
9
|
+
created_at TEXT NOT NULL,
|
|
10
|
+
updated_at TEXT NOT NULL
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
CREATE TABLE IF NOT EXISTS nodes (
|
|
14
|
+
id TEXT PRIMARY KEY,
|
|
15
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
16
|
+
type TEXT NOT NULL,
|
|
17
|
+
title TEXT NOT NULL,
|
|
18
|
+
description TEXT,
|
|
19
|
+
status TEXT NOT NULL DEFAULT 'backlog',
|
|
20
|
+
priority INTEGER NOT NULL DEFAULT 3,
|
|
21
|
+
xp_size TEXT,
|
|
22
|
+
estimate_minutes INTEGER,
|
|
23
|
+
tags TEXT, -- JSON array
|
|
24
|
+
parent_id TEXT,
|
|
25
|
+
sprint TEXT,
|
|
26
|
+
source_file TEXT,
|
|
27
|
+
source_start_line INTEGER,
|
|
28
|
+
source_end_line INTEGER,
|
|
29
|
+
source_confidence REAL,
|
|
30
|
+
acceptance_criteria TEXT, -- JSON array
|
|
31
|
+
blocked INTEGER NOT NULL DEFAULT 0,
|
|
32
|
+
metadata TEXT, -- JSON object
|
|
33
|
+
created_at TEXT NOT NULL,
|
|
34
|
+
updated_at TEXT NOT NULL
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
38
|
+
id TEXT PRIMARY KEY,
|
|
39
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
40
|
+
from_node TEXT NOT NULL REFERENCES nodes(id),
|
|
41
|
+
to_node TEXT NOT NULL REFERENCES nodes(id),
|
|
42
|
+
relation_type TEXT NOT NULL,
|
|
43
|
+
weight REAL,
|
|
44
|
+
reason TEXT,
|
|
45
|
+
metadata TEXT, -- JSON object
|
|
46
|
+
created_at TEXT NOT NULL
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
CREATE TABLE IF NOT EXISTS snapshots (
|
|
50
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
51
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
52
|
+
data TEXT NOT NULL, -- JSON dump of GraphDocument
|
|
53
|
+
created_at TEXT NOT NULL
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
CREATE TABLE IF NOT EXISTS import_history (
|
|
57
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
58
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
59
|
+
source_file TEXT NOT NULL,
|
|
60
|
+
nodes_created INTEGER NOT NULL DEFAULT 0,
|
|
61
|
+
edges_created INTEGER NOT NULL DEFAULT 0,
|
|
62
|
+
imported_at TEXT NOT NULL
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
-- Indexes for common query patterns
|
|
66
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_project ON nodes(project_id);
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(project_id, type);
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_status ON nodes(project_id, status);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_parent ON nodes(parent_id);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_edges_project ON edges(project_id);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(from_node);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(to_node);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_project ON snapshots(project_id);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_imports_project ON import_history(project_id);
|
|
75
|
+
`,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
version: 2,
|
|
79
|
+
description: "FTS5 full-text search index on nodes",
|
|
80
|
+
sql: `
|
|
81
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
|
|
82
|
+
title, description, tags,
|
|
83
|
+
content='nodes', content_rowid='rowid'
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
-- Populate FTS from existing nodes
|
|
87
|
+
INSERT INTO nodes_fts(rowid, title, description, tags)
|
|
88
|
+
SELECT rowid, title, COALESCE(description, ''), COALESCE(tags, '')
|
|
89
|
+
FROM nodes;
|
|
90
|
+
|
|
91
|
+
-- Sync triggers: keep FTS in sync with nodes table
|
|
92
|
+
CREATE TRIGGER IF NOT EXISTS nodes_fts_insert AFTER INSERT ON nodes BEGIN
|
|
93
|
+
INSERT INTO nodes_fts(rowid, title, description, tags)
|
|
94
|
+
VALUES (NEW.rowid, NEW.title, COALESCE(NEW.description, ''), COALESCE(NEW.tags, ''));
|
|
95
|
+
END;
|
|
96
|
+
|
|
97
|
+
CREATE TRIGGER IF NOT EXISTS nodes_fts_delete AFTER DELETE ON nodes BEGIN
|
|
98
|
+
INSERT INTO nodes_fts(nodes_fts, rowid, title, description, tags)
|
|
99
|
+
VALUES ('delete', OLD.rowid, OLD.title, COALESCE(OLD.description, ''), COALESCE(OLD.tags, ''));
|
|
100
|
+
END;
|
|
101
|
+
|
|
102
|
+
CREATE TRIGGER IF NOT EXISTS nodes_fts_update AFTER UPDATE ON nodes BEGIN
|
|
103
|
+
INSERT INTO nodes_fts(nodes_fts, rowid, title, description, tags)
|
|
104
|
+
VALUES ('delete', OLD.rowid, OLD.title, COALESCE(OLD.description, ''), COALESCE(OLD.tags, ''));
|
|
105
|
+
INSERT INTO nodes_fts(rowid, title, description, tags)
|
|
106
|
+
VALUES (NEW.rowid, NEW.title, COALESCE(NEW.description, ''), COALESCE(NEW.tags, ''));
|
|
107
|
+
END;
|
|
108
|
+
`,
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
export function runMigrations(db) {
|
|
112
|
+
// Create migrations tracking table
|
|
113
|
+
db.exec(`
|
|
114
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
115
|
+
version INTEGER PRIMARY KEY,
|
|
116
|
+
description TEXT NOT NULL,
|
|
117
|
+
applied_at TEXT NOT NULL
|
|
118
|
+
);
|
|
119
|
+
`);
|
|
120
|
+
const applied = new Set(db
|
|
121
|
+
.prepare("SELECT version FROM _migrations")
|
|
122
|
+
.all()
|
|
123
|
+
.map((row) => row.version));
|
|
124
|
+
for (const migration of migrations) {
|
|
125
|
+
if (applied.has(migration.version))
|
|
126
|
+
continue;
|
|
127
|
+
db.transaction(() => {
|
|
128
|
+
db.exec(migration.sql);
|
|
129
|
+
db.prepare("INSERT INTO _migrations (version, description, applied_at) VALUES (?, ?, ?)").run(migration.version, migration.description, new Date().toISOString());
|
|
130
|
+
})();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function configureDb(db) {
|
|
134
|
+
db.pragma("journal_mode = WAL");
|
|
135
|
+
db.pragma("foreign_keys = ON");
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/core/store/migrations.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,GAAgB;IAC9B;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,oEAAoE;QACjF,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAsEJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,sCAAsC;QACnD,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4BJ;KACF;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,EAAqB;IACjD,mCAAmC;IACnC,EAAE,CAAC,IAAI,CAAC;;;;;;GAMP,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,EAAE;SACC,OAAO,CAAC,iCAAiC,CAAC;SAC1C,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAE,GAA2B,CAAC,OAAO,CAAC,CACtD,CAAC;IAEF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC;YAAE,SAAS;QAE7C,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;YAClB,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACvB,EAAE,CAAC,OAAO,CACR,6EAA6E,CAC9E,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5E,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAqB;IAC/C,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { GraphDocument, GraphNode, GraphEdge, GraphProject, NodeType, NodeStatus } from "../graph/graph-types.js";
|
|
2
|
+
export declare class SqliteStore {
|
|
3
|
+
private db;
|
|
4
|
+
private projectId;
|
|
5
|
+
private constructor();
|
|
6
|
+
/**
|
|
7
|
+
* Open (or create) a store at basePath/.mcp-graph/graph.db.
|
|
8
|
+
* Pass ":memory:" for in-memory testing.
|
|
9
|
+
*/
|
|
10
|
+
static open(basePath?: string): SqliteStore;
|
|
11
|
+
close(): void;
|
|
12
|
+
initProject(name?: string): GraphProject;
|
|
13
|
+
getProject(): GraphProject | null;
|
|
14
|
+
private ensureProject;
|
|
15
|
+
insertNode(node: GraphNode): void;
|
|
16
|
+
getNodeById(id: string): GraphNode | null;
|
|
17
|
+
getAllNodes(): GraphNode[];
|
|
18
|
+
getNodesByType(type: NodeType): GraphNode[];
|
|
19
|
+
getNodesByStatus(status: NodeStatus): GraphNode[];
|
|
20
|
+
getChildNodes(parentId: string): GraphNode[];
|
|
21
|
+
updateNodeStatus(id: string, status: NodeStatus): GraphNode | null;
|
|
22
|
+
updateNode(id: string, fields: Partial<Pick<GraphNode, "title" | "description" | "type" | "priority" | "xpSize" | "estimateMinutes" | "tags" | "parentId" | "sprint" | "blocked" | "acceptanceCriteria" | "metadata">>): GraphNode | null;
|
|
23
|
+
deleteNode(id: string): boolean;
|
|
24
|
+
deleteEdge(id: string): boolean;
|
|
25
|
+
insertEdge(edge: GraphEdge): void;
|
|
26
|
+
getEdgesFrom(nodeId: string): GraphEdge[];
|
|
27
|
+
getEdgesTo(nodeId: string): GraphEdge[];
|
|
28
|
+
getAllEdges(): GraphEdge[];
|
|
29
|
+
/**
|
|
30
|
+
* Check if a source file has been previously imported.
|
|
31
|
+
*/
|
|
32
|
+
hasImport(sourceFile: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Delete all nodes (and their edges) that were imported from a specific source file.
|
|
35
|
+
* Also removes the import history entry so re-import is clean.
|
|
36
|
+
*/
|
|
37
|
+
clearImportedNodes(sourceFile: string): {
|
|
38
|
+
nodesDeleted: number;
|
|
39
|
+
edgesDeleted: number;
|
|
40
|
+
};
|
|
41
|
+
bulkInsert(nodes: GraphNode[], edges: GraphEdge[]): void;
|
|
42
|
+
createSnapshot(): number;
|
|
43
|
+
recordImport(sourceFile: string, nodesCreated: number, edgesCreated: number): void;
|
|
44
|
+
getStats(): {
|
|
45
|
+
totalNodes: number;
|
|
46
|
+
totalEdges: number;
|
|
47
|
+
byType: Record<string, number>;
|
|
48
|
+
byStatus: Record<string, number>;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Search nodes using FTS5 with BM25 ranking.
|
|
52
|
+
* Returns nodes ordered by relevance score.
|
|
53
|
+
*/
|
|
54
|
+
searchNodes(query: string, limit?: number): Array<GraphNode & {
|
|
55
|
+
score: number;
|
|
56
|
+
}>;
|
|
57
|
+
bulkUpdateStatus(ids: string[], status: NodeStatus): {
|
|
58
|
+
updated: string[];
|
|
59
|
+
notFound: string[];
|
|
60
|
+
};
|
|
61
|
+
restoreSnapshot(snapshotId: number): void;
|
|
62
|
+
listSnapshots(): Array<{
|
|
63
|
+
snapshotId: number;
|
|
64
|
+
createdAt: string;
|
|
65
|
+
}>;
|
|
66
|
+
toGraphDocument(): GraphDocument;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=sqlite-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../../src/core/store/sqlite-store.ts"],"names":[],"mappings":"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"}
|