From 8486bbcdc1604ccb41f8ef52839dac2dc29cb8a5 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 11 Aug 2026 15:22:03 -0400 Subject: [PATCH] refactor(core): unify fff runtime adapters (#41827) --- packages/core/src/filesystem/fff.bun.ts | 101 ++--------------------- packages/core/src/filesystem/fff.node.ts | 100 ++-------------------- packages/core/src/filesystem/fff.ts | 66 +++++++++++++++ 3 files changed, 79 insertions(+), 188 deletions(-) create mode 100644 packages/core/src/filesystem/fff.ts diff --git a/packages/core/src/filesystem/fff.bun.ts b/packages/core/src/filesystem/fff.bun.ts index 085cc8de2f..48a6246f15 100644 --- a/packages/core/src/filesystem/fff.bun.ts +++ b/packages/core/src/filesystem/fff.bun.ts @@ -1,102 +1,15 @@ -import { - FileFinder, - type DirItem, - type DirSearchResult, - type FileItem, - type InitOptions, - type MixedItem, - type MixedSearchResult, - type SearchResult, -} from "@ff-labs/fff-bun" +import { FileFinder } from "@ff-labs/fff-bun" +import { bind } from "./fff" + +export type { Directory, DirSearch, File, Init, Mixed, MixedSearch, Picker, Result, Search } from "./fff" declare global { const FFF_LIBC: "gnu" | "musl" } -export type Result = { ok: true; value: T } | { ok: false; error: string } +const adapter = bind(FileFinder) -export type Init = InitOptions - -export interface Search { - items: FileItem[] - scores: SearchResult["scores"] - totalMatched: number - totalFiles: number -} - -export interface DirSearch { - items: DirItem[] - scores: DirSearchResult["scores"] - totalMatched: number - totalDirs: number -} - -export interface MixedSearch { - items: MixedItem[] - scores: MixedSearchResult["scores"] - totalMatched: number - totalFiles: number - totalDirs: number -} - -export type File = FileItem -export type Directory = DirItem -export type Mixed = MixedItem -export interface Picker { - destroy(): void - isScanning(): boolean - waitForScan(timeoutMs?: number): Promise> - refreshGitStatus(): Result - fileSearch( - query: string, - opts?: { - currentFile?: string - pageIndex?: number - pageSize?: number - }, - ): Result - directorySearch( - query: string, - opts?: { - currentFile?: string - pageIndex?: number - pageSize?: number - }, - ): Result - mixedSearch( - query: string, - opts?: { - currentFile?: string - pageIndex?: number - pageSize?: number - }, - ): Result - trackQuery(query: string, file: string): Result - getHistoricalQuery(offset: number): Result -} - -export function available() { - return FileFinder.isAvailable() -} - -export function create(opts: Init): Result { - const made = FileFinder.create(opts) - if (!made.ok) return made - const pick = made.value - return { - ok: true, - value: { - destroy: () => pick.destroy(), - isScanning: () => pick.isScanning(), - waitForScan: (timeoutMs) => pick.waitForScan(timeoutMs), - refreshGitStatus: () => pick.refreshGitStatus(), - fileSearch: (query, next) => pick.fileSearch(query, next), - directorySearch: (query, next) => pick.directorySearch(query, next), - mixedSearch: (query, next) => pick.mixedSearch(query, next), - trackQuery: (query, file) => pick.trackQuery(query, file), - getHistoricalQuery: (offset) => pick.getHistoricalQuery(offset), - }, - } -} +export const available = adapter.available +export const create = adapter.create export * as Fff from "./fff.bun" diff --git a/packages/core/src/filesystem/fff.node.ts b/packages/core/src/filesystem/fff.node.ts index 9f14876e75..07bb00743a 100644 --- a/packages/core/src/filesystem/fff.node.ts +++ b/packages/core/src/filesystem/fff.node.ts @@ -1,100 +1,12 @@ -import type { - DirItem, - DirSearchResult, - FileItem, - InitOptions, - MixedItem, - MixedSearchResult, - SearchResult, -} from "@ff-labs/fff-node" +import { bind } from "./fff" + +export type { Directory, DirSearch, File, Init, Mixed, MixedSearch, Picker, Result, Search } from "./fff" const { FileFinder } = await import("@ff-labs/fff-node").catch(() => ({ FileFinder: undefined })) -export type Result = { ok: true; value: T } | { ok: false; error: string } +const adapter = bind(FileFinder, "fff unavailable on node runtime") -export type Init = InitOptions - -export interface Search { - items: FileItem[] - scores: SearchResult["scores"] - totalMatched: number - totalFiles: number -} - -export interface DirSearch { - items: DirItem[] - scores: DirSearchResult["scores"] - totalMatched: number - totalDirs: number -} - -export interface MixedSearch { - items: MixedItem[] - scores: MixedSearchResult["scores"] - totalMatched: number - totalFiles: number - totalDirs: number -} - -export type File = FileItem -export type Directory = DirItem -export type Mixed = MixedItem -export interface Picker { - destroy(): void - isScanning(): boolean - waitForScan(timeoutMs?: number): Promise> - refreshGitStatus(): Result - fileSearch( - query: string, - opts?: { - currentFile?: string - pageIndex?: number - pageSize?: number - }, - ): Result - directorySearch( - query: string, - opts?: { - currentFile?: string - pageIndex?: number - pageSize?: number - }, - ): Result - mixedSearch( - query: string, - opts?: { - currentFile?: string - pageIndex?: number - pageSize?: number - }, - ): Result - trackQuery(query: string, file: string): Result - getHistoricalQuery(offset: number): Result -} - -export function available() { - return FileFinder?.isAvailable() ?? false -} - -export function create(opts: Init): Result { - if (!FileFinder) return { ok: false, error: "fff unavailable on node runtime" } - const made = FileFinder.create(opts) - if (!made.ok) return made - const pick = made.value - return { - ok: true, - value: { - destroy: () => pick.destroy(), - isScanning: () => pick.isScanning(), - waitForScan: (timeoutMs) => pick.waitForScan(timeoutMs), - refreshGitStatus: () => pick.refreshGitStatus(), - fileSearch: (query, next) => pick.fileSearch(query, next), - directorySearch: (query, next) => pick.directorySearch(query, next), - mixedSearch: (query, next) => pick.mixedSearch(query, next), - trackQuery: (query, file) => pick.trackQuery(query, file), - getHistoricalQuery: (offset) => pick.getHistoricalQuery(offset), - }, - } -} +export const available = adapter.available +export const create = adapter.create export * as Fff from "./fff.node" diff --git a/packages/core/src/filesystem/fff.ts b/packages/core/src/filesystem/fff.ts new file mode 100644 index 0000000000..daf6ea2abb --- /dev/null +++ b/packages/core/src/filesystem/fff.ts @@ -0,0 +1,66 @@ +export type Result = { ok: true; value: T } | { ok: false; error: string } + +export interface Init { + basePath: string + aiMode?: boolean + disableMmapCache?: boolean + disableContentIndexing?: boolean +} + +export interface SearchOptions { + currentFile?: string + pageIndex?: number + pageSize?: number +} + +export interface File { + relativePath: string +} + +export interface Directory { + relativePath: string +} + +export type Mixed = { type: "file"; item: File } | { type: "directory"; item: Directory } + +export interface Score { + total: number +} + +export interface Search { + items: File[] + scores: Score[] +} + +export interface DirSearch { + items: Directory[] + scores: Score[] +} + +export interface MixedSearch { + items: Mixed[] + scores: Score[] +} + +export interface Picker { + destroy(): void + fileSearch(query: string, options?: SearchOptions): Result + directorySearch(query: string, options?: SearchOptions): Result + mixedSearch(query: string, options?: SearchOptions): Result +} + +export interface Backend { + isAvailable(): boolean + create(options: Init): Result +} + +export function bind(backend: Backend | undefined, unavailable = "fff unavailable") { + return { + available: () => backend?.isAvailable() ?? false, + create: (options: Init): Result => + backend?.create(options) ?? { + ok: false, + error: unavailable, + }, + } +}