@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.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // src/binary.ts
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { createRequire } from "node:module";
4
- import { dirname, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
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 "node:child_process";
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: 5000
32
+ timeout: 5e3
33
33
  });
34
34
  } catch (e) {
35
- const err = e;
36
- output = String(err?.stdout ?? "") + String(err?.stderr ?? "");
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("fff native library not found. Run `npx @ff-labs/fff-node download` or build from source with `cargo build --release -p fff-c`");
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 === undefined)
254
- return obj;
255
- if (typeof obj !== "object")
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(/_([a-z])/g, (_, letter) => letter.toUpperCase());
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: undefined };
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) ?? undefined;
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("fff_grep_result_get_match", handlePtr, i, FFF_GREP_MATCH_STRUCT);
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("fff_search_result_get_item", handlePtr, i, FFF_FILE_ITEM_STRUCT);
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("fff_search_result_get_score", handlePtr, i, FFF_SCORE_STRUCT);
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("fff_dir_search_result_get_item", handlePtr, i, FFF_DIR_ITEM_STRUCT);
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("fff_dir_search_result_get_score", handlePtr, i, FFF_SCORE_STRUCT);
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("fff_mixed_search_result_get_item", handlePtr, i, FFF_MIXED_ITEM_STRUCT);
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("fff_mixed_search_result_get_score", handlePtr, i, FFF_SCORE_STRUCT);
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("fff_wait_for_scan", [DataType.External, DataType.U64], [handle, timeoutMs]);
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("fff_restart_index", [DataType.External, DataType.String], [handle, newPath]);
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("fff_track_query", [DataType.External, DataType.String, DataType.String], [handle, query, filePath]);
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("fff_get_historical_query", [DataType.External, DataType.U64], [handle, offset]);
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 === undefined || events.length === 0)
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("fff_set_watch_callback", [DataType.External, DataType.External, DataType.U64], [handle, trampoline, 0]);
1236
- if (registered.ok)
1237
- watchInstances.add(handle);
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
- return registered;
1255
- const created = callIntResult("fff_watch_args", [DataType.External, DataType.String, DataType.StringArray, DataType.U32], [handle, pattern, ignore, ignore.length]);
1256
- if (!created.ok)
1257
- return created;
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("fff_unwatch", [DataType.External, DataType.U64], [handle, watchId]);
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("fff_health_check", [DataType.U64, DataType.String], [0, testPath]);
1276
- }
1277
- return callJsonResult("fff_health_check", [DataType.External, DataType.String], [handle, testPath]);
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
- class FileFinder {
1408
+ var FileFinder = class _FileFinder {
1298
1409
  handle;
1299
- watchers = new Set;
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(options.basePath, options.frecencyDbPath ?? "", options.historyDbPath ?? "", options.useUnsafeNoLock ?? false, !(options.disableMmapCache ?? false), !(options.disableContentIndexing ?? options.disableMmapCache ?? false), !(options.disableWatch ?? false), options.aiMode ?? false, options.logFilePath ?? "", options.logLevel ?? "", options.cacheBudgetMaxFiles ?? 0, options.cacheBudgetMaxBytes ?? 0, options.cacheBudgetMaxFileSize ?? 0, options.enableFsRootScanning ?? false, options.enableHomeDirScanning ?? false, options.followSymlinks ?? false);
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 FileFinder(result.value) };
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
- return guard;
1332
- return ffiSearch(guard.value, query, options?.currentFile ?? "", options?.maxThreads ?? 0, options?.pageIndex ?? 0, options?.pageSize ?? 0, options?.comboBoostMultiplier ?? 0, options?.minComboCount ?? 0);
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
- return guard;
1338
- return ffiGlob(guard.value, pattern, options?.currentFile ?? "", options?.maxThreads ?? 0, options?.pageIndex ?? 0, options?.pageSize ?? 0);
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
- return guard;
1344
- return ffiSearchDirectories(guard.value, query, options?.currentFile ?? null, options?.maxThreads ?? 0, options?.pageIndex ?? 0, options?.pageSize ?? 0);
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
- return guard;
1350
- return ffiSearchMixed(guard.value, query, options?.currentFile ?? "", options?.maxThreads ?? 0, options?.pageIndex ?? 0, options?.pageSize ?? 0, options?.comboBoostMultiplier ?? 0, options?.minComboCount ?? 0);
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
- return guard;
1356
- return ffiLiveGrep(guard.value, query, options?.mode ?? "plain", options?.maxFileSize ?? 0, options?.maxMatchesPerFile ?? 0, options?.smartCase ?? true, options?.cursor?._offset ?? 0, options?.pageSize ?? 0, options?.timeBudgetMs ?? 0, options?.beforeContext ?? 0, options?.afterContext ?? 0, options?.classifyDefinitions ?? false);
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(guard.value, options.patterns.join(`
1366
- `), options.constraints ?? "", options.maxFileSize ?? 0, options.maxMatchesPerFile ?? 0, options.smartCase ?? true, options.cursor?._offset ?? 0, options.pageSize ?? 0, options.timeBudgetMs ?? 0, options.beforeContext ?? 0, options.afterContext ?? 0, options.classifyDefinitions ?? false);
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
- async waitForScan(timeoutMs = 5000) {
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
- waitForScanBlocking(timeoutMs = 5000) {
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
- async waitForIndexReady(timeoutMs = 5000) {
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
- return;
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
- ok,
1493
- getTriple,
1494
- getNpmPackageName,
1495
- getLibFilename,
1496
- getLibExtension,
1497
- findBinary,
1498
- err,
1499
- closeLibrary,
1936
+ FileFinder,
1500
1937
  binaryExists,
1501
- FileFinder
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