@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/LICENSE +1 -1
- package/README.ko.md +234 -290
- package/README.md +337 -341
- package/dist/index.js +6 -4
- package/dist/index.js.map +15 -14
- package/dist/migrations/0001_add_line_count.sql +1 -0
- package/dist/migrations/meta/_journal.json +7 -0
- package/dist/src/extractor/types.d.ts +5 -1
- package/dist/src/gildash.d.ts +379 -3
- package/dist/src/index.d.ts +3 -1
- package/dist/src/indexer/index-coordinator.d.ts +21 -0
- package/dist/src/search/dependency-graph.d.ts +28 -0
- package/dist/src/search/index.d.ts +2 -0
- package/dist/src/search/pattern-search.d.ts +30 -0
- package/dist/src/search/symbol-search.d.ts +12 -0
- package/dist/src/store/repositories/file.repository.d.ts +2 -0
- package/dist/src/store/repositories/symbol.repository.d.ts +2 -0
- package/dist/src/store/schema.d.ts +17 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,505 +6,501 @@
|
|
|
6
6
|
[](https://github.com/zipbul/gildash/actions/workflows/ci.yml)
|
|
7
7
|
[](./LICENSE)
|
|
8
8
|
|
|
9
|
-
A **Bun-native** TypeScript code
|
|
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
|
|
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
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
##
|
|
82
|
+
## 📖 Usage Guide
|
|
105
83
|
|
|
106
|
-
###
|
|
84
|
+
### Symbol Search
|
|
107
85
|
|
|
108
|
-
|
|
86
|
+
Search indexed symbols with FTS5 full-text, exact match, regex, or decorator filters.
|
|
109
87
|
|
|
110
88
|
```ts
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
const results = ledger.searchSymbols({ text: 'handleClick' });
|
|
95
|
+
// Regex pattern
|
|
96
|
+
const handlers = ledger.searchSymbols({ regex: '^handle.*Click$' });
|
|
141
97
|
|
|
142
|
-
//
|
|
143
|
-
const
|
|
98
|
+
// Decorator filter
|
|
99
|
+
const injectables = ledger.searchSymbols({ decorator: 'Injectable' });
|
|
144
100
|
|
|
145
|
-
//
|
|
146
|
-
const
|
|
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
|
-
|
|
197
|
-
const
|
|
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
|
-
|
|
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
|
-
###
|
|
120
|
+
### Dependency Analysis
|
|
237
121
|
|
|
238
|
-
|
|
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
|
-
|
|
243
|
-
```
|
|
127
|
+
const importers = ledger.getDependents('src/utils.ts');
|
|
244
128
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
---
|
|
129
|
+
// Transitive impact — which files are affected by a change?
|
|
130
|
+
const affected = await ledger.getAffected(['src/utils.ts']);
|
|
248
131
|
|
|
249
|
-
|
|
132
|
+
// Full import graph (adjacency list)
|
|
133
|
+
const graph = await ledger.getImportGraph();
|
|
250
134
|
|
|
251
|
-
|
|
135
|
+
// Transitive dependencies (forward BFS)
|
|
136
|
+
const transitive = await ledger.getTransitiveDependencies('src/app.ts');
|
|
252
137
|
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
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
|
-
###
|
|
145
|
+
### Code Quality Analysis
|
|
263
146
|
|
|
264
|
-
|
|
147
|
+
Detect dead exports, inspect module interfaces, and measure coupling.
|
|
265
148
|
|
|
266
149
|
```ts
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
```
|
|
150
|
+
// Dead exports — exported symbols never imported anywhere
|
|
151
|
+
const dead = ledger.getDeadExports();
|
|
270
152
|
|
|
271
|
-
|
|
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
|
-
|
|
159
|
+
// Module public interface — all exported symbols with metadata
|
|
160
|
+
const iface = ledger.getModuleInterface('src/services/user.ts');
|
|
278
161
|
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
###
|
|
168
|
+
### Pattern Matching & Tracing
|
|
290
169
|
|
|
291
|
-
|
|
170
|
+
Search code by AST structure and trace symbol origins through re-export chains.
|
|
292
171
|
|
|
293
172
|
```ts
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
179
|
+
// Resolve re-export chains — find where a symbol is actually defined
|
|
180
|
+
const resolved = ledger.resolveSymbol('MyComponent', 'src/index.ts');
|
|
299
181
|
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
188
|
+
## 🔧 Scan-only Mode
|
|
316
189
|
|
|
317
|
-
|
|
190
|
+
For CI pipelines or one-shot analysis, disable the file watcher:
|
|
318
191
|
|
|
319
192
|
```ts
|
|
320
|
-
const
|
|
321
|
-
|
|
193
|
+
const ledger = await Gildash.open({
|
|
194
|
+
projectRoot: '/path/to/project',
|
|
195
|
+
watchMode: false, // no watcher, no heartbeat
|
|
322
196
|
});
|
|
323
197
|
|
|
324
|
-
//
|
|
325
|
-
unsubscribe();
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
Returns `() => void`
|
|
329
|
-
|
|
330
|
-
---
|
|
331
|
-
|
|
332
|
-
### `ledger.projects`
|
|
198
|
+
// ... run your queries ...
|
|
333
199
|
|
|
334
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
```ts
|
|
344
|
-
interface ProjectBoundary {
|
|
345
|
-
dir: string;
|
|
346
|
-
project: string;
|
|
347
|
-
}
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
---
|
|
203
|
+
<br>
|
|
351
204
|
|
|
352
|
-
|
|
205
|
+
## ❌ Error Handling
|
|
353
206
|
|
|
354
|
-
|
|
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
|
-
|
|
358
|
-
// → { symbolCount: 1234, fileCount: 56 }
|
|
359
|
-
```
|
|
210
|
+
import { isErr } from '@zipbul/result';
|
|
360
211
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
### `
|
|
224
|
+
### `Gildash.open(options)`
|
|
385
225
|
|
|
386
|
-
|
|
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
|
-
|
|
389
|
-
const symbols = ledger.extractSymbols(parsed);
|
|
390
|
-
```
|
|
235
|
+
Returns `Promise<Gildash>` (wrapped in `Result`).
|
|
391
236
|
|
|
392
|
-
|
|
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
|
-
|
|
301
|
+
<br>
|
|
397
302
|
|
|
398
|
-
|
|
303
|
+
<details>
|
|
304
|
+
<summary><strong>Type Definitions</strong></summary>
|
|
399
305
|
|
|
400
306
|
```ts
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
413
|
-
|
|
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
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
429
|
-
|
|
381
|
+
interface FanMetrics {
|
|
382
|
+
filePath: string;
|
|
383
|
+
fanIn: number; // files importing this file
|
|
384
|
+
fanOut: number; // files this file imports
|
|
385
|
+
}
|
|
430
386
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
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
|
-
|
|
406
|
+
interface PatternMatch {
|
|
407
|
+
filePath: string;
|
|
408
|
+
startLine: number;
|
|
409
|
+
endLine: number;
|
|
410
|
+
matchedText: string;
|
|
411
|
+
}
|
|
443
412
|
|
|
444
|
-
|
|
413
|
+
interface ResolvedSymbol {
|
|
414
|
+
originalName: string;
|
|
415
|
+
originalFilePath: string;
|
|
416
|
+
reExportChain: Array<{ filePath: string; exportedAs: string }>;
|
|
417
|
+
}
|
|
445
418
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
419
|
+
interface HeritageNode {
|
|
420
|
+
symbolName: string;
|
|
421
|
+
filePath: string;
|
|
422
|
+
kind?: 'extends' | 'implements';
|
|
423
|
+
children: HeritageNode[];
|
|
452
424
|
}
|
|
453
|
-
```
|
|
454
425
|
|
|
455
|
-
|
|
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
|
-
|
|
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
|
-
|
|
454
|
+
// ── Errors ──────────────────────────────────────────────────────────────
|
|
462
455
|
|
|
463
|
-
|
|
464
|
-
|
|
456
|
+
interface GildashError {
|
|
457
|
+
type: GildashErrorType; // see Error Types table below
|
|
458
|
+
message: string;
|
|
459
|
+
cause?: unknown;
|
|
460
|
+
}
|
|
465
461
|
```
|
|
466
462
|
|
|
467
|
-
|
|
463
|
+
</details>
|
|
468
464
|
|
|
469
465
|
<br>
|
|
470
466
|
|
|
471
|
-
##
|
|
472
|
-
|
|
473
|
-
All errors extend `GildashError`, which extends `Error`.
|
|
467
|
+
## ⚠️ Error Types
|
|
474
468
|
|
|
475
|
-
|
|
|
476
|
-
|
|
477
|
-
| `
|
|
478
|
-
| `
|
|
479
|
-
| `
|
|
480
|
-
| `
|
|
481
|
-
| `
|
|
482
|
-
| `
|
|
483
|
-
| `
|
|
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
|
|
492
|
-
├── Extractor — Symbol & relation extraction (imports, calls, heritage)
|
|
493
|
-
├── Store — bun:sqlite + drizzle-orm (files
|
|
494
|
-
├── Indexer —
|
|
495
|
-
├── Search —
|
|
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
|
|
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
|
|
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)
|