@ff-labs/fff-node 0.10.5-dev.774d6bc → 0.10.5-nightly.f565d37
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/index.cjs +729 -291
- package/dist/index.cjs.map +1 -14
- package/dist/index.js +617 -174
- package/dist/index.js.map +1 -14
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/binary.ts
|
|
2
|
-
import { existsSync, readFileSync } from "
|
|
3
|
-
import { createRequire } from "
|
|
4
|
-
import { dirname, join } from "
|
|
5
|
-
import { fileURLToPath } from "
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
6
|
|
|
7
7
|
// src/platform.ts
|
|
8
|
-
import { execSync } from "
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
9
|
function getTriple() {
|
|
10
10
|
const platform = process.platform;
|
|
11
11
|
const arch = process.arch;
|
|
@@ -29,11 +29,11 @@ function detectLinuxLibc() {
|
|
|
29
29
|
try {
|
|
30
30
|
output = execSync("ldd --version 2>&1", {
|
|
31
31
|
encoding: "utf-8",
|
|
32
|
-
timeout:
|
|
32
|
+
timeout: 5e3
|
|
33
33
|
});
|
|
34
34
|
} catch (e) {
|
|
35
|
-
const
|
|
36
|
-
output = String(
|
|
35
|
+
const err2 = e;
|
|
36
|
+
output = String(err2?.stdout ?? "") + String(err2?.stderr ?? "");
|
|
37
37
|
}
|
|
38
38
|
if (output.toLowerCase().includes("musl")) {
|
|
39
39
|
return "unknown-linux-musl";
|
|
@@ -92,10 +92,8 @@ function getNpmPackageName() {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// src/binary.ts
|
|
95
|
-
var __dirname = "/home/runner/work/fff/fff/packages/fff-node/src";
|
|
96
95
|
function getCurrentDir() {
|
|
97
|
-
if (typeof __dirname !== "undefined")
|
|
98
|
-
return __dirname;
|
|
96
|
+
if (typeof __dirname !== "undefined") return __dirname;
|
|
99
97
|
const url = import.meta.url;
|
|
100
98
|
if (url.startsWith("file://")) {
|
|
101
99
|
return dirname(fileURLToPath(url));
|
|
@@ -105,14 +103,15 @@ function getCurrentDir() {
|
|
|
105
103
|
function getPackageDir() {
|
|
106
104
|
const currentDir = getCurrentDir();
|
|
107
105
|
let dir = currentDir;
|
|
108
|
-
for (let i = 0;i < 5; i++) {
|
|
106
|
+
for (let i = 0; i < 5; i++) {
|
|
109
107
|
if (existsSync(join(dir, "package.json"))) {
|
|
110
108
|
try {
|
|
111
109
|
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
112
110
|
if (pkg.name === "@ff-labs/fff-node") {
|
|
113
111
|
return dir;
|
|
114
112
|
}
|
|
115
|
-
} catch {
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
116
115
|
}
|
|
117
116
|
dir = dirname(dir);
|
|
118
117
|
}
|
|
@@ -131,7 +130,8 @@ function resolveFromNpmPackage() {
|
|
|
131
130
|
if (existsSync(binaryPath)) {
|
|
132
131
|
return binaryPath;
|
|
133
132
|
}
|
|
134
|
-
} catch {
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
135
|
return null;
|
|
136
136
|
}
|
|
137
137
|
function getDevBinaryPath() {
|
|
@@ -156,21 +156,18 @@ function isDevWorkspace() {
|
|
|
156
156
|
function findBinary() {
|
|
157
157
|
if (isDevWorkspace()) {
|
|
158
158
|
const binPath = join(getPackageDir(), "bin", getLibFilename());
|
|
159
|
-
if (existsSync(binPath))
|
|
160
|
-
return binPath;
|
|
159
|
+
if (existsSync(binPath)) return binPath;
|
|
161
160
|
const devPath = getDevBinaryPath();
|
|
162
|
-
if (devPath)
|
|
163
|
-
return devPath;
|
|
161
|
+
if (devPath) return devPath;
|
|
164
162
|
const npmPath2 = resolveFromNpmPackage();
|
|
165
|
-
if (npmPath2)
|
|
166
|
-
return npmPath2;
|
|
163
|
+
if (npmPath2) return npmPath2;
|
|
167
164
|
return null;
|
|
168
165
|
}
|
|
169
166
|
const npmPath = resolveFromNpmPackage();
|
|
170
|
-
if (npmPath)
|
|
171
|
-
return npmPath;
|
|
167
|
+
if (npmPath) return npmPath;
|
|
172
168
|
return getDevBinaryPath();
|
|
173
169
|
}
|
|
170
|
+
|
|
174
171
|
// src/ffi.ts
|
|
175
172
|
import {
|
|
176
173
|
close,
|
|
@@ -240,32 +237,32 @@ var FFF_RESULT_STRUCT = {
|
|
|
240
237
|
int_value: DataType.I64
|
|
241
238
|
};
|
|
242
239
|
function loadLibrary() {
|
|
243
|
-
if (isLoaded)
|
|
244
|
-
return;
|
|
240
|
+
if (isLoaded) return;
|
|
245
241
|
const binaryPath = findBinary();
|
|
246
242
|
if (!binaryPath) {
|
|
247
|
-
throw new Error(
|
|
243
|
+
throw new Error(
|
|
244
|
+
"fff native library not found. Run `npx @ff-labs/fff-node download` or build from source with `cargo build --release -p fff-c`"
|
|
245
|
+
);
|
|
248
246
|
}
|
|
249
247
|
open({ library: LIBRARY_KEY, path: binaryPath });
|
|
250
248
|
isLoaded = true;
|
|
251
249
|
}
|
|
252
250
|
function snakeToCamel(obj) {
|
|
253
|
-
if (obj === null || obj ===
|
|
254
|
-
|
|
255
|
-
if (
|
|
256
|
-
return obj;
|
|
257
|
-
if (Array.isArray(obj))
|
|
258
|
-
return obj.map(snakeToCamel);
|
|
251
|
+
if (obj === null || obj === void 0) return obj;
|
|
252
|
+
if (typeof obj !== "object") return obj;
|
|
253
|
+
if (Array.isArray(obj)) return obj.map(snakeToCamel);
|
|
259
254
|
const result = {};
|
|
260
255
|
for (const [key, value] of Object.entries(obj)) {
|
|
261
|
-
const camelKey = key.replace(
|
|
256
|
+
const camelKey = key.replace(
|
|
257
|
+
/_([a-z])/g,
|
|
258
|
+
(_, letter) => letter.toUpperCase()
|
|
259
|
+
);
|
|
262
260
|
result[camelKey] = snakeToCamel(value);
|
|
263
261
|
}
|
|
264
262
|
return result;
|
|
265
263
|
}
|
|
266
264
|
function readCString(ptr) {
|
|
267
|
-
if (isNullPointer(ptr))
|
|
268
|
-
return null;
|
|
265
|
+
if (isNullPointer(ptr)) return null;
|
|
269
266
|
try {
|
|
270
267
|
const [str] = restorePointer({
|
|
271
268
|
retType: [DataType.String],
|
|
@@ -300,7 +297,8 @@ function freeResult(resultPtr) {
|
|
|
300
297
|
paramsType: [DataType.External],
|
|
301
298
|
paramsValue: [resultPtr]
|
|
302
299
|
});
|
|
303
|
-
} catch {
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
304
302
|
}
|
|
305
303
|
function readResultEnvelope(funcName, paramsType, paramsValue) {
|
|
306
304
|
loadLibrary();
|
|
@@ -314,51 +312,43 @@ function readResultEnvelope(funcName, paramsType, paramsValue) {
|
|
|
314
312
|
}
|
|
315
313
|
function callVoidResult(funcName, paramsType, paramsValue) {
|
|
316
314
|
const res = readResultEnvelope(funcName, paramsType, paramsValue);
|
|
317
|
-
if ("ok" in res)
|
|
318
|
-
return res;
|
|
315
|
+
if ("ok" in res) return res;
|
|
319
316
|
freeResult(res.rawPtr);
|
|
320
|
-
return { ok: true, value:
|
|
317
|
+
return { ok: true, value: void 0 };
|
|
321
318
|
}
|
|
322
319
|
function callIntResult(funcName, paramsType, paramsValue) {
|
|
323
320
|
const res = readResultEnvelope(funcName, paramsType, paramsValue);
|
|
324
|
-
if ("ok" in res)
|
|
325
|
-
return res;
|
|
321
|
+
if ("ok" in res) return res;
|
|
326
322
|
const value = Number(res.struct.int_value);
|
|
327
323
|
freeResult(res.rawPtr);
|
|
328
324
|
return { ok: true, value };
|
|
329
325
|
}
|
|
330
326
|
function callBoolResult(funcName, paramsType, paramsValue) {
|
|
331
327
|
const res = readResultEnvelope(funcName, paramsType, paramsValue);
|
|
332
|
-
if ("ok" in res)
|
|
333
|
-
return res;
|
|
328
|
+
if ("ok" in res) return res;
|
|
334
329
|
const value = Number(res.struct.int_value) !== 0;
|
|
335
330
|
freeResult(res.rawPtr);
|
|
336
331
|
return { ok: true, value };
|
|
337
332
|
}
|
|
338
333
|
function callStringResult(funcName, paramsType, paramsValue) {
|
|
339
334
|
const res = readResultEnvelope(funcName, paramsType, paramsValue);
|
|
340
|
-
if ("ok" in res)
|
|
341
|
-
return res;
|
|
335
|
+
if ("ok" in res) return res;
|
|
342
336
|
const handlePtr = res.struct.handle;
|
|
343
337
|
freeResult(res.rawPtr);
|
|
344
|
-
if (isNullPointer(handlePtr))
|
|
345
|
-
return { ok: true, value: null };
|
|
338
|
+
if (isNullPointer(handlePtr)) return { ok: true, value: null };
|
|
346
339
|
const str = readCString(handlePtr);
|
|
347
340
|
freeString(handlePtr);
|
|
348
341
|
return { ok: true, value: str };
|
|
349
342
|
}
|
|
350
343
|
function callJsonResult(funcName, paramsType, paramsValue) {
|
|
351
344
|
const res = readResultEnvelope(funcName, paramsType, paramsValue);
|
|
352
|
-
if ("ok" in res)
|
|
353
|
-
return res;
|
|
345
|
+
if ("ok" in res) return res;
|
|
354
346
|
const handlePtr = res.struct.handle;
|
|
355
347
|
freeResult(res.rawPtr);
|
|
356
|
-
if (isNullPointer(handlePtr))
|
|
357
|
-
return { ok: true, value: undefined };
|
|
348
|
+
if (isNullPointer(handlePtr)) return { ok: true, value: void 0 };
|
|
358
349
|
const jsonStr = readCString(handlePtr);
|
|
359
350
|
freeString(handlePtr);
|
|
360
|
-
if (jsonStr === null || jsonStr === "")
|
|
361
|
-
return { ok: true, value: undefined };
|
|
351
|
+
if (jsonStr === null || jsonStr === "") return { ok: true, value: void 0 };
|
|
362
352
|
try {
|
|
363
353
|
return { ok: true, value: snakeToCamel(JSON.parse(jsonStr)) };
|
|
364
354
|
} catch {
|
|
@@ -374,7 +364,8 @@ function freeString(ptr) {
|
|
|
374
364
|
paramsType: [DataType.External],
|
|
375
365
|
paramsValue: [ptr]
|
|
376
366
|
});
|
|
377
|
-
} catch {
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
378
369
|
}
|
|
379
370
|
function ffiCreate(basePath, frecencyDbPath, historyDbPath, _useUnsafeNoLock, enableMmapCache, enableContentIndexing, watch, aiMode, logFilePath, logLevel, cacheBudgetMaxFiles, cacheBudgetMaxBytes, cacheBudgetMaxFileSize, enableFsRootScanning, enableHomeDirScanning, followSymlinks) {
|
|
380
371
|
loadLibrary();
|
|
@@ -463,6 +454,7 @@ var FFF_SEARCH_RESULT_STRUCT = {
|
|
|
463
454
|
count: DataType.U32,
|
|
464
455
|
total_matched: DataType.U32,
|
|
465
456
|
total_files: DataType.U32,
|
|
457
|
+
// FffLocation inlined (flattened)
|
|
466
458
|
location_tag: DataType.U8,
|
|
467
459
|
location_line: DataType.I32,
|
|
468
460
|
location_col: DataType.I32,
|
|
@@ -500,6 +492,7 @@ var FFF_MIXED_SEARCH_RESULT_STRUCT = {
|
|
|
500
492
|
total_matched: DataType.U32,
|
|
501
493
|
total_files: DataType.U32,
|
|
502
494
|
total_dirs: DataType.U32,
|
|
495
|
+
// FffLocation inlined (flattened)
|
|
503
496
|
location_tag: DataType.U8,
|
|
504
497
|
location_line: DataType.I32,
|
|
505
498
|
location_col: DataType.I32,
|
|
@@ -526,6 +519,7 @@ var FFF_GREP_MATCH_STRUCT = {
|
|
|
526
519
|
context_before_count: DataType.U32,
|
|
527
520
|
context_after_count: DataType.U32,
|
|
528
521
|
fuzzy_score: DataType.U32,
|
|
522
|
+
// actually u16 in C, but ffi-rs doesn't so we read it as u32 with padding
|
|
529
523
|
has_fuzzy_score: DataType.U8,
|
|
530
524
|
is_binary: DataType.U8,
|
|
531
525
|
is_definition: DataType.U8
|
|
@@ -627,10 +621,9 @@ function ptrOffset(base, bytes) {
|
|
|
627
621
|
});
|
|
628
622
|
}
|
|
629
623
|
function readCStringArray(ptrArray, count) {
|
|
630
|
-
if (count === 0 || isNullPointer(ptrArray))
|
|
631
|
-
return [];
|
|
624
|
+
if (count === 0 || isNullPointer(ptrArray)) return [];
|
|
632
625
|
const result = [];
|
|
633
|
-
for (let i = 0;i < count; i++) {
|
|
626
|
+
for (let i = 0; i < count; i++) {
|
|
634
627
|
const elemPtr = ptrOffset(ptrArray, i * 8);
|
|
635
628
|
const [charPtr] = restorePointer({
|
|
636
629
|
retType: [DataType.External],
|
|
@@ -642,7 +635,7 @@ function readCStringArray(ptrArray, count) {
|
|
|
642
635
|
}
|
|
643
636
|
function readGrepMatchFromRaw(raw) {
|
|
644
637
|
const matchRanges = [];
|
|
645
|
-
for (let i = 0;i < raw.match_ranges_count; i++) {
|
|
638
|
+
for (let i = 0; i < raw.match_ranges_count; i++) {
|
|
646
639
|
const rangePtr = ptrOffset(raw.match_ranges, i * 8);
|
|
647
640
|
const [rangeRaw] = restorePointer({
|
|
648
641
|
retType: [FFF_MATCH_RANGE_STRUCT],
|
|
@@ -702,10 +695,15 @@ function parseGrepResult(rawPtr) {
|
|
|
702
695
|
paramsValue: wrapPointer([handlePtr])
|
|
703
696
|
});
|
|
704
697
|
const count = gr.count;
|
|
705
|
-
const regexFallbackError = readCString(gr.regex_fallback_error) ??
|
|
698
|
+
const regexFallbackError = readCString(gr.regex_fallback_error) ?? void 0;
|
|
706
699
|
const items = [];
|
|
707
|
-
for (let i = 0;i < count; i++) {
|
|
708
|
-
const rawMatch = callAccessor(
|
|
700
|
+
for (let i = 0; i < count; i++) {
|
|
701
|
+
const rawMatch = callAccessor(
|
|
702
|
+
"fff_grep_result_get_match",
|
|
703
|
+
handlePtr,
|
|
704
|
+
i,
|
|
705
|
+
FFF_GREP_MATCH_STRUCT
|
|
706
|
+
);
|
|
709
707
|
items.push(readGrepMatchFromRaw(rawMatch));
|
|
710
708
|
}
|
|
711
709
|
load({
|
|
@@ -768,10 +766,20 @@ function parseSearchResult(rawPtr) {
|
|
|
768
766
|
}
|
|
769
767
|
const items = [];
|
|
770
768
|
const scores = [];
|
|
771
|
-
for (let i = 0;i < count; i++) {
|
|
772
|
-
const rawItem = callAccessor(
|
|
769
|
+
for (let i = 0; i < count; i++) {
|
|
770
|
+
const rawItem = callAccessor(
|
|
771
|
+
"fff_search_result_get_item",
|
|
772
|
+
handlePtr,
|
|
773
|
+
i,
|
|
774
|
+
FFF_FILE_ITEM_STRUCT
|
|
775
|
+
);
|
|
773
776
|
items.push(readFileItemFromRaw(rawItem));
|
|
774
|
-
const rawScore = callAccessor(
|
|
777
|
+
const rawScore = callAccessor(
|
|
778
|
+
"fff_search_result_get_score",
|
|
779
|
+
handlePtr,
|
|
780
|
+
i,
|
|
781
|
+
FFF_SCORE_STRUCT
|
|
782
|
+
);
|
|
775
783
|
scores.push(readScoreFromRaw(rawScore));
|
|
776
784
|
}
|
|
777
785
|
load({
|
|
@@ -816,10 +824,20 @@ function parseDirSearchResult(rawPtr) {
|
|
|
816
824
|
const count = sr.count;
|
|
817
825
|
const items = [];
|
|
818
826
|
const scores = [];
|
|
819
|
-
for (let i = 0;i < count; i++) {
|
|
820
|
-
const rawItem = callAccessor(
|
|
827
|
+
for (let i = 0; i < count; i++) {
|
|
828
|
+
const rawItem = callAccessor(
|
|
829
|
+
"fff_dir_search_result_get_item",
|
|
830
|
+
handlePtr,
|
|
831
|
+
i,
|
|
832
|
+
FFF_DIR_ITEM_STRUCT
|
|
833
|
+
);
|
|
821
834
|
items.push(readDirItemFromRaw(rawItem));
|
|
822
|
-
const rawScore = callAccessor(
|
|
835
|
+
const rawScore = callAccessor(
|
|
836
|
+
"fff_dir_search_result_get_score",
|
|
837
|
+
handlePtr,
|
|
838
|
+
i,
|
|
839
|
+
FFF_SCORE_STRUCT
|
|
840
|
+
);
|
|
823
841
|
scores.push(readScoreFromRaw(rawScore));
|
|
824
842
|
}
|
|
825
843
|
load({
|
|
@@ -879,10 +897,20 @@ function parseMixedSearchResult(rawPtr) {
|
|
|
879
897
|
}
|
|
880
898
|
const items = [];
|
|
881
899
|
const scores = [];
|
|
882
|
-
for (let i = 0;i < count; i++) {
|
|
883
|
-
const rawItem = callAccessor(
|
|
900
|
+
for (let i = 0; i < count; i++) {
|
|
901
|
+
const rawItem = callAccessor(
|
|
902
|
+
"fff_mixed_search_result_get_item",
|
|
903
|
+
handlePtr,
|
|
904
|
+
i,
|
|
905
|
+
FFF_MIXED_ITEM_STRUCT
|
|
906
|
+
);
|
|
884
907
|
items.push(readMixedItemFromRaw(rawItem));
|
|
885
|
-
const rawScore = callAccessor(
|
|
908
|
+
const rawScore = callAccessor(
|
|
909
|
+
"fff_mixed_search_result_get_score",
|
|
910
|
+
handlePtr,
|
|
911
|
+
i,
|
|
912
|
+
FFF_SCORE_STRUCT
|
|
913
|
+
);
|
|
886
914
|
scores.push(readScoreFromRaw(rawScore));
|
|
887
915
|
}
|
|
888
916
|
load({
|
|
@@ -912,13 +940,21 @@ function ffiSearch(handle, query, currentFile, maxThreads, pageIndex, pageSize,
|
|
|
912
940
|
retType: DataType.External,
|
|
913
941
|
paramsType: [
|
|
914
942
|
DataType.External,
|
|
943
|
+
// handle
|
|
915
944
|
DataType.String,
|
|
945
|
+
// query
|
|
916
946
|
DataType.String,
|
|
947
|
+
// current_file
|
|
917
948
|
DataType.U32,
|
|
949
|
+
// max_threads
|
|
918
950
|
DataType.U32,
|
|
951
|
+
// page_index
|
|
919
952
|
DataType.U32,
|
|
953
|
+
// page_size
|
|
920
954
|
DataType.I32,
|
|
955
|
+
// combo_boost_multiplier
|
|
921
956
|
DataType.U32
|
|
957
|
+
// min_combo_count
|
|
922
958
|
],
|
|
923
959
|
paramsValue: [
|
|
924
960
|
handle,
|
|
@@ -942,11 +978,17 @@ function ffiGlob(handle, pattern, currentFile, maxThreads, pageIndex, pageSize)
|
|
|
942
978
|
retType: DataType.External,
|
|
943
979
|
paramsType: [
|
|
944
980
|
DataType.External,
|
|
981
|
+
// handle
|
|
945
982
|
DataType.String,
|
|
983
|
+
// pattern
|
|
946
984
|
DataType.String,
|
|
985
|
+
// current_file
|
|
947
986
|
DataType.U32,
|
|
987
|
+
// max_threads
|
|
948
988
|
DataType.U32,
|
|
989
|
+
// page_index
|
|
949
990
|
DataType.U32
|
|
991
|
+
// page_size
|
|
950
992
|
],
|
|
951
993
|
paramsValue: [handle, pattern, currentFile, maxThreads, pageIndex, pageSize],
|
|
952
994
|
freeResultMemory: false
|
|
@@ -961,11 +1003,17 @@ function ffiSearchDirectories(handle, query, currentFile, maxThreads, pageIndex,
|
|
|
961
1003
|
retType: DataType.External,
|
|
962
1004
|
paramsType: [
|
|
963
1005
|
DataType.External,
|
|
1006
|
+
// handle
|
|
964
1007
|
DataType.String,
|
|
1008
|
+
// query
|
|
965
1009
|
DataType.String,
|
|
1010
|
+
// current_file
|
|
966
1011
|
DataType.U32,
|
|
1012
|
+
// max_threads
|
|
967
1013
|
DataType.U32,
|
|
1014
|
+
// page_index
|
|
968
1015
|
DataType.U32
|
|
1016
|
+
// page_size
|
|
969
1017
|
],
|
|
970
1018
|
paramsValue: [handle, query, currentFile ?? "", maxThreads, pageIndex, pageSize],
|
|
971
1019
|
freeResultMemory: false
|
|
@@ -980,13 +1028,21 @@ function ffiSearchMixed(handle, query, currentFile, maxThreads, pageIndex, pageS
|
|
|
980
1028
|
retType: DataType.External,
|
|
981
1029
|
paramsType: [
|
|
982
1030
|
DataType.External,
|
|
1031
|
+
// handle
|
|
983
1032
|
DataType.String,
|
|
1033
|
+
// query
|
|
984
1034
|
DataType.String,
|
|
1035
|
+
// current_file
|
|
985
1036
|
DataType.U32,
|
|
1037
|
+
// max_threads
|
|
986
1038
|
DataType.U32,
|
|
1039
|
+
// page_index
|
|
987
1040
|
DataType.U32,
|
|
1041
|
+
// page_size
|
|
988
1042
|
DataType.I32,
|
|
1043
|
+
// combo_boost_multiplier
|
|
989
1044
|
DataType.U32
|
|
1045
|
+
// min_combo_count
|
|
990
1046
|
],
|
|
991
1047
|
paramsValue: [
|
|
992
1048
|
handle,
|
|
@@ -1010,17 +1066,29 @@ function ffiLiveGrep(handle, query, mode, maxFileSize, maxMatchesPerFile, smartC
|
|
|
1010
1066
|
retType: DataType.External,
|
|
1011
1067
|
paramsType: [
|
|
1012
1068
|
DataType.External,
|
|
1069
|
+
// handle
|
|
1013
1070
|
DataType.String,
|
|
1071
|
+
// query
|
|
1014
1072
|
DataType.U8,
|
|
1073
|
+
// mode
|
|
1015
1074
|
DataType.U64,
|
|
1075
|
+
// max_file_size
|
|
1016
1076
|
DataType.U32,
|
|
1077
|
+
// max_matches_per_file
|
|
1017
1078
|
DataType.Boolean,
|
|
1079
|
+
// smart_case
|
|
1018
1080
|
DataType.U32,
|
|
1081
|
+
// file_offset
|
|
1019
1082
|
DataType.U32,
|
|
1083
|
+
// page_limit
|
|
1020
1084
|
DataType.U64,
|
|
1085
|
+
// time_budget_ms
|
|
1021
1086
|
DataType.U32,
|
|
1087
|
+
// before_context
|
|
1022
1088
|
DataType.U32,
|
|
1089
|
+
// after_context
|
|
1023
1090
|
DataType.Boolean
|
|
1091
|
+
// classify_definitions
|
|
1024
1092
|
],
|
|
1025
1093
|
paramsValue: [
|
|
1026
1094
|
handle,
|
|
@@ -1048,17 +1116,29 @@ function ffiMultiGrep(handle, patternsJoined, constraints, maxFileSize, maxMatch
|
|
|
1048
1116
|
retType: DataType.External,
|
|
1049
1117
|
paramsType: [
|
|
1050
1118
|
DataType.External,
|
|
1119
|
+
// handle
|
|
1051
1120
|
DataType.String,
|
|
1121
|
+
// patterns_joined
|
|
1052
1122
|
DataType.String,
|
|
1123
|
+
// constraints
|
|
1053
1124
|
DataType.U64,
|
|
1125
|
+
// max_file_size
|
|
1054
1126
|
DataType.U32,
|
|
1127
|
+
// max_matches_per_file
|
|
1055
1128
|
DataType.Boolean,
|
|
1129
|
+
// smart_case
|
|
1056
1130
|
DataType.U32,
|
|
1131
|
+
// file_offset
|
|
1057
1132
|
DataType.U32,
|
|
1133
|
+
// page_limit
|
|
1058
1134
|
DataType.U64,
|
|
1135
|
+
// time_budget_ms
|
|
1059
1136
|
DataType.U32,
|
|
1137
|
+
// before_context
|
|
1060
1138
|
DataType.U32,
|
|
1139
|
+
// after_context
|
|
1061
1140
|
DataType.Boolean
|
|
1141
|
+
// classify_definitions
|
|
1062
1142
|
],
|
|
1063
1143
|
paramsValue: [
|
|
1064
1144
|
handle,
|
|
@@ -1103,12 +1183,10 @@ var FFF_SCAN_PROGRESS_STRUCT = {
|
|
|
1103
1183
|
function ffiGetScanProgress(handle) {
|
|
1104
1184
|
loadLibrary();
|
|
1105
1185
|
const res = readResultEnvelope("fff_get_scan_progress", [DataType.External], [handle]);
|
|
1106
|
-
if ("ok" in res)
|
|
1107
|
-
return res;
|
|
1186
|
+
if ("ok" in res) return res;
|
|
1108
1187
|
const handlePtr = res.struct.handle;
|
|
1109
1188
|
freeResult(res.rawPtr);
|
|
1110
|
-
if (isNullPointer(handlePtr))
|
|
1111
|
-
return err("scan progress returned null");
|
|
1189
|
+
if (isNullPointer(handlePtr)) return err("scan progress returned null");
|
|
1112
1190
|
const [sp] = restorePointer({
|
|
1113
1191
|
retType: [FFF_SCAN_PROGRESS_STRUCT],
|
|
1114
1192
|
paramsValue: wrapPointer([handlePtr])
|
|
@@ -1129,19 +1207,35 @@ function ffiGetScanProgress(handle) {
|
|
|
1129
1207
|
return { ok: true, value: result };
|
|
1130
1208
|
}
|
|
1131
1209
|
function ffiWaitForScan(handle, timeoutMs) {
|
|
1132
|
-
return callBoolResult(
|
|
1210
|
+
return callBoolResult(
|
|
1211
|
+
"fff_wait_for_scan",
|
|
1212
|
+
[DataType.External, DataType.U64],
|
|
1213
|
+
[handle, timeoutMs]
|
|
1214
|
+
);
|
|
1133
1215
|
}
|
|
1134
1216
|
function ffiRestartIndex(handle, newPath) {
|
|
1135
|
-
return callVoidResult(
|
|
1217
|
+
return callVoidResult(
|
|
1218
|
+
"fff_restart_index",
|
|
1219
|
+
[DataType.External, DataType.String],
|
|
1220
|
+
[handle, newPath]
|
|
1221
|
+
);
|
|
1136
1222
|
}
|
|
1137
1223
|
function ffiRefreshGitStatus(handle) {
|
|
1138
1224
|
return callIntResult("fff_refresh_git_status", [DataType.External], [handle]);
|
|
1139
1225
|
}
|
|
1140
1226
|
function ffiTrackQuery(handle, query, filePath) {
|
|
1141
|
-
return callBoolResult(
|
|
1227
|
+
return callBoolResult(
|
|
1228
|
+
"fff_track_query",
|
|
1229
|
+
[DataType.External, DataType.String, DataType.String],
|
|
1230
|
+
[handle, query, filePath]
|
|
1231
|
+
);
|
|
1142
1232
|
}
|
|
1143
1233
|
function ffiGetHistoricalQuery(handle, offset) {
|
|
1144
|
-
return callStringResult(
|
|
1234
|
+
return callStringResult(
|
|
1235
|
+
"fff_get_historical_query",
|
|
1236
|
+
[DataType.External, DataType.U64],
|
|
1237
|
+
[handle, offset]
|
|
1238
|
+
);
|
|
1145
1239
|
}
|
|
1146
1240
|
function watchKindFromU8(kind) {
|
|
1147
1241
|
switch (kind) {
|
|
@@ -1159,8 +1253,8 @@ var WATCH_TRAMPOLINE_TYPE = funcConstructor({
|
|
|
1159
1253
|
paramsType: [DataType.U64, DataType.U64, DataType.U64],
|
|
1160
1254
|
retType: DataType.Void
|
|
1161
1255
|
});
|
|
1162
|
-
var watchHandlers = new Map;
|
|
1163
|
-
var watchInstances = new Set;
|
|
1256
|
+
var watchHandlers = /* @__PURE__ */ new Map();
|
|
1257
|
+
var watchInstances = /* @__PURE__ */ new Set();
|
|
1164
1258
|
var watchTrampoline = null;
|
|
1165
1259
|
function addressToExternal(address) {
|
|
1166
1260
|
return load({
|
|
@@ -1181,7 +1275,7 @@ function consumeWatchBatch(address) {
|
|
|
1181
1275
|
paramsValue: [batchPtr]
|
|
1182
1276
|
});
|
|
1183
1277
|
const events = [];
|
|
1184
|
-
for (let i = 0;i < count; i++) {
|
|
1278
|
+
for (let i = 0; i < count; i++) {
|
|
1185
1279
|
const path = load({
|
|
1186
1280
|
library: LIBRARY_KEY,
|
|
1187
1281
|
funcName: "fff_watch_events_get_path",
|
|
@@ -1213,11 +1307,11 @@ function consumeWatchBatch(address) {
|
|
|
1213
1307
|
function watchTrampolineImpl(watchId, batchAddress, _userData) {
|
|
1214
1308
|
const events = consumeWatchBatch(batchAddress);
|
|
1215
1309
|
const handler = watchHandlers.get(Number(watchId));
|
|
1216
|
-
if (handler ===
|
|
1217
|
-
return;
|
|
1310
|
+
if (handler === void 0 || events.length === 0) return;
|
|
1218
1311
|
try {
|
|
1219
1312
|
handler(events);
|
|
1220
|
-
} catch {
|
|
1313
|
+
} catch {
|
|
1314
|
+
}
|
|
1221
1315
|
}
|
|
1222
1316
|
function ensureWatchTrampoline() {
|
|
1223
1317
|
if (watchTrampoline === null) {
|
|
@@ -1229,12 +1323,14 @@ function ensureWatchTrampoline() {
|
|
|
1229
1323
|
return unwrapPointer(watchTrampoline)[0];
|
|
1230
1324
|
}
|
|
1231
1325
|
function ensureWatchCallbackRegistered(handle) {
|
|
1232
|
-
if (watchInstances.has(handle))
|
|
1233
|
-
return { ok: true, value: undefined };
|
|
1326
|
+
if (watchInstances.has(handle)) return { ok: true, value: void 0 };
|
|
1234
1327
|
const trampoline = ensureWatchTrampoline();
|
|
1235
|
-
const registered = callVoidResult(
|
|
1236
|
-
|
|
1237
|
-
|
|
1328
|
+
const registered = callVoidResult(
|
|
1329
|
+
"fff_set_watch_callback",
|
|
1330
|
+
[DataType.External, DataType.External, DataType.U64],
|
|
1331
|
+
[handle, trampoline, 0]
|
|
1332
|
+
);
|
|
1333
|
+
if (registered.ok) watchInstances.add(handle);
|
|
1238
1334
|
return registered;
|
|
1239
1335
|
}
|
|
1240
1336
|
function releaseWatchTrampolineIfIdle() {
|
|
@@ -1250,16 +1346,22 @@ function releaseWatchTrampolineIfIdle() {
|
|
|
1250
1346
|
function ffiWatch(handle, pattern, ignore, callback) {
|
|
1251
1347
|
loadLibrary();
|
|
1252
1348
|
const registered = ensureWatchCallbackRegistered(handle);
|
|
1253
|
-
if (!registered.ok)
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1349
|
+
if (!registered.ok) return registered;
|
|
1350
|
+
const created = callIntResult(
|
|
1351
|
+
"fff_watch_args",
|
|
1352
|
+
[DataType.External, DataType.String, DataType.StringArray, DataType.U32],
|
|
1353
|
+
[handle, pattern, ignore, ignore.length]
|
|
1354
|
+
);
|
|
1355
|
+
if (!created.ok) return created;
|
|
1258
1356
|
watchHandlers.set(created.value, callback);
|
|
1259
1357
|
return created;
|
|
1260
1358
|
}
|
|
1261
1359
|
function ffiUnwatch(handle, watchId) {
|
|
1262
|
-
const result = callBoolResult(
|
|
1360
|
+
const result = callBoolResult(
|
|
1361
|
+
"fff_unwatch",
|
|
1362
|
+
[DataType.External, DataType.U64],
|
|
1363
|
+
[handle, watchId]
|
|
1364
|
+
);
|
|
1263
1365
|
watchHandlers.delete(watchId);
|
|
1264
1366
|
return result;
|
|
1265
1367
|
}
|
|
@@ -1272,9 +1374,17 @@ function ffiWatchCleanupAfterDestroy(handle, watchIds) {
|
|
|
1272
1374
|
}
|
|
1273
1375
|
function ffiHealthCheck(handle, testPath) {
|
|
1274
1376
|
if (handle === null) {
|
|
1275
|
-
return callJsonResult(
|
|
1276
|
-
|
|
1277
|
-
|
|
1377
|
+
return callJsonResult(
|
|
1378
|
+
"fff_health_check",
|
|
1379
|
+
[DataType.U64, DataType.String],
|
|
1380
|
+
[0, testPath]
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
return callJsonResult(
|
|
1384
|
+
"fff_health_check",
|
|
1385
|
+
[DataType.External, DataType.String],
|
|
1386
|
+
[handle, testPath]
|
|
1387
|
+
);
|
|
1278
1388
|
}
|
|
1279
1389
|
function ensureLoaded() {
|
|
1280
1390
|
loadLibrary();
|
|
@@ -1293,20 +1403,65 @@ function closeLibrary() {
|
|
|
1293
1403
|
isLoaded = false;
|
|
1294
1404
|
}
|
|
1295
1405
|
}
|
|
1406
|
+
|
|
1296
1407
|
// src/finder.ts
|
|
1297
|
-
|
|
1408
|
+
var FileFinder = class _FileFinder {
|
|
1298
1409
|
handle;
|
|
1299
|
-
|
|
1410
|
+
/** Native ids of this instance's active watch subscriptions. */
|
|
1411
|
+
watchers = /* @__PURE__ */ new Set();
|
|
1300
1412
|
constructor(handle) {
|
|
1301
1413
|
this.handle = handle;
|
|
1302
1414
|
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Create a new file finder instance.
|
|
1417
|
+
*
|
|
1418
|
+
* @param options - Initialization options
|
|
1419
|
+
* @returns Result containing the new FileFinder instance or an error
|
|
1420
|
+
*
|
|
1421
|
+
* @example
|
|
1422
|
+
* ```typescript
|
|
1423
|
+
* // Basic initialization
|
|
1424
|
+
* const finder = FileFinder.create({ basePath: "/path/to/project" });
|
|
1425
|
+
*
|
|
1426
|
+
* // With custom database paths
|
|
1427
|
+
* const finder = FileFinder.create({
|
|
1428
|
+
* basePath: "/path/to/project",
|
|
1429
|
+
* frecencyDbPath: "/custom/frecency.mdb",
|
|
1430
|
+
* historyDbPath: "/custom/history.mdb",
|
|
1431
|
+
* });
|
|
1432
|
+
* ```
|
|
1433
|
+
*/
|
|
1303
1434
|
static create(options) {
|
|
1304
|
-
const result = ffiCreate(
|
|
1435
|
+
const result = ffiCreate(
|
|
1436
|
+
options.basePath,
|
|
1437
|
+
options.frecencyDbPath ?? "",
|
|
1438
|
+
options.historyDbPath ?? "",
|
|
1439
|
+
options.useUnsafeNoLock ?? false,
|
|
1440
|
+
!(options.disableMmapCache ?? false),
|
|
1441
|
+
!(options.disableContentIndexing ?? options.disableMmapCache ?? false),
|
|
1442
|
+
!(options.disableWatch ?? false),
|
|
1443
|
+
options.aiMode ?? false,
|
|
1444
|
+
options.logFilePath ?? "",
|
|
1445
|
+
options.logLevel ?? "",
|
|
1446
|
+
options.cacheBudgetMaxFiles ?? 0,
|
|
1447
|
+
options.cacheBudgetMaxBytes ?? 0,
|
|
1448
|
+
options.cacheBudgetMaxFileSize ?? 0,
|
|
1449
|
+
options.enableFsRootScanning ?? false,
|
|
1450
|
+
options.enableHomeDirScanning ?? false,
|
|
1451
|
+
options.followSymlinks ?? false
|
|
1452
|
+
);
|
|
1305
1453
|
if (!result.ok) {
|
|
1306
1454
|
return result;
|
|
1307
1455
|
}
|
|
1308
|
-
return { ok: true, value: new
|
|
1309
|
-
}
|
|
1456
|
+
return { ok: true, value: new _FileFinder(result.value) };
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Destroy and clean up all resources.
|
|
1460
|
+
*
|
|
1461
|
+
* Call this when you're done using the file finder to free memory
|
|
1462
|
+
* and stop background file watching. After calling this, the instance
|
|
1463
|
+
* must not be used again.
|
|
1464
|
+
*/
|
|
1310
1465
|
destroy() {
|
|
1311
1466
|
if (this.handle !== null) {
|
|
1312
1467
|
const handle = this.handle;
|
|
@@ -1316,82 +1471,314 @@ class FileFinder {
|
|
|
1316
1471
|
this.watchers.clear();
|
|
1317
1472
|
}
|
|
1318
1473
|
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Check if this instance has been destroyed.
|
|
1476
|
+
*/
|
|
1319
1477
|
get isDestroyed() {
|
|
1320
1478
|
return this.handle === null;
|
|
1321
1479
|
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Guard that returns an error if the instance has been destroyed.
|
|
1482
|
+
*/
|
|
1322
1483
|
ensureAlive() {
|
|
1323
1484
|
if (this.handle === null) {
|
|
1324
1485
|
return err("FileFinder instance has been destroyed.");
|
|
1325
1486
|
}
|
|
1326
1487
|
return { ok: true, value: this.handle };
|
|
1327
1488
|
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Search for files matching the query.
|
|
1491
|
+
*
|
|
1492
|
+
* The query supports fuzzy matching and special syntax:
|
|
1493
|
+
* - `foo bar` - Match files containing "foo" and "bar"
|
|
1494
|
+
* - `src/` - Match files in src directory
|
|
1495
|
+
* - `file.ts:42` - Match file.ts with line 42
|
|
1496
|
+
* - `file.ts:42:10` - Match file.ts with line 42, column 10
|
|
1497
|
+
*
|
|
1498
|
+
* @param query - Search query string
|
|
1499
|
+
* @param options - Search options
|
|
1500
|
+
* @returns Search results with matched files and scores
|
|
1501
|
+
*
|
|
1502
|
+
* @example
|
|
1503
|
+
* ```typescript
|
|
1504
|
+
* const result = finder.search("main.ts", { pageSize: 10 });
|
|
1505
|
+
* if (result.ok) {
|
|
1506
|
+
* console.log(`Found ${result.value.totalMatched} files`);
|
|
1507
|
+
* for (const item of result.value.items) {
|
|
1508
|
+
* console.log(item.relativePath);
|
|
1509
|
+
* }
|
|
1510
|
+
* }
|
|
1511
|
+
* ```
|
|
1512
|
+
*/
|
|
1328
1513
|
fileSearch(query, options) {
|
|
1329
1514
|
const guard = this.ensureAlive();
|
|
1330
|
-
if (!guard.ok)
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1515
|
+
if (!guard.ok) return guard;
|
|
1516
|
+
return ffiSearch(
|
|
1517
|
+
guard.value,
|
|
1518
|
+
query,
|
|
1519
|
+
options?.currentFile ?? "",
|
|
1520
|
+
options?.maxThreads ?? 0,
|
|
1521
|
+
options?.pageIndex ?? 0,
|
|
1522
|
+
options?.pageSize ?? 0,
|
|
1523
|
+
options?.comboBoostMultiplier ?? 0,
|
|
1524
|
+
options?.minComboCount ?? 0
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Filters files using glob wildcard expression.
|
|
1529
|
+
*
|
|
1530
|
+
* The pattern is applied as a single pass SIMD optimized prefiltering
|
|
1531
|
+
* without any fuzzy matching involved. Faster and 100% compatible to npm `glob`.
|
|
1532
|
+
*
|
|
1533
|
+
* @param pattern - Glob pattern (required, non-empty)
|
|
1534
|
+
* @param options - Glob search options (pagination, max threads, current file)
|
|
1535
|
+
* @returns Search results with files matching the glob
|
|
1536
|
+
*
|
|
1537
|
+
* @example
|
|
1538
|
+
* ```typescript
|
|
1539
|
+
* const result = finder.glob("**\/*.rs", { pageSize: 100 });
|
|
1540
|
+
* if (result.ok) {
|
|
1541
|
+
* for (const item of result.value.items) {
|
|
1542
|
+
* console.log(item.relativePath);
|
|
1543
|
+
* }
|
|
1544
|
+
* }
|
|
1545
|
+
* ```
|
|
1546
|
+
*/
|
|
1334
1547
|
glob(pattern, options) {
|
|
1335
1548
|
const guard = this.ensureAlive();
|
|
1336
|
-
if (!guard.ok)
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1549
|
+
if (!guard.ok) return guard;
|
|
1550
|
+
return ffiGlob(
|
|
1551
|
+
guard.value,
|
|
1552
|
+
pattern,
|
|
1553
|
+
options?.currentFile ?? "",
|
|
1554
|
+
options?.maxThreads ?? 0,
|
|
1555
|
+
options?.pageIndex ?? 0,
|
|
1556
|
+
options?.pageSize ?? 0
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Search for directories matching the query.
|
|
1561
|
+
*
|
|
1562
|
+
* @param query - Search query string
|
|
1563
|
+
* @param options - Directory search options
|
|
1564
|
+
* @returns Search results with matched directories and scores
|
|
1565
|
+
*
|
|
1566
|
+
* @example
|
|
1567
|
+
* ```typescript
|
|
1568
|
+
* const result = finder.directorySearch("src/comp", { pageSize: 10 });
|
|
1569
|
+
* if (result.ok) {
|
|
1570
|
+
* console.log(`Found ${result.value.totalMatched} directories`);
|
|
1571
|
+
* for (const item of result.value.items) {
|
|
1572
|
+
* console.log(item.relativePath);
|
|
1573
|
+
* }
|
|
1574
|
+
* }
|
|
1575
|
+
* ```
|
|
1576
|
+
*/
|
|
1340
1577
|
directorySearch(query, options) {
|
|
1341
1578
|
const guard = this.ensureAlive();
|
|
1342
|
-
if (!guard.ok)
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1579
|
+
if (!guard.ok) return guard;
|
|
1580
|
+
return ffiSearchDirectories(
|
|
1581
|
+
guard.value,
|
|
1582
|
+
query,
|
|
1583
|
+
options?.currentFile ?? null,
|
|
1584
|
+
options?.maxThreads ?? 0,
|
|
1585
|
+
options?.pageIndex ?? 0,
|
|
1586
|
+
options?.pageSize ?? 0
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Search for files and directories matching the query (mixed results).
|
|
1591
|
+
*
|
|
1592
|
+
* Results are interleaved by total score in descending order, mixing
|
|
1593
|
+
* both file and directory items.
|
|
1594
|
+
*
|
|
1595
|
+
* @param query - Search query string
|
|
1596
|
+
* @param options - Search options
|
|
1597
|
+
* @returns Mixed search results with files and directories interleaved by score
|
|
1598
|
+
*
|
|
1599
|
+
* @example
|
|
1600
|
+
* ```typescript
|
|
1601
|
+
* const result = finder.mixedSearch("main", { pageSize: 20 });
|
|
1602
|
+
* if (result.ok) {
|
|
1603
|
+
* for (const entry of result.value.items) {
|
|
1604
|
+
* if (entry.type === "file") {
|
|
1605
|
+
* console.log(`File: ${entry.item.relativePath}`);
|
|
1606
|
+
* } else {
|
|
1607
|
+
* console.log(`Dir: ${entry.item.relativePath}`);
|
|
1608
|
+
* }
|
|
1609
|
+
* }
|
|
1610
|
+
* }
|
|
1611
|
+
* ```
|
|
1612
|
+
*/
|
|
1346
1613
|
mixedSearch(query, options) {
|
|
1347
1614
|
const guard = this.ensureAlive();
|
|
1348
|
-
if (!guard.ok)
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1615
|
+
if (!guard.ok) return guard;
|
|
1616
|
+
return ffiSearchMixed(
|
|
1617
|
+
guard.value,
|
|
1618
|
+
query,
|
|
1619
|
+
options?.currentFile ?? "",
|
|
1620
|
+
options?.maxThreads ?? 0,
|
|
1621
|
+
options?.pageIndex ?? 0,
|
|
1622
|
+
options?.pageSize ?? 0,
|
|
1623
|
+
options?.comboBoostMultiplier ?? 0,
|
|
1624
|
+
options?.minComboCount ?? 0
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Search file contents (live grep).
|
|
1629
|
+
*
|
|
1630
|
+
* Searches through the contents of indexed files using the specified mode:
|
|
1631
|
+
* - `"plain"` (default): SIMD-accelerated literal text matching
|
|
1632
|
+
* - `"regex"`: Regular expression matching
|
|
1633
|
+
* - `"fuzzy"`: Smith-Waterman fuzzy matching per line
|
|
1634
|
+
*
|
|
1635
|
+
* Supports pagination for large result sets. The result includes a `nextCursor`
|
|
1636
|
+
* that can be passed back to fetch the next page.
|
|
1637
|
+
*
|
|
1638
|
+
* The query also supports constraint syntax:
|
|
1639
|
+
* - `*.ts pattern` - Only search in TypeScript files
|
|
1640
|
+
* - `src/ pattern` - Only search in the src directory
|
|
1641
|
+
*
|
|
1642
|
+
* @param query - Search query string
|
|
1643
|
+
* @param options - Grep options (mode, pagination, limits)
|
|
1644
|
+
* @returns Grep results with matched lines and file metadata
|
|
1645
|
+
*
|
|
1646
|
+
* @example
|
|
1647
|
+
* ```typescript
|
|
1648
|
+
* // First page
|
|
1649
|
+
* const result = finder.grep("TODO", { mode: "plain" });
|
|
1650
|
+
* if (result.ok) {
|
|
1651
|
+
* for (const match of result.value.items) {
|
|
1652
|
+
* console.log(`${match.relativePath}:${match.lineNumber}: ${match.lineContent}`);
|
|
1653
|
+
* }
|
|
1654
|
+
* // Fetch next page
|
|
1655
|
+
* if (result.value.nextCursor) {
|
|
1656
|
+
* const page2 = finder.grep("TODO", {
|
|
1657
|
+
* cursor: result.value.nextCursor,
|
|
1658
|
+
* });
|
|
1659
|
+
* }
|
|
1660
|
+
* }
|
|
1661
|
+
* ```
|
|
1662
|
+
*/
|
|
1352
1663
|
grep(query, options) {
|
|
1353
1664
|
const guard = this.ensureAlive();
|
|
1354
|
-
if (!guard.ok)
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1665
|
+
if (!guard.ok) return guard;
|
|
1666
|
+
return ffiLiveGrep(
|
|
1667
|
+
guard.value,
|
|
1668
|
+
query,
|
|
1669
|
+
options?.mode ?? "plain",
|
|
1670
|
+
options?.maxFileSize ?? 0,
|
|
1671
|
+
options?.maxMatchesPerFile ?? 0,
|
|
1672
|
+
options?.smartCase ?? true,
|
|
1673
|
+
options?.cursor?._offset ?? 0,
|
|
1674
|
+
options?.pageSize ?? 0,
|
|
1675
|
+
options?.timeBudgetMs ?? 0,
|
|
1676
|
+
options?.beforeContext ?? 0,
|
|
1677
|
+
options?.afterContext ?? 0,
|
|
1678
|
+
options?.classifyDefinitions ?? false
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Multi-pattern OR search using Aho-Corasick.
|
|
1683
|
+
*
|
|
1684
|
+
* Searches for lines matching ANY of the provided patterns using
|
|
1685
|
+
* SIMD-accelerated multi-needle matching. Faster than regex alternation
|
|
1686
|
+
* for literal text searches.
|
|
1687
|
+
*
|
|
1688
|
+
* Supports pagination. The result includes a `nextCursor` that can be
|
|
1689
|
+
* passed back to fetch the next page.
|
|
1690
|
+
*
|
|
1691
|
+
* @param options - Multi-grep options including patterns and optional constraints
|
|
1692
|
+
* @returns Grep results with matched lines and file metadata
|
|
1693
|
+
*
|
|
1694
|
+
* @example
|
|
1695
|
+
* ```typescript
|
|
1696
|
+
* const result = finder.multiGrep({
|
|
1697
|
+
* patterns: ["VideoFrame", "video_frame", "PreloadedImage"],
|
|
1698
|
+
* });
|
|
1699
|
+
* if (result.ok) {
|
|
1700
|
+
* for (const match of result.value.items) {
|
|
1701
|
+
* console.log(`${match.relativePath}:${match.lineNumber}: ${match.lineContent}`);
|
|
1702
|
+
* }
|
|
1703
|
+
* }
|
|
1704
|
+
* ```
|
|
1705
|
+
*/
|
|
1358
1706
|
multiGrep(options) {
|
|
1359
1707
|
const guard = this.ensureAlive();
|
|
1360
|
-
if (!guard.ok)
|
|
1361
|
-
return guard;
|
|
1708
|
+
if (!guard.ok) return guard;
|
|
1362
1709
|
if (!options.patterns || options.patterns.length === 0) {
|
|
1363
1710
|
return err("patterns array must have at least 1 element");
|
|
1364
1711
|
}
|
|
1365
|
-
return ffiMultiGrep(
|
|
1366
|
-
|
|
1367
|
-
|
|
1712
|
+
return ffiMultiGrep(
|
|
1713
|
+
guard.value,
|
|
1714
|
+
options.patterns.join("\n"),
|
|
1715
|
+
options.constraints ?? "",
|
|
1716
|
+
options.maxFileSize ?? 0,
|
|
1717
|
+
options.maxMatchesPerFile ?? 0,
|
|
1718
|
+
options.smartCase ?? true,
|
|
1719
|
+
options.cursor?._offset ?? 0,
|
|
1720
|
+
options.pageSize ?? 0,
|
|
1721
|
+
options.timeBudgetMs ?? 0,
|
|
1722
|
+
options.beforeContext ?? 0,
|
|
1723
|
+
options.afterContext ?? 0,
|
|
1724
|
+
options.classifyDefinitions ?? false
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Trigger a rescan of the indexed directory.
|
|
1729
|
+
*
|
|
1730
|
+
* This is useful after major file system changes that the
|
|
1731
|
+
* background watcher might have missed.
|
|
1732
|
+
*/
|
|
1368
1733
|
scanFiles() {
|
|
1369
1734
|
const guard = this.ensureAlive();
|
|
1370
|
-
if (!guard.ok)
|
|
1371
|
-
return guard;
|
|
1735
|
+
if (!guard.ok) return guard;
|
|
1372
1736
|
return ffiScanFiles(guard.value);
|
|
1373
1737
|
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Check if a scan is currently in progress.
|
|
1740
|
+
*/
|
|
1374
1741
|
isScanning() {
|
|
1375
|
-
if (this.handle === null)
|
|
1376
|
-
return false;
|
|
1742
|
+
if (this.handle === null) return false;
|
|
1377
1743
|
return ffiIsScanning(this.handle);
|
|
1378
1744
|
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Get the base path of the file picker (the root directory being indexed).
|
|
1747
|
+
*/
|
|
1379
1748
|
getBasePath() {
|
|
1380
1749
|
const guard = this.ensureAlive();
|
|
1381
|
-
if (!guard.ok)
|
|
1382
|
-
return guard;
|
|
1750
|
+
if (!guard.ok) return guard;
|
|
1383
1751
|
return ffiGetBasePath(guard.value);
|
|
1384
1752
|
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Get the current scan progress.
|
|
1755
|
+
*/
|
|
1385
1756
|
getScanProgress() {
|
|
1386
1757
|
const guard = this.ensureAlive();
|
|
1387
|
-
if (!guard.ok)
|
|
1388
|
-
return guard;
|
|
1758
|
+
if (!guard.ok) return guard;
|
|
1389
1759
|
return ffiGetScanProgress(guard.value);
|
|
1390
1760
|
}
|
|
1391
|
-
|
|
1761
|
+
/**
|
|
1762
|
+
* Wait for the initial file scan to complete.
|
|
1763
|
+
* Non-blocking: polls `isScanning` and yields to the event loop between checks.
|
|
1764
|
+
*
|
|
1765
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)
|
|
1766
|
+
* @returns true if scan completed, false if timed out
|
|
1767
|
+
*
|
|
1768
|
+
* @example
|
|
1769
|
+
* ```typescript
|
|
1770
|
+
* const finder = FileFinder.create({ basePath: "/path/to/project" });
|
|
1771
|
+
* if (finder.ok) {
|
|
1772
|
+
* const completed = await finder.value.waitForScan(10000);
|
|
1773
|
+
* if (!completed.ok || !completed.value) {
|
|
1774
|
+
* console.warn("Scan did not complete in time");
|
|
1775
|
+
* }
|
|
1776
|
+
* }
|
|
1777
|
+
* ```
|
|
1778
|
+
*/
|
|
1779
|
+
async waitForScan(timeoutMs = 5e3) {
|
|
1392
1780
|
const guard = this.ensureAlive();
|
|
1393
|
-
if (!guard.ok)
|
|
1394
|
-
return guard;
|
|
1781
|
+
if (!guard.ok) return guard;
|
|
1395
1782
|
const deadline = Date.now() + timeoutMs;
|
|
1396
1783
|
while (this.isScanning()) {
|
|
1397
1784
|
if (Date.now() >= deadline) {
|
|
@@ -1401,21 +1788,36 @@ class FileFinder {
|
|
|
1401
1788
|
}
|
|
1402
1789
|
return { ok: true, value: true };
|
|
1403
1790
|
}
|
|
1404
|
-
|
|
1791
|
+
/**
|
|
1792
|
+
* Wait for the initial file scan to complete, blocking the calling thread.
|
|
1793
|
+
*
|
|
1794
|
+
* Backed by the native `fff_wait_for_scan` call. Prefer {@link waitForScan}
|
|
1795
|
+
* unless you specifically need synchronous blocking behaviour.
|
|
1796
|
+
*
|
|
1797
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)
|
|
1798
|
+
* @returns true if scan completed, false if timed out
|
|
1799
|
+
*/
|
|
1800
|
+
waitForScanBlocking(timeoutMs = 5e3) {
|
|
1405
1801
|
const guard = this.ensureAlive();
|
|
1406
|
-
if (!guard.ok)
|
|
1407
|
-
return guard;
|
|
1802
|
+
if (!guard.ok) return guard;
|
|
1408
1803
|
return ffiWaitForScan(guard.value, timeoutMs);
|
|
1409
1804
|
}
|
|
1410
|
-
|
|
1805
|
+
/**
|
|
1806
|
+
* Wait until the index is fully ready: the scan has finished and the warmup
|
|
1807
|
+
* (content indexing / bigram) phase has completed.
|
|
1808
|
+
*
|
|
1809
|
+
* Non-blocking: polls `getScanProgress` and yields to the event loop.
|
|
1810
|
+
*
|
|
1811
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)
|
|
1812
|
+
* @returns true if the index became ready, false if timed out
|
|
1813
|
+
*/
|
|
1814
|
+
async waitForIndexReady(timeoutMs = 5e3) {
|
|
1411
1815
|
const guard = this.ensureAlive();
|
|
1412
|
-
if (!guard.ok)
|
|
1413
|
-
return guard;
|
|
1816
|
+
if (!guard.ok) return guard;
|
|
1414
1817
|
const deadline = Date.now() + timeoutMs;
|
|
1415
1818
|
while (true) {
|
|
1416
1819
|
const progress = this.getScanProgress();
|
|
1417
|
-
if (!progress.ok)
|
|
1418
|
-
return progress;
|
|
1820
|
+
if (!progress.ok) return progress;
|
|
1419
1821
|
if (!progress.value.isScanning && progress.value.isWarmupComplete) {
|
|
1420
1822
|
return { ok: true, value: true };
|
|
1421
1823
|
}
|
|
@@ -1425,28 +1827,51 @@ class FileFinder {
|
|
|
1425
1827
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1426
1828
|
}
|
|
1427
1829
|
}
|
|
1830
|
+
/**
|
|
1831
|
+
* Change the indexed directory to a new path.
|
|
1832
|
+
*
|
|
1833
|
+
* This stops the current file watcher and starts indexing the new directory.
|
|
1834
|
+
*
|
|
1835
|
+
* @param newPath - New directory path to index
|
|
1836
|
+
*/
|
|
1428
1837
|
reindex(newPath) {
|
|
1429
1838
|
const guard = this.ensureAlive();
|
|
1430
|
-
if (!guard.ok)
|
|
1431
|
-
return guard;
|
|
1839
|
+
if (!guard.ok) return guard;
|
|
1432
1840
|
return ffiRestartIndex(guard.value, newPath);
|
|
1433
1841
|
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Refresh the git status cache.
|
|
1844
|
+
*
|
|
1845
|
+
* @returns Number of files with updated git status
|
|
1846
|
+
*/
|
|
1434
1847
|
refreshGitStatus() {
|
|
1435
1848
|
const guard = this.ensureAlive();
|
|
1436
|
-
if (!guard.ok)
|
|
1437
|
-
return guard;
|
|
1849
|
+
if (!guard.ok) return guard;
|
|
1438
1850
|
return ffiRefreshGitStatus(guard.value);
|
|
1439
1851
|
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Track query completion for smart suggestions.
|
|
1854
|
+
*
|
|
1855
|
+
* Call this when a user selects a file from search results.
|
|
1856
|
+
* This helps improve future search rankings for similar queries.
|
|
1857
|
+
*
|
|
1858
|
+
* @param query - The search query that was used
|
|
1859
|
+
* @param selectedFilePath - The file path that was selected
|
|
1860
|
+
*/
|
|
1440
1861
|
trackQuery(query, selectedFilePath) {
|
|
1441
1862
|
const guard = this.ensureAlive();
|
|
1442
|
-
if (!guard.ok)
|
|
1443
|
-
return guard;
|
|
1863
|
+
if (!guard.ok) return guard;
|
|
1444
1864
|
return ffiTrackQuery(guard.value, query, selectedFilePath);
|
|
1445
1865
|
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Get a historical query by offset.
|
|
1868
|
+
*
|
|
1869
|
+
* @param offset - Offset from most recent (0 = most recent)
|
|
1870
|
+
* @returns The historical query string, or null if not found
|
|
1871
|
+
*/
|
|
1446
1872
|
getHistoricalQuery(offset) {
|
|
1447
1873
|
const guard = this.ensureAlive();
|
|
1448
|
-
if (!guard.ok)
|
|
1449
|
-
return guard;
|
|
1874
|
+
if (!guard.ok) return guard;
|
|
1450
1875
|
return ffiGetHistoricalQuery(guard.value, offset);
|
|
1451
1876
|
}
|
|
1452
1877
|
watch(patternOrCallback, callbackOrOptions, maybeOptions) {
|
|
@@ -1458,47 +1883,65 @@ class FileFinder {
|
|
|
1458
1883
|
return err("watch callback must be a function");
|
|
1459
1884
|
}
|
|
1460
1885
|
const guard = this.ensureAlive();
|
|
1461
|
-
if (!guard.ok)
|
|
1462
|
-
return guard;
|
|
1886
|
+
if (!guard.ok) return guard;
|
|
1463
1887
|
const created = ffiWatch(guard.value, pattern, options?.ignore ?? [], callback);
|
|
1464
|
-
if (!created.ok)
|
|
1465
|
-
return created;
|
|
1888
|
+
if (!created.ok) return created;
|
|
1466
1889
|
const watchId = created.value;
|
|
1467
1890
|
this.watchers.add(watchId);
|
|
1468
1891
|
return {
|
|
1469
1892
|
ok: true,
|
|
1470
1893
|
value: () => {
|
|
1471
|
-
if (!this.watchers.delete(watchId))
|
|
1472
|
-
|
|
1473
|
-
if (this.handle !== null)
|
|
1474
|
-
ffiUnwatch(this.handle, watchId);
|
|
1894
|
+
if (!this.watchers.delete(watchId)) return;
|
|
1895
|
+
if (this.handle !== null) ffiUnwatch(this.handle, watchId);
|
|
1475
1896
|
}
|
|
1476
1897
|
};
|
|
1477
1898
|
}
|
|
1899
|
+
/**
|
|
1900
|
+
* Get health check information.
|
|
1901
|
+
*
|
|
1902
|
+
* Useful for debugging and verifying the file finder is working correctly.
|
|
1903
|
+
*
|
|
1904
|
+
* @param testPath - Optional path to test git repository detection
|
|
1905
|
+
*/
|
|
1478
1906
|
healthCheck(testPath) {
|
|
1479
1907
|
return ffiHealthCheck(this.handle, testPath || "");
|
|
1480
1908
|
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Check if the native library is available.
|
|
1911
|
+
*/
|
|
1481
1912
|
static isAvailable() {
|
|
1482
1913
|
return isAvailable();
|
|
1483
1914
|
}
|
|
1915
|
+
/**
|
|
1916
|
+
* Ensure the native library is loaded.
|
|
1917
|
+
*
|
|
1918
|
+
* Loads the native library from the platform-specific npm package
|
|
1919
|
+
* or a local dev build. Throws if the binary is not found.
|
|
1920
|
+
*/
|
|
1484
1921
|
static ensureLoaded() {
|
|
1485
1922
|
ensureLoaded();
|
|
1486
1923
|
}
|
|
1924
|
+
/**
|
|
1925
|
+
* Get a health check without requiring an instance.
|
|
1926
|
+
*
|
|
1927
|
+
* Returns limited info (version + git only, no picker/frecency/query data).
|
|
1928
|
+
*
|
|
1929
|
+
* @param testPath - Optional path to test git repository detection
|
|
1930
|
+
*/
|
|
1487
1931
|
static healthCheckStatic(testPath) {
|
|
1488
1932
|
return ffiHealthCheck(null, testPath || "");
|
|
1489
1933
|
}
|
|
1490
|
-
}
|
|
1934
|
+
};
|
|
1491
1935
|
export {
|
|
1492
|
-
|
|
1493
|
-
getTriple,
|
|
1494
|
-
getNpmPackageName,
|
|
1495
|
-
getLibFilename,
|
|
1496
|
-
getLibExtension,
|
|
1497
|
-
findBinary,
|
|
1498
|
-
err,
|
|
1499
|
-
closeLibrary,
|
|
1936
|
+
FileFinder,
|
|
1500
1937
|
binaryExists,
|
|
1501
|
-
|
|
1938
|
+
closeLibrary,
|
|
1939
|
+
err,
|
|
1940
|
+
findBinary,
|
|
1941
|
+
getLibExtension,
|
|
1942
|
+
getLibFilename,
|
|
1943
|
+
getNpmPackageName,
|
|
1944
|
+
getTriple,
|
|
1945
|
+
ok
|
|
1502
1946
|
};
|
|
1503
|
-
|
|
1504
|
-
//# debugId=12D927B23EC3D3EE64756E2164756E21
|
|
1947
|
+
//# sourceMappingURL=index.js.map
|