@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.map CHANGED
@@ -1,14 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/binary.ts", "../src/platform.ts", "../src/ffi.ts", "../src/fff-api.ts", "../src/finder.ts"],
4
- "sourcesContent": [
5
- "/**\n * Binary resolution utilities for fff-node\n *\n * Resolves the native library from:\n * 1. Platform-specific npm package (e.g. @ff-labs/fff-bin-darwin-arm64)\n * 2. Local dev build (target/release or target/debug)\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getLibFilename, getNpmPackageName } from \"./platform.js\";\n\n/**\n * Get the current file's directory\n */\nfunction getCurrentDir(): string {\n // CJS build: import.meta.url is inlined at bundle time, __dirname is the truth\n if (typeof __dirname !== \"undefined\") return __dirname;\n\n const url = import.meta.url;\n\n if (url.startsWith(\"file://\")) {\n return dirname(fileURLToPath(url));\n }\n return dirname(url);\n}\n\n/**\n * Get the package root directory\n */\nfunction getPackageDir(): string {\n const currentDir = getCurrentDir();\n // In dev: src/ -> package root\n // In dist: dist/ -> package root\n // We look for package.json to find the actual root\n let dir = currentDir;\n for (let i = 0; i < 5; i++) {\n if (existsSync(join(dir, \"package.json\"))) {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, \"package.json\"), \"utf-8\"));\n if (pkg.name === \"@ff-labs/fff-node\") {\n return dir;\n }\n } catch {\n // Not our package.json, keep going up\n }\n }\n dir = dirname(dir);\n }\n // Fallback: assume we're one level deep in src/\n return dirname(currentDir);\n}\n\n/**\n * Check if the binary exists in any known location\n */\nexport function binaryExists(): boolean {\n return findBinary() !== null;\n}\n\n/**\n * Try to resolve the binary from the platform-specific npm package.\n *\n * When users install @ff-labs/fff-node, npm automatically installs the matching\n * optionalDependency (e.g. @ff-labs/fff-bin-darwin-arm64). We resolve the binary\n * path by requiring that package's package.json and looking for the binary\n * in the same directory.\n */\nfunction resolveFromNpmPackage(): string | null {\n const packageName = getNpmPackageName();\n\n try {\n // Use createRequire to resolve the platform package's location\n const require = createRequire(join(getPackageDir(), \"package.json\"));\n const packageJsonPath = require.resolve(`${packageName}/package.json`);\n const packageDir = dirname(packageJsonPath);\n const binaryPath = join(packageDir, getLibFilename());\n\n if (existsSync(binaryPath)) {\n return binaryPath;\n }\n } catch {\n // Package not installed - this is expected on unsupported platforms\n // or when installed without optional dependencies\n }\n\n return null;\n}\n\n/**\n * Get the development binary path (for local development)\n */\nfunction getDevBinaryPath(): string | null {\n const packageDir = getPackageDir();\n const workspaceRoot = join(packageDir, \"..\", \"..\");\n\n const possiblePaths = [\n join(workspaceRoot, \"target\", \"release\", getLibFilename()),\n join(workspaceRoot, \"target\", \"debug\", getLibFilename()),\n ];\n\n for (const path of possiblePaths) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nfunction isDevWorkspace(): boolean {\n const packageDir = getPackageDir();\n const workspaceRoot = join(packageDir, \"..\", \"..\");\n return existsSync(join(workspaceRoot, \"Cargo.toml\"));\n}\n\n/**\n * Find the native library binary.\n *\n * Resolution order:\n * - Dev workspace: local dev build first, then npm package\n * - Production: npm package first, then dev build\n *\n * @returns Absolute path to the library, or null if not found\n */\nexport function findBinary(): string | null {\n if (isDevWorkspace()) {\n // 1. Local bin/ directory (populated by `make prepare-node`)\n const binPath = join(getPackageDir(), \"bin\", getLibFilename());\n if (existsSync(binPath)) return binPath;\n\n // 2. Local dev build (target/release or target/debug)\n const devPath = getDevBinaryPath();\n if (devPath) return devPath;\n\n // 3. Fallback to npm package\n const npmPath = resolveFromNpmPackage();\n if (npmPath) return npmPath;\n\n return null;\n }\n\n // Production: npm package first\n const npmPath = resolveFromNpmPackage();\n if (npmPath) return npmPath;\n\n // Fallback: local dev build (e.g. user built from source)\n return getDevBinaryPath();\n}\n",
6
- "/**\n * Platform detection utilities for downloading the correct binary\n */\n\nimport { execSync } from \"node:child_process\";\n\n/**\n * Get the platform triple (e.g., \"x86_64-unknown-linux-gnu\")\n */\nexport function getTriple(): string {\n const platform = process.platform;\n const arch = process.arch;\n\n let osName: string;\n if (platform === \"darwin\") {\n osName = \"apple-darwin\";\n } else if (platform === \"android\") {\n osName = \"linux-android\";\n } else if (platform === \"linux\") {\n osName = detectLinuxLibc();\n } else if (platform === \"win32\") {\n osName = \"pc-windows-msvc\";\n } else {\n throw new Error(`Unsupported platform: ${platform}`);\n }\n\n const archName = normalizeArch(arch);\n return `${archName}-${osName}`;\n}\n\n/**\n * Detect whether we're on musl or glibc Linux\n */\nfunction detectLinuxLibc(): string {\n let output = \"\";\n try {\n output = execSync(\"ldd --version 2>&1\", {\n encoding: \"utf-8\",\n timeout: 5000,\n });\n } catch (e: unknown) {\n const err = e as { stdout?: string | Buffer; stderr?: string | Buffer };\n output = String(err?.stdout ?? \"\") + String(err?.stderr ?? \"\");\n }\n\n // ldd on musl can produce stdout with musl either with exit code 1 or 0\n if (output.toLowerCase().includes(\"musl\")) {\n return \"unknown-linux-musl\";\n }\n return \"unknown-linux-gnu\";\n}\n\n/**\n * Normalize architecture name to Rust target format\n */\nfunction normalizeArch(arch: string): string {\n switch (arch) {\n case \"x64\":\n case \"amd64\":\n return \"x86_64\";\n case \"arm64\":\n return \"aarch64\";\n case \"arm\":\n return \"arm\";\n default:\n throw new Error(`Unsupported architecture: ${arch}`);\n }\n}\n\n/**\n * Get the library file extension for the current platform\n */\nexport function getLibExtension(): \"dylib\" | \"so\" | \"dll\" {\n switch (process.platform) {\n case \"darwin\":\n return \"dylib\";\n case \"win32\":\n return \"dll\";\n default:\n return \"so\";\n }\n}\n\n/**\n * Get the library filename prefix (empty on Windows)\n */\nexport function getLibPrefix(): string {\n return process.platform === \"win32\" ? \"\" : \"lib\";\n}\n\n/**\n * Get the full library filename for the current platform\n */\nexport function getLibFilename(): string {\n const prefix = getLibPrefix();\n const ext = getLibExtension();\n return `${prefix}fff_c.${ext}`;\n}\n\n/**\n * Map from Rust target triple to npm platform package name.\n * The @ff-labs/fff-bin-* packages contain the pre-built libfff_c\n * shared library and are runtime-agnostic (used by both Bun and Node).\n */\nconst TRIPLE_TO_NPM_PACKAGE: Record<string, string> = {\n \"aarch64-apple-darwin\": \"@ff-labs/fff-bin-darwin-arm64\",\n \"x86_64-apple-darwin\": \"@ff-labs/fff-bin-darwin-x64\",\n \"x86_64-unknown-linux-gnu\": \"@ff-labs/fff-bin-linux-x64-gnu\",\n \"aarch64-unknown-linux-gnu\": \"@ff-labs/fff-bin-linux-arm64-gnu\",\n \"x86_64-unknown-linux-musl\": \"@ff-labs/fff-bin-linux-x64-musl\",\n \"aarch64-unknown-linux-musl\": \"@ff-labs/fff-bin-linux-arm64-musl\",\n \"x86_64-pc-windows-msvc\": \"@ff-labs/fff-bin-win32-x64\",\n \"aarch64-pc-windows-msvc\": \"@ff-labs/fff-bin-win32-arm64\",\n \"aarch64-linux-android\": \"@ff-labs/fff-bin-android-arm64\",\n};\n\n/**\n * Get the npm package name for the current platform's native binary.\n *\n * @returns Package name like \"@ff-labs/fff-bin-darwin-arm64\"\n * @throws If the current platform is not supported\n */\nexport function getNpmPackageName(): string {\n const triple = getTriple();\n const packageName = TRIPLE_TO_NPM_PACKAGE[triple];\n if (!packageName) {\n throw new Error(`No npm package available for platform: ${triple}`);\n }\n return packageName;\n}\n",
7
- "/**\n * Node.js FFI bindings for the fff-c native library using ffi-rs\n *\n * This module uses ffi-rs to call into the Rust C library.\n * All functions follow the Result pattern for error handling.\n *\n * The API is instance-based: `ffiCreate` returns an opaque handle that must\n * be passed to all subsequent calls and freed with `ffiDestroy`.\n *\n * ## Memory management\n *\n * Every `fff_*` function returning `*mut FffResult` allocates with Rust's Box.\n * We MUST call `fff_free_result` to properly deallocate (not libc::free).\n *\n * ## FffResult struct reading\n *\n * The FffResult struct layout (#[repr(C)]):\n * offset 0: success (bool, 1 byte + 7 padding)\n * offset 8: data pointer (8 bytes) - *mut c_char (JSON string or null)\n * offset 16: error pointer (8 bytes) - *mut c_char (error message or null)\n * offset 24: handle pointer (8 bytes) - *mut c_void (instance handle or null)\n *\n * ## Two-step approach for reading + freeing\n *\n * ffi-rs auto-dereferences struct retType pointers, losing the original pointer.\n * We solve this by:\n * 1. Calling the C function with `retType: DataType.External` to get the raw pointer\n * 2. Using `restorePointer` to read the struct fields from the raw pointer\n * 3. Calling `fff_free_result` with the original raw pointer\n *\n * ## Null pointer detection\n *\n * `isNullPointer` from ffi-rs correctly detects null C pointers wrapped as\n * V8 External objects. We use this instead of truthy checks.\n */\n\nimport {\n close,\n createPointer,\n DataType,\n type FieldType,\n freePointer,\n funcConstructor,\n isNullPointer,\n type JsExternal,\n load,\n open,\n PointerType,\n restorePointer,\n unwrapPointer,\n wrapPointer,\n} from \"ffi-rs\";\nimport { findBinary } from \"./binary.js\";\nimport type {\n DirItem,\n DirSearchResult,\n FileItem,\n GrepMatch,\n GrepResult,\n Location,\n MixedItem,\n MixedSearchResult,\n Result,\n Score,\n SearchResult,\n WatchEvent,\n WatchEventKind,\n} from \"./fff-api.js\";\nimport { createGrepCursor, err } from \"./fff-api.js\";\n\nconst LIBRARY_KEY = \"fff_c\";\n\nconst FFF_CREATE_OPTIONS_STRUCT = {\n version: DataType.U32,\n base_path: DataType.String,\n frecency_db_path: DataType.String,\n history_db_path: DataType.String,\n enable_mmap_cache: DataType.U8,\n enable_content_indexing: DataType.U8,\n watch: DataType.U8,\n ai_mode: DataType.U8,\n log_file_path: DataType.String,\n log_level: DataType.String,\n cache_budget_max_files: DataType.U64,\n cache_budget_max_bytes: DataType.U64,\n cache_budget_max_file_size: DataType.U64,\n enable_fs_root_scanning: DataType.U8,\n enable_home_dir_scanning: DataType.U8,\n follow_symlinks: DataType.U8,\n};\n\n// ALWAYS KEEP IN SYNC WITH fff.h\nconst FFF_CREATE_OPTIONS_VERSION = 2;\n\n/** Grep mode constants matching the C API (u8). */\nconst GREP_MODE_PLAIN = 0;\nconst GREP_MODE_REGEX = 1;\nconst GREP_MODE_FUZZY = 2;\n\n/** Map string mode to u8 */\nfunction grepModeToU8(mode?: string): number {\n switch (mode) {\n case \"regex\":\n return GREP_MODE_REGEX;\n case \"fuzzy\":\n return GREP_MODE_FUZZY;\n default:\n return GREP_MODE_PLAIN;\n }\n}\n\n// Track whether the library is loaded\nlet isLoaded = false;\n\n/**\n * Struct type definition for FffResult used with restorePointer.\n *\n * Uses U8 for the bool success field (correct alignment with ffi-rs).\n * Uses External for ALL pointer fields to avoid hangs on null char* pointers\n * (ffi-rs hangs when trying to read DataType.String from null char*).\n */\nconst FFF_RESULT_STRUCT = {\n success: DataType.U8,\n error: DataType.External,\n handle: DataType.External,\n int_value: DataType.I64,\n};\n\ninterface FffResultRaw {\n success: number;\n error: JsExternal;\n handle: JsExternal;\n int_value: number;\n}\n\n/**\n * Load the native library using ffi-rs\n */\nfunction loadLibrary(): void {\n if (isLoaded) return;\n\n const binaryPath = findBinary();\n if (!binaryPath) {\n throw new Error(\n \"fff native library not found. Run `npx @ff-labs/fff-node download` or build from source with `cargo build --release -p fff-c`\",\n );\n }\n\n open({ library: LIBRARY_KEY, path: binaryPath });\n isLoaded = true;\n}\n\n/**\n * Convert snake_case keys to camelCase recursively\n */\nfunction snakeToCamel(obj: unknown): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj !== \"object\") return obj;\n if (Array.isArray(obj)) return obj.map(snakeToCamel);\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n const camelKey = key.replace(/_([a-z])/g, (_, letter: string) =>\n letter.toUpperCase(),\n );\n result[camelKey] = snakeToCamel(value);\n }\n return result;\n}\n\n/**\n * Read a C string (char*) from an ffi-rs External pointer.\n *\n * Uses restorePointer + wrapPointer to dereference the char* and read the\n * null-terminated string. Returns null if the pointer is null.\n */\nfunction readCString(ptr: JsExternal): string | null {\n if (isNullPointer(ptr)) return null;\n try {\n const [str] = restorePointer({\n retType: [DataType.String],\n paramsValue: wrapPointer([ptr]),\n });\n return str as string;\n } catch {\n return null;\n }\n}\n\n/**\n * Call a C function that returns `*mut FffResult` and get both the raw pointer\n * (for freeing) and the parsed struct fields.\n *\n * Step 1: Call function with `DataType.External` retType → raw pointer\n * Step 2: Use `restorePointer` to read struct fields from the raw pointer\n */\nfunction callRaw(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): { rawPtr: JsExternal; struct: FffResultRaw } {\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName,\n retType: DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n }) as JsExternal;\n\n const [structData] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n return { rawPtr, struct: structData };\n}\n\n/**\n * Free a FffResult pointer by calling fff_free_result.\n *\n * This frees the FffResult struct and its data/error strings using Rust's\n * Box::from_raw and CString::from_raw. The handle field is NOT freed.\n */\nfunction freeResult(resultPtr: JsExternal): void {\n try {\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [resultPtr],\n });\n } catch {\n // Ignore cleanup errors\n }\n}\n\n/**\n * Read the FffResult envelope from a raw call. Returns the parsed struct + raw pointer.\n * On error, frees the result and returns a Result error.\n */\nfunction readResultEnvelope(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): { rawPtr: JsExternal; struct: FffResultRaw } | Result<never> {\n loadLibrary();\n const { rawPtr, struct: structData } = callRaw(funcName, paramsType, paramsValue);\n\n if (structData.success === 0) {\n const errorStr = readCString(structData.error);\n freeResult(rawPtr);\n return err(errorStr || \"Unknown error\");\n }\n\n return { rawPtr, struct: structData };\n}\n\n/** Call a function returning FffResult with void payload. */\nfunction callVoidResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<void> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n freeResult(res.rawPtr);\n return { ok: true, value: undefined };\n}\n\n/** Call a function returning FffResult with int_value payload. */\nfunction callIntResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<number> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const value = Number(res.struct.int_value);\n freeResult(res.rawPtr);\n return { ok: true, value };\n}\n\n/** Call a function returning FffResult with bool in int_value. */\nfunction callBoolResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<boolean> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const value = Number(res.struct.int_value) !== 0;\n freeResult(res.rawPtr);\n return { ok: true, value };\n}\n\n/** Call a function returning FffResult with a C string in handle. */\nfunction callStringResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<string | null> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const handlePtr = res.struct.handle;\n freeResult(res.rawPtr);\n if (isNullPointer(handlePtr)) return { ok: true, value: null };\n const str = readCString(handlePtr);\n freeString(handlePtr);\n return { ok: true, value: str };\n}\n\n/** Call a function returning FffResult with a JSON string in handle. */\nfunction callJsonResult<T>(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<T> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const handlePtr = res.struct.handle;\n freeResult(res.rawPtr);\n if (isNullPointer(handlePtr)) return { ok: true, value: undefined as T };\n const jsonStr = readCString(handlePtr);\n freeString(handlePtr);\n if (jsonStr === null || jsonStr === \"\") return { ok: true, value: undefined as T };\n try {\n return { ok: true, value: snakeToCamel(JSON.parse(jsonStr)) as T };\n } catch {\n return { ok: true, value: jsonStr as T };\n }\n}\n\n/** Free a C string via fff_free_string. */\nfunction freeString(ptr: JsExternal): void {\n try {\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_string\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [ptr],\n });\n } catch {\n // Ignore\n }\n}\n\n/**\n * Opaque native handle type. Callers must not inspect or modify this value.\n */\nexport type NativeHandle = JsExternal;\n\nexport function ffiCreate(\n basePath: string,\n frecencyDbPath: string,\n historyDbPath: string,\n _useUnsafeNoLock: boolean,\n enableMmapCache: boolean,\n enableContentIndexing: boolean,\n watch: boolean,\n aiMode: boolean,\n logFilePath: string,\n logLevel: string,\n cacheBudgetMaxFiles: number,\n cacheBudgetMaxBytes: number,\n cacheBudgetMaxFileSize: number,\n enableFsRootScanning: boolean,\n enableHomeDirScanning: boolean,\n followSymlinks: boolean,\n): Result<NativeHandle> {\n loadLibrary();\n\n const optsValue = {\n version: FFF_CREATE_OPTIONS_VERSION,\n base_path: basePath,\n frecency_db_path: frecencyDbPath,\n history_db_path: historyDbPath,\n enable_mmap_cache: enableMmapCache ? 1 : 0,\n enable_content_indexing: enableContentIndexing ? 1 : 0,\n watch: watch ? 1 : 0,\n ai_mode: aiMode ? 1 : 0,\n log_file_path: logFilePath,\n log_level: logLevel,\n cache_budget_max_files: cacheBudgetMaxFiles,\n cache_budget_max_bytes: cacheBudgetMaxBytes,\n cache_budget_max_file_size: cacheBudgetMaxFileSize,\n enable_fs_root_scanning: enableFsRootScanning ? 1 : 0,\n enable_home_dir_scanning: enableHomeDirScanning ? 1 : 0,\n follow_symlinks: followSymlinks ? 1 : 0,\n };\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_create_instance_with\",\n retType: DataType.External,\n paramsType: [FFF_CREATE_OPTIONS_STRUCT],\n paramsValue: [optsValue],\n freeResultMemory: false,\n }) as JsExternal;\n\n const [structData] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = structData.success !== 0;\n\n try {\n if (success) {\n const handle = structData.handle;\n if (isNullPointer(handle)) {\n return err(\"fff_create_instance_with returned null handle\");\n }\n return { ok: true, value: handle };\n } else {\n const errorStr = readCString(structData.error);\n return err(errorStr || \"Unknown error\");\n }\n } finally {\n freeResult(rawPtr);\n }\n}\n\n/**\n * Destroy and clean up an instance.\n */\nexport function ffiDestroy(handle: NativeHandle): void {\n loadLibrary();\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_destroy\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handle],\n });\n}\n\n// ---------------------------------------------------------------------------\n// Struct type definitions for restorePointer (must match #[repr(C)] layout)\n// ---------------------------------------------------------------------------\n\nconst FFF_FILE_ITEM_STRUCT = {\n relative_path: DataType.External,\n file_name: DataType.External,\n git_status: DataType.External,\n size: DataType.U64,\n modified: DataType.U64,\n access_frecency_score: DataType.I64,\n modification_frecency_score: DataType.I64,\n total_frecency_score: DataType.I64,\n is_binary: DataType.U8,\n};\n\ninterface FffFileItemRaw {\n relative_path: JsExternal;\n file_name: JsExternal;\n git_status: JsExternal;\n size: number;\n modified: number;\n access_frecency_score: number;\n modification_frecency_score: number;\n total_frecency_score: number;\n is_binary: number;\n}\n\nconst FFF_SCORE_STRUCT = {\n total: DataType.I32,\n base_score: DataType.I32,\n filename_bonus: DataType.I32,\n special_filename_bonus: DataType.I32,\n frecency_boost: DataType.I32,\n distance_penalty: DataType.I32,\n current_file_penalty: DataType.I32,\n combo_match_boost: DataType.I32,\n exact_match: DataType.U8,\n match_type: DataType.External,\n};\n\ninterface FffScoreRaw {\n total: number;\n base_score: number;\n filename_bonus: number;\n special_filename_bonus: number;\n frecency_boost: number;\n distance_penalty: number;\n current_file_penalty: number;\n combo_match_boost: number;\n exact_match: number;\n match_type: JsExternal;\n}\n\nconst FFF_SEARCH_RESULT_STRUCT = {\n items: DataType.External,\n scores: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_files: DataType.U32,\n // FffLocation inlined (flattened)\n location_tag: DataType.U8,\n location_line: DataType.I32,\n location_col: DataType.I32,\n location_end_line: DataType.I32,\n location_end_col: DataType.I32,\n};\n\ninterface FffSearchResultRaw {\n items: JsExternal;\n scores: JsExternal;\n count: number;\n total_matched: number;\n total_files: number;\n location_tag: number;\n location_line: number;\n location_col: number;\n location_end_line: number;\n location_end_col: number;\n}\n\n// FffDirItem struct (#[repr(C)]): char* (8) + char* (8) + i32 (4) + 4 padding = 24 bytes\nconst FFF_DIR_ITEM_STRUCT = {\n relative_path: DataType.External,\n dir_name: DataType.External,\n max_access_frecency: DataType.I32,\n};\n\ninterface FffDirItemRaw {\n relative_path: JsExternal;\n dir_name: JsExternal;\n max_access_frecency: number;\n}\n\nconst FFF_DIR_SEARCH_RESULT_STRUCT = {\n items: DataType.External,\n scores: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_dirs: DataType.U32,\n};\n\ninterface FffDirSearchResultRaw {\n items: JsExternal;\n scores: JsExternal;\n count: number;\n total_matched: number;\n total_dirs: number;\n}\n\n// FffMixedItem struct (#[repr(C)]): u8 (1) + 7 padding + char* (8) + char* (8) + char* (8)\n// + u64 (8) + u64 (8) + i64 (8) + i64 (8) + i64 (8) + bool (1) + 7 padding = 80 bytes\nconst FFF_MIXED_ITEM_STRUCT = {\n item_type: DataType.U8,\n relative_path: DataType.External,\n display_name: DataType.External,\n git_status: DataType.External,\n size: DataType.U64,\n modified: DataType.U64,\n access_frecency_score: DataType.I64,\n modification_frecency_score: DataType.I64,\n total_frecency_score: DataType.I64,\n is_binary: DataType.U8,\n};\n\ninterface FffMixedItemRaw {\n item_type: number;\n relative_path: JsExternal;\n display_name: JsExternal;\n git_status: JsExternal;\n size: number;\n modified: number;\n access_frecency_score: number;\n modification_frecency_score: number;\n total_frecency_score: number;\n is_binary: number;\n}\n\nconst FFF_MIXED_SEARCH_RESULT_STRUCT = {\n items: DataType.External,\n scores: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_files: DataType.U32,\n total_dirs: DataType.U32,\n // FffLocation inlined (flattened)\n location_tag: DataType.U8,\n location_line: DataType.I32,\n location_col: DataType.I32,\n location_end_line: DataType.I32,\n location_end_col: DataType.I32,\n};\n\ninterface FffMixedSearchResultRaw {\n items: JsExternal;\n scores: JsExternal;\n count: number;\n total_matched: number;\n total_files: number;\n total_dirs: number;\n location_tag: number;\n location_line: number;\n location_col: number;\n location_end_line: number;\n location_end_col: number;\n}\n\nconst FFF_GREP_MATCH_STRUCT = {\n relative_path: DataType.External,\n file_name: DataType.External,\n git_status: DataType.External,\n line_content: DataType.External,\n match_ranges: DataType.External,\n context_before: DataType.External,\n context_after: DataType.External,\n size: DataType.U64,\n modified: DataType.U64,\n total_frecency_score: DataType.I64,\n access_frecency_score: DataType.I64,\n modification_frecency_score: DataType.I64,\n line_number: DataType.U64,\n byte_offset: DataType.U64,\n col: DataType.U32,\n match_ranges_count: DataType.U32,\n context_before_count: DataType.U32,\n context_after_count: DataType.U32,\n fuzzy_score: DataType.U32, // actually u16 in C, but ffi-rs doesn't so we read it as u32 with padding\n has_fuzzy_score: DataType.U8,\n is_binary: DataType.U8,\n is_definition: DataType.U8,\n};\n\ninterface FffGrepMatchRaw {\n relative_path: JsExternal;\n file_name: JsExternal;\n git_status: JsExternal;\n line_content: JsExternal;\n match_ranges: JsExternal;\n context_before: JsExternal;\n context_after: JsExternal;\n size: number;\n modified: number;\n total_frecency_score: number;\n access_frecency_score: number;\n modification_frecency_score: number;\n line_number: number;\n byte_offset: number;\n col: number;\n match_ranges_count: number;\n context_before_count: number;\n context_after_count: number;\n fuzzy_score: number;\n has_fuzzy_score: number;\n is_binary: number;\n is_definition: number;\n}\n\nconst FFF_GREP_RESULT_STRUCT = {\n items: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_files_searched: DataType.U32,\n total_files: DataType.U32,\n filtered_file_count: DataType.U32,\n next_file_offset: DataType.U32,\n regex_fallback_error: DataType.External,\n};\n\ninterface FffGrepResultRaw {\n items: JsExternal;\n count: number;\n total_matched: number;\n total_files_searched: number;\n total_files: number;\n filtered_file_count: number;\n next_file_offset: number;\n regex_fallback_error: JsExternal;\n}\n\nconst FFF_MATCH_RANGE_STRUCT = {\n start: DataType.U32,\n end: DataType.U32,\n};\n\ninterface FffMatchRangeRaw {\n start: number;\n end: number;\n}\n\n// ---------------------------------------------------------------------------\n// Struct reading helpers\n// ---------------------------------------------------------------------------\n\nfunction readFileItemFromRaw(raw: FffFileItemRaw): FileItem {\n return {\n relativePath: readCString(raw.relative_path) ?? \"\",\n fileName: readCString(raw.file_name) ?? \"\",\n gitStatus: readCString(raw.git_status) ?? \"\",\n size: Number(raw.size),\n modified: Number(raw.modified),\n accessFrecencyScore: Number(raw.access_frecency_score),\n modificationFrecencyScore: Number(raw.modification_frecency_score),\n totalFrecencyScore: Number(raw.total_frecency_score),\n };\n}\n\nfunction readScoreFromRaw(raw: FffScoreRaw): Score {\n return {\n total: raw.total,\n baseScore: raw.base_score,\n filenameBonus: raw.filename_bonus,\n specialFilenameBonus: raw.special_filename_bonus,\n frecencyBoost: raw.frecency_boost,\n distancePenalty: raw.distance_penalty,\n currentFilePenalty: raw.current_file_penalty,\n comboMatchBoost: raw.combo_match_boost,\n exactMatch: raw.exact_match !== 0,\n matchType: readCString(raw.match_type) ?? \"\",\n };\n}\n\nfunction readDirItemFromRaw(raw: FffDirItemRaw): DirItem {\n return {\n relativePath: readCString(raw.relative_path) ?? \"\",\n dirName: readCString(raw.dir_name) ?? \"\",\n maxAccessFrecency: raw.max_access_frecency,\n };\n}\n\nfunction readMixedItemFromRaw(raw: FffMixedItemRaw): MixedItem {\n if (raw.item_type === 1) {\n // Directory\n return {\n type: \"directory\",\n item: {\n relativePath: readCString(raw.relative_path) ?? \"\",\n dirName: readCString(raw.display_name) ?? \"\",\n maxAccessFrecency: Number(raw.access_frecency_score),\n },\n };\n }\n // File (item_type === 0)\n return {\n type: \"file\",\n item: {\n relativePath: readCString(raw.relative_path) ?? \"\",\n fileName: readCString(raw.display_name) ?? \"\",\n gitStatus: readCString(raw.git_status) ?? \"\",\n size: Number(raw.size),\n modified: Number(raw.modified),\n accessFrecencyScore: Number(raw.access_frecency_score),\n modificationFrecencyScore: Number(raw.modification_frecency_score),\n totalFrecencyScore: Number(raw.total_frecency_score),\n },\n };\n}\n\n/**\n * Call an accessor function that returns a pointer to a struct element,\n * then read the struct from that pointer.\n */\nfunction callAccessor<T>(\n funcName: string,\n resultPtr: JsExternal,\n index: number,\n structDef: Record<string, DataType>,\n): T {\n loadLibrary();\n const elemPtr = load({\n library: LIBRARY_KEY,\n funcName,\n retType: DataType.External,\n paramsType: [DataType.External, DataType.U32],\n paramsValue: [resultPtr, index],\n }) as JsExternal;\n\n const [raw] = restorePointer({\n retType: [structDef],\n paramsValue: wrapPointer([elemPtr]),\n }) as unknown as [T];\n\n return raw;\n}\n\n/**\n * Offset a pointer by `bytes` using the C API helper.\n */\nfunction ptrOffset(base: JsExternal, bytes: number): JsExternal {\n return load({\n library: LIBRARY_KEY,\n funcName: \"fff_ptr_offset\",\n retType: DataType.External,\n paramsType: [DataType.External, DataType.U64],\n paramsValue: [base, bytes],\n }) as JsExternal;\n}\n\n/**\n * Read a C string array (char**) of `count` elements.\n */\nfunction readCStringArray(ptrArray: JsExternal, count: number): string[] {\n if (count === 0 || isNullPointer(ptrArray)) return [];\n const result: string[] = [];\n for (let i = 0; i < count; i++) {\n const elemPtr = ptrOffset(ptrArray, i * 8);\n const [charPtr] = restorePointer({\n retType: [DataType.External],\n paramsValue: [elemPtr],\n }) as unknown as [JsExternal];\n result.push(readCString(charPtr) ?? \"\");\n }\n return result;\n}\n\nfunction readGrepMatchFromRaw(raw: FffGrepMatchRaw): GrepMatch {\n // Read match_ranges array via pointer offsets\n const matchRanges: [number, number][] = [];\n for (let i = 0; i < raw.match_ranges_count; i++) {\n const rangePtr = ptrOffset(raw.match_ranges, i * 8); // FffMatchRange is 8 bytes\n const [rangeRaw] = restorePointer({\n retType: [FFF_MATCH_RANGE_STRUCT],\n paramsValue: wrapPointer([rangePtr]),\n }) as unknown as [FffMatchRangeRaw];\n matchRanges.push([rangeRaw.start, rangeRaw.end]);\n }\n\n const match: GrepMatch = {\n relativePath: readCString(raw.relative_path) ?? \"\",\n fileName: readCString(raw.file_name) ?? \"\",\n gitStatus: readCString(raw.git_status) ?? \"\",\n lineContent: readCString(raw.line_content) ?? \"\",\n size: Number(raw.size),\n modified: Number(raw.modified),\n totalFrecencyScore: Number(raw.total_frecency_score),\n accessFrecencyScore: Number(raw.access_frecency_score),\n modificationFrecencyScore: Number(raw.modification_frecency_score),\n isBinary: raw.is_binary !== 0,\n lineNumber: Number(raw.line_number),\n col: raw.col,\n byteOffset: Number(raw.byte_offset),\n matchRanges,\n };\n\n if (raw.has_fuzzy_score !== 0) {\n match.fuzzyScore = raw.fuzzy_score;\n }\n if (raw.context_before_count > 0) {\n match.contextBefore = readCStringArray(raw.context_before, raw.context_before_count);\n }\n if (raw.context_after_count > 0) {\n match.contextAfter = readCStringArray(raw.context_after, raw.context_after_count);\n }\n if (raw.is_definition !== 0) {\n match.isDefinition = true;\n }\n\n return match;\n}\n\n/**\n * Parse an FffGrepResult from `FffResult.handle`, then free native memory.\n */\nfunction parseGrepResult(rawPtr: JsExternal): Result<GrepResult> {\n loadLibrary();\n\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"grep returned null result\");\n }\n\n const [gr] = restorePointer({\n retType: [FFF_GREP_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffGrepResultRaw];\n\n const count = gr.count;\n const regexFallbackError = readCString(gr.regex_fallback_error) ?? undefined;\n\n const items: GrepMatch[] = [];\n for (let i = 0; i < count; i++) {\n const rawMatch = callAccessor<FffGrepMatchRaw>(\n \"fff_grep_result_get_match\",\n handlePtr,\n i,\n FFF_GREP_MATCH_STRUCT,\n );\n items.push(readGrepMatchFromRaw(rawMatch));\n }\n\n // Free native grep result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_grep_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n const grepResult: GrepResult = {\n items,\n totalMatched: gr.total_matched,\n totalFilesSearched: gr.total_files_searched,\n totalFiles: gr.total_files,\n filteredFileCount: gr.filtered_file_count,\n nextCursor: gr.next_file_offset > 0 ? createGrepCursor(gr.next_file_offset) : null,\n };\n if (regexFallbackError) {\n grepResult.regexFallbackError = regexFallbackError;\n }\n return { ok: true, value: grepResult };\n}\n\n/**\n * Parse an FffSearchResult from `FffResult.handle`, then free native memory.\n */\nfunction parseSearchResult(rawPtr: JsExternal): Result<SearchResult> {\n loadLibrary();\n\n // Read FffResult envelope\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n // Free the FffResult envelope (does NOT free handle)\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"fff_search returned null search result\");\n }\n\n // Read FffSearchResult struct\n const [sr] = restorePointer({\n retType: [FFF_SEARCH_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffSearchResultRaw];\n\n const count = sr.count;\n\n // Read location\n let location: Location | undefined;\n if (sr.location_tag === 1) {\n location = { type: \"line\", line: sr.location_line };\n } else if (sr.location_tag === 2) {\n location = {\n type: \"position\",\n line: sr.location_line,\n col: sr.location_col,\n };\n } else if (sr.location_tag === 3) {\n location = {\n type: \"range\",\n start: { line: sr.location_line, col: sr.location_col },\n end: { line: sr.location_end_line, col: sr.location_end_col },\n };\n }\n\n // Read items and scores via accessor functions\n const items: FileItem[] = [];\n const scores: Score[] = [];\n\n for (let i = 0; i < count; i++) {\n const rawItem = callAccessor<FffFileItemRaw>(\n \"fff_search_result_get_item\",\n handlePtr,\n i,\n FFF_FILE_ITEM_STRUCT,\n );\n items.push(readFileItemFromRaw(rawItem));\n\n const rawScore = callAccessor<FffScoreRaw>(\n \"fff_search_result_get_score\",\n handlePtr,\n i,\n FFF_SCORE_STRUCT,\n );\n scores.push(readScoreFromRaw(rawScore));\n }\n\n // Free native search result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_search_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n const result: SearchResult = {\n items,\n scores,\n totalMatched: sr.total_matched,\n totalFiles: sr.total_files,\n };\n if (location) {\n result.location = location;\n }\n return { ok: true, value: result };\n}\n\n/**\n * Parse an FffDirSearchResult from `FffResult.handle`, then free native memory.\n */\nfunction parseDirSearchResult(rawPtr: JsExternal): Result<DirSearchResult> {\n loadLibrary();\n\n // Read FffResult envelope\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n // Free the FffResult envelope (does NOT free handle)\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"fff_search_directories returned null search result\");\n }\n\n // Read FffDirSearchResult struct\n const [sr] = restorePointer({\n retType: [FFF_DIR_SEARCH_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffDirSearchResultRaw];\n\n const count = sr.count;\n\n // Read items and scores via accessor functions\n const items: DirItem[] = [];\n const scores: Score[] = [];\n\n for (let i = 0; i < count; i++) {\n const rawItem = callAccessor<FffDirItemRaw>(\n \"fff_dir_search_result_get_item\",\n handlePtr,\n i,\n FFF_DIR_ITEM_STRUCT,\n );\n items.push(readDirItemFromRaw(rawItem));\n\n const rawScore = callAccessor<FffScoreRaw>(\n \"fff_dir_search_result_get_score\",\n handlePtr,\n i,\n FFF_SCORE_STRUCT,\n );\n scores.push(readScoreFromRaw(rawScore));\n }\n\n // Free native dir search result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_dir_search_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n return {\n ok: true,\n value: {\n items,\n scores,\n totalMatched: sr.total_matched,\n totalDirs: sr.total_dirs,\n },\n };\n}\n\n/**\n * Parse an FffMixedSearchResult from `FffResult.handle`, then free native memory.\n */\nfunction parseMixedSearchResult(rawPtr: JsExternal): Result<MixedSearchResult> {\n loadLibrary();\n\n // Read FffResult envelope\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n // Free the FffResult envelope (does NOT free handle)\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"fff_search_mixed returned null search result\");\n }\n\n // Read FffMixedSearchResult struct\n const [sr] = restorePointer({\n retType: [FFF_MIXED_SEARCH_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffMixedSearchResultRaw];\n\n const count = sr.count;\n\n // Read location\n let location: Location | undefined;\n if (sr.location_tag === 1) {\n location = { type: \"line\", line: sr.location_line };\n } else if (sr.location_tag === 2) {\n location = {\n type: \"position\",\n line: sr.location_line,\n col: sr.location_col,\n };\n } else if (sr.location_tag === 3) {\n location = {\n type: \"range\",\n start: { line: sr.location_line, col: sr.location_col },\n end: { line: sr.location_end_line, col: sr.location_end_col },\n };\n }\n\n // Read items and scores via accessor functions\n const items: MixedItem[] = [];\n const scores: Score[] = [];\n\n for (let i = 0; i < count; i++) {\n const rawItem = callAccessor<FffMixedItemRaw>(\n \"fff_mixed_search_result_get_item\",\n handlePtr,\n i,\n FFF_MIXED_ITEM_STRUCT,\n );\n items.push(readMixedItemFromRaw(rawItem));\n\n const rawScore = callAccessor<FffScoreRaw>(\n \"fff_mixed_search_result_get_score\",\n handlePtr,\n i,\n FFF_SCORE_STRUCT,\n );\n scores.push(readScoreFromRaw(rawScore));\n }\n\n // Free native mixed search result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_mixed_search_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n const result: MixedSearchResult = {\n items,\n scores,\n totalMatched: sr.total_matched,\n totalFiles: sr.total_files,\n totalDirs: sr.total_dirs,\n };\n if (location) {\n result.location = location;\n }\n return { ok: true, value: result };\n}\n\n/**\n * Perform fuzzy search.\n */\nexport function ffiSearch(\n handle: NativeHandle,\n query: string,\n currentFile: string,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n comboBoostMultiplier: number,\n minComboCount: number,\n): Result<SearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_search\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n DataType.I32, // combo_boost_multiplier\n DataType.U32, // min_combo_count\n ],\n paramsValue: [\n handle,\n query,\n currentFile,\n maxThreads,\n pageIndex,\n pageSize,\n comboBoostMultiplier,\n minComboCount,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseSearchResult(rawPtr);\n}\n\n/**\n * Glob-only search. Bypasses the regular query parser, applies the pattern\n * as a single `Constraint::Glob`, ranks by frecency, paginates.\n */\nexport function ffiGlob(\n handle: NativeHandle,\n pattern: string,\n currentFile: string,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n): Result<SearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_glob\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // pattern\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n ],\n paramsValue: [handle, pattern, currentFile, maxThreads, pageIndex, pageSize],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseSearchResult(rawPtr);\n}\n\n/**\n * Perform fuzzy directory search.\n */\nexport function ffiSearchDirectories(\n handle: NativeHandle,\n query: string,\n currentFile: string | null,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n): Result<DirSearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_search_directories\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n ],\n paramsValue: [handle, query, currentFile ?? \"\", maxThreads, pageIndex, pageSize],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseDirSearchResult(rawPtr);\n}\n\n/**\n * Perform mixed (files + directories) fuzzy search.\n */\nexport function ffiSearchMixed(\n handle: NativeHandle,\n query: string,\n currentFile: string,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n comboBoostMultiplier: number,\n minComboCount: number,\n): Result<MixedSearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_search_mixed\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n DataType.I32, // combo_boost_multiplier\n DataType.U32, // min_combo_count\n ],\n paramsValue: [\n handle,\n query,\n currentFile,\n maxThreads,\n pageIndex,\n pageSize,\n comboBoostMultiplier,\n minComboCount,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseMixedSearchResult(rawPtr);\n}\n\n/**\n * Live grep - search file contents.\n */\nexport function ffiLiveGrep(\n handle: NativeHandle,\n query: string,\n mode: string,\n maxFileSize: number,\n maxMatchesPerFile: number,\n smartCase: boolean,\n fileOffset: number,\n pageLimit: number,\n timeBudgetMs: number,\n beforeContext: number,\n afterContext: number,\n classifyDefinitions: boolean,\n): Result<GrepResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_live_grep\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.U8, // mode\n DataType.U64, // max_file_size\n DataType.U32, // max_matches_per_file\n DataType.Boolean, // smart_case\n DataType.U32, // file_offset\n DataType.U32, // page_limit\n DataType.U64, // time_budget_ms\n DataType.U32, // before_context\n DataType.U32, // after_context\n DataType.Boolean, // classify_definitions\n ],\n paramsValue: [\n handle,\n query,\n grepModeToU8(mode),\n maxFileSize,\n maxMatchesPerFile,\n smartCase,\n fileOffset,\n pageLimit,\n timeBudgetMs,\n beforeContext,\n afterContext,\n classifyDefinitions,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseGrepResult(rawPtr);\n}\n\n/**\n * Multi-pattern grep - Aho-Corasick multi-needle search.\n */\nexport function ffiMultiGrep(\n handle: NativeHandle,\n patternsJoined: string,\n constraints: string,\n maxFileSize: number,\n maxMatchesPerFile: number,\n smartCase: boolean,\n fileOffset: number,\n pageLimit: number,\n timeBudgetMs: number,\n beforeContext: number,\n afterContext: number,\n classifyDefinitions: boolean,\n): Result<GrepResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_multi_grep\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // patterns_joined\n DataType.String, // constraints\n DataType.U64, // max_file_size\n DataType.U32, // max_matches_per_file\n DataType.Boolean, // smart_case\n DataType.U32, // file_offset\n DataType.U32, // page_limit\n DataType.U64, // time_budget_ms\n DataType.U32, // before_context\n DataType.U32, // after_context\n DataType.Boolean, // classify_definitions\n ],\n paramsValue: [\n handle,\n patternsJoined,\n constraints,\n maxFileSize,\n maxMatchesPerFile,\n smartCase,\n fileOffset,\n pageLimit,\n timeBudgetMs,\n beforeContext,\n afterContext,\n classifyDefinitions,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseGrepResult(rawPtr);\n}\n\n/**\n * Trigger file scan.\n */\nexport function ffiScanFiles(handle: NativeHandle): Result<void> {\n return callVoidResult(\"fff_scan_files\", [DataType.External], [handle]);\n}\n\n/**\n * Check if scanning.\n */\nexport function ffiIsScanning(handle: NativeHandle): boolean {\n loadLibrary();\n return load({\n library: LIBRARY_KEY,\n funcName: \"fff_is_scanning\",\n retType: DataType.Boolean,\n paramsType: [DataType.External],\n paramsValue: [handle],\n }) as boolean;\n}\n\n/**\n * Get the base path of the file picker.\n */\nexport function ffiGetBasePath(handle: NativeHandle): Result<string | null> {\n return callStringResult(\"fff_get_base_path\", [DataType.External], [handle]);\n}\n\n// FffScanProgress struct definition\nconst FFF_SCAN_PROGRESS_STRUCT = {\n scanned_files_count: DataType.U64,\n is_scanning: DataType.U8,\n is_watcher_ready: DataType.U8,\n is_warmup_complete: DataType.U8,\n};\n\ninterface FffScanProgressRaw {\n scanned_files_count: number;\n is_scanning: number;\n is_watcher_ready: number;\n is_warmup_complete: number;\n}\n\n/**\n * Get scan progress.\n */\nexport function ffiGetScanProgress(handle: NativeHandle): Result<{\n scannedFilesCount: number;\n isScanning: boolean;\n isWatcherReady: boolean;\n isWarmupComplete: boolean;\n}> {\n loadLibrary();\n const res = readResultEnvelope(\"fff_get_scan_progress\", [DataType.External], [handle]);\n if (\"ok\" in res) return res;\n\n const handlePtr = res.struct.handle;\n freeResult(res.rawPtr);\n\n if (isNullPointer(handlePtr)) return err(\"scan progress returned null\");\n\n const [sp] = restorePointer({\n retType: [FFF_SCAN_PROGRESS_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffScanProgressRaw];\n\n const result = {\n scannedFilesCount: Number(sp.scanned_files_count),\n isScanning: sp.is_scanning !== 0,\n isWatcherReady: sp.is_watcher_ready !== 0,\n isWarmupComplete: sp.is_warmup_complete !== 0,\n };\n\n // Free native scan progress\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_scan_progress\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n return { ok: true, value: result };\n}\n\n/**\n * Wait for a tree scan to complete.\n */\nexport function ffiWaitForScan(handle: NativeHandle, timeoutMs: number): Result<boolean> {\n return callBoolResult(\n \"fff_wait_for_scan\",\n [DataType.External, DataType.U64],\n [handle, timeoutMs],\n );\n}\n\n/**\n * Restart index in new path.\n */\nexport function ffiRestartIndex(handle: NativeHandle, newPath: string): Result<void> {\n return callVoidResult(\n \"fff_restart_index\",\n [DataType.External, DataType.String],\n [handle, newPath],\n );\n}\n\n/**\n * Refresh git status.\n */\nexport function ffiRefreshGitStatus(handle: NativeHandle): Result<number> {\n return callIntResult(\"fff_refresh_git_status\", [DataType.External], [handle]);\n}\n\n/**\n * Track query completion.\n */\nexport function ffiTrackQuery(\n handle: NativeHandle,\n query: string,\n filePath: string,\n): Result<boolean> {\n return callBoolResult(\n \"fff_track_query\",\n [DataType.External, DataType.String, DataType.String],\n [handle, query, filePath],\n );\n}\n\n/**\n * Get historical query.\n */\nexport function ffiGetHistoricalQuery(\n handle: NativeHandle,\n offset: number,\n): Result<string | null> {\n return callStringResult(\n \"fff_get_historical_query\",\n [DataType.External, DataType.U64],\n [handle, offset],\n );\n}\n\n// ALWAYS KEEP IN SYNC WITH fff.h\n//\n// Note: node uses `fff_watch_args` (flattened options) because ffi-rs cannot\n// marshal a `*const *const c_char` field inside a struct param — StringArray\n// is only supported as a top-level parameter.\n//\n// Batch contents are read through the C accessors (fff_watch_events_count /\n// fff_watch_events_get_path / fff_watch_events_get_kind), so no struct\n// layout knowledge lives on this side.\n\n/** Map the C kind byte to the public WatchEventKind. */\nfunction watchKindFromU8(kind: number): WatchEventKind {\n switch (kind) {\n case 0:\n return \"created\";\n case 1:\n return \"modified\";\n case 2:\n return \"removed\";\n default:\n return \"rescan\";\n }\n}\n\n/** Trampoline signature: (watch id, batch address, user_data — unused). */\nconst WATCH_TRAMPOLINE_TYPE = funcConstructor({\n paramsType: [DataType.U64, DataType.U64, DataType.U64],\n retType: DataType.Void,\n});\n\n/** JS handlers keyed by process-unique native watch id. */\nconst watchHandlers = new Map<number, (events: WatchEvent[]) => void>();\n/** Instances (by handle identity) that ever created a watch subscription. */\nconst watchInstances = new Set<unknown>();\n\n/** Lazily created process-wide trampoline (createPointer result). */\nlet watchTrampoline: JsExternal[] | null = null;\n\n/** Convert a raw u64 address delivered through the trampoline to a JsExternal. */\nfunction addressToExternal(address: number): JsExternal {\n return load({\n library: LIBRARY_KEY,\n funcName: \"fff_ptr_offset\",\n retType: DataType.External,\n paramsType: [DataType.U64, DataType.U64],\n paramsValue: [address, 0],\n }) as unknown as JsExternal;\n}\n\n/** Parse an FffWatchEventBatch at `address` and free the native memory. */\nfunction consumeWatchBatch(address: number): WatchEvent[] {\n const batchPtr = addressToExternal(address);\n const count = load({\n library: LIBRARY_KEY,\n funcName: \"fff_watch_events_count\",\n retType: DataType.U32,\n paramsType: [DataType.External],\n paramsValue: [batchPtr],\n }) as unknown as number;\n\n const events: WatchEvent[] = [];\n for (let i = 0; i < count; i++) {\n const path = load({\n library: LIBRARY_KEY,\n funcName: \"fff_watch_events_get_path\",\n retType: DataType.External,\n paramsType: [DataType.External, DataType.U32],\n paramsValue: [batchPtr, i],\n }) as unknown as JsExternal;\n const kind = load({\n library: LIBRARY_KEY,\n funcName: \"fff_watch_events_get_kind\",\n retType: DataType.U8,\n paramsType: [DataType.External, DataType.U32],\n paramsValue: [batchPtr, i],\n }) as unknown as number;\n events.push({\n path: readCString(path) ?? \"\",\n kind: watchKindFromU8(kind),\n });\n }\n\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_watch_events\",\n retType: DataType.Void,\n paramsType: [DataType.U64],\n paramsValue: [address],\n });\n\n return events;\n}\n\n/**\n * The single native->JS entry point for all watch subscriptions. Runs on\n * the JS thread (threadsafe_function delivery); the batch is owned by us\n * and freed inside `consumeWatchBatch`. Unknown watch ids (unsubscribe\n * races) are benign: the batch is freed and dropped.\n */\nfunction watchTrampolineImpl(\n watchId: number,\n batchAddress: number,\n _userData: number,\n): void {\n const events = consumeWatchBatch(batchAddress);\n const handler = watchHandlers.get(Number(watchId));\n if (handler === undefined || events.length === 0) return;\n try {\n handler(events);\n } catch {\n // User callback errors must not propagate into the FFI layer\n }\n}\n\nfunction ensureWatchTrampoline(): JsExternal {\n if (watchTrampoline === null) {\n watchTrampoline = createPointer({\n paramsType: [WATCH_TRAMPOLINE_TYPE],\n paramsValue: [watchTrampolineImpl],\n });\n }\n return unwrapPointer(watchTrampoline)[0] as JsExternal;\n}\n\n// fff watcher uses a single cross-boundary FFI callback to deliver all events which we then manually\n// mapping to the user's javascript functions\nfunction ensureWatchCallbackRegistered(handle: NativeHandle): Result<void> {\n if (watchInstances.has(handle as unknown)) return { ok: true, value: undefined };\n const trampoline = ensureWatchTrampoline();\n const registered = callVoidResult(\n \"fff_set_watch_callback\",\n [DataType.External, DataType.External, DataType.U64],\n [handle, trampoline, 0],\n );\n if (registered.ok) watchInstances.add(handle as unknown);\n return registered;\n}\n\nfunction releaseWatchTrampolineIfIdle(): void {\n if (watchHandlers.size > 0 || watchInstances.size > 0 || watchTrampoline === null)\n return;\n freePointer({\n paramsType: [WATCH_TRAMPOLINE_TYPE],\n paramsValue: watchTrampoline,\n pointerType: PointerType.RsPointer,\n });\n watchTrampoline = null;\n}\n\n/**\n * Create a push-mode watch subscription. `callback` receives a normalized\n * batch of up to 128 events, delivered on the JS event loop.\n *\n * Returns the native watch id to pass to `ffiUnwatch`.\n */\nexport function ffiWatch(\n handle: NativeHandle,\n pattern: string,\n ignore: string[],\n callback: (events: WatchEvent[]) => void,\n): Result<number> {\n loadLibrary();\n\n const registered = ensureWatchCallbackRegistered(handle);\n if (!registered.ok) return registered;\n\n const created = callIntResult(\n \"fff_watch_args\",\n [DataType.External, DataType.String, DataType.StringArray, DataType.U32],\n [handle, pattern, ignore, ignore.length],\n );\n\n if (!created.ok) return created;\n\n // No startup race: threadsafe delivery lands on the JS event loop, so this\n // synchronous set always precedes the first routing lookup for this id.\n watchHandlers.set(created.value, callback);\n return created;\n}\n\n/**\n * Remove a watch subscription. Drops the JS handler synchronously — once\n * this returns the callback can never run again (a late native tail batch\n * misses the map lookup and is dropped).\n */\nexport function ffiUnwatch(handle: NativeHandle, watchId: number): Result<boolean> {\n const result = callBoolResult(\n \"fff_unwatch\",\n [DataType.External, DataType.U64],\n [handle, watchId],\n );\n watchHandlers.delete(watchId);\n return result;\n}\n\n/**\n * Post-`ffiDestroy` cleanup for an instance's watch state: drops any\n * handlers that were never explicitly unwatched and releases the process\n * trampoline when this was the last watching instance.\n */\nexport function ffiWatchCleanupAfterDestroy(\n handle: NativeHandle,\n watchIds: Iterable<number>,\n): void {\n for (const id of watchIds) {\n watchHandlers.delete(id);\n }\n watchInstances.delete(handle as unknown);\n releaseWatchTrampolineIfIdle();\n}\n\n/**\n * Health check.\n *\n * `handle` can be null for a limited check (version + git only).\n * When null, we pass DataType.U64 with value 0 as a null pointer workaround\n * since ffi-rs does not accept `null` for External parameters.\n */\nexport function ffiHealthCheck(\n handle: NativeHandle | null,\n testPath: string,\n): Result<unknown> {\n if (handle === null) {\n // Use U64(0) as a null pointer since ffi-rs rejects null for External params\n return callJsonResult<unknown>(\n \"fff_health_check\",\n [DataType.U64, DataType.String],\n [0, testPath],\n );\n }\n\n return callJsonResult<unknown>(\n \"fff_health_check\",\n [DataType.External, DataType.String],\n [handle, testPath],\n );\n}\n\n/**\n * Ensure the library is loaded.\n *\n * Loads the native library from the platform-specific npm package\n * or a local dev build. Throws if the binary is not found.\n */\nexport function ensureLoaded(): void {\n loadLibrary();\n}\n\n/**\n * Check if the library is available.\n */\nexport function isAvailable(): boolean {\n try {\n loadLibrary();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Close the library and release ffi-rs resources.\n * Call this when completely done with the library.\n */\nexport function closeLibrary(): void {\n if (isLoaded) {\n close(LIBRARY_KEY);\n isLoaded = false;\n }\n}\n",
8
- "// ----------------------------------------------------------------------------\n// GENERATED FILE - DO NOT EDIT.\n// Copied from: packages/shared/fff-api.ts\n// Run make sync-js-api from the repo root to regenerate.\n// ----------------------------------------------------------------------------\n\n/**\n * The shared public API surface for the fff file finder, implemented identically\n * by `@ff-labs/fff-node` and `@ff-labs/fff-bun`.\n *\n * This file is the single source of truth for every type, helper, and the\n * `FileFinderApi` interface that crosses the package boundary. It is copied\n * verbatim into each package's `src/fff-api.ts` by `make sync-api`.\n *\n * Anything that is not part of the public API (FFI struct layouts, binary\n * loading, platform detection, etc.) stays as per-package internal\n * implementation and must NOT live here.\n *\n * Keep this file self-contained: it must not import from any package-local\n * module, since each package compiles its own copy.\n */\n\n/**\n * Result type for all operations - follows the Result pattern\n */\nexport type Result<T> = { ok: true; value: T } | { ok: false; error: string };\n\n/**\n * Helper to create a successful result\n */\nexport function ok<T>(value: T): Result<T> {\n return { ok: true, value };\n}\n\n/**\n * Helper to create an error result\n */\nexport function err<T>(error: string): Result<T> {\n return { ok: false, error };\n}\n\n/**\n * Initialization options for the file finder\n */\nexport interface InitOptions {\n /** Base directory to index (required) */\n basePath: string;\n /** Path to frecency database (optional, omit to skip frecency initialization) */\n frecencyDbPath?: string;\n /** Path to query history database (optional, omit to skip query tracker initialization) */\n historyDbPath?: string;\n /** @deprecated no-op */\n useUnsafeNoLock?: boolean;\n /**\n * Disable mmap cache warmup after the initial scan. When mmap cache is\n * enabled (the default), the first grep search is as fast as subsequent\n * ones at the cost of a longer scan time and higher initial memory usage.\n */\n disableMmapCache?: boolean;\n /**\n * Disable the content index built after the initial scan.\n * Content indexing enables faster content-aware filtering during grep.\n */\n disableContentIndexing?: boolean;\n /**\n * Disable the background file-system watcher. When the watcher is\n * disabled, files are scanned once but not monitored for changes.\n * (default: false)\n */\n disableWatch?: boolean;\n /** enables optimizations for AI agent assistants. Provide as true if running via mcp/agent */\n aiMode?: boolean;\n /**\n * Path to the tracing log file. When set, the shared FFF tracing subscriber\n * is installed on first init and file output is written here. Omit to leave\n * logging uninitialized.\n */\n logFilePath?: string;\n /**\n * Log level for the tracing subscriber: \"trace\", \"debug\", \"info\", \"warn\",\n * or \"error\". Defaults to \"info\". Ignored when `logFilePath` is not set.\n */\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\";\n /**\n * Override for the content cache file-count cap. When omitted, the picker\n * auto-sizes the budget from the final scanned file count.\n */\n cacheBudgetMaxFiles?: number;\n /** Override for the content cache byte cap. See `cacheBudgetMaxFiles`. */\n cacheBudgetMaxBytes?: number;\n /** Override for the per-file byte cap in the content cache. */\n cacheBudgetMaxFileSize?: number;\n /**\n * Allow indexing the filesystem root (`/`).\n * Off by default, having fff instance at the large folder will generally require\n * file watcher and indexing which will consume a lot of resources if performed uncontrolled\n **/\n enableFsRootScanning?: boolean;\n /** Allow indexing the user's home directory. Same trade-off as `enableFsRootScanning`. */\n enableHomeDirScanning?: boolean;\n /** Follow symlinks for directories */\n followSymlinks?: boolean;\n}\n\n/**\n * Search options for fuzzy file search\n */\nexport interface SearchOptions {\n /** Maximum threads for parallel search (0 = auto) */\n maxThreads?: number;\n /** Current file path (for deprioritization in results) */\n currentFile?: string;\n /** Combo boost score multiplier (default: 100) */\n comboBoostMultiplier?: number;\n /** Minimum combo count for boost (default: 3) */\n minComboCount?: number;\n /** Page index for pagination (default: 0) */\n pageIndex?: number;\n /** Page size for pagination (default: 100) */\n pageSize?: number;\n}\n\n/**\n * Options for `glob`, the constraint-only search.\n *\n * The pattern is applied as a single pass SIMD optimized prefiltering\n * without any fuzzy matching involved. Faster and 100% compatible to npm `glob`.\n */\nexport interface GlobOptions {\n /** Maximum threads for parallel filtering (0 = auto). */\n maxThreads?: number;\n /** Current file path (for deprioritization in results). */\n currentFile?: string;\n /** Page index for pagination (default: 0). */\n pageIndex?: number;\n /** Page size for pagination (default: 100). */\n pageSize?: number;\n}\n\n/**\n * A file item in search results\n */\nexport interface FileItem {\n /** Path relative to the indexed directory */\n relativePath: string;\n /** File name only */\n fileName: string;\n /** File size in bytes */\n size: number;\n /** Last modified timestamp (Unix seconds) */\n modified: number;\n /** Frecency score based on access patterns */\n accessFrecencyScore: number;\n /** Frecency score based on modification time */\n modificationFrecencyScore: number;\n /** Combined frecency score */\n totalFrecencyScore: number;\n /** Git status: 'clean', 'modified', 'untracked', 'staged_new', etc. */\n gitStatus: string;\n}\n\n/**\n * Score breakdown for a search result\n */\nexport interface Score {\n /** Total combined score */\n total: number;\n /** Base fuzzy match score */\n baseScore: number;\n /** Bonus for filename match */\n filenameBonus: number;\n /** Bonus for special filenames (index.ts, main.rs, etc.) */\n specialFilenameBonus: number;\n /** Boost from frecency */\n frecencyBoost: number;\n /** Penalty for distance in path */\n distancePenalty: number;\n /** Penalty if this is the current file */\n currentFilePenalty: number;\n /** Boost from query history combo matching */\n comboMatchBoost: number;\n /** Whether this was an exact match */\n exactMatch: boolean;\n /** Type of match: 'fuzzy', 'exact', 'prefix', etc. */\n matchType: string;\n}\n\n/**\n * Location in file (from query like \"file.ts:42\")\n */\nexport type Location =\n | { type: \"line\"; line: number }\n | { type: \"position\"; line: number; col: number }\n | {\n type: \"range\";\n start: { line: number; col: number };\n end: { line: number; col: number };\n };\n\n/**\n * Search result from fuzzy file search\n */\nexport interface SearchResult {\n /** Matched file items */\n items: FileItem[];\n /** Corresponding scores for each item */\n scores: Score[];\n /** Total number of files that matched */\n totalMatched: number;\n /** Total number of indexed files */\n totalFiles: number;\n /** Location parsed from query (e.g., \"file.ts:42:10\") */\n location?: Location;\n}\n\n/**\n * A directory item in search results\n */\nexport interface DirItem {\n /** Path relative to the indexed directory (e.g., \"src/components/\") */\n relativePath: string;\n /** Last path segment (e.g., \"components/\" for \"src/components/\") */\n dirName: string;\n /** Maximum access frecency score among direct child files */\n maxAccessFrecency: number;\n}\n\n/**\n * Search options for directory search (subset of SearchOptions)\n */\nexport interface DirSearchOptions {\n /** Maximum threads for parallel search (0 = auto) */\n maxThreads?: number;\n /** Current file path (for distance scoring) */\n currentFile?: string;\n /** Page index for pagination (default: 0) */\n pageIndex?: number;\n /** Page size for pagination (default: 100) */\n pageSize?: number;\n}\n\n/**\n * Search result from fuzzy directory search\n */\nexport interface DirSearchResult {\n /** Matched directory items */\n items: DirItem[];\n /** Corresponding scores for each item */\n scores: Score[];\n /** Total number of directories that matched */\n totalMatched: number;\n /** Total number of indexed directories */\n totalDirs: number;\n}\n\n/**\n * A single item in a mixed (files + directories) search result\n */\nexport type MixedItem =\n | { type: \"file\"; item: FileItem }\n | { type: \"directory\"; item: DirItem };\n\n/**\n * Search result from mixed (files + directories) fuzzy search.\n * Items are interleaved by total score in descending order.\n */\nexport interface MixedSearchResult {\n /** Matched items (files and directories interleaved by score) */\n items: MixedItem[];\n /** Corresponding scores for each item */\n scores: Score[];\n /** Total number of items (files + dirs) that matched */\n totalMatched: number;\n /** Total number of indexed files */\n totalFiles: number;\n /** Total number of indexed directories */\n totalDirs: number;\n /** Location parsed from query */\n location?: Location;\n}\n\n/**\n * Scan progress information\n */\nexport interface ScanProgress {\n /** Number of files scanned so far */\n scannedFilesCount: number;\n /** Whether a scan is currently in progress */\n isScanning: boolean;\n /** Whether the background file watcher is ready */\n isWatcherReady: boolean;\n /** Whether the warmup/bigram phase has completed */\n isWarmupComplete: boolean;\n}\n\n/**\n * Normalized watch event kind.\n * A file removed and recreated in one processed batch is marked as modified.\n *\n * rescan = internal OS buffers were overloaded, some events might be missing.\n * The `path` is going to be a folder needs to be rescanned\n */\nexport type WatchEventKind = \"created\" | \"modified\" | \"removed\" | \"rescan\";\n\n/** A single filesystem change notification. */\nexport interface WatchEvent {\n /** Absolute path of the affected file (base path to rescan if `kind ==rescan`) */\n path: string;\n kind: WatchEventKind;\n}\n\n/** Options for watch subscriptions. */\nexport interface WatchOptions {\n /** Additional glob wildcard patterns to ignore */\n ignore?: string[];\n}\n\n/**\n * Receives normalized batches of up to 128 events. Each path appears once.\n */\nexport type WatchBatchCallback = (events: WatchEvent[]) => void;\n\n/** Call me to unsubscribe. */\nexport type WatchUnsubscribe = () => void;\n\n/** Database health information */\nexport interface DbHealth {\n /** Path to the database */\n path: string;\n /** Size of the database on disk in bytes */\n diskSize: number;\n}\n\n/**\n * Health check result\n */\nexport interface HealthCheck {\n /** Library version */\n version: string;\n /** Git integration status */\n git: {\n /** Whether git2 library is available */\n available: boolean;\n /** Whether a git repository was found */\n repositoryFound: boolean;\n /** Git working directory path */\n workdir?: string;\n /** libgit2 version string */\n libgit2Version: string;\n /** Error message if git detection failed */\n error?: string;\n };\n /** File picker status */\n filePicker: {\n /** Whether the file picker is initialized */\n initialized: boolean;\n /** Base path being indexed */\n basePath?: string;\n /** Whether a scan is in progress */\n isScanning?: boolean;\n /** Number of indexed files */\n indexedFiles?: number;\n /** Error message if there's an issue */\n error?: string;\n };\n /** Frecency database status */\n frecency: {\n /** Whether frecency tracking is initialized */\n initialized: boolean;\n /** Database health information */\n dbHealthcheck?: DbHealth;\n /** Error message if there's an issue */\n error?: string;\n };\n /** Query tracker status */\n queryTracker: {\n /** Whether query tracking is initialized */\n initialized: boolean;\n /** Database health information */\n dbHealthcheck?: DbHealth;\n /** Error message if there's an issue */\n error?: string;\n };\n}\n\n/**\n * Grep search mode\n */\nexport type GrepMode = \"plain\" | \"regex\" | \"fuzzy\";\n\n/**\n * Opaque pagination cursor for grep results.\n * Pass this to `GrepOptions.cursor` to fetch the next page.\n * Do not construct or modify this — use the `nextCursor` from a previous `GrepResult`.\n */\nexport interface GrepCursor {\n /** @internal */\n readonly __brand: \"GrepCursor\";\n /** @internal */\n readonly _offset: number;\n}\n\n/**\n * @internal Create a GrepCursor from a raw file offset.\n */\nexport function createGrepCursor(offset: number): GrepCursor {\n return { __brand: \"GrepCursor\" as const, _offset: offset };\n}\n\n/**\n * Options for live grep (content search)\n *\n * Files are searched sequentially in frecency order (most recently/frequently\n * accessed first). The engine returns a `nextCursor` for fetching the next page.\n */\nexport interface GrepOptions {\n /** Maximum file size to search in bytes. Files larger than this are skipped. (default: 10MB) */\n maxFileSize?: number;\n /** Maximum matching lines to collect from a single file (default: 200) */\n maxMatchesPerFile?: number;\n /** Smart case: case-insensitive when the query is all lowercase, case-sensitive otherwise (default: true) */\n smartCase?: boolean;\n /**\n * Pagination cursor from a previous `GrepResult.nextCursor`.\n * Omit (or pass `null`) for the first page.\n */\n cursor?: GrepCursor | null;\n /** Search mode (default: \"plain\") */\n mode?: GrepMode;\n /**\n * Maximum wall-clock time in milliseconds to spend searching before returning\n * partial results. 0 = unlimited. (default: 0)\n */\n timeBudgetMs?: number;\n /** Number of context lines to include before each match (default: 0) */\n beforeContext?: number;\n /** Number of context lines to include after each match (default: 0) */\n afterContext?: number;\n /** Maximum matches to return in this page across all files (default: 50) */\n pageSize?: number;\n /**\n * When true, classify each match line as a code definition (struct/fn/class/...)\n * and expose it via `GrepMatch.isDefinition`. Let callers re-rank defs first\n * without a TS-side regex port. (default: false)\n */\n classifyDefinitions?: boolean;\n}\n\n/**\n * A single grep match with file and line information\n */\nexport interface GrepMatch {\n /** Path relative to the indexed directory */\n relativePath: string;\n /** File name only */\n fileName: string;\n /** Git status */\n gitStatus: string;\n /** File size in bytes */\n size: number;\n /** Last modified timestamp (Unix seconds) */\n modified: number;\n /** Whether the file is binary */\n isBinary: boolean;\n /** Combined frecency score */\n totalFrecencyScore: number;\n /** Access-based frecency score */\n accessFrecencyScore: number;\n /** Modification-based frecency score */\n modificationFrecencyScore: number;\n /** 1-based line number of the match */\n lineNumber: number;\n /** 0-based byte column of first match start */\n col: number;\n /** Absolute byte offset of the matched line from file start */\n byteOffset: number;\n /** The matched line text (may be truncated) */\n lineContent: string;\n /** Byte offset pairs [start, end] within lineContent for highlighting */\n matchRanges: [number, number][];\n /** Fuzzy match score (only in fuzzy mode) */\n fuzzyScore?: number;\n /** Lines before the match (context). Empty array when context is 0. */\n contextBefore?: string[];\n /** Lines after the match (context). Empty array when context is 0. */\n contextAfter?: string[];\n /** Whether this line is a code definition (only populated when `classifyDefinitions: true`). */\n isDefinition?: boolean;\n}\n\n/**\n * Result from a grep search\n */\nexport interface GrepResult {\n /** Matched items with file and line information. At most `max_matches_per_file`. */\n items: GrepMatch[];\n /** Total number of matches collected (always equal to items.length). */\n totalMatched: number;\n /** Number of files actually opened and searched in this call */\n totalFilesSearched: number;\n /** Total number of indexed files (before any filtering) */\n totalFiles: number;\n /** Number of files eligible for search after filtering out binary files, oversized files, and constraint mismatches */\n filteredFileCount: number;\n /**\n * Cursor for the next page, or `null` if all eligible files have been searched.\n * Pass this as `GrepOptions.cursor` to continue from where this call left off.\n */\n nextCursor: GrepCursor | null;\n /** When regex mode fails to compile the pattern, the engine falls back to literal matching and this field contains the compilation error */\n regexFallbackError?: string;\n}\n\n/**\n * Options for multi-pattern grep (Aho-Corasick multi-needle search)\n *\n * Searches for lines matching ANY of the provided patterns using\n * SIMD-accelerated Aho-Corasick multi-pattern matching.\n */\nexport interface MultiGrepOptions {\n /** Patterns to search for (OR logic — matches lines containing any pattern) */\n patterns: string[];\n /** File constraints like \"*.rs\" or \"/src/\" */\n constraints?: string;\n /** Maximum file size to search in bytes (default: 10MB) */\n maxFileSize?: number;\n /** Maximum matching lines to collect from a single file (default: 0 = unlimited) */\n maxMatchesPerFile?: number;\n /** Smart case: case-insensitive when all patterns are lowercase (default: true) */\n smartCase?: boolean;\n /**\n * Pagination cursor from a previous `GrepResult.nextCursor`.\n * Omit (or pass `null`) for the first page.\n */\n cursor?: GrepCursor | null;\n /**\n * Maximum wall-clock time in milliseconds to spend searching before returning\n * partial results. 0 = unlimited. (default: 0)\n */\n timeBudgetMs?: number;\n /** Number of context lines to include before each match (default: 0) */\n beforeContext?: number;\n /** Number of context lines to include after each match (default: 0) */\n afterContext?: number;\n /** Maximum matches to return in this page across all files (default: 50) */\n pageSize?: number;\n /**\n * When true, classify each match line as a code definition (struct/fn/class/...)\n * and expose it via `GrepMatch.isDefinition`. (default: false)\n */\n classifyDefinitions?: boolean;\n}\n\n/**\n * The shared instance surface implemented by `FileFinder` in both\n * `@ff-labs/fff-node` and `@ff-labs/fff-bun`.\n *\n * Both packages must implement this identically. Only instance members belong\n * here. Static helpers (`create`, `isAvailable`, `ensureLoaded`,\n * `healthCheckStatic`) are package-specific and intentionally excluded.\n */\nexport interface FileFinderApi {\n /** Whether the instance has been destroyed. */\n readonly isDestroyed: boolean;\n\n /** Destroy and free all native resources. */\n destroy(): void;\n\n /** Fuzzy file search. */\n fileSearch(query: string, options?: SearchOptions): Result<SearchResult>;\n\n /** Glob-only filtering (no fuzzy matching). */\n glob(pattern: string, options?: GlobOptions): Result<SearchResult>;\n\n /** Fuzzy directory search. */\n directorySearch(query: string, options?: DirSearchOptions): Result<DirSearchResult>;\n\n /** Fuzzy search over files and directories interleaved by score. */\n mixedSearch(query: string, options?: SearchOptions): Result<MixedSearchResult>;\n\n /** Content search (live grep). */\n grep(query: string, options?: GrepOptions): Result<GrepResult>;\n\n /** Multi-pattern OR content search (Aho-Corasick). */\n multiGrep(options: MultiGrepOptions): Result<GrepResult>;\n\n /** Trigger an async rescan of the indexed directory. */\n scanFiles(): Result<void>;\n\n /** Whether a scan is currently in progress. */\n isScanning(): boolean;\n\n /** The root directory being indexed. */\n getBasePath(): Result<string | null>;\n\n /** Current scan progress snapshot. */\n getScanProgress(): Result<ScanProgress>;\n\n /**\n * Wait for the initial file scan to complete.\n *\n * Non-blocking: polls `isScanning` and yields to the event loop between\n * checks, so other async work keeps running while waiting.\n */\n waitForScan(timeoutMs?: number): Promise<Result<boolean>>;\n\n /**\n * Wait for the initial file scan to complete, blocking the calling thread.\n *\n * Backed by the native `fff_wait_for_scan` call. Prefer `waitForScan` unless\n * you specifically need synchronous blocking behaviour.\n */\n waitForScanBlocking(timeoutMs?: number): Result<boolean>;\n\n /**\n * Wait until the index is fully ready: the scan has finished and the warmup\n * (content indexing / bigram) phase has completed.\n *\n * Non-blocking: polls `getScanProgress` and yields to the event loop.\n */\n waitForIndexReady(timeoutMs?: number): Promise<Result<boolean>>;\n\n /** Restart indexing in a new directory. */\n reindex(newPath: string): Result<void>;\n\n /** Refresh the git status cache. Returns the number of updated files. */\n refreshGitStatus(): Result<number>;\n\n /** Record that `selectedFilePath` was chosen for `query`. */\n trackQuery(query: string, selectedFilePath: string): Result<boolean>;\n\n /** Get a historical query by offset (0 = most recent). */\n getHistoricalQuery(offset: number): Result<string | null>;\n\n /**\n * Subscribe to filesystem changes matching `pattern`.\n *\n * Patterns may be base-relative globs (./ works), exact paths inside the indexed\n * tree, or existing directories. An empty pattern watches the whole tree.\n *\n * Events are debounced and submitted in batches per 100-ms window at most 128 events.\n * Gitignored and other ignored files are never triggering watcher.\n */\n watch(callback: WatchBatchCallback, options?: WatchOptions): Result<WatchUnsubscribe>;\n watch(\n pattern: string,\n callback: WatchBatchCallback,\n options?: WatchOptions,\n ): Result<WatchUnsubscribe>;\n\n /** Health/diagnostics information for this instance. */\n healthCheck(testPath?: string): Result<HealthCheck>;\n}\n",
9
- "/**\n * FileFinder - High-level API for the fff file finder\n *\n * This class provides a type-safe, ergonomic API for file finding operations.\n * Each instance owns an independent native file picker that can be created\n * and destroyed independently. Multiple instances can coexist.\n *\n * All methods return Result types for explicit error handling.\n */\n\nimport {\n ensureLoaded,\n ffiCreate,\n ffiDestroy,\n ffiGetBasePath,\n ffiGetHistoricalQuery,\n ffiGetScanProgress,\n ffiGlob,\n ffiHealthCheck,\n ffiIsScanning,\n ffiLiveGrep,\n ffiMultiGrep,\n ffiRefreshGitStatus,\n ffiRestartIndex,\n ffiScanFiles,\n ffiSearch,\n ffiSearchDirectories,\n ffiSearchMixed,\n ffiTrackQuery,\n ffiUnwatch,\n ffiWaitForScan,\n ffiWatch,\n ffiWatchCleanupAfterDestroy,\n isAvailable,\n type NativeHandle,\n} from \"./ffi.js\";\n\nimport type {\n DirSearchOptions,\n DirSearchResult,\n FileFinderApi,\n GlobOptions,\n GrepOptions,\n GrepResult,\n HealthCheck,\n InitOptions,\n MixedSearchResult,\n MultiGrepOptions,\n Result,\n ScanProgress,\n SearchOptions,\n SearchResult,\n WatchBatchCallback,\n WatchOptions,\n WatchUnsubscribe,\n} from \"./fff-api.js\";\n\nimport { err } from \"./fff-api.js\";\n\n/**\n * FileFinder - Fast file finder with fuzzy search\n *\n * Each instance is backed by an independent native file picker. Create as many\n * as you need and destroy them when done.\n *\n * @example\n *\n * ```ts\n * import { FileFinder } from \"@ff-labs/fff-node\";\n *\n * // Create an instance\n * const finder = FileFinder.create({ basePath: \"/path/to/project\" });\n * if (!finder.ok) {\n * console.error(finder.error);\n * process.exit(1);\n * }\n *\n * // Wait for initial scan\n * await finder.value.waitForScan(5000);\n *\n * // Search for files\n * const search = finder.value.search(\"main.ts\");\n * if (search.ok) {\n * for (const item of search.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n *\n * // Cleanup\n * finder.value.destroy();\n * ```\n */\nexport class FileFinder implements FileFinderApi {\n private handle: NativeHandle | null;\n /** Native ids of this instance's active watch subscriptions. */\n private watchers = new Set<number>();\n\n private constructor(handle: NativeHandle) {\n this.handle = handle;\n }\n\n /**\n * Create a new file finder instance.\n *\n * @param options - Initialization options\n * @returns Result containing the new FileFinder instance or an error\n *\n * @example\n * ```typescript\n * // Basic initialization\n * const finder = FileFinder.create({ basePath: \"/path/to/project\" });\n *\n * // With custom database paths\n * const finder = FileFinder.create({\n * basePath: \"/path/to/project\",\n * frecencyDbPath: \"/custom/frecency.mdb\",\n * historyDbPath: \"/custom/history.mdb\",\n * });\n * ```\n */\n static create(options: InitOptions): Result<FileFinder> {\n const result = ffiCreate(\n options.basePath,\n options.frecencyDbPath ?? \"\",\n options.historyDbPath ?? \"\",\n options.useUnsafeNoLock ?? false,\n !(options.disableMmapCache ?? false),\n !(options.disableContentIndexing ?? options.disableMmapCache ?? false),\n !(options.disableWatch ?? false),\n options.aiMode ?? false,\n options.logFilePath ?? \"\",\n options.logLevel ?? \"\",\n options.cacheBudgetMaxFiles ?? 0,\n options.cacheBudgetMaxBytes ?? 0,\n options.cacheBudgetMaxFileSize ?? 0,\n options.enableFsRootScanning ?? false,\n options.enableHomeDirScanning ?? false,\n options.followSymlinks ?? false,\n );\n\n if (!result.ok) {\n return result;\n }\n\n return { ok: true, value: new FileFinder(result.value) };\n }\n\n /**\n * Destroy and clean up all resources.\n *\n * Call this when you're done using the file finder to free memory\n * and stop background file watching. After calling this, the instance\n * must not be used again.\n */\n destroy(): void {\n if (this.handle !== null) {\n const handle = this.handle;\n ffiDestroy(handle);\n this.handle = null;\n // ffiDestroy unsubscribes every watch (a delivery racing it is a\n // benign id-map miss); dropping the remaining handlers afterwards is\n // safe, and the process-wide trampoline is released when this was the\n // last watching instance.\n ffiWatchCleanupAfterDestroy(handle, this.watchers);\n this.watchers.clear();\n }\n }\n\n /**\n * Check if this instance has been destroyed.\n */\n get isDestroyed(): boolean {\n return this.handle === null;\n }\n\n /**\n * Guard that returns an error if the instance has been destroyed.\n */\n private ensureAlive(): Result<NativeHandle> {\n if (this.handle === null) {\n return err(\"FileFinder instance has been destroyed.\");\n }\n return { ok: true, value: this.handle };\n }\n\n /**\n * Search for files matching the query.\n *\n * The query supports fuzzy matching and special syntax:\n * - `foo bar` - Match files containing \"foo\" and \"bar\"\n * - `src/` - Match files in src directory\n * - `file.ts:42` - Match file.ts with line 42\n * - `file.ts:42:10` - Match file.ts with line 42, column 10\n *\n * @param query - Search query string\n * @param options - Search options\n * @returns Search results with matched files and scores\n *\n * @example\n * ```typescript\n * const result = finder.search(\"main.ts\", { pageSize: 10 });\n * if (result.ok) {\n * console.log(`Found ${result.value.totalMatched} files`);\n * for (const item of result.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n * ```\n */\n fileSearch(query: string, options?: SearchOptions): Result<SearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiSearch(\n guard.value,\n query,\n options?.currentFile ?? \"\",\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n options?.comboBoostMultiplier ?? 0,\n options?.minComboCount ?? 0,\n );\n }\n\n /**\n * Filters files using glob wildcard expression.\n *\n * The pattern is applied as a single pass SIMD optimized prefiltering\n * without any fuzzy matching involved. Faster and 100% compatible to npm `glob`.\n *\n * @param pattern - Glob pattern (required, non-empty)\n * @param options - Glob search options (pagination, max threads, current file)\n * @returns Search results with files matching the glob\n *\n * @example\n * ```typescript\n * const result = finder.glob(\"**\\/*.rs\", { pageSize: 100 });\n * if (result.ok) {\n * for (const item of result.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n * ```\n */\n glob(pattern: string, options?: GlobOptions): Result<SearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiGlob(\n guard.value,\n pattern,\n options?.currentFile ?? \"\",\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n );\n }\n\n /**\n * Search for directories matching the query.\n *\n * @param query - Search query string\n * @param options - Directory search options\n * @returns Search results with matched directories and scores\n *\n * @example\n * ```typescript\n * const result = finder.directorySearch(\"src/comp\", { pageSize: 10 });\n * if (result.ok) {\n * console.log(`Found ${result.value.totalMatched} directories`);\n * for (const item of result.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n * ```\n */\n directorySearch(query: string, options?: DirSearchOptions): Result<DirSearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiSearchDirectories(\n guard.value,\n query,\n options?.currentFile ?? null,\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n );\n }\n\n /**\n * Search for files and directories matching the query (mixed results).\n *\n * Results are interleaved by total score in descending order, mixing\n * both file and directory items.\n *\n * @param query - Search query string\n * @param options - Search options\n * @returns Mixed search results with files and directories interleaved by score\n *\n * @example\n * ```typescript\n * const result = finder.mixedSearch(\"main\", { pageSize: 20 });\n * if (result.ok) {\n * for (const entry of result.value.items) {\n * if (entry.type === \"file\") {\n * console.log(`File: ${entry.item.relativePath}`);\n * } else {\n * console.log(`Dir: ${entry.item.relativePath}`);\n * }\n * }\n * }\n * ```\n */\n mixedSearch(query: string, options?: SearchOptions): Result<MixedSearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiSearchMixed(\n guard.value,\n query,\n options?.currentFile ?? \"\",\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n options?.comboBoostMultiplier ?? 0,\n options?.minComboCount ?? 0,\n );\n }\n\n /**\n * Search file contents (live grep).\n *\n * Searches through the contents of indexed files using the specified mode:\n * - `\"plain\"` (default): SIMD-accelerated literal text matching\n * - `\"regex\"`: Regular expression matching\n * - `\"fuzzy\"`: Smith-Waterman fuzzy matching per line\n *\n * Supports pagination for large result sets. The result includes a `nextCursor`\n * that can be passed back to fetch the next page.\n *\n * The query also supports constraint syntax:\n * - `*.ts pattern` - Only search in TypeScript files\n * - `src/ pattern` - Only search in the src directory\n *\n * @param query - Search query string\n * @param options - Grep options (mode, pagination, limits)\n * @returns Grep results with matched lines and file metadata\n *\n * @example\n * ```typescript\n * // First page\n * const result = finder.grep(\"TODO\", { mode: \"plain\" });\n * if (result.ok) {\n * for (const match of result.value.items) {\n * console.log(`${match.relativePath}:${match.lineNumber}: ${match.lineContent}`);\n * }\n * // Fetch next page\n * if (result.value.nextCursor) {\n * const page2 = finder.grep(\"TODO\", {\n * cursor: result.value.nextCursor,\n * });\n * }\n * }\n * ```\n */\n grep(query: string, options?: GrepOptions): Result<GrepResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiLiveGrep(\n guard.value,\n query,\n options?.mode ?? \"plain\",\n options?.maxFileSize ?? 0,\n options?.maxMatchesPerFile ?? 0,\n options?.smartCase ?? true,\n options?.cursor?._offset ?? 0,\n options?.pageSize ?? 0,\n options?.timeBudgetMs ?? 0,\n options?.beforeContext ?? 0,\n options?.afterContext ?? 0,\n options?.classifyDefinitions ?? false,\n );\n }\n\n /**\n * Multi-pattern OR search using Aho-Corasick.\n *\n * Searches for lines matching ANY of the provided patterns using\n * SIMD-accelerated multi-needle matching. Faster than regex alternation\n * for literal text searches.\n *\n * Supports pagination. The result includes a `nextCursor` that can be\n * passed back to fetch the next page.\n *\n * @param options - Multi-grep options including patterns and optional constraints\n * @returns Grep results with matched lines and file metadata\n *\n * @example\n * ```typescript\n * const result = finder.multiGrep({\n * patterns: [\"VideoFrame\", \"video_frame\", \"PreloadedImage\"],\n * });\n * if (result.ok) {\n * for (const match of result.value.items) {\n * console.log(`${match.relativePath}:${match.lineNumber}: ${match.lineContent}`);\n * }\n * }\n * ```\n */\n multiGrep(options: MultiGrepOptions): Result<GrepResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n if (!options.patterns || options.patterns.length === 0) {\n return err(\"patterns array must have at least 1 element\");\n }\n\n return ffiMultiGrep(\n guard.value,\n options.patterns.join(\"\\n\"),\n options.constraints ?? \"\",\n options.maxFileSize ?? 0,\n options.maxMatchesPerFile ?? 0,\n options.smartCase ?? true,\n options.cursor?._offset ?? 0,\n options.pageSize ?? 0,\n options.timeBudgetMs ?? 0,\n options.beforeContext ?? 0,\n options.afterContext ?? 0,\n options.classifyDefinitions ?? false,\n );\n }\n\n /**\n * Trigger a rescan of the indexed directory.\n *\n * This is useful after major file system changes that the\n * background watcher might have missed.\n */\n scanFiles(): Result<void> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiScanFiles(guard.value);\n }\n\n /**\n * Check if a scan is currently in progress.\n */\n isScanning(): boolean {\n if (this.handle === null) return false;\n return ffiIsScanning(this.handle);\n }\n\n /**\n * Get the base path of the file picker (the root directory being indexed).\n */\n getBasePath(): Result<string | null> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiGetBasePath(guard.value);\n }\n\n /**\n * Get the current scan progress.\n */\n getScanProgress(): Result<ScanProgress> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiGetScanProgress(guard.value) as Result<ScanProgress>;\n }\n\n /**\n * Wait for the initial file scan to complete.\n * Non-blocking: polls `isScanning` and yields to the event loop between checks.\n *\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)\n * @returns true if scan completed, false if timed out\n *\n * @example\n * ```typescript\n * const finder = FileFinder.create({ basePath: \"/path/to/project\" });\n * if (finder.ok) {\n * const completed = await finder.value.waitForScan(10000);\n * if (!completed.ok || !completed.value) {\n * console.warn(\"Scan did not complete in time\");\n * }\n * }\n * ```\n */\n async waitForScan(timeoutMs: number = 5000): Promise<Result<boolean>> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n const deadline = Date.now() + timeoutMs;\n while (this.isScanning()) {\n if (Date.now() >= deadline) {\n return { ok: true, value: false };\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return { ok: true, value: true };\n }\n\n /**\n * Wait for the initial file scan to complete, blocking the calling thread.\n *\n * Backed by the native `fff_wait_for_scan` call. Prefer {@link waitForScan}\n * unless you specifically need synchronous blocking behaviour.\n *\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)\n * @returns true if scan completed, false if timed out\n */\n waitForScanBlocking(timeoutMs: number = 5000): Result<boolean> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiWaitForScan(guard.value, timeoutMs);\n }\n\n /**\n * Wait until the index is fully ready: the scan has finished and the warmup\n * (content indexing / bigram) phase has completed.\n *\n * Non-blocking: polls `getScanProgress` and yields to the event loop.\n *\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)\n * @returns true if the index became ready, false if timed out\n */\n async waitForIndexReady(timeoutMs: number = 5000): Promise<Result<boolean>> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n const deadline = Date.now() + timeoutMs;\n while (true) {\n const progress = this.getScanProgress();\n if (!progress.ok) return progress;\n if (!progress.value.isScanning && progress.value.isWarmupComplete) {\n return { ok: true, value: true };\n }\n if (Date.now() >= deadline) {\n return { ok: true, value: false };\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n }\n\n /**\n * Change the indexed directory to a new path.\n *\n * This stops the current file watcher and starts indexing the new directory.\n *\n * @param newPath - New directory path to index\n */\n reindex(newPath: string): Result<void> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiRestartIndex(guard.value, newPath);\n }\n\n /**\n * Refresh the git status cache.\n *\n * @returns Number of files with updated git status\n */\n refreshGitStatus(): Result<number> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiRefreshGitStatus(guard.value);\n }\n\n /**\n * Track query completion for smart suggestions.\n *\n * Call this when a user selects a file from search results.\n * This helps improve future search rankings for similar queries.\n *\n * @param query - The search query that was used\n * @param selectedFilePath - The file path that was selected\n */\n trackQuery(query: string, selectedFilePath: string): Result<boolean> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiTrackQuery(guard.value, query, selectedFilePath);\n }\n\n /**\n * Get a historical query by offset.\n *\n * @param offset - Offset from most recent (0 = most recent)\n * @returns The historical query string, or null if not found\n */\n getHistoricalQuery(offset: number): Result<string | null> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiGetHistoricalQuery(guard.value, offset);\n }\n\n /**\n * Subscribe to filesystem changes matching `pattern`.\n *\n * Pattern semantics:\n * - Wildcards (`*.rs`, `src/**`, `./**\\/*.ts`) — glob matched against the\n * base-path-relative path. Absolute globs must be under the base path.\n * - No wildcards — resolved against the base path (must stay inside it):\n * an existing directory subscribes to its whole subtree, anything else\n * is an exact file path.\n * - Omitted — subscribes to the entire indexed tree: `watch(callback)`.\n *\n * Push-based: events are delivered by the native watcher through a single\n * process-wide callback trampoline (no polling, no idle wakeups). The\n * callback receives normalized batches of up to 128 events on the JS event\n * loop, with each path appearing at most once.\n *\n * Requires the instance to be created with watching enabled (default).\n *\n * @param pattern - Glob pattern, exact file path, or directory\n * @param callback - Invoked with each batch of events\n * @param options - Watch options\n * @returns Unsubscribe handle; call it to stop\n *\n * @example\n * ```typescript\n * const sub = finder.watch(\"**\\/*.rs\", (events) => {\n * for (const e of events) console.log(e.kind, e.path);\n * });\n * if (sub.ok) sub.value(); // unsubscribe\n *\n * // no pattern: everything under the indexed base path\n * const all = finder.watch((events) => console.log(events.length));\n * ```\n */\n watch(callback: WatchBatchCallback, options?: WatchOptions): Result<WatchUnsubscribe>;\n watch(\n pattern: string,\n callback: WatchBatchCallback,\n options?: WatchOptions,\n ): Result<WatchUnsubscribe>;\n watch(\n patternOrCallback: string | WatchBatchCallback,\n callbackOrOptions?: WatchBatchCallback | WatchOptions,\n maybeOptions?: WatchOptions,\n ): Result<WatchUnsubscribe> {\n // Overload shift: watch(cb, opts?) -> empty pattern = whole tree.\n const noPattern = typeof patternOrCallback === \"function\";\n const pattern = noPattern ? \"\" : patternOrCallback;\n const callback = noPattern\n ? patternOrCallback\n : (callbackOrOptions as WatchBatchCallback);\n const options = noPattern\n ? (callbackOrOptions as WatchOptions | undefined)\n : maybeOptions;\n\n if (typeof callback !== \"function\") {\n return err(\"watch callback must be a function\");\n }\n\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n const created = ffiWatch(guard.value, pattern, options?.ignore ?? [], callback);\n if (!created.ok) return created;\n\n const watchId = created.value;\n this.watchers.add(watchId);\n\n return {\n ok: true,\n value: () => {\n if (!this.watchers.delete(watchId)) return;\n if (this.handle !== null) ffiUnwatch(this.handle, watchId);\n },\n };\n }\n\n /**\n * Get health check information.\n *\n * Useful for debugging and verifying the file finder is working correctly.\n *\n * @param testPath - Optional path to test git repository detection\n */\n healthCheck(testPath?: string): Result<HealthCheck> {\n return ffiHealthCheck(this.handle, testPath || \"\") as Result<HealthCheck>;\n }\n\n /**\n * Check if the native library is available.\n */\n static isAvailable(): boolean {\n return isAvailable();\n }\n\n /**\n * Ensure the native library is loaded.\n *\n * Loads the native library from the platform-specific npm package\n * or a local dev build. Throws if the binary is not found.\n */\n static ensureLoaded(): void {\n ensureLoaded();\n }\n\n /**\n * Get a health check without requiring an instance.\n *\n * Returns limited info (version + git only, no picker/frecency/query data).\n *\n * @param testPath - Optional path to test git repository detection\n */\n static healthCheckStatic(testPath?: string): Result<HealthCheck> {\n return ffiHealthCheck(null, testPath || \"\") as Result<HealthCheck>;\n }\n}\n"
10
- ],
11
- "mappings": "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",
12
- "debugId": "12D927B23EC3D3EE64756E2164756E21",
13
- "names": []
14
- }
1
+ {"version":3,"sources":["../src/binary.ts","../src/platform.ts","../src/ffi.ts","../src/fff-api.ts","../src/finder.ts"],"sourcesContent":["/**\n * Binary resolution utilities for fff-node\n *\n * Resolves the native library from:\n * 1. Platform-specific npm package (e.g. @ff-labs/fff-bin-darwin-arm64)\n * 2. Local dev build (target/release or target/debug)\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getLibFilename, getNpmPackageName } from \"./platform.js\";\n\n/**\n * Get the current file's directory\n */\nfunction getCurrentDir(): string {\n // CJS build: import.meta.url is inlined at bundle time, __dirname is the truth\n if (typeof __dirname !== \"undefined\") return __dirname;\n\n const url = import.meta.url;\n\n if (url.startsWith(\"file://\")) {\n return dirname(fileURLToPath(url));\n }\n return dirname(url);\n}\n\n/**\n * Get the package root directory\n */\nfunction getPackageDir(): string {\n const currentDir = getCurrentDir();\n // In dev: src/ -> package root\n // In dist: dist/ -> package root\n // We look for package.json to find the actual root\n let dir = currentDir;\n for (let i = 0; i < 5; i++) {\n if (existsSync(join(dir, \"package.json\"))) {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, \"package.json\"), \"utf-8\"));\n if (pkg.name === \"@ff-labs/fff-node\") {\n return dir;\n }\n } catch {\n // Not our package.json, keep going up\n }\n }\n dir = dirname(dir);\n }\n // Fallback: assume we're one level deep in src/\n return dirname(currentDir);\n}\n\n/**\n * Check if the binary exists in any known location\n */\nexport function binaryExists(): boolean {\n return findBinary() !== null;\n}\n\n/**\n * Try to resolve the binary from the platform-specific npm package.\n *\n * When users install @ff-labs/fff-node, npm automatically installs the matching\n * optionalDependency (e.g. @ff-labs/fff-bin-darwin-arm64). We resolve the binary\n * path by requiring that package's package.json and looking for the binary\n * in the same directory.\n */\nfunction resolveFromNpmPackage(): string | null {\n const packageName = getNpmPackageName();\n\n try {\n // Use createRequire to resolve the platform package's location\n const require = createRequire(join(getPackageDir(), \"package.json\"));\n const packageJsonPath = require.resolve(`${packageName}/package.json`);\n const packageDir = dirname(packageJsonPath);\n const binaryPath = join(packageDir, getLibFilename());\n\n if (existsSync(binaryPath)) {\n return binaryPath;\n }\n } catch {\n // Package not installed - this is expected on unsupported platforms\n // or when installed without optional dependencies\n }\n\n return null;\n}\n\n/**\n * Get the development binary path (for local development)\n */\nfunction getDevBinaryPath(): string | null {\n const packageDir = getPackageDir();\n const workspaceRoot = join(packageDir, \"..\", \"..\");\n\n const possiblePaths = [\n join(workspaceRoot, \"target\", \"release\", getLibFilename()),\n join(workspaceRoot, \"target\", \"debug\", getLibFilename()),\n ];\n\n for (const path of possiblePaths) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n}\n\nfunction isDevWorkspace(): boolean {\n const packageDir = getPackageDir();\n const workspaceRoot = join(packageDir, \"..\", \"..\");\n return existsSync(join(workspaceRoot, \"Cargo.toml\"));\n}\n\n/**\n * Find the native library binary.\n *\n * Resolution order:\n * - Dev workspace: local dev build first, then npm package\n * - Production: npm package first, then dev build\n *\n * @returns Absolute path to the library, or null if not found\n */\nexport function findBinary(): string | null {\n if (isDevWorkspace()) {\n // 1. Local bin/ directory (populated by `make prepare-node`)\n const binPath = join(getPackageDir(), \"bin\", getLibFilename());\n if (existsSync(binPath)) return binPath;\n\n // 2. Local dev build (target/release or target/debug)\n const devPath = getDevBinaryPath();\n if (devPath) return devPath;\n\n // 3. Fallback to npm package\n const npmPath = resolveFromNpmPackage();\n if (npmPath) return npmPath;\n\n return null;\n }\n\n // Production: npm package first\n const npmPath = resolveFromNpmPackage();\n if (npmPath) return npmPath;\n\n // Fallback: local dev build (e.g. user built from source)\n return getDevBinaryPath();\n}\n","/**\n * Platform detection utilities for downloading the correct binary\n */\n\nimport { execSync } from \"node:child_process\";\n\n/**\n * Get the platform triple (e.g., \"x86_64-unknown-linux-gnu\")\n */\nexport function getTriple(): string {\n const platform = process.platform;\n const arch = process.arch;\n\n let osName: string;\n if (platform === \"darwin\") {\n osName = \"apple-darwin\";\n } else if (platform === \"android\") {\n osName = \"linux-android\";\n } else if (platform === \"linux\") {\n osName = detectLinuxLibc();\n } else if (platform === \"win32\") {\n osName = \"pc-windows-msvc\";\n } else {\n throw new Error(`Unsupported platform: ${platform}`);\n }\n\n const archName = normalizeArch(arch);\n return `${archName}-${osName}`;\n}\n\n/**\n * Detect whether we're on musl or glibc Linux\n */\nfunction detectLinuxLibc(): string {\n let output = \"\";\n try {\n output = execSync(\"ldd --version 2>&1\", {\n encoding: \"utf-8\",\n timeout: 5000,\n });\n } catch (e: unknown) {\n const err = e as { stdout?: string | Buffer; stderr?: string | Buffer };\n output = String(err?.stdout ?? \"\") + String(err?.stderr ?? \"\");\n }\n\n // ldd on musl can produce stdout with musl either with exit code 1 or 0\n if (output.toLowerCase().includes(\"musl\")) {\n return \"unknown-linux-musl\";\n }\n return \"unknown-linux-gnu\";\n}\n\n/**\n * Normalize architecture name to Rust target format\n */\nfunction normalizeArch(arch: string): string {\n switch (arch) {\n case \"x64\":\n case \"amd64\":\n return \"x86_64\";\n case \"arm64\":\n return \"aarch64\";\n case \"arm\":\n return \"arm\";\n default:\n throw new Error(`Unsupported architecture: ${arch}`);\n }\n}\n\n/**\n * Get the library file extension for the current platform\n */\nexport function getLibExtension(): \"dylib\" | \"so\" | \"dll\" {\n switch (process.platform) {\n case \"darwin\":\n return \"dylib\";\n case \"win32\":\n return \"dll\";\n default:\n return \"so\";\n }\n}\n\n/**\n * Get the library filename prefix (empty on Windows)\n */\nexport function getLibPrefix(): string {\n return process.platform === \"win32\" ? \"\" : \"lib\";\n}\n\n/**\n * Get the full library filename for the current platform\n */\nexport function getLibFilename(): string {\n const prefix = getLibPrefix();\n const ext = getLibExtension();\n return `${prefix}fff_c.${ext}`;\n}\n\n/**\n * Map from Rust target triple to npm platform package name.\n * The @ff-labs/fff-bin-* packages contain the pre-built libfff_c\n * shared library and are runtime-agnostic (used by both Bun and Node).\n */\nconst TRIPLE_TO_NPM_PACKAGE: Record<string, string> = {\n \"aarch64-apple-darwin\": \"@ff-labs/fff-bin-darwin-arm64\",\n \"x86_64-apple-darwin\": \"@ff-labs/fff-bin-darwin-x64\",\n \"x86_64-unknown-linux-gnu\": \"@ff-labs/fff-bin-linux-x64-gnu\",\n \"aarch64-unknown-linux-gnu\": \"@ff-labs/fff-bin-linux-arm64-gnu\",\n \"x86_64-unknown-linux-musl\": \"@ff-labs/fff-bin-linux-x64-musl\",\n \"aarch64-unknown-linux-musl\": \"@ff-labs/fff-bin-linux-arm64-musl\",\n \"x86_64-pc-windows-msvc\": \"@ff-labs/fff-bin-win32-x64\",\n \"aarch64-pc-windows-msvc\": \"@ff-labs/fff-bin-win32-arm64\",\n \"aarch64-linux-android\": \"@ff-labs/fff-bin-android-arm64\",\n};\n\n/**\n * Get the npm package name for the current platform's native binary.\n *\n * @returns Package name like \"@ff-labs/fff-bin-darwin-arm64\"\n * @throws If the current platform is not supported\n */\nexport function getNpmPackageName(): string {\n const triple = getTriple();\n const packageName = TRIPLE_TO_NPM_PACKAGE[triple];\n if (!packageName) {\n throw new Error(`No npm package available for platform: ${triple}`);\n }\n return packageName;\n}\n","/**\n * Node.js FFI bindings for the fff-c native library using ffi-rs\n *\n * This module uses ffi-rs to call into the Rust C library.\n * All functions follow the Result pattern for error handling.\n *\n * The API is instance-based: `ffiCreate` returns an opaque handle that must\n * be passed to all subsequent calls and freed with `ffiDestroy`.\n *\n * ## Memory management\n *\n * Every `fff_*` function returning `*mut FffResult` allocates with Rust's Box.\n * We MUST call `fff_free_result` to properly deallocate (not libc::free).\n *\n * ## FffResult struct reading\n *\n * The FffResult struct layout (#[repr(C)]):\n * offset 0: success (bool, 1 byte + 7 padding)\n * offset 8: data pointer (8 bytes) - *mut c_char (JSON string or null)\n * offset 16: error pointer (8 bytes) - *mut c_char (error message or null)\n * offset 24: handle pointer (8 bytes) - *mut c_void (instance handle or null)\n *\n * ## Two-step approach for reading + freeing\n *\n * ffi-rs auto-dereferences struct retType pointers, losing the original pointer.\n * We solve this by:\n * 1. Calling the C function with `retType: DataType.External` to get the raw pointer\n * 2. Using `restorePointer` to read the struct fields from the raw pointer\n * 3. Calling `fff_free_result` with the original raw pointer\n *\n * ## Null pointer detection\n *\n * `isNullPointer` from ffi-rs correctly detects null C pointers wrapped as\n * V8 External objects. We use this instead of truthy checks.\n */\n\nimport {\n close,\n createPointer,\n DataType,\n type FieldType,\n freePointer,\n funcConstructor,\n isNullPointer,\n type JsExternal,\n load,\n open,\n PointerType,\n restorePointer,\n unwrapPointer,\n wrapPointer,\n} from \"ffi-rs\";\nimport { findBinary } from \"./binary.js\";\nimport type {\n DirItem,\n DirSearchResult,\n FileItem,\n GrepMatch,\n GrepResult,\n Location,\n MixedItem,\n MixedSearchResult,\n Result,\n Score,\n SearchResult,\n WatchEvent,\n WatchEventKind,\n} from \"./fff-api.js\";\nimport { createGrepCursor, err } from \"./fff-api.js\";\n\nconst LIBRARY_KEY = \"fff_c\";\n\nconst FFF_CREATE_OPTIONS_STRUCT = {\n version: DataType.U32,\n base_path: DataType.String,\n frecency_db_path: DataType.String,\n history_db_path: DataType.String,\n enable_mmap_cache: DataType.U8,\n enable_content_indexing: DataType.U8,\n watch: DataType.U8,\n ai_mode: DataType.U8,\n log_file_path: DataType.String,\n log_level: DataType.String,\n cache_budget_max_files: DataType.U64,\n cache_budget_max_bytes: DataType.U64,\n cache_budget_max_file_size: DataType.U64,\n enable_fs_root_scanning: DataType.U8,\n enable_home_dir_scanning: DataType.U8,\n follow_symlinks: DataType.U8,\n};\n\n// ALWAYS KEEP IN SYNC WITH fff.h\nconst FFF_CREATE_OPTIONS_VERSION = 2;\n\n/** Grep mode constants matching the C API (u8). */\nconst GREP_MODE_PLAIN = 0;\nconst GREP_MODE_REGEX = 1;\nconst GREP_MODE_FUZZY = 2;\n\n/** Map string mode to u8 */\nfunction grepModeToU8(mode?: string): number {\n switch (mode) {\n case \"regex\":\n return GREP_MODE_REGEX;\n case \"fuzzy\":\n return GREP_MODE_FUZZY;\n default:\n return GREP_MODE_PLAIN;\n }\n}\n\n// Track whether the library is loaded\nlet isLoaded = false;\n\n/**\n * Struct type definition for FffResult used with restorePointer.\n *\n * Uses U8 for the bool success field (correct alignment with ffi-rs).\n * Uses External for ALL pointer fields to avoid hangs on null char* pointers\n * (ffi-rs hangs when trying to read DataType.String from null char*).\n */\nconst FFF_RESULT_STRUCT = {\n success: DataType.U8,\n error: DataType.External,\n handle: DataType.External,\n int_value: DataType.I64,\n};\n\ninterface FffResultRaw {\n success: number;\n error: JsExternal;\n handle: JsExternal;\n int_value: number;\n}\n\n/**\n * Load the native library using ffi-rs\n */\nfunction loadLibrary(): void {\n if (isLoaded) return;\n\n const binaryPath = findBinary();\n if (!binaryPath) {\n throw new Error(\n \"fff native library not found. Run `npx @ff-labs/fff-node download` or build from source with `cargo build --release -p fff-c`\",\n );\n }\n\n open({ library: LIBRARY_KEY, path: binaryPath });\n isLoaded = true;\n}\n\n/**\n * Convert snake_case keys to camelCase recursively\n */\nfunction snakeToCamel(obj: unknown): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj !== \"object\") return obj;\n if (Array.isArray(obj)) return obj.map(snakeToCamel);\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n const camelKey = key.replace(/_([a-z])/g, (_, letter: string) =>\n letter.toUpperCase(),\n );\n result[camelKey] = snakeToCamel(value);\n }\n return result;\n}\n\n/**\n * Read a C string (char*) from an ffi-rs External pointer.\n *\n * Uses restorePointer + wrapPointer to dereference the char* and read the\n * null-terminated string. Returns null if the pointer is null.\n */\nfunction readCString(ptr: JsExternal): string | null {\n if (isNullPointer(ptr)) return null;\n try {\n const [str] = restorePointer({\n retType: [DataType.String],\n paramsValue: wrapPointer([ptr]),\n });\n return str as string;\n } catch {\n return null;\n }\n}\n\n/**\n * Call a C function that returns `*mut FffResult` and get both the raw pointer\n * (for freeing) and the parsed struct fields.\n *\n * Step 1: Call function with `DataType.External` retType → raw pointer\n * Step 2: Use `restorePointer` to read struct fields from the raw pointer\n */\nfunction callRaw(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): { rawPtr: JsExternal; struct: FffResultRaw } {\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName,\n retType: DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n }) as JsExternal;\n\n const [structData] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n return { rawPtr, struct: structData };\n}\n\n/**\n * Free a FffResult pointer by calling fff_free_result.\n *\n * This frees the FffResult struct and its data/error strings using Rust's\n * Box::from_raw and CString::from_raw. The handle field is NOT freed.\n */\nfunction freeResult(resultPtr: JsExternal): void {\n try {\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [resultPtr],\n });\n } catch {\n // Ignore cleanup errors\n }\n}\n\n/**\n * Read the FffResult envelope from a raw call. Returns the parsed struct + raw pointer.\n * On error, frees the result and returns a Result error.\n */\nfunction readResultEnvelope(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): { rawPtr: JsExternal; struct: FffResultRaw } | Result<never> {\n loadLibrary();\n const { rawPtr, struct: structData } = callRaw(funcName, paramsType, paramsValue);\n\n if (structData.success === 0) {\n const errorStr = readCString(structData.error);\n freeResult(rawPtr);\n return err(errorStr || \"Unknown error\");\n }\n\n return { rawPtr, struct: structData };\n}\n\n/** Call a function returning FffResult with void payload. */\nfunction callVoidResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<void> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n freeResult(res.rawPtr);\n return { ok: true, value: undefined };\n}\n\n/** Call a function returning FffResult with int_value payload. */\nfunction callIntResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<number> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const value = Number(res.struct.int_value);\n freeResult(res.rawPtr);\n return { ok: true, value };\n}\n\n/** Call a function returning FffResult with bool in int_value. */\nfunction callBoolResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<boolean> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const value = Number(res.struct.int_value) !== 0;\n freeResult(res.rawPtr);\n return { ok: true, value };\n}\n\n/** Call a function returning FffResult with a C string in handle. */\nfunction callStringResult(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<string | null> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const handlePtr = res.struct.handle;\n freeResult(res.rawPtr);\n if (isNullPointer(handlePtr)) return { ok: true, value: null };\n const str = readCString(handlePtr);\n freeString(handlePtr);\n return { ok: true, value: str };\n}\n\n/** Call a function returning FffResult with a JSON string in handle. */\nfunction callJsonResult<T>(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<T> {\n const res = readResultEnvelope(funcName, paramsType, paramsValue);\n if (\"ok\" in res) return res;\n const handlePtr = res.struct.handle;\n freeResult(res.rawPtr);\n if (isNullPointer(handlePtr)) return { ok: true, value: undefined as T };\n const jsonStr = readCString(handlePtr);\n freeString(handlePtr);\n if (jsonStr === null || jsonStr === \"\") return { ok: true, value: undefined as T };\n try {\n return { ok: true, value: snakeToCamel(JSON.parse(jsonStr)) as T };\n } catch {\n return { ok: true, value: jsonStr as T };\n }\n}\n\n/** Free a C string via fff_free_string. */\nfunction freeString(ptr: JsExternal): void {\n try {\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_string\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [ptr],\n });\n } catch {\n // Ignore\n }\n}\n\n/**\n * Opaque native handle type. Callers must not inspect or modify this value.\n */\nexport type NativeHandle = JsExternal;\n\nexport function ffiCreate(\n basePath: string,\n frecencyDbPath: string,\n historyDbPath: string,\n _useUnsafeNoLock: boolean,\n enableMmapCache: boolean,\n enableContentIndexing: boolean,\n watch: boolean,\n aiMode: boolean,\n logFilePath: string,\n logLevel: string,\n cacheBudgetMaxFiles: number,\n cacheBudgetMaxBytes: number,\n cacheBudgetMaxFileSize: number,\n enableFsRootScanning: boolean,\n enableHomeDirScanning: boolean,\n followSymlinks: boolean,\n): Result<NativeHandle> {\n loadLibrary();\n\n const optsValue = {\n version: FFF_CREATE_OPTIONS_VERSION,\n base_path: basePath,\n frecency_db_path: frecencyDbPath,\n history_db_path: historyDbPath,\n enable_mmap_cache: enableMmapCache ? 1 : 0,\n enable_content_indexing: enableContentIndexing ? 1 : 0,\n watch: watch ? 1 : 0,\n ai_mode: aiMode ? 1 : 0,\n log_file_path: logFilePath,\n log_level: logLevel,\n cache_budget_max_files: cacheBudgetMaxFiles,\n cache_budget_max_bytes: cacheBudgetMaxBytes,\n cache_budget_max_file_size: cacheBudgetMaxFileSize,\n enable_fs_root_scanning: enableFsRootScanning ? 1 : 0,\n enable_home_dir_scanning: enableHomeDirScanning ? 1 : 0,\n follow_symlinks: followSymlinks ? 1 : 0,\n };\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_create_instance_with\",\n retType: DataType.External,\n paramsType: [FFF_CREATE_OPTIONS_STRUCT],\n paramsValue: [optsValue],\n freeResultMemory: false,\n }) as JsExternal;\n\n const [structData] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = structData.success !== 0;\n\n try {\n if (success) {\n const handle = structData.handle;\n if (isNullPointer(handle)) {\n return err(\"fff_create_instance_with returned null handle\");\n }\n return { ok: true, value: handle };\n } else {\n const errorStr = readCString(structData.error);\n return err(errorStr || \"Unknown error\");\n }\n } finally {\n freeResult(rawPtr);\n }\n}\n\n/**\n * Destroy and clean up an instance.\n */\nexport function ffiDestroy(handle: NativeHandle): void {\n loadLibrary();\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_destroy\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handle],\n });\n}\n\n// ---------------------------------------------------------------------------\n// Struct type definitions for restorePointer (must match #[repr(C)] layout)\n// ---------------------------------------------------------------------------\n\nconst FFF_FILE_ITEM_STRUCT = {\n relative_path: DataType.External,\n file_name: DataType.External,\n git_status: DataType.External,\n size: DataType.U64,\n modified: DataType.U64,\n access_frecency_score: DataType.I64,\n modification_frecency_score: DataType.I64,\n total_frecency_score: DataType.I64,\n is_binary: DataType.U8,\n};\n\ninterface FffFileItemRaw {\n relative_path: JsExternal;\n file_name: JsExternal;\n git_status: JsExternal;\n size: number;\n modified: number;\n access_frecency_score: number;\n modification_frecency_score: number;\n total_frecency_score: number;\n is_binary: number;\n}\n\nconst FFF_SCORE_STRUCT = {\n total: DataType.I32,\n base_score: DataType.I32,\n filename_bonus: DataType.I32,\n special_filename_bonus: DataType.I32,\n frecency_boost: DataType.I32,\n distance_penalty: DataType.I32,\n current_file_penalty: DataType.I32,\n combo_match_boost: DataType.I32,\n exact_match: DataType.U8,\n match_type: DataType.External,\n};\n\ninterface FffScoreRaw {\n total: number;\n base_score: number;\n filename_bonus: number;\n special_filename_bonus: number;\n frecency_boost: number;\n distance_penalty: number;\n current_file_penalty: number;\n combo_match_boost: number;\n exact_match: number;\n match_type: JsExternal;\n}\n\nconst FFF_SEARCH_RESULT_STRUCT = {\n items: DataType.External,\n scores: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_files: DataType.U32,\n // FffLocation inlined (flattened)\n location_tag: DataType.U8,\n location_line: DataType.I32,\n location_col: DataType.I32,\n location_end_line: DataType.I32,\n location_end_col: DataType.I32,\n};\n\ninterface FffSearchResultRaw {\n items: JsExternal;\n scores: JsExternal;\n count: number;\n total_matched: number;\n total_files: number;\n location_tag: number;\n location_line: number;\n location_col: number;\n location_end_line: number;\n location_end_col: number;\n}\n\n// FffDirItem struct (#[repr(C)]): char* (8) + char* (8) + i32 (4) + 4 padding = 24 bytes\nconst FFF_DIR_ITEM_STRUCT = {\n relative_path: DataType.External,\n dir_name: DataType.External,\n max_access_frecency: DataType.I32,\n};\n\ninterface FffDirItemRaw {\n relative_path: JsExternal;\n dir_name: JsExternal;\n max_access_frecency: number;\n}\n\nconst FFF_DIR_SEARCH_RESULT_STRUCT = {\n items: DataType.External,\n scores: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_dirs: DataType.U32,\n};\n\ninterface FffDirSearchResultRaw {\n items: JsExternal;\n scores: JsExternal;\n count: number;\n total_matched: number;\n total_dirs: number;\n}\n\n// FffMixedItem struct (#[repr(C)]): u8 (1) + 7 padding + char* (8) + char* (8) + char* (8)\n// + u64 (8) + u64 (8) + i64 (8) + i64 (8) + i64 (8) + bool (1) + 7 padding = 80 bytes\nconst FFF_MIXED_ITEM_STRUCT = {\n item_type: DataType.U8,\n relative_path: DataType.External,\n display_name: DataType.External,\n git_status: DataType.External,\n size: DataType.U64,\n modified: DataType.U64,\n access_frecency_score: DataType.I64,\n modification_frecency_score: DataType.I64,\n total_frecency_score: DataType.I64,\n is_binary: DataType.U8,\n};\n\ninterface FffMixedItemRaw {\n item_type: number;\n relative_path: JsExternal;\n display_name: JsExternal;\n git_status: JsExternal;\n size: number;\n modified: number;\n access_frecency_score: number;\n modification_frecency_score: number;\n total_frecency_score: number;\n is_binary: number;\n}\n\nconst FFF_MIXED_SEARCH_RESULT_STRUCT = {\n items: DataType.External,\n scores: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_files: DataType.U32,\n total_dirs: DataType.U32,\n // FffLocation inlined (flattened)\n location_tag: DataType.U8,\n location_line: DataType.I32,\n location_col: DataType.I32,\n location_end_line: DataType.I32,\n location_end_col: DataType.I32,\n};\n\ninterface FffMixedSearchResultRaw {\n items: JsExternal;\n scores: JsExternal;\n count: number;\n total_matched: number;\n total_files: number;\n total_dirs: number;\n location_tag: number;\n location_line: number;\n location_col: number;\n location_end_line: number;\n location_end_col: number;\n}\n\nconst FFF_GREP_MATCH_STRUCT = {\n relative_path: DataType.External,\n file_name: DataType.External,\n git_status: DataType.External,\n line_content: DataType.External,\n match_ranges: DataType.External,\n context_before: DataType.External,\n context_after: DataType.External,\n size: DataType.U64,\n modified: DataType.U64,\n total_frecency_score: DataType.I64,\n access_frecency_score: DataType.I64,\n modification_frecency_score: DataType.I64,\n line_number: DataType.U64,\n byte_offset: DataType.U64,\n col: DataType.U32,\n match_ranges_count: DataType.U32,\n context_before_count: DataType.U32,\n context_after_count: DataType.U32,\n fuzzy_score: DataType.U32, // actually u16 in C, but ffi-rs doesn't so we read it as u32 with padding\n has_fuzzy_score: DataType.U8,\n is_binary: DataType.U8,\n is_definition: DataType.U8,\n};\n\ninterface FffGrepMatchRaw {\n relative_path: JsExternal;\n file_name: JsExternal;\n git_status: JsExternal;\n line_content: JsExternal;\n match_ranges: JsExternal;\n context_before: JsExternal;\n context_after: JsExternal;\n size: number;\n modified: number;\n total_frecency_score: number;\n access_frecency_score: number;\n modification_frecency_score: number;\n line_number: number;\n byte_offset: number;\n col: number;\n match_ranges_count: number;\n context_before_count: number;\n context_after_count: number;\n fuzzy_score: number;\n has_fuzzy_score: number;\n is_binary: number;\n is_definition: number;\n}\n\nconst FFF_GREP_RESULT_STRUCT = {\n items: DataType.External,\n count: DataType.U32,\n total_matched: DataType.U32,\n total_files_searched: DataType.U32,\n total_files: DataType.U32,\n filtered_file_count: DataType.U32,\n next_file_offset: DataType.U32,\n regex_fallback_error: DataType.External,\n};\n\ninterface FffGrepResultRaw {\n items: JsExternal;\n count: number;\n total_matched: number;\n total_files_searched: number;\n total_files: number;\n filtered_file_count: number;\n next_file_offset: number;\n regex_fallback_error: JsExternal;\n}\n\nconst FFF_MATCH_RANGE_STRUCT = {\n start: DataType.U32,\n end: DataType.U32,\n};\n\ninterface FffMatchRangeRaw {\n start: number;\n end: number;\n}\n\n// ---------------------------------------------------------------------------\n// Struct reading helpers\n// ---------------------------------------------------------------------------\n\nfunction readFileItemFromRaw(raw: FffFileItemRaw): FileItem {\n return {\n relativePath: readCString(raw.relative_path) ?? \"\",\n fileName: readCString(raw.file_name) ?? \"\",\n gitStatus: readCString(raw.git_status) ?? \"\",\n size: Number(raw.size),\n modified: Number(raw.modified),\n accessFrecencyScore: Number(raw.access_frecency_score),\n modificationFrecencyScore: Number(raw.modification_frecency_score),\n totalFrecencyScore: Number(raw.total_frecency_score),\n };\n}\n\nfunction readScoreFromRaw(raw: FffScoreRaw): Score {\n return {\n total: raw.total,\n baseScore: raw.base_score,\n filenameBonus: raw.filename_bonus,\n specialFilenameBonus: raw.special_filename_bonus,\n frecencyBoost: raw.frecency_boost,\n distancePenalty: raw.distance_penalty,\n currentFilePenalty: raw.current_file_penalty,\n comboMatchBoost: raw.combo_match_boost,\n exactMatch: raw.exact_match !== 0,\n matchType: readCString(raw.match_type) ?? \"\",\n };\n}\n\nfunction readDirItemFromRaw(raw: FffDirItemRaw): DirItem {\n return {\n relativePath: readCString(raw.relative_path) ?? \"\",\n dirName: readCString(raw.dir_name) ?? \"\",\n maxAccessFrecency: raw.max_access_frecency,\n };\n}\n\nfunction readMixedItemFromRaw(raw: FffMixedItemRaw): MixedItem {\n if (raw.item_type === 1) {\n // Directory\n return {\n type: \"directory\",\n item: {\n relativePath: readCString(raw.relative_path) ?? \"\",\n dirName: readCString(raw.display_name) ?? \"\",\n maxAccessFrecency: Number(raw.access_frecency_score),\n },\n };\n }\n // File (item_type === 0)\n return {\n type: \"file\",\n item: {\n relativePath: readCString(raw.relative_path) ?? \"\",\n fileName: readCString(raw.display_name) ?? \"\",\n gitStatus: readCString(raw.git_status) ?? \"\",\n size: Number(raw.size),\n modified: Number(raw.modified),\n accessFrecencyScore: Number(raw.access_frecency_score),\n modificationFrecencyScore: Number(raw.modification_frecency_score),\n totalFrecencyScore: Number(raw.total_frecency_score),\n },\n };\n}\n\n/**\n * Call an accessor function that returns a pointer to a struct element,\n * then read the struct from that pointer.\n */\nfunction callAccessor<T>(\n funcName: string,\n resultPtr: JsExternal,\n index: number,\n structDef: Record<string, DataType>,\n): T {\n loadLibrary();\n const elemPtr = load({\n library: LIBRARY_KEY,\n funcName,\n retType: DataType.External,\n paramsType: [DataType.External, DataType.U32],\n paramsValue: [resultPtr, index],\n }) as JsExternal;\n\n const [raw] = restorePointer({\n retType: [structDef],\n paramsValue: wrapPointer([elemPtr]),\n }) as unknown as [T];\n\n return raw;\n}\n\n/**\n * Offset a pointer by `bytes` using the C API helper.\n */\nfunction ptrOffset(base: JsExternal, bytes: number): JsExternal {\n return load({\n library: LIBRARY_KEY,\n funcName: \"fff_ptr_offset\",\n retType: DataType.External,\n paramsType: [DataType.External, DataType.U64],\n paramsValue: [base, bytes],\n }) as JsExternal;\n}\n\n/**\n * Read a C string array (char**) of `count` elements.\n */\nfunction readCStringArray(ptrArray: JsExternal, count: number): string[] {\n if (count === 0 || isNullPointer(ptrArray)) return [];\n const result: string[] = [];\n for (let i = 0; i < count; i++) {\n const elemPtr = ptrOffset(ptrArray, i * 8);\n const [charPtr] = restorePointer({\n retType: [DataType.External],\n paramsValue: [elemPtr],\n }) as unknown as [JsExternal];\n result.push(readCString(charPtr) ?? \"\");\n }\n return result;\n}\n\nfunction readGrepMatchFromRaw(raw: FffGrepMatchRaw): GrepMatch {\n // Read match_ranges array via pointer offsets\n const matchRanges: [number, number][] = [];\n for (let i = 0; i < raw.match_ranges_count; i++) {\n const rangePtr = ptrOffset(raw.match_ranges, i * 8); // FffMatchRange is 8 bytes\n const [rangeRaw] = restorePointer({\n retType: [FFF_MATCH_RANGE_STRUCT],\n paramsValue: wrapPointer([rangePtr]),\n }) as unknown as [FffMatchRangeRaw];\n matchRanges.push([rangeRaw.start, rangeRaw.end]);\n }\n\n const match: GrepMatch = {\n relativePath: readCString(raw.relative_path) ?? \"\",\n fileName: readCString(raw.file_name) ?? \"\",\n gitStatus: readCString(raw.git_status) ?? \"\",\n lineContent: readCString(raw.line_content) ?? \"\",\n size: Number(raw.size),\n modified: Number(raw.modified),\n totalFrecencyScore: Number(raw.total_frecency_score),\n accessFrecencyScore: Number(raw.access_frecency_score),\n modificationFrecencyScore: Number(raw.modification_frecency_score),\n isBinary: raw.is_binary !== 0,\n lineNumber: Number(raw.line_number),\n col: raw.col,\n byteOffset: Number(raw.byte_offset),\n matchRanges,\n };\n\n if (raw.has_fuzzy_score !== 0) {\n match.fuzzyScore = raw.fuzzy_score;\n }\n if (raw.context_before_count > 0) {\n match.contextBefore = readCStringArray(raw.context_before, raw.context_before_count);\n }\n if (raw.context_after_count > 0) {\n match.contextAfter = readCStringArray(raw.context_after, raw.context_after_count);\n }\n if (raw.is_definition !== 0) {\n match.isDefinition = true;\n }\n\n return match;\n}\n\n/**\n * Parse an FffGrepResult from `FffResult.handle`, then free native memory.\n */\nfunction parseGrepResult(rawPtr: JsExternal): Result<GrepResult> {\n loadLibrary();\n\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"grep returned null result\");\n }\n\n const [gr] = restorePointer({\n retType: [FFF_GREP_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffGrepResultRaw];\n\n const count = gr.count;\n const regexFallbackError = readCString(gr.regex_fallback_error) ?? undefined;\n\n const items: GrepMatch[] = [];\n for (let i = 0; i < count; i++) {\n const rawMatch = callAccessor<FffGrepMatchRaw>(\n \"fff_grep_result_get_match\",\n handlePtr,\n i,\n FFF_GREP_MATCH_STRUCT,\n );\n items.push(readGrepMatchFromRaw(rawMatch));\n }\n\n // Free native grep result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_grep_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n const grepResult: GrepResult = {\n items,\n totalMatched: gr.total_matched,\n totalFilesSearched: gr.total_files_searched,\n totalFiles: gr.total_files,\n filteredFileCount: gr.filtered_file_count,\n nextCursor: gr.next_file_offset > 0 ? createGrepCursor(gr.next_file_offset) : null,\n };\n if (regexFallbackError) {\n grepResult.regexFallbackError = regexFallbackError;\n }\n return { ok: true, value: grepResult };\n}\n\n/**\n * Parse an FffSearchResult from `FffResult.handle`, then free native memory.\n */\nfunction parseSearchResult(rawPtr: JsExternal): Result<SearchResult> {\n loadLibrary();\n\n // Read FffResult envelope\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n // Free the FffResult envelope (does NOT free handle)\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"fff_search returned null search result\");\n }\n\n // Read FffSearchResult struct\n const [sr] = restorePointer({\n retType: [FFF_SEARCH_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffSearchResultRaw];\n\n const count = sr.count;\n\n // Read location\n let location: Location | undefined;\n if (sr.location_tag === 1) {\n location = { type: \"line\", line: sr.location_line };\n } else if (sr.location_tag === 2) {\n location = {\n type: \"position\",\n line: sr.location_line,\n col: sr.location_col,\n };\n } else if (sr.location_tag === 3) {\n location = {\n type: \"range\",\n start: { line: sr.location_line, col: sr.location_col },\n end: { line: sr.location_end_line, col: sr.location_end_col },\n };\n }\n\n // Read items and scores via accessor functions\n const items: FileItem[] = [];\n const scores: Score[] = [];\n\n for (let i = 0; i < count; i++) {\n const rawItem = callAccessor<FffFileItemRaw>(\n \"fff_search_result_get_item\",\n handlePtr,\n i,\n FFF_FILE_ITEM_STRUCT,\n );\n items.push(readFileItemFromRaw(rawItem));\n\n const rawScore = callAccessor<FffScoreRaw>(\n \"fff_search_result_get_score\",\n handlePtr,\n i,\n FFF_SCORE_STRUCT,\n );\n scores.push(readScoreFromRaw(rawScore));\n }\n\n // Free native search result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_search_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n const result: SearchResult = {\n items,\n scores,\n totalMatched: sr.total_matched,\n totalFiles: sr.total_files,\n };\n if (location) {\n result.location = location;\n }\n return { ok: true, value: result };\n}\n\n/**\n * Parse an FffDirSearchResult from `FffResult.handle`, then free native memory.\n */\nfunction parseDirSearchResult(rawPtr: JsExternal): Result<DirSearchResult> {\n loadLibrary();\n\n // Read FffResult envelope\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n // Free the FffResult envelope (does NOT free handle)\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"fff_search_directories returned null search result\");\n }\n\n // Read FffDirSearchResult struct\n const [sr] = restorePointer({\n retType: [FFF_DIR_SEARCH_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffDirSearchResultRaw];\n\n const count = sr.count;\n\n // Read items and scores via accessor functions\n const items: DirItem[] = [];\n const scores: Score[] = [];\n\n for (let i = 0; i < count; i++) {\n const rawItem = callAccessor<FffDirItemRaw>(\n \"fff_dir_search_result_get_item\",\n handlePtr,\n i,\n FFF_DIR_ITEM_STRUCT,\n );\n items.push(readDirItemFromRaw(rawItem));\n\n const rawScore = callAccessor<FffScoreRaw>(\n \"fff_dir_search_result_get_score\",\n handlePtr,\n i,\n FFF_SCORE_STRUCT,\n );\n scores.push(readScoreFromRaw(rawScore));\n }\n\n // Free native dir search result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_dir_search_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n return {\n ok: true,\n value: {\n items,\n scores,\n totalMatched: sr.total_matched,\n totalDirs: sr.total_dirs,\n },\n };\n}\n\n/**\n * Parse an FffMixedSearchResult from `FffResult.handle`, then free native memory.\n */\nfunction parseMixedSearchResult(rawPtr: JsExternal): Result<MixedSearchResult> {\n loadLibrary();\n\n // Read FffResult envelope\n const [envelope] = restorePointer({\n retType: [FFF_RESULT_STRUCT],\n paramsValue: wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n\n const success = envelope.success !== 0;\n\n if (!success) {\n const errorMsg = readCString(envelope.error) || \"Unknown error\";\n freeResult(rawPtr);\n return err(errorMsg);\n }\n\n const handlePtr = envelope.handle;\n // Free the FffResult envelope (does NOT free handle)\n freeResult(rawPtr);\n\n if (isNullPointer(handlePtr)) {\n return err(\"fff_search_mixed returned null search result\");\n }\n\n // Read FffMixedSearchResult struct\n const [sr] = restorePointer({\n retType: [FFF_MIXED_SEARCH_RESULT_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffMixedSearchResultRaw];\n\n const count = sr.count;\n\n // Read location\n let location: Location | undefined;\n if (sr.location_tag === 1) {\n location = { type: \"line\", line: sr.location_line };\n } else if (sr.location_tag === 2) {\n location = {\n type: \"position\",\n line: sr.location_line,\n col: sr.location_col,\n };\n } else if (sr.location_tag === 3) {\n location = {\n type: \"range\",\n start: { line: sr.location_line, col: sr.location_col },\n end: { line: sr.location_end_line, col: sr.location_end_col },\n };\n }\n\n // Read items and scores via accessor functions\n const items: MixedItem[] = [];\n const scores: Score[] = [];\n\n for (let i = 0; i < count; i++) {\n const rawItem = callAccessor<FffMixedItemRaw>(\n \"fff_mixed_search_result_get_item\",\n handlePtr,\n i,\n FFF_MIXED_ITEM_STRUCT,\n );\n items.push(readMixedItemFromRaw(rawItem));\n\n const rawScore = callAccessor<FffScoreRaw>(\n \"fff_mixed_search_result_get_score\",\n handlePtr,\n i,\n FFF_SCORE_STRUCT,\n );\n scores.push(readScoreFromRaw(rawScore));\n }\n\n // Free native mixed search result\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_mixed_search_result\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n const result: MixedSearchResult = {\n items,\n scores,\n totalMatched: sr.total_matched,\n totalFiles: sr.total_files,\n totalDirs: sr.total_dirs,\n };\n if (location) {\n result.location = location;\n }\n return { ok: true, value: result };\n}\n\n/**\n * Perform fuzzy search.\n */\nexport function ffiSearch(\n handle: NativeHandle,\n query: string,\n currentFile: string,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n comboBoostMultiplier: number,\n minComboCount: number,\n): Result<SearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_search\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n DataType.I32, // combo_boost_multiplier\n DataType.U32, // min_combo_count\n ],\n paramsValue: [\n handle,\n query,\n currentFile,\n maxThreads,\n pageIndex,\n pageSize,\n comboBoostMultiplier,\n minComboCount,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseSearchResult(rawPtr);\n}\n\n/**\n * Glob-only search. Bypasses the regular query parser, applies the pattern\n * as a single `Constraint::Glob`, ranks by frecency, paginates.\n */\nexport function ffiGlob(\n handle: NativeHandle,\n pattern: string,\n currentFile: string,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n): Result<SearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_glob\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // pattern\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n ],\n paramsValue: [handle, pattern, currentFile, maxThreads, pageIndex, pageSize],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseSearchResult(rawPtr);\n}\n\n/**\n * Perform fuzzy directory search.\n */\nexport function ffiSearchDirectories(\n handle: NativeHandle,\n query: string,\n currentFile: string | null,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n): Result<DirSearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_search_directories\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n ],\n paramsValue: [handle, query, currentFile ?? \"\", maxThreads, pageIndex, pageSize],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseDirSearchResult(rawPtr);\n}\n\n/**\n * Perform mixed (files + directories) fuzzy search.\n */\nexport function ffiSearchMixed(\n handle: NativeHandle,\n query: string,\n currentFile: string,\n maxThreads: number,\n pageIndex: number,\n pageSize: number,\n comboBoostMultiplier: number,\n minComboCount: number,\n): Result<MixedSearchResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_search_mixed\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.String, // current_file\n DataType.U32, // max_threads\n DataType.U32, // page_index\n DataType.U32, // page_size\n DataType.I32, // combo_boost_multiplier\n DataType.U32, // min_combo_count\n ],\n paramsValue: [\n handle,\n query,\n currentFile,\n maxThreads,\n pageIndex,\n pageSize,\n comboBoostMultiplier,\n minComboCount,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseMixedSearchResult(rawPtr);\n}\n\n/**\n * Live grep - search file contents.\n */\nexport function ffiLiveGrep(\n handle: NativeHandle,\n query: string,\n mode: string,\n maxFileSize: number,\n maxMatchesPerFile: number,\n smartCase: boolean,\n fileOffset: number,\n pageLimit: number,\n timeBudgetMs: number,\n beforeContext: number,\n afterContext: number,\n classifyDefinitions: boolean,\n): Result<GrepResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_live_grep\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // query\n DataType.U8, // mode\n DataType.U64, // max_file_size\n DataType.U32, // max_matches_per_file\n DataType.Boolean, // smart_case\n DataType.U32, // file_offset\n DataType.U32, // page_limit\n DataType.U64, // time_budget_ms\n DataType.U32, // before_context\n DataType.U32, // after_context\n DataType.Boolean, // classify_definitions\n ],\n paramsValue: [\n handle,\n query,\n grepModeToU8(mode),\n maxFileSize,\n maxMatchesPerFile,\n smartCase,\n fileOffset,\n pageLimit,\n timeBudgetMs,\n beforeContext,\n afterContext,\n classifyDefinitions,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseGrepResult(rawPtr);\n}\n\n/**\n * Multi-pattern grep - Aho-Corasick multi-needle search.\n */\nexport function ffiMultiGrep(\n handle: NativeHandle,\n patternsJoined: string,\n constraints: string,\n maxFileSize: number,\n maxMatchesPerFile: number,\n smartCase: boolean,\n fileOffset: number,\n pageLimit: number,\n timeBudgetMs: number,\n beforeContext: number,\n afterContext: number,\n classifyDefinitions: boolean,\n): Result<GrepResult> {\n loadLibrary();\n\n const rawPtr = load({\n library: LIBRARY_KEY,\n funcName: \"fff_multi_grep\",\n retType: DataType.External,\n paramsType: [\n DataType.External, // handle\n DataType.String, // patterns_joined\n DataType.String, // constraints\n DataType.U64, // max_file_size\n DataType.U32, // max_matches_per_file\n DataType.Boolean, // smart_case\n DataType.U32, // file_offset\n DataType.U32, // page_limit\n DataType.U64, // time_budget_ms\n DataType.U32, // before_context\n DataType.U32, // after_context\n DataType.Boolean, // classify_definitions\n ],\n paramsValue: [\n handle,\n patternsJoined,\n constraints,\n maxFileSize,\n maxMatchesPerFile,\n smartCase,\n fileOffset,\n pageLimit,\n timeBudgetMs,\n beforeContext,\n afterContext,\n classifyDefinitions,\n ],\n freeResultMemory: false,\n }) as JsExternal;\n\n return parseGrepResult(rawPtr);\n}\n\n/**\n * Trigger file scan.\n */\nexport function ffiScanFiles(handle: NativeHandle): Result<void> {\n return callVoidResult(\"fff_scan_files\", [DataType.External], [handle]);\n}\n\n/**\n * Check if scanning.\n */\nexport function ffiIsScanning(handle: NativeHandle): boolean {\n loadLibrary();\n return load({\n library: LIBRARY_KEY,\n funcName: \"fff_is_scanning\",\n retType: DataType.Boolean,\n paramsType: [DataType.External],\n paramsValue: [handle],\n }) as boolean;\n}\n\n/**\n * Get the base path of the file picker.\n */\nexport function ffiGetBasePath(handle: NativeHandle): Result<string | null> {\n return callStringResult(\"fff_get_base_path\", [DataType.External], [handle]);\n}\n\n// FffScanProgress struct definition\nconst FFF_SCAN_PROGRESS_STRUCT = {\n scanned_files_count: DataType.U64,\n is_scanning: DataType.U8,\n is_watcher_ready: DataType.U8,\n is_warmup_complete: DataType.U8,\n};\n\ninterface FffScanProgressRaw {\n scanned_files_count: number;\n is_scanning: number;\n is_watcher_ready: number;\n is_warmup_complete: number;\n}\n\n/**\n * Get scan progress.\n */\nexport function ffiGetScanProgress(handle: NativeHandle): Result<{\n scannedFilesCount: number;\n isScanning: boolean;\n isWatcherReady: boolean;\n isWarmupComplete: boolean;\n}> {\n loadLibrary();\n const res = readResultEnvelope(\"fff_get_scan_progress\", [DataType.External], [handle]);\n if (\"ok\" in res) return res;\n\n const handlePtr = res.struct.handle;\n freeResult(res.rawPtr);\n\n if (isNullPointer(handlePtr)) return err(\"scan progress returned null\");\n\n const [sp] = restorePointer({\n retType: [FFF_SCAN_PROGRESS_STRUCT],\n paramsValue: wrapPointer([handlePtr]),\n }) as unknown as [FffScanProgressRaw];\n\n const result = {\n scannedFilesCount: Number(sp.scanned_files_count),\n isScanning: sp.is_scanning !== 0,\n isWatcherReady: sp.is_watcher_ready !== 0,\n isWarmupComplete: sp.is_warmup_complete !== 0,\n };\n\n // Free native scan progress\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_scan_progress\",\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [handlePtr],\n });\n\n return { ok: true, value: result };\n}\n\n/**\n * Wait for a tree scan to complete.\n */\nexport function ffiWaitForScan(handle: NativeHandle, timeoutMs: number): Result<boolean> {\n return callBoolResult(\n \"fff_wait_for_scan\",\n [DataType.External, DataType.U64],\n [handle, timeoutMs],\n );\n}\n\n/**\n * Restart index in new path.\n */\nexport function ffiRestartIndex(handle: NativeHandle, newPath: string): Result<void> {\n return callVoidResult(\n \"fff_restart_index\",\n [DataType.External, DataType.String],\n [handle, newPath],\n );\n}\n\n/**\n * Refresh git status.\n */\nexport function ffiRefreshGitStatus(handle: NativeHandle): Result<number> {\n return callIntResult(\"fff_refresh_git_status\", [DataType.External], [handle]);\n}\n\n/**\n * Track query completion.\n */\nexport function ffiTrackQuery(\n handle: NativeHandle,\n query: string,\n filePath: string,\n): Result<boolean> {\n return callBoolResult(\n \"fff_track_query\",\n [DataType.External, DataType.String, DataType.String],\n [handle, query, filePath],\n );\n}\n\n/**\n * Get historical query.\n */\nexport function ffiGetHistoricalQuery(\n handle: NativeHandle,\n offset: number,\n): Result<string | null> {\n return callStringResult(\n \"fff_get_historical_query\",\n [DataType.External, DataType.U64],\n [handle, offset],\n );\n}\n\n// ALWAYS KEEP IN SYNC WITH fff.h\n//\n// Note: node uses `fff_watch_args` (flattened options) because ffi-rs cannot\n// marshal a `*const *const c_char` field inside a struct param — StringArray\n// is only supported as a top-level parameter.\n//\n// Batch contents are read through the C accessors (fff_watch_events_count /\n// fff_watch_events_get_path / fff_watch_events_get_kind), so no struct\n// layout knowledge lives on this side.\n\n/** Map the C kind byte to the public WatchEventKind. */\nfunction watchKindFromU8(kind: number): WatchEventKind {\n switch (kind) {\n case 0:\n return \"created\";\n case 1:\n return \"modified\";\n case 2:\n return \"removed\";\n default:\n return \"rescan\";\n }\n}\n\n/** Trampoline signature: (watch id, batch address, user_data — unused). */\nconst WATCH_TRAMPOLINE_TYPE = funcConstructor({\n paramsType: [DataType.U64, DataType.U64, DataType.U64],\n retType: DataType.Void,\n});\n\n/** JS handlers keyed by process-unique native watch id. */\nconst watchHandlers = new Map<number, (events: WatchEvent[]) => void>();\n/** Instances (by handle identity) that ever created a watch subscription. */\nconst watchInstances = new Set<unknown>();\n\n/** Lazily created process-wide trampoline (createPointer result). */\nlet watchTrampoline: JsExternal[] | null = null;\n\n/** Convert a raw u64 address delivered through the trampoline to a JsExternal. */\nfunction addressToExternal(address: number): JsExternal {\n return load({\n library: LIBRARY_KEY,\n funcName: \"fff_ptr_offset\",\n retType: DataType.External,\n paramsType: [DataType.U64, DataType.U64],\n paramsValue: [address, 0],\n }) as unknown as JsExternal;\n}\n\n/** Parse an FffWatchEventBatch at `address` and free the native memory. */\nfunction consumeWatchBatch(address: number): WatchEvent[] {\n const batchPtr = addressToExternal(address);\n const count = load({\n library: LIBRARY_KEY,\n funcName: \"fff_watch_events_count\",\n retType: DataType.U32,\n paramsType: [DataType.External],\n paramsValue: [batchPtr],\n }) as unknown as number;\n\n const events: WatchEvent[] = [];\n for (let i = 0; i < count; i++) {\n const path = load({\n library: LIBRARY_KEY,\n funcName: \"fff_watch_events_get_path\",\n retType: DataType.External,\n paramsType: [DataType.External, DataType.U32],\n paramsValue: [batchPtr, i],\n }) as unknown as JsExternal;\n const kind = load({\n library: LIBRARY_KEY,\n funcName: \"fff_watch_events_get_kind\",\n retType: DataType.U8,\n paramsType: [DataType.External, DataType.U32],\n paramsValue: [batchPtr, i],\n }) as unknown as number;\n events.push({\n path: readCString(path) ?? \"\",\n kind: watchKindFromU8(kind),\n });\n }\n\n load({\n library: LIBRARY_KEY,\n funcName: \"fff_free_watch_events\",\n retType: DataType.Void,\n paramsType: [DataType.U64],\n paramsValue: [address],\n });\n\n return events;\n}\n\n/**\n * The single native->JS entry point for all watch subscriptions. Runs on\n * the JS thread (threadsafe_function delivery); the batch is owned by us\n * and freed inside `consumeWatchBatch`. Unknown watch ids (unsubscribe\n * races) are benign: the batch is freed and dropped.\n */\nfunction watchTrampolineImpl(\n watchId: number,\n batchAddress: number,\n _userData: number,\n): void {\n const events = consumeWatchBatch(batchAddress);\n const handler = watchHandlers.get(Number(watchId));\n if (handler === undefined || events.length === 0) return;\n try {\n handler(events);\n } catch {\n // User callback errors must not propagate into the FFI layer\n }\n}\n\nfunction ensureWatchTrampoline(): JsExternal {\n if (watchTrampoline === null) {\n watchTrampoline = createPointer({\n paramsType: [WATCH_TRAMPOLINE_TYPE],\n paramsValue: [watchTrampolineImpl],\n });\n }\n return unwrapPointer(watchTrampoline)[0] as JsExternal;\n}\n\n// fff watcher uses a single cross-boundary FFI callback to deliver all events which we then manually\n// mapping to the user's javascript functions\nfunction ensureWatchCallbackRegistered(handle: NativeHandle): Result<void> {\n if (watchInstances.has(handle as unknown)) return { ok: true, value: undefined };\n const trampoline = ensureWatchTrampoline();\n const registered = callVoidResult(\n \"fff_set_watch_callback\",\n [DataType.External, DataType.External, DataType.U64],\n [handle, trampoline, 0],\n );\n if (registered.ok) watchInstances.add(handle as unknown);\n return registered;\n}\n\nfunction releaseWatchTrampolineIfIdle(): void {\n if (watchHandlers.size > 0 || watchInstances.size > 0 || watchTrampoline === null)\n return;\n freePointer({\n paramsType: [WATCH_TRAMPOLINE_TYPE],\n paramsValue: watchTrampoline,\n pointerType: PointerType.RsPointer,\n });\n watchTrampoline = null;\n}\n\n/**\n * Create a push-mode watch subscription. `callback` receives a normalized\n * batch of up to 128 events, delivered on the JS event loop.\n *\n * Returns the native watch id to pass to `ffiUnwatch`.\n */\nexport function ffiWatch(\n handle: NativeHandle,\n pattern: string,\n ignore: string[],\n callback: (events: WatchEvent[]) => void,\n): Result<number> {\n loadLibrary();\n\n const registered = ensureWatchCallbackRegistered(handle);\n if (!registered.ok) return registered;\n\n const created = callIntResult(\n \"fff_watch_args\",\n [DataType.External, DataType.String, DataType.StringArray, DataType.U32],\n [handle, pattern, ignore, ignore.length],\n );\n\n if (!created.ok) return created;\n\n // No startup race: threadsafe delivery lands on the JS event loop, so this\n // synchronous set always precedes the first routing lookup for this id.\n watchHandlers.set(created.value, callback);\n return created;\n}\n\n/**\n * Remove a watch subscription. Drops the JS handler synchronously — once\n * this returns the callback can never run again (a late native tail batch\n * misses the map lookup and is dropped).\n */\nexport function ffiUnwatch(handle: NativeHandle, watchId: number): Result<boolean> {\n const result = callBoolResult(\n \"fff_unwatch\",\n [DataType.External, DataType.U64],\n [handle, watchId],\n );\n watchHandlers.delete(watchId);\n return result;\n}\n\n/**\n * Post-`ffiDestroy` cleanup for an instance's watch state: drops any\n * handlers that were never explicitly unwatched and releases the process\n * trampoline when this was the last watching instance.\n */\nexport function ffiWatchCleanupAfterDestroy(\n handle: NativeHandle,\n watchIds: Iterable<number>,\n): void {\n for (const id of watchIds) {\n watchHandlers.delete(id);\n }\n watchInstances.delete(handle as unknown);\n releaseWatchTrampolineIfIdle();\n}\n\n/**\n * Health check.\n *\n * `handle` can be null for a limited check (version + git only).\n * When null, we pass DataType.U64 with value 0 as a null pointer workaround\n * since ffi-rs does not accept `null` for External parameters.\n */\nexport function ffiHealthCheck(\n handle: NativeHandle | null,\n testPath: string,\n): Result<unknown> {\n if (handle === null) {\n // Use U64(0) as a null pointer since ffi-rs rejects null for External params\n return callJsonResult<unknown>(\n \"fff_health_check\",\n [DataType.U64, DataType.String],\n [0, testPath],\n );\n }\n\n return callJsonResult<unknown>(\n \"fff_health_check\",\n [DataType.External, DataType.String],\n [handle, testPath],\n );\n}\n\n/**\n * Ensure the library is loaded.\n *\n * Loads the native library from the platform-specific npm package\n * or a local dev build. Throws if the binary is not found.\n */\nexport function ensureLoaded(): void {\n loadLibrary();\n}\n\n/**\n * Check if the library is available.\n */\nexport function isAvailable(): boolean {\n try {\n loadLibrary();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Close the library and release ffi-rs resources.\n * Call this when completely done with the library.\n */\nexport function closeLibrary(): void {\n if (isLoaded) {\n close(LIBRARY_KEY);\n isLoaded = false;\n }\n}\n","// ----------------------------------------------------------------------------\n// GENERATED FILE - DO NOT EDIT.\n// Copied from: packages/shared/fff-api.ts\n// Run make sync-js-api from the repo root to regenerate.\n// ----------------------------------------------------------------------------\n\n/**\n * The shared public API surface for the fff file finder, implemented identically\n * by `@ff-labs/fff-node` and `@ff-labs/fff-bun`.\n *\n * This file is the single source of truth for every type, helper, and the\n * `FileFinderApi` interface that crosses the package boundary. It is copied\n * verbatim into each package's `src/fff-api.ts` by `make sync-api`.\n *\n * Anything that is not part of the public API (FFI struct layouts, binary\n * loading, platform detection, etc.) stays as per-package internal\n * implementation and must NOT live here.\n *\n * Keep this file self-contained: it must not import from any package-local\n * module, since each package compiles its own copy.\n */\n\n/**\n * Result type for all operations - follows the Result pattern\n */\nexport type Result<T> = { ok: true; value: T } | { ok: false; error: string };\n\n/**\n * Helper to create a successful result\n */\nexport function ok<T>(value: T): Result<T> {\n return { ok: true, value };\n}\n\n/**\n * Helper to create an error result\n */\nexport function err<T>(error: string): Result<T> {\n return { ok: false, error };\n}\n\n/**\n * Initialization options for the file finder\n */\nexport interface InitOptions {\n /** Base directory to index (required) */\n basePath: string;\n /** Path to frecency database (optional, omit to skip frecency initialization) */\n frecencyDbPath?: string;\n /** Path to query history database (optional, omit to skip query tracker initialization) */\n historyDbPath?: string;\n /** @deprecated no-op */\n useUnsafeNoLock?: boolean;\n /**\n * Disable mmap cache warmup after the initial scan. When mmap cache is\n * enabled (the default), the first grep search is as fast as subsequent\n * ones at the cost of a longer scan time and higher initial memory usage.\n */\n disableMmapCache?: boolean;\n /**\n * Disable the content index built after the initial scan.\n * Content indexing enables faster content-aware filtering during grep.\n */\n disableContentIndexing?: boolean;\n /**\n * Disable the background file-system watcher. When the watcher is\n * disabled, files are scanned once but not monitored for changes.\n * (default: false)\n */\n disableWatch?: boolean;\n /** enables optimizations for AI agent assistants. Provide as true if running via mcp/agent */\n aiMode?: boolean;\n /**\n * Path to the tracing log file. When set, the shared FFF tracing subscriber\n * is installed on first init and file output is written here. Omit to leave\n * logging uninitialized.\n */\n logFilePath?: string;\n /**\n * Log level for the tracing subscriber: \"trace\", \"debug\", \"info\", \"warn\",\n * or \"error\". Defaults to \"info\". Ignored when `logFilePath` is not set.\n */\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\";\n /**\n * Override for the content cache file-count cap. When omitted, the picker\n * auto-sizes the budget from the final scanned file count.\n */\n cacheBudgetMaxFiles?: number;\n /** Override for the content cache byte cap. See `cacheBudgetMaxFiles`. */\n cacheBudgetMaxBytes?: number;\n /** Override for the per-file byte cap in the content cache. */\n cacheBudgetMaxFileSize?: number;\n /**\n * Allow indexing the filesystem root (`/`).\n * Off by default, having fff instance at the large folder will generally require\n * file watcher and indexing which will consume a lot of resources if performed uncontrolled\n **/\n enableFsRootScanning?: boolean;\n /** Allow indexing the user's home directory. Same trade-off as `enableFsRootScanning`. */\n enableHomeDirScanning?: boolean;\n /** Follow symlinks for directories */\n followSymlinks?: boolean;\n}\n\n/**\n * Search options for fuzzy file search\n */\nexport interface SearchOptions {\n /** Maximum threads for parallel search (0 = auto) */\n maxThreads?: number;\n /** Current file path (for deprioritization in results) */\n currentFile?: string;\n /** Combo boost score multiplier (default: 100) */\n comboBoostMultiplier?: number;\n /** Minimum combo count for boost (default: 3) */\n minComboCount?: number;\n /** Page index for pagination (default: 0) */\n pageIndex?: number;\n /** Page size for pagination (default: 100) */\n pageSize?: number;\n}\n\n/**\n * Options for `glob`, the constraint-only search.\n *\n * The pattern is applied as a single pass SIMD optimized prefiltering\n * without any fuzzy matching involved. Faster and 100% compatible to npm `glob`.\n */\nexport interface GlobOptions {\n /** Maximum threads for parallel filtering (0 = auto). */\n maxThreads?: number;\n /** Current file path (for deprioritization in results). */\n currentFile?: string;\n /** Page index for pagination (default: 0). */\n pageIndex?: number;\n /** Page size for pagination (default: 100). */\n pageSize?: number;\n}\n\n/**\n * A file item in search results\n */\nexport interface FileItem {\n /** Path relative to the indexed directory */\n relativePath: string;\n /** File name only */\n fileName: string;\n /** File size in bytes */\n size: number;\n /** Last modified timestamp (Unix seconds) */\n modified: number;\n /** Frecency score based on access patterns */\n accessFrecencyScore: number;\n /** Frecency score based on modification time */\n modificationFrecencyScore: number;\n /** Combined frecency score */\n totalFrecencyScore: number;\n /** Git status: 'clean', 'modified', 'untracked', 'staged_new', etc. */\n gitStatus: string;\n}\n\n/**\n * Score breakdown for a search result\n */\nexport interface Score {\n /** Total combined score */\n total: number;\n /** Base fuzzy match score */\n baseScore: number;\n /** Bonus for filename match */\n filenameBonus: number;\n /** Bonus for special filenames (index.ts, main.rs, etc.) */\n specialFilenameBonus: number;\n /** Boost from frecency */\n frecencyBoost: number;\n /** Penalty for distance in path */\n distancePenalty: number;\n /** Penalty if this is the current file */\n currentFilePenalty: number;\n /** Boost from query history combo matching */\n comboMatchBoost: number;\n /** Whether this was an exact match */\n exactMatch: boolean;\n /** Type of match: 'fuzzy', 'exact', 'prefix', etc. */\n matchType: string;\n}\n\n/**\n * Location in file (from query like \"file.ts:42\")\n */\nexport type Location =\n | { type: \"line\"; line: number }\n | { type: \"position\"; line: number; col: number }\n | {\n type: \"range\";\n start: { line: number; col: number };\n end: { line: number; col: number };\n };\n\n/**\n * Search result from fuzzy file search\n */\nexport interface SearchResult {\n /** Matched file items */\n items: FileItem[];\n /** Corresponding scores for each item */\n scores: Score[];\n /** Total number of files that matched */\n totalMatched: number;\n /** Total number of indexed files */\n totalFiles: number;\n /** Location parsed from query (e.g., \"file.ts:42:10\") */\n location?: Location;\n}\n\n/**\n * A directory item in search results\n */\nexport interface DirItem {\n /** Path relative to the indexed directory (e.g., \"src/components/\") */\n relativePath: string;\n /** Last path segment (e.g., \"components/\" for \"src/components/\") */\n dirName: string;\n /** Maximum access frecency score among direct child files */\n maxAccessFrecency: number;\n}\n\n/**\n * Search options for directory search (subset of SearchOptions)\n */\nexport interface DirSearchOptions {\n /** Maximum threads for parallel search (0 = auto) */\n maxThreads?: number;\n /** Current file path (for distance scoring) */\n currentFile?: string;\n /** Page index for pagination (default: 0) */\n pageIndex?: number;\n /** Page size for pagination (default: 100) */\n pageSize?: number;\n}\n\n/**\n * Search result from fuzzy directory search\n */\nexport interface DirSearchResult {\n /** Matched directory items */\n items: DirItem[];\n /** Corresponding scores for each item */\n scores: Score[];\n /** Total number of directories that matched */\n totalMatched: number;\n /** Total number of indexed directories */\n totalDirs: number;\n}\n\n/**\n * A single item in a mixed (files + directories) search result\n */\nexport type MixedItem =\n | { type: \"file\"; item: FileItem }\n | { type: \"directory\"; item: DirItem };\n\n/**\n * Search result from mixed (files + directories) fuzzy search.\n * Items are interleaved by total score in descending order.\n */\nexport interface MixedSearchResult {\n /** Matched items (files and directories interleaved by score) */\n items: MixedItem[];\n /** Corresponding scores for each item */\n scores: Score[];\n /** Total number of items (files + dirs) that matched */\n totalMatched: number;\n /** Total number of indexed files */\n totalFiles: number;\n /** Total number of indexed directories */\n totalDirs: number;\n /** Location parsed from query */\n location?: Location;\n}\n\n/**\n * Scan progress information\n */\nexport interface ScanProgress {\n /** Number of files scanned so far */\n scannedFilesCount: number;\n /** Whether a scan is currently in progress */\n isScanning: boolean;\n /** Whether the background file watcher is ready */\n isWatcherReady: boolean;\n /** Whether the warmup/bigram phase has completed */\n isWarmupComplete: boolean;\n}\n\n/**\n * Normalized watch event kind.\n * A file removed and recreated in one processed batch is marked as modified.\n *\n * rescan = internal OS buffers were overloaded, some events might be missing.\n * The `path` is going to be a folder needs to be rescanned\n */\nexport type WatchEventKind = \"created\" | \"modified\" | \"removed\" | \"rescan\";\n\n/** A single filesystem change notification. */\nexport interface WatchEvent {\n /** Absolute path of the affected file (base path to rescan if `kind ==rescan`) */\n path: string;\n kind: WatchEventKind;\n}\n\n/** Options for watch subscriptions. */\nexport interface WatchOptions {\n /** Additional glob wildcard patterns to ignore */\n ignore?: string[];\n}\n\n/**\n * Receives normalized batches of up to 128 events. Each path appears once.\n */\nexport type WatchBatchCallback = (events: WatchEvent[]) => void;\n\n/** Call me to unsubscribe. */\nexport type WatchUnsubscribe = () => void;\n\n/** Database health information */\nexport interface DbHealth {\n /** Path to the database */\n path: string;\n /** Size of the database on disk in bytes */\n diskSize: number;\n}\n\n/**\n * Health check result\n */\nexport interface HealthCheck {\n /** Library version */\n version: string;\n /** Git integration status */\n git: {\n /** Whether git2 library is available */\n available: boolean;\n /** Whether a git repository was found */\n repositoryFound: boolean;\n /** Git working directory path */\n workdir?: string;\n /** libgit2 version string */\n libgit2Version: string;\n /** Error message if git detection failed */\n error?: string;\n };\n /** File picker status */\n filePicker: {\n /** Whether the file picker is initialized */\n initialized: boolean;\n /** Base path being indexed */\n basePath?: string;\n /** Whether a scan is in progress */\n isScanning?: boolean;\n /** Number of indexed files */\n indexedFiles?: number;\n /** Error message if there's an issue */\n error?: string;\n };\n /** Frecency database status */\n frecency: {\n /** Whether frecency tracking is initialized */\n initialized: boolean;\n /** Database health information */\n dbHealthcheck?: DbHealth;\n /** Error message if there's an issue */\n error?: string;\n };\n /** Query tracker status */\n queryTracker: {\n /** Whether query tracking is initialized */\n initialized: boolean;\n /** Database health information */\n dbHealthcheck?: DbHealth;\n /** Error message if there's an issue */\n error?: string;\n };\n}\n\n/**\n * Grep search mode\n */\nexport type GrepMode = \"plain\" | \"regex\" | \"fuzzy\";\n\n/**\n * Opaque pagination cursor for grep results.\n * Pass this to `GrepOptions.cursor` to fetch the next page.\n * Do not construct or modify this — use the `nextCursor` from a previous `GrepResult`.\n */\nexport interface GrepCursor {\n /** @internal */\n readonly __brand: \"GrepCursor\";\n /** @internal */\n readonly _offset: number;\n}\n\n/**\n * @internal Create a GrepCursor from a raw file offset.\n */\nexport function createGrepCursor(offset: number): GrepCursor {\n return { __brand: \"GrepCursor\" as const, _offset: offset };\n}\n\n/**\n * Options for live grep (content search)\n *\n * Files are searched sequentially in frecency order (most recently/frequently\n * accessed first). The engine returns a `nextCursor` for fetching the next page.\n */\nexport interface GrepOptions {\n /** Maximum file size to search in bytes. Files larger than this are skipped. (default: 10MB) */\n maxFileSize?: number;\n /** Maximum matching lines to collect from a single file (default: 200) */\n maxMatchesPerFile?: number;\n /** Smart case: case-insensitive when the query is all lowercase, case-sensitive otherwise (default: true) */\n smartCase?: boolean;\n /**\n * Pagination cursor from a previous `GrepResult.nextCursor`.\n * Omit (or pass `null`) for the first page.\n */\n cursor?: GrepCursor | null;\n /** Search mode (default: \"plain\") */\n mode?: GrepMode;\n /**\n * Maximum wall-clock time in milliseconds to spend searching before returning\n * partial results. 0 = unlimited. (default: 0)\n */\n timeBudgetMs?: number;\n /** Number of context lines to include before each match (default: 0) */\n beforeContext?: number;\n /** Number of context lines to include after each match (default: 0) */\n afterContext?: number;\n /** Maximum matches to return in this page across all files (default: 50) */\n pageSize?: number;\n /**\n * When true, classify each match line as a code definition (struct/fn/class/...)\n * and expose it via `GrepMatch.isDefinition`. Let callers re-rank defs first\n * without a TS-side regex port. (default: false)\n */\n classifyDefinitions?: boolean;\n}\n\n/**\n * A single grep match with file and line information\n */\nexport interface GrepMatch {\n /** Path relative to the indexed directory */\n relativePath: string;\n /** File name only */\n fileName: string;\n /** Git status */\n gitStatus: string;\n /** File size in bytes */\n size: number;\n /** Last modified timestamp (Unix seconds) */\n modified: number;\n /** Whether the file is binary */\n isBinary: boolean;\n /** Combined frecency score */\n totalFrecencyScore: number;\n /** Access-based frecency score */\n accessFrecencyScore: number;\n /** Modification-based frecency score */\n modificationFrecencyScore: number;\n /** 1-based line number of the match */\n lineNumber: number;\n /** 0-based byte column of first match start */\n col: number;\n /** Absolute byte offset of the matched line from file start */\n byteOffset: number;\n /** The matched line text (may be truncated) */\n lineContent: string;\n /** Byte offset pairs [start, end] within lineContent for highlighting */\n matchRanges: [number, number][];\n /** Fuzzy match score (only in fuzzy mode) */\n fuzzyScore?: number;\n /** Lines before the match (context). Empty array when context is 0. */\n contextBefore?: string[];\n /** Lines after the match (context). Empty array when context is 0. */\n contextAfter?: string[];\n /** Whether this line is a code definition (only populated when `classifyDefinitions: true`). */\n isDefinition?: boolean;\n}\n\n/**\n * Result from a grep search\n */\nexport interface GrepResult {\n /** Matched items with file and line information. At most `max_matches_per_file`. */\n items: GrepMatch[];\n /** Total number of matches collected (always equal to items.length). */\n totalMatched: number;\n /** Number of files actually opened and searched in this call */\n totalFilesSearched: number;\n /** Total number of indexed files (before any filtering) */\n totalFiles: number;\n /** Number of files eligible for search after filtering out binary files, oversized files, and constraint mismatches */\n filteredFileCount: number;\n /**\n * Cursor for the next page, or `null` if all eligible files have been searched.\n * Pass this as `GrepOptions.cursor` to continue from where this call left off.\n */\n nextCursor: GrepCursor | null;\n /** When regex mode fails to compile the pattern, the engine falls back to literal matching and this field contains the compilation error */\n regexFallbackError?: string;\n}\n\n/**\n * Options for multi-pattern grep (Aho-Corasick multi-needle search)\n *\n * Searches for lines matching ANY of the provided patterns using\n * SIMD-accelerated Aho-Corasick multi-pattern matching.\n */\nexport interface MultiGrepOptions {\n /** Patterns to search for (OR logic — matches lines containing any pattern) */\n patterns: string[];\n /** File constraints like \"*.rs\" or \"/src/\" */\n constraints?: string;\n /** Maximum file size to search in bytes (default: 10MB) */\n maxFileSize?: number;\n /** Maximum matching lines to collect from a single file (default: 0 = unlimited) */\n maxMatchesPerFile?: number;\n /** Smart case: case-insensitive when all patterns are lowercase (default: true) */\n smartCase?: boolean;\n /**\n * Pagination cursor from a previous `GrepResult.nextCursor`.\n * Omit (or pass `null`) for the first page.\n */\n cursor?: GrepCursor | null;\n /**\n * Maximum wall-clock time in milliseconds to spend searching before returning\n * partial results. 0 = unlimited. (default: 0)\n */\n timeBudgetMs?: number;\n /** Number of context lines to include before each match (default: 0) */\n beforeContext?: number;\n /** Number of context lines to include after each match (default: 0) */\n afterContext?: number;\n /** Maximum matches to return in this page across all files (default: 50) */\n pageSize?: number;\n /**\n * When true, classify each match line as a code definition (struct/fn/class/...)\n * and expose it via `GrepMatch.isDefinition`. (default: false)\n */\n classifyDefinitions?: boolean;\n}\n\n/**\n * The shared instance surface implemented by `FileFinder` in both\n * `@ff-labs/fff-node` and `@ff-labs/fff-bun`.\n *\n * Both packages must implement this identically. Only instance members belong\n * here. Static helpers (`create`, `isAvailable`, `ensureLoaded`,\n * `healthCheckStatic`) are package-specific and intentionally excluded.\n */\nexport interface FileFinderApi {\n /** Whether the instance has been destroyed. */\n readonly isDestroyed: boolean;\n\n /** Destroy and free all native resources. */\n destroy(): void;\n\n /** Fuzzy file search. */\n fileSearch(query: string, options?: SearchOptions): Result<SearchResult>;\n\n /** Glob-only filtering (no fuzzy matching). */\n glob(pattern: string, options?: GlobOptions): Result<SearchResult>;\n\n /** Fuzzy directory search. */\n directorySearch(query: string, options?: DirSearchOptions): Result<DirSearchResult>;\n\n /** Fuzzy search over files and directories interleaved by score. */\n mixedSearch(query: string, options?: SearchOptions): Result<MixedSearchResult>;\n\n /** Content search (live grep). */\n grep(query: string, options?: GrepOptions): Result<GrepResult>;\n\n /** Multi-pattern OR content search (Aho-Corasick). */\n multiGrep(options: MultiGrepOptions): Result<GrepResult>;\n\n /** Trigger an async rescan of the indexed directory. */\n scanFiles(): Result<void>;\n\n /** Whether a scan is currently in progress. */\n isScanning(): boolean;\n\n /** The root directory being indexed. */\n getBasePath(): Result<string | null>;\n\n /** Current scan progress snapshot. */\n getScanProgress(): Result<ScanProgress>;\n\n /**\n * Wait for the initial file scan to complete.\n *\n * Non-blocking: polls `isScanning` and yields to the event loop between\n * checks, so other async work keeps running while waiting.\n */\n waitForScan(timeoutMs?: number): Promise<Result<boolean>>;\n\n /**\n * Wait for the initial file scan to complete, blocking the calling thread.\n *\n * Backed by the native `fff_wait_for_scan` call. Prefer `waitForScan` unless\n * you specifically need synchronous blocking behaviour.\n */\n waitForScanBlocking(timeoutMs?: number): Result<boolean>;\n\n /**\n * Wait until the index is fully ready: the scan has finished and the warmup\n * (content indexing / bigram) phase has completed.\n *\n * Non-blocking: polls `getScanProgress` and yields to the event loop.\n */\n waitForIndexReady(timeoutMs?: number): Promise<Result<boolean>>;\n\n /** Restart indexing in a new directory. */\n reindex(newPath: string): Result<void>;\n\n /** Refresh the git status cache. Returns the number of updated files. */\n refreshGitStatus(): Result<number>;\n\n /** Record that `selectedFilePath` was chosen for `query`. */\n trackQuery(query: string, selectedFilePath: string): Result<boolean>;\n\n /** Get a historical query by offset (0 = most recent). */\n getHistoricalQuery(offset: number): Result<string | null>;\n\n /**\n * Subscribe to filesystem changes matching `pattern`.\n *\n * Patterns may be base-relative globs (./ works), exact paths inside the indexed\n * tree, or existing directories. An empty pattern watches the whole tree.\n *\n * Events are debounced and submitted in batches per 100-ms window at most 128 events.\n * Gitignored and other ignored files are never triggering watcher.\n */\n watch(callback: WatchBatchCallback, options?: WatchOptions): Result<WatchUnsubscribe>;\n watch(\n pattern: string,\n callback: WatchBatchCallback,\n options?: WatchOptions,\n ): Result<WatchUnsubscribe>;\n\n /** Health/diagnostics information for this instance. */\n healthCheck(testPath?: string): Result<HealthCheck>;\n}\n","/**\n * FileFinder - High-level API for the fff file finder\n *\n * This class provides a type-safe, ergonomic API for file finding operations.\n * Each instance owns an independent native file picker that can be created\n * and destroyed independently. Multiple instances can coexist.\n *\n * All methods return Result types for explicit error handling.\n */\n\nimport {\n ensureLoaded,\n ffiCreate,\n ffiDestroy,\n ffiGetBasePath,\n ffiGetHistoricalQuery,\n ffiGetScanProgress,\n ffiGlob,\n ffiHealthCheck,\n ffiIsScanning,\n ffiLiveGrep,\n ffiMultiGrep,\n ffiRefreshGitStatus,\n ffiRestartIndex,\n ffiScanFiles,\n ffiSearch,\n ffiSearchDirectories,\n ffiSearchMixed,\n ffiTrackQuery,\n ffiUnwatch,\n ffiWaitForScan,\n ffiWatch,\n ffiWatchCleanupAfterDestroy,\n isAvailable,\n type NativeHandle,\n} from \"./ffi.js\";\n\nimport type {\n DirSearchOptions,\n DirSearchResult,\n FileFinderApi,\n GlobOptions,\n GrepOptions,\n GrepResult,\n HealthCheck,\n InitOptions,\n MixedSearchResult,\n MultiGrepOptions,\n Result,\n ScanProgress,\n SearchOptions,\n SearchResult,\n WatchBatchCallback,\n WatchOptions,\n WatchUnsubscribe,\n} from \"./fff-api.js\";\n\nimport { err } from \"./fff-api.js\";\n\n/**\n * FileFinder - Fast file finder with fuzzy search\n *\n * Each instance is backed by an independent native file picker. Create as many\n * as you need and destroy them when done.\n *\n * @example\n *\n * ```ts\n * import { FileFinder } from \"@ff-labs/fff-node\";\n *\n * // Create an instance\n * const finder = FileFinder.create({ basePath: \"/path/to/project\" });\n * if (!finder.ok) {\n * console.error(finder.error);\n * process.exit(1);\n * }\n *\n * // Wait for initial scan\n * await finder.value.waitForScan(5000);\n *\n * // Search for files\n * const search = finder.value.search(\"main.ts\");\n * if (search.ok) {\n * for (const item of search.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n *\n * // Cleanup\n * finder.value.destroy();\n * ```\n */\nexport class FileFinder implements FileFinderApi {\n private handle: NativeHandle | null;\n /** Native ids of this instance's active watch subscriptions. */\n private watchers = new Set<number>();\n\n private constructor(handle: NativeHandle) {\n this.handle = handle;\n }\n\n /**\n * Create a new file finder instance.\n *\n * @param options - Initialization options\n * @returns Result containing the new FileFinder instance or an error\n *\n * @example\n * ```typescript\n * // Basic initialization\n * const finder = FileFinder.create({ basePath: \"/path/to/project\" });\n *\n * // With custom database paths\n * const finder = FileFinder.create({\n * basePath: \"/path/to/project\",\n * frecencyDbPath: \"/custom/frecency.mdb\",\n * historyDbPath: \"/custom/history.mdb\",\n * });\n * ```\n */\n static create(options: InitOptions): Result<FileFinder> {\n const result = ffiCreate(\n options.basePath,\n options.frecencyDbPath ?? \"\",\n options.historyDbPath ?? \"\",\n options.useUnsafeNoLock ?? false,\n !(options.disableMmapCache ?? false),\n !(options.disableContentIndexing ?? options.disableMmapCache ?? false),\n !(options.disableWatch ?? false),\n options.aiMode ?? false,\n options.logFilePath ?? \"\",\n options.logLevel ?? \"\",\n options.cacheBudgetMaxFiles ?? 0,\n options.cacheBudgetMaxBytes ?? 0,\n options.cacheBudgetMaxFileSize ?? 0,\n options.enableFsRootScanning ?? false,\n options.enableHomeDirScanning ?? false,\n options.followSymlinks ?? false,\n );\n\n if (!result.ok) {\n return result;\n }\n\n return { ok: true, value: new FileFinder(result.value) };\n }\n\n /**\n * Destroy and clean up all resources.\n *\n * Call this when you're done using the file finder to free memory\n * and stop background file watching. After calling this, the instance\n * must not be used again.\n */\n destroy(): void {\n if (this.handle !== null) {\n const handle = this.handle;\n ffiDestroy(handle);\n this.handle = null;\n // ffiDestroy unsubscribes every watch (a delivery racing it is a\n // benign id-map miss); dropping the remaining handlers afterwards is\n // safe, and the process-wide trampoline is released when this was the\n // last watching instance.\n ffiWatchCleanupAfterDestroy(handle, this.watchers);\n this.watchers.clear();\n }\n }\n\n /**\n * Check if this instance has been destroyed.\n */\n get isDestroyed(): boolean {\n return this.handle === null;\n }\n\n /**\n * Guard that returns an error if the instance has been destroyed.\n */\n private ensureAlive(): Result<NativeHandle> {\n if (this.handle === null) {\n return err(\"FileFinder instance has been destroyed.\");\n }\n return { ok: true, value: this.handle };\n }\n\n /**\n * Search for files matching the query.\n *\n * The query supports fuzzy matching and special syntax:\n * - `foo bar` - Match files containing \"foo\" and \"bar\"\n * - `src/` - Match files in src directory\n * - `file.ts:42` - Match file.ts with line 42\n * - `file.ts:42:10` - Match file.ts with line 42, column 10\n *\n * @param query - Search query string\n * @param options - Search options\n * @returns Search results with matched files and scores\n *\n * @example\n * ```typescript\n * const result = finder.search(\"main.ts\", { pageSize: 10 });\n * if (result.ok) {\n * console.log(`Found ${result.value.totalMatched} files`);\n * for (const item of result.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n * ```\n */\n fileSearch(query: string, options?: SearchOptions): Result<SearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiSearch(\n guard.value,\n query,\n options?.currentFile ?? \"\",\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n options?.comboBoostMultiplier ?? 0,\n options?.minComboCount ?? 0,\n );\n }\n\n /**\n * Filters files using glob wildcard expression.\n *\n * The pattern is applied as a single pass SIMD optimized prefiltering\n * without any fuzzy matching involved. Faster and 100% compatible to npm `glob`.\n *\n * @param pattern - Glob pattern (required, non-empty)\n * @param options - Glob search options (pagination, max threads, current file)\n * @returns Search results with files matching the glob\n *\n * @example\n * ```typescript\n * const result = finder.glob(\"**\\/*.rs\", { pageSize: 100 });\n * if (result.ok) {\n * for (const item of result.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n * ```\n */\n glob(pattern: string, options?: GlobOptions): Result<SearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiGlob(\n guard.value,\n pattern,\n options?.currentFile ?? \"\",\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n );\n }\n\n /**\n * Search for directories matching the query.\n *\n * @param query - Search query string\n * @param options - Directory search options\n * @returns Search results with matched directories and scores\n *\n * @example\n * ```typescript\n * const result = finder.directorySearch(\"src/comp\", { pageSize: 10 });\n * if (result.ok) {\n * console.log(`Found ${result.value.totalMatched} directories`);\n * for (const item of result.value.items) {\n * console.log(item.relativePath);\n * }\n * }\n * ```\n */\n directorySearch(query: string, options?: DirSearchOptions): Result<DirSearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiSearchDirectories(\n guard.value,\n query,\n options?.currentFile ?? null,\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n );\n }\n\n /**\n * Search for files and directories matching the query (mixed results).\n *\n * Results are interleaved by total score in descending order, mixing\n * both file and directory items.\n *\n * @param query - Search query string\n * @param options - Search options\n * @returns Mixed search results with files and directories interleaved by score\n *\n * @example\n * ```typescript\n * const result = finder.mixedSearch(\"main\", { pageSize: 20 });\n * if (result.ok) {\n * for (const entry of result.value.items) {\n * if (entry.type === \"file\") {\n * console.log(`File: ${entry.item.relativePath}`);\n * } else {\n * console.log(`Dir: ${entry.item.relativePath}`);\n * }\n * }\n * }\n * ```\n */\n mixedSearch(query: string, options?: SearchOptions): Result<MixedSearchResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiSearchMixed(\n guard.value,\n query,\n options?.currentFile ?? \"\",\n options?.maxThreads ?? 0,\n options?.pageIndex ?? 0,\n options?.pageSize ?? 0,\n options?.comboBoostMultiplier ?? 0,\n options?.minComboCount ?? 0,\n );\n }\n\n /**\n * Search file contents (live grep).\n *\n * Searches through the contents of indexed files using the specified mode:\n * - `\"plain\"` (default): SIMD-accelerated literal text matching\n * - `\"regex\"`: Regular expression matching\n * - `\"fuzzy\"`: Smith-Waterman fuzzy matching per line\n *\n * Supports pagination for large result sets. The result includes a `nextCursor`\n * that can be passed back to fetch the next page.\n *\n * The query also supports constraint syntax:\n * - `*.ts pattern` - Only search in TypeScript files\n * - `src/ pattern` - Only search in the src directory\n *\n * @param query - Search query string\n * @param options - Grep options (mode, pagination, limits)\n * @returns Grep results with matched lines and file metadata\n *\n * @example\n * ```typescript\n * // First page\n * const result = finder.grep(\"TODO\", { mode: \"plain\" });\n * if (result.ok) {\n * for (const match of result.value.items) {\n * console.log(`${match.relativePath}:${match.lineNumber}: ${match.lineContent}`);\n * }\n * // Fetch next page\n * if (result.value.nextCursor) {\n * const page2 = finder.grep(\"TODO\", {\n * cursor: result.value.nextCursor,\n * });\n * }\n * }\n * ```\n */\n grep(query: string, options?: GrepOptions): Result<GrepResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n return ffiLiveGrep(\n guard.value,\n query,\n options?.mode ?? \"plain\",\n options?.maxFileSize ?? 0,\n options?.maxMatchesPerFile ?? 0,\n options?.smartCase ?? true,\n options?.cursor?._offset ?? 0,\n options?.pageSize ?? 0,\n options?.timeBudgetMs ?? 0,\n options?.beforeContext ?? 0,\n options?.afterContext ?? 0,\n options?.classifyDefinitions ?? false,\n );\n }\n\n /**\n * Multi-pattern OR search using Aho-Corasick.\n *\n * Searches for lines matching ANY of the provided patterns using\n * SIMD-accelerated multi-needle matching. Faster than regex alternation\n * for literal text searches.\n *\n * Supports pagination. The result includes a `nextCursor` that can be\n * passed back to fetch the next page.\n *\n * @param options - Multi-grep options including patterns and optional constraints\n * @returns Grep results with matched lines and file metadata\n *\n * @example\n * ```typescript\n * const result = finder.multiGrep({\n * patterns: [\"VideoFrame\", \"video_frame\", \"PreloadedImage\"],\n * });\n * if (result.ok) {\n * for (const match of result.value.items) {\n * console.log(`${match.relativePath}:${match.lineNumber}: ${match.lineContent}`);\n * }\n * }\n * ```\n */\n multiGrep(options: MultiGrepOptions): Result<GrepResult> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n if (!options.patterns || options.patterns.length === 0) {\n return err(\"patterns array must have at least 1 element\");\n }\n\n return ffiMultiGrep(\n guard.value,\n options.patterns.join(\"\\n\"),\n options.constraints ?? \"\",\n options.maxFileSize ?? 0,\n options.maxMatchesPerFile ?? 0,\n options.smartCase ?? true,\n options.cursor?._offset ?? 0,\n options.pageSize ?? 0,\n options.timeBudgetMs ?? 0,\n options.beforeContext ?? 0,\n options.afterContext ?? 0,\n options.classifyDefinitions ?? false,\n );\n }\n\n /**\n * Trigger a rescan of the indexed directory.\n *\n * This is useful after major file system changes that the\n * background watcher might have missed.\n */\n scanFiles(): Result<void> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiScanFiles(guard.value);\n }\n\n /**\n * Check if a scan is currently in progress.\n */\n isScanning(): boolean {\n if (this.handle === null) return false;\n return ffiIsScanning(this.handle);\n }\n\n /**\n * Get the base path of the file picker (the root directory being indexed).\n */\n getBasePath(): Result<string | null> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiGetBasePath(guard.value);\n }\n\n /**\n * Get the current scan progress.\n */\n getScanProgress(): Result<ScanProgress> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiGetScanProgress(guard.value) as Result<ScanProgress>;\n }\n\n /**\n * Wait for the initial file scan to complete.\n * Non-blocking: polls `isScanning` and yields to the event loop between checks.\n *\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)\n * @returns true if scan completed, false if timed out\n *\n * @example\n * ```typescript\n * const finder = FileFinder.create({ basePath: \"/path/to/project\" });\n * if (finder.ok) {\n * const completed = await finder.value.waitForScan(10000);\n * if (!completed.ok || !completed.value) {\n * console.warn(\"Scan did not complete in time\");\n * }\n * }\n * ```\n */\n async waitForScan(timeoutMs: number = 5000): Promise<Result<boolean>> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n const deadline = Date.now() + timeoutMs;\n while (this.isScanning()) {\n if (Date.now() >= deadline) {\n return { ok: true, value: false };\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return { ok: true, value: true };\n }\n\n /**\n * Wait for the initial file scan to complete, blocking the calling thread.\n *\n * Backed by the native `fff_wait_for_scan` call. Prefer {@link waitForScan}\n * unless you specifically need synchronous blocking behaviour.\n *\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)\n * @returns true if scan completed, false if timed out\n */\n waitForScanBlocking(timeoutMs: number = 5000): Result<boolean> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiWaitForScan(guard.value, timeoutMs);\n }\n\n /**\n * Wait until the index is fully ready: the scan has finished and the warmup\n * (content indexing / bigram) phase has completed.\n *\n * Non-blocking: polls `getScanProgress` and yields to the event loop.\n *\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)\n * @returns true if the index became ready, false if timed out\n */\n async waitForIndexReady(timeoutMs: number = 5000): Promise<Result<boolean>> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n const deadline = Date.now() + timeoutMs;\n while (true) {\n const progress = this.getScanProgress();\n if (!progress.ok) return progress;\n if (!progress.value.isScanning && progress.value.isWarmupComplete) {\n return { ok: true, value: true };\n }\n if (Date.now() >= deadline) {\n return { ok: true, value: false };\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n }\n\n /**\n * Change the indexed directory to a new path.\n *\n * This stops the current file watcher and starts indexing the new directory.\n *\n * @param newPath - New directory path to index\n */\n reindex(newPath: string): Result<void> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiRestartIndex(guard.value, newPath);\n }\n\n /**\n * Refresh the git status cache.\n *\n * @returns Number of files with updated git status\n */\n refreshGitStatus(): Result<number> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiRefreshGitStatus(guard.value);\n }\n\n /**\n * Track query completion for smart suggestions.\n *\n * Call this when a user selects a file from search results.\n * This helps improve future search rankings for similar queries.\n *\n * @param query - The search query that was used\n * @param selectedFilePath - The file path that was selected\n */\n trackQuery(query: string, selectedFilePath: string): Result<boolean> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiTrackQuery(guard.value, query, selectedFilePath);\n }\n\n /**\n * Get a historical query by offset.\n *\n * @param offset - Offset from most recent (0 = most recent)\n * @returns The historical query string, or null if not found\n */\n getHistoricalQuery(offset: number): Result<string | null> {\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n return ffiGetHistoricalQuery(guard.value, offset);\n }\n\n /**\n * Subscribe to filesystem changes matching `pattern`.\n *\n * Pattern semantics:\n * - Wildcards (`*.rs`, `src/**`, `./**\\/*.ts`) — glob matched against the\n * base-path-relative path. Absolute globs must be under the base path.\n * - No wildcards — resolved against the base path (must stay inside it):\n * an existing directory subscribes to its whole subtree, anything else\n * is an exact file path.\n * - Omitted — subscribes to the entire indexed tree: `watch(callback)`.\n *\n * Push-based: events are delivered by the native watcher through a single\n * process-wide callback trampoline (no polling, no idle wakeups). The\n * callback receives normalized batches of up to 128 events on the JS event\n * loop, with each path appearing at most once.\n *\n * Requires the instance to be created with watching enabled (default).\n *\n * @param pattern - Glob pattern, exact file path, or directory\n * @param callback - Invoked with each batch of events\n * @param options - Watch options\n * @returns Unsubscribe handle; call it to stop\n *\n * @example\n * ```typescript\n * const sub = finder.watch(\"**\\/*.rs\", (events) => {\n * for (const e of events) console.log(e.kind, e.path);\n * });\n * if (sub.ok) sub.value(); // unsubscribe\n *\n * // no pattern: everything under the indexed base path\n * const all = finder.watch((events) => console.log(events.length));\n * ```\n */\n watch(callback: WatchBatchCallback, options?: WatchOptions): Result<WatchUnsubscribe>;\n watch(\n pattern: string,\n callback: WatchBatchCallback,\n options?: WatchOptions,\n ): Result<WatchUnsubscribe>;\n watch(\n patternOrCallback: string | WatchBatchCallback,\n callbackOrOptions?: WatchBatchCallback | WatchOptions,\n maybeOptions?: WatchOptions,\n ): Result<WatchUnsubscribe> {\n // Overload shift: watch(cb, opts?) -> empty pattern = whole tree.\n const noPattern = typeof patternOrCallback === \"function\";\n const pattern = noPattern ? \"\" : patternOrCallback;\n const callback = noPattern\n ? patternOrCallback\n : (callbackOrOptions as WatchBatchCallback);\n const options = noPattern\n ? (callbackOrOptions as WatchOptions | undefined)\n : maybeOptions;\n\n if (typeof callback !== \"function\") {\n return err(\"watch callback must be a function\");\n }\n\n const guard = this.ensureAlive();\n if (!guard.ok) return guard;\n\n const created = ffiWatch(guard.value, pattern, options?.ignore ?? [], callback);\n if (!created.ok) return created;\n\n const watchId = created.value;\n this.watchers.add(watchId);\n\n return {\n ok: true,\n value: () => {\n if (!this.watchers.delete(watchId)) return;\n if (this.handle !== null) ffiUnwatch(this.handle, watchId);\n },\n };\n }\n\n /**\n * Get health check information.\n *\n * Useful for debugging and verifying the file finder is working correctly.\n *\n * @param testPath - Optional path to test git repository detection\n */\n healthCheck(testPath?: string): Result<HealthCheck> {\n return ffiHealthCheck(this.handle, testPath || \"\") as Result<HealthCheck>;\n }\n\n /**\n * Check if the native library is available.\n */\n static isAvailable(): boolean {\n return isAvailable();\n }\n\n /**\n * Ensure the native library is loaded.\n *\n * Loads the native library from the platform-specific npm package\n * or a local dev build. Throws if the binary is not found.\n */\n static ensureLoaded(): void {\n ensureLoaded();\n }\n\n /**\n * Get a health check without requiring an instance.\n *\n * Returns limited info (version + git only, no picker/frecency/query data).\n *\n * @param testPath - Optional path to test git repository detection\n */\n static healthCheckStatic(testPath?: string): Result<HealthCheck> {\n return ffiHealthCheck(null, testPath || \"\") as Result<HealthCheck>;\n }\n}\n"],"mappings":"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","names":["err","require","npmPath"]}