@ff-labs/bun 0.1.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.
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Platform detection utilities for downloading the correct binary
3
+ */
4
+
5
+ import { execSync } from "node:child_process";
6
+
7
+ /**
8
+ * Get the platform triple (e.g., "x86_64-unknown-linux-gnu")
9
+ */
10
+ export function getTriple(): string {
11
+ const platform = process.platform;
12
+ const arch = process.arch;
13
+
14
+ let osName: string;
15
+ if (platform === "darwin") {
16
+ osName = "apple-darwin";
17
+ } else if (platform === "linux") {
18
+ osName = detectLinuxLibc();
19
+ } else if (platform === "win32") {
20
+ osName = "pc-windows-msvc";
21
+ } else {
22
+ throw new Error(`Unsupported platform: ${platform}`);
23
+ }
24
+
25
+ const archName = normalizeArch(arch);
26
+ return `${archName}-${osName}`;
27
+ }
28
+
29
+ /**
30
+ * Detect whether we're on musl or glibc Linux
31
+ */
32
+ function detectLinuxLibc(): string {
33
+ try {
34
+ const lddOutput = execSync("ldd --version 2>&1", {
35
+ encoding: "utf-8",
36
+ timeout: 5000,
37
+ });
38
+ if (lddOutput.toLowerCase().includes("musl")) {
39
+ return "unknown-linux-musl";
40
+ }
41
+ } catch {
42
+ // ldd failed, assume glibc
43
+ }
44
+ return "unknown-linux-gnu";
45
+ }
46
+
47
+ /**
48
+ * Normalize architecture name to Rust target format
49
+ */
50
+ function normalizeArch(arch: string): string {
51
+ switch (arch) {
52
+ case "x64":
53
+ case "amd64":
54
+ return "x86_64";
55
+ case "arm64":
56
+ return "aarch64";
57
+ case "arm":
58
+ return "arm";
59
+ default:
60
+ throw new Error(`Unsupported architecture: ${arch}`);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get the library file extension for the current platform
66
+ */
67
+ export function getLibExtension(): "dylib" | "so" | "dll" {
68
+ switch (process.platform) {
69
+ case "darwin":
70
+ return "dylib";
71
+ case "win32":
72
+ return "dll";
73
+ default:
74
+ return "so";
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Get the library filename prefix (empty on Windows)
80
+ */
81
+ export function getLibPrefix(): string {
82
+ return process.platform === "win32" ? "" : "lib";
83
+ }
84
+
85
+ /**
86
+ * Get the full library filename for the current platform
87
+ */
88
+ export function getLibFilename(): string {
89
+ const prefix = getLibPrefix();
90
+ const ext = getLibExtension();
91
+ return `${prefix}fff_c.${ext}`;
92
+ }
package/src/types.ts ADDED
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Result type for all operations - follows the Result pattern
3
+ */
4
+ export type Result<T> =
5
+ | { ok: true; value: T }
6
+ | { ok: false; error: string };
7
+
8
+ /**
9
+ * Helper to create a successful result
10
+ */
11
+ export function ok<T>(value: T): Result<T> {
12
+ return { ok: true, value };
13
+ }
14
+
15
+ /**
16
+ * Helper to create an error result
17
+ */
18
+ export function err<T>(error: string): Result<T> {
19
+ return { ok: false, error };
20
+ }
21
+
22
+ /**
23
+ * Initialization options for the file finder
24
+ */
25
+ export interface InitOptions {
26
+ /** Base directory to index (required) */
27
+ basePath: string;
28
+ /** Path to frecency database (optional, defaults to ~/.fff/frecency.mdb) */
29
+ frecencyDbPath?: string;
30
+ /** Path to query history database (optional, defaults to ~/.fff/history.mdb) */
31
+ historyDbPath?: string;
32
+ /** Use unsafe no-lock mode for databases (optional, defaults to false) */
33
+ useUnsafeNoLock?: boolean;
34
+ /** Skip database initialization entirely (optional, defaults to false) */
35
+ skipDatabases?: boolean;
36
+ }
37
+
38
+ /**
39
+ * Search options for fuzzy file search
40
+ */
41
+ export interface SearchOptions {
42
+ /** Maximum threads for parallel search (0 = auto) */
43
+ maxThreads?: number;
44
+ /** Current file path (for deprioritization in results) */
45
+ currentFile?: string;
46
+ /** Combo boost score multiplier (default: 100) */
47
+ comboBoostMultiplier?: number;
48
+ /** Minimum combo count for boost (default: 3) */
49
+ minComboCount?: number;
50
+ /** Page index for pagination (default: 0) */
51
+ pageIndex?: number;
52
+ /** Page size for pagination (default: 100) */
53
+ pageSize?: number;
54
+ }
55
+
56
+ /**
57
+ * A file item in search results
58
+ */
59
+ export interface FileItem {
60
+ /** Absolute path to the file */
61
+ path: string;
62
+ /** Path relative to the indexed directory */
63
+ relativePath: string;
64
+ /** File name only */
65
+ fileName: string;
66
+ /** File size in bytes */
67
+ size: number;
68
+ /** Last modified timestamp (Unix seconds) */
69
+ modified: number;
70
+ /** Frecency score based on access patterns */
71
+ accessFrecencyScore: number;
72
+ /** Frecency score based on modification time */
73
+ modificationFrecencyScore: number;
74
+ /** Combined frecency score */
75
+ totalFrecencyScore: number;
76
+ /** Git status: 'clean', 'modified', 'untracked', 'staged_new', etc. */
77
+ gitStatus: string;
78
+ }
79
+
80
+ /**
81
+ * Score breakdown for a search result
82
+ */
83
+ export interface Score {
84
+ /** Total combined score */
85
+ total: number;
86
+ /** Base fuzzy match score */
87
+ baseScore: number;
88
+ /** Bonus for filename match */
89
+ filenameBonus: number;
90
+ /** Bonus for special filenames (index.ts, main.rs, etc.) */
91
+ specialFilenameBonus: number;
92
+ /** Boost from frecency */
93
+ frecencyBoost: number;
94
+ /** Penalty for distance in path */
95
+ distancePenalty: number;
96
+ /** Penalty if this is the current file */
97
+ currentFilePenalty: number;
98
+ /** Boost from query history combo matching */
99
+ comboMatchBoost: number;
100
+ /** Whether this was an exact match */
101
+ exactMatch: boolean;
102
+ /** Type of match: 'fuzzy', 'exact', 'prefix', etc. */
103
+ matchType: string;
104
+ }
105
+
106
+ /**
107
+ * Location in file (from query like "file.ts:42")
108
+ */
109
+ export type Location =
110
+ | { type: "line"; line: number }
111
+ | { type: "position"; line: number; col: number }
112
+ | {
113
+ type: "range";
114
+ start: { line: number; col: number };
115
+ end: { line: number; col: number };
116
+ };
117
+
118
+ /**
119
+ * Search result from fuzzy file search
120
+ */
121
+ export interface SearchResult {
122
+ /** Matched file items */
123
+ items: FileItem[];
124
+ /** Corresponding scores for each item */
125
+ scores: Score[];
126
+ /** Total number of files that matched */
127
+ totalMatched: number;
128
+ /** Total number of indexed files */
129
+ totalFiles: number;
130
+ /** Location parsed from query (e.g., "file.ts:42:10") */
131
+ location?: Location;
132
+ }
133
+
134
+ /**
135
+ * Scan progress information
136
+ */
137
+ export interface ScanProgress {
138
+ /** Number of files scanned so far */
139
+ scannedFilesCount: number;
140
+ /** Whether a scan is currently in progress */
141
+ isScanning: boolean;
142
+ }
143
+
144
+ /**
145
+ * Database health information
146
+ */
147
+ export interface DbHealth {
148
+ /** Path to the database */
149
+ path: string;
150
+ /** Size of the database on disk in bytes */
151
+ diskSize: number;
152
+ }
153
+
154
+ /**
155
+ * Health check result
156
+ */
157
+ export interface HealthCheck {
158
+ /** Library version */
159
+ version: string;
160
+ /** Git integration status */
161
+ git: {
162
+ /** Whether git2 library is available */
163
+ available: boolean;
164
+ /** Whether a git repository was found */
165
+ repositoryFound: boolean;
166
+ /** Git working directory path */
167
+ workdir?: string;
168
+ /** libgit2 version string */
169
+ libgit2Version: string;
170
+ /** Error message if git detection failed */
171
+ error?: string;
172
+ };
173
+ /** File picker status */
174
+ filePicker: {
175
+ /** Whether the file picker is initialized */
176
+ initialized: boolean;
177
+ /** Base path being indexed */
178
+ basePath?: string;
179
+ /** Whether a scan is in progress */
180
+ isScanning?: boolean;
181
+ /** Number of indexed files */
182
+ indexedFiles?: number;
183
+ /** Error message if there's an issue */
184
+ error?: string;
185
+ };
186
+ /** Frecency database status */
187
+ frecency: {
188
+ /** Whether frecency tracking is initialized */
189
+ initialized: boolean;
190
+ /** Database health information */
191
+ dbHealthcheck?: DbHealth;
192
+ /** Error message if there's an issue */
193
+ error?: string;
194
+ };
195
+ /** Query tracker status */
196
+ queryTracker: {
197
+ /** Whether query tracking is initialized */
198
+ initialized: boolean;
199
+ /** Database health information */
200
+ dbHealthcheck?: DbHealth;
201
+ /** Error message if there's an issue */
202
+ error?: string;
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Internal: Options format sent to Rust FFI
208
+ * @internal
209
+ */
210
+ export interface InitOptionsInternal {
211
+ base_path: string;
212
+ frecency_db_path?: string;
213
+ history_db_path?: string;
214
+ use_unsafe_no_lock: boolean;
215
+ skip_databases: boolean;
216
+ }
217
+
218
+ /**
219
+ * Internal: Search options format sent to Rust FFI
220
+ * @internal
221
+ */
222
+ export interface SearchOptionsInternal {
223
+ max_threads?: number;
224
+ current_file?: string;
225
+ combo_boost_multiplier?: number;
226
+ min_combo_count?: number;
227
+ page_index?: number;
228
+ page_size?: number;
229
+ }
230
+
231
+ /**
232
+ * Convert public InitOptions to internal format
233
+ * @internal
234
+ */
235
+ export function toInternalInitOptions(opts: InitOptions): InitOptionsInternal {
236
+ return {
237
+ base_path: opts.basePath,
238
+ frecency_db_path: opts.frecencyDbPath,
239
+ history_db_path: opts.historyDbPath,
240
+ use_unsafe_no_lock: opts.useUnsafeNoLock ?? false,
241
+ skip_databases: opts.skipDatabases ?? false,
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Convert public SearchOptions to internal format
247
+ * @internal
248
+ */
249
+ export function toInternalSearchOptions(
250
+ opts?: SearchOptions
251
+ ): SearchOptionsInternal {
252
+ return {
253
+ max_threads: opts?.maxThreads,
254
+ current_file: opts?.currentFile,
255
+ combo_boost_multiplier: opts?.comboBoostMultiplier,
256
+ min_combo_count: opts?.minComboCount,
257
+ page_index: opts?.pageIndex,
258
+ page_size: opts?.pageSize,
259
+ };
260
+ }