@sakiori/fff-node 0.11.1-sakiori.20261105.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,32 @@
1
- # fff - Fast File Finder
2
-
3
- High-performance fuzzy file finder for Node.js, powered by Rust. Extremely fast live file, content, and directory search with a typo-resistant algorithm. As well as regex, plain-text, multi-occurrence and typo-resistant content search.
4
-
5
- Comes with built-in git status support, frecency ranking, content indexing and many more! Designed for LLM agent tools that search through codebases or agentic RAG document search.
6
-
7
- This is the Node package of the fork of fff at <https://gitlab.com/sakiori-oss/fff>. Its indexes live in the shared fff service of the current OS user, which `fff-mcp` agents of the same release use too, so a directory that an application and its agents search is indexed once.
1
+ # @sakiori/fff-node
2
+
3
+ Fast, typo-resistant file, directory and content search for Node.js, from the
4
+ [sakiori-oss fork of fff](https://gitlab.com/sakiori-oss/fff). The search itself is upstream
5
+ fff's: fuzzy path matching, globs, plain-text, regex and multi-pattern grep, git status and
6
+ frecency ranking.
7
+
8
+ The difference is where the index lives. Upstream's `@ff-labs/fff-node` keeps it in your
9
+ process. This package keeps it in the shared fff service of the current OS user, which the
10
+ `fff-mcp` agents of the same fork release use too, so an application and its agents index a
11
+ directory once.
12
+
13
+ Compared with `@ff-labs/fff-node`, this package:
14
+
15
+ - returns a Promise from `FileFinder.create` and from every instance method except
16
+ `getBasePath`, `isDestroyed` and the methods listed as unavailable below, and runs each
17
+ call off the event loop; upstream's `destroy`, `isScanning`, searches and greps are
18
+ synchronous. Results keep upstream's `Result` shape;
19
+ - fails a call after a bound when the service does not answer (`timeoutMs` in
20
+ `FileFinder.create`, see [Timeouts](#timeouts));
21
+ - adds `notifyChanged` for files your program changes, `excludeFrecency` for rankings
22
+ without the shared frecency, `serviceDir` for a separate service namespace, and repository
23
+ and ref events in `watch`;
24
+ - shares a service only with the `fff-mcp` of the same fork release, which has the
25
+ package's version;
26
+ - has no `waitForScanBlocking`; `trackQuery`, `getHistoricalQuery` and `refreshGitStatus`
27
+ return an error, `reindex` rescans only the instance's own directory, `historyDbPath` is
28
+ not supported, and `cacheBudgetMaxBytes` and `cacheBudgetMaxFileSize` are ignored
29
+ ([Unavailable methods](#unavailable-methods)).
8
30
 
9
31
  ## Installation
10
32
 
@@ -25,10 +47,16 @@ The native library and the `fff-mcp` service executable come in platform package
25
47
 
26
48
  The package installs on macOS, Linux and FreeBSD, on x64 and ARM64; npm and pnpm refuse it on any other platform (`EBADPLATFORM`). FreeBSD has no platform package: build the native files from source (see [Platforms without a prebuilt package](#platforms-without-a-prebuilt-package)), or `FileFinder.create` fails with `unsupportedPlatform`. An install that omits optional dependencies has no native files, and `FileFinder.create` fails with `libraryUnavailable`. The glibc packages need glibc 2.28 or later.
27
49
 
28
- A project that imports the upstream name `@ff-labs/fff-node` keeps its imports with a package-manager alias: `npm install @ff-labs/fff-node@npm:@sakiori/fff-node@<version>`.
50
+ A project that imports the upstream name `@ff-labs/fff-node` can keep its imports with a package-manager alias, `npm install @ff-labs/fff-node@npm:@sakiori/fff-node@^1.0.0`, but its calls still need the changes listed at the top.
29
51
 
30
52
  Requires Node.js 20.19 or later in the 20 line, or 22.12 and later. The package is an ES module; CommonJS `require` loads the same module, which relies on `require` of ES modules in those Node.js versions. In Electron, that takes a release whose bundled Node.js is one of them.
31
53
 
54
+ ### Versions
55
+
56
+ The package is published at the version of the fork release that builds it, a semantic version of the fork's own: a breaking change of the package's API or its `exports` raises the major version, an addition the minor, and a fix the patch. A change of the platforms or Node.js versions it supports raises at least the minor version, and the maintainers mark one that drops support as breaking; a release that changes only the native code is a new version too. Depend on a range of one major, such as the `^1.0.0` that `npm install` writes, to take every later release of it. The platform packages come at exactly the Node package's version. The package shares its service with the `fff-mcp` archives of the same version; the release's notes name the upstream fff release it carries.
57
+
58
+ Versions before 1.0.0, `0.11.1-sakiori.<date>.<n>`, were prereleases, which a range does not follow past their own `major.minor.patch`; replace an exact pin of one with `^1.0.0`.
59
+
32
60
  ### Electron
33
61
 
34
62
  Unpack the platform package's whole directory from the application archive (for example with electron-builder's `asarUnpack`). The package then loads the library and starts the service from `app.asar.unpacked`, and reports an error when only one of the two files is unpacked.
@@ -104,6 +132,14 @@ While the shared index scans, the `scannedFilesCount` of `getScanProgress` count
104
132
 
105
133
  An index shared with agents may track frecency, which moves recently and frequently used files up. Pass `excludeFrecency: true` to `fileSearch`, `directorySearch`, `mixedSearch`, `glob`, `grep` or `multiGrep` to get the order an index without frecency gives: an empty query lists entries in path order, and grep searches the newest files first. Other clients of the index keep their ranking.
106
134
 
135
+ ## Ignore rules
136
+
137
+ `extraIgnore` adds `.gitignore` rules, relative to `basePath`, to the rules the index applies; a `!` prefix re-includes a path, and an ignored parent directory needs re-including too. `disableGit: true` skips Git discovery, status, ranking and Git ignore rules, and the index then includes hidden files but not Git metadata under `.git`. `disableIgnoreFiles: true` skips the rules of `.ignore` files. Instances share an index only when these options match, and an instance with `extraIgnore` uses an index rooted at its own directory.
138
+
139
+ ```ts
140
+ await FileFinder.create({ basePath: process.cwd(), extraIgnore: ["/generated/", "!/keep.log"] });
141
+ ```
142
+
107
143
  ## Service namespace
108
144
 
109
145
  Services live in a per-user namespace directory. `serviceDir` in `FileFinder.create`, or the `FFF_MCP_SERVICE_DIR` environment variable, selects another one, for example for tests. `FFF_MCP_IDLE_TIMEOUT_SECS` sets how long a service this package starts keeps running without clients; the default is one hour.
@@ -131,7 +167,7 @@ const watched = await finder.watch("src/**", (events) => {
131
167
  if (watched.ok) watched.value(); // unsubscribe
132
168
  ```
133
169
 
134
- The pattern and the `ignore` entries are globs, exact paths or directories relative to the instance's directory; without a pattern the whole directory is watched. Ignored files never produce events. Batches reach the callback on the event loop with absolute paths and the kinds `created`, `modified`, `removed`, `renamed` (with `from`) and `rescan`. A `rescan`, naming the instance's directory, replaces changes that could not be delivered: when the callback falls behind, after a rescan of the index, or after the connection to the service broke, which the subscription reopens by itself. Re-read whatever you derive from the directory when one arrives. With `repository: true`, a `repository` event names the work tree whenever the git state of a repository inside the instance's directory, or containing it, changes, as `git add`, a commit or a checkout do; files under `.git` never appear as file events. With `refs: true`, a `refs` event names the work tree whenever one of its branches, tags or remote-tracking refs is created, moved or deleted, as `git branch`, `git tag`, a commit, `git fetch` or `git pack-refs` do, or its HEAD moves; a linked worktree shares its main repository's refs, and a switch in a linked worktree of the repository is told to every subscription of the repository, naming that worktree where the subscription's directory holds it and another work tree of the repository otherwise, so re-read the repository's worktrees when one arrives. A callback that falls behind by more than 1024 events gets one `rescan` for what it missed. A `failed` event, carrying `error` and `code`, ends a subscription the service can no longer serve, as after its directory was removed. Once the unsubscribe function returns, or `destroy` was called, the callback is not called again. A ready subscription does not keep the process alive, and a pending one holds none of the package's workers. On an instance created with `disableWatch`, `watch` fails with `unavailable`.
170
+ The pattern and the `ignore` entries are globs, exact paths or directories relative to the instance's directory; without a pattern the whole directory is watched. A glob without `/` matches the file name at any depth, so `ignore: ["*.log"]` also drops `a/b/x.log`, while the directories above a file do not match it: exclude a directory with its path or `dir/**`. Ignored files never produce events. Batches reach the callback on the event loop with absolute paths and the kinds `created`, `modified`, `removed`, `renamed` (with `from`) and `rescan`. A `rescan`, naming the instance's directory, replaces changes that could not be delivered: when the callback falls behind, after a rescan of the index, or after the connection to the service broke, which the subscription reopens by itself. Re-read whatever you derive from the directory when one arrives. With `repository: true`, a `repository` event names the work tree whenever the git state of a repository inside the instance's directory, or containing it, changes, as `git add`, a commit or a checkout do; files under `.git` never appear as file events. With `refs: true`, a `refs` event names the work tree whenever one of its branches, tags or remote-tracking refs is created, moved or deleted, as `git branch`, `git tag`, a commit, `git fetch` or `git pack-refs` do, or its HEAD moves; a linked worktree shares its main repository's refs, and a switch in a linked worktree of the repository is told to every subscription of the repository, naming that worktree where the subscription's directory holds it and another work tree of the repository otherwise, so re-read the repository's worktrees when one arrives. A callback that falls behind by more than 1024 events gets one `rescan` for what it missed. A `failed` event, carrying `error` and `code`, ends a subscription the service can no longer serve, as after its directory was removed. Once the unsubscribe function returns, or `destroy` was called, the callback is not called again. A ready subscription does not keep the process alive, and a pending one holds none of the package's workers. On an instance created with `disableWatch`, `watch` fails with `unavailable`.
135
171
 
136
172
  ## Timeouts
137
173
 
@@ -139,7 +175,7 @@ A service that does not answer makes `FileFinder.create` fail after 90 seconds a
139
175
 
140
176
  ## Unavailable methods
141
177
 
142
- `trackQuery`, `getHistoricalQuery` and `refreshGitStatus` have no shared service equivalent and return an error naming them as unavailable over the shared service; the service keeps the index and git status current itself. `reindex` rescans the instance's own directory; create a new instance for another directory. `historyDbPath` is not supported.
178
+ `waitForScanBlocking` does not exist; await `waitForScan` or `waitForIndexReady`. `trackQuery`, `getHistoricalQuery` and `refreshGitStatus` have no shared service equivalent and return an error naming them as unavailable over the shared service; the service keeps the index and git status current itself. `reindex` rescans the instance's own directory; create a new instance for another directory. `historyDbPath` is not supported.
143
179
 
144
180
  ## API Reference
145
181
 
package/dist/fff-api.d.ts CHANGED
@@ -37,6 +37,12 @@ export declare function err<T>(error: string): Result<T>;
37
37
  export interface InitOptions {
38
38
  /** Base directory to index (required) */
39
39
  basePath: string;
40
+ /** Extra `.gitignore` rules relative to `basePath`. Use `!pattern` to re-include paths. Requires a zlob build. */
41
+ extraIgnore?: readonly string[];
42
+ /** Disable Git integration and Git ignore rules for this index. Requires a zlob build. */
43
+ disableGit?: boolean;
44
+ /** Skip rules from `.ignore` files. Requires a zlob build. */
45
+ disableIgnoreFiles?: boolean;
40
46
  /** Path to frecency database (optional, omit to skip frecency initialization) */
41
47
  frecencyDbPath?: string;
42
48
  /** Path to query history database (optional, omit to skip query tracker initialization) */
@@ -308,7 +314,7 @@ export interface WatchEvent {
308
314
  }
309
315
  /** Options for watch subscriptions. */
310
316
  export interface WatchOptions {
311
- /** Additional glob wildcard patterns to ignore */
317
+ /** Additional glob wildcard patterns to ignore; one without `/` matches the file name at any depth */
312
318
  ignore?: string[];
313
319
  }
314
320
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"fff-api.d.ts","sourceRoot":"","sources":["../src/fff-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fff-api.d.ts","sourceRoot":"","sources":["../src/fff-api.ts"],"names":[],"mappings":"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"}
package/dist/ffi.d.ts CHANGED
@@ -39,7 +39,7 @@ export declare function nativeInstanceCount(): number;
39
39
  * Open a session on the shared service on a worker thread. `timeoutMs` bounds
40
40
  * opening it and each later call (0 takes the library's defaults).
41
41
  */
42
- export declare function ffiCreate(basePath: string, frecencyDbPath: string, enableMmapCache: boolean, enableContentIndexing: boolean, watch: boolean, aiMode: boolean, logFilePath: string, logLevel: string, cacheBudgetMaxFiles: number, enableFsRootScanning: boolean, enableHomeDirScanning: boolean, followSymlinks: boolean, serviceDir: string, timeoutMs: number): Promise<Result<NativeHandle>>;
42
+ export declare function ffiCreate(basePath: string, frecencyDbPath: string, enableMmapCache: boolean, enableContentIndexing: boolean, watch: boolean, aiMode: boolean, logFilePath: string, logLevel: string, cacheBudgetMaxFiles: number, enableFsRootScanning: boolean, enableHomeDirScanning: boolean, followSymlinks: boolean, extraIgnore: string, disableGit: boolean, disableIgnoreFiles: boolean, serviceDir: string, timeoutMs: number): Promise<Result<NativeHandle>>;
43
43
  /**
44
44
  * End an instance's session and free it on a worker thread. No other call may
45
45
  * be in flight on the instance.
package/dist/ffi.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ffi.d.ts","sourceRoot":"","sources":["../src/ffi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,EAAmC,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AA4D3E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAiBlC;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AA+JD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,OAAO,EACxB,qBAAqB,EAAE,OAAO,EAC9B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM,EAC3B,oBAAoB,EAAE,OAAO,EAC7B,qBAAqB,EAAE,OAAO,EAC9B,cAAc,EAAE,OAAO,EACvB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAmC/B;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAMpE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,CAAC,EACpC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CASpB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAIpF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EAAE,EAChB,UAAU,EAAE,OAAO,EACnB,IAAI,EAAE,OAAO,GACZ,MAAM,CAAC,MAAM,CAAC,CAchB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAGhF;AAOD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAI1E;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAGvB;AAYD;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAG1B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAOtE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAGnC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAOlD"}
1
+ {"version":3,"file":"ffi.d.ts","sourceRoot":"","sources":["../src/ffi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,EAAmC,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAgE3E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAiBlC;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AA+JD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,OAAO,EACxB,qBAAqB,EAAE,OAAO,EAC9B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM,EAC3B,oBAAoB,EAAE,OAAO,EAC7B,qBAAqB,EAAE,OAAO,EAC9B,cAAc,EAAE,OAAO,EACvB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,OAAO,EACnB,kBAAkB,EAAE,OAAO,EAC3B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CA8C/B;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAMpE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,CAAC,EACpC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CASpB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAIpF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EAAE,EAChB,UAAU,EAAE,OAAO,EACnB,IAAI,EAAE,OAAO,GACZ,MAAM,CAAC,MAAM,CAAC,CAchB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAGhF;AAOD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAI1E;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAGvB;AAYD;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAG1B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAOtE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAGnC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAOlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"finder.d.ts","sourceRoot":"","sources":["../src/finder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"finder.d.ts","sourceRoot":"","sources":["../src/finder.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -43,7 +43,7 @@
43
43
  */
44
44
  export { binaryExists, findBinary, findNativeFiles, findServiceExecutable, type NativeFiles, unpackedAsarPath, } from "./binary.js";
45
45
  export { closeLibrary, nativeInstanceCount } from "./ffi.js";
46
- export type { DbHealth, DirItem, DirSearchOptions, DirSearchResult, FileItem, GrepCursor, GrepMatch, GrepMode, DefinitionMode, GrepOptions, GrepResult, HealthCheck, InitOptions, Location, MixedItem, MixedSearchResult, MultiGrepOptions, ScanProgress, Score, SearchOptions, SearchResult, WatchBatchCallback, WatchEvent, WatchEventKind, WatchOptions, WatchUnsubscribe, } from "./fff-api.js";
46
+ export type { DbHealth, DirItem, DirSearchOptions, DirSearchResult, FileItem, GlobOptions, GrepCursor, GrepMatch, GrepMode, DefinitionMode, GrepOptions, GrepResult, HealthCheck, InitOptions, Location, MixedItem, MixedSearchResult, MultiGrepOptions, ScanProgress, Score, SearchOptions, SearchResult, WatchBatchCallback, WatchEvent, WatchEventKind, WatchOptions, WatchUnsubscribe, } from "./fff-api.js";
47
47
  export { ErrorCode, type Failure, type Result, err, ok } from "./errors.js";
48
48
  export { FileFinder } from "./finder.js";
49
49
  export { getLibExtension, getLibFilename, getNpmPackageName, getServiceExecutableFilename, getTriple, } from "./platform.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EACL,YAAY,EACZ,UAAU,EACV,eAAe,EACf,qBAAqB,EACrB,KAAK,WAAW,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC7D,YAAY,EACV,QAAQ,EACR,OAAO,EACP,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACR,UAAU,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,YAAY,EACZ,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,4BAA4B,EAC5B,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EACL,YAAY,EACZ,UAAU,EACV,eAAe,EACf,qBAAqB,EACrB,KAAK,WAAW,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC7D,YAAY,EACV,QAAQ,EACR,OAAO,EACP,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACR,WAAW,EACX,UAAU,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,YAAY,EACZ,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,4BAA4B,EAC5B,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -495,10 +495,14 @@ function createOptionsStruct() {
495
495
  cache_budget_max_file_size: DataType.U64,
496
496
  enable_fs_root_scanning: DataType.U8,
497
497
  enable_home_dir_scanning: DataType.U8,
498
- follow_symlinks: DataType.U8
498
+ follow_symlinks: DataType.U8,
499
+ extra_ignore: DataType.String,
500
+ disable_git: DataType.U8,
501
+ disable_ignore_files: DataType.U8
499
502
  };
500
503
  }
501
- var FFF_CREATE_OPTIONS_VERSION = 2;
504
+ var FFF_CREATE_OPTIONS_VERSION = 3;
505
+ var LEGACY_CREATE_OPTIONS_VERSION = 2;
502
506
  function resultStruct() {
503
507
  const { DataType } = ffi();
504
508
  return {
@@ -631,11 +635,15 @@ function snakeToCamel(obj) {
631
635
  }
632
636
  return result;
633
637
  }
634
- async function ffiCreate(basePath, frecencyDbPath, enableMmapCache, enableContentIndexing, watch, aiMode, logFilePath, logLevel, cacheBudgetMaxFiles, enableFsRootScanning, enableHomeDirScanning, followSymlinks, serviceDir, timeoutMs) {
638
+ async function ffiCreate(basePath, frecencyDbPath, enableMmapCache, enableContentIndexing, watch, aiMode, logFilePath, logLevel, cacheBudgetMaxFiles, enableFsRootScanning, enableHomeDirScanning, followSymlinks, extraIgnore, disableGit, disableIgnoreFiles, serviceDir, timeoutMs) {
639
+ if (extraIgnore.includes("\0")) {
640
+ return err("extraIgnore cannot contain NUL bytes", ErrorCode.invalidRequest);
641
+ }
635
642
  const library = loadLibrary();
636
643
  if (!library.ok) return library;
644
+ const version = extraIgnore || disableGit || disableIgnoreFiles ? FFF_CREATE_OPTIONS_VERSION : LEGACY_CREATE_OPTIONS_VERSION;
637
645
  const optsValue = {
638
- version: FFF_CREATE_OPTIONS_VERSION,
646
+ version,
639
647
  base_path: basePath,
640
648
  frecency_db_path: frecencyDbPath,
641
649
  history_db_path: "",
@@ -650,7 +658,10 @@ async function ffiCreate(basePath, frecencyDbPath, enableMmapCache, enableConten
650
658
  cache_budget_max_file_size: 0,
651
659
  enable_fs_root_scanning: enableFsRootScanning ? 1 : 0,
652
660
  enable_home_dir_scanning: enableHomeDirScanning ? 1 : 0,
653
- follow_symlinks: followSymlinks ? 1 : 0
661
+ follow_symlinks: followSymlinks ? 1 : 0,
662
+ extra_ignore: extraIgnore,
663
+ disable_git: disableGit ? 1 : 0,
664
+ disable_ignore_files: disableIgnoreFiles ? 1 : 0
654
665
  };
655
666
  const created = await runOnWorker("create", [
656
667
  optsValue,
@@ -904,6 +915,9 @@ var FileFinder = class _FileFinder {
904
915
  options.enableFsRootScanning ?? false,
905
916
  options.enableHomeDirScanning ?? false,
906
917
  options.followSymlinks ?? false,
918
+ options.extraIgnore?.join("\n") ?? "",
919
+ options.disableGit ?? false,
920
+ options.disableIgnoreFiles ?? false,
907
921
  options.serviceDir ?? "",
908
922
  timeoutMs
909
923
  );
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/binary.ts","../src/platform.ts","../src/ffi.ts","../src/native-pool.ts","../src/native-worker.ts","../src/fff-api.ts","../src/finder.ts"],"sourcesContent":["/**\n * Native file resolution for fff-node\n *\n * The native library and the fff-mcp service executable must come from one\n * build, so both are resolved from the same location:\n * 1. The FFF_C_LIB environment variable (explicit path to libfff_c.so/.dylib/.dll,\n * with fff-mcp beside it)\n * 2. Platform-specific npm package (e.g. @sakiori/fff-bin-darwin-arm64)\n * 3. Local dev build (bin/, then target/release or target/debug)\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ErrorCode, err, type Result } from \"./errors.js\";\nimport {\n getLibFilename,\n getServiceExecutableFilename,\n getTriple,\n TRIPLE_TO_NPM_PACKAGE,\n} from \"./platform.js\";\n\nconst PACKAGE_NAME = \"@sakiori/fff-node\";\n\n/** The native library and the service executable of one build. */\nexport interface NativeFiles {\n library: string;\n executable: string;\n}\n\n/**\n * Get the current file's directory\n */\nfunction getCurrentDir(): string {\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 === PACKAGE_NAME) {\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 * Map a path inside an `.asar` archive to its `.asar.unpacked` counterpart when\n * the unpacked file exists. Native files cannot be loaded or executed from inside\n * an archive, so Electron applications unpack them beside it.\n */\nexport function unpackedAsarPath(path: string): string {\n const segments = path.split(sep);\n for (let i = 0; i < segments.length; i++) {\n if (!segments[i].endsWith(\".asar\")) continue;\n const unpacked = [...segments];\n unpacked[i] = `${segments[i]}.unpacked`;\n const candidate = unpacked.join(sep);\n if (existsSync(candidate)) return candidate;\n }\n return path;\n}\n\n/**\n * Both native files in `dir`, taken from its `.asar.unpacked` counterpart when\n * that exists, so they always come from one directory. Null when neither is\n * there; an error when only one is, or when they are inside an archive.\n */\nfunction nativeFilesIn(dir: string): Result<NativeFiles> | null {\n const unpacked = unpackedAsarPath(dir);\n const library = join(unpacked, getLibFilename());\n const executable = join(unpacked, getServiceExecutableFilename());\n const [hasLibrary, hasExecutable] = [existsSync(library), existsSync(executable)];\n if (hasLibrary && hasExecutable) {\n if (unpacked === dir && dir.split(sep).some((segment) => segment.endsWith(\".asar\"))) {\n return err(\n `The native files of ${dir} are inside an asar archive; unpack the platform package's directory`,\n ErrorCode.libraryUnavailable,\n );\n }\n return { ok: true, value: { library, executable } };\n }\n if (hasLibrary || hasExecutable) {\n const missing = hasLibrary ? executable : library;\n return err(\n `${missing} is missing beside the other native file; ship both from one build`,\n ErrorCode.libraryUnavailable,\n );\n }\n return null;\n}\n\n/**\n * Resolve the native files from the platform-specific npm package.\n *\n * @sakiori/fff-node lists every platform package as an optional dependency, and an\n * install brings the host's one (e.g. @sakiori/fff-bin-darwin-arm64), which holds\n * both files at its root.\n */\nfunction resolveFromNpmPackage(packageName: string): Result<NativeFiles> | null {\n let packageJsonPath: string;\n try {\n const require = createRequire(join(getPackageDir(), \"package.json\"));\n packageJsonPath = require.resolve(`${packageName}/package.json`);\n } catch {\n // Not installed, e.g. optional dependencies omitted or left out of a bundle\n return null;\n }\n return nativeFilesIn(dirname(packageJsonPath));\n}\n\n/**\n * Resolve the native files from an explicit library path, for platforms without a\n * prebuilt package (for example a libfff_c.so built from source on FreeBSD) or any\n * custom build. The service executable of the same build must sit beside it.\n */\nfunction resolveFromOverride(library: string): Result<NativeFiles> {\n if (!existsSync(library)) {\n return err(\n `FFF_C_LIB points to a file that does not exist: ${library}`,\n ErrorCode.libraryUnavailable,\n );\n }\n const executable = join(dirname(library), getServiceExecutableFilename());\n if (!existsSync(executable)) {\n return err(\n `${executable} is missing beside FFF_C_LIB; ship both from one build`,\n ErrorCode.libraryUnavailable,\n );\n }\n return { ok: true, value: { library, executable } };\n}\n\n/**\n * Get the development build (for local development)\n */\nfunction resolveFromDevBuild(): Result<NativeFiles> | null {\n const packageDir = getPackageDir();\n const workspaceRoot = join(packageDir, \"..\", \"..\");\n for (const dir of [\n join(packageDir, \"bin\"),\n join(workspaceRoot, \"target\", \"release\"),\n join(workspaceRoot, \"target\", \"debug\"),\n ]) {\n const found = nativeFilesIn(dir);\n if (found) return found;\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// Resolved once per process; the files do not move while it runs.\nlet resolved: Result<NativeFiles> | undefined;\n\nfunction resolveNativeFiles(): Result<NativeFiles> {\n const override = process.env[\"FFF_C_LIB\"];\n if (override) return resolveFromOverride(override);\n let triple: string;\n try {\n triple = getTriple();\n } catch (error) {\n return err(String(error instanceof Error ? error.message : error), ErrorCode.unsupportedPlatform);\n }\n const packageName = TRIPLE_TO_NPM_PACKAGE[triple];\n const fromPackage = () => (packageName ? resolveFromNpmPackage(packageName) : null);\n const found = isDevWorkspace()\n ? (resolveFromDevBuild() ?? fromPackage())\n : (fromPackage() ?? resolveFromDevBuild());\n if (found) return found;\n if (!packageName) {\n return err(\n `@sakiori/fff-node supports macOS and Linux with glibc or musl, each on x64 and arm64, not ${triple}; set FFF_C_LIB to a source build`,\n ErrorCode.unsupportedPlatform,\n );\n }\n return err(\n `fff native files not found: install ${packageName} beside @sakiori/fff-node, which an install that omits optional dependencies skips, or build libfff_c and fff-mcp from source with \\`make prepare-node\\``,\n ErrorCode.libraryUnavailable,\n );\n}\n\n/**\n * Find the native library and the service executable.\n *\n * Resolution order:\n * - FFF_C_LIB (if set) always wins\n * - Dev workspace: local dev build first, then npm package\n * - Production: npm package first, then dev build\n *\n * @returns Absolute paths of both files from one directory, or why they cannot be used\n */\nexport function findNativeFiles(): Result<NativeFiles> {\n resolved ??= resolveNativeFiles();\n return resolved;\n}\n\n/**\n * Find the native library.\n *\n * @returns Absolute path to the library, or null if not found\n */\nexport function findBinary(): string | null {\n const files = findNativeFiles();\n return files.ok ? files.value.library : null;\n}\n\n/**\n * Find the fff-mcp executable that starts the shared service.\n *\n * @returns Absolute path to the executable, or null if not found\n */\nexport function findServiceExecutable(): string | null {\n const files = findNativeFiles();\n return files.ok ? files.value.executable : null;\n}\n\n/**\n * Check if the native files exist in any known location\n */\nexport function binaryExists(): boolean {\n return findNativeFiles().ok;\n}\n","/**\n * Platform detection utilities for downloading the correct binary\n */\n\nimport { readFileSync } from \"node:fs\";\n\nlet triple: string | undefined;\n\n/**\n * Get the platform triple (e.g., \"x86_64-unknown-linux-gnu\"), detected once per process.\n */\nexport function getTriple(): string {\n triple ??= detectTriple();\n return triple;\n}\n\nfunction detectTriple(): 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 if (platform === \"freebsd\") {\n osName = \"unknown-freebsd\";\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, as detect-libc does: the process\n * report names the glibc a glibc Node.js runs on, or the musl objects it\n * loaded; otherwise the system's `ldd` script names its C library.\n */\nfunction detectLinuxLibc(): string {\n const report = processReport();\n if (report?.header?.glibcVersionRuntime) return \"unknown-linux-gnu\";\n if (report?.sharedObjects?.some((object) => /\\/(ld-musl-|libc\\.musl-)/.test(object))) {\n return \"unknown-linux-musl\";\n }\n try {\n if (readFileSync(\"/usr/bin/ldd\", \"utf-8\").includes(\"musl\")) return \"unknown-linux-musl\";\n } catch {\n // No ldd script: glibc is the common case.\n }\n return \"unknown-linux-gnu\";\n}\n\ninterface LibcReport {\n header?: { glibcVersionRuntime?: string };\n sharedObjects?: string[];\n}\n\nfunction processReport(): LibcReport | null {\n // `excludeNetwork` exists from Node 20.13 and 22.0; older versions ignore it.\n const report = process.report as\n | (NodeJS.ProcessReport & { excludeNetwork?: boolean })\n | undefined;\n if (!report) return null;\n // Network details are not needed and slow the report down.\n const excludeNetwork = report.excludeNetwork;\n report.excludeNetwork = true;\n try {\n return report.getReport() as LibcReport;\n } catch {\n return null;\n } finally {\n report.excludeNetwork = excludeNetwork;\n }\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 * Get the filename of the shared service executable for the current platform\n */\nexport function getServiceExecutableFilename(): string {\n return process.platform === \"win32\" ? \"fff-mcp.exe\" : \"fff-mcp\";\n}\n\n/**\n * Map from Rust target triple to npm platform package name.\n * Each @sakiori/fff-bin-* package carries the libfff_c shared library and the\n * fff-mcp service executable of the same build.\n */\nexport const TRIPLE_TO_NPM_PACKAGE: Record<string, string> = {\n \"aarch64-apple-darwin\": \"@sakiori/fff-bin-darwin-arm64\",\n \"x86_64-apple-darwin\": \"@sakiori/fff-bin-darwin-x64\",\n \"x86_64-unknown-linux-gnu\": \"@sakiori/fff-bin-linux-x64-gnu\",\n \"aarch64-unknown-linux-gnu\": \"@sakiori/fff-bin-linux-arm64-gnu\",\n \"x86_64-unknown-linux-musl\": \"@sakiori/fff-bin-linux-x64-musl\",\n \"aarch64-unknown-linux-musl\": \"@sakiori/fff-bin-linux-arm64-musl\",\n};\n\n/**\n * Get the npm package name for the current platform's native binaries.\n *\n * @returns Package name like \"@sakiori/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 * Every instance is a session on the shared fff service. Searches, greps and\n * scan state travel through `fff_service_call`, which takes one request of the\n * service's program protocol as JSON and returns the whole reply as one JSON\n * document, so a result of any size is decoded in a single pass.\n *\n * Calls that reach the service block until it answers or their time limit\n * passes, so they run on the package's worker threads (see `native-pool.ts`)\n * and resolve a Promise. Only calls that need no service run on the calling\n * thread. An instance is identified by its native address, a BigInt, which\n * every thread of the process can pass back to the library.\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 * ffi-rs auto-dereferences struct retType pointers, losing the original pointer.\n * We call the C function with `retType: DataType.External` to get the raw\n * pointer, read the struct with `restorePointer`, then free the raw pointer\n * with `fff_free_result`. `isNullPointer` detects null C pointers.\n */\n\nimport { createRequire } from \"node:module\";\nimport type * as FfiRs from \"ffi-rs\";\nimport type { FieldType, JsExternal } from \"ffi-rs\";\nimport { findNativeFiles, type NativeFiles } from \"./binary.js\";\nimport { ErrorCode, err, nativeErrorCode, type Result } from \"./errors.js\";\nimport { closeNativePool, type NativeReply, type NativeSetup, runNative } from \"./native-pool.js\";\nimport {\n closeWatchDelivery,\n type DeliverySetup,\n forgetWatchCallback,\n registerWatchCallback,\n} from \"./watch-delivery.js\";\n\nconst LIBRARY_KEY = \"fff_c\";\n\nfunction createOptionsStruct() {\n const { DataType } = ffi();\n return {\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\n// ALWAYS KEEP IN SYNC WITH fff.h\nconst FFF_CREATE_OPTIONS_VERSION = 2;\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 */\nfunction resultStruct() {\n const { DataType } = ffi();\n return {\n success: DataType.U8,\n error: DataType.External,\n handle: DataType.External,\n int_value: DataType.I64,\n };\n}\n\ninterface FffResultRaw {\n success: number;\n error: JsExternal;\n handle: JsExternal;\n int_value: number;\n}\n\n/**\n * Opaque native handle: the instance's address. Callers must not inspect or\n * modify this value.\n */\nexport type NativeHandle = bigint;\n\n// The native files of the loaded library, resolved once per process.\nlet loaded: NativeFiles | null = null;\n\n// ffi-rs, required once the platform's native files are found: it refuses to load on a\n// platform it has no binary for, which must be a failed Result rather than a failed import.\nlet ffiModule: typeof FfiRs | null = null;\n\nfunction ffi(): typeof FfiRs {\n if (!ffiModule) throw new Error(\"the native library is not loaded\");\n return ffiModule;\n}\n\n// Native instances created and not yet freed.\nlet liveInstances = 0;\n\n/**\n * The number of native instances this process created and has not freed yet;\n * each holds a session on the service. A destroyed `FileFinder` frees its own.\n */\nexport function nativeInstanceCount(): number {\n return liveInstances;\n}\n\n/**\n * Load the native library, or report why it cannot be loaded.\n */\nfunction loadLibrary(): Result<NativeFiles> {\n if (loaded) return { ok: true, value: loaded };\n const found = findNativeFiles();\n if (!found.ok) return found;\n try {\n ffiModule ??= createRequire(import.meta.url)(\"ffi-rs\") as typeof FfiRs;\n ffiModule.open({ library: LIBRARY_KEY, path: found.value.library });\n } catch (error) {\n return err(\n `Cannot load ${found.value.library}: ${String(error)}`,\n ErrorCode.libraryUnavailable,\n );\n }\n loaded = found.value;\n return found;\n}\n\n/**\n * What a worker thread loads: the same library file as the main thread, and the\n * ffi-rs module this package resolves.\n */\nfunction nativeSetup(): NativeSetup {\n const library = loaded?.library;\n if (!library) throw new Error(\"the native library is not loaded\");\n return {\n ffiPath: createRequire(import.meta.url).resolve(\"ffi-rs\"),\n library,\n createOptions: createOptionsStruct(),\n };\n}\n\n/** Run an operation of the native worker once the library is known to load. */\nfunction runOnWorker<T>(operation: string, args: unknown[]): Promise<NativeReply<T>> {\n const library = loadLibrary();\n if (!library.ok) return Promise.resolve(library);\n return runNative<T>(nativeSetup, operation, args);\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 (ffi().isNullPointer(ptr)) return null;\n try {\n const [str] = ffi().restorePointer({\n retType: [ffi().DataType.String],\n paramsValue: ffi().wrapPointer([ptr]),\n });\n return str as string;\n } catch {\n return null;\n }\n}\n\n/** Free a pointer the library allocated with the given free function. */\nfunction freeWith(funcName: string, ptr: JsExternal): void {\n try {\n ffi().load({\n library: LIBRARY_KEY,\n funcName,\n retType: ffi().DataType.Void,\n paramsType: [ffi().DataType.External],\n paramsValue: [ptr],\n });\n } catch {\n // Ignore cleanup errors\n }\n}\n\n/**\n * Call a function returning FffResult with a C string in handle, on the calling\n * thread, for calls that need no service.\n */\nfunction callStringSync(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<string | null> {\n const library = loadLibrary();\n if (!library.ok) return library;\n const rawPtr = ffi().load({\n library: LIBRARY_KEY,\n funcName,\n retType: ffi().DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n }) as JsExternal;\n const [struct] = ffi().restorePointer({\n retType: [resultStruct()],\n paramsValue: ffi().wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n if (struct.success === 0) {\n const error = readCString(struct.error);\n freeWith(\"fff_free_result\", rawPtr);\n return err(error || \"Unknown error\", nativeErrorCode(Number(struct.int_value)));\n }\n const handle = struct.handle;\n freeWith(\"fff_free_result\", rawPtr);\n if (ffi().isNullPointer(handle)) return { ok: true, value: null };\n const text = readCString(handle);\n freeWith(\"fff_free_string\", handle);\n return { ok: true, value: text };\n}\n\n/**\n * Call a function returning FffResult with its value in `int_value`, on the\n * calling thread, for calls that need no service.\n */\nfunction callIntSync(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<number> {\n const library = loadLibrary();\n if (!library.ok) return library;\n const rawPtr = ffi().load({\n library: LIBRARY_KEY,\n funcName,\n retType: ffi().DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n }) as JsExternal;\n const [struct] = ffi().restorePointer({\n retType: [resultStruct()],\n paramsValue: ffi().wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n const failed = struct.success === 0;\n const error = failed ? readCString(struct.error) : null;\n const value = Number(struct.int_value);\n freeWith(\"fff_free_result\", rawPtr);\n return failed ? err(error || \"Unknown error\", nativeErrorCode(value)) : { ok: true, value };\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) => letter.toUpperCase());\n result[camelKey] = snakeToCamel(value);\n }\n return result;\n}\n\n/**\n * Open a session on the shared service on a worker thread. `timeoutMs` bounds\n * opening it and each later call (0 takes the library's defaults).\n */\nexport async function ffiCreate(\n basePath: string,\n frecencyDbPath: string,\n enableMmapCache: boolean,\n enableContentIndexing: boolean,\n watch: boolean,\n aiMode: boolean,\n logFilePath: string,\n logLevel: string,\n cacheBudgetMaxFiles: number,\n enableFsRootScanning: boolean,\n enableHomeDirScanning: boolean,\n followSymlinks: boolean,\n serviceDir: string,\n timeoutMs: number,\n): Promise<Result<NativeHandle>> {\n const library = loadLibrary();\n if (!library.ok) return library;\n\n const optsValue = {\n version: FFF_CREATE_OPTIONS_VERSION,\n base_path: basePath,\n frecency_db_path: frecencyDbPath,\n history_db_path: \"\",\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: 0,\n cache_budget_max_file_size: 0,\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 created = await runOnWorker<bigint>(\"create\", [\n optsValue,\n library.value.executable,\n serviceDir,\n timeoutMs,\n ]);\n if (!created.ok) return created;\n if (created.value === 0n) {\n return err(\"fff_create_service_instance returned null handle\");\n }\n liveInstances++;\n return { ok: true, value: created.value };\n}\n\n/**\n * End an instance's session and free it on a worker thread. No other call may\n * be in flight on the instance.\n */\nexport async function ffiDestroy(handle: NativeHandle): Promise<void> {\n const destroyed = await runOnWorker(\"destroy\", [handle]);\n if (destroyed.ok) {\n liveInstances--;\n forgetWatchCallback(handle);\n }\n}\n\n/**\n * Send one request of the service's program protocol on a worker thread and\n * decode the reply.\n */\nexport async function ffiServiceCall<T>(\n handle: NativeHandle,\n request: unknown,\n): Promise<Result<T>> {\n const reply = await runOnWorker<string | null>(\"call\", [handle, JSON.stringify(request)]);\n if (!reply.ok) return reply;\n if (reply.value === null) return err(\"fff_service_call returned no reply\");\n try {\n return { ok: true, value: JSON.parse(reply.value) as T };\n } catch (error) {\n return err(`Cannot decode the service reply: ${String(error)}`);\n }\n}\n\n/**\n * Point the instance's watch callback at the delivery worker.\n */\nexport function ffiRegisterWatchCallback(handle: NativeHandle): Promise<Result<void>> {\n const library = loadLibrary();\n if (!library.ok) return Promise.resolve(library);\n return registerWatchCallback(deliverySetup, handle);\n}\n\n/**\n * Start a subscription to the changes under an instance's directory, on the\n * calling thread: the library returns its watch id at once and reports\n * readiness through the callback. No call on the instance may free it meanwhile.\n */\nexport function ffiWatchStart(\n handle: NativeHandle,\n pattern: string,\n ignore: string[],\n repository: boolean,\n refs: boolean,\n): Result<number> {\n const { DataType } = ffi();\n return callIntSync(\n \"fff_watch_args_ex2\",\n [\n DataType.BigInt,\n DataType.String,\n DataType.StringArray,\n DataType.U32,\n DataType.Boolean,\n DataType.Boolean,\n ],\n [handle, pattern, ignore, ignore.length, repository, refs],\n );\n}\n\n/**\n * End a subscription on the calling thread; the library stops delivering to it first.\n *\n * The library joins the subscription's thread before it returns, which cannot hold up\n * the event loop for long: that thread waits only where the end of the subscription\n * interrupts it (reading the service, backing off, reconnecting), and its delivery\n * into the callback queues a batch for the delivery worker without waiting for it.\n */\nexport function ffiUnwatch(handle: NativeHandle, watchId: number): Result<number> {\n const { DataType } = ffi();\n return callIntSync(\"fff_unwatch\", [DataType.BigInt, DataType.U64], [handle, watchId]);\n}\n\nfunction deliverySetup(): DeliverySetup {\n const { ffiPath, library } = nativeSetup();\n return { ffiPath, library };\n}\n\n/**\n * Get the base path of the file picker; the instance knows it without the service.\n */\nexport function ffiGetBasePath(handle: NativeHandle): Result<string | null> {\n const library = loadLibrary();\n if (!library.ok) return library;\n return callStringSync(\"fff_get_base_path\", [ffi().DataType.BigInt], [handle]);\n}\n\n/**\n * Restart the index in `newPath`, which for a service instance rescans its own\n * directory, on a worker thread.\n */\nexport async function ffiRestartIndex(\n handle: NativeHandle,\n newPath: string,\n): Promise<Result<void>> {\n const done = await runOnWorker<null>(\"restart\", [handle, newPath]);\n return done.ok ? { ok: true, value: undefined } : done;\n}\n\nfunction healthResult(json: Result<string | null>): Result<unknown> {\n if (!json.ok) return json;\n if (json.value === null || json.value === \"\") return { ok: true, value: undefined };\n try {\n return { ok: true, value: snakeToCamel(JSON.parse(json.value)) };\n } catch {\n return { ok: true, value: json.value };\n }\n}\n\n/**\n * Health check of an instance, which asks its service, on a worker thread.\n */\nexport async function ffiHealthCheck(\n handle: NativeHandle,\n testPath: string,\n): Promise<Result<unknown>> {\n const json = await runOnWorker<string | null>(\"health\", [handle, testPath]);\n return json.ok ? healthResult(json) : json;\n}\n\n/**\n * Health check without an instance (version and git only), on the calling\n * thread. A zero address stands in for the null pointer.\n */\nexport function ffiHealthCheckStatic(testPath: string): Result<unknown> {\n const library = loadLibrary();\n if (!library.ok) return library;\n const { DataType } = ffi();\n return healthResult(\n callStringSync(\"fff_health_check\", [DataType.BigInt, DataType.String], [0n, 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 const library = loadLibrary();\n if (!library.ok) throw new Error(library.error);\n}\n\n/**\n * Check if the library is available.\n */\nexport function isAvailable(): boolean {\n return loadLibrary().ok;\n}\n\n/**\n * Close the library and stop the worker threads that call it and deliver watch\n * events. Call this when completely done with the library; instances must have\n * been destroyed.\n */\nexport async function closeLibrary(): Promise<void> {\n await closeWatchDelivery();\n await closeNativePool();\n if (loaded) {\n ffi().close(LIBRARY_KEY);\n loaded = null;\n }\n}\n","/**\n * The worker threads that run the package's blocking native calls.\n *\n * Calls that reach the shared service block their thread until the service\n * answers or the call's time limit passes. They run here, on a few worker\n * threads the package owns, rather than on the event loop or on the libuv\n * thread pool, whose threads the file system, DNS, crypto and zlib APIs of the\n * whole process share. Workers start when a call needs one, and hold the\n * process open only while they run a call; one that dies fails its call and is\n * replaced by the next call that needs a worker.\n */\n\nimport { MessageChannel, type MessagePort, Worker } from \"node:worker_threads\";\nimport { ErrorCode, type Failure, nativeErrorCode } from \"./errors.js\";\nimport { NATIVE_WORKER_SOURCE } from \"./native-worker.js\";\n\n/**\n * The most worker threads the package runs. Each instance has at most one\n * call in flight, so this many instances can reach the service at once; an\n * unresponsive service holds at most this many threads until its calls' time\n * limits pass, and nothing outside the package waits for them.\n */\nexport const NATIVE_POOL_SIZE = 4;\n\n/** The reply of a native call. */\nexport type NativeReply<T> = { ok: true; value: T } | Failure;\n\n// A worker's reply, with a failure's `FFF_ERROR_*` kind as `nativeCode`.\ntype WorkerReply = { ok: true; value: unknown } | { ok: false; error: string; nativeCode: number };\n\n/** What every worker needs to load the native library. */\nexport interface NativeSetup {\n ffiPath: string;\n library: string;\n createOptions: unknown;\n}\n\ninterface Job {\n operation: string;\n args: unknown[];\n settle: (reply: NativeReply<unknown>) => void;\n}\n\ninterface Slot {\n worker: Worker;\n // Calls use a channel of their own: Node posts its own messages to a worker's\n // parent port, such as the modules it requires under `--watch`.\n port: MessagePort;\n job: Job | null;\n}\n\nclass NativePool {\n private readonly slots: Slot[] = [];\n private readonly waiting: Job[] = [];\n private nextId = 0;\n\n constructor(private readonly setup: () => NativeSetup) {}\n\n run<T>(operation: string, args: unknown[]): Promise<NativeReply<T>> {\n return new Promise((resolve) => {\n this.waiting.push({\n operation,\n args,\n settle: resolve as (reply: NativeReply<unknown>) => void,\n });\n this.dispatch();\n });\n }\n\n /** Stops every worker; calls in flight fail, and later calls start new workers. */\n async close(): Promise<void> {\n for (const job of this.waiting.splice(0)) {\n job.settle({ ok: false, error: \"fff native workers were closed\", code: ErrorCode.failed });\n }\n await Promise.all(this.slots.map((slot) => slot.worker.terminate()));\n }\n\n private dispatch(): void {\n while (this.waiting.length > 0) {\n const slot = this.slots.find((candidate) => candidate.job === null) ?? this.start();\n if (!slot) return;\n const job = this.waiting.shift() as Job;\n slot.job = job;\n slot.worker.ref();\n slot.port.ref();\n slot.port.postMessage({ id: this.nextId++, operation: job.operation, args: job.args });\n }\n }\n\n private start(): Slot | null {\n if (this.slots.length >= NATIVE_POOL_SIZE) return null;\n const { port1: port, port2: workerPort } = new MessageChannel();\n const worker = new Worker(NATIVE_WORKER_SOURCE, {\n eval: true,\n // The host's flags, as --input-type=module, would change how the source is read.\n execArgv: [],\n workerData: { ...this.setup(), port: workerPort },\n transferList: [workerPort],\n });\n worker.unref();\n port.unref();\n const slot: Slot = { worker, port, job: null };\n port.on(\"message\", ({ reply }: { reply: WorkerReply }) => {\n const job = slot.job;\n slot.job = null;\n worker.unref();\n port.unref();\n job?.settle(\n reply.ok\n ? reply\n : { ok: false, error: reply.error, code: nativeErrorCode(reply.nativeCode) },\n );\n this.dispatch();\n });\n let failure = \"\";\n worker.on(\"error\", (error) => {\n failure = String(error);\n });\n worker.on(\"exit\", (code) => {\n port.close();\n this.slots.splice(this.slots.indexOf(slot), 1);\n const job = slot.job;\n slot.job = null;\n job?.settle({\n ok: false,\n error: `fff native worker exited (${failure || `code ${code}`})`,\n code: ErrorCode.failed,\n });\n this.dispatch();\n });\n this.slots.push(slot);\n return slot;\n }\n}\n\nlet pool: NativePool | null = null;\n\n/** Run one native operation on a worker thread. */\nexport function runNative<T>(\n setup: () => NativeSetup,\n operation: string,\n args: unknown[],\n): Promise<NativeReply<T>> {\n pool ??= new NativePool(setup);\n return pool.run<T>(operation, args);\n}\n\n/** Stop the worker threads; the next native call starts new ones. */\nexport async function closeNativePool(): Promise<void> {\n const closing = pool;\n pool = null;\n await closing?.close();\n}\n","/**\n * Source of the worker threads that make the blocking calls into libfff_c.\n *\n * It runs as an `eval` worker, a CommonJS script, so it needs no file of its\n * own next to the bundle. The main thread passes the paths it resolved, the\n * ffi-rs entry and the native library (an `.asar.unpacked` path inside\n * Electron archives), and the layout of the create options in `workerData`.\n * Calls arrive on the private port in `workerData`, never on the parent port,\n * which Node also uses for messages of its own. Each message is one call; the\n * reply carries plain values only: the instance handle as a BigInt address, and\n * a failure's `FFF_ERROR_*` kind as `nativeCode`.\n */\nexport const NATIVE_WORKER_SOURCE = String.raw`\n\"use strict\";\nconst { workerData } = require(\"node:worker_threads\");\nconst port = workerData.port;\nconst ffi = require(workerData.ffiPath);\nconst { DataType } = ffi;\nconst LIBRARY = \"fff_c\";\nffi.open({ library: LIBRARY, path: workerData.library });\n\nconst RESULT = {\n success: DataType.U8,\n error: DataType.External,\n handle: DataType.External,\n int_value: DataType.I64,\n};\nconst HANDLE_RESULT = { ...RESULT, handle: DataType.BigInt };\nconst CREATE_OPTIONS = workerData.createOptions;\n\nfunction call(funcName, paramsType, paramsValue) {\n return ffi.load({\n library: LIBRARY,\n funcName,\n retType: DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n });\n}\n\nfunction free(funcName, pointer) {\n ffi.load({\n library: LIBRARY,\n funcName,\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [pointer],\n });\n}\n\nfunction cString(pointer) {\n if (ffi.isNullPointer(pointer)) return null;\n const [text] = ffi.restorePointer({\n retType: [DataType.String],\n paramsValue: ffi.wrapPointer([pointer]),\n });\n return text;\n}\n\n// Reads an FffResult, frees it and returns a reply; payload reads its handle field.\nfunction envelope(pointer, layout, payload) {\n const [result] = ffi.restorePointer({ retType: [layout], paramsValue: ffi.wrapPointer([pointer]) });\n if (result.success === 0) {\n const error = cString(result.error) || \"Unknown error\";\n free(\"fff_free_result\", pointer);\n return { ok: false, error, nativeCode: Number(result.int_value) };\n }\n const value = payload(result.handle);\n free(\"fff_free_result\", pointer);\n return { ok: true, value };\n}\n\nfunction stringPayload(handle) {\n if (ffi.isNullPointer(handle)) return null;\n const text = cString(handle);\n free(\"fff_free_string\", handle);\n return text;\n}\n\nconst operations = {\n create(options, executable, serviceDir, timeoutMs) {\n const pointer = call(\n \"fff_create_service_instance\",\n [CREATE_OPTIONS, DataType.String, DataType.String, DataType.U64],\n [options, executable, serviceDir, timeoutMs],\n );\n return envelope(pointer, HANDLE_RESULT, (handle) => handle);\n },\n call(handle, request) {\n const pointer = call(\"fff_service_call\", [DataType.BigInt, DataType.String], [handle, request]);\n return envelope(pointer, RESULT, stringPayload);\n },\n restart(handle, path) {\n const pointer = call(\"fff_restart_index\", [DataType.BigInt, DataType.String], [handle, path]);\n return envelope(pointer, RESULT, () => null);\n },\n health(handle, testPath) {\n const pointer = call(\"fff_health_check\", [DataType.BigInt, DataType.String], [handle, testPath]);\n return envelope(pointer, RESULT, stringPayload);\n },\n destroy(handle) {\n ffi.load({\n library: LIBRARY,\n funcName: \"fff_destroy\",\n retType: DataType.Void,\n paramsType: [DataType.BigInt],\n paramsValue: [handle],\n });\n return { ok: true, value: null };\n },\n};\n\nport.on(\"message\", ({ id, operation, args }) => {\n let reply;\n try {\n reply = operations[operation](...args);\n } catch (error) {\n reply = { ok: false, error: \"fff native call failed: \" + String(error), nativeCode: 0 };\n }\n port.postMessage({ id, reply });\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 /** Bonus for query segments aligning with path segments */\n pathAlignmentBonus: 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 *\n * renamed = the file was moved to the other path without changing the content\n */\nexport type WatchEventKind = \"created\" | \"modified\" | \"removed\" | \"rescan\" | \"renamed\";\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 /** Absolute path the file moved from. Only set when `kind == \"renamed\"`. */\n from?: string;\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\nexport type DefinitionMode = \"off\" | \"classify\" | \"definition_only\" | \"usage_only\";\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 /**\n * Apply `timeBudgetMs` even before anything matched. Off by default, so a\n * zero-match query scans every candidate file. (default: false)\n */\n enforceTimeBudget?: boolean;\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 /** Select matching definitions/usages, or classify all matches. Defaults to \"off\".\n * Legacy booleans map false to \"off\" and true to \"classify\". Keep the same mode with a cursor. */\n classifyDefinitions?: DefinitionMode | 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 the query matches a declared name. Available when classification is enabled. */\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 /**\n * Apply `timeBudgetMs` even before anything matched. Off by default, so a\n * zero-match query scans every candidate file. (default: false)\n */\n enforceTimeBudget?: boolean;\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 /** Select matching definitions/usages, or classify all matches. Defaults to \"off\".\n * Legacy booleans map false to \"off\" and true to \"classify\". Keep the same mode with a cursor. */\n classifyDefinitions?: DefinitionMode | 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 over a 50-ms window and submitted in batches of 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/** @internal Convert the public mode to the C integer argument. */\nexport function definitionModeToInt(\n mode: DefinitionMode | boolean | undefined,\n): number | undefined {\n switch (mode) {\n case undefined:\n case false:\n case \"off\":\n return 0;\n case true:\n case \"classify\":\n return 1;\n case \"definition_only\":\n return 2;\n case \"usage_only\":\n return 3;\n default:\n return undefined;\n }\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 is a session on the shared fff service of the package's\n * release, which owns the index; instances of any process searching the same\n * directory share one index with each other and with `fff-mcp` agents.\n *\n * Every method that reaches the service returns a Promise of a Result and runs\n * its native call on one of the package's worker threads, so a slow or\n * unresponsive service blocks neither the event loop nor the libuv thread pool.\n * Methods that need no service stay synchronous.\n */\n\nimport {\n ensureLoaded,\n ffiCreate,\n ffiDestroy,\n ffiGetBasePath,\n ffiHealthCheck,\n ffiHealthCheckStatic,\n ffiRestartIndex,\n ffiServiceCall,\n ffiRegisterWatchCallback,\n ffiUnwatch,\n ffiWatchStart,\n isAvailable,\n type NativeHandle,\n} from \"./ffi.js\";\n\nimport type {\n DirSearchOptions,\n DirSearchResult,\n GlobOptions,\n GrepCursor,\n GrepOptions,\n GrepResult,\n HealthCheck,\n MixedSearchResult,\n MultiGrepOptions,\n ScanProgress,\n SearchOptions,\n SearchResult,\n WatchUnsubscribe,\n} from \"./fff-api.js\";\nimport { definitionModeToInt } from \"./fff-api.js\";\n\nimport { ErrorCode, err, type Result } from \"./errors.js\";\nimport type {\n FrecencyOptions,\n ServiceInitOptions,\n ServiceWatchBatchCallback,\n ServiceWatchOptions,\n} from \"./service-options.js\";\nimport { deleteWatchListener, setWatchListener } from \"./watch-delivery.js\";\n\n/** Interval of the readiness polls of `waitForScan` and `waitForIndexReady`. */\nconst READY_POLL_MS = 50;\n\n/** The error of a method the shared service has no equivalent for. */\nfunction unavailable<T>(method: string): Result<T> {\n return err(`FileFinder.${method} is unavailable over the shared service`, ErrorCode.unavailable);\n}\n\n/** Where a grep continues: a file offset into the order of the index that returned it. */\ninterface ServiceCursor {\n index: string;\n offset: number;\n}\n\n/** The opaque cursor of the shared API, carrying the index it belongs to. */\ninterface IndexedGrepCursor extends GrepCursor {\n readonly _index: string;\n}\n\n/** A grep reply before its cursor is wrapped. */\ntype GrepReply = Omit<GrepResult, \"nextCursor\"> & { nextCursor: ServiceCursor | null };\n\nconst GREP_MODES = { plain: \"PlainText\", regex: \"Regex\", fuzzy: \"Fuzzy\" } as const;\n\n/** The service's definition modes, indexed by `definitionModeToInt`. */\nconst DEFINITION_MODES = [\"Off\", \"Classify\", \"DefinitionOnly\", \"UsageOnly\"] as const;\n\nfunction serviceCursor(cursor: GrepCursor | null | undefined): Result<ServiceCursor | null> {\n if (!cursor) return { ok: true, value: null };\n const index = (cursor as Partial<IndexedGrepCursor>)._index;\n if (index === undefined) {\n return err(\n \"The grep cursor was not returned by a FileFinder grep; search again without it\",\n ErrorCode.invalidRequest,\n );\n }\n return { ok: true, value: { index, offset: cursor._offset } };\n}\n\nfunction grepResult(reply: Result<GrepReply>): Result<GrepResult> {\n if (!reply.ok) return reply;\n const next = reply.value.nextCursor;\n const nextCursor: IndexedGrepCursor | null = next\n ? { __brand: \"GrepCursor\", _offset: next.offset, _index: next.index }\n : null;\n return { ok: true, value: { ...reply.value, nextCursor } };\n}\n\nfunction grepOptions(options: GrepOptions & FrecencyOptions, cursor: ServiceCursor | null) {\n const definitionMode = definitionModeToInt(options.classifyDefinitions);\n if (definitionMode === undefined) {\n return err(\"Invalid classifyDefinitions mode\", ErrorCode.invalidRequest);\n }\n const value = {\n max_file_size: options.maxFileSize || 10 * 1024 * 1024,\n max_matches_per_file: options.maxMatchesPerFile ?? 0,\n smart_case: options.smartCase ?? true,\n cursor,\n page_size: options.pageSize || 50,\n time_budget_ms: options.timeBudgetMs ?? 0,\n enforce_time_budget: options.enforceTimeBudget ?? false,\n before_context: options.beforeContext ?? 0,\n after_context: options.afterContext ?? 0,\n classify_definitions: DEFINITION_MODES[definitionMode],\n exclude_frecency: options.excludeFrecency ?? false,\n };\n return { ok: true as const, value };\n}\n\n/**\n * The native calls of one instance, one at a time.\n *\n * The service session answers one call at a time anyway; queuing here keeps an\n * instance to one worker thread, so other instances keep the rest. When a call\n * finds the service lost (it did not answer within its bound, or the\n * connection broke), the calls queued behind it fail with the same error\n * rather than each waiting out that bound again; the next call opens a new\n * session. A call run with `always` runs regardless, as freeing the instance\n * must.\n */\nclass CallQueue {\n private tail: Promise<unknown> = Promise.resolve();\n private lost = { epoch: 0, error: \"\" };\n\n run<T>(\n call: () => Promise<Result<T>>,\n always = false,\n ): Promise<Result<T>> {\n const epoch = this.lost.epoch;\n const result = this.tail.then(async (): Promise<Result<T>> => {\n if (!always && this.lost.epoch !== epoch) return err(this.lost.error, ErrorCode.lostService);\n let outcome: Result<T>;\n try {\n outcome = await call();\n } catch (error) {\n return err(`fff native call failed: ${String(error)}`);\n }\n if (outcome.ok) return outcome;\n if (outcome.code === ErrorCode.lostService) {\n this.lost = { epoch: this.lost.epoch + 1, error: outcome.error };\n }\n return outcome;\n });\n this.tail = result.catch(() => undefined);\n return result;\n }\n}\n\nfunction page(options?: SearchOptions & FrecencyOptions) {\n return {\n current_file: options?.currentFile || null,\n offset: options?.pageIndex ?? 0,\n limit: options?.pageSize || 100,\n exclude_frecency: options?.excludeFrecency ?? false,\n };\n}\n\n/**\n * FileFinder - Fast file finder with fuzzy search\n *\n * Each instance is a session on the shared fff service. Create as many as you\n * need and destroy them when done. Calls on one instance may overlap; they\n * reach the service one at a time, in call order.\n *\n * @example\n *\n * ```ts\n * import { FileFinder } from \"@sakiori/fff-node\";\n *\n * // Create an instance\n * const finder = await 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 = await finder.value.fileSearch(\"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 * await finder.value.destroy();\n * ```\n */\nexport class FileFinder {\n private handle: NativeHandle | null;\n private readonly calls = new CallQueue();\n private readonly watching: boolean;\n /** The instance's subscriptions by native id, with how to fail one still pending. */\n private readonly watches = new Map<number, (failure: Result<WatchUnsubscribe>) => void>();\n /** Registrations of the watch callback in flight, which `destroy` lets finish. */\n private readonly registrations = new Set<Promise<unknown>>();\n\n private constructor(handle: NativeHandle, watching: boolean) {\n this.handle = handle;\n this.watching = watching;\n }\n\n /**\n * Create a new file finder instance.\n *\n * Opens a session on the shared fff service, starting the service from the\n * platform package's `fff-mcp` when none of this release runs. When the\n * service cannot be started or reached within `timeoutMs` (90 seconds by\n * default), the result carries its reason.\n *\n * @param options - Initialization options\n * @returns Result containing the new FileFinder instance or an error\n *\n * @example\n * ```typescript\n * const finder = await FileFinder.create({ basePath: \"/path/to/project\" });\n * ```\n */\n static async create(options: ServiceInitOptions): Promise<Result<FileFinder>> {\n if (options.historyDbPath) {\n return err(\"historyDbPath is unavailable over the shared service\", ErrorCode.unavailable);\n }\n const timeoutMs = options.timeoutMs ?? 0;\n if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0) {\n return err(\n \"timeoutMs must be a non-negative integer number of milliseconds\",\n ErrorCode.invalidRequest,\n );\n }\n let result: Result<NativeHandle>;\n try {\n result = await ffiCreate(\n options.basePath,\n options.frecencyDbPath ?? \"\",\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.enableFsRootScanning ?? false,\n options.enableHomeDirScanning ?? false,\n options.followSymlinks ?? false,\n options.serviceDir ?? \"\",\n timeoutMs,\n );\n } catch (error) {\n return err(`fff native call failed: ${String(error)}`);\n }\n\n if (!result.ok) {\n return result;\n }\n\n return { ok: true, value: new FileFinder(result.value, !(options.disableWatch ?? false)) };\n }\n\n /**\n * Destroy the instance and end its service session.\n *\n * Calls made after this report the instance as destroyed; calls made before\n * it finish first. Its watch subscriptions end, and their callbacks are not\n * called once this is called. The service releases the index once no\n * session uses it. An instance that is never destroyed keeps its session\n * until the process exits.\n */\n async destroy(): Promise<void> {\n const handle = this.handle;\n if (handle === null) return;\n this.handle = null;\n for (const [watchId, settle] of this.watches) {\n deleteWatchListener(watchId);\n settle(err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed));\n }\n this.watches.clear();\n await Promise.allSettled([...this.registrations]);\n await this.calls.run(async () => {\n await ffiDestroy(handle);\n return { ok: true, value: undefined };\n }, true);\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 * Run a native call of this instance, or report it destroyed.\n */\n private call<T>(\n native: (handle: NativeHandle) => Promise<Result<T>>,\n ): Promise<Result<T>> {\n const handle = this.handle;\n if (handle === null) {\n return Promise.resolve(err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed));\n }\n return this.calls.run(() => native(handle));\n }\n\n /**\n * Send one request of the service's program protocol.\n */\n private request<T>(request: unknown): Promise<Result<T>> {\n return this.call((handle) => ffiServiceCall<T>(handle, request));\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 = await finder.fileSearch(\"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(\n query: string,\n options?: SearchOptions & FrecencyOptions,\n ): Promise<Result<SearchResult>> {\n return this.request({ SearchFiles: { query, ...page(options) } });\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 = await 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 & FrecencyOptions): Promise<Result<SearchResult>> {\n return this.request({ Glob: { pattern, ...page(options) } });\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 = await 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(\n query: string,\n options?: DirSearchOptions & FrecencyOptions,\n ): Promise<Result<DirSearchResult>> {\n return this.request({ SearchDirectories: { query, ...page(options) } });\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 = await 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(\n query: string,\n options?: SearchOptions & FrecencyOptions,\n ): Promise<Result<MixedSearchResult>> {\n return this.request({ SearchMixed: { query, ...page(options) } });\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 * A grep may take its `timeBudgetMs` beyond the instance's `timeoutMs`\n * before it fails as unanswered.\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 = await 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 = await finder.grep(\"TODO\", {\n * cursor: result.value.nextCursor,\n * });\n * }\n * }\n * ```\n */\n async grep(query: string, options?: GrepOptions & FrecencyOptions): Promise<Result<GrepResult>> {\n const cursor = serviceCursor(options?.cursor);\n if (!cursor.ok) return cursor;\n const serviceOptions = grepOptions(options ?? {}, cursor.value);\n if (!serviceOptions.ok) return serviceOptions;\n const request = {\n query,\n mode: GREP_MODES[options?.mode ?? \"plain\"] ?? GREP_MODES.plain,\n ai_syntax: false,\n options: serviceOptions.value,\n };\n return grepResult(await this.request<GrepReply>({ Grep: request }));\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 = await 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 async multiGrep(options: MultiGrepOptions & FrecencyOptions): Promise<Result<GrepResult>> {\n if (!options.patterns || options.patterns.length === 0) {\n return err(\"patterns array must have at least 1 element\", ErrorCode.invalidRequest);\n }\n\n const cursor = serviceCursor(options.cursor);\n if (!cursor.ok) return cursor;\n const serviceOptions = grepOptions(options, cursor.value);\n if (!serviceOptions.ok) return serviceOptions;\n const request = {\n patterns: options.patterns,\n constraints: options.constraints || null,\n options: serviceOptions.value,\n };\n return grepResult(await this.request<GrepReply>({ MultiGrep: request }));\n }\n\n /**\n * Trigger a rescan of the shared index, coalesced with one already running.\n *\n * This is useful after major file system changes that the\n * background watcher might have missed.\n */\n async scanFiles(): Promise<Result<void>> {\n const done = await this.request<null>(\"Rescan\");\n return done.ok ? { ok: true, value: undefined } : done;\n }\n\n /**\n * Apply changes the program just made to files of the instance's directory, so\n * that a search after the returned Promise resolves sees them, without waiting\n * for the service's watcher or a rescan.\n *\n * Each path, absolute or relative to the instance's directory, is added or\n * updated when it exists and the index does not ignore it, and removed, with\n * any indexed files below it, when it does not exist; the content index\n * follows. A symlink is not added unless the index follows symlinks, as its\n * scan would not add it either. Report files rather than new directories: an index without a\n * watcher does not walk a reported directory. Changes the watcher also\n * delivers are applied once more, harmlessly. While the index scans, the\n * changes are applied when the scan commits.\n *\n * @param paths - Files the program wrote, created, renamed or deleted\n * @returns Nothing once the index holds the changes; `outsideScope` when a\n * path lies outside the instance's directory, which applies none of them\n *\n * @example\n * ```typescript\n * await writeFile(join(root, \"src/new.ts\"), source);\n * await finder.notifyChanged([\"src/new.ts\"]);\n * const found = await finder.fileSearch(\"new.ts\"); // includes src/new.ts\n * ```\n */\n async notifyChanged(paths: string[]): Promise<Result<void>> {\n const done = await this.request<null>({ Changed: { paths } });\n return done.ok ? { ok: true, value: undefined } : done;\n }\n\n /**\n * Check if a scan is currently in progress, or a requested rescan waits for one.\n * An unreachable service reports false; `waitForScan` and `getScanProgress`\n * report its error.\n */\n async isScanning(): Promise<boolean> {\n const scanning = await this.scanning();\n return scanning.ok && scanning.value;\n }\n\n private scanning(): Promise<Result<boolean>> {\n return this.request<boolean>(\"IsScanning\");\n }\n\n /**\n * Get the base path of the file picker (the root directory being indexed).\n */\n getBasePath(): Result<string | null> {\n if (this.handle === null) {\n return err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed);\n }\n return ffiGetBasePath(this.handle);\n }\n\n /**\n * Get the current scan progress.\n *\n * Once the index is ready, `scannedFilesCount` counts the files of the\n * instance's directory. While the shared index scans, it counts the files\n * scanned so far, which may include files outside that directory when the\n * index covers a broader one.\n */\n getScanProgress(): Promise<Result<ScanProgress>> {\n return this.request<ScanProgress>(\"Progress\");\n }\n\n /**\n * Wait for the initial file scan to complete, polling `isScanning`.\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 completed = await finder.waitForScan(10000);\n * if (!completed.ok || !completed.value) {\n * console.warn(\"Scan did not complete in time\");\n * }\n * ```\n */\n async waitForScan(timeoutMs: number = 5000): Promise<Result<boolean>> {\n const deadline = Date.now() + timeoutMs;\n while (true) {\n const scanning = await this.scanning();\n if (!scanning.ok) return scanning;\n if (!scanning.value) return { ok: true, value: true };\n if (Date.now() >= deadline) return { ok: true, value: false };\n await new Promise((resolve) => setTimeout(resolve, READY_POLL_MS));\n }\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. Polls `getScanProgress`.\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 deadline = Date.now() + timeoutMs;\n while (true) {\n const progress = await 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, READY_POLL_MS));\n }\n }\n\n /**\n * Rescan the indexed directory.\n *\n * A session stays on its directory, so another path fails; create a new\n * instance for it.\n *\n * @param newPath - The instance's directory\n */\n reindex(newPath: string): Promise<Result<void>> {\n return this.call((handle) => ffiRestartIndex(handle, newPath));\n }\n\n /**\n * Unavailable: the shared service keeps git status current itself.\n */\n refreshGitStatus(): Result<number> {\n return unavailable(\"refreshGitStatus\");\n }\n\n /**\n * Unavailable: query history has no shared service equivalent.\n */\n trackQuery(_query: string, _selectedFilePath: string): Result<boolean> {\n return unavailable(\"trackQuery\");\n }\n\n /**\n * Unavailable: query history has no shared service equivalent.\n */\n getHistoricalQuery(_offset: number): Result<string | null> {\n return unavailable(\"getHistoricalQuery\");\n }\n\n /**\n * Subscribe to changes of the files under the instance's directory, as the\n * shared service's watcher sees them.\n *\n * `pattern` may be a glob relative to the instance's directory (`./` works),\n * an exact path, or a directory, relative or absolute inside the instance's\n * directory; without one, the subscription covers the whole directory.\n * Ignored files never trigger an event. The callback receives batches on the\n * event loop, with absolute paths. When changes are lost, because the\n * callback runs slower than they come, the index was rescanned, the session\n * moved to another index or the connection to the service broke, one\n * `rescan` event naming the instance's directory replaces them; a broken\n * connection reconnects by itself, starting the service when none runs. A\n * callback falls behind once more than 1024 of its events wait on the event\n * loop. A `failed` event ends a subscription the service can no longer\n * serve, as after its directory was removed, or that a reconnect was refused.\n * Once ready, neither the subscription nor its delivery keeps the process\n * alive, and subscribing holds none of the package's native workers.\n *\n * @returns Once every later change is delivered, the function that ends the\n * subscription: once it returns, the callback is not called again.\n * `unavailable` when the instance was created with `disableWatch`, and the\n * service's reason when it refuses the subscription.\n *\n * @example\n * ```typescript\n * const watched = await finder.watch(\"src/**\", (events) => {\n * for (const event of events) console.log(event.kind, event.path);\n * }, { repository: true });\n * if (watched.ok) watched.value(); // unsubscribe\n * ```\n */\n watch(\n callback: ServiceWatchBatchCallback,\n options?: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>>;\n watch(\n pattern: string,\n callback: ServiceWatchBatchCallback,\n options?: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>>;\n watch(\n first: string | ServiceWatchBatchCallback,\n second?: ServiceWatchBatchCallback | ServiceWatchOptions,\n third?: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>> {\n const [pattern, callback, options] =\n typeof first === \"string\"\n ? [first, second as ServiceWatchBatchCallback, third]\n : [\"\", first, second as ServiceWatchOptions | undefined];\n if (typeof callback !== \"function\") {\n return Promise.resolve(\n err(\"watch needs a callback for the batches\", ErrorCode.invalidRequest),\n );\n }\n const handle = this.handle;\n if (handle === null) {\n return Promise.resolve(err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed));\n }\n if (!this.watching) {\n return Promise.resolve(\n err(\n \"FileFinder.watch is unavailable on an instance created with disableWatch\",\n ErrorCode.unavailable,\n ),\n );\n }\n return this.subscribe(handle, pattern, callback, options ?? {});\n }\n\n private async subscribe(\n handle: NativeHandle,\n pattern: string,\n callback: ServiceWatchBatchCallback,\n options: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>> {\n const registration = ffiRegisterWatchCallback(handle);\n this.registrations.add(registration);\n const registered = await registration;\n this.registrations.delete(registration);\n if (!registered.ok) return registered;\n if (this.handle !== handle) {\n return err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed);\n }\n // The library returns at once and reports readiness through the callback, so no\n // native worker waits for the index.\n const started = ffiWatchStart(\n handle,\n pattern,\n options.ignore ?? [],\n options.repository ?? false,\n options.refs ?? false,\n );\n if (!started.ok) return started;\n const watchId = started.value;\n const base = ffiGetBasePath(handle);\n const directory = base.ok && base.value ? base.value : \"\";\n return new Promise((resolve) => {\n let ready = false;\n const end = () => {\n this.watches.delete(watchId);\n deleteWatchListener(watchId);\n if (this.handle === handle) ffiUnwatch(handle, watchId);\n };\n const unsubscribe: WatchUnsubscribe = () => {\n if (this.watches.has(watchId)) end();\n };\n this.watches.set(watchId, resolve);\n setWatchListener(watchId, {\n handle,\n ready: () => {\n ready = true;\n resolve({ ok: true, value: unsubscribe });\n },\n events: (events) => {\n if (ready) callback(events);\n },\n lost: () => {\n if (ready) return callback([{ path: directory, kind: \"rescan\" }]);\n end();\n resolve(err(\"The watch delivery restarted before the subscription was ready\"));\n },\n failed: (error, code) => {\n end();\n if (ready) callback([{ path: directory, kind: \"failed\", error, code }]);\n else resolve(err(error, code));\n },\n });\n });\n }\n\n /**\n * Get health check information, including the instance's index and git\n * state as its service reports them.\n *\n * @param testPath - Optional path to test git repository detection\n */\n healthCheck(testPath?: string): Promise<Result<HealthCheck>> {\n return this.call(\n (handle) =>\n ffiHealthCheck(handle, testPath || \"\") as Promise<Result<HealthCheck>>,\n );\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 an instance: the library version and the git\n * repository of `testPath`, without asking a service.\n *\n * @param testPath - Optional path to test git repository detection\n */\n static healthCheckStatic(testPath?: string): Result<HealthCheck> {\n return ffiHealthCheckStatic(testPath || \"\") as Result<HealthCheck>;\n }\n}\n"],"mappings":"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","names":["readFileSync","triple","readFileSync","require","triple","createRequire","createRequire"]}
1
+ {"version":3,"sources":["../src/binary.ts","../src/platform.ts","../src/ffi.ts","../src/native-pool.ts","../src/native-worker.ts","../src/fff-api.ts","../src/finder.ts"],"sourcesContent":["/**\n * Native file resolution for fff-node\n *\n * The native library and the fff-mcp service executable must come from one\n * build, so both are resolved from the same location:\n * 1. The FFF_C_LIB environment variable (explicit path to libfff_c.so/.dylib/.dll,\n * with fff-mcp beside it)\n * 2. Platform-specific npm package (e.g. @sakiori/fff-bin-darwin-arm64)\n * 3. Local dev build (bin/, then target/release or target/debug)\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ErrorCode, err, type Result } from \"./errors.js\";\nimport {\n getLibFilename,\n getServiceExecutableFilename,\n getTriple,\n TRIPLE_TO_NPM_PACKAGE,\n} from \"./platform.js\";\n\nconst PACKAGE_NAME = \"@sakiori/fff-node\";\n\n/** The native library and the service executable of one build. */\nexport interface NativeFiles {\n library: string;\n executable: string;\n}\n\n/**\n * Get the current file's directory\n */\nfunction getCurrentDir(): string {\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 === PACKAGE_NAME) {\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 * Map a path inside an `.asar` archive to its `.asar.unpacked` counterpart when\n * the unpacked file exists. Native files cannot be loaded or executed from inside\n * an archive, so Electron applications unpack them beside it.\n */\nexport function unpackedAsarPath(path: string): string {\n const segments = path.split(sep);\n for (let i = 0; i < segments.length; i++) {\n if (!segments[i].endsWith(\".asar\")) continue;\n const unpacked = [...segments];\n unpacked[i] = `${segments[i]}.unpacked`;\n const candidate = unpacked.join(sep);\n if (existsSync(candidate)) return candidate;\n }\n return path;\n}\n\n/**\n * Both native files in `dir`, taken from its `.asar.unpacked` counterpart when\n * that exists, so they always come from one directory. Null when neither is\n * there; an error when only one is, or when they are inside an archive.\n */\nfunction nativeFilesIn(dir: string): Result<NativeFiles> | null {\n const unpacked = unpackedAsarPath(dir);\n const library = join(unpacked, getLibFilename());\n const executable = join(unpacked, getServiceExecutableFilename());\n const [hasLibrary, hasExecutable] = [existsSync(library), existsSync(executable)];\n if (hasLibrary && hasExecutable) {\n if (unpacked === dir && dir.split(sep).some((segment) => segment.endsWith(\".asar\"))) {\n return err(\n `The native files of ${dir} are inside an asar archive; unpack the platform package's directory`,\n ErrorCode.libraryUnavailable,\n );\n }\n return { ok: true, value: { library, executable } };\n }\n if (hasLibrary || hasExecutable) {\n const missing = hasLibrary ? executable : library;\n return err(\n `${missing} is missing beside the other native file; ship both from one build`,\n ErrorCode.libraryUnavailable,\n );\n }\n return null;\n}\n\n/**\n * Resolve the native files from the platform-specific npm package.\n *\n * @sakiori/fff-node lists every platform package as an optional dependency, and an\n * install brings the host's one (e.g. @sakiori/fff-bin-darwin-arm64), which holds\n * both files at its root.\n */\nfunction resolveFromNpmPackage(packageName: string): Result<NativeFiles> | null {\n let packageJsonPath: string;\n try {\n const require = createRequire(join(getPackageDir(), \"package.json\"));\n packageJsonPath = require.resolve(`${packageName}/package.json`);\n } catch {\n // Not installed, e.g. optional dependencies omitted or left out of a bundle\n return null;\n }\n return nativeFilesIn(dirname(packageJsonPath));\n}\n\n/**\n * Resolve the native files from an explicit library path, for platforms without a\n * prebuilt package (for example a libfff_c.so built from source on FreeBSD) or any\n * custom build. The service executable of the same build must sit beside it.\n */\nfunction resolveFromOverride(library: string): Result<NativeFiles> {\n if (!existsSync(library)) {\n return err(\n `FFF_C_LIB points to a file that does not exist: ${library}`,\n ErrorCode.libraryUnavailable,\n );\n }\n const executable = join(dirname(library), getServiceExecutableFilename());\n if (!existsSync(executable)) {\n return err(\n `${executable} is missing beside FFF_C_LIB; ship both from one build`,\n ErrorCode.libraryUnavailable,\n );\n }\n return { ok: true, value: { library, executable } };\n}\n\n/**\n * Get the development build (for local development)\n */\nfunction resolveFromDevBuild(): Result<NativeFiles> | null {\n const packageDir = getPackageDir();\n const workspaceRoot = join(packageDir, \"..\", \"..\");\n for (const dir of [\n join(packageDir, \"bin\"),\n join(workspaceRoot, \"target\", \"release\"),\n join(workspaceRoot, \"target\", \"debug\"),\n ]) {\n const found = nativeFilesIn(dir);\n if (found) return found;\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// Resolved once per process; the files do not move while it runs.\nlet resolved: Result<NativeFiles> | undefined;\n\nfunction resolveNativeFiles(): Result<NativeFiles> {\n const override = process.env[\"FFF_C_LIB\"];\n if (override) return resolveFromOverride(override);\n let triple: string;\n try {\n triple = getTriple();\n } catch (error) {\n return err(String(error instanceof Error ? error.message : error), ErrorCode.unsupportedPlatform);\n }\n const packageName = TRIPLE_TO_NPM_PACKAGE[triple];\n const fromPackage = () => (packageName ? resolveFromNpmPackage(packageName) : null);\n const found = isDevWorkspace()\n ? (resolveFromDevBuild() ?? fromPackage())\n : (fromPackage() ?? resolveFromDevBuild());\n if (found) return found;\n if (!packageName) {\n return err(\n `@sakiori/fff-node supports macOS and Linux with glibc or musl, each on x64 and arm64, not ${triple}; set FFF_C_LIB to a source build`,\n ErrorCode.unsupportedPlatform,\n );\n }\n return err(\n `fff native files not found: install ${packageName} beside @sakiori/fff-node, which an install that omits optional dependencies skips, or build libfff_c and fff-mcp from source with \\`make prepare-node\\``,\n ErrorCode.libraryUnavailable,\n );\n}\n\n/**\n * Find the native library and the service executable.\n *\n * Resolution order:\n * - FFF_C_LIB (if set) always wins\n * - Dev workspace: local dev build first, then npm package\n * - Production: npm package first, then dev build\n *\n * @returns Absolute paths of both files from one directory, or why they cannot be used\n */\nexport function findNativeFiles(): Result<NativeFiles> {\n resolved ??= resolveNativeFiles();\n return resolved;\n}\n\n/**\n * Find the native library.\n *\n * @returns Absolute path to the library, or null if not found\n */\nexport function findBinary(): string | null {\n const files = findNativeFiles();\n return files.ok ? files.value.library : null;\n}\n\n/**\n * Find the fff-mcp executable that starts the shared service.\n *\n * @returns Absolute path to the executable, or null if not found\n */\nexport function findServiceExecutable(): string | null {\n const files = findNativeFiles();\n return files.ok ? files.value.executable : null;\n}\n\n/**\n * Check if the native files exist in any known location\n */\nexport function binaryExists(): boolean {\n return findNativeFiles().ok;\n}\n","/**\n * Platform detection utilities for downloading the correct binary\n */\n\nimport { readFileSync } from \"node:fs\";\n\nlet triple: string | undefined;\n\n/**\n * Get the platform triple (e.g., \"x86_64-unknown-linux-gnu\"), detected once per process.\n */\nexport function getTriple(): string {\n triple ??= detectTriple();\n return triple;\n}\n\nfunction detectTriple(): 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 if (platform === \"freebsd\") {\n osName = \"unknown-freebsd\";\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, as detect-libc does: the process\n * report names the glibc a glibc Node.js runs on, or the musl objects it\n * loaded; otherwise the system's `ldd` script names its C library.\n */\nfunction detectLinuxLibc(): string {\n const report = processReport();\n if (report?.header?.glibcVersionRuntime) return \"unknown-linux-gnu\";\n if (report?.sharedObjects?.some((object) => /\\/(ld-musl-|libc\\.musl-)/.test(object))) {\n return \"unknown-linux-musl\";\n }\n try {\n if (readFileSync(\"/usr/bin/ldd\", \"utf-8\").includes(\"musl\")) return \"unknown-linux-musl\";\n } catch {\n // No ldd script: glibc is the common case.\n }\n return \"unknown-linux-gnu\";\n}\n\ninterface LibcReport {\n header?: { glibcVersionRuntime?: string };\n sharedObjects?: string[];\n}\n\nfunction processReport(): LibcReport | null {\n // `excludeNetwork` exists from Node 20.13 and 22.0; older versions ignore it.\n const report = process.report as\n | (NodeJS.ProcessReport & { excludeNetwork?: boolean })\n | undefined;\n if (!report) return null;\n // Network details are not needed and slow the report down.\n const excludeNetwork = report.excludeNetwork;\n report.excludeNetwork = true;\n try {\n return report.getReport() as LibcReport;\n } catch {\n return null;\n } finally {\n report.excludeNetwork = excludeNetwork;\n }\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 * Get the filename of the shared service executable for the current platform\n */\nexport function getServiceExecutableFilename(): string {\n return process.platform === \"win32\" ? \"fff-mcp.exe\" : \"fff-mcp\";\n}\n\n/**\n * Map from Rust target triple to npm platform package name.\n * Each @sakiori/fff-bin-* package carries the libfff_c shared library and the\n * fff-mcp service executable of the same build.\n */\nexport const TRIPLE_TO_NPM_PACKAGE: Record<string, string> = {\n \"aarch64-apple-darwin\": \"@sakiori/fff-bin-darwin-arm64\",\n \"x86_64-apple-darwin\": \"@sakiori/fff-bin-darwin-x64\",\n \"x86_64-unknown-linux-gnu\": \"@sakiori/fff-bin-linux-x64-gnu\",\n \"aarch64-unknown-linux-gnu\": \"@sakiori/fff-bin-linux-arm64-gnu\",\n \"x86_64-unknown-linux-musl\": \"@sakiori/fff-bin-linux-x64-musl\",\n \"aarch64-unknown-linux-musl\": \"@sakiori/fff-bin-linux-arm64-musl\",\n};\n\n/**\n * Get the npm package name for the current platform's native binaries.\n *\n * @returns Package name like \"@sakiori/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 * Every instance is a session on the shared fff service. Searches, greps and\n * scan state travel through `fff_service_call`, which takes one request of the\n * service's program protocol as JSON and returns the whole reply as one JSON\n * document, so a result of any size is decoded in a single pass.\n *\n * Calls that reach the service block until it answers or their time limit\n * passes, so they run on the package's worker threads (see `native-pool.ts`)\n * and resolve a Promise. Only calls that need no service run on the calling\n * thread. An instance is identified by its native address, a BigInt, which\n * every thread of the process can pass back to the library.\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 * ffi-rs auto-dereferences struct retType pointers, losing the original pointer.\n * We call the C function with `retType: DataType.External` to get the raw\n * pointer, read the struct with `restorePointer`, then free the raw pointer\n * with `fff_free_result`. `isNullPointer` detects null C pointers.\n */\n\nimport { createRequire } from \"node:module\";\nimport type * as FfiRs from \"ffi-rs\";\nimport type { FieldType, JsExternal } from \"ffi-rs\";\nimport { findNativeFiles, type NativeFiles } from \"./binary.js\";\nimport { ErrorCode, err, nativeErrorCode, type Result } from \"./errors.js\";\nimport { closeNativePool, type NativeReply, type NativeSetup, runNative } from \"./native-pool.js\";\nimport {\n closeWatchDelivery,\n type DeliverySetup,\n forgetWatchCallback,\n registerWatchCallback,\n} from \"./watch-delivery.js\";\n\nconst LIBRARY_KEY = \"fff_c\";\n\nfunction createOptionsStruct() {\n const { DataType } = ffi();\n return {\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 extra_ignore: DataType.String,\n disable_git: DataType.U8,\n disable_ignore_files: DataType.U8,\n };\n}\n\n// ALWAYS KEEP IN SYNC WITH fff.h\nconst FFF_CREATE_OPTIONS_VERSION = 3;\nconst LEGACY_CREATE_OPTIONS_VERSION = 2;\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 */\nfunction resultStruct() {\n const { DataType } = ffi();\n return {\n success: DataType.U8,\n error: DataType.External,\n handle: DataType.External,\n int_value: DataType.I64,\n };\n}\n\ninterface FffResultRaw {\n success: number;\n error: JsExternal;\n handle: JsExternal;\n int_value: number;\n}\n\n/**\n * Opaque native handle: the instance's address. Callers must not inspect or\n * modify this value.\n */\nexport type NativeHandle = bigint;\n\n// The native files of the loaded library, resolved once per process.\nlet loaded: NativeFiles | null = null;\n\n// ffi-rs, required once the platform's native files are found: it refuses to load on a\n// platform it has no binary for, which must be a failed Result rather than a failed import.\nlet ffiModule: typeof FfiRs | null = null;\n\nfunction ffi(): typeof FfiRs {\n if (!ffiModule) throw new Error(\"the native library is not loaded\");\n return ffiModule;\n}\n\n// Native instances created and not yet freed.\nlet liveInstances = 0;\n\n/**\n * The number of native instances this process created and has not freed yet;\n * each holds a session on the service. A destroyed `FileFinder` frees its own.\n */\nexport function nativeInstanceCount(): number {\n return liveInstances;\n}\n\n/**\n * Load the native library, or report why it cannot be loaded.\n */\nfunction loadLibrary(): Result<NativeFiles> {\n if (loaded) return { ok: true, value: loaded };\n const found = findNativeFiles();\n if (!found.ok) return found;\n try {\n ffiModule ??= createRequire(import.meta.url)(\"ffi-rs\") as typeof FfiRs;\n ffiModule.open({ library: LIBRARY_KEY, path: found.value.library });\n } catch (error) {\n return err(\n `Cannot load ${found.value.library}: ${String(error)}`,\n ErrorCode.libraryUnavailable,\n );\n }\n loaded = found.value;\n return found;\n}\n\n/**\n * What a worker thread loads: the same library file as the main thread, and the\n * ffi-rs module this package resolves.\n */\nfunction nativeSetup(): NativeSetup {\n const library = loaded?.library;\n if (!library) throw new Error(\"the native library is not loaded\");\n return {\n ffiPath: createRequire(import.meta.url).resolve(\"ffi-rs\"),\n library,\n createOptions: createOptionsStruct(),\n };\n}\n\n/** Run an operation of the native worker once the library is known to load. */\nfunction runOnWorker<T>(operation: string, args: unknown[]): Promise<NativeReply<T>> {\n const library = loadLibrary();\n if (!library.ok) return Promise.resolve(library);\n return runNative<T>(nativeSetup, operation, args);\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 (ffi().isNullPointer(ptr)) return null;\n try {\n const [str] = ffi().restorePointer({\n retType: [ffi().DataType.String],\n paramsValue: ffi().wrapPointer([ptr]),\n });\n return str as string;\n } catch {\n return null;\n }\n}\n\n/** Free a pointer the library allocated with the given free function. */\nfunction freeWith(funcName: string, ptr: JsExternal): void {\n try {\n ffi().load({\n library: LIBRARY_KEY,\n funcName,\n retType: ffi().DataType.Void,\n paramsType: [ffi().DataType.External],\n paramsValue: [ptr],\n });\n } catch {\n // Ignore cleanup errors\n }\n}\n\n/**\n * Call a function returning FffResult with a C string in handle, on the calling\n * thread, for calls that need no service.\n */\nfunction callStringSync(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<string | null> {\n const library = loadLibrary();\n if (!library.ok) return library;\n const rawPtr = ffi().load({\n library: LIBRARY_KEY,\n funcName,\n retType: ffi().DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n }) as JsExternal;\n const [struct] = ffi().restorePointer({\n retType: [resultStruct()],\n paramsValue: ffi().wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n if (struct.success === 0) {\n const error = readCString(struct.error);\n freeWith(\"fff_free_result\", rawPtr);\n return err(error || \"Unknown error\", nativeErrorCode(Number(struct.int_value)));\n }\n const handle = struct.handle;\n freeWith(\"fff_free_result\", rawPtr);\n if (ffi().isNullPointer(handle)) return { ok: true, value: null };\n const text = readCString(handle);\n freeWith(\"fff_free_string\", handle);\n return { ok: true, value: text };\n}\n\n/**\n * Call a function returning FffResult with its value in `int_value`, on the\n * calling thread, for calls that need no service.\n */\nfunction callIntSync(\n funcName: string,\n paramsType: FieldType[],\n paramsValue: unknown[],\n): Result<number> {\n const library = loadLibrary();\n if (!library.ok) return library;\n const rawPtr = ffi().load({\n library: LIBRARY_KEY,\n funcName,\n retType: ffi().DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n }) as JsExternal;\n const [struct] = ffi().restorePointer({\n retType: [resultStruct()],\n paramsValue: ffi().wrapPointer([rawPtr]),\n }) as unknown as [FffResultRaw];\n const failed = struct.success === 0;\n const error = failed ? readCString(struct.error) : null;\n const value = Number(struct.int_value);\n freeWith(\"fff_free_result\", rawPtr);\n return failed ? err(error || \"Unknown error\", nativeErrorCode(value)) : { ok: true, value };\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) => letter.toUpperCase());\n result[camelKey] = snakeToCamel(value);\n }\n return result;\n}\n\n/**\n * Open a session on the shared service on a worker thread. `timeoutMs` bounds\n * opening it and each later call (0 takes the library's defaults).\n */\nexport async function ffiCreate(\n basePath: string,\n frecencyDbPath: string,\n enableMmapCache: boolean,\n enableContentIndexing: boolean,\n watch: boolean,\n aiMode: boolean,\n logFilePath: string,\n logLevel: string,\n cacheBudgetMaxFiles: number,\n enableFsRootScanning: boolean,\n enableHomeDirScanning: boolean,\n followSymlinks: boolean,\n extraIgnore: string,\n disableGit: boolean,\n disableIgnoreFiles: boolean,\n serviceDir: string,\n timeoutMs: number,\n): Promise<Result<NativeHandle>> {\n if (extraIgnore.includes(\"\\0\")) {\n return err(\"extraIgnore cannot contain NUL bytes\", ErrorCode.invalidRequest);\n }\n const library = loadLibrary();\n if (!library.ok) return library;\n\n const version =\n extraIgnore || disableGit || disableIgnoreFiles\n ? FFF_CREATE_OPTIONS_VERSION\n : LEGACY_CREATE_OPTIONS_VERSION;\n\n const optsValue = {\n version,\n base_path: basePath,\n frecency_db_path: frecencyDbPath,\n history_db_path: \"\",\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: 0,\n cache_budget_max_file_size: 0,\n enable_fs_root_scanning: enableFsRootScanning ? 1 : 0,\n enable_home_dir_scanning: enableHomeDirScanning ? 1 : 0,\n follow_symlinks: followSymlinks ? 1 : 0,\n extra_ignore: extraIgnore,\n disable_git: disableGit ? 1 : 0,\n disable_ignore_files: disableIgnoreFiles ? 1 : 0,\n };\n\n const created = await runOnWorker<bigint>(\"create\", [\n optsValue,\n library.value.executable,\n serviceDir,\n timeoutMs,\n ]);\n if (!created.ok) return created;\n if (created.value === 0n) {\n return err(\"fff_create_service_instance returned null handle\");\n }\n liveInstances++;\n return { ok: true, value: created.value };\n}\n\n/**\n * End an instance's session and free it on a worker thread. No other call may\n * be in flight on the instance.\n */\nexport async function ffiDestroy(handle: NativeHandle): Promise<void> {\n const destroyed = await runOnWorker(\"destroy\", [handle]);\n if (destroyed.ok) {\n liveInstances--;\n forgetWatchCallback(handle);\n }\n}\n\n/**\n * Send one request of the service's program protocol on a worker thread and\n * decode the reply.\n */\nexport async function ffiServiceCall<T>(\n handle: NativeHandle,\n request: unknown,\n): Promise<Result<T>> {\n const reply = await runOnWorker<string | null>(\"call\", [handle, JSON.stringify(request)]);\n if (!reply.ok) return reply;\n if (reply.value === null) return err(\"fff_service_call returned no reply\");\n try {\n return { ok: true, value: JSON.parse(reply.value) as T };\n } catch (error) {\n return err(`Cannot decode the service reply: ${String(error)}`);\n }\n}\n\n/**\n * Point the instance's watch callback at the delivery worker.\n */\nexport function ffiRegisterWatchCallback(handle: NativeHandle): Promise<Result<void>> {\n const library = loadLibrary();\n if (!library.ok) return Promise.resolve(library);\n return registerWatchCallback(deliverySetup, handle);\n}\n\n/**\n * Start a subscription to the changes under an instance's directory, on the\n * calling thread: the library returns its watch id at once and reports\n * readiness through the callback. No call on the instance may free it meanwhile.\n */\nexport function ffiWatchStart(\n handle: NativeHandle,\n pattern: string,\n ignore: string[],\n repository: boolean,\n refs: boolean,\n): Result<number> {\n const { DataType } = ffi();\n return callIntSync(\n \"fff_watch_args_ex2\",\n [\n DataType.BigInt,\n DataType.String,\n DataType.StringArray,\n DataType.U32,\n DataType.Boolean,\n DataType.Boolean,\n ],\n [handle, pattern, ignore, ignore.length, repository, refs],\n );\n}\n\n/**\n * End a subscription on the calling thread; the library stops delivering to it first.\n *\n * The library joins the subscription's thread before it returns, which cannot hold up\n * the event loop for long: that thread waits only where the end of the subscription\n * interrupts it (reading the service, backing off, reconnecting), and its delivery\n * into the callback queues a batch for the delivery worker without waiting for it.\n */\nexport function ffiUnwatch(handle: NativeHandle, watchId: number): Result<number> {\n const { DataType } = ffi();\n return callIntSync(\"fff_unwatch\", [DataType.BigInt, DataType.U64], [handle, watchId]);\n}\n\nfunction deliverySetup(): DeliverySetup {\n const { ffiPath, library } = nativeSetup();\n return { ffiPath, library };\n}\n\n/**\n * Get the base path of the file picker; the instance knows it without the service.\n */\nexport function ffiGetBasePath(handle: NativeHandle): Result<string | null> {\n const library = loadLibrary();\n if (!library.ok) return library;\n return callStringSync(\"fff_get_base_path\", [ffi().DataType.BigInt], [handle]);\n}\n\n/**\n * Restart the index in `newPath`, which for a service instance rescans its own\n * directory, on a worker thread.\n */\nexport async function ffiRestartIndex(\n handle: NativeHandle,\n newPath: string,\n): Promise<Result<void>> {\n const done = await runOnWorker<null>(\"restart\", [handle, newPath]);\n return done.ok ? { ok: true, value: undefined } : done;\n}\n\nfunction healthResult(json: Result<string | null>): Result<unknown> {\n if (!json.ok) return json;\n if (json.value === null || json.value === \"\") return { ok: true, value: undefined };\n try {\n return { ok: true, value: snakeToCamel(JSON.parse(json.value)) };\n } catch {\n return { ok: true, value: json.value };\n }\n}\n\n/**\n * Health check of an instance, which asks its service, on a worker thread.\n */\nexport async function ffiHealthCheck(\n handle: NativeHandle,\n testPath: string,\n): Promise<Result<unknown>> {\n const json = await runOnWorker<string | null>(\"health\", [handle, testPath]);\n return json.ok ? healthResult(json) : json;\n}\n\n/**\n * Health check without an instance (version and git only), on the calling\n * thread. A zero address stands in for the null pointer.\n */\nexport function ffiHealthCheckStatic(testPath: string): Result<unknown> {\n const library = loadLibrary();\n if (!library.ok) return library;\n const { DataType } = ffi();\n return healthResult(\n callStringSync(\"fff_health_check\", [DataType.BigInt, DataType.String], [0n, 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 const library = loadLibrary();\n if (!library.ok) throw new Error(library.error);\n}\n\n/**\n * Check if the library is available.\n */\nexport function isAvailable(): boolean {\n return loadLibrary().ok;\n}\n\n/**\n * Close the library and stop the worker threads that call it and deliver watch\n * events. Call this when completely done with the library; instances must have\n * been destroyed.\n */\nexport async function closeLibrary(): Promise<void> {\n await closeWatchDelivery();\n await closeNativePool();\n if (loaded) {\n ffi().close(LIBRARY_KEY);\n loaded = null;\n }\n}\n","/**\n * The worker threads that run the package's blocking native calls.\n *\n * Calls that reach the shared service block their thread until the service\n * answers or the call's time limit passes. They run here, on a few worker\n * threads the package owns, rather than on the event loop or on the libuv\n * thread pool, whose threads the file system, DNS, crypto and zlib APIs of the\n * whole process share. Workers start when a call needs one, and hold the\n * process open only while they run a call; one that dies fails its call and is\n * replaced by the next call that needs a worker.\n */\n\nimport { MessageChannel, type MessagePort, Worker } from \"node:worker_threads\";\nimport { ErrorCode, type Failure, nativeErrorCode } from \"./errors.js\";\nimport { NATIVE_WORKER_SOURCE } from \"./native-worker.js\";\n\n/**\n * The most worker threads the package runs. Each instance has at most one\n * call in flight, so this many instances can reach the service at once; an\n * unresponsive service holds at most this many threads until its calls' time\n * limits pass, and nothing outside the package waits for them.\n */\nexport const NATIVE_POOL_SIZE = 4;\n\n/** The reply of a native call. */\nexport type NativeReply<T> = { ok: true; value: T } | Failure;\n\n// A worker's reply, with a failure's `FFF_ERROR_*` kind as `nativeCode`.\ntype WorkerReply = { ok: true; value: unknown } | { ok: false; error: string; nativeCode: number };\n\n/** What every worker needs to load the native library. */\nexport interface NativeSetup {\n ffiPath: string;\n library: string;\n createOptions: unknown;\n}\n\ninterface Job {\n operation: string;\n args: unknown[];\n settle: (reply: NativeReply<unknown>) => void;\n}\n\ninterface Slot {\n worker: Worker;\n // Calls use a channel of their own: Node posts its own messages to a worker's\n // parent port, such as the modules it requires under `--watch`.\n port: MessagePort;\n job: Job | null;\n}\n\nclass NativePool {\n private readonly slots: Slot[] = [];\n private readonly waiting: Job[] = [];\n private nextId = 0;\n\n constructor(private readonly setup: () => NativeSetup) {}\n\n run<T>(operation: string, args: unknown[]): Promise<NativeReply<T>> {\n return new Promise((resolve) => {\n this.waiting.push({\n operation,\n args,\n settle: resolve as (reply: NativeReply<unknown>) => void,\n });\n this.dispatch();\n });\n }\n\n /** Stops every worker; calls in flight fail, and later calls start new workers. */\n async close(): Promise<void> {\n for (const job of this.waiting.splice(0)) {\n job.settle({ ok: false, error: \"fff native workers were closed\", code: ErrorCode.failed });\n }\n await Promise.all(this.slots.map((slot) => slot.worker.terminate()));\n }\n\n private dispatch(): void {\n while (this.waiting.length > 0) {\n const slot = this.slots.find((candidate) => candidate.job === null) ?? this.start();\n if (!slot) return;\n const job = this.waiting.shift() as Job;\n slot.job = job;\n slot.worker.ref();\n slot.port.ref();\n slot.port.postMessage({ id: this.nextId++, operation: job.operation, args: job.args });\n }\n }\n\n private start(): Slot | null {\n if (this.slots.length >= NATIVE_POOL_SIZE) return null;\n const { port1: port, port2: workerPort } = new MessageChannel();\n const worker = new Worker(NATIVE_WORKER_SOURCE, {\n eval: true,\n // The host's flags, as --input-type=module, would change how the source is read.\n execArgv: [],\n workerData: { ...this.setup(), port: workerPort },\n transferList: [workerPort],\n });\n worker.unref();\n port.unref();\n const slot: Slot = { worker, port, job: null };\n port.on(\"message\", ({ reply }: { reply: WorkerReply }) => {\n const job = slot.job;\n slot.job = null;\n worker.unref();\n port.unref();\n job?.settle(\n reply.ok\n ? reply\n : { ok: false, error: reply.error, code: nativeErrorCode(reply.nativeCode) },\n );\n this.dispatch();\n });\n let failure = \"\";\n worker.on(\"error\", (error) => {\n failure = String(error);\n });\n worker.on(\"exit\", (code) => {\n port.close();\n this.slots.splice(this.slots.indexOf(slot), 1);\n const job = slot.job;\n slot.job = null;\n job?.settle({\n ok: false,\n error: `fff native worker exited (${failure || `code ${code}`})`,\n code: ErrorCode.failed,\n });\n this.dispatch();\n });\n this.slots.push(slot);\n return slot;\n }\n}\n\nlet pool: NativePool | null = null;\n\n/** Run one native operation on a worker thread. */\nexport function runNative<T>(\n setup: () => NativeSetup,\n operation: string,\n args: unknown[],\n): Promise<NativeReply<T>> {\n pool ??= new NativePool(setup);\n return pool.run<T>(operation, args);\n}\n\n/** Stop the worker threads; the next native call starts new ones. */\nexport async function closeNativePool(): Promise<void> {\n const closing = pool;\n pool = null;\n await closing?.close();\n}\n","/**\n * Source of the worker threads that make the blocking calls into libfff_c.\n *\n * It runs as an `eval` worker, a CommonJS script, so it needs no file of its\n * own next to the bundle. The main thread passes the paths it resolved, the\n * ffi-rs entry and the native library (an `.asar.unpacked` path inside\n * Electron archives), and the layout of the create options in `workerData`.\n * Calls arrive on the private port in `workerData`, never on the parent port,\n * which Node also uses for messages of its own. Each message is one call; the\n * reply carries plain values only: the instance handle as a BigInt address, and\n * a failure's `FFF_ERROR_*` kind as `nativeCode`.\n */\nexport const NATIVE_WORKER_SOURCE = String.raw`\n\"use strict\";\nconst { workerData } = require(\"node:worker_threads\");\nconst port = workerData.port;\nconst ffi = require(workerData.ffiPath);\nconst { DataType } = ffi;\nconst LIBRARY = \"fff_c\";\nffi.open({ library: LIBRARY, path: workerData.library });\n\nconst RESULT = {\n success: DataType.U8,\n error: DataType.External,\n handle: DataType.External,\n int_value: DataType.I64,\n};\nconst HANDLE_RESULT = { ...RESULT, handle: DataType.BigInt };\nconst CREATE_OPTIONS = workerData.createOptions;\n\nfunction call(funcName, paramsType, paramsValue) {\n return ffi.load({\n library: LIBRARY,\n funcName,\n retType: DataType.External,\n paramsType,\n paramsValue,\n freeResultMemory: false,\n });\n}\n\nfunction free(funcName, pointer) {\n ffi.load({\n library: LIBRARY,\n funcName,\n retType: DataType.Void,\n paramsType: [DataType.External],\n paramsValue: [pointer],\n });\n}\n\nfunction cString(pointer) {\n if (ffi.isNullPointer(pointer)) return null;\n const [text] = ffi.restorePointer({\n retType: [DataType.String],\n paramsValue: ffi.wrapPointer([pointer]),\n });\n return text;\n}\n\n// Reads an FffResult, frees it and returns a reply; payload reads its handle field.\nfunction envelope(pointer, layout, payload) {\n const [result] = ffi.restorePointer({ retType: [layout], paramsValue: ffi.wrapPointer([pointer]) });\n if (result.success === 0) {\n const error = cString(result.error) || \"Unknown error\";\n free(\"fff_free_result\", pointer);\n return { ok: false, error, nativeCode: Number(result.int_value) };\n }\n const value = payload(result.handle);\n free(\"fff_free_result\", pointer);\n return { ok: true, value };\n}\n\nfunction stringPayload(handle) {\n if (ffi.isNullPointer(handle)) return null;\n const text = cString(handle);\n free(\"fff_free_string\", handle);\n return text;\n}\n\nconst operations = {\n create(options, executable, serviceDir, timeoutMs) {\n const pointer = call(\n \"fff_create_service_instance\",\n [CREATE_OPTIONS, DataType.String, DataType.String, DataType.U64],\n [options, executable, serviceDir, timeoutMs],\n );\n return envelope(pointer, HANDLE_RESULT, (handle) => handle);\n },\n call(handle, request) {\n const pointer = call(\"fff_service_call\", [DataType.BigInt, DataType.String], [handle, request]);\n return envelope(pointer, RESULT, stringPayload);\n },\n restart(handle, path) {\n const pointer = call(\"fff_restart_index\", [DataType.BigInt, DataType.String], [handle, path]);\n return envelope(pointer, RESULT, () => null);\n },\n health(handle, testPath) {\n const pointer = call(\"fff_health_check\", [DataType.BigInt, DataType.String], [handle, testPath]);\n return envelope(pointer, RESULT, stringPayload);\n },\n destroy(handle) {\n ffi.load({\n library: LIBRARY,\n funcName: \"fff_destroy\",\n retType: DataType.Void,\n paramsType: [DataType.BigInt],\n paramsValue: [handle],\n });\n return { ok: true, value: null };\n },\n};\n\nport.on(\"message\", ({ id, operation, args }) => {\n let reply;\n try {\n reply = operations[operation](...args);\n } catch (error) {\n reply = { ok: false, error: \"fff native call failed: \" + String(error), nativeCode: 0 };\n }\n port.postMessage({ id, reply });\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 /** Extra `.gitignore` rules relative to `basePath`. Use `!pattern` to re-include paths. Requires a zlob build. */\n extraIgnore?: readonly string[];\n /** Disable Git integration and Git ignore rules for this index. Requires a zlob build. */\n disableGit?: boolean;\n /** Skip rules from `.ignore` files. Requires a zlob build. */\n disableIgnoreFiles?: boolean;\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 /** Bonus for query segments aligning with path segments */\n pathAlignmentBonus: 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 *\n * renamed = the file was moved to the other path without changing the content\n */\nexport type WatchEventKind = \"created\" | \"modified\" | \"removed\" | \"rescan\" | \"renamed\";\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 /** Absolute path the file moved from. Only set when `kind == \"renamed\"`. */\n from?: string;\n}\n\n/** Options for watch subscriptions. */\nexport interface WatchOptions {\n /** Additional glob wildcard patterns to ignore; one without `/` matches the file name at any depth */\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\nexport type DefinitionMode = \"off\" | \"classify\" | \"definition_only\" | \"usage_only\";\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 /**\n * Apply `timeBudgetMs` even before anything matched. Off by default, so a\n * zero-match query scans every candidate file. (default: false)\n */\n enforceTimeBudget?: boolean;\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 /** Select matching definitions/usages, or classify all matches. Defaults to \"off\".\n * Legacy booleans map false to \"off\" and true to \"classify\". Keep the same mode with a cursor. */\n classifyDefinitions?: DefinitionMode | 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 the query matches a declared name. Available when classification is enabled. */\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 /**\n * Apply `timeBudgetMs` even before anything matched. Off by default, so a\n * zero-match query scans every candidate file. (default: false)\n */\n enforceTimeBudget?: boolean;\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 /** Select matching definitions/usages, or classify all matches. Defaults to \"off\".\n * Legacy booleans map false to \"off\" and true to \"classify\". Keep the same mode with a cursor. */\n classifyDefinitions?: DefinitionMode | 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 over a 50-ms window and submitted in batches of 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/** @internal Convert the public mode to the C integer argument. */\nexport function definitionModeToInt(\n mode: DefinitionMode | boolean | undefined,\n): number | undefined {\n switch (mode) {\n case undefined:\n case false:\n case \"off\":\n return 0;\n case true:\n case \"classify\":\n return 1;\n case \"definition_only\":\n return 2;\n case \"usage_only\":\n return 3;\n default:\n return undefined;\n }\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 is a session on the shared fff service of the package's\n * release, which owns the index; instances of any process searching the same\n * directory share one index with each other and with `fff-mcp` agents.\n *\n * Every method that reaches the service returns a Promise of a Result and runs\n * its native call on one of the package's worker threads, so a slow or\n * unresponsive service blocks neither the event loop nor the libuv thread pool.\n * Methods that need no service stay synchronous.\n */\n\nimport {\n ensureLoaded,\n ffiCreate,\n ffiDestroy,\n ffiGetBasePath,\n ffiHealthCheck,\n ffiHealthCheckStatic,\n ffiRestartIndex,\n ffiServiceCall,\n ffiRegisterWatchCallback,\n ffiUnwatch,\n ffiWatchStart,\n isAvailable,\n type NativeHandle,\n} from \"./ffi.js\";\n\nimport type {\n DirSearchOptions,\n DirSearchResult,\n GlobOptions,\n GrepCursor,\n GrepOptions,\n GrepResult,\n HealthCheck,\n MixedSearchResult,\n MultiGrepOptions,\n ScanProgress,\n SearchOptions,\n SearchResult,\n WatchUnsubscribe,\n} from \"./fff-api.js\";\nimport { definitionModeToInt } from \"./fff-api.js\";\n\nimport { ErrorCode, err, type Result } from \"./errors.js\";\nimport type {\n FrecencyOptions,\n ServiceInitOptions,\n ServiceWatchBatchCallback,\n ServiceWatchOptions,\n} from \"./service-options.js\";\nimport { deleteWatchListener, setWatchListener } from \"./watch-delivery.js\";\n\n/** Interval of the readiness polls of `waitForScan` and `waitForIndexReady`. */\nconst READY_POLL_MS = 50;\n\n/** The error of a method the shared service has no equivalent for. */\nfunction unavailable<T>(method: string): Result<T> {\n return err(`FileFinder.${method} is unavailable over the shared service`, ErrorCode.unavailable);\n}\n\n/** Where a grep continues: a file offset into the order of the index that returned it. */\ninterface ServiceCursor {\n index: string;\n offset: number;\n}\n\n/** The opaque cursor of the shared API, carrying the index it belongs to. */\ninterface IndexedGrepCursor extends GrepCursor {\n readonly _index: string;\n}\n\n/** A grep reply before its cursor is wrapped. */\ntype GrepReply = Omit<GrepResult, \"nextCursor\"> & { nextCursor: ServiceCursor | null };\n\nconst GREP_MODES = { plain: \"PlainText\", regex: \"Regex\", fuzzy: \"Fuzzy\" } as const;\n\n/** The service's definition modes, indexed by `definitionModeToInt`. */\nconst DEFINITION_MODES = [\"Off\", \"Classify\", \"DefinitionOnly\", \"UsageOnly\"] as const;\n\nfunction serviceCursor(cursor: GrepCursor | null | undefined): Result<ServiceCursor | null> {\n if (!cursor) return { ok: true, value: null };\n const index = (cursor as Partial<IndexedGrepCursor>)._index;\n if (index === undefined) {\n return err(\n \"The grep cursor was not returned by a FileFinder grep; search again without it\",\n ErrorCode.invalidRequest,\n );\n }\n return { ok: true, value: { index, offset: cursor._offset } };\n}\n\nfunction grepResult(reply: Result<GrepReply>): Result<GrepResult> {\n if (!reply.ok) return reply;\n const next = reply.value.nextCursor;\n const nextCursor: IndexedGrepCursor | null = next\n ? { __brand: \"GrepCursor\", _offset: next.offset, _index: next.index }\n : null;\n return { ok: true, value: { ...reply.value, nextCursor } };\n}\n\nfunction grepOptions(options: GrepOptions & FrecencyOptions, cursor: ServiceCursor | null) {\n const definitionMode = definitionModeToInt(options.classifyDefinitions);\n if (definitionMode === undefined) {\n return err(\"Invalid classifyDefinitions mode\", ErrorCode.invalidRequest);\n }\n const value = {\n max_file_size: options.maxFileSize || 10 * 1024 * 1024,\n max_matches_per_file: options.maxMatchesPerFile ?? 0,\n smart_case: options.smartCase ?? true,\n cursor,\n page_size: options.pageSize || 50,\n time_budget_ms: options.timeBudgetMs ?? 0,\n enforce_time_budget: options.enforceTimeBudget ?? false,\n before_context: options.beforeContext ?? 0,\n after_context: options.afterContext ?? 0,\n classify_definitions: DEFINITION_MODES[definitionMode],\n exclude_frecency: options.excludeFrecency ?? false,\n };\n return { ok: true as const, value };\n}\n\n/**\n * The native calls of one instance, one at a time.\n *\n * The service session answers one call at a time anyway; queuing here keeps an\n * instance to one worker thread, so other instances keep the rest. When a call\n * finds the service lost (it did not answer within its bound, or the\n * connection broke), the calls queued behind it fail with the same error\n * rather than each waiting out that bound again; the next call opens a new\n * session. A call run with `always` runs regardless, as freeing the instance\n * must.\n */\nclass CallQueue {\n private tail: Promise<unknown> = Promise.resolve();\n private lost = { epoch: 0, error: \"\" };\n\n run<T>(\n call: () => Promise<Result<T>>,\n always = false,\n ): Promise<Result<T>> {\n const epoch = this.lost.epoch;\n const result = this.tail.then(async (): Promise<Result<T>> => {\n if (!always && this.lost.epoch !== epoch) return err(this.lost.error, ErrorCode.lostService);\n let outcome: Result<T>;\n try {\n outcome = await call();\n } catch (error) {\n return err(`fff native call failed: ${String(error)}`);\n }\n if (outcome.ok) return outcome;\n if (outcome.code === ErrorCode.lostService) {\n this.lost = { epoch: this.lost.epoch + 1, error: outcome.error };\n }\n return outcome;\n });\n this.tail = result.catch(() => undefined);\n return result;\n }\n}\n\nfunction page(options?: SearchOptions & FrecencyOptions) {\n return {\n current_file: options?.currentFile || null,\n offset: options?.pageIndex ?? 0,\n limit: options?.pageSize || 100,\n exclude_frecency: options?.excludeFrecency ?? false,\n };\n}\n\n/**\n * FileFinder - Fast file finder with fuzzy search\n *\n * Each instance is a session on the shared fff service. Create as many as you\n * need and destroy them when done. Calls on one instance may overlap; they\n * reach the service one at a time, in call order.\n *\n * @example\n *\n * ```ts\n * import { FileFinder } from \"@sakiori/fff-node\";\n *\n * // Create an instance\n * const finder = await 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 = await finder.value.fileSearch(\"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 * await finder.value.destroy();\n * ```\n */\nexport class FileFinder {\n private handle: NativeHandle | null;\n private readonly calls = new CallQueue();\n private readonly watching: boolean;\n /** The instance's subscriptions by native id, with how to fail one still pending. */\n private readonly watches = new Map<number, (failure: Result<WatchUnsubscribe>) => void>();\n /** Registrations of the watch callback in flight, which `destroy` lets finish. */\n private readonly registrations = new Set<Promise<unknown>>();\n\n private constructor(handle: NativeHandle, watching: boolean) {\n this.handle = handle;\n this.watching = watching;\n }\n\n /**\n * Create a new file finder instance.\n *\n * Opens a session on the shared fff service, starting the service from the\n * platform package's `fff-mcp` when none of this release runs. When the\n * service cannot be started or reached within `timeoutMs` (90 seconds by\n * default), the result carries its reason.\n *\n * @param options - Initialization options\n * @returns Result containing the new FileFinder instance or an error\n *\n * @example\n * ```typescript\n * const finder = await FileFinder.create({ basePath: \"/path/to/project\" });\n * ```\n */\n static async create(options: ServiceInitOptions): Promise<Result<FileFinder>> {\n if (options.historyDbPath) {\n return err(\"historyDbPath is unavailable over the shared service\", ErrorCode.unavailable);\n }\n const timeoutMs = options.timeoutMs ?? 0;\n if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0) {\n return err(\n \"timeoutMs must be a non-negative integer number of milliseconds\",\n ErrorCode.invalidRequest,\n );\n }\n let result: Result<NativeHandle>;\n try {\n result = await ffiCreate(\n options.basePath,\n options.frecencyDbPath ?? \"\",\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.enableFsRootScanning ?? false,\n options.enableHomeDirScanning ?? false,\n options.followSymlinks ?? false,\n options.extraIgnore?.join(\"\\n\") ?? \"\",\n options.disableGit ?? false,\n options.disableIgnoreFiles ?? false,\n options.serviceDir ?? \"\",\n timeoutMs,\n );\n } catch (error) {\n return err(`fff native call failed: ${String(error)}`);\n }\n\n if (!result.ok) {\n return result;\n }\n\n return { ok: true, value: new FileFinder(result.value, !(options.disableWatch ?? false)) };\n }\n\n /**\n * Destroy the instance and end its service session.\n *\n * Calls made after this report the instance as destroyed; calls made before\n * it finish first. Its watch subscriptions end, and their callbacks are not\n * called once this is called. The service releases the index once no\n * session uses it. An instance that is never destroyed keeps its session\n * until the process exits.\n */\n async destroy(): Promise<void> {\n const handle = this.handle;\n if (handle === null) return;\n this.handle = null;\n for (const [watchId, settle] of this.watches) {\n deleteWatchListener(watchId);\n settle(err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed));\n }\n this.watches.clear();\n await Promise.allSettled([...this.registrations]);\n await this.calls.run(async () => {\n await ffiDestroy(handle);\n return { ok: true, value: undefined };\n }, true);\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 * Run a native call of this instance, or report it destroyed.\n */\n private call<T>(\n native: (handle: NativeHandle) => Promise<Result<T>>,\n ): Promise<Result<T>> {\n const handle = this.handle;\n if (handle === null) {\n return Promise.resolve(err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed));\n }\n return this.calls.run(() => native(handle));\n }\n\n /**\n * Send one request of the service's program protocol.\n */\n private request<T>(request: unknown): Promise<Result<T>> {\n return this.call((handle) => ffiServiceCall<T>(handle, request));\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 = await finder.fileSearch(\"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(\n query: string,\n options?: SearchOptions & FrecencyOptions,\n ): Promise<Result<SearchResult>> {\n return this.request({ SearchFiles: { query, ...page(options) } });\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 = await 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 & FrecencyOptions): Promise<Result<SearchResult>> {\n return this.request({ Glob: { pattern, ...page(options) } });\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 = await 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(\n query: string,\n options?: DirSearchOptions & FrecencyOptions,\n ): Promise<Result<DirSearchResult>> {\n return this.request({ SearchDirectories: { query, ...page(options) } });\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 = await 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(\n query: string,\n options?: SearchOptions & FrecencyOptions,\n ): Promise<Result<MixedSearchResult>> {\n return this.request({ SearchMixed: { query, ...page(options) } });\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 * A grep may take its `timeBudgetMs` beyond the instance's `timeoutMs`\n * before it fails as unanswered.\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 = await 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 = await finder.grep(\"TODO\", {\n * cursor: result.value.nextCursor,\n * });\n * }\n * }\n * ```\n */\n async grep(query: string, options?: GrepOptions & FrecencyOptions): Promise<Result<GrepResult>> {\n const cursor = serviceCursor(options?.cursor);\n if (!cursor.ok) return cursor;\n const serviceOptions = grepOptions(options ?? {}, cursor.value);\n if (!serviceOptions.ok) return serviceOptions;\n const request = {\n query,\n mode: GREP_MODES[options?.mode ?? \"plain\"] ?? GREP_MODES.plain,\n ai_syntax: false,\n options: serviceOptions.value,\n };\n return grepResult(await this.request<GrepReply>({ Grep: request }));\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 = await 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 async multiGrep(options: MultiGrepOptions & FrecencyOptions): Promise<Result<GrepResult>> {\n if (!options.patterns || options.patterns.length === 0) {\n return err(\"patterns array must have at least 1 element\", ErrorCode.invalidRequest);\n }\n\n const cursor = serviceCursor(options.cursor);\n if (!cursor.ok) return cursor;\n const serviceOptions = grepOptions(options, cursor.value);\n if (!serviceOptions.ok) return serviceOptions;\n const request = {\n patterns: options.patterns,\n constraints: options.constraints || null,\n options: serviceOptions.value,\n };\n return grepResult(await this.request<GrepReply>({ MultiGrep: request }));\n }\n\n /**\n * Trigger a rescan of the shared index, coalesced with one already running.\n *\n * This is useful after major file system changes that the\n * background watcher might have missed.\n */\n async scanFiles(): Promise<Result<void>> {\n const done = await this.request<null>(\"Rescan\");\n return done.ok ? { ok: true, value: undefined } : done;\n }\n\n /**\n * Apply changes the program just made to files of the instance's directory, so\n * that a search after the returned Promise resolves sees them, without waiting\n * for the service's watcher or a rescan.\n *\n * Each path, absolute or relative to the instance's directory, is added or\n * updated when it exists and the index does not ignore it, and removed, with\n * any indexed files below it, when it does not exist; the content index\n * follows. A symlink is not added unless the index follows symlinks, as its\n * scan would not add it either. Report files rather than new directories: an index without a\n * watcher does not walk a reported directory. Changes the watcher also\n * delivers are applied once more, harmlessly. While the index scans, the\n * changes are applied when the scan commits.\n *\n * @param paths - Files the program wrote, created, renamed or deleted\n * @returns Nothing once the index holds the changes; `outsideScope` when a\n * path lies outside the instance's directory, which applies none of them\n *\n * @example\n * ```typescript\n * await writeFile(join(root, \"src/new.ts\"), source);\n * await finder.notifyChanged([\"src/new.ts\"]);\n * const found = await finder.fileSearch(\"new.ts\"); // includes src/new.ts\n * ```\n */\n async notifyChanged(paths: string[]): Promise<Result<void>> {\n const done = await this.request<null>({ Changed: { paths } });\n return done.ok ? { ok: true, value: undefined } : done;\n }\n\n /**\n * Check if a scan is currently in progress, or a requested rescan waits for one.\n * An unreachable service reports false; `waitForScan` and `getScanProgress`\n * report its error.\n */\n async isScanning(): Promise<boolean> {\n const scanning = await this.scanning();\n return scanning.ok && scanning.value;\n }\n\n private scanning(): Promise<Result<boolean>> {\n return this.request<boolean>(\"IsScanning\");\n }\n\n /**\n * Get the base path of the file picker (the root directory being indexed).\n */\n getBasePath(): Result<string | null> {\n if (this.handle === null) {\n return err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed);\n }\n return ffiGetBasePath(this.handle);\n }\n\n /**\n * Get the current scan progress.\n *\n * Once the index is ready, `scannedFilesCount` counts the files of the\n * instance's directory. While the shared index scans, it counts the files\n * scanned so far, which may include files outside that directory when the\n * index covers a broader one.\n */\n getScanProgress(): Promise<Result<ScanProgress>> {\n return this.request<ScanProgress>(\"Progress\");\n }\n\n /**\n * Wait for the initial file scan to complete, polling `isScanning`.\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 completed = await finder.waitForScan(10000);\n * if (!completed.ok || !completed.value) {\n * console.warn(\"Scan did not complete in time\");\n * }\n * ```\n */\n async waitForScan(timeoutMs: number = 5000): Promise<Result<boolean>> {\n const deadline = Date.now() + timeoutMs;\n while (true) {\n const scanning = await this.scanning();\n if (!scanning.ok) return scanning;\n if (!scanning.value) return { ok: true, value: true };\n if (Date.now() >= deadline) return { ok: true, value: false };\n await new Promise((resolve) => setTimeout(resolve, READY_POLL_MS));\n }\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. Polls `getScanProgress`.\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 deadline = Date.now() + timeoutMs;\n while (true) {\n const progress = await 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, READY_POLL_MS));\n }\n }\n\n /**\n * Rescan the indexed directory.\n *\n * A session stays on its directory, so another path fails; create a new\n * instance for it.\n *\n * @param newPath - The instance's directory\n */\n reindex(newPath: string): Promise<Result<void>> {\n return this.call((handle) => ffiRestartIndex(handle, newPath));\n }\n\n /**\n * Unavailable: the shared service keeps git status current itself.\n */\n refreshGitStatus(): Result<number> {\n return unavailable(\"refreshGitStatus\");\n }\n\n /**\n * Unavailable: query history has no shared service equivalent.\n */\n trackQuery(_query: string, _selectedFilePath: string): Result<boolean> {\n return unavailable(\"trackQuery\");\n }\n\n /**\n * Unavailable: query history has no shared service equivalent.\n */\n getHistoricalQuery(_offset: number): Result<string | null> {\n return unavailable(\"getHistoricalQuery\");\n }\n\n /**\n * Subscribe to changes of the files under the instance's directory, as the\n * shared service's watcher sees them.\n *\n * `pattern` may be a glob relative to the instance's directory (`./` works),\n * an exact path, or a directory, relative or absolute inside the instance's\n * directory; without one, the subscription covers the whole directory.\n * Ignored files never trigger an event. The callback receives batches on the\n * event loop, with absolute paths. When changes are lost, because the\n * callback runs slower than they come, the index was rescanned, the session\n * moved to another index or the connection to the service broke, one\n * `rescan` event naming the instance's directory replaces them; a broken\n * connection reconnects by itself, starting the service when none runs. A\n * callback falls behind once more than 1024 of its events wait on the event\n * loop. A `failed` event ends a subscription the service can no longer\n * serve, as after its directory was removed, or that a reconnect was refused.\n * Once ready, neither the subscription nor its delivery keeps the process\n * alive, and subscribing holds none of the package's native workers.\n *\n * @returns Once every later change is delivered, the function that ends the\n * subscription: once it returns, the callback is not called again.\n * `unavailable` when the instance was created with `disableWatch`, and the\n * service's reason when it refuses the subscription.\n *\n * @example\n * ```typescript\n * const watched = await finder.watch(\"src/**\", (events) => {\n * for (const event of events) console.log(event.kind, event.path);\n * }, { repository: true });\n * if (watched.ok) watched.value(); // unsubscribe\n * ```\n */\n watch(\n callback: ServiceWatchBatchCallback,\n options?: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>>;\n watch(\n pattern: string,\n callback: ServiceWatchBatchCallback,\n options?: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>>;\n watch(\n first: string | ServiceWatchBatchCallback,\n second?: ServiceWatchBatchCallback | ServiceWatchOptions,\n third?: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>> {\n const [pattern, callback, options] =\n typeof first === \"string\"\n ? [first, second as ServiceWatchBatchCallback, third]\n : [\"\", first, second as ServiceWatchOptions | undefined];\n if (typeof callback !== \"function\") {\n return Promise.resolve(\n err(\"watch needs a callback for the batches\", ErrorCode.invalidRequest),\n );\n }\n const handle = this.handle;\n if (handle === null) {\n return Promise.resolve(err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed));\n }\n if (!this.watching) {\n return Promise.resolve(\n err(\n \"FileFinder.watch is unavailable on an instance created with disableWatch\",\n ErrorCode.unavailable,\n ),\n );\n }\n return this.subscribe(handle, pattern, callback, options ?? {});\n }\n\n private async subscribe(\n handle: NativeHandle,\n pattern: string,\n callback: ServiceWatchBatchCallback,\n options: ServiceWatchOptions,\n ): Promise<Result<WatchUnsubscribe>> {\n const registration = ffiRegisterWatchCallback(handle);\n this.registrations.add(registration);\n const registered = await registration;\n this.registrations.delete(registration);\n if (!registered.ok) return registered;\n if (this.handle !== handle) {\n return err(\"FileFinder instance has been destroyed.\", ErrorCode.destroyed);\n }\n // The library returns at once and reports readiness through the callback, so no\n // native worker waits for the index.\n const started = ffiWatchStart(\n handle,\n pattern,\n options.ignore ?? [],\n options.repository ?? false,\n options.refs ?? false,\n );\n if (!started.ok) return started;\n const watchId = started.value;\n const base = ffiGetBasePath(handle);\n const directory = base.ok && base.value ? base.value : \"\";\n return new Promise((resolve) => {\n let ready = false;\n const end = () => {\n this.watches.delete(watchId);\n deleteWatchListener(watchId);\n if (this.handle === handle) ffiUnwatch(handle, watchId);\n };\n const unsubscribe: WatchUnsubscribe = () => {\n if (this.watches.has(watchId)) end();\n };\n this.watches.set(watchId, resolve);\n setWatchListener(watchId, {\n handle,\n ready: () => {\n ready = true;\n resolve({ ok: true, value: unsubscribe });\n },\n events: (events) => {\n if (ready) callback(events);\n },\n lost: () => {\n if (ready) return callback([{ path: directory, kind: \"rescan\" }]);\n end();\n resolve(err(\"The watch delivery restarted before the subscription was ready\"));\n },\n failed: (error, code) => {\n end();\n if (ready) callback([{ path: directory, kind: \"failed\", error, code }]);\n else resolve(err(error, code));\n },\n });\n });\n }\n\n /**\n * Get health check information, including the instance's index and git\n * state as its service reports them.\n *\n * @param testPath - Optional path to test git repository detection\n */\n healthCheck(testPath?: string): Promise<Result<HealthCheck>> {\n return this.call(\n (handle) =>\n ffiHealthCheck(handle, testPath || \"\") as Promise<Result<HealthCheck>>,\n );\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 an instance: the library version and the git\n * repository of `testPath`, without asking a service.\n *\n * @param testPath - Optional path to test git repository detection\n */\n static healthCheckStatic(testPath?: string): Result<HealthCheck> {\n return ffiHealthCheckStatic(testPath || \"\") as Result<HealthCheck>;\n }\n}\n"],"mappings":"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","names":["readFileSync","triple","readFileSync","require","triple","createRequire","createRequire"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sakiori/fff-node",
3
- "version": "0.11.1-sakiori.20261105.1",
3
+ "version": "1.0.0",
4
4
  "private": false,
5
5
  "description": "Fuzzy file finder for Node.js over the shared fff service - perfect for LLM agent tools",
6
6
  "type": "module",
@@ -22,6 +22,8 @@
22
22
  "build": "npm run build:bundle && npm run build:types",
23
23
  "build:bundle": "tsup src/index.ts src/watch-delivery.ts --format esm --platform node --target node20 --external ffi-rs --sourcemap",
24
24
  "build:types": "tsc -p tsconfig.build.json",
25
+ "api:update": "npm run build:types && api-extractor run --local",
26
+ "api:check": "npm run build:types && api-extractor run",
25
27
  "test": "node --test --test-concurrency=1 test/e2e.mjs test/service.mjs",
26
28
  "typecheck": "tsc --noEmit"
27
29
  },
@@ -65,16 +67,17 @@
65
67
  "ffi-rs": "^1.0.0"
66
68
  },
67
69
  "devDependencies": {
68
- "typescript": "^5.0.0",
70
+ "@microsoft/api-extractor": "7.60.1",
69
71
  "@types/node": "^22.0.0",
70
- "tsup": "^8.5.0"
72
+ "tsup": "^8.5.0",
73
+ "typescript": "5.9.3"
71
74
  },
72
75
  "optionalDependencies": {
73
- "@sakiori/fff-bin-darwin-arm64": "0.11.1-sakiori.20261105.1",
74
- "@sakiori/fff-bin-darwin-x64": "0.11.1-sakiori.20261105.1",
75
- "@sakiori/fff-bin-linux-arm64-gnu": "0.11.1-sakiori.20261105.1",
76
- "@sakiori/fff-bin-linux-arm64-musl": "0.11.1-sakiori.20261105.1",
77
- "@sakiori/fff-bin-linux-x64-gnu": "0.11.1-sakiori.20261105.1",
78
- "@sakiori/fff-bin-linux-x64-musl": "0.11.1-sakiori.20261105.1"
76
+ "@sakiori/fff-bin-darwin-arm64": "1.0.0",
77
+ "@sakiori/fff-bin-darwin-x64": "1.0.0",
78
+ "@sakiori/fff-bin-linux-arm64-gnu": "1.0.0",
79
+ "@sakiori/fff-bin-linux-arm64-musl": "1.0.0",
80
+ "@sakiori/fff-bin-linux-x64-gnu": "1.0.0",
81
+ "@sakiori/fff-bin-linux-x64-musl": "1.0.0"
79
82
  }
80
83
  }