@ff-labs/fff-node 0.10.4-nightly.e2cad2f → 0.10.5-dev.4d662ef

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 ADDED
@@ -0,0 +1,1947 @@
1
+ // src/binary.ts
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { createRequire } from "module";
4
+ import { dirname, join } from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ // src/platform.ts
8
+ import { execSync } from "child_process";
9
+ function getTriple() {
10
+ const platform = process.platform;
11
+ const arch = process.arch;
12
+ let osName;
13
+ if (platform === "darwin") {
14
+ osName = "apple-darwin";
15
+ } else if (platform === "android") {
16
+ osName = "linux-android";
17
+ } else if (platform === "linux") {
18
+ osName = detectLinuxLibc();
19
+ } else if (platform === "win32") {
20
+ osName = "pc-windows-msvc";
21
+ } else {
22
+ throw new Error(`Unsupported platform: ${platform}`);
23
+ }
24
+ const archName = normalizeArch(arch);
25
+ return `${archName}-${osName}`;
26
+ }
27
+ function detectLinuxLibc() {
28
+ let output = "";
29
+ try {
30
+ output = execSync("ldd --version 2>&1", {
31
+ encoding: "utf-8",
32
+ timeout: 5e3
33
+ });
34
+ } catch (e) {
35
+ const err2 = e;
36
+ output = String(err2?.stdout ?? "") + String(err2?.stderr ?? "");
37
+ }
38
+ if (output.toLowerCase().includes("musl")) {
39
+ return "unknown-linux-musl";
40
+ }
41
+ return "unknown-linux-gnu";
42
+ }
43
+ function normalizeArch(arch) {
44
+ switch (arch) {
45
+ case "x64":
46
+ case "amd64":
47
+ return "x86_64";
48
+ case "arm64":
49
+ return "aarch64";
50
+ case "arm":
51
+ return "arm";
52
+ default:
53
+ throw new Error(`Unsupported architecture: ${arch}`);
54
+ }
55
+ }
56
+ function getLibExtension() {
57
+ switch (process.platform) {
58
+ case "darwin":
59
+ return "dylib";
60
+ case "win32":
61
+ return "dll";
62
+ default:
63
+ return "so";
64
+ }
65
+ }
66
+ function getLibPrefix() {
67
+ return process.platform === "win32" ? "" : "lib";
68
+ }
69
+ function getLibFilename() {
70
+ const prefix = getLibPrefix();
71
+ const ext = getLibExtension();
72
+ return `${prefix}fff_c.${ext}`;
73
+ }
74
+ var TRIPLE_TO_NPM_PACKAGE = {
75
+ "aarch64-apple-darwin": "@ff-labs/fff-bin-darwin-arm64",
76
+ "x86_64-apple-darwin": "@ff-labs/fff-bin-darwin-x64",
77
+ "x86_64-unknown-linux-gnu": "@ff-labs/fff-bin-linux-x64-gnu",
78
+ "aarch64-unknown-linux-gnu": "@ff-labs/fff-bin-linux-arm64-gnu",
79
+ "x86_64-unknown-linux-musl": "@ff-labs/fff-bin-linux-x64-musl",
80
+ "aarch64-unknown-linux-musl": "@ff-labs/fff-bin-linux-arm64-musl",
81
+ "x86_64-pc-windows-msvc": "@ff-labs/fff-bin-win32-x64",
82
+ "aarch64-pc-windows-msvc": "@ff-labs/fff-bin-win32-arm64",
83
+ "aarch64-linux-android": "@ff-labs/fff-bin-android-arm64"
84
+ };
85
+ function getNpmPackageName() {
86
+ const triple = getTriple();
87
+ const packageName = TRIPLE_TO_NPM_PACKAGE[triple];
88
+ if (!packageName) {
89
+ throw new Error(`No npm package available for platform: ${triple}`);
90
+ }
91
+ return packageName;
92
+ }
93
+
94
+ // src/binary.ts
95
+ function getCurrentDir() {
96
+ if (typeof __dirname !== "undefined") return __dirname;
97
+ const url = import.meta.url;
98
+ if (url.startsWith("file://")) {
99
+ return dirname(fileURLToPath(url));
100
+ }
101
+ return dirname(url);
102
+ }
103
+ function getPackageDir() {
104
+ const currentDir = getCurrentDir();
105
+ let dir = currentDir;
106
+ for (let i = 0; i < 5; i++) {
107
+ if (existsSync(join(dir, "package.json"))) {
108
+ try {
109
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
110
+ if (pkg.name === "@ff-labs/fff-node") {
111
+ return dir;
112
+ }
113
+ } catch {
114
+ }
115
+ }
116
+ dir = dirname(dir);
117
+ }
118
+ return dirname(currentDir);
119
+ }
120
+ function binaryExists() {
121
+ return findBinary() !== null;
122
+ }
123
+ function resolveFromNpmPackage() {
124
+ const packageName = getNpmPackageName();
125
+ try {
126
+ const require2 = createRequire(join(getPackageDir(), "package.json"));
127
+ const packageJsonPath = require2.resolve(`${packageName}/package.json`);
128
+ const packageDir = dirname(packageJsonPath);
129
+ const binaryPath = join(packageDir, getLibFilename());
130
+ if (existsSync(binaryPath)) {
131
+ return binaryPath;
132
+ }
133
+ } catch {
134
+ }
135
+ return null;
136
+ }
137
+ function getDevBinaryPath() {
138
+ const packageDir = getPackageDir();
139
+ const workspaceRoot = join(packageDir, "..", "..");
140
+ const possiblePaths = [
141
+ join(workspaceRoot, "target", "release", getLibFilename()),
142
+ join(workspaceRoot, "target", "debug", getLibFilename())
143
+ ];
144
+ for (const path of possiblePaths) {
145
+ if (existsSync(path)) {
146
+ return path;
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+ function isDevWorkspace() {
152
+ const packageDir = getPackageDir();
153
+ const workspaceRoot = join(packageDir, "..", "..");
154
+ return existsSync(join(workspaceRoot, "Cargo.toml"));
155
+ }
156
+ function findBinary() {
157
+ if (isDevWorkspace()) {
158
+ const binPath = join(getPackageDir(), "bin", getLibFilename());
159
+ if (existsSync(binPath)) return binPath;
160
+ const devPath = getDevBinaryPath();
161
+ if (devPath) return devPath;
162
+ const npmPath2 = resolveFromNpmPackage();
163
+ if (npmPath2) return npmPath2;
164
+ return null;
165
+ }
166
+ const npmPath = resolveFromNpmPackage();
167
+ if (npmPath) return npmPath;
168
+ return getDevBinaryPath();
169
+ }
170
+
171
+ // src/ffi.ts
172
+ import {
173
+ close,
174
+ createPointer,
175
+ DataType,
176
+ freePointer,
177
+ funcConstructor,
178
+ isNullPointer,
179
+ load,
180
+ open,
181
+ PointerType,
182
+ restorePointer,
183
+ unwrapPointer,
184
+ wrapPointer
185
+ } from "ffi-rs";
186
+
187
+ // src/fff-api.ts
188
+ function ok(value) {
189
+ return { ok: true, value };
190
+ }
191
+ function err(error) {
192
+ return { ok: false, error };
193
+ }
194
+ function createGrepCursor(offset) {
195
+ return { __brand: "GrepCursor", _offset: offset };
196
+ }
197
+
198
+ // src/ffi.ts
199
+ var LIBRARY_KEY = "fff_c";
200
+ var FFF_CREATE_OPTIONS_STRUCT = {
201
+ version: DataType.U32,
202
+ base_path: DataType.String,
203
+ frecency_db_path: DataType.String,
204
+ history_db_path: DataType.String,
205
+ enable_mmap_cache: DataType.U8,
206
+ enable_content_indexing: DataType.U8,
207
+ watch: DataType.U8,
208
+ ai_mode: DataType.U8,
209
+ log_file_path: DataType.String,
210
+ log_level: DataType.String,
211
+ cache_budget_max_files: DataType.U64,
212
+ cache_budget_max_bytes: DataType.U64,
213
+ cache_budget_max_file_size: DataType.U64,
214
+ enable_fs_root_scanning: DataType.U8,
215
+ enable_home_dir_scanning: DataType.U8,
216
+ follow_symlinks: DataType.U8
217
+ };
218
+ var FFF_CREATE_OPTIONS_VERSION = 2;
219
+ var GREP_MODE_PLAIN = 0;
220
+ var GREP_MODE_REGEX = 1;
221
+ var GREP_MODE_FUZZY = 2;
222
+ function grepModeToU8(mode) {
223
+ switch (mode) {
224
+ case "regex":
225
+ return GREP_MODE_REGEX;
226
+ case "fuzzy":
227
+ return GREP_MODE_FUZZY;
228
+ default:
229
+ return GREP_MODE_PLAIN;
230
+ }
231
+ }
232
+ var isLoaded = false;
233
+ var FFF_RESULT_STRUCT = {
234
+ success: DataType.U8,
235
+ error: DataType.External,
236
+ handle: DataType.External,
237
+ int_value: DataType.I64
238
+ };
239
+ function loadLibrary() {
240
+ if (isLoaded) return;
241
+ const binaryPath = findBinary();
242
+ if (!binaryPath) {
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
+ );
246
+ }
247
+ open({ library: LIBRARY_KEY, path: binaryPath });
248
+ isLoaded = true;
249
+ }
250
+ function snakeToCamel(obj) {
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);
254
+ const result = {};
255
+ for (const [key, value] of Object.entries(obj)) {
256
+ const camelKey = key.replace(
257
+ /_([a-z])/g,
258
+ (_, letter) => letter.toUpperCase()
259
+ );
260
+ result[camelKey] = snakeToCamel(value);
261
+ }
262
+ return result;
263
+ }
264
+ function readCString(ptr) {
265
+ if (isNullPointer(ptr)) return null;
266
+ try {
267
+ const [str] = restorePointer({
268
+ retType: [DataType.String],
269
+ paramsValue: wrapPointer([ptr])
270
+ });
271
+ return str;
272
+ } catch {
273
+ return null;
274
+ }
275
+ }
276
+ function callRaw(funcName, paramsType, paramsValue) {
277
+ const rawPtr = load({
278
+ library: LIBRARY_KEY,
279
+ funcName,
280
+ retType: DataType.External,
281
+ paramsType,
282
+ paramsValue,
283
+ freeResultMemory: false
284
+ });
285
+ const [structData] = restorePointer({
286
+ retType: [FFF_RESULT_STRUCT],
287
+ paramsValue: wrapPointer([rawPtr])
288
+ });
289
+ return { rawPtr, struct: structData };
290
+ }
291
+ function freeResult(resultPtr) {
292
+ try {
293
+ load({
294
+ library: LIBRARY_KEY,
295
+ funcName: "fff_free_result",
296
+ retType: DataType.Void,
297
+ paramsType: [DataType.External],
298
+ paramsValue: [resultPtr]
299
+ });
300
+ } catch {
301
+ }
302
+ }
303
+ function readResultEnvelope(funcName, paramsType, paramsValue) {
304
+ loadLibrary();
305
+ const { rawPtr, struct: structData } = callRaw(funcName, paramsType, paramsValue);
306
+ if (structData.success === 0) {
307
+ const errorStr = readCString(structData.error);
308
+ freeResult(rawPtr);
309
+ return err(errorStr || "Unknown error");
310
+ }
311
+ return { rawPtr, struct: structData };
312
+ }
313
+ function callVoidResult(funcName, paramsType, paramsValue) {
314
+ const res = readResultEnvelope(funcName, paramsType, paramsValue);
315
+ if ("ok" in res) return res;
316
+ freeResult(res.rawPtr);
317
+ return { ok: true, value: void 0 };
318
+ }
319
+ function callIntResult(funcName, paramsType, paramsValue) {
320
+ const res = readResultEnvelope(funcName, paramsType, paramsValue);
321
+ if ("ok" in res) return res;
322
+ const value = Number(res.struct.int_value);
323
+ freeResult(res.rawPtr);
324
+ return { ok: true, value };
325
+ }
326
+ function callBoolResult(funcName, paramsType, paramsValue) {
327
+ const res = readResultEnvelope(funcName, paramsType, paramsValue);
328
+ if ("ok" in res) return res;
329
+ const value = Number(res.struct.int_value) !== 0;
330
+ freeResult(res.rawPtr);
331
+ return { ok: true, value };
332
+ }
333
+ function callStringResult(funcName, paramsType, paramsValue) {
334
+ const res = readResultEnvelope(funcName, paramsType, paramsValue);
335
+ if ("ok" in res) return res;
336
+ const handlePtr = res.struct.handle;
337
+ freeResult(res.rawPtr);
338
+ if (isNullPointer(handlePtr)) return { ok: true, value: null };
339
+ const str = readCString(handlePtr);
340
+ freeString(handlePtr);
341
+ return { ok: true, value: str };
342
+ }
343
+ function callJsonResult(funcName, paramsType, paramsValue) {
344
+ const res = readResultEnvelope(funcName, paramsType, paramsValue);
345
+ if ("ok" in res) return res;
346
+ const handlePtr = res.struct.handle;
347
+ freeResult(res.rawPtr);
348
+ if (isNullPointer(handlePtr)) return { ok: true, value: void 0 };
349
+ const jsonStr = readCString(handlePtr);
350
+ freeString(handlePtr);
351
+ if (jsonStr === null || jsonStr === "") return { ok: true, value: void 0 };
352
+ try {
353
+ return { ok: true, value: snakeToCamel(JSON.parse(jsonStr)) };
354
+ } catch {
355
+ return { ok: true, value: jsonStr };
356
+ }
357
+ }
358
+ function freeString(ptr) {
359
+ try {
360
+ load({
361
+ library: LIBRARY_KEY,
362
+ funcName: "fff_free_string",
363
+ retType: DataType.Void,
364
+ paramsType: [DataType.External],
365
+ paramsValue: [ptr]
366
+ });
367
+ } catch {
368
+ }
369
+ }
370
+ function ffiCreate(basePath, frecencyDbPath, historyDbPath, _useUnsafeNoLock, enableMmapCache, enableContentIndexing, watch, aiMode, logFilePath, logLevel, cacheBudgetMaxFiles, cacheBudgetMaxBytes, cacheBudgetMaxFileSize, enableFsRootScanning, enableHomeDirScanning, followSymlinks) {
371
+ loadLibrary();
372
+ const optsValue = {
373
+ version: FFF_CREATE_OPTIONS_VERSION,
374
+ base_path: basePath,
375
+ frecency_db_path: frecencyDbPath,
376
+ history_db_path: historyDbPath,
377
+ enable_mmap_cache: enableMmapCache ? 1 : 0,
378
+ enable_content_indexing: enableContentIndexing ? 1 : 0,
379
+ watch: watch ? 1 : 0,
380
+ ai_mode: aiMode ? 1 : 0,
381
+ log_file_path: logFilePath,
382
+ log_level: logLevel,
383
+ cache_budget_max_files: cacheBudgetMaxFiles,
384
+ cache_budget_max_bytes: cacheBudgetMaxBytes,
385
+ cache_budget_max_file_size: cacheBudgetMaxFileSize,
386
+ enable_fs_root_scanning: enableFsRootScanning ? 1 : 0,
387
+ enable_home_dir_scanning: enableHomeDirScanning ? 1 : 0,
388
+ follow_symlinks: followSymlinks ? 1 : 0
389
+ };
390
+ const rawPtr = load({
391
+ library: LIBRARY_KEY,
392
+ funcName: "fff_create_instance_with",
393
+ retType: DataType.External,
394
+ paramsType: [FFF_CREATE_OPTIONS_STRUCT],
395
+ paramsValue: [optsValue],
396
+ freeResultMemory: false
397
+ });
398
+ const [structData] = restorePointer({
399
+ retType: [FFF_RESULT_STRUCT],
400
+ paramsValue: wrapPointer([rawPtr])
401
+ });
402
+ const success = structData.success !== 0;
403
+ try {
404
+ if (success) {
405
+ const handle = structData.handle;
406
+ if (isNullPointer(handle)) {
407
+ return err("fff_create_instance_with returned null handle");
408
+ }
409
+ return { ok: true, value: handle };
410
+ } else {
411
+ const errorStr = readCString(structData.error);
412
+ return err(errorStr || "Unknown error");
413
+ }
414
+ } finally {
415
+ freeResult(rawPtr);
416
+ }
417
+ }
418
+ function ffiDestroy(handle) {
419
+ loadLibrary();
420
+ load({
421
+ library: LIBRARY_KEY,
422
+ funcName: "fff_destroy",
423
+ retType: DataType.Void,
424
+ paramsType: [DataType.External],
425
+ paramsValue: [handle]
426
+ });
427
+ }
428
+ var FFF_FILE_ITEM_STRUCT = {
429
+ relative_path: DataType.External,
430
+ file_name: DataType.External,
431
+ git_status: DataType.External,
432
+ size: DataType.U64,
433
+ modified: DataType.U64,
434
+ access_frecency_score: DataType.I64,
435
+ modification_frecency_score: DataType.I64,
436
+ total_frecency_score: DataType.I64,
437
+ is_binary: DataType.U8
438
+ };
439
+ var FFF_SCORE_STRUCT = {
440
+ total: DataType.I32,
441
+ base_score: DataType.I32,
442
+ filename_bonus: DataType.I32,
443
+ special_filename_bonus: DataType.I32,
444
+ frecency_boost: DataType.I32,
445
+ distance_penalty: DataType.I32,
446
+ current_file_penalty: DataType.I32,
447
+ combo_match_boost: DataType.I32,
448
+ exact_match: DataType.U8,
449
+ match_type: DataType.External
450
+ };
451
+ var FFF_SEARCH_RESULT_STRUCT = {
452
+ items: DataType.External,
453
+ scores: DataType.External,
454
+ count: DataType.U32,
455
+ total_matched: DataType.U32,
456
+ total_files: DataType.U32,
457
+ // FffLocation inlined (flattened)
458
+ location_tag: DataType.U8,
459
+ location_line: DataType.I32,
460
+ location_col: DataType.I32,
461
+ location_end_line: DataType.I32,
462
+ location_end_col: DataType.I32
463
+ };
464
+ var FFF_DIR_ITEM_STRUCT = {
465
+ relative_path: DataType.External,
466
+ dir_name: DataType.External,
467
+ max_access_frecency: DataType.I32
468
+ };
469
+ var FFF_DIR_SEARCH_RESULT_STRUCT = {
470
+ items: DataType.External,
471
+ scores: DataType.External,
472
+ count: DataType.U32,
473
+ total_matched: DataType.U32,
474
+ total_dirs: DataType.U32
475
+ };
476
+ var FFF_MIXED_ITEM_STRUCT = {
477
+ item_type: DataType.U8,
478
+ relative_path: DataType.External,
479
+ display_name: DataType.External,
480
+ git_status: DataType.External,
481
+ size: DataType.U64,
482
+ modified: DataType.U64,
483
+ access_frecency_score: DataType.I64,
484
+ modification_frecency_score: DataType.I64,
485
+ total_frecency_score: DataType.I64,
486
+ is_binary: DataType.U8
487
+ };
488
+ var FFF_MIXED_SEARCH_RESULT_STRUCT = {
489
+ items: DataType.External,
490
+ scores: DataType.External,
491
+ count: DataType.U32,
492
+ total_matched: DataType.U32,
493
+ total_files: DataType.U32,
494
+ total_dirs: DataType.U32,
495
+ // FffLocation inlined (flattened)
496
+ location_tag: DataType.U8,
497
+ location_line: DataType.I32,
498
+ location_col: DataType.I32,
499
+ location_end_line: DataType.I32,
500
+ location_end_col: DataType.I32
501
+ };
502
+ var FFF_GREP_MATCH_STRUCT = {
503
+ relative_path: DataType.External,
504
+ file_name: DataType.External,
505
+ git_status: DataType.External,
506
+ line_content: DataType.External,
507
+ match_ranges: DataType.External,
508
+ context_before: DataType.External,
509
+ context_after: DataType.External,
510
+ size: DataType.U64,
511
+ modified: DataType.U64,
512
+ total_frecency_score: DataType.I64,
513
+ access_frecency_score: DataType.I64,
514
+ modification_frecency_score: DataType.I64,
515
+ line_number: DataType.U64,
516
+ byte_offset: DataType.U64,
517
+ col: DataType.U32,
518
+ match_ranges_count: DataType.U32,
519
+ context_before_count: DataType.U32,
520
+ context_after_count: DataType.U32,
521
+ fuzzy_score: DataType.U32,
522
+ // actually u16 in C, but ffi-rs doesn't so we read it as u32 with padding
523
+ has_fuzzy_score: DataType.U8,
524
+ is_binary: DataType.U8,
525
+ is_definition: DataType.U8
526
+ };
527
+ var FFF_GREP_RESULT_STRUCT = {
528
+ items: DataType.External,
529
+ count: DataType.U32,
530
+ total_matched: DataType.U32,
531
+ total_files_searched: DataType.U32,
532
+ total_files: DataType.U32,
533
+ filtered_file_count: DataType.U32,
534
+ next_file_offset: DataType.U32,
535
+ regex_fallback_error: DataType.External
536
+ };
537
+ var FFF_MATCH_RANGE_STRUCT = {
538
+ start: DataType.U32,
539
+ end: DataType.U32
540
+ };
541
+ function readFileItemFromRaw(raw) {
542
+ return {
543
+ relativePath: readCString(raw.relative_path) ?? "",
544
+ fileName: readCString(raw.file_name) ?? "",
545
+ gitStatus: readCString(raw.git_status) ?? "",
546
+ size: Number(raw.size),
547
+ modified: Number(raw.modified),
548
+ accessFrecencyScore: Number(raw.access_frecency_score),
549
+ modificationFrecencyScore: Number(raw.modification_frecency_score),
550
+ totalFrecencyScore: Number(raw.total_frecency_score)
551
+ };
552
+ }
553
+ function readScoreFromRaw(raw) {
554
+ return {
555
+ total: raw.total,
556
+ baseScore: raw.base_score,
557
+ filenameBonus: raw.filename_bonus,
558
+ specialFilenameBonus: raw.special_filename_bonus,
559
+ frecencyBoost: raw.frecency_boost,
560
+ distancePenalty: raw.distance_penalty,
561
+ currentFilePenalty: raw.current_file_penalty,
562
+ comboMatchBoost: raw.combo_match_boost,
563
+ exactMatch: raw.exact_match !== 0,
564
+ matchType: readCString(raw.match_type) ?? ""
565
+ };
566
+ }
567
+ function readDirItemFromRaw(raw) {
568
+ return {
569
+ relativePath: readCString(raw.relative_path) ?? "",
570
+ dirName: readCString(raw.dir_name) ?? "",
571
+ maxAccessFrecency: raw.max_access_frecency
572
+ };
573
+ }
574
+ function readMixedItemFromRaw(raw) {
575
+ if (raw.item_type === 1) {
576
+ return {
577
+ type: "directory",
578
+ item: {
579
+ relativePath: readCString(raw.relative_path) ?? "",
580
+ dirName: readCString(raw.display_name) ?? "",
581
+ maxAccessFrecency: Number(raw.access_frecency_score)
582
+ }
583
+ };
584
+ }
585
+ return {
586
+ type: "file",
587
+ item: {
588
+ relativePath: readCString(raw.relative_path) ?? "",
589
+ fileName: readCString(raw.display_name) ?? "",
590
+ gitStatus: readCString(raw.git_status) ?? "",
591
+ size: Number(raw.size),
592
+ modified: Number(raw.modified),
593
+ accessFrecencyScore: Number(raw.access_frecency_score),
594
+ modificationFrecencyScore: Number(raw.modification_frecency_score),
595
+ totalFrecencyScore: Number(raw.total_frecency_score)
596
+ }
597
+ };
598
+ }
599
+ function callAccessor(funcName, resultPtr, index, structDef) {
600
+ loadLibrary();
601
+ const elemPtr = load({
602
+ library: LIBRARY_KEY,
603
+ funcName,
604
+ retType: DataType.External,
605
+ paramsType: [DataType.External, DataType.U32],
606
+ paramsValue: [resultPtr, index]
607
+ });
608
+ const [raw] = restorePointer({
609
+ retType: [structDef],
610
+ paramsValue: wrapPointer([elemPtr])
611
+ });
612
+ return raw;
613
+ }
614
+ function ptrOffset(base, bytes) {
615
+ return load({
616
+ library: LIBRARY_KEY,
617
+ funcName: "fff_ptr_offset",
618
+ retType: DataType.External,
619
+ paramsType: [DataType.External, DataType.U64],
620
+ paramsValue: [base, bytes]
621
+ });
622
+ }
623
+ function readCStringArray(ptrArray, count) {
624
+ if (count === 0 || isNullPointer(ptrArray)) return [];
625
+ const result = [];
626
+ for (let i = 0; i < count; i++) {
627
+ const elemPtr = ptrOffset(ptrArray, i * 8);
628
+ const [charPtr] = restorePointer({
629
+ retType: [DataType.External],
630
+ paramsValue: [elemPtr]
631
+ });
632
+ result.push(readCString(charPtr) ?? "");
633
+ }
634
+ return result;
635
+ }
636
+ function readGrepMatchFromRaw(raw) {
637
+ const matchRanges = [];
638
+ for (let i = 0; i < raw.match_ranges_count; i++) {
639
+ const rangePtr = ptrOffset(raw.match_ranges, i * 8);
640
+ const [rangeRaw] = restorePointer({
641
+ retType: [FFF_MATCH_RANGE_STRUCT],
642
+ paramsValue: wrapPointer([rangePtr])
643
+ });
644
+ matchRanges.push([rangeRaw.start, rangeRaw.end]);
645
+ }
646
+ const match = {
647
+ relativePath: readCString(raw.relative_path) ?? "",
648
+ fileName: readCString(raw.file_name) ?? "",
649
+ gitStatus: readCString(raw.git_status) ?? "",
650
+ lineContent: readCString(raw.line_content) ?? "",
651
+ size: Number(raw.size),
652
+ modified: Number(raw.modified),
653
+ totalFrecencyScore: Number(raw.total_frecency_score),
654
+ accessFrecencyScore: Number(raw.access_frecency_score),
655
+ modificationFrecencyScore: Number(raw.modification_frecency_score),
656
+ isBinary: raw.is_binary !== 0,
657
+ lineNumber: Number(raw.line_number),
658
+ col: raw.col,
659
+ byteOffset: Number(raw.byte_offset),
660
+ matchRanges
661
+ };
662
+ if (raw.has_fuzzy_score !== 0) {
663
+ match.fuzzyScore = raw.fuzzy_score;
664
+ }
665
+ if (raw.context_before_count > 0) {
666
+ match.contextBefore = readCStringArray(raw.context_before, raw.context_before_count);
667
+ }
668
+ if (raw.context_after_count > 0) {
669
+ match.contextAfter = readCStringArray(raw.context_after, raw.context_after_count);
670
+ }
671
+ if (raw.is_definition !== 0) {
672
+ match.isDefinition = true;
673
+ }
674
+ return match;
675
+ }
676
+ function parseGrepResult(rawPtr) {
677
+ loadLibrary();
678
+ const [envelope] = restorePointer({
679
+ retType: [FFF_RESULT_STRUCT],
680
+ paramsValue: wrapPointer([rawPtr])
681
+ });
682
+ const success = envelope.success !== 0;
683
+ if (!success) {
684
+ const errorMsg = readCString(envelope.error) || "Unknown error";
685
+ freeResult(rawPtr);
686
+ return err(errorMsg);
687
+ }
688
+ const handlePtr = envelope.handle;
689
+ freeResult(rawPtr);
690
+ if (isNullPointer(handlePtr)) {
691
+ return err("grep returned null result");
692
+ }
693
+ const [gr] = restorePointer({
694
+ retType: [FFF_GREP_RESULT_STRUCT],
695
+ paramsValue: wrapPointer([handlePtr])
696
+ });
697
+ const count = gr.count;
698
+ const regexFallbackError = readCString(gr.regex_fallback_error) ?? void 0;
699
+ const items = [];
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
+ );
707
+ items.push(readGrepMatchFromRaw(rawMatch));
708
+ }
709
+ load({
710
+ library: LIBRARY_KEY,
711
+ funcName: "fff_free_grep_result",
712
+ retType: DataType.Void,
713
+ paramsType: [DataType.External],
714
+ paramsValue: [handlePtr]
715
+ });
716
+ const grepResult = {
717
+ items,
718
+ totalMatched: gr.total_matched,
719
+ totalFilesSearched: gr.total_files_searched,
720
+ totalFiles: gr.total_files,
721
+ filteredFileCount: gr.filtered_file_count,
722
+ nextCursor: gr.next_file_offset > 0 ? createGrepCursor(gr.next_file_offset) : null
723
+ };
724
+ if (regexFallbackError) {
725
+ grepResult.regexFallbackError = regexFallbackError;
726
+ }
727
+ return { ok: true, value: grepResult };
728
+ }
729
+ function parseSearchResult(rawPtr) {
730
+ loadLibrary();
731
+ const [envelope] = restorePointer({
732
+ retType: [FFF_RESULT_STRUCT],
733
+ paramsValue: wrapPointer([rawPtr])
734
+ });
735
+ const success = envelope.success !== 0;
736
+ if (!success) {
737
+ const errorMsg = readCString(envelope.error) || "Unknown error";
738
+ freeResult(rawPtr);
739
+ return err(errorMsg);
740
+ }
741
+ const handlePtr = envelope.handle;
742
+ freeResult(rawPtr);
743
+ if (isNullPointer(handlePtr)) {
744
+ return err("fff_search returned null search result");
745
+ }
746
+ const [sr] = restorePointer({
747
+ retType: [FFF_SEARCH_RESULT_STRUCT],
748
+ paramsValue: wrapPointer([handlePtr])
749
+ });
750
+ const count = sr.count;
751
+ let location;
752
+ if (sr.location_tag === 1) {
753
+ location = { type: "line", line: sr.location_line };
754
+ } else if (sr.location_tag === 2) {
755
+ location = {
756
+ type: "position",
757
+ line: sr.location_line,
758
+ col: sr.location_col
759
+ };
760
+ } else if (sr.location_tag === 3) {
761
+ location = {
762
+ type: "range",
763
+ start: { line: sr.location_line, col: sr.location_col },
764
+ end: { line: sr.location_end_line, col: sr.location_end_col }
765
+ };
766
+ }
767
+ const items = [];
768
+ const scores = [];
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
+ );
776
+ items.push(readFileItemFromRaw(rawItem));
777
+ const rawScore = callAccessor(
778
+ "fff_search_result_get_score",
779
+ handlePtr,
780
+ i,
781
+ FFF_SCORE_STRUCT
782
+ );
783
+ scores.push(readScoreFromRaw(rawScore));
784
+ }
785
+ load({
786
+ library: LIBRARY_KEY,
787
+ funcName: "fff_free_search_result",
788
+ retType: DataType.Void,
789
+ paramsType: [DataType.External],
790
+ paramsValue: [handlePtr]
791
+ });
792
+ const result = {
793
+ items,
794
+ scores,
795
+ totalMatched: sr.total_matched,
796
+ totalFiles: sr.total_files
797
+ };
798
+ if (location) {
799
+ result.location = location;
800
+ }
801
+ return { ok: true, value: result };
802
+ }
803
+ function parseDirSearchResult(rawPtr) {
804
+ loadLibrary();
805
+ const [envelope] = restorePointer({
806
+ retType: [FFF_RESULT_STRUCT],
807
+ paramsValue: wrapPointer([rawPtr])
808
+ });
809
+ const success = envelope.success !== 0;
810
+ if (!success) {
811
+ const errorMsg = readCString(envelope.error) || "Unknown error";
812
+ freeResult(rawPtr);
813
+ return err(errorMsg);
814
+ }
815
+ const handlePtr = envelope.handle;
816
+ freeResult(rawPtr);
817
+ if (isNullPointer(handlePtr)) {
818
+ return err("fff_search_directories returned null search result");
819
+ }
820
+ const [sr] = restorePointer({
821
+ retType: [FFF_DIR_SEARCH_RESULT_STRUCT],
822
+ paramsValue: wrapPointer([handlePtr])
823
+ });
824
+ const count = sr.count;
825
+ const items = [];
826
+ const scores = [];
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
+ );
834
+ items.push(readDirItemFromRaw(rawItem));
835
+ const rawScore = callAccessor(
836
+ "fff_dir_search_result_get_score",
837
+ handlePtr,
838
+ i,
839
+ FFF_SCORE_STRUCT
840
+ );
841
+ scores.push(readScoreFromRaw(rawScore));
842
+ }
843
+ load({
844
+ library: LIBRARY_KEY,
845
+ funcName: "fff_free_dir_search_result",
846
+ retType: DataType.Void,
847
+ paramsType: [DataType.External],
848
+ paramsValue: [handlePtr]
849
+ });
850
+ return {
851
+ ok: true,
852
+ value: {
853
+ items,
854
+ scores,
855
+ totalMatched: sr.total_matched,
856
+ totalDirs: sr.total_dirs
857
+ }
858
+ };
859
+ }
860
+ function parseMixedSearchResult(rawPtr) {
861
+ loadLibrary();
862
+ const [envelope] = restorePointer({
863
+ retType: [FFF_RESULT_STRUCT],
864
+ paramsValue: wrapPointer([rawPtr])
865
+ });
866
+ const success = envelope.success !== 0;
867
+ if (!success) {
868
+ const errorMsg = readCString(envelope.error) || "Unknown error";
869
+ freeResult(rawPtr);
870
+ return err(errorMsg);
871
+ }
872
+ const handlePtr = envelope.handle;
873
+ freeResult(rawPtr);
874
+ if (isNullPointer(handlePtr)) {
875
+ return err("fff_search_mixed returned null search result");
876
+ }
877
+ const [sr] = restorePointer({
878
+ retType: [FFF_MIXED_SEARCH_RESULT_STRUCT],
879
+ paramsValue: wrapPointer([handlePtr])
880
+ });
881
+ const count = sr.count;
882
+ let location;
883
+ if (sr.location_tag === 1) {
884
+ location = { type: "line", line: sr.location_line };
885
+ } else if (sr.location_tag === 2) {
886
+ location = {
887
+ type: "position",
888
+ line: sr.location_line,
889
+ col: sr.location_col
890
+ };
891
+ } else if (sr.location_tag === 3) {
892
+ location = {
893
+ type: "range",
894
+ start: { line: sr.location_line, col: sr.location_col },
895
+ end: { line: sr.location_end_line, col: sr.location_end_col }
896
+ };
897
+ }
898
+ const items = [];
899
+ const scores = [];
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
+ );
907
+ items.push(readMixedItemFromRaw(rawItem));
908
+ const rawScore = callAccessor(
909
+ "fff_mixed_search_result_get_score",
910
+ handlePtr,
911
+ i,
912
+ FFF_SCORE_STRUCT
913
+ );
914
+ scores.push(readScoreFromRaw(rawScore));
915
+ }
916
+ load({
917
+ library: LIBRARY_KEY,
918
+ funcName: "fff_free_mixed_search_result",
919
+ retType: DataType.Void,
920
+ paramsType: [DataType.External],
921
+ paramsValue: [handlePtr]
922
+ });
923
+ const result = {
924
+ items,
925
+ scores,
926
+ totalMatched: sr.total_matched,
927
+ totalFiles: sr.total_files,
928
+ totalDirs: sr.total_dirs
929
+ };
930
+ if (location) {
931
+ result.location = location;
932
+ }
933
+ return { ok: true, value: result };
934
+ }
935
+ function ffiSearch(handle, query, currentFile, maxThreads, pageIndex, pageSize, comboBoostMultiplier, minComboCount) {
936
+ loadLibrary();
937
+ const rawPtr = load({
938
+ library: LIBRARY_KEY,
939
+ funcName: "fff_search",
940
+ retType: DataType.External,
941
+ paramsType: [
942
+ DataType.External,
943
+ // handle
944
+ DataType.String,
945
+ // query
946
+ DataType.String,
947
+ // current_file
948
+ DataType.U32,
949
+ // max_threads
950
+ DataType.U32,
951
+ // page_index
952
+ DataType.U32,
953
+ // page_size
954
+ DataType.I32,
955
+ // combo_boost_multiplier
956
+ DataType.U32
957
+ // min_combo_count
958
+ ],
959
+ paramsValue: [
960
+ handle,
961
+ query,
962
+ currentFile,
963
+ maxThreads,
964
+ pageIndex,
965
+ pageSize,
966
+ comboBoostMultiplier,
967
+ minComboCount
968
+ ],
969
+ freeResultMemory: false
970
+ });
971
+ return parseSearchResult(rawPtr);
972
+ }
973
+ function ffiGlob(handle, pattern, currentFile, maxThreads, pageIndex, pageSize) {
974
+ loadLibrary();
975
+ const rawPtr = load({
976
+ library: LIBRARY_KEY,
977
+ funcName: "fff_glob",
978
+ retType: DataType.External,
979
+ paramsType: [
980
+ DataType.External,
981
+ // handle
982
+ DataType.String,
983
+ // pattern
984
+ DataType.String,
985
+ // current_file
986
+ DataType.U32,
987
+ // max_threads
988
+ DataType.U32,
989
+ // page_index
990
+ DataType.U32
991
+ // page_size
992
+ ],
993
+ paramsValue: [handle, pattern, currentFile, maxThreads, pageIndex, pageSize],
994
+ freeResultMemory: false
995
+ });
996
+ return parseSearchResult(rawPtr);
997
+ }
998
+ function ffiSearchDirectories(handle, query, currentFile, maxThreads, pageIndex, pageSize) {
999
+ loadLibrary();
1000
+ const rawPtr = load({
1001
+ library: LIBRARY_KEY,
1002
+ funcName: "fff_search_directories",
1003
+ retType: DataType.External,
1004
+ paramsType: [
1005
+ DataType.External,
1006
+ // handle
1007
+ DataType.String,
1008
+ // query
1009
+ DataType.String,
1010
+ // current_file
1011
+ DataType.U32,
1012
+ // max_threads
1013
+ DataType.U32,
1014
+ // page_index
1015
+ DataType.U32
1016
+ // page_size
1017
+ ],
1018
+ paramsValue: [handle, query, currentFile ?? "", maxThreads, pageIndex, pageSize],
1019
+ freeResultMemory: false
1020
+ });
1021
+ return parseDirSearchResult(rawPtr);
1022
+ }
1023
+ function ffiSearchMixed(handle, query, currentFile, maxThreads, pageIndex, pageSize, comboBoostMultiplier, minComboCount) {
1024
+ loadLibrary();
1025
+ const rawPtr = load({
1026
+ library: LIBRARY_KEY,
1027
+ funcName: "fff_search_mixed",
1028
+ retType: DataType.External,
1029
+ paramsType: [
1030
+ DataType.External,
1031
+ // handle
1032
+ DataType.String,
1033
+ // query
1034
+ DataType.String,
1035
+ // current_file
1036
+ DataType.U32,
1037
+ // max_threads
1038
+ DataType.U32,
1039
+ // page_index
1040
+ DataType.U32,
1041
+ // page_size
1042
+ DataType.I32,
1043
+ // combo_boost_multiplier
1044
+ DataType.U32
1045
+ // min_combo_count
1046
+ ],
1047
+ paramsValue: [
1048
+ handle,
1049
+ query,
1050
+ currentFile,
1051
+ maxThreads,
1052
+ pageIndex,
1053
+ pageSize,
1054
+ comboBoostMultiplier,
1055
+ minComboCount
1056
+ ],
1057
+ freeResultMemory: false
1058
+ });
1059
+ return parseMixedSearchResult(rawPtr);
1060
+ }
1061
+ function ffiLiveGrep(handle, query, mode, maxFileSize, maxMatchesPerFile, smartCase, fileOffset, pageLimit, timeBudgetMs, beforeContext, afterContext, classifyDefinitions) {
1062
+ loadLibrary();
1063
+ const rawPtr = load({
1064
+ library: LIBRARY_KEY,
1065
+ funcName: "fff_live_grep",
1066
+ retType: DataType.External,
1067
+ paramsType: [
1068
+ DataType.External,
1069
+ // handle
1070
+ DataType.String,
1071
+ // query
1072
+ DataType.U8,
1073
+ // mode
1074
+ DataType.U64,
1075
+ // max_file_size
1076
+ DataType.U32,
1077
+ // max_matches_per_file
1078
+ DataType.Boolean,
1079
+ // smart_case
1080
+ DataType.U32,
1081
+ // file_offset
1082
+ DataType.U32,
1083
+ // page_limit
1084
+ DataType.U64,
1085
+ // time_budget_ms
1086
+ DataType.U32,
1087
+ // before_context
1088
+ DataType.U32,
1089
+ // after_context
1090
+ DataType.Boolean
1091
+ // classify_definitions
1092
+ ],
1093
+ paramsValue: [
1094
+ handle,
1095
+ query,
1096
+ grepModeToU8(mode),
1097
+ maxFileSize,
1098
+ maxMatchesPerFile,
1099
+ smartCase,
1100
+ fileOffset,
1101
+ pageLimit,
1102
+ timeBudgetMs,
1103
+ beforeContext,
1104
+ afterContext,
1105
+ classifyDefinitions
1106
+ ],
1107
+ freeResultMemory: false
1108
+ });
1109
+ return parseGrepResult(rawPtr);
1110
+ }
1111
+ function ffiMultiGrep(handle, patternsJoined, constraints, maxFileSize, maxMatchesPerFile, smartCase, fileOffset, pageLimit, timeBudgetMs, beforeContext, afterContext, classifyDefinitions) {
1112
+ loadLibrary();
1113
+ const rawPtr = load({
1114
+ library: LIBRARY_KEY,
1115
+ funcName: "fff_multi_grep",
1116
+ retType: DataType.External,
1117
+ paramsType: [
1118
+ DataType.External,
1119
+ // handle
1120
+ DataType.String,
1121
+ // patterns_joined
1122
+ DataType.String,
1123
+ // constraints
1124
+ DataType.U64,
1125
+ // max_file_size
1126
+ DataType.U32,
1127
+ // max_matches_per_file
1128
+ DataType.Boolean,
1129
+ // smart_case
1130
+ DataType.U32,
1131
+ // file_offset
1132
+ DataType.U32,
1133
+ // page_limit
1134
+ DataType.U64,
1135
+ // time_budget_ms
1136
+ DataType.U32,
1137
+ // before_context
1138
+ DataType.U32,
1139
+ // after_context
1140
+ DataType.Boolean
1141
+ // classify_definitions
1142
+ ],
1143
+ paramsValue: [
1144
+ handle,
1145
+ patternsJoined,
1146
+ constraints,
1147
+ maxFileSize,
1148
+ maxMatchesPerFile,
1149
+ smartCase,
1150
+ fileOffset,
1151
+ pageLimit,
1152
+ timeBudgetMs,
1153
+ beforeContext,
1154
+ afterContext,
1155
+ classifyDefinitions
1156
+ ],
1157
+ freeResultMemory: false
1158
+ });
1159
+ return parseGrepResult(rawPtr);
1160
+ }
1161
+ function ffiScanFiles(handle) {
1162
+ return callVoidResult("fff_scan_files", [DataType.External], [handle]);
1163
+ }
1164
+ function ffiIsScanning(handle) {
1165
+ loadLibrary();
1166
+ return load({
1167
+ library: LIBRARY_KEY,
1168
+ funcName: "fff_is_scanning",
1169
+ retType: DataType.Boolean,
1170
+ paramsType: [DataType.External],
1171
+ paramsValue: [handle]
1172
+ });
1173
+ }
1174
+ function ffiGetBasePath(handle) {
1175
+ return callStringResult("fff_get_base_path", [DataType.External], [handle]);
1176
+ }
1177
+ var FFF_SCAN_PROGRESS_STRUCT = {
1178
+ scanned_files_count: DataType.U64,
1179
+ is_scanning: DataType.U8,
1180
+ is_watcher_ready: DataType.U8,
1181
+ is_warmup_complete: DataType.U8
1182
+ };
1183
+ function ffiGetScanProgress(handle) {
1184
+ loadLibrary();
1185
+ const res = readResultEnvelope("fff_get_scan_progress", [DataType.External], [handle]);
1186
+ if ("ok" in res) return res;
1187
+ const handlePtr = res.struct.handle;
1188
+ freeResult(res.rawPtr);
1189
+ if (isNullPointer(handlePtr)) return err("scan progress returned null");
1190
+ const [sp] = restorePointer({
1191
+ retType: [FFF_SCAN_PROGRESS_STRUCT],
1192
+ paramsValue: wrapPointer([handlePtr])
1193
+ });
1194
+ const result = {
1195
+ scannedFilesCount: Number(sp.scanned_files_count),
1196
+ isScanning: sp.is_scanning !== 0,
1197
+ isWatcherReady: sp.is_watcher_ready !== 0,
1198
+ isWarmupComplete: sp.is_warmup_complete !== 0
1199
+ };
1200
+ load({
1201
+ library: LIBRARY_KEY,
1202
+ funcName: "fff_free_scan_progress",
1203
+ retType: DataType.Void,
1204
+ paramsType: [DataType.External],
1205
+ paramsValue: [handlePtr]
1206
+ });
1207
+ return { ok: true, value: result };
1208
+ }
1209
+ function ffiWaitForScan(handle, timeoutMs) {
1210
+ return callBoolResult(
1211
+ "fff_wait_for_scan",
1212
+ [DataType.External, DataType.U64],
1213
+ [handle, timeoutMs]
1214
+ );
1215
+ }
1216
+ function ffiRestartIndex(handle, newPath) {
1217
+ return callVoidResult(
1218
+ "fff_restart_index",
1219
+ [DataType.External, DataType.String],
1220
+ [handle, newPath]
1221
+ );
1222
+ }
1223
+ function ffiRefreshGitStatus(handle) {
1224
+ return callIntResult("fff_refresh_git_status", [DataType.External], [handle]);
1225
+ }
1226
+ function ffiTrackQuery(handle, query, filePath) {
1227
+ return callBoolResult(
1228
+ "fff_track_query",
1229
+ [DataType.External, DataType.String, DataType.String],
1230
+ [handle, query, filePath]
1231
+ );
1232
+ }
1233
+ function ffiGetHistoricalQuery(handle, offset) {
1234
+ return callStringResult(
1235
+ "fff_get_historical_query",
1236
+ [DataType.External, DataType.U64],
1237
+ [handle, offset]
1238
+ );
1239
+ }
1240
+ function watchKindFromU8(kind) {
1241
+ switch (kind) {
1242
+ case 0:
1243
+ return "created";
1244
+ case 1:
1245
+ return "modified";
1246
+ case 2:
1247
+ return "removed";
1248
+ default:
1249
+ return "rescan";
1250
+ }
1251
+ }
1252
+ var WATCH_TRAMPOLINE_TYPE = funcConstructor({
1253
+ paramsType: [DataType.U64, DataType.U64, DataType.U64],
1254
+ retType: DataType.Void
1255
+ });
1256
+ var watchHandlers = /* @__PURE__ */ new Map();
1257
+ var watchInstances = /* @__PURE__ */ new Set();
1258
+ var watchTrampoline = null;
1259
+ function addressToExternal(address) {
1260
+ return load({
1261
+ library: LIBRARY_KEY,
1262
+ funcName: "fff_ptr_offset",
1263
+ retType: DataType.External,
1264
+ paramsType: [DataType.U64, DataType.U64],
1265
+ paramsValue: [address, 0]
1266
+ });
1267
+ }
1268
+ function consumeWatchBatch(address) {
1269
+ const batchPtr = addressToExternal(address);
1270
+ const count = load({
1271
+ library: LIBRARY_KEY,
1272
+ funcName: "fff_watch_events_count",
1273
+ retType: DataType.U32,
1274
+ paramsType: [DataType.External],
1275
+ paramsValue: [batchPtr]
1276
+ });
1277
+ const events = [];
1278
+ for (let i = 0; i < count; i++) {
1279
+ const path = load({
1280
+ library: LIBRARY_KEY,
1281
+ funcName: "fff_watch_events_get_path",
1282
+ retType: DataType.External,
1283
+ paramsType: [DataType.External, DataType.U32],
1284
+ paramsValue: [batchPtr, i]
1285
+ });
1286
+ const kind = load({
1287
+ library: LIBRARY_KEY,
1288
+ funcName: "fff_watch_events_get_kind",
1289
+ retType: DataType.U8,
1290
+ paramsType: [DataType.External, DataType.U32],
1291
+ paramsValue: [batchPtr, i]
1292
+ });
1293
+ events.push({
1294
+ path: readCString(path) ?? "",
1295
+ kind: watchKindFromU8(kind)
1296
+ });
1297
+ }
1298
+ load({
1299
+ library: LIBRARY_KEY,
1300
+ funcName: "fff_free_watch_events",
1301
+ retType: DataType.Void,
1302
+ paramsType: [DataType.U64],
1303
+ paramsValue: [address]
1304
+ });
1305
+ return events;
1306
+ }
1307
+ function watchTrampolineImpl(watchId, batchAddress, _userData) {
1308
+ const events = consumeWatchBatch(batchAddress);
1309
+ const handler = watchHandlers.get(Number(watchId));
1310
+ if (handler === void 0 || events.length === 0) return;
1311
+ try {
1312
+ handler(events);
1313
+ } catch {
1314
+ }
1315
+ }
1316
+ function ensureWatchTrampoline() {
1317
+ if (watchTrampoline === null) {
1318
+ watchTrampoline = createPointer({
1319
+ paramsType: [WATCH_TRAMPOLINE_TYPE],
1320
+ paramsValue: [watchTrampolineImpl]
1321
+ });
1322
+ }
1323
+ return unwrapPointer(watchTrampoline)[0];
1324
+ }
1325
+ function ensureWatchCallbackRegistered(handle) {
1326
+ if (watchInstances.has(handle)) return { ok: true, value: void 0 };
1327
+ const trampoline = ensureWatchTrampoline();
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);
1334
+ return registered;
1335
+ }
1336
+ function releaseWatchTrampolineIfIdle() {
1337
+ if (watchHandlers.size > 0 || watchInstances.size > 0 || watchTrampoline === null)
1338
+ return;
1339
+ freePointer({
1340
+ paramsType: [WATCH_TRAMPOLINE_TYPE],
1341
+ paramsValue: watchTrampoline,
1342
+ pointerType: PointerType.RsPointer
1343
+ });
1344
+ watchTrampoline = null;
1345
+ }
1346
+ function ffiWatch(handle, pattern, ignore, callback) {
1347
+ loadLibrary();
1348
+ const registered = ensureWatchCallbackRegistered(handle);
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;
1356
+ watchHandlers.set(created.value, callback);
1357
+ return created;
1358
+ }
1359
+ function ffiUnwatch(handle, watchId) {
1360
+ const result = callBoolResult(
1361
+ "fff_unwatch",
1362
+ [DataType.External, DataType.U64],
1363
+ [handle, watchId]
1364
+ );
1365
+ watchHandlers.delete(watchId);
1366
+ return result;
1367
+ }
1368
+ function ffiWatchCleanupAfterDestroy(handle, watchIds) {
1369
+ for (const id of watchIds) {
1370
+ watchHandlers.delete(id);
1371
+ }
1372
+ watchInstances.delete(handle);
1373
+ releaseWatchTrampolineIfIdle();
1374
+ }
1375
+ function ffiHealthCheck(handle, testPath) {
1376
+ if (handle === null) {
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
+ );
1388
+ }
1389
+ function ensureLoaded() {
1390
+ loadLibrary();
1391
+ }
1392
+ function isAvailable() {
1393
+ try {
1394
+ loadLibrary();
1395
+ return true;
1396
+ } catch {
1397
+ return false;
1398
+ }
1399
+ }
1400
+ function closeLibrary() {
1401
+ if (isLoaded) {
1402
+ close(LIBRARY_KEY);
1403
+ isLoaded = false;
1404
+ }
1405
+ }
1406
+
1407
+ // src/finder.ts
1408
+ var FileFinder = class _FileFinder {
1409
+ handle;
1410
+ /** Native ids of this instance's active watch subscriptions. */
1411
+ watchers = /* @__PURE__ */ new Set();
1412
+ constructor(handle) {
1413
+ this.handle = handle;
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
+ */
1434
+ static create(options) {
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
+ );
1453
+ if (!result.ok) {
1454
+ return result;
1455
+ }
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
+ */
1465
+ destroy() {
1466
+ if (this.handle !== null) {
1467
+ const handle = this.handle;
1468
+ ffiDestroy(handle);
1469
+ this.handle = null;
1470
+ ffiWatchCleanupAfterDestroy(handle, this.watchers);
1471
+ this.watchers.clear();
1472
+ }
1473
+ }
1474
+ /**
1475
+ * Check if this instance has been destroyed.
1476
+ */
1477
+ get isDestroyed() {
1478
+ return this.handle === null;
1479
+ }
1480
+ /**
1481
+ * Guard that returns an error if the instance has been destroyed.
1482
+ */
1483
+ ensureAlive() {
1484
+ if (this.handle === null) {
1485
+ return err("FileFinder instance has been destroyed.");
1486
+ }
1487
+ return { ok: true, value: this.handle };
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
+ */
1513
+ fileSearch(query, options) {
1514
+ const guard = this.ensureAlive();
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
+ */
1547
+ glob(pattern, options) {
1548
+ const guard = this.ensureAlive();
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
+ */
1577
+ directorySearch(query, options) {
1578
+ const guard = this.ensureAlive();
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
+ */
1613
+ mixedSearch(query, options) {
1614
+ const guard = this.ensureAlive();
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
+ */
1663
+ grep(query, options) {
1664
+ const guard = this.ensureAlive();
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
+ */
1706
+ multiGrep(options) {
1707
+ const guard = this.ensureAlive();
1708
+ if (!guard.ok) return guard;
1709
+ if (!options.patterns || options.patterns.length === 0) {
1710
+ return err("patterns array must have at least 1 element");
1711
+ }
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
+ */
1733
+ scanFiles() {
1734
+ const guard = this.ensureAlive();
1735
+ if (!guard.ok) return guard;
1736
+ return ffiScanFiles(guard.value);
1737
+ }
1738
+ /**
1739
+ * Check if a scan is currently in progress.
1740
+ */
1741
+ isScanning() {
1742
+ if (this.handle === null) return false;
1743
+ return ffiIsScanning(this.handle);
1744
+ }
1745
+ /**
1746
+ * Get the base path of the file picker (the root directory being indexed).
1747
+ */
1748
+ getBasePath() {
1749
+ const guard = this.ensureAlive();
1750
+ if (!guard.ok) return guard;
1751
+ return ffiGetBasePath(guard.value);
1752
+ }
1753
+ /**
1754
+ * Get the current scan progress.
1755
+ */
1756
+ getScanProgress() {
1757
+ const guard = this.ensureAlive();
1758
+ if (!guard.ok) return guard;
1759
+ return ffiGetScanProgress(guard.value);
1760
+ }
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) {
1780
+ const guard = this.ensureAlive();
1781
+ if (!guard.ok) return guard;
1782
+ const deadline = Date.now() + timeoutMs;
1783
+ while (this.isScanning()) {
1784
+ if (Date.now() >= deadline) {
1785
+ return { ok: true, value: false };
1786
+ }
1787
+ await new Promise((resolve) => setTimeout(resolve, 50));
1788
+ }
1789
+ return { ok: true, value: true };
1790
+ }
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) {
1801
+ const guard = this.ensureAlive();
1802
+ if (!guard.ok) return guard;
1803
+ return ffiWaitForScan(guard.value, timeoutMs);
1804
+ }
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) {
1815
+ const guard = this.ensureAlive();
1816
+ if (!guard.ok) return guard;
1817
+ const deadline = Date.now() + timeoutMs;
1818
+ while (true) {
1819
+ const progress = this.getScanProgress();
1820
+ if (!progress.ok) return progress;
1821
+ if (!progress.value.isScanning && progress.value.isWarmupComplete) {
1822
+ return { ok: true, value: true };
1823
+ }
1824
+ if (Date.now() >= deadline) {
1825
+ return { ok: true, value: false };
1826
+ }
1827
+ await new Promise((resolve) => setTimeout(resolve, 50));
1828
+ }
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
+ */
1837
+ reindex(newPath) {
1838
+ const guard = this.ensureAlive();
1839
+ if (!guard.ok) return guard;
1840
+ return ffiRestartIndex(guard.value, newPath);
1841
+ }
1842
+ /**
1843
+ * Refresh the git status cache.
1844
+ *
1845
+ * @returns Number of files with updated git status
1846
+ */
1847
+ refreshGitStatus() {
1848
+ const guard = this.ensureAlive();
1849
+ if (!guard.ok) return guard;
1850
+ return ffiRefreshGitStatus(guard.value);
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
+ */
1861
+ trackQuery(query, selectedFilePath) {
1862
+ const guard = this.ensureAlive();
1863
+ if (!guard.ok) return guard;
1864
+ return ffiTrackQuery(guard.value, query, selectedFilePath);
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
+ */
1872
+ getHistoricalQuery(offset) {
1873
+ const guard = this.ensureAlive();
1874
+ if (!guard.ok) return guard;
1875
+ return ffiGetHistoricalQuery(guard.value, offset);
1876
+ }
1877
+ watch(patternOrCallback, callbackOrOptions, maybeOptions) {
1878
+ const noPattern = typeof patternOrCallback === "function";
1879
+ const pattern = noPattern ? "" : patternOrCallback;
1880
+ const callback = noPattern ? patternOrCallback : callbackOrOptions;
1881
+ const options = noPattern ? callbackOrOptions : maybeOptions;
1882
+ if (typeof callback !== "function") {
1883
+ return err("watch callback must be a function");
1884
+ }
1885
+ const guard = this.ensureAlive();
1886
+ if (!guard.ok) return guard;
1887
+ const created = ffiWatch(guard.value, pattern, options?.ignore ?? [], callback);
1888
+ if (!created.ok) return created;
1889
+ const watchId = created.value;
1890
+ this.watchers.add(watchId);
1891
+ return {
1892
+ ok: true,
1893
+ value: () => {
1894
+ if (!this.watchers.delete(watchId)) return;
1895
+ if (this.handle !== null) ffiUnwatch(this.handle, watchId);
1896
+ }
1897
+ };
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
+ */
1906
+ healthCheck(testPath) {
1907
+ return ffiHealthCheck(this.handle, testPath || "");
1908
+ }
1909
+ /**
1910
+ * Check if the native library is available.
1911
+ */
1912
+ static isAvailable() {
1913
+ return isAvailable();
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
+ */
1921
+ static ensureLoaded() {
1922
+ ensureLoaded();
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
+ */
1931
+ static healthCheckStatic(testPath) {
1932
+ return ffiHealthCheck(null, testPath || "");
1933
+ }
1934
+ };
1935
+ export {
1936
+ FileFinder,
1937
+ binaryExists,
1938
+ closeLibrary,
1939
+ err,
1940
+ findBinary,
1941
+ getLibExtension,
1942
+ getLibFilename,
1943
+ getNpmPackageName,
1944
+ getTriple,
1945
+ ok
1946
+ };
1947
+ //# sourceMappingURL=index.js.map