@zvec/zvec-grep 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.
Files changed (201) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +222 -0
  3. package/README_CN.md +222 -0
  4. package/dist/cli/args.d.ts +2 -0
  5. package/dist/cli/args.js +611 -0
  6. package/dist/cli/args.js.map +1 -0
  7. package/dist/cli/commands.d.ts +2 -0
  8. package/dist/cli/commands.js +270 -0
  9. package/dist/cli/commands.js.map +1 -0
  10. package/dist/cli/errors.d.ts +6 -0
  11. package/dist/cli/errors.js +97 -0
  12. package/dist/cli/errors.js.map +1 -0
  13. package/dist/cli/format/context.d.ts +5 -0
  14. package/dist/cli/format/context.js +490 -0
  15. package/dist/cli/format/context.js.map +1 -0
  16. package/dist/cli/format/debug.d.ts +4 -0
  17. package/dist/cli/format/debug.js +42 -0
  18. package/dist/cli/format/debug.js.map +1 -0
  19. package/dist/cli/format/highlight.d.ts +3 -0
  20. package/dist/cli/format/highlight.js +106 -0
  21. package/dist/cli/format/highlight.js.map +1 -0
  22. package/dist/cli/format/progress.d.ts +9 -0
  23. package/dist/cli/format/progress.js +92 -0
  24. package/dist/cli/format/progress.js.map +1 -0
  25. package/dist/cli/format/range.d.ts +3 -0
  26. package/dist/cli/format/range.js +24 -0
  27. package/dist/cli/format/range.js.map +1 -0
  28. package/dist/cli/format/status.d.ts +7 -0
  29. package/dist/cli/format/status.js +200 -0
  30. package/dist/cli/format/status.js.map +1 -0
  31. package/dist/cli/help.d.ts +1 -0
  32. package/dist/cli/help.js +88 -0
  33. package/dist/cli/help.js.map +1 -0
  34. package/dist/cli/index.d.ts +2 -0
  35. package/dist/cli/index.js +32 -0
  36. package/dist/cli/index.js.map +1 -0
  37. package/dist/cli/types.d.ts +56 -0
  38. package/dist/cli/types.js +10 -0
  39. package/dist/cli/types.js.map +1 -0
  40. package/dist/cli/version.d.ts +1 -0
  41. package/dist/cli/version.js +11 -0
  42. package/dist/cli/version.js.map +1 -0
  43. package/dist/engine/collection/index.d.ts +50 -0
  44. package/dist/engine/collection/index.js +388 -0
  45. package/dist/engine/collection/index.js.map +1 -0
  46. package/dist/engine/errors/details.d.ts +6 -0
  47. package/dist/engine/errors/details.js +27 -0
  48. package/dist/engine/errors/details.js.map +1 -0
  49. package/dist/engine/errors/index.d.ts +14 -0
  50. package/dist/engine/errors/index.js +16 -0
  51. package/dist/engine/errors/index.js.map +1 -0
  52. package/dist/engine/models/catalog.d.ts +47 -0
  53. package/dist/engine/models/catalog.js +68 -0
  54. package/dist/engine/models/catalog.js.map +1 -0
  55. package/dist/engine/models/embeddings.d.ts +25 -0
  56. package/dist/engine/models/embeddings.js +100 -0
  57. package/dist/engine/models/embeddings.js.map +1 -0
  58. package/dist/engine/models/factory.d.ts +5 -0
  59. package/dist/engine/models/factory.js +66 -0
  60. package/dist/engine/models/factory.js.map +1 -0
  61. package/dist/engine/models/index.d.ts +6 -0
  62. package/dist/engine/models/index.js +5 -0
  63. package/dist/engine/models/index.js.map +1 -0
  64. package/dist/engine/models/providers/index.d.ts +2 -0
  65. package/dist/engine/models/providers/index.js +3 -0
  66. package/dist/engine/models/providers/index.js.map +1 -0
  67. package/dist/engine/models/providers/llama-cpp/embedding.d.ts +94 -0
  68. package/dist/engine/models/providers/llama-cpp/embedding.js +459 -0
  69. package/dist/engine/models/providers/llama-cpp/embedding.js.map +1 -0
  70. package/dist/engine/models/providers/llama-cpp/index.d.ts +1 -0
  71. package/dist/engine/models/providers/llama-cpp/index.js +2 -0
  72. package/dist/engine/models/providers/llama-cpp/index.js.map +1 -0
  73. package/dist/engine/models/providers/qwen/embedding.d.ts +38 -0
  74. package/dist/engine/models/providers/qwen/embedding.js +322 -0
  75. package/dist/engine/models/providers/qwen/embedding.js.map +1 -0
  76. package/dist/engine/models/providers/qwen/index.d.ts +1 -0
  77. package/dist/engine/models/providers/qwen/index.js +2 -0
  78. package/dist/engine/models/providers/qwen/index.js.map +1 -0
  79. package/dist/engine/models/ranking.d.ts +15 -0
  80. package/dist/engine/models/ranking.js +2 -0
  81. package/dist/engine/models/ranking.js.map +1 -0
  82. package/dist/engine/models/types.d.ts +30 -0
  83. package/dist/engine/models/types.js +2 -0
  84. package/dist/engine/models/types.js.map +1 -0
  85. package/dist/engine/pipeline/indexing/extractor/code/adapter.d.ts +19 -0
  86. package/dist/engine/pipeline/indexing/extractor/code/adapter.js +24 -0
  87. package/dist/engine/pipeline/indexing/extractor/code/adapter.js.map +1 -0
  88. package/dist/engine/pipeline/indexing/extractor/code/extractor.d.ts +14 -0
  89. package/dist/engine/pipeline/indexing/extractor/code/extractor.js +594 -0
  90. package/dist/engine/pipeline/indexing/extractor/code/extractor.js.map +1 -0
  91. package/dist/engine/pipeline/indexing/extractor/code/families/c-family.d.ts +2 -0
  92. package/dist/engine/pipeline/indexing/extractor/code/families/c-family.js +116 -0
  93. package/dist/engine/pipeline/indexing/extractor/code/families/c-family.js.map +1 -0
  94. package/dist/engine/pipeline/indexing/extractor/code/families/js-ts.d.ts +12 -0
  95. package/dist/engine/pipeline/indexing/extractor/code/families/js-ts.js +112 -0
  96. package/dist/engine/pipeline/indexing/extractor/code/families/js-ts.js.map +1 -0
  97. package/dist/engine/pipeline/indexing/extractor/code/families/metadata.d.ts +8 -0
  98. package/dist/engine/pipeline/indexing/extractor/code/families/metadata.js +83 -0
  99. package/dist/engine/pipeline/indexing/extractor/code/families/metadata.js.map +1 -0
  100. package/dist/engine/pipeline/indexing/extractor/code/families/name-field.d.ts +3 -0
  101. package/dist/engine/pipeline/indexing/extractor/code/families/name-field.js +22 -0
  102. package/dist/engine/pipeline/indexing/extractor/code/families/name-field.js.map +1 -0
  103. package/dist/engine/pipeline/indexing/extractor/code/languages/c.d.ts +1 -0
  104. package/dist/engine/pipeline/indexing/extractor/code/languages/c.js +15 -0
  105. package/dist/engine/pipeline/indexing/extractor/code/languages/c.js.map +1 -0
  106. package/dist/engine/pipeline/indexing/extractor/code/languages/cpp.d.ts +1 -0
  107. package/dist/engine/pipeline/indexing/extractor/code/languages/cpp.js +18 -0
  108. package/dist/engine/pipeline/indexing/extractor/code/languages/cpp.js.map +1 -0
  109. package/dist/engine/pipeline/indexing/extractor/code/languages/go.d.ts +2 -0
  110. package/dist/engine/pipeline/indexing/extractor/code/languages/go.js +72 -0
  111. package/dist/engine/pipeline/indexing/extractor/code/languages/go.js.map +1 -0
  112. package/dist/engine/pipeline/indexing/extractor/code/languages/java.d.ts +1 -0
  113. package/dist/engine/pipeline/indexing/extractor/code/languages/java.js +17 -0
  114. package/dist/engine/pipeline/indexing/extractor/code/languages/java.js.map +1 -0
  115. package/dist/engine/pipeline/indexing/extractor/code/languages/javascript.d.ts +2 -0
  116. package/dist/engine/pipeline/indexing/extractor/code/languages/javascript.js +25 -0
  117. package/dist/engine/pipeline/indexing/extractor/code/languages/javascript.js.map +1 -0
  118. package/dist/engine/pipeline/indexing/extractor/code/languages/python.d.ts +2 -0
  119. package/dist/engine/pipeline/indexing/extractor/code/languages/python.js +53 -0
  120. package/dist/engine/pipeline/indexing/extractor/code/languages/python.js.map +1 -0
  121. package/dist/engine/pipeline/indexing/extractor/code/languages/rust.d.ts +2 -0
  122. package/dist/engine/pipeline/indexing/extractor/code/languages/rust.js +29 -0
  123. package/dist/engine/pipeline/indexing/extractor/code/languages/rust.js.map +1 -0
  124. package/dist/engine/pipeline/indexing/extractor/code/languages/typescript.d.ts +2 -0
  125. package/dist/engine/pipeline/indexing/extractor/code/languages/typescript.js +37 -0
  126. package/dist/engine/pipeline/indexing/extractor/code/languages/typescript.js.map +1 -0
  127. package/dist/engine/pipeline/indexing/extractor/index.d.ts +44 -0
  128. package/dist/engine/pipeline/indexing/extractor/index.js +281 -0
  129. package/dist/engine/pipeline/indexing/extractor/index.js.map +1 -0
  130. package/dist/engine/pipeline/indexing/extractor/markdown/extractor.d.ts +13 -0
  131. package/dist/engine/pipeline/indexing/extractor/markdown/extractor.js +339 -0
  132. package/dist/engine/pipeline/indexing/extractor/markdown/extractor.js.map +1 -0
  133. package/dist/engine/pipeline/indexing/extractor/tree-sitter/grammar.d.ts +4 -0
  134. package/dist/engine/pipeline/indexing/extractor/tree-sitter/grammar.js +49 -0
  135. package/dist/engine/pipeline/indexing/extractor/tree-sitter/grammar.js.map +1 -0
  136. package/dist/engine/pipeline/indexing/extractor/tree-sitter/nodes.d.ts +3 -0
  137. package/dist/engine/pipeline/indexing/extractor/tree-sitter/nodes.js +26 -0
  138. package/dist/engine/pipeline/indexing/extractor/tree-sitter/nodes.js.map +1 -0
  139. package/dist/engine/pipeline/indexing/extractor/tree-sitter/parser.d.ts +2 -0
  140. package/dist/engine/pipeline/indexing/extractor/tree-sitter/parser.js +31 -0
  141. package/dist/engine/pipeline/indexing/extractor/tree-sitter/parser.js.map +1 -0
  142. package/dist/engine/pipeline/indexing/ids.d.ts +1 -0
  143. package/dist/engine/pipeline/indexing/ids.js +5 -0
  144. package/dist/engine/pipeline/indexing/ids.js.map +1 -0
  145. package/dist/engine/pipeline/indexing/index.d.ts +12 -0
  146. package/dist/engine/pipeline/indexing/index.js +538 -0
  147. package/dist/engine/pipeline/indexing/index.js.map +1 -0
  148. package/dist/engine/pipeline/indexing/root-paths.d.ts +7 -0
  149. package/dist/engine/pipeline/indexing/root-paths.js +151 -0
  150. package/dist/engine/pipeline/indexing/root-paths.js.map +1 -0
  151. package/dist/engine/pipeline/indexing/scanner/index.d.ts +5 -0
  152. package/dist/engine/pipeline/indexing/scanner/index.js +504 -0
  153. package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -0
  154. package/dist/engine/pipeline/search/index.d.ts +12 -0
  155. package/dist/engine/pipeline/search/index.js +761 -0
  156. package/dist/engine/pipeline/search/index.js.map +1 -0
  157. package/dist/engine/service/index.d.ts +2 -0
  158. package/dist/engine/service/index.js +2 -0
  159. package/dist/engine/service/index.js.map +1 -0
  160. package/dist/engine/service/lexical.d.ts +29 -0
  161. package/dist/engine/service/lexical.js +482 -0
  162. package/dist/engine/service/lexical.js.map +1 -0
  163. package/dist/engine/service/root.d.ts +15 -0
  164. package/dist/engine/service/root.js +44 -0
  165. package/dist/engine/service/root.js.map +1 -0
  166. package/dist/engine/service/types.d.ts +157 -0
  167. package/dist/engine/service/types.js +2 -0
  168. package/dist/engine/service/types.js.map +1 -0
  169. package/dist/engine/service/zvec-grep.d.ts +2 -0
  170. package/dist/engine/service/zvec-grep.js +935 -0
  171. package/dist/engine/service/zvec-grep.js.map +1 -0
  172. package/dist/engine/storage/index.d.ts +42 -0
  173. package/dist/engine/storage/index.js +2 -0
  174. package/dist/engine/storage/index.js.map +1 -0
  175. package/dist/engine/storage/zvec.d.ts +51 -0
  176. package/dist/engine/storage/zvec.js +706 -0
  177. package/dist/engine/storage/zvec.js.map +1 -0
  178. package/dist/engine/types.d.ts +266 -0
  179. package/dist/engine/types.js +14 -0
  180. package/dist/engine/types.js.map +1 -0
  181. package/dist/engine/utils/glob.d.ts +6 -0
  182. package/dist/engine/utils/glob.js +110 -0
  183. package/dist/engine/utils/glob.js.map +1 -0
  184. package/dist/engine/utils/hash.d.ts +2 -0
  185. package/dist/engine/utils/hash.js +8 -0
  186. package/dist/engine/utils/hash.js.map +1 -0
  187. package/dist/engine/utils/json.d.ts +4 -0
  188. package/dist/engine/utils/json.js +45 -0
  189. package/dist/engine/utils/json.js.map +1 -0
  190. package/dist/engine/utils/lock.d.ts +19 -0
  191. package/dist/engine/utils/lock.js +254 -0
  192. package/dist/engine/utils/lock.js.map +1 -0
  193. package/dist/engine/utils/path.d.ts +4 -0
  194. package/dist/engine/utils/path.js +18 -0
  195. package/dist/engine/utils/path.js.map +1 -0
  196. package/dist/index.d.ts +6 -0
  197. package/dist/index.js +4 -0
  198. package/dist/index.js.map +1 -0
  199. package/package.json +47 -0
  200. package/skills/zvec-grep/SKILL.md +141 -0
  201. package/skills/zvec-grep/agents/openai.yaml +4 -0
@@ -0,0 +1,706 @@
1
+ import { ZVecCollectionSchema, ZVecCreateAndOpen, ZVecDataType, ZVecIndexType, ZVecInitialize, ZVecLogLevel, ZVecMetricType, ZVecOpen, } from "@zvec/zvec";
2
+ import { closeSync, existsSync, mkdirSync, openSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { EngineError } from "../errors/index.js";
5
+ import { writeJsonFileSync } from "../utils/json.js";
6
+ import { normalizePath } from "../utils/path.js";
7
+ const ENTITY_VECTOR_FIELD = "embedding";
8
+ const ENTITY_TEXT_FIELD = "text";
9
+ const FILES_MANIFEST = "files.json";
10
+ const ZVEC_PATH = "index.zvec";
11
+ const ZVEC_LOCK_FILE = "LOCK";
12
+ const NO_MATCH_FILTER = "file_id = '__zvec_grep_no_match__'";
13
+ const ZVEC_UPSERT_BATCH_SIZE = 1024;
14
+ const ZVEC_OPEN_RETRY_ATTEMPTS = 8;
15
+ const ZVEC_OPEN_RETRY_BASE_DELAY_MS = 100;
16
+ const ZVEC_OPEN_RETRY_MAX_DELAY_MS = 1000;
17
+ let zvecInitialized = false;
18
+ export class ZvecCollectionStorage {
19
+ collectionPath;
20
+ embedding;
21
+ readOnly;
22
+ collection;
23
+ filesPath;
24
+ filesById = new Map();
25
+ filesByAbsolutePath = new Map();
26
+ needsOptimize = false;
27
+ constructor(collectionPath, embedding, files = [], readOnly = false) {
28
+ this.collectionPath = collectionPath;
29
+ this.embedding = embedding;
30
+ this.readOnly = readOnly;
31
+ initializeZvec();
32
+ if (!readOnly) {
33
+ mkdirSync(collectionPath, { recursive: true });
34
+ }
35
+ this.filesPath = join(collectionPath, FILES_MANIFEST);
36
+ for (const file of files) {
37
+ this.rememberFile(file);
38
+ }
39
+ const zvecPath = join(collectionPath, ZVEC_PATH);
40
+ if (existsSync(zvecPath)) {
41
+ this.collection = openZvecCollection(zvecPath, readOnly, "open", () => ZVecOpen(zvecPath, { readOnly }));
42
+ }
43
+ else if (readOnly) {
44
+ throw new EngineError("zvec collection storage does not exist", {
45
+ code: "ZVEC_GREP.ENGINE.STORAGE.ZVEC_COLLECTION_MISSING",
46
+ context: `path=${zvecPath}`,
47
+ });
48
+ }
49
+ else {
50
+ this.collection = openZvecCollection(zvecPath, readOnly, "create", () => ZVecCreateAndOpen(zvecPath, createSchema(embedding)));
51
+ }
52
+ }
53
+ static filesManifestPath(collectionPath) {
54
+ return join(collectionPath, FILES_MANIFEST);
55
+ }
56
+ static emptyManifest() {
57
+ return { files: [] };
58
+ }
59
+ getFileById(fileId) {
60
+ const file = this.filesById.get(fileId);
61
+ return file ? fileRecordToInfo(file) : null;
62
+ }
63
+ getFileByPath(absolutePath) {
64
+ const file = this.filesByAbsolutePath.get(normalizePath(absolutePath));
65
+ return file ? fileRecordToInfo(file) : null;
66
+ }
67
+ listFiles() {
68
+ return [...this.filesById.values()].map(fileRecordToInfo);
69
+ }
70
+ listEntitiesByFile(fileId, options = {}) {
71
+ const file = this.filesById.get(fileId);
72
+ if (!file) {
73
+ return [];
74
+ }
75
+ const offset = options.offset ?? 0;
76
+ const limit = options.limit ?? file.entityIds.length;
77
+ const ids = file.entityIds.slice(offset, offset + limit);
78
+ return this.fetchStoredEntities(ids);
79
+ }
80
+ getEntity(entityId, options = {}) {
81
+ const fragment = this.getFragment(entityId, options);
82
+ if (!fragment) {
83
+ return null;
84
+ }
85
+ if (fragment.fragment.group && fragment.fragment.group !== fragment.fragment.id) {
86
+ return this.getEntity(fragment.fragment.group, options);
87
+ }
88
+ const entity = fragmentToEntity(fragment.fragment);
89
+ return fragment.vector
90
+ ? { file: fragment.file, entity, vector: fragment.vector }
91
+ : { file: fragment.file, entity };
92
+ }
93
+ getFragment(fragmentId, options = {}) {
94
+ const docs = this.collection.fetchSync({
95
+ ids: fragmentId,
96
+ includeVector: options.includeVector ?? false,
97
+ });
98
+ const doc = docs[fragmentId];
99
+ if (!doc) {
100
+ return null;
101
+ }
102
+ const stored = this.docToStoredFragment(doc);
103
+ if (!stored) {
104
+ return null;
105
+ }
106
+ const vector = options.includeVector
107
+ ? readVector(doc.vectors[ENTITY_VECTOR_FIELD])
108
+ : undefined;
109
+ return vector ? { ...stored, vector } : stored;
110
+ }
111
+ upsertFile(file, fragments, vectors) {
112
+ if (fragments.length !== vectors.length) {
113
+ throw new EngineError("Storage received mismatched entity/vector counts", {
114
+ code: "ZVEC_GREP.ENGINE.STORAGE.ENTITY_VECTOR_COUNT_MISMATCH",
115
+ context: `fileId=${file.id} fragmentCount=${fragments.length} vectorCount=${vectors.length}`,
116
+ });
117
+ }
118
+ validateFragmentGroups(file.id, fragments);
119
+ this.markFileDirty(file);
120
+ this.deleteFileDocuments(file.id);
121
+ const now = Date.now();
122
+ const entityIds = publicEntityIds(fragments);
123
+ const indexedFile = {
124
+ ...file,
125
+ absolutePath: normalizePath(file.absolutePath),
126
+ indexStatus: {
127
+ indexedTime: now,
128
+ entityCount: entityIds.length,
129
+ },
130
+ entityIds,
131
+ };
132
+ if (fragments.length > 0) {
133
+ const docs = fragments.map((fragment, index) => ({
134
+ id: fragment.id,
135
+ vectors: {
136
+ [ENTITY_VECTOR_FIELD]: vectors[index],
137
+ },
138
+ fields: fragmentToFields(indexedFile, fragment, index),
139
+ }));
140
+ this.upsertDocs(file.id, docs);
141
+ this.needsOptimize = true;
142
+ }
143
+ this.rememberFile(indexedFile);
144
+ this.persistFiles();
145
+ }
146
+ upsertDocs(fileId, docs) {
147
+ for (let start = 0; start < docs.length; start += ZVEC_UPSERT_BATCH_SIZE) {
148
+ const batch = docs.slice(start, start + ZVEC_UPSERT_BATCH_SIZE);
149
+ const statuses = this.collection.upsertSync(batch);
150
+ const failed = statuses.find((status) => !status.ok);
151
+ if (failed) {
152
+ throw new EngineError("zvec failed to upsert entity documents", {
153
+ code: "ZVEC_GREP.ENGINE.STORAGE.ZVEC_UPSERT_FAILED",
154
+ context: `fileId=${fileId} batchStart=${start} batchSize=${batch.length} code=${failed.code} message=${failed.message}`,
155
+ });
156
+ }
157
+ }
158
+ }
159
+ markFileFailed(file, error) {
160
+ this.deleteFileDocuments(file.id);
161
+ this.rememberFile({
162
+ ...file,
163
+ absolutePath: normalizePath(file.absolutePath),
164
+ indexStatus: {
165
+ indexedTime: null,
166
+ entityCount: 0,
167
+ error,
168
+ },
169
+ entityIds: [],
170
+ });
171
+ this.persistFiles();
172
+ }
173
+ deleteFile(fileId) {
174
+ const existing = this.filesById.get(fileId);
175
+ this.deleteFileDocuments(fileId);
176
+ if (existing) {
177
+ this.filesById.delete(fileId);
178
+ this.filesByAbsolutePath.delete(normalizePath(existing.absolutePath));
179
+ }
180
+ this.persistFiles();
181
+ }
182
+ searchFts(query, limit, filter) {
183
+ const zvecFilter = buildFilter(filter);
184
+ const docs = this.collection.querySync({
185
+ fieldName: ENTITY_TEXT_FIELD,
186
+ fts: { matchString: query },
187
+ ...(zvecFilter ? { filter: zvecFilter } : {}),
188
+ topk: limit,
189
+ includeVector: false,
190
+ });
191
+ return docsToHits(docs, "fts", this);
192
+ }
193
+ searchVector(vector, limit, filter) {
194
+ const zvecFilter = buildFilter(filter);
195
+ const docs = this.collection.querySync({
196
+ fieldName: ENTITY_VECTOR_FIELD,
197
+ vector: [...vector],
198
+ ...(zvecFilter ? { filter: zvecFilter } : {}),
199
+ topk: limit,
200
+ includeVector: false,
201
+ });
202
+ return docsToHits(docs, "vector", this);
203
+ }
204
+ optimize() {
205
+ if (this.needsOptimize) {
206
+ this.collection.optimizeSync();
207
+ this.needsOptimize = false;
208
+ }
209
+ }
210
+ close() {
211
+ this.optimize();
212
+ this.collection.closeSync();
213
+ }
214
+ fetchStoredEntities(ids) {
215
+ if (ids.length === 0) {
216
+ return [];
217
+ }
218
+ const docs = this.collection.fetchSync({
219
+ ids: [...ids],
220
+ includeVector: false,
221
+ });
222
+ const entities = [];
223
+ for (const id of ids) {
224
+ const doc = docs[id];
225
+ const entity = doc ? this.docToStoredEntity(doc) : null;
226
+ if (entity) {
227
+ entities.push(entity);
228
+ }
229
+ }
230
+ return entities;
231
+ }
232
+ docToStoredEntity(doc) {
233
+ const stored = this.docToStoredFragment(doc);
234
+ if (!stored) {
235
+ return null;
236
+ }
237
+ if (stored.fragment.group && stored.fragment.group !== stored.fragment.id) {
238
+ return this.getEntity(stored.fragment.group);
239
+ }
240
+ return {
241
+ file: stored.file,
242
+ entity: fragmentToEntity(stored.fragment),
243
+ };
244
+ }
245
+ docToStoredFragment(doc) {
246
+ const fileId = readStringField(doc, "file_id");
247
+ const file = this.filesById.get(fileId);
248
+ if (!file) {
249
+ return null;
250
+ }
251
+ return {
252
+ file: fileRecordToInfo(file),
253
+ fragment: {
254
+ id: doc.id,
255
+ group: readNullableStringFieldFromFields(doc.fields, "group") ?? undefined,
256
+ fileId,
257
+ range: parseRange(readStringField(doc, "range_json")),
258
+ content: parseContent(doc.fields),
259
+ metadata: parseMetadata(doc.fields),
260
+ },
261
+ };
262
+ }
263
+ rememberFile(file) {
264
+ const normalized = {
265
+ ...file,
266
+ absolutePath: normalizePath(file.absolutePath),
267
+ };
268
+ this.filesById.set(normalized.id, normalized);
269
+ this.filesByAbsolutePath.set(normalized.absolutePath, normalized);
270
+ }
271
+ markFileDirty(file) {
272
+ this.rememberFile({
273
+ ...file,
274
+ absolutePath: normalizePath(file.absolutePath),
275
+ indexStatus: {
276
+ indexedTime: null,
277
+ entityCount: 0,
278
+ },
279
+ entityIds: [],
280
+ });
281
+ this.persistFiles();
282
+ }
283
+ deleteFileDocuments(fileId) {
284
+ this.collection.deleteByFilterSync(`file_id = ${quoteFilterString(fileId)}`);
285
+ this.needsOptimize = true;
286
+ }
287
+ persistFiles() {
288
+ const manifest = {
289
+ files: [...this.filesById.values()].sort((left, right) => left.relativePath.localeCompare(right.relativePath)),
290
+ };
291
+ writeJsonFileSync(this.filesPath, manifest);
292
+ }
293
+ }
294
+ function fileRecordToInfo(file) {
295
+ const { entityIds: _entityIds, ...info } = file;
296
+ return info;
297
+ }
298
+ function fragmentToEntity(fragment) {
299
+ return {
300
+ id: publicEntityId(fragment),
301
+ fileId: fragment.fileId,
302
+ range: fragment.range,
303
+ content: fragment.content,
304
+ metadata: fragment.metadata,
305
+ };
306
+ }
307
+ function publicEntityId(fragment) {
308
+ return fragment.group ?? fragment.id;
309
+ }
310
+ function publicEntityIds(fragments) {
311
+ const ids = new Set();
312
+ for (const fragment of fragments) {
313
+ if (!fragment.group || fragment.group === fragment.id) {
314
+ ids.add(publicEntityId(fragment));
315
+ }
316
+ }
317
+ return [...ids];
318
+ }
319
+ function validateFragmentGroups(fileId, fragments) {
320
+ const ids = new Set();
321
+ const groups = new Map();
322
+ for (const fragment of fragments) {
323
+ if (fragment.fileId !== fileId) {
324
+ throw new EngineError("Entity fragment belongs to the wrong file", {
325
+ code: "ZVEC_GREP.ENGINE.STORAGE.FRAGMENT_FILE_MISMATCH",
326
+ context: `fileId=${fileId} fragmentId=${fragment.id} fragmentFileId=${fragment.fileId}`,
327
+ });
328
+ }
329
+ if (ids.has(fragment.id)) {
330
+ throw new EngineError("Duplicate entity fragment id", {
331
+ code: "ZVEC_GREP.ENGINE.STORAGE.DUPLICATE_FRAGMENT_ID",
332
+ context: `fileId=${fileId} fragmentId=${fragment.id}`,
333
+ });
334
+ }
335
+ ids.add(fragment.id);
336
+ if (fragment.group) {
337
+ const group = groups.get(fragment.group) ?? [];
338
+ group.push(fragment);
339
+ groups.set(fragment.group, group);
340
+ }
341
+ }
342
+ for (const [groupId, group] of groups) {
343
+ const majorCount = group.filter((fragment) => fragment.id === groupId).length;
344
+ if (majorCount !== 1) {
345
+ throw new EngineError("Fragment group must have exactly one major fragment", {
346
+ code: "ZVEC_GREP.ENGINE.STORAGE.INVALID_FRAGMENT_GROUP",
347
+ context: `fileId=${fileId} group=${groupId} majorCount=${majorCount}`,
348
+ });
349
+ }
350
+ }
351
+ }
352
+ function initializeZvec() {
353
+ if (zvecInitialized) {
354
+ return;
355
+ }
356
+ ZVecInitialize({ logLevel: ZVecLogLevel.WARN });
357
+ zvecInitialized = true;
358
+ }
359
+ function openZvecCollection(zvecPath, readOnly, action, open) {
360
+ const lockWritable = canTouchZvecLock(zvecPath);
361
+ let lastError;
362
+ for (let attempt = 0; attempt < ZVEC_OPEN_RETRY_ATTEMPTS; attempt++) {
363
+ try {
364
+ return open();
365
+ }
366
+ catch (error) {
367
+ lastError = error;
368
+ if (attempt + 1 >= ZVEC_OPEN_RETRY_ATTEMPTS
369
+ || !isRetryableZvecOpenError(error, lockWritable)) {
370
+ break;
371
+ }
372
+ sleepSync(zvecOpenRetryDelayMs(attempt));
373
+ }
374
+ }
375
+ throw new EngineError("Failed to open zvec collection storage", {
376
+ code: "ZVEC_GREP.ENGINE.STORAGE.ZVEC_OPEN_FAILED",
377
+ context: `path=${zvecPath} action=${action} readOnly=${readOnly} attempts=${ZVEC_OPEN_RETRY_ATTEMPTS}`,
378
+ cause: lastError,
379
+ });
380
+ }
381
+ function canTouchZvecLock(zvecPath) {
382
+ const lockPath = join(zvecPath, ZVEC_LOCK_FILE);
383
+ if (!existsSync(lockPath)) {
384
+ return true;
385
+ }
386
+ try {
387
+ const fd = openSync(lockPath, "a");
388
+ closeSync(fd);
389
+ return true;
390
+ }
391
+ catch {
392
+ return false;
393
+ }
394
+ }
395
+ function isRetryableZvecOpenError(error, lockWritable) {
396
+ if (!lockWritable) {
397
+ return false;
398
+ }
399
+ const message = error instanceof Error ? error.message : String(error);
400
+ if (!/lock/i.test(message)) {
401
+ return false;
402
+ }
403
+ return !/permission|access denied|eacces|eperm|read-only/i.test(message);
404
+ }
405
+ function zvecOpenRetryDelayMs(attempt) {
406
+ const exponential = ZVEC_OPEN_RETRY_BASE_DELAY_MS * 2 ** attempt;
407
+ return Math.min(exponential, ZVEC_OPEN_RETRY_MAX_DELAY_MS);
408
+ }
409
+ function sleepSync(ms) {
410
+ const buffer = new SharedArrayBuffer(4);
411
+ const values = new Int32Array(buffer);
412
+ Atomics.wait(values, 0, 0, ms);
413
+ }
414
+ function createSchema(embedding) {
415
+ return new ZVecCollectionSchema({
416
+ name: "zvec_grep_entities",
417
+ fields: [
418
+ indexedStringField("group", true),
419
+ indexedStringField("file_id"),
420
+ stringField("content_kind"),
421
+ stringField("content_hash", true),
422
+ stringField("metadata_kind", true),
423
+ indexedStringField("symbol_type", true),
424
+ indexedStringField("symbol_name", true),
425
+ stringField("symbol_scope", true),
426
+ stringField("symbol_signature", true),
427
+ stringField("symbol_doc", true),
428
+ stringField("symbol_modifiers", true),
429
+ stringField("node_type", true),
430
+ stringField("heading", true),
431
+ {
432
+ name: "heading_level",
433
+ dataType: ZVecDataType.INT32,
434
+ nullable: true,
435
+ },
436
+ {
437
+ name: ENTITY_TEXT_FIELD,
438
+ dataType: ZVecDataType.STRING,
439
+ nullable: false,
440
+ indexParams: {
441
+ indexType: ZVecIndexType.FTS,
442
+ tokenizerName: "jieba",
443
+ filters: ["lowercase"],
444
+ },
445
+ },
446
+ {
447
+ name: "fragment_index",
448
+ dataType: ZVecDataType.INT32,
449
+ nullable: false,
450
+ },
451
+ { name: "range_json", dataType: ZVecDataType.STRING, nullable: false },
452
+ { name: "content_base64", dataType: ZVecDataType.STRING, nullable: true },
453
+ { name: "image_format", dataType: ZVecDataType.STRING, nullable: true },
454
+ ],
455
+ vectors: {
456
+ name: ENTITY_VECTOR_FIELD,
457
+ dataType: ZVecDataType.VECTOR_FP32,
458
+ dimension: embedding.dimension,
459
+ indexParams: {
460
+ indexType: ZVecIndexType.HNSW,
461
+ metricType: metricToZvec(embedding.metric),
462
+ },
463
+ },
464
+ });
465
+ }
466
+ function indexedStringField(name, nullable = false) {
467
+ return {
468
+ name,
469
+ dataType: ZVecDataType.STRING,
470
+ nullable,
471
+ indexParams: { indexType: ZVecIndexType.INVERT },
472
+ };
473
+ }
474
+ function stringField(name, nullable = false) {
475
+ return {
476
+ name,
477
+ dataType: ZVecDataType.STRING,
478
+ nullable,
479
+ };
480
+ }
481
+ function metricToZvec(metric) {
482
+ if (metric === "cosine") {
483
+ return ZVecMetricType.COSINE;
484
+ }
485
+ if (metric === "dot") {
486
+ return ZVecMetricType.IP;
487
+ }
488
+ if (metric === "euclidean") {
489
+ return ZVecMetricType.L2;
490
+ }
491
+ throw new EngineError("Unsupported vector metric for zvec storage", {
492
+ code: "ZVEC_GREP.ENGINE.STORAGE.UNSUPPORTED_VECTOR_METRIC",
493
+ context: `metric=${metric}`,
494
+ });
495
+ }
496
+ function fragmentToFields(file, fragment, fragmentIndex) {
497
+ const contentFields = contentToFields(fragment.content);
498
+ return {
499
+ ...optionalFields({
500
+ group: fragment.group,
501
+ content_hash: file.contentHash,
502
+ }),
503
+ file_id: file.id,
504
+ content_kind: fragment.content.kind,
505
+ ...metadataToFields(fragment.metadata),
506
+ fragment_index: fragmentIndex,
507
+ range_json: JSON.stringify(fragment.range),
508
+ ...contentFields,
509
+ };
510
+ }
511
+ function contentToFields(content) {
512
+ if (content.kind === "text") {
513
+ return {
514
+ text: content.text,
515
+ };
516
+ }
517
+ return {
518
+ text: `[image:${content.format}]`,
519
+ content_base64: Buffer.from(content.data).toString("base64"),
520
+ image_format: content.format,
521
+ };
522
+ }
523
+ function metadataToFields(metadata) {
524
+ if (!metadata) {
525
+ return {};
526
+ }
527
+ if (metadata.kind === "code") {
528
+ return {
529
+ metadata_kind: "code",
530
+ symbol_type: metadata.symbolType,
531
+ ...optionalFields({
532
+ symbol_name: metadata.symbolName,
533
+ symbol_scope: metadata.scope,
534
+ symbol_signature: metadata.signature,
535
+ symbol_doc: metadata.doc,
536
+ symbol_modifiers: metadata.modifiers.length > 0 ? metadata.modifiers.join(" ") : null,
537
+ node_type: metadata.nodeType,
538
+ }),
539
+ };
540
+ }
541
+ return {
542
+ metadata_kind: "markdown",
543
+ ...optionalFields({
544
+ symbol_scope: metadata.scope,
545
+ heading: metadata.heading,
546
+ heading_level: metadata.level,
547
+ }),
548
+ };
549
+ }
550
+ function optionalFields(fields) {
551
+ const result = {};
552
+ for (const [key, value] of Object.entries(fields)) {
553
+ if (value !== null && value !== undefined) {
554
+ result[key] = value;
555
+ }
556
+ }
557
+ return result;
558
+ }
559
+ function parseContent(fields) {
560
+ const kind = fields.content_kind;
561
+ if (kind === "text") {
562
+ return {
563
+ kind: "text",
564
+ text: readStringFieldFromFields(fields, "text"),
565
+ };
566
+ }
567
+ if (kind === "image") {
568
+ return {
569
+ kind: "image",
570
+ data: Buffer.from(readStringFieldFromFields(fields, "content_base64"), "base64"),
571
+ format: readStringFieldFromFields(fields, "image_format"),
572
+ };
573
+ }
574
+ throw new EngineError("Stored entity has unsupported content kind", {
575
+ code: "ZVEC_GREP.ENGINE.STORAGE.UNSUPPORTED_STORED_CONTENT_KIND",
576
+ context: `contentKind=${String(kind)}`,
577
+ });
578
+ }
579
+ function parseMetadata(fields) {
580
+ const kind = fields.metadata_kind;
581
+ if (kind === "code") {
582
+ return {
583
+ kind: "code",
584
+ symbolType: readStringFieldFromFields(fields, "symbol_type"),
585
+ symbolName: readNullableStringFieldFromFields(fields, "symbol_name"),
586
+ scope: readNullableStringFieldFromFields(fields, "symbol_scope"),
587
+ nodeType: readNullableStringFieldFromFields(fields, "node_type"),
588
+ signature: readNullableStringFieldFromFields(fields, "symbol_signature"),
589
+ doc: readNullableStringFieldFromFields(fields, "symbol_doc"),
590
+ modifiers: readCodeModifiers(readNullableStringFieldFromFields(fields, "symbol_modifiers")),
591
+ };
592
+ }
593
+ if (kind === "markdown") {
594
+ return {
595
+ kind: "markdown",
596
+ heading: readNullableStringFieldFromFields(fields, "heading"),
597
+ level: readNullableNumberFieldFromFields(fields, "heading_level"),
598
+ scope: readNullableStringFieldFromFields(fields, "symbol_scope"),
599
+ };
600
+ }
601
+ return undefined;
602
+ }
603
+ function readCodeModifiers(value) {
604
+ if (!value) {
605
+ return [];
606
+ }
607
+ return value.split(/\s+/).filter((item) => item === "exported"
608
+ || item === "async"
609
+ || item === "static"
610
+ || item === "public"
611
+ || item === "private"
612
+ || item === "protected"
613
+ || item === "internal");
614
+ }
615
+ function parseRange(value) {
616
+ return JSON.parse(value);
617
+ }
618
+ function docsToHits(docs, path, storage) {
619
+ const hits = [];
620
+ for (const doc of docs) {
621
+ const stored = storage.docToStoredFragment(doc);
622
+ if (stored) {
623
+ hits.push({
624
+ ...stored,
625
+ path,
626
+ score: doc.score,
627
+ });
628
+ }
629
+ }
630
+ return hits;
631
+ }
632
+ function readStringField(doc, field) {
633
+ return readStringFieldFromFields(doc.fields, field);
634
+ }
635
+ function readStringFieldFromFields(fields, field) {
636
+ const value = fields[field];
637
+ if (typeof value === "string") {
638
+ return value;
639
+ }
640
+ if (value === null || value === undefined) {
641
+ return "";
642
+ }
643
+ return String(value);
644
+ }
645
+ function readNullableStringFieldFromFields(fields, field) {
646
+ const value = fields[field];
647
+ if (value === null || value === undefined || value === "") {
648
+ return null;
649
+ }
650
+ return String(value);
651
+ }
652
+ function readNullableNumberFieldFromFields(fields, field) {
653
+ const value = fields[field];
654
+ if (typeof value === "number" && Number.isFinite(value)) {
655
+ return value === 0 ? null : value;
656
+ }
657
+ if (typeof value === "string" && value.length > 0) {
658
+ const parsed = Number.parseInt(value, 10);
659
+ return Number.isFinite(parsed) && parsed !== 0 ? parsed : null;
660
+ }
661
+ return null;
662
+ }
663
+ function readVector(vector) {
664
+ if (!vector) {
665
+ return undefined;
666
+ }
667
+ if (Array.isArray(vector)) {
668
+ return vector;
669
+ }
670
+ if (ArrayBuffer.isView(vector)) {
671
+ return [...vector];
672
+ }
673
+ return undefined;
674
+ }
675
+ function buildFilter(filter) {
676
+ if (!filter) {
677
+ return undefined;
678
+ }
679
+ const clauses = [];
680
+ if (filter.fileIds) {
681
+ clauses.push(buildNonEmptyInFilter("file_id", filter.fileIds));
682
+ }
683
+ if (filter.groupIds) {
684
+ clauses.push(buildNonEmptyInFilter("group", filter.groupIds));
685
+ }
686
+ if (filter.symbolNames) {
687
+ clauses.push(buildNonEmptyInFilter("symbol_name", filter.symbolNames));
688
+ }
689
+ if (filter.symbolTypes) {
690
+ clauses.push(buildNonEmptyInFilter("symbol_type", filter.symbolTypes));
691
+ }
692
+ return clauses.length > 0 ? clauses.join(" AND ") : undefined;
693
+ }
694
+ function buildNonEmptyInFilter(field, values) {
695
+ return values.length > 0 ? buildInFilter(field, values) : NO_MATCH_FILTER;
696
+ }
697
+ function buildInFilter(field, values) {
698
+ if (values.length === 1) {
699
+ return `${field} = ${quoteFilterString(values[0])}`;
700
+ }
701
+ return `(${values.map((value) => `${field} = ${quoteFilterString(value)}`).join(" OR ")})`;
702
+ }
703
+ function quoteFilterString(value) {
704
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
705
+ }
706
+ //# sourceMappingURL=zvec.js.map