@amsterdamdatalabs/enact-docs-search 0.1.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/NOTICE +50 -0
- package/README.md +35 -0
- package/dist/adapter/repo-walker.d.ts +22 -0
- package/dist/adapter/repo-walker.d.ts.map +1 -0
- package/dist/adapter/repo-walker.js +96 -0
- package/dist/adapter/repo-walker.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +310 -0
- package/dist/cli.js.map +1 -0
- package/dist/embeddings/embedding-service.d.ts +12 -0
- package/dist/embeddings/embedding-service.d.ts.map +1 -0
- package/dist/embeddings/embedding-service.js +67 -0
- package/dist/embeddings/embedding-service.js.map +1 -0
- package/dist/indexing/chunker.d.ts +52 -0
- package/dist/indexing/chunker.d.ts.map +1 -0
- package/dist/indexing/chunker.js +196 -0
- package/dist/indexing/chunker.js.map +1 -0
- package/dist/indexing/indexing-service.d.ts +29 -0
- package/dist/indexing/indexing-service.d.ts.map +1 -0
- package/dist/indexing/indexing-service.js +151 -0
- package/dist/indexing/indexing-service.js.map +1 -0
- package/dist/search/search-service.d.ts +53 -0
- package/dist/search/search-service.d.ts.map +1 -0
- package/dist/search/search-service.js +139 -0
- package/dist/search/search-service.js.map +1 -0
- package/dist/search/snippet.d.ts +65 -0
- package/dist/search/snippet.d.ts.map +1 -0
- package/dist/search/snippet.js +352 -0
- package/dist/search/snippet.js.map +1 -0
- package/dist/storage/document-store.d.ts +72 -0
- package/dist/storage/document-store.d.ts.map +1 -0
- package/dist/storage/document-store.js +134 -0
- package/dist/storage/document-store.js.map +1 -0
- package/dist/storage/persistence.d.ts +55 -0
- package/dist/storage/persistence.d.ts.map +1 -0
- package/dist/storage/persistence.js +132 -0
- package/dist/storage/persistence.js.map +1 -0
- package/dist/storage/vector-store.d.ts +63 -0
- package/dist/storage/vector-store.d.ts.map +1 -0
- package/dist/storage/vector-store.js +172 -0
- package/dist/storage/vector-store.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from obsidian-qmd (MIT): https://github.com/thirteen37/obsidian-qmd
|
|
3
|
+
* Source: src/storage/DocumentStore.ts — the original had no Obsidian
|
|
4
|
+
* dependency at all (pure MiniSearch wrapper). Adjustments: added
|
|
5
|
+
* `startLine` to the indexed schema for path:line output, added a `scope`
|
|
6
|
+
* pre-filter to `search()`, and dropped the Obsidian-flavoured
|
|
7
|
+
* `pluginBasePath` option that was already unused upstream.
|
|
8
|
+
*
|
|
9
|
+
* DocumentStore provides full-text search using MiniSearch.
|
|
10
|
+
* Implements proper BM25 ranking with inverted index.
|
|
11
|
+
*/
|
|
12
|
+
import MiniSearch from "minisearch";
|
|
13
|
+
import { scopeOfPath } from "../types.js";
|
|
14
|
+
import { extractQueryTerms, buildSnippet } from "../search/snippet.js";
|
|
15
|
+
const MINISEARCH_OPTIONS = {
|
|
16
|
+
fields: ["content"],
|
|
17
|
+
storeFields: ["filePath", "chunkIndex", "mtimeMs", "startLine", "headingBreadcrumb"],
|
|
18
|
+
searchOptions: {
|
|
19
|
+
boost: { content: 1 },
|
|
20
|
+
fuzzy: 0.2,
|
|
21
|
+
prefix: true,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export class DocumentStore {
|
|
25
|
+
documents = new Map();
|
|
26
|
+
searchIndex = null;
|
|
27
|
+
ready = false;
|
|
28
|
+
async initialize(existingData) {
|
|
29
|
+
this.documents = new Map();
|
|
30
|
+
const isValidData = existingData && existingData.index !== undefined && Array.isArray(existingData.documents);
|
|
31
|
+
if (isValidData) {
|
|
32
|
+
this.searchIndex = MiniSearch.loadJSON(JSON.stringify(existingData.index), {
|
|
33
|
+
fields: [...MINISEARCH_OPTIONS.fields],
|
|
34
|
+
storeFields: [...MINISEARCH_OPTIONS.storeFields],
|
|
35
|
+
});
|
|
36
|
+
for (const doc of existingData.documents) {
|
|
37
|
+
this.documents.set(doc.id, doc);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.searchIndex = new MiniSearch({
|
|
42
|
+
fields: [...MINISEARCH_OPTIONS.fields],
|
|
43
|
+
storeFields: [...MINISEARCH_OPTIONS.storeFields],
|
|
44
|
+
searchOptions: { ...MINISEARCH_OPTIONS.searchOptions },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
this.ready = true;
|
|
48
|
+
}
|
|
49
|
+
isReady() {
|
|
50
|
+
return this.ready && this.searchIndex !== null;
|
|
51
|
+
}
|
|
52
|
+
async indexDocuments(docs) {
|
|
53
|
+
if (!this.searchIndex)
|
|
54
|
+
throw new Error("Document store not initialized");
|
|
55
|
+
for (const doc of docs) {
|
|
56
|
+
if (this.documents.has(doc.id)) {
|
|
57
|
+
const existing = this.documents.get(doc.id);
|
|
58
|
+
this.searchIndex.remove(existing);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this.searchIndex.addAll(docs);
|
|
62
|
+
for (const doc of docs) {
|
|
63
|
+
this.documents.set(doc.id, doc);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async getDocument(id) {
|
|
67
|
+
return this.documents.get(id) ?? null;
|
|
68
|
+
}
|
|
69
|
+
async deleteByFilePath(filePath) {
|
|
70
|
+
if (!this.searchIndex)
|
|
71
|
+
throw new Error("Document store not initialized");
|
|
72
|
+
const toDelete = [];
|
|
73
|
+
for (const doc of this.documents.values()) {
|
|
74
|
+
if (doc.filePath === filePath) {
|
|
75
|
+
toDelete.push(doc);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const doc of toDelete) {
|
|
79
|
+
this.searchIndex.remove(doc);
|
|
80
|
+
this.documents.delete(doc.id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Search for documents using MiniSearch with BM25 scoring. */
|
|
84
|
+
async search(query, options = {}) {
|
|
85
|
+
if (!this.searchIndex)
|
|
86
|
+
throw new Error("Document store not initialized");
|
|
87
|
+
const trimmedQuery = query.trim();
|
|
88
|
+
if (!trimmedQuery)
|
|
89
|
+
return [];
|
|
90
|
+
const limit = options.limit ?? 20;
|
|
91
|
+
const scope = options.scope ?? "all";
|
|
92
|
+
const miniSearchResults = this.searchIndex.search(trimmedQuery, {
|
|
93
|
+
filter: (result) => scope === "all" || scopeOfPath(result.filePath) === scope,
|
|
94
|
+
});
|
|
95
|
+
const results = [];
|
|
96
|
+
for (const result of miniSearchResults.slice(0, limit)) {
|
|
97
|
+
const doc = this.documents.get(result.id);
|
|
98
|
+
if (!doc)
|
|
99
|
+
continue;
|
|
100
|
+
const terms = extractQueryTerms(trimmedQuery);
|
|
101
|
+
const snippet = await buildSnippet({ filePath: doc.filePath, content: doc.content, startLine: doc.startLine }, terms, options.highlight ?? "plain", options.repoRoot);
|
|
102
|
+
results.push({
|
|
103
|
+
id: doc.id,
|
|
104
|
+
filePath: doc.filePath,
|
|
105
|
+
chunkIndex: doc.chunkIndex,
|
|
106
|
+
startLine: doc.startLine,
|
|
107
|
+
score: result.score,
|
|
108
|
+
snippet,
|
|
109
|
+
headingBreadcrumb: doc.headingBreadcrumb,
|
|
110
|
+
content: doc.content,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
async getDocumentCount() {
|
|
116
|
+
return this.documents.size;
|
|
117
|
+
}
|
|
118
|
+
async getAllFilePaths() {
|
|
119
|
+
const filePaths = new Set();
|
|
120
|
+
for (const doc of this.documents.values()) {
|
|
121
|
+
filePaths.add(doc.filePath);
|
|
122
|
+
}
|
|
123
|
+
return Array.from(filePaths);
|
|
124
|
+
}
|
|
125
|
+
export() {
|
|
126
|
+
if (!this.searchIndex)
|
|
127
|
+
throw new Error("Document store not initialized");
|
|
128
|
+
return {
|
|
129
|
+
documents: Array.from(this.documents.values()),
|
|
130
|
+
index: this.searchIndex.toJSON(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=document-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-store.js","sourceRoot":"","sources":["../../src/storage/document-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ExportedDocumentStore } from "./document-store.js";
|
|
2
|
+
import type { SidecarData } from "./vector-store.js";
|
|
3
|
+
import type { Scope } from "../types.js";
|
|
4
|
+
export interface ManifestEntry {
|
|
5
|
+
/** mtime (epoch ms) at the time this file was last indexed */
|
|
6
|
+
mtimeMs: number;
|
|
7
|
+
}
|
|
8
|
+
export interface Manifest {
|
|
9
|
+
/** Schema/model version — bump to force a full rebuild on incompatible change */
|
|
10
|
+
version: number;
|
|
11
|
+
/** Embedding model id, so a model change forces re-embedding */
|
|
12
|
+
embeddingModel: string;
|
|
13
|
+
/** repo-relative file path -> last-indexed mtime */
|
|
14
|
+
files: Record<string, ManifestEntry>;
|
|
15
|
+
/** Informational audit trail: scopes passed to a successful `index` run.
|
|
16
|
+
* Not used for freshness logic — see checkFreshness()'s doc comment. */
|
|
17
|
+
indexedScopes: string[];
|
|
18
|
+
/** epoch ms of the last successful index run */
|
|
19
|
+
lastIndexedAt: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const MANIFEST_VERSION = 1;
|
|
22
|
+
export declare const EMBEDDING_MODEL_ID = "Xenova/all-MiniLM-L6-v2";
|
|
23
|
+
/** Deterministic, collision-resistant per-repo cache directory. */
|
|
24
|
+
export declare function repoCacheDir(repoPath: string): string;
|
|
25
|
+
export declare function paths(cacheDir: string): {
|
|
26
|
+
manifest: string;
|
|
27
|
+
documentStore: string;
|
|
28
|
+
vectorIndex: string;
|
|
29
|
+
vectorSidecar: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function ensureCacheDir(cacheDir: string): Promise<void>;
|
|
32
|
+
export declare function readManifest(cacheDir: string): Promise<Manifest | null>;
|
|
33
|
+
export declare function writeManifest(cacheDir: string, manifest: Manifest): Promise<void>;
|
|
34
|
+
export declare function emptyManifest(): Manifest;
|
|
35
|
+
export declare function readDocumentStoreExport(cacheDir: string): Promise<ExportedDocumentStore | null>;
|
|
36
|
+
export declare function writeDocumentStoreExport(cacheDir: string, data: ExportedDocumentStore): Promise<void>;
|
|
37
|
+
export declare function readVectorSidecar(cacheDir: string): Promise<SidecarData | null>;
|
|
38
|
+
export declare function writeVectorSidecar(cacheDir: string, data: SidecarData): Promise<void>;
|
|
39
|
+
export declare function vectorIndexExists(cacheDir: string): Promise<boolean>;
|
|
40
|
+
export interface FreshnessReport {
|
|
41
|
+
/** Repo-relative paths that are new, changed, or removed since the last index for this scope. */
|
|
42
|
+
staleCount: number;
|
|
43
|
+
/** True if this scope has never been indexed at all. */
|
|
44
|
+
neverIndexed: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Compare the current on-disk files (already filtered to a scope by the
|
|
48
|
+
* caller) against the manifest, without mutating anything. Used by `query`
|
|
49
|
+
* to refresh a stale or nonexistent index before searching (I-08).
|
|
50
|
+
*/
|
|
51
|
+
export declare function checkFreshness(manifest: Manifest | null, scope: Scope, currentFiles: {
|
|
52
|
+
path: string;
|
|
53
|
+
mtimeMs: number;
|
|
54
|
+
}[]): FreshnessReport;
|
|
55
|
+
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/storage/persistence.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACrC;6EACyE;IACzE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,kBAAkB,4BAA4B,CAAC;AAQ5D,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKrD;AAOD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM;;;;;EAOrC;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAS7E;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvF;AAED,wBAAgB,aAAa,IAAI,QAAQ,CAQxC;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAOvC;AAED,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAOrF;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3F;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAI1E;AAED,MAAM,WAAW,eAAe;IAC9B,iGAAiG;IACjG,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,GAChD,eAAe,CA0BjB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* persistence.ts — on-disk cache for one target repo's index.
|
|
3
|
+
*
|
|
4
|
+
* Written fresh: obsidian-qmd persists into the Obsidian vault via
|
|
5
|
+
* localforage/IndexedDB, which has no equivalent outside a browser. This
|
|
6
|
+
* lays the same DocumentStore/VectorStore export shapes out as plain files
|
|
7
|
+
* under a per-repo cache directory, plus a manifest that tracks each
|
|
8
|
+
* indexed file's mtime (for incremental re-indexing) and which scopes have
|
|
9
|
+
* ever been indexed (so a query can refresh an un-indexed scope instead of
|
|
10
|
+
* silently returning nothing).
|
|
11
|
+
*/
|
|
12
|
+
import { mkdir, readFile, writeFile, stat } from "node:fs/promises";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
import { scopesToRoots } from "../types.js";
|
|
17
|
+
export const MANIFEST_VERSION = 1;
|
|
18
|
+
export const EMBEDDING_MODEL_ID = "Xenova/all-MiniLM-L6-v2";
|
|
19
|
+
function cacheRoot() {
|
|
20
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
21
|
+
const base = xdg && xdg.trim().length > 0 ? xdg : join(homedir(), ".cache");
|
|
22
|
+
return join(base, "enact-docs-search");
|
|
23
|
+
}
|
|
24
|
+
/** Deterministic, collision-resistant per-repo cache directory. */
|
|
25
|
+
export function repoCacheDir(repoPath) {
|
|
26
|
+
const abs = resolve(repoPath);
|
|
27
|
+
const hash = createHash("sha256").update(abs).digest("hex").slice(0, 16);
|
|
28
|
+
const base = abs.split("/").filter(Boolean).pop() ?? "repo";
|
|
29
|
+
return join(cacheRoot(), `${base}-${hash}`);
|
|
30
|
+
}
|
|
31
|
+
const MANIFEST_FILE = "manifest.json";
|
|
32
|
+
const DOCUMENT_STORE_FILE = "document-store.json";
|
|
33
|
+
const VECTOR_INDEX_FILE = "vector-index.bin";
|
|
34
|
+
const VECTOR_SIDECAR_FILE = "vector-sidecar.json";
|
|
35
|
+
export function paths(cacheDir) {
|
|
36
|
+
return {
|
|
37
|
+
manifest: join(cacheDir, MANIFEST_FILE),
|
|
38
|
+
documentStore: join(cacheDir, DOCUMENT_STORE_FILE),
|
|
39
|
+
vectorIndex: join(cacheDir, VECTOR_INDEX_FILE),
|
|
40
|
+
vectorSidecar: join(cacheDir, VECTOR_SIDECAR_FILE),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export async function ensureCacheDir(cacheDir) {
|
|
44
|
+
await mkdir(cacheDir, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
export async function readManifest(cacheDir) {
|
|
47
|
+
try {
|
|
48
|
+
const raw = await readFile(paths(cacheDir).manifest, "utf-8");
|
|
49
|
+
const parsed = JSON.parse(raw);
|
|
50
|
+
if (parsed.version !== MANIFEST_VERSION)
|
|
51
|
+
return null;
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export async function writeManifest(cacheDir, manifest) {
|
|
59
|
+
await writeFile(paths(cacheDir).manifest, JSON.stringify(manifest, null, 2), "utf-8");
|
|
60
|
+
}
|
|
61
|
+
export function emptyManifest() {
|
|
62
|
+
return {
|
|
63
|
+
version: MANIFEST_VERSION,
|
|
64
|
+
embeddingModel: EMBEDDING_MODEL_ID,
|
|
65
|
+
files: {},
|
|
66
|
+
indexedScopes: [],
|
|
67
|
+
lastIndexedAt: 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export async function readDocumentStoreExport(cacheDir) {
|
|
71
|
+
try {
|
|
72
|
+
const raw = await readFile(paths(cacheDir).documentStore, "utf-8");
|
|
73
|
+
return JSON.parse(raw);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function writeDocumentStoreExport(cacheDir, data) {
|
|
80
|
+
await writeFile(paths(cacheDir).documentStore, JSON.stringify(data), "utf-8");
|
|
81
|
+
}
|
|
82
|
+
export async function readVectorSidecar(cacheDir) {
|
|
83
|
+
try {
|
|
84
|
+
const raw = await readFile(paths(cacheDir).vectorSidecar, "utf-8");
|
|
85
|
+
return JSON.parse(raw);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export async function writeVectorSidecar(cacheDir, data) {
|
|
92
|
+
await writeFile(paths(cacheDir).vectorSidecar, JSON.stringify(data), "utf-8");
|
|
93
|
+
}
|
|
94
|
+
export async function vectorIndexExists(cacheDir) {
|
|
95
|
+
return stat(paths(cacheDir).vectorIndex)
|
|
96
|
+
.then(() => true)
|
|
97
|
+
.catch(() => false);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Compare the current on-disk files (already filtered to a scope by the
|
|
101
|
+
* caller) against the manifest, without mutating anything. Used by `query`
|
|
102
|
+
* to refresh a stale or nonexistent index before searching (I-08).
|
|
103
|
+
*/
|
|
104
|
+
export function checkFreshness(manifest, scope, currentFiles) {
|
|
105
|
+
// "Never indexed" is judged by overlap with the manifest, not by a scope
|
|
106
|
+
// label match: indexing with --scope all and then querying --scope docs
|
|
107
|
+
// (a subset) is a legitimate, fully-covered case. If there are files on
|
|
108
|
+
// disk in this scope but none of them appear in the manifest at all, no
|
|
109
|
+
// index run has ever covered this scope.
|
|
110
|
+
if (!manifest)
|
|
111
|
+
return { staleCount: 0, neverIndexed: true };
|
|
112
|
+
if (currentFiles.length > 0 && currentFiles.every((f) => !(f.path in manifest.files))) {
|
|
113
|
+
return { staleCount: 0, neverIndexed: true };
|
|
114
|
+
}
|
|
115
|
+
let staleCount = 0;
|
|
116
|
+
const seen = new Set();
|
|
117
|
+
for (const f of currentFiles) {
|
|
118
|
+
seen.add(f.path);
|
|
119
|
+
const entry = manifest.files[f.path];
|
|
120
|
+
if (!entry || entry.mtimeMs !== f.mtimeMs)
|
|
121
|
+
staleCount++;
|
|
122
|
+
}
|
|
123
|
+
// Files present in the manifest for this scope but deleted on disk also count as stale.
|
|
124
|
+
const roots = scopesToRoots(scope);
|
|
125
|
+
for (const path of Object.keys(manifest.files)) {
|
|
126
|
+
const inScope = roots.some((r) => path === r || path.startsWith(r + "/"));
|
|
127
|
+
if (inScope && !seen.has(path))
|
|
128
|
+
staleCount++;
|
|
129
|
+
}
|
|
130
|
+
return { staleCount, neverIndexed: false };
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/storage/persistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Scope } from "../types.js";
|
|
2
|
+
export declare class DependencyError extends Error {
|
|
3
|
+
}
|
|
4
|
+
export interface VectorDocument {
|
|
5
|
+
id: string;
|
|
6
|
+
filePath: string;
|
|
7
|
+
chunkIndex: number;
|
|
8
|
+
embedding: number[];
|
|
9
|
+
}
|
|
10
|
+
export interface VectorSearchResult {
|
|
11
|
+
id: string;
|
|
12
|
+
filePath: string;
|
|
13
|
+
chunkIndex: number;
|
|
14
|
+
/** Cosine similarity score (0-1, higher is closer) */
|
|
15
|
+
score: number;
|
|
16
|
+
}
|
|
17
|
+
export interface VectorStoreOptions {
|
|
18
|
+
dimensions: number;
|
|
19
|
+
}
|
|
20
|
+
interface VectorMetadata {
|
|
21
|
+
id: string;
|
|
22
|
+
filePath: string;
|
|
23
|
+
chunkIndex: number;
|
|
24
|
+
}
|
|
25
|
+
export declare class VectorStore {
|
|
26
|
+
private index;
|
|
27
|
+
private dimensions;
|
|
28
|
+
private ready;
|
|
29
|
+
private idToLabel;
|
|
30
|
+
private labelToId;
|
|
31
|
+
private metadata;
|
|
32
|
+
private nextLabel;
|
|
33
|
+
private deletedLabels;
|
|
34
|
+
private maxElements;
|
|
35
|
+
private readonly M;
|
|
36
|
+
private readonly efConstruction;
|
|
37
|
+
private readonly efSearch;
|
|
38
|
+
constructor(options: VectorStoreOptions);
|
|
39
|
+
/** Initialize a fresh, empty index. */
|
|
40
|
+
initialize(): Promise<void>;
|
|
41
|
+
/** Load a previously persisted index plus its label/metadata sidecar. */
|
|
42
|
+
loadFromDisk(indexPath: string, sidecar: SidecarData): Promise<void>;
|
|
43
|
+
/** Persist the raw HNSW index plus the sidecar label/metadata mapping. */
|
|
44
|
+
saveToDisk(indexPath: string): Promise<SidecarData>;
|
|
45
|
+
isReady(): boolean;
|
|
46
|
+
private resizeIfNeeded;
|
|
47
|
+
add(doc: VectorDocument): Promise<void>;
|
|
48
|
+
delete(id: string): Promise<void>;
|
|
49
|
+
deleteByFilePath(filePath: string): Promise<void>;
|
|
50
|
+
/** Search for similar vectors using HNSW, optionally restricted to a scope. */
|
|
51
|
+
search(query: number[], k: number, scope?: Scope): Promise<VectorSearchResult[]>;
|
|
52
|
+
getCount(): Promise<number>;
|
|
53
|
+
has(id: string): Promise<boolean>;
|
|
54
|
+
}
|
|
55
|
+
export interface SidecarData {
|
|
56
|
+
idToLabel: [string, number][];
|
|
57
|
+
metadata: [string, VectorMetadata][];
|
|
58
|
+
nextLabel: number;
|
|
59
|
+
deletedLabels: number[];
|
|
60
|
+
dimensions: number;
|
|
61
|
+
}
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=vector-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-store.d.ts","sourceRoot":"","sources":["../../src/storage/vector-store.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAyB7C,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAS;IAEtB,OAAO,CAAC,SAAS,CAAkC;IACnD,OAAO,CAAC,SAAS,CAAkC;IACnD,OAAO,CAAC,QAAQ,CAA0C;IAE1D,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAgB;IAErC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAM;IACxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAO;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAM;gBAEnB,OAAO,EAAE,kBAAkB;IAIvC,uCAAuC;IACjC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC,yEAAyE;IACnE,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1E,0EAA0E;IACpE,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAYzD,OAAO,IAAI,OAAO;IAIlB,OAAO,CAAC,cAAc;IAShB,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBvC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcjC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvD,+EAA+E;IACzE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,KAAa,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IA2CvF,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAI3B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGxC;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { scopeOfPath } from "../types.js";
|
|
2
|
+
export class DependencyError extends Error {
|
|
3
|
+
}
|
|
4
|
+
async function loadHierarchicalNSW() {
|
|
5
|
+
try {
|
|
6
|
+
const mod = (await import("hnswlib-node"));
|
|
7
|
+
const HierarchicalNSW = mod.HierarchicalNSW ?? mod.default?.HierarchicalNSW;
|
|
8
|
+
if (!HierarchicalNSW)
|
|
9
|
+
throw new Error("HierarchicalNSW export is missing");
|
|
10
|
+
return HierarchicalNSW;
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw new DependencyError("Semantic search requires hnswlib-node, which is not installed (or its " +
|
|
14
|
+
"native binding failed to build for this platform).\n" +
|
|
15
|
+
"Run `npm install` in the enact-docs-search directory, then retry.\n" +
|
|
16
|
+
`Original error: ${error.message}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class VectorStore {
|
|
20
|
+
index = null;
|
|
21
|
+
dimensions;
|
|
22
|
+
ready = false;
|
|
23
|
+
idToLabel = new Map();
|
|
24
|
+
labelToId = new Map();
|
|
25
|
+
metadata = new Map();
|
|
26
|
+
nextLabel = 0;
|
|
27
|
+
deletedLabels = [];
|
|
28
|
+
maxElements = 10000;
|
|
29
|
+
M = 16;
|
|
30
|
+
efConstruction = 200;
|
|
31
|
+
efSearch = 50;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this.dimensions = options.dimensions;
|
|
34
|
+
}
|
|
35
|
+
/** Initialize a fresh, empty index. */
|
|
36
|
+
async initialize() {
|
|
37
|
+
const HierarchicalNSW = await loadHierarchicalNSW();
|
|
38
|
+
this.index = new HierarchicalNSW("cosine", this.dimensions);
|
|
39
|
+
this.index.initIndex(this.maxElements, this.M, this.efConstruction, 100);
|
|
40
|
+
this.index.setEf(this.efSearch);
|
|
41
|
+
this.idToLabel.clear();
|
|
42
|
+
this.labelToId.clear();
|
|
43
|
+
this.metadata.clear();
|
|
44
|
+
this.nextLabel = 0;
|
|
45
|
+
this.deletedLabels = [];
|
|
46
|
+
this.ready = true;
|
|
47
|
+
}
|
|
48
|
+
/** Load a previously persisted index plus its label/metadata sidecar. */
|
|
49
|
+
async loadFromDisk(indexPath, sidecar) {
|
|
50
|
+
const HierarchicalNSW = await loadHierarchicalNSW();
|
|
51
|
+
this.index = new HierarchicalNSW("cosine", this.dimensions);
|
|
52
|
+
await this.index.readIndex(indexPath, true);
|
|
53
|
+
this.index.setEf(this.efSearch);
|
|
54
|
+
this.maxElements = this.index.getMaxElements();
|
|
55
|
+
this.idToLabel = new Map(sidecar.idToLabel);
|
|
56
|
+
this.labelToId = new Map(sidecar.idToLabel.map(([id, label]) => [label, id]));
|
|
57
|
+
this.metadata = new Map(sidecar.metadata);
|
|
58
|
+
this.nextLabel = sidecar.nextLabel;
|
|
59
|
+
this.deletedLabels = [...sidecar.deletedLabels];
|
|
60
|
+
this.ready = true;
|
|
61
|
+
}
|
|
62
|
+
/** Persist the raw HNSW index plus the sidecar label/metadata mapping. */
|
|
63
|
+
async saveToDisk(indexPath) {
|
|
64
|
+
if (!this.index)
|
|
65
|
+
throw new Error("Vector store not initialized");
|
|
66
|
+
await this.index.writeIndex(indexPath);
|
|
67
|
+
return {
|
|
68
|
+
idToLabel: Array.from(this.idToLabel.entries()),
|
|
69
|
+
metadata: Array.from(this.metadata.entries()),
|
|
70
|
+
nextLabel: this.nextLabel,
|
|
71
|
+
deletedLabels: [...this.deletedLabels],
|
|
72
|
+
dimensions: this.dimensions,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
isReady() {
|
|
76
|
+
return this.ready;
|
|
77
|
+
}
|
|
78
|
+
resizeIfNeeded() {
|
|
79
|
+
if (!this.index)
|
|
80
|
+
return;
|
|
81
|
+
const currentCount = this.idToLabel.size;
|
|
82
|
+
if (currentCount >= this.maxElements - 1) {
|
|
83
|
+
this.maxElements *= 2;
|
|
84
|
+
this.index.resizeIndex(this.maxElements);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async add(doc) {
|
|
88
|
+
if (!this.ready || !this.index)
|
|
89
|
+
throw new Error("Vector store not initialized");
|
|
90
|
+
if (this.idToLabel.has(doc.id)) {
|
|
91
|
+
await this.delete(doc.id);
|
|
92
|
+
}
|
|
93
|
+
this.resizeIfNeeded();
|
|
94
|
+
// Deliberately not reusing labels from `deletedLabels`: hnswlib-node's
|
|
95
|
+
// addPoint(..., replaceDeleted=true) label-reuse path is unreliable in
|
|
96
|
+
// this binding (it can throw "Can't use addPoint to update deleted
|
|
97
|
+
// elements..." even on a freshly-marked-deleted label). Always taking a
|
|
98
|
+
// fresh label and leaving markDelete's tombstone in place is simpler and
|
|
99
|
+
// correct; at this corpus size (~1k chunks) the wasted slots are noise.
|
|
100
|
+
const label = this.nextLabel++;
|
|
101
|
+
this.index.addPoint(doc.embedding, label, false);
|
|
102
|
+
this.idToLabel.set(doc.id, label);
|
|
103
|
+
this.labelToId.set(label, doc.id);
|
|
104
|
+
this.metadata.set(doc.id, { id: doc.id, filePath: doc.filePath, chunkIndex: doc.chunkIndex });
|
|
105
|
+
}
|
|
106
|
+
async delete(id) {
|
|
107
|
+
if (!this.ready || !this.index)
|
|
108
|
+
throw new Error("Vector store not initialized");
|
|
109
|
+
const label = this.idToLabel.get(id);
|
|
110
|
+
if (label === undefined)
|
|
111
|
+
return;
|
|
112
|
+
this.index.markDelete(label);
|
|
113
|
+
this.deletedLabels.push(label);
|
|
114
|
+
this.idToLabel.delete(id);
|
|
115
|
+
this.labelToId.delete(label);
|
|
116
|
+
this.metadata.delete(id);
|
|
117
|
+
}
|
|
118
|
+
async deleteByFilePath(filePath) {
|
|
119
|
+
if (!this.ready)
|
|
120
|
+
throw new Error("Vector store not initialized");
|
|
121
|
+
const toDelete = [];
|
|
122
|
+
for (const [id, meta] of this.metadata) {
|
|
123
|
+
if (meta.filePath === filePath)
|
|
124
|
+
toDelete.push(id);
|
|
125
|
+
}
|
|
126
|
+
for (const id of toDelete)
|
|
127
|
+
await this.delete(id);
|
|
128
|
+
}
|
|
129
|
+
/** Search for similar vectors using HNSW, optionally restricted to a scope. */
|
|
130
|
+
async search(query, k, scope = "all") {
|
|
131
|
+
if (!this.ready || !this.index)
|
|
132
|
+
throw new Error("Vector store not initialized");
|
|
133
|
+
const actualCount = this.idToLabel.size;
|
|
134
|
+
if (actualCount === 0)
|
|
135
|
+
return [];
|
|
136
|
+
const actualK = Math.min(k, actualCount);
|
|
137
|
+
const filter = scope === "all"
|
|
138
|
+
? undefined
|
|
139
|
+
: (label) => {
|
|
140
|
+
const id = this.labelToId.get(label);
|
|
141
|
+
const meta = id ? this.metadata.get(id) : undefined;
|
|
142
|
+
return !!meta && scopeOfPath(meta.filePath) === scope;
|
|
143
|
+
};
|
|
144
|
+
const result = this.index.searchKnn(query, actualK, filter);
|
|
145
|
+
const results = [];
|
|
146
|
+
for (let i = 0; i < result.neighbors.length; i++) {
|
|
147
|
+
const label = result.neighbors[i];
|
|
148
|
+
const distance = result.distances[i];
|
|
149
|
+
const id = this.labelToId.get(label);
|
|
150
|
+
if (!id)
|
|
151
|
+
continue;
|
|
152
|
+
const meta = this.metadata.get(id);
|
|
153
|
+
if (!meta)
|
|
154
|
+
continue;
|
|
155
|
+
const similarity = 1 - distance;
|
|
156
|
+
results.push({
|
|
157
|
+
id,
|
|
158
|
+
filePath: meta.filePath,
|
|
159
|
+
chunkIndex: meta.chunkIndex,
|
|
160
|
+
score: Math.max(0, Math.min(1, similarity)),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return results;
|
|
164
|
+
}
|
|
165
|
+
async getCount() {
|
|
166
|
+
return this.idToLabel.size;
|
|
167
|
+
}
|
|
168
|
+
async has(id) {
|
|
169
|
+
return this.idToLabel.has(id);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=vector-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-store.js","sourceRoot":"","sources":["../../src/storage/vector-store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for enact-docs-search.
|
|
3
|
+
*/
|
|
4
|
+
/** The three known knowledge trees, plus "all" meaning all of them together. */
|
|
5
|
+
export type Scope = "docs" | "plans" | "specs" | "all";
|
|
6
|
+
/** Root directory name (relative to the target repo) for each concrete scope. */
|
|
7
|
+
export declare const SCOPE_ROOTS: Record<Exclude<Scope, "all">, string>;
|
|
8
|
+
export declare function scopesToRoots(scope: Scope): string[];
|
|
9
|
+
/** Which concrete scope a repo-relative path belongs to, if any. */
|
|
10
|
+
export declare function scopeOfPath(relPath: string): Exclude<Scope, "all"> | null;
|
|
11
|
+
export type SearchMode = "keyword" | "semantic" | "hybrid";
|
|
12
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,gFAAgF;AAChF,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;AAEvD,iFAAiF;AACjF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,CAI7D,CAAC;AAEF,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAGpD;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,IAAI,CAOzE;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for enact-docs-search.
|
|
3
|
+
*/
|
|
4
|
+
/** Root directory name (relative to the target repo) for each concrete scope. */
|
|
5
|
+
export const SCOPE_ROOTS = {
|
|
6
|
+
docs: "docs",
|
|
7
|
+
plans: ".plans",
|
|
8
|
+
specs: ".specs",
|
|
9
|
+
};
|
|
10
|
+
export function scopesToRoots(scope) {
|
|
11
|
+
if (scope === "all")
|
|
12
|
+
return Object.values(SCOPE_ROOTS);
|
|
13
|
+
return [SCOPE_ROOTS[scope]];
|
|
14
|
+
}
|
|
15
|
+
/** Which concrete scope a repo-relative path belongs to, if any. */
|
|
16
|
+
export function scopeOfPath(relPath) {
|
|
17
|
+
for (const [scope, root] of Object.entries(SCOPE_ROOTS)) {
|
|
18
|
+
if (relPath === root || relPath.startsWith(root + "/")) {
|
|
19
|
+
return scope;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAA0C;IAChE,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,QAAQ;CAChB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,KAAY;IACxC,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACvD,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACxD,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACvD,OAAO,KAA8B,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|