@zipbul/gildash 0.3.0 → 0.4.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
@@ -6,505 +6,501 @@
6
6
  [![CI](https://github.com/zipbul/gildash/actions/workflows/ci.yml/badge.svg)](https://github.com/zipbul/gildash/actions/workflows/ci.yml)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
8
8
 
9
- A **Bun-native** TypeScript code indexer. Extracts symbols, tracks cross-file relationships, and builds a full dependency graph — all stored in a local SQLite database.
9
+ A **Bun-native** TypeScript code intelligence engine.
10
+
11
+ gildash indexes your TypeScript codebase into a local SQLite database, then lets you search symbols, trace cross-file relationships, analyze dependency graphs, and match structural patterns — all with incremental, file-watcher-driven updates.
12
+
13
+ ## 💡 Why gildash?
14
+
15
+ | Problem | How gildash solves it |
16
+ |---------|----------------------|
17
+ | "Which files break if I change this module?" | Directed import graph with transitive impact analysis |
18
+ | "Are there any circular dependencies?" | Cycle detection across the full import graph |
19
+ | "Where is this symbol actually defined?" | Re-export chain resolution to the original source |
20
+ | "Which exports are never imported?" | Dead-export detection across projects |
21
+ | "Find every `console.log(...)` call" | AST-level structural pattern search via [ast-grep](https://ast-grep.github.io/) |
10
22
 
11
23
  <br>
12
24
 
13
- ## Features
25
+ ## Features
14
26
 
15
27
  - **Symbol extraction** — Functions, classes, variables, types, interfaces, enums, and properties extracted via [oxc-parser](https://oxc.rs) AST
16
- - **Relation tracking** — `import`, `calls`, `extends`, `implements` relationships across files
17
- - **Full-text search** — SQLite FTS5-powered symbol name search
18
- - **Dependency graph** — Directed import graph with cycle detection and transitive impact analysis
28
+ - **Relation tracking** — `import`, `re-exports`, `type-references`, `calls`, `extends`, `implements` relationships across files
29
+ - **Full-text search** — SQLite FTS5-powered symbol name search with exact match, regex, and decorator filtering
30
+ - **Dependency graph** — Directed import graph with cycle detection, transitive impact analysis, and internal caching
31
+ - **Structural pattern matching** — AST-level code search via [@ast-grep/napi](https://ast-grep.github.io/)
19
32
  - **Incremental indexing** — `@parcel/watcher`-based file change detection; only re-indexes modified files
33
+ - **Symbol-level diff** — `changedSymbols` in `IndexResult` tracks added/modified/removed symbols per index cycle
20
34
  - **Multi-process safe** — Owner/reader role separation guarantees a single writer per database
35
+ - **Scan-only mode** — `watchMode: false` for one-shot indexing without file watcher overhead
36
+ - **External package indexing** — Index `.d.ts` type declarations from `node_modules`
21
37
 
22
38
  <br>
23
39
 
24
- ## Requirements
40
+ ## 📋 Requirements
25
41
 
26
42
  - **Bun** v1.3 or higher
27
43
  - TypeScript source files: `.ts`, `.mts`, `.cts`
28
44
 
29
45
  <br>
30
46
 
31
- ## Installation
47
+ ## 📦 Installation
32
48
 
33
49
  ```bash
34
50
  bun add @zipbul/gildash
35
51
  ```
36
52
 
53
+ > **Peer dependency** — [`@zipbul/result`](https://www.npmjs.com/package/@zipbul/result) is required. All public methods return `Result<T, GildashError>`.
54
+
37
55
  <br>
38
56
 
39
- ## Quick Start
57
+ ## 🚀 Quick Start
40
58
 
41
59
  ```ts
42
60
  import { Gildash } from '@zipbul/gildash';
61
+ import { isErr } from '@zipbul/result';
43
62
 
63
+ // 1. Open — indexes every .ts file on first run, then watches for changes
44
64
  const ledger = await Gildash.open({
45
65
  projectRoot: '/absolute/path/to/project',
46
66
  });
47
67
 
48
- // Search for exported classes by name
49
- const hits = ledger.searchSymbols({
50
- text: 'UserService',
51
- kind: 'class',
52
- isExported: true,
53
- });
54
-
55
- // Exact name match (not FTS prefix)
56
- const exact = ledger.searchSymbols({ text: 'UserService', exact: true });
57
-
58
- // Find what a file imports
59
- const deps = ledger.getDependencies('src/app.ts');
60
-
61
- // Find everything affected by a change
62
- const affected = await ledger.getAffected(['src/utils.ts']);
63
-
64
- // Detect circular dependencies
65
- if (await ledger.hasCycle()) {
66
- console.warn('Circular dependency detected');
68
+ // 2. Search find symbols by name
69
+ const result = ledger.searchSymbols({ text: 'UserService', kind: 'class' });
70
+ if (!isErr(result)) {
71
+ result.forEach(s => console.log(`${s.name} → ${s.filePath}`));
67
72
  }
68
73
 
69
- // File metadata & symbols
70
- const fileInfo = ledger.getFileInfo('src/app.ts');
71
- const symbols = ledger.getSymbolsByFile('src/app.ts');
72
-
73
- // Cached AST lookup
74
- const ast = ledger.getParsedAst('/absolute/path/to/src/app.ts');
75
-
76
- // Subscribe to index-complete events
77
- const unsubscribe = ledger.onIndexed((result) => {
78
- console.log(`Indexed ${result.indexedFiles} files in ${result.durationMs}ms`);
79
- });
80
-
74
+ // 3. Close release resources
81
75
  await ledger.close();
82
76
  ```
83
77
 
84
- ### Error handling
85
-
86
- All errors extend `GildashError`:
87
-
88
- ```ts
89
- import { Gildash, GildashError, ParseError } from '@zipbul/gildash';
90
-
91
- try {
92
- const ledger = await Gildash.open({ projectRoot: '/path' });
93
- } catch (err) {
94
- if (err instanceof ParseError) {
95
- // AST parsing failure
96
- } else if (err instanceof GildashError) {
97
- // Any gildash error
98
- }
99
- }
100
- ```
78
+ That's it. Project discovery (monorepo-aware), incremental re-indexing, and multi-process safety are handled automatically.
101
79
 
102
80
  <br>
103
81
 
104
- ## API
82
+ ## 📖 Usage Guide
105
83
 
106
- ### `Gildash.open(options)`
84
+ ### Symbol Search
107
85
 
108
- Creates and returns a `Gildash` instance. Performs a full index on first run, then watches for file changes.
86
+ Search indexed symbols with FTS5 full-text, exact match, regex, or decorator filters.
109
87
 
110
88
  ```ts
111
- const ledger = await Gildash.open({
112
- projectRoot: '/absolute/path',
113
- extensions: ['.ts', '.mts', '.cts'],
114
- ignorePatterns: ['dist', 'vendor'],
115
- parseCacheCapacity: 500,
116
- logger: console,
117
- });
118
- ```
89
+ // Full-text search (FTS5 prefix matching)
90
+ const hits = ledger.searchSymbols({ text: 'handle' });
119
91
 
120
- #### options
121
-
122
- | Option | Type | Default | Description |
123
- |--------|------|---------|-------------|
124
- | `projectRoot` | `string` | — | Absolute path to project root **(required)** |
125
- | `extensions` | `string[]` | `['.ts', '.mts', '.cts']` | File extensions to index |
126
- | `ignorePatterns` | `string[]` | `[]` | Glob patterns to exclude |
127
- | `parseCacheCapacity` | `number` | `500` | LRU parse-cache capacity |
128
- | `logger` | `Logger` | `console` | Custom logger (`{ error(...args): void }`) |
129
-
130
- Returns `Promise<Gildash>`
131
-
132
- ---
133
-
134
- ### `ledger.searchSymbols(query)`
135
-
136
- Search symbols by name (FTS5 full-text), kind, file path, and/or export status.
92
+ // Exact name match
93
+ const exact = ledger.searchSymbols({ text: 'UserService', exact: true });
137
94
 
138
- ```ts
139
- // Full-text search
140
- const results = ledger.searchSymbols({ text: 'handleClick' });
95
+ // Regex pattern
96
+ const handlers = ledger.searchSymbols({ regex: '^handle.*Click$' });
141
97
 
142
- // Exact name match (not FTS prefix)
143
- const exact = ledger.searchSymbols({ text: 'UserService', exact: true });
98
+ // Decorator filter
99
+ const injectables = ledger.searchSymbols({ decorator: 'Injectable' });
144
100
 
145
- // Filter by kind + export status
146
- const classes = ledger.searchSymbols({
101
+ // Combine filters
102
+ const exportedClasses = ledger.searchSymbols({
147
103
  kind: 'class',
148
104
  isExported: true,
149
105
  limit: 50,
150
106
  });
151
-
152
- // Scope to a specific file
153
- const inFile = ledger.searchSymbols({
154
- filePath: 'src/services/user.ts',
155
- });
156
- ```
157
-
158
- #### SymbolSearchQuery
159
-
160
- | Field | Type | Description |
161
- |-------|------|-------------|
162
- | `text` | `string?` | FTS5 full-text search query |
163
- | `exact` | `boolean?` | When `true`, `text` is treated as an exact name match (not FTS prefix) |
164
- | `kind` | `SymbolKind?` | `'function'` \| `'method'` \| `'class'` \| `'variable'` \| `'type'` \| `'interface'` \| `'enum'` \| `'property'` |
165
- | `filePath` | `string?` | Filter by file path |
166
- | `isExported` | `boolean?` | Filter by export status |
167
- | `project` | `string?` | Project name (monorepo) |
168
- | `limit` | `number?` | Max results (default: `100`) |
169
-
170
- Returns `SymbolSearchResult[]`
171
-
172
- ```ts
173
- interface SymbolSearchResult {
174
- id: number;
175
- filePath: string;
176
- kind: SymbolKind;
177
- name: string;
178
- span: {
179
- start: { line: number; column: number };
180
- end: { line: number; column: number };
181
- };
182
- isExported: boolean;
183
- signature: string | null;
184
- fingerprint: string | null;
185
- detail: Record<string, unknown>;
186
- }
187
107
  ```
188
108
 
189
- ---
190
-
191
- ### `ledger.searchRelations(query)`
192
-
193
- Search cross-file relationships by source/destination file, symbol name, or relation type.
109
+ Use `searchRelations()` to find cross-file relationships:
194
110
 
195
111
  ```ts
196
- // All imports from a file
197
- const imports = ledger.searchRelations({
198
- srcFilePath: 'src/app.ts',
199
- type: 'imports',
200
- });
201
-
202
- // Find callers of a specific function
203
- const callers = ledger.searchRelations({
204
- dstSymbolName: 'processOrder',
205
- type: 'calls',
206
- });
112
+ const imports = ledger.searchRelations({ srcFilePath: 'src/app.ts', type: 'imports' });
113
+ const callers = ledger.searchRelations({ dstSymbolName: 'processOrder', type: 'calls' });
207
114
  ```
208
115
 
209
- #### RelationSearchQuery
210
-
211
- | Field | Type | Description |
212
- |-------|------|-------------|
213
- | `srcFilePath` | `string?` | Source file path |
214
- | `srcSymbolName` | `string?` | Source symbol name |
215
- | `dstFilePath` | `string?` | Destination file path |
216
- | `dstSymbolName` | `string?` | Destination symbol name |
217
- | `type` | `'imports'` \| `'calls'` \| `'extends'` \| `'implements'`? | Relation type |
218
- | `project` | `string?` | Project name |
219
- | `limit` | `number?` | Max results (default: `500`) |
220
-
221
- Returns `CodeRelation[]`
222
-
223
- ```ts
224
- interface CodeRelation {
225
- type: 'imports' | 'calls' | 'extends' | 'implements';
226
- srcFilePath: string;
227
- srcSymbolName: string | null; // null = module-level
228
- dstFilePath: string;
229
- dstSymbolName: string | null;
230
- metaJson?: string;
231
- }
232
- ```
116
+ For monorepo projects, `searchAllSymbols()` and `searchAllRelations()` search across every discovered project.
233
117
 
234
118
  ---
235
119
 
236
- ### `ledger.getDependencies(filePath, project?)`
120
+ ### Dependency Analysis
237
121
 
238
- Returns files that the given file imports.
122
+ Analyze import graphs, detect cycles, and compute change impact.
239
123
 
240
124
  ```ts
125
+ // Direct imports / importers
241
126
  const deps = ledger.getDependencies('src/app.ts');
242
- // ['src/utils.ts', 'src/config.ts', ...]
243
- ```
127
+ const importers = ledger.getDependents('src/utils.ts');
244
128
 
245
- Returns `string[]`
246
-
247
- ---
129
+ // Transitive impact — which files are affected by a change?
130
+ const affected = await ledger.getAffected(['src/utils.ts']);
248
131
 
249
- ### `ledger.getDependents(filePath, project?)`
132
+ // Full import graph (adjacency list)
133
+ const graph = await ledger.getImportGraph();
250
134
 
251
- Returns files that import the given file.
135
+ // Transitive dependencies (forward BFS)
136
+ const transitive = await ledger.getTransitiveDependencies('src/app.ts');
252
137
 
253
- ```ts
254
- const dependents = ledger.getDependents('src/utils.ts');
255
- // ['src/app.ts', 'src/services/user.ts', ...]
138
+ // Circular dependency detection
139
+ const hasCycles = await ledger.hasCycle();
140
+ const cyclePaths = await ledger.getCyclePaths();
256
141
  ```
257
142
 
258
- Returns `string[]`
259
-
260
143
  ---
261
144
 
262
- ### `ledger.getAffected(changedFiles, project?)`
145
+ ### Code Quality Analysis
263
146
 
264
- Computes the full transitive set of files affected by file changes.
147
+ Detect dead exports, inspect module interfaces, and measure coupling.
265
148
 
266
149
  ```ts
267
- const affected = await ledger.getAffected(['src/utils.ts']);
268
- // ['src/app.ts', 'src/services/user.ts', 'src/main.ts', ...]
269
- ```
150
+ // Dead exports exported symbols never imported anywhere
151
+ const dead = ledger.getDeadExports();
270
152
 
271
- Returns `Promise<string[]>`
153
+ // File statistics — line count, symbol count, size
154
+ const stats = ledger.getFileStats('src/app.ts');
272
155
 
273
- ---
274
-
275
- ### `ledger.hasCycle(project?)`
156
+ // Fan-in / fan-out coupling metrics
157
+ const fan = await ledger.getFanMetrics('src/app.ts');
276
158
 
277
- Detects circular dependencies in the import graph.
159
+ // Module public interface all exported symbols with metadata
160
+ const iface = ledger.getModuleInterface('src/services/user.ts');
278
161
 
279
- ```ts
280
- if (await ledger.hasCycle()) {
281
- console.warn('Circular dependency detected');
282
- }
162
+ // Full symbol detail — members, jsDoc, decorators, type info
163
+ const full = ledger.getFullSymbol('UserService', 'src/services/user.ts');
283
164
  ```
284
165
 
285
- Returns `Promise<boolean>`
286
-
287
166
  ---
288
167
 
289
- ### `ledger.reindex()`
168
+ ### Pattern Matching & Tracing
290
169
 
291
- Forces a full re-index. Only available when the instance holds the owner role.
170
+ Search code by AST structure and trace symbol origins through re-export chains.
292
171
 
293
172
  ```ts
294
- const result = await ledger.reindex();
295
- console.log(`Indexed ${result.indexedFiles} files in ${result.durationMs}ms`);
296
- ```
173
+ // Structural pattern search (ast-grep syntax)
174
+ const logs = await ledger.findPattern('console.log($$$)');
175
+ const hooks = await ledger.findPattern('useState($A)', {
176
+ filePaths: ['src/components/App.tsx'],
177
+ });
297
178
 
298
- Returns `Promise<IndexResult>`
179
+ // Resolve re-export chains — find where a symbol is actually defined
180
+ const resolved = ledger.resolveSymbol('MyComponent', 'src/index.ts');
299
181
 
300
- ```ts
301
- interface IndexResult {
302
- indexedFiles: number;
303
- removedFiles: number;
304
- totalSymbols: number;
305
- totalRelations: number;
306
- durationMs: number;
307
- changedFiles: string[];
308
- deletedFiles: string[];
309
- failedFiles: string[];
310
- }
182
+ // Heritage chain — extends/implements tree traversal
183
+ const tree = await ledger.getHeritageChain('UserService', 'src/services/user.ts');
311
184
  ```
312
185
 
313
- ---
186
+ <br>
314
187
 
315
- ### `ledger.onIndexed(callback)`
188
+ ## 🔧 Scan-only Mode
316
189
 
317
- Subscribes to index-complete events. Returns an unsubscribe function.
190
+ For CI pipelines or one-shot analysis, disable the file watcher:
318
191
 
319
192
  ```ts
320
- const unsubscribe = ledger.onIndexed((result) => {
321
- console.log(`Indexed ${result.indexedFiles} files`);
193
+ const ledger = await Gildash.open({
194
+ projectRoot: '/path/to/project',
195
+ watchMode: false, // no watcher, no heartbeat
322
196
  });
323
197
 
324
- // Later
325
- unsubscribe();
326
- ```
327
-
328
- Returns `() => void`
329
-
330
- ---
331
-
332
- ### `ledger.projects`
198
+ // ... run your queries ...
333
199
 
334
- Detected project boundaries. In a monorepo, multiple projects are discovered automatically from `package.json` files.
335
-
336
- ```ts
337
- const boundaries = ledger.projects;
338
- // → [{ dir: '.', project: 'my-app' }, { dir: 'packages/core', project: '@my/core' }]
200
+ await ledger.close({ cleanup: true }); // delete DB files after use
339
201
  ```
340
202
 
341
- Type: `ProjectBoundary[]`
342
-
343
- ```ts
344
- interface ProjectBoundary {
345
- dir: string;
346
- project: string;
347
- }
348
- ```
349
-
350
- ---
203
+ <br>
351
204
 
352
- ### `ledger.getStats(project?)`
205
+ ## ❌ Error Handling
353
206
 
354
- Returns symbol count statistics.
207
+ Every public method returns `Result<T, GildashError>` from [`@zipbul/result`](https://www.npmjs.com/package/@zipbul/result). Use `isErr()` to branch:
355
208
 
356
209
  ```ts
357
- const stats = ledger.getStats();
358
- // → { symbolCount: 1234, fileCount: 56 }
359
- ```
210
+ import { isErr } from '@zipbul/result';
360
211
 
361
- Returns `SymbolStats`
362
-
363
- ```ts
364
- interface SymbolStats {
365
- symbolCount: number;
366
- fileCount: number;
212
+ const result = ledger.searchSymbols({ text: 'foo' });
213
+ if (isErr(result)) {
214
+ console.error(result.data.type, result.data.message);
215
+ } else {
216
+ console.log(`Found ${result.length} symbols`);
367
217
  }
368
218
  ```
369
219
 
370
- ---
371
-
372
- ### `ledger.parseSource(filePath, sourceText)`
373
-
374
- Parses a TypeScript file and caches the AST internally.
375
-
376
- ```ts
377
- const parsed = ledger.parseSource('src/foo.ts', sourceCode);
378
- ```
379
-
380
- Returns `ParsedFile`
220
+ <br>
381
221
 
382
- ---
222
+ ## ⚙️ Configuration
383
223
 
384
- ### `ledger.extractSymbols(parsed)`
224
+ ### `Gildash.open(options)`
385
225
 
386
- Extracts symbols from a parsed file.
226
+ | Option | Type | Default | Description |
227
+ |--------|------|---------|-------------|
228
+ | `projectRoot` | `string` | — | Absolute path to project root **(required)** |
229
+ | `extensions` | `string[]` | `['.ts', '.mts', '.cts']` | File extensions to index |
230
+ | `ignorePatterns` | `string[]` | `[]` | Glob patterns to exclude |
231
+ | `parseCacheCapacity` | `number` | `500` | LRU parse-cache capacity |
232
+ | `logger` | `Logger` | `console` | Custom logger (`{ error(...args): void }`) |
233
+ | `watchMode` | `boolean` | `true` | `false` disables the file watcher (scan-only mode) |
387
234
 
388
- ```ts
389
- const symbols = ledger.extractSymbols(parsed);
390
- ```
235
+ Returns `Promise<Gildash>` (wrapped in `Result`).
391
236
 
392
- Returns `ExtractedSymbol[]`
237
+ <br>
393
238
 
394
- ---
239
+ ## 🔍 API Reference
240
+
241
+ ### Search
242
+
243
+ | Method | Returns | Description |
244
+ |--------|---------|-------------|
245
+ | `searchSymbols(query)` | `Result<SymbolSearchResult[]>` | FTS5 full-text + exact / regex / decorator filters |
246
+ | `searchRelations(query)` | `Result<CodeRelation[]>` | Filter by file, symbol, or relation type |
247
+ | `searchAllSymbols(query)` | `Result<SymbolSearchResult[]>` | Cross-project symbol search |
248
+ | `searchAllRelations(query)` | `Result<CodeRelation[]>` | Cross-project relation search |
249
+ | `listIndexedFiles(project?)` | `Result<FileRecord[]>` | All indexed files for a project |
250
+ | `getSymbolsByFile(filePath)` | `Result<SymbolSearchResult[]>` | All symbols in a single file |
251
+
252
+ ### Dependency Graph
253
+
254
+ | Method | Returns | Description |
255
+ |--------|---------|-------------|
256
+ | `getDependencies(filePath)` | `Result<string[]>` | Files imported by `filePath` |
257
+ | `getDependents(filePath)` | `Result<string[]>` | Files that import `filePath` |
258
+ | `getAffected(changedFiles)` | `Promise<Result<string[]>>` | Transitive impact set |
259
+ | `hasCycle(project?)` | `Promise<Result<boolean>>` | Circular dependency check |
260
+ | `getCyclePaths(project?)` | `Promise<Result<string[][]>>` | All cycle paths |
261
+ | `getImportGraph(project?)` | `Promise<Result<Map>>` | Full adjacency list |
262
+ | `getTransitiveDependencies(filePath)` | `Promise<Result<string[]>>` | Forward transitive BFS |
263
+
264
+ ### Analysis
265
+
266
+ | Method | Returns | Description |
267
+ |--------|---------|-------------|
268
+ | `getDeadExports(project?, opts?)` | `Result<Array>` | Unused exported symbols |
269
+ | `getFullSymbol(name, filePath)` | `Result<FullSymbol>` | Members, jsDoc, decorators, type info |
270
+ | `getFileStats(filePath)` | `Result<FileStats>` | Line count, symbol count, size |
271
+ | `getFanMetrics(filePath)` | `Promise<Result<FanMetrics>>` | Fan-in / fan-out coupling |
272
+ | `getModuleInterface(filePath)` | `Result<ModuleInterface>` | Public exports with metadata |
273
+ | `getInternalRelations(filePath)` | `Result<CodeRelation[]>` | Intra-file relations |
274
+ | `diffSymbols(before, after)` | `SymbolDiff` | Snapshot diff (added / removed / modified) |
275
+
276
+ ### Advanced
277
+
278
+ | Method | Returns | Description |
279
+ |--------|---------|-------------|
280
+ | `findPattern(pattern, opts?)` | `Promise<Result<PatternMatch[]>>` | AST structural search (ast-grep) |
281
+ | `resolveSymbol(name, filePath)` | `Result<ResolvedSymbol>` | Follow re-export chain to original |
282
+ | `getHeritageChain(name, filePath)` | `Promise<Result<HeritageNode>>` | extends / implements tree |
283
+ | `indexExternalPackages(packages)` | `Promise<Result<IndexResult[]>>` | Index `.d.ts` from `node_modules` |
284
+ | `batchParse(filePaths)` | `Promise<Result<Map>>` | Concurrent multi-file parsing |
285
+
286
+ ### Lifecycle & Low-level
287
+
288
+ | Method | Returns | Description |
289
+ |--------|---------|-------------|
290
+ | `reindex()` | `Promise<Result<IndexResult>>` | Force full re-index (owner only) |
291
+ | `onIndexed(callback)` | `() => void` | Subscribe to index-complete events |
292
+ | `parseSource(filePath, src)` | `Result<ParsedFile>` | Parse & cache a single file |
293
+ | `extractSymbols(parsed)` | `Result<ExtractedSymbol[]>` | Extract symbols from parsed AST |
294
+ | `extractRelations(parsed)` | `Result<CodeRelation[]>` | Extract relations from parsed AST |
295
+ | `getParsedAst(filePath)` | `ParsedFile \| undefined` | Cached AST lookup (read-only) |
296
+ | `getFileInfo(filePath)` | `Result<FileRecord \| null>` | File metadata (hash, mtime, size) |
297
+ | `getStats(project?)` | `Result<SymbolStats>` | Symbol / file count statistics |
298
+ | `projects` | `ProjectBoundary[]` | Discovered project boundaries |
299
+ | `close(opts?)` | `Promise<Result<void>>` | Shutdown (pass `{ cleanup: true }` to delete DB) |
395
300
 
396
- ### `ledger.extractRelations(parsed)`
301
+ <br>
397
302
 
398
- Extracts cross-file relations from a parsed file.
303
+ <details>
304
+ <summary><strong>Type Definitions</strong></summary>
399
305
 
400
306
  ```ts
401
- const relations = ledger.extractRelations(parsed);
402
- ```
403
-
404
- Returns `CodeRelation[]`
405
-
406
- ---
407
-
408
- ### `ledger.getParsedAst(filePath)`
409
-
410
- Retrieves a previously-parsed AST from the internal LRU cache.
307
+ // ── Search ──────────────────────────────────────────────────────────────
308
+
309
+ interface SymbolSearchQuery {
310
+ text?: string; // FTS5 full-text query
311
+ exact?: boolean; // exact name match (not prefix)
312
+ kind?: SymbolKind; // 'function' | 'method' | 'class' | 'variable' | 'type' | 'interface' | 'enum' | 'property'
313
+ filePath?: string;
314
+ isExported?: boolean;
315
+ project?: string;
316
+ limit?: number; // default: 100
317
+ decorator?: string; // e.g. 'Injectable'
318
+ regex?: string; // regex applied to symbol name
319
+ }
411
320
 
412
- Returns `undefined` if the file has not been parsed or was evicted from the cache.
413
- The returned object is shared with the internal cache — treat it as **read-only**.
321
+ interface SymbolSearchResult {
322
+ id: number;
323
+ filePath: string;
324
+ kind: SymbolKind;
325
+ name: string;
326
+ span: { start: { line: number; column: number }; end: { line: number; column: number } };
327
+ isExported: boolean;
328
+ signature: string | null;
329
+ fingerprint: string | null;
330
+ detail: Record<string, unknown>;
331
+ }
414
332
 
415
- ```ts
416
- const ast = ledger.getParsedAst('/absolute/path/to/src/app.ts');
417
- if (ast) {
418
- console.log(ast.program.body.length, 'AST nodes');
333
+ interface RelationSearchQuery {
334
+ srcFilePath?: string;
335
+ srcSymbolName?: string;
336
+ dstFilePath?: string;
337
+ dstSymbolName?: string;
338
+ type?: 'imports' | 'type-references' | 're-exports' | 'calls' | 'extends' | 'implements';
339
+ project?: string;
340
+ limit?: number; // default: 500
419
341
  }
420
- ```
421
342
 
422
- Returns `ParsedFile | undefined`
343
+ interface CodeRelation {
344
+ type: 'imports' | 'type-references' | 're-exports' | 'calls' | 'extends' | 'implements';
345
+ srcFilePath: string;
346
+ srcSymbolName: string | null;
347
+ dstFilePath: string;
348
+ dstSymbolName: string | null;
349
+ metaJson?: string;
350
+ meta?: Record<string, unknown>; // auto-parsed from metaJson
351
+ }
423
352
 
424
- ---
353
+ // ── Analysis ────────────────────────────────────────────────────────────
354
+
355
+ interface FullSymbol extends SymbolSearchResult {
356
+ members?: Array<{
357
+ name: string;
358
+ kind: string;
359
+ type?: string;
360
+ visibility?: string;
361
+ isStatic?: boolean;
362
+ isReadonly?: boolean;
363
+ }>;
364
+ jsDoc?: string;
365
+ parameters?: string;
366
+ returnType?: string;
367
+ heritage?: string[];
368
+ decorators?: Array<{ name: string; arguments?: string }>;
369
+ typeParameters?: string;
370
+ }
425
371
 
426
- ### `ledger.getFileInfo(filePath, project?)`
372
+ interface FileStats {
373
+ filePath: string;
374
+ lineCount: number;
375
+ symbolCount: number;
376
+ relationCount: number;
377
+ size: number;
378
+ exportedSymbolCount: number;
379
+ }
427
380
 
428
- Retrieves metadata for an indexed file, including content hash, mtime, and size.
429
- Returns `null` if the file has not been indexed yet.
381
+ interface FanMetrics {
382
+ filePath: string;
383
+ fanIn: number; // files importing this file
384
+ fanOut: number; // files this file imports
385
+ }
430
386
 
431
- ```ts
432
- const info = ledger.getFileInfo('src/app.ts');
433
- if (!isErr(info) && info !== null) {
434
- console.log(`Hash: ${info.contentHash}, Size: ${info.size}`);
387
+ interface ModuleInterface {
388
+ filePath: string;
389
+ exports: Array<{
390
+ name: string;
391
+ kind: SymbolKind;
392
+ parameters?: string;
393
+ returnType?: string;
394
+ jsDoc?: string;
395
+ }>;
435
396
  }
436
- ```
437
397
 
438
- Returns `Result<FileRecord | null, GildashError>`
398
+ interface SymbolDiff {
399
+ added: SymbolSearchResult[];
400
+ removed: SymbolSearchResult[];
401
+ modified: Array<{ before: SymbolSearchResult; after: SymbolSearchResult }>;
402
+ }
439
403
 
440
- ---
404
+ // ── Advanced ────────────────────────────────────────────────────────────
441
405
 
442
- ### `ledger.getSymbolsByFile(filePath, project?)`
406
+ interface PatternMatch {
407
+ filePath: string;
408
+ startLine: number;
409
+ endLine: number;
410
+ matchedText: string;
411
+ }
443
412
 
444
- Lists all symbols declared in a specific file. Convenience wrapper around `searchSymbols` with a `filePath` filter.
413
+ interface ResolvedSymbol {
414
+ originalName: string;
415
+ originalFilePath: string;
416
+ reExportChain: Array<{ filePath: string; exportedAs: string }>;
417
+ }
445
418
 
446
- ```ts
447
- const symbols = ledger.getSymbolsByFile('src/app.ts');
448
- if (!isErr(symbols)) {
449
- for (const sym of symbols) {
450
- console.log(`${sym.kind}: ${sym.name}`);
451
- }
419
+ interface HeritageNode {
420
+ symbolName: string;
421
+ filePath: string;
422
+ kind?: 'extends' | 'implements';
423
+ children: HeritageNode[];
452
424
  }
453
- ```
454
425
 
455
- Returns `Result<SymbolSearchResult[], GildashError>`
426
+ // ── Indexing ────────────────────────────────────────────────────────────
456
427
 
457
- ---
428
+ interface IndexResult {
429
+ indexedFiles: number;
430
+ removedFiles: number;
431
+ totalSymbols: number;
432
+ totalRelations: number;
433
+ durationMs: number;
434
+ changedFiles: string[];
435
+ deletedFiles: string[];
436
+ failedFiles: string[];
437
+ changedSymbols: {
438
+ added: Array<{ name: string; filePath: string; kind: string }>;
439
+ modified: Array<{ name: string; filePath: string; kind: string }>;
440
+ removed: Array<{ name: string; filePath: string; kind: string }>;
441
+ };
442
+ }
458
443
 
459
- ### `ledger.close()`
444
+ interface FileRecord {
445
+ project: string;
446
+ filePath: string;
447
+ mtimeMs: number;
448
+ size: number;
449
+ contentHash: string;
450
+ updatedAt: string;
451
+ lineCount?: number | null;
452
+ }
460
453
 
461
- Graceful shutdown. Stops the watcher, releases the database, and removes signal handlers.
454
+ // ── Errors ──────────────────────────────────────────────────────────────
462
455
 
463
- ```ts
464
- await ledger.close();
456
+ interface GildashError {
457
+ type: GildashErrorType; // see Error Types table below
458
+ message: string;
459
+ cause?: unknown;
460
+ }
465
461
  ```
466
462
 
467
- Returns `Promise<void>`
463
+ </details>
468
464
 
469
465
  <br>
470
466
 
471
- ## Errors
472
-
473
- All errors extend `GildashError`, which extends `Error`.
467
+ ## ⚠️ Error Types
474
468
 
475
- | Class | When |
476
- |-------|------|
477
- | `GildashError` | Base class for all errors |
478
- | `WatcherError` | File watcher start/stop failure |
479
- | `ParseError` | AST parsing failure |
480
- | `ExtractError` | Symbol/relation extraction failure |
481
- | `IndexError` | Indexing pipeline failure |
482
- | `StoreError` | Database operation failure |
483
- | `SearchError` | Search query failure |
469
+ | Type | When |
470
+ |------|------|
471
+ | `watcher` | File watcher start / stop failure |
472
+ | `parse` | AST parsing failure |
473
+ | `extract` | Symbol / relation extraction failure |
474
+ | `index` | Indexing pipeline failure |
475
+ | `store` | Database operation failure |
476
+ | `search` | Search query failure |
477
+ | `closed` | Operation on a closed instance |
478
+ | `validation` | Invalid input (e.g. missing `node_modules` package) |
479
+ | `close` | Error during shutdown |
484
480
 
485
481
  <br>
486
482
 
487
- ## Architecture
483
+ ## 🏗 Architecture
488
484
 
489
485
  ```
490
486
  Gildash (Facade)
491
- ├── Parser — oxc-parser-based TypeScript AST parser
492
- ├── Extractor — Symbol & relation extraction (imports, calls, heritage)
493
- ├── Store — bun:sqlite + drizzle-orm (files, symbols, relations, FTS5)
494
- ├── Indexer — Change detection → parse → extract → store pipeline
495
- ├── Search — Symbol search, relation search, dependency graph
487
+ ├── Parser — oxc-parser-based TypeScript AST parsing
488
+ ├── Extractor — Symbol & relation extraction (imports, re-exports, type-refs, calls, heritage)
489
+ ├── Store — bun:sqlite + drizzle-orm (files · symbols · relations · FTS5)
490
+ ├── Indexer — File change → parse → extract → store pipeline, symbol-level diff
491
+ ├── Search — FTS + regex + decorator search, relation queries, dependency graph, ast-grep
496
492
  └── Watcher — @parcel/watcher + owner/reader role management
497
493
  ```
498
494
 
499
495
  ### Owner / Reader Pattern
500
496
 
501
- When multiple processes share the same SQLite database, a single-writer guarantee is enforced.
497
+ When multiple processes share the same SQLite database, gildash enforces a single-writer guarantee:
502
498
 
503
499
  - **Owner** — Runs the file watcher, performs indexing, sends a heartbeat every 30 s
504
- - **Reader** — Read-only access; polls owner status every 60 s and self-promotes if the owner goes stale
500
+ - **Reader** — Read-only access; polls owner health every 60 s and self-promotes if the owner goes stale
505
501
 
506
502
  <br>
507
503
 
508
- ## License
504
+ ## 📄 License
509
505
 
510
506
  [MIT](./LICENSE) © [zipbul](https://github.com/zipbul)