@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.ko.md CHANGED
@@ -6,19 +6,34 @@
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
- **Bun 네이티브** TypeScript 코드 인덱서.
10
- 심볼 추출, 파일 간 관계 추적, 의존성 그래프 구축을 하나의 로컬 SQLite 데이터베이스로 제공합니다.
9
+ **Bun 네이티브** TypeScript 코드 인텔리전스 엔진.
10
+
11
+ gildash는 TypeScript 코드베이스를 로컬 SQLite 데이터베이스에 인덱싱하여, 심볼 검색 · 파일 간 관계 추적 · 의존성 그래프 분석 · 구조적 패턴 매칭을 제공합니다. 파일 변경을 감시하며 증분(incremental) 재인덱싱을 자동으로 수행합니다.
12
+
13
+ ## 💡 왜 gildash인가?
14
+
15
+ | 문제 | gildash의 해결 방식 |
16
+ |------|---------------------|
17
+ | "이 모듈을 바꾸면 어디가 깨지지?" | 방향 import 그래프 + 전이적(transitive) 영향도 분석 |
18
+ | "순환 의존성이 있나?" | 전체 import 그래프에서 순환 감지 |
19
+ | "이 심볼이 실제로 어디서 정의된 거지?" | re-export 체인을 따라가 원본 소스까지 추적 |
20
+ | "사용되지 않는 export는?" | 프로젝트 전체에서 미사용 export 탐지 |
21
+ | "모든 `console.log(...)` 호출을 찾아줘" | [ast-grep](https://ast-grep.github.io/) 기반 AST 레벨 구조적 패턴 검색 |
11
22
 
12
23
  <br>
13
24
 
14
25
  ## ✨ 주요 기능
15
26
 
16
- - **심볼 추출** — 함수, 클래스, 변수, 타입, 인터페이스, 열거형, 프로퍼티를 AST 수준에서 추출
17
- - **관계 분석** — `import`, `calls`, `extends`, `implements` 관계를 파일 간에 추적
18
- - **전문 검색** — SQLite FTS5 기반 심볼 이름 전문 검색
19
- - **의존성 그래프** — 방향 import 그래프로 순환 감지 전이적(transitive) 영향도 분석
27
+ - **심볼 추출** — 함수, 클래스, 변수, 타입, 인터페이스, 열거형, 프로퍼티를 [oxc-parser](https://oxc.rs) AST 수준에서 추출
28
+ - **관계 분석** — `import`, `re-exports`, `type-references`, `calls`, `extends`, `implements` 관계를 파일 간에 추적
29
+ - **전문 검색** — SQLite FTS5 기반 심볼 이름 전문 검색 + 정확 일치(exact), 정규식(regex), 데코레이터(decorator) 필터
30
+ - **의존성 그래프** — 방향 import 그래프로 순환 감지, 전이적(transitive) 영향도 분석, 내부 캐싱
31
+ - **구조적 패턴 매칭** — [@ast-grep/napi](https://ast-grep.github.io/) 기반 AST 레벨 코드 검색
20
32
  - **증분 인덱싱** — `@parcel/watcher` 기반 파일 변경 감지, 변경된 파일만 재인덱싱
33
+ - **심볼 레벨 diff** — `IndexResult`의 `changedSymbols`로 인덱싱 사이클 당 추가/수정/삭제된 심볼 추적
21
34
  - **멀티 프로세스 안전** — owner/reader 역할 분리로 단일 writer 보장
35
+ - **스캔 전용 모드** — `watchMode: false`로 파일 워처 없이 1회성 인덱싱
36
+ - **외부 패키지 인덱싱** — `node_modules`의 `.d.ts` 타입 선언 인덱싱
22
37
 
23
38
  <br>
24
39
 
@@ -35,396 +50,325 @@
35
50
  bun add @zipbul/gildash
36
51
  ```
37
52
 
53
+ > **피어 의존성** — [`@zipbul/result`](https://www.npmjs.com/package/@zipbul/result)가 필요합니다. 모든 public 메서드는 `Result<T, GildashError>`를 반환합니다.
54
+
38
55
  <br>
39
56
 
40
57
  ## 🚀 빠른 시작
41
58
 
42
59
  ```ts
43
60
  import { Gildash } from '@zipbul/gildash';
61
+ import { isErr } from '@zipbul/result';
44
62
 
45
- // 인덱서 열기 — 최초 실행 시 전체 인덱싱 자동 수행, 이후 파일 변경을 감시
63
+ // 1. 열기 — 최초 실행 시 전체 .ts 파일 인덱싱, 이후 파일 변경 감시
46
64
  const ledger = await Gildash.open({
47
65
  projectRoot: '/absolute/path/to/project',
48
66
  });
49
67
 
50
- // 심볼 검색
51
- const hits = ledger.searchSymbols({ text: 'UserService', kind: 'class' });
52
-
53
- // 정확한 이름 매칭
54
- const exact = ledger.searchSymbols({ text: 'UserService', exact: true });
55
-
56
- // 의존성 그래프 조회
57
- const deps = ledger.getDependencies('src/app.ts');
58
- const affected = await ledger.getAffected(['src/utils.ts']);
59
- const cyclic = await ledger.hasCycle();
60
-
61
- // 파일 정보 및 심볼 조회
62
- const fileInfo = ledger.getFileInfo('src/app.ts');
63
- const symbols = ledger.getSymbolsByFile('src/app.ts');
64
-
65
- // 캐시된 AST 조회
66
- const ast = ledger.getParsedAst('/absolute/path/to/src/app.ts');
68
+ // 2. 검색 — 이름으로 심볼 찾기
69
+ const result = ledger.searchSymbols({ text: 'UserService', kind: 'class' });
70
+ if (!isErr(result)) {
71
+ result.forEach(s => console.log(`${s.name} → ${s.filePath}`));
72
+ }
67
73
 
74
+ // 3. 종료 — 리소스 해제
68
75
  await ledger.close();
69
76
  ```
70
77
 
71
- <br>
72
-
73
- ## 🔍 API 개요
74
-
75
- | 메서드 | 반환 타입 | 설명 |
76
- |--------|-----------|------|
77
- | `searchSymbols(query)` | `SymbolSearchResult[]` | FTS5 전문 검색 + 필터 조합. `exact` 옵션 지원 |
78
- | `searchRelations(query)` | `CodeRelation[]` | 파일/심볼/관계 유형 필터 |
79
- | `getDependencies(filePath, project?)` | `string[]` | 이 파일이 import하는 파일 목록 |
80
- | `getDependents(filePath, project?)` | `string[]` | 이 파일을 import하는 파일 목록 |
81
- | `getAffected(changedFiles, project?)` | `Promise<string[]>` | 변경 파일의 전이적 영향 범위 |
82
- | `hasCycle(project?)` | `Promise<boolean>` | 순환 의존성 감지 |
83
- | `reindex()` | `Promise<IndexResult>` | 강제 전체 재인덱싱 |
84
- | `onIndexed(callback)` | `() => void` | 인덱싱 완료 이벤트 구독 |
85
- | `parseSource(filePath, src)` | `ParsedFile` | 파일 파싱 후 AST 캐시 |
86
- | `extractSymbols(parsed)` | `ExtractedSymbol[]` | 파싱된 파일에서 심볼 추출 |
87
- | `extractRelations(parsed)` | `CodeRelation[]` | 파싱된 파일에서 관계 추출 |
88
- | `getParsedAst(filePath)` | `ParsedFile \| undefined` | 캐시된 AST 조회 |
89
- | `getFileInfo(filePath, project?)` | `FileRecord \| null` | 인덱싱된 파일 메타데이터 조회 |
90
- | `getSymbolsByFile(filePath, project?)` | `SymbolSearchResult[]` | 특정 파일의 모든 심볼 조회 |
91
- | `projects` | `ProjectBoundary[]` | 감지된 프로젝트 경계 (모노레포) |
92
- | `getStats(project?)` | `SymbolStats` | 심볼 통계 |
93
- | `close()` | `Promise<void>` | 인덱서 종료 |
78
+ 프로젝트 탐색(모노레포 지원), 증분 재인덱싱, 멀티 프로세스 안전 모두 자동으로 처리됩니다.
94
79
 
95
80
  <br>
96
81
 
97
- ## ⚙️ API 레퍼런스
82
+ ## 📖 사용 가이드
98
83
 
99
- ### `Gildash.open(options)`
84
+ ### 심볼 검색
100
85
 
101
- 인덱서 인스턴스를 생성합니다. 최초 실행 전체 인덱싱을 수행하고, 이후 파일 변경을 감시합니다.
86
+ 인덱싱된 심볼을 FTS5 전문 검색, 정확 일치, 정규식, 데코레이터 필터로 검색합니다.
102
87
 
103
88
  ```ts
104
- const ledger = await Gildash.open({
105
- projectRoot: '/absolute/path', // 필수. 절대 경로
106
- extensions: ['.ts', '.mts', '.cts'], // 선택. 인덱싱 대상 확장자
107
- ignorePatterns: ['dist', 'vendor'], // 선택. 무시할 디렉토리/패턴
108
- parseCacheCapacity: 500, // 선택. 파싱 캐시 크기
109
- });
110
- ```
111
-
112
- | 옵션 | 타입 | 기본값 | 설명 |
113
- |------|------|--------|------|
114
- | `projectRoot` | `string` | — | 프로젝트 루트 절대 경로 **(필수)** |
115
- | `extensions` | `string[]` | `['.ts', '.mts', '.cts']` | 인덱싱 대상 파일 확장자 |
116
- | `ignorePatterns` | `string[]` | `[]` | 무시할 경로 패턴 |
117
- | `parseCacheCapacity` | `number` | `500` | LRU 파싱 캐시 최대 크기 |
118
- | `logger` | `Logger` | `console` | 커스텀 로거 (`{ error(...args): void }`) |
119
-
120
- **반환**: `Promise<Gildash>`
121
-
122
- ---
123
-
124
- ### `ledger.close()`
125
-
126
- 인덱서를 종료합니다. watcher 중지, DB 연결 해제, 시그널 핸들러 제거를 수행합니다.
127
-
128
- ```ts
129
- await ledger.close();
130
- ```
131
-
132
- **반환**: `Promise<void>`
89
+ // 전문 검색 (FTS5 접두사 매칭)
90
+ const hits = ledger.searchSymbols({ text: 'handle' });
133
91
 
134
- ---
135
-
136
- ### `ledger.searchSymbols(query)`
137
-
138
- 심볼을 검색합니다. FTS5 전문 검색과 필터를 조합할 수 있습니다.
92
+ // 정확한 이름 매칭
93
+ const exact = ledger.searchSymbols({ text: 'UserService', exact: true });
139
94
 
140
- ```ts
141
- // 이름으로 검색
142
- const results = ledger.searchSymbols({ text: 'handleClick' });
95
+ // 정규식 패턴
96
+ const handlers = ledger.searchSymbols({ regex: '^handle.*Click$' });
143
97
 
144
- // 정확한 이름 매칭 (FTS prefix가 아닌 완전 일치)
145
- const exact = ledger.searchSymbols({ text: 'UserService', exact: true });
98
+ // 데코레이터 필터
99
+ const injectables = ledger.searchSymbols({ decorator: 'Injectable' });
146
100
 
147
- // 종류 + export 여부 필터
148
- const classes = ledger.searchSymbols({
101
+ // 필터 조합
102
+ const exportedClasses = ledger.searchSymbols({
149
103
  kind: 'class',
150
104
  isExported: true,
151
105
  limit: 50,
152
106
  });
153
-
154
- // 파일 경로 필터
155
- const inFile = ledger.searchSymbols({
156
- filePath: 'src/services/user.ts',
157
- });
158
107
  ```
159
108
 
160
- | 필드 | 타입 | 설명 |
161
- |------|------|------|
162
- | `text` | `string?` | FTS5 전문 검색 쿼리 |
163
- | `exact` | `boolean?` | `true`이면 `text`를 정확한 이름으로 매칭 (FTS prefix 아님) |
164
- | `kind` | `SymbolKind?` | `'function'` \| `'method'` \| `'class'` \| `'variable'` \| `'type'` \| `'interface'` \| `'enum'` \| `'property'` |
165
- | `filePath` | `string?` | 특정 파일 경로 필터 |
166
- | `isExported` | `boolean?` | export 여부 필터 |
167
- | `project` | `string?` | 프로젝트 이름 (모노레포 지원) |
168
- | `limit` | `number?` | 최대 결과 수 |
169
-
170
- **반환**: `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
- ```
188
-
189
- ---
190
-
191
- ### `ledger.searchRelations(query)`
192
-
193
- 파일/심볼 간 관계를 검색합니다.
109
+ `searchRelations()`로 파일 관계를 검색할 있습니다:
194
110
 
195
111
  ```ts
196
- // 특정 파일이 import하는 관계
197
- const imports = ledger.searchRelations({
198
- srcFilePath: 'src/app.ts',
199
- type: 'imports',
200
- });
201
-
202
- // 특정 심볼을 호출하는 관계
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
- | `srcFilePath` | `string?` | 출발 파일 경로 |
212
- | `srcSymbolName` | `string?` | 출발 심볼 이름 |
213
- | `dstFilePath` | `string?` | 도착 파일 경로 |
214
- | `dstSymbolName` | `string?` | 도착 심볼 이름 |
215
- | `type` | `'imports'` \| `'calls'` \| `'extends'` \| `'implements'`? | 관계 유형 |
216
- | `project` | `string?` | 프로젝트 이름 |
217
- | `limit` | `number?` | 최대 결과 수 |
218
-
219
- **반환**: `CodeRelation[]`
220
-
221
- ```ts
222
- interface CodeRelation {
223
- type: 'imports' | 'calls' | 'extends' | 'implements';
224
- srcFilePath: string;
225
- srcSymbolName: string | null; // null = 모듈 레벨
226
- dstFilePath: string;
227
- dstSymbolName: string | null;
228
- metaJson?: string;
229
- }
230
- ```
116
+ 모노레포 프로젝트에서는 `searchAllSymbols()`와 `searchAllRelations()`로 전체 프로젝트를 검색합니다.
231
117
 
232
118
  ---
233
119
 
234
- ### `ledger.getDependencies(filePath, project?)`
120
+ ### 의존성 분석
235
121
 
236
- 특정 파일이 import하는 파일 목록을 반환합니다.
122
+ import 그래프 분석, 순환 감지, 변경 영향 범위 계산을 수행합니다.
237
123
 
238
124
  ```ts
125
+ // 직접 import / importer 목록
239
126
  const deps = ledger.getDependencies('src/app.ts');
240
- // ['src/utils.ts', 'src/config.ts', ...]
241
- ```
127
+ const importers = ledger.getDependents('src/utils.ts');
242
128
 
243
- **반환**: `string[]`
244
-
245
- ---
129
+ // 전이적 영향 — 파일 변경 시 어떤 파일이 영향을 받는가?
130
+ const affected = await ledger.getAffected(['src/utils.ts']);
246
131
 
247
- ### `ledger.getDependents(filePath, project?)`
132
+ // 전체 import 그래프 (인접 리스트)
133
+ const graph = await ledger.getImportGraph();
248
134
 
249
- 특정 파일을 import하는 파일 목록을 반환합니다.
135
+ // 전이적 의존성 (전방 BFS)
136
+ const transitive = await ledger.getTransitiveDependencies('src/app.ts');
250
137
 
251
- ```ts
252
- const dependents = ledger.getDependents('src/utils.ts');
253
- // ['src/app.ts', 'src/services/user.ts', ...]
138
+ // 순환 의존성 감지
139
+ const hasCycles = await ledger.hasCycle();
140
+ const cyclePaths = await ledger.getCyclePaths();
254
141
  ```
255
142
 
256
- **반환**: `string[]`
257
-
258
143
  ---
259
144
 
260
- ### `ledger.getAffected(changedFiles, project?)`
145
+ ### 코드 품질 분석
261
146
 
262
- 변경된 파일들의 영향을 받는 모든 파일을 전이적(transitive)으로 계산합니다.
147
+ 미사용 export 감지, 모듈 인터페이스 조회, 결합도 측정을 수행합니다.
263
148
 
264
149
  ```ts
265
- const affected = await ledger.getAffected(['src/utils.ts']);
266
- // ['src/app.ts', 'src/services/user.ts', 'src/main.ts', ...]
267
- ```
268
-
269
- **반환**: `Promise<string[]>`
150
+ // 미사용 export 어디에서도 import되지 않은 exported 심볼
151
+ const dead = ledger.getDeadExports();
270
152
 
271
- ---
153
+ // 파일 통계 — 라인 수, 심볼 수, 파일 크기
154
+ const stats = ledger.getFileStats('src/app.ts');
272
155
 
273
- ### `ledger.hasCycle(project?)`
156
+ // Fan-in / Fan-out 결합도 메트릭
157
+ const fan = await ledger.getFanMetrics('src/app.ts');
274
158
 
275
- 프로젝트의 import 그래프에 순환 의존성이 있는지 검사합니다.
159
+ // 모듈 공개 인터페이스 모든 exported 심볼과 메타데이터
160
+ const iface = ledger.getModuleInterface('src/services/user.ts');
276
161
 
277
- ```ts
278
- const cyclic = await ledger.hasCycle();
279
- if (cyclic) {
280
- console.warn('순환 의존성이 감지되었습니다');
281
- }
162
+ // 상세 심볼 정보 — 멤버, jsDoc, 데코레이터, 타입 정보
163
+ const full = ledger.getFullSymbol('UserService', 'src/services/user.ts');
282
164
  ```
283
165
 
284
- **반환**: `Promise<boolean>`
285
-
286
166
  ---
287
167
 
288
- ### `ledger.reindex()`
168
+ ### 패턴 매칭 & 추적
289
169
 
290
- 수동으로 전체 재인덱싱을 수행합니다. owner 역할에서만 사용 가능합니다.
170
+ AST 구조로 코드를 검색하고, re-export 체인을 통해 심볼 원본을 추적합니다.
291
171
 
292
172
  ```ts
293
- const result = await ledger.reindex();
294
- ```
173
+ // 구조적 패턴 검색 (ast-grep 문법)
174
+ const logs = await ledger.findPattern('console.log($$$)');
175
+ const hooks = await ledger.findPattern('useState($A)', {
176
+ filePaths: ['src/components/App.tsx'],
177
+ });
295
178
 
296
- **반환**: `Promise<IndexResult>`
179
+ // re-export 체인 추적 — 심볼이 실제로 정의된 위치 찾기
180
+ const resolved = ledger.resolveSymbol('MyComponent', 'src/index.ts');
297
181
 
298
- ---
182
+ // 상속 체인 — extends/implements 트리 순회
183
+ const tree = await ledger.getHeritageChain('UserService', 'src/services/user.ts');
184
+ ```
299
185
 
300
- ### `ledger.onIndexed(callback)`
186
+ <br>
187
+
188
+ ## 🔧 스캔 전용 모드
301
189
 
302
- 인덱싱 완료 이벤트를 구독합니다.
190
+ CI 파이프라인이나 1회성 분석에서는 파일 워처를 비활성화합니다:
303
191
 
304
192
  ```ts
305
- const unsubscribe = ledger.onIndexed((result) => {
306
- console.log(`인덱싱 완료: ${result.indexedFiles}개 파일`);
193
+ const ledger = await Gildash.open({
194
+ projectRoot: '/path/to/project',
195
+ watchMode: false, // 워처 없음, heartbeat 없음
307
196
  });
308
197
 
309
- // 구독 해제
310
- unsubscribe();
311
- ```
312
-
313
- **반환**: `() => void` (구독 해제 함수)
198
+ // ... 쿼리 실행 ...
314
199
 
315
- ---
316
-
317
- ### `ledger.projects`
318
-
319
- 감지된 프로젝트 경계 목록을 반환합니다 (모노레포에서 여러 프로젝트 감지).
320
-
321
- ```ts
322
- const boundaries = ledger.projects;
323
- // → [{ project: 'my-app', root: '/path/to/project' }, ...]
200
+ await ledger.close({ cleanup: true }); // DB 파일까지 삭제
324
201
  ```
325
202
 
326
- **타입**: `ProjectBoundary[]`
327
-
328
- ---
203
+ <br>
329
204
 
330
- ### `ledger.getStats(project?)`
205
+ ## ❌ 에러 처리
331
206
 
332
- 심볼 통계를 반환합니다.
207
+ 모든 public 메서드는 [`@zipbul/result`](https://www.npmjs.com/package/@zipbul/result)의 `Result<T, GildashError>`를 반환합니다. `isErr()`로 에러를 분기합니다:
333
208
 
334
209
  ```ts
335
- const stats = ledger.getStats();
210
+ import { isErr } from '@zipbul/result';
211
+
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(`${result.length}개 심볼 발견`);
217
+ }
336
218
  ```
337
219
 
338
- **반환**: `SymbolStats`
220
+ <br>
339
221
 
340
- ---
222
+ ## ⚙️ 설정
341
223
 
342
- ### `ledger.parseSource(filePath, sourceText)`
224
+ ### `Gildash.open(options)`
343
225
 
344
- 파일을 파싱하여 AST를 반환합니다. 결과는 내부 캐시에 저장됩니다.
226
+ | 옵션 | 타입 | 기본값 | 설명 |
227
+ |------|------|--------|------|
228
+ | `projectRoot` | `string` | — | 프로젝트 루트 절대 경로 **(필수)** |
229
+ | `extensions` | `string[]` | `['.ts', '.mts', '.cts']` | 인덱싱 대상 파일 확장자 |
230
+ | `ignorePatterns` | `string[]` | `[]` | 무시할 글로브 패턴 |
231
+ | `parseCacheCapacity` | `number` | `500` | LRU 파싱 캐시 최대 크기 |
232
+ | `logger` | `Logger` | `console` | 커스텀 로거 (`{ error(...args): void }`) |
233
+ | `watchMode` | `boolean` | `true` | `false`이면 파일 워처 비활성화 (스캔 전용 모드) |
345
234
 
346
- ```ts
347
- const parsed = ledger.parseSource('/path/to/file.ts', sourceCode);
348
- ```
235
+ **반환**: `Promise<Gildash>` (`Result`로 래핑됨)
349
236
 
350
- **반환**: `ParsedFile`
237
+ <br>
351
238
 
352
- ---
239
+ ## 🔍 API 레퍼런스
353
240
 
354
- ### `ledger.extractSymbols(parsed)`
241
+ ### 검색
355
242
 
356
- 파싱된 파일에서 심볼을 추출합니다.
243
+ | 메서드 | 반환 타입 | 설명 |
244
+ |--------|-----------|------|
245
+ | `searchSymbols(query)` | `Result<SymbolSearchResult[]>` | FTS5 전문검색 + exact/regex/decorator 필터 |
246
+ | `searchRelations(query)` | `Result<CodeRelation[]>` | 파일, 심볼, 관계 유형 필터 |
247
+ | `searchAllSymbols(query)` | `Result<SymbolSearchResult[]>` | 전체 프로젝트 심볼 검색 |
248
+ | `searchAllRelations(query)` | `Result<CodeRelation[]>` | 전체 프로젝트 관계 검색 |
249
+ | `listIndexedFiles(project?)` | `Result<FileRecord[]>` | 인덱싱된 파일 목록 |
250
+ | `getSymbolsByFile(filePath)` | `Result<SymbolSearchResult[]>` | 단일 파일의 모든 심볼 |
357
251
 
358
- ```ts
359
- const symbols = ledger.extractSymbols(parsed);
360
- ```
252
+ ### 의존성 그래프
361
253
 
362
- **반환**: `ExtractedSymbol[]`
254
+ | 메서드 | 반환 타입 | 설명 |
255
+ |--------|-----------|------|
256
+ | `getDependencies(filePath)` | `Result<string[]>` | `filePath`가 import하는 파일 목록 |
257
+ | `getDependents(filePath)` | `Result<string[]>` | `filePath`를 import하는 파일 목록 |
258
+ | `getAffected(changedFiles)` | `Promise<Result<string[]>>` | 전이적 영향 범위 |
259
+ | `hasCycle(project?)` | `Promise<Result<boolean>>` | 순환 의존성 감지 |
260
+ | `getCyclePaths(project?)` | `Promise<Result<string[][]>>` | 모든 순환 경로 |
261
+ | `getImportGraph(project?)` | `Promise<Result<Map>>` | 전체 인접 리스트 |
262
+ | `getTransitiveDependencies(filePath)` | `Promise<Result<string[]>>` | 전방 전이적 BFS |
363
263
 
364
- ---
264
+ ### 분석
365
265
 
366
- ### `ledger.extractRelations(parsed)`
266
+ | 메서드 | 반환 타입 | 설명 |
267
+ |--------|-----------|------|
268
+ | `getDeadExports(project?, opts?)` | `Result<Array>` | 미사용 exported 심볼 |
269
+ | `getFullSymbol(name, filePath)` | `Result<FullSymbol>` | 멤버, jsDoc, 데코레이터, 타입 정보 |
270
+ | `getFileStats(filePath)` | `Result<FileStats>` | 라인 수, 심볼 수, 파일 크기 |
271
+ | `getFanMetrics(filePath)` | `Promise<Result<FanMetrics>>` | fan-in/fan-out 결합도 |
272
+ | `getModuleInterface(filePath)` | `Result<ModuleInterface>` | 공개 export와 메타데이터 |
273
+ | `getInternalRelations(filePath)` | `Result<CodeRelation[]>` | 파일 내부 관계 |
274
+ | `diffSymbols(before, after)` | `SymbolDiff` | 스냅샷 diff (추가/삭제/수정) |
367
275
 
368
- 파싱된 파일에서 관계를 추출합니다.
276
+ ### 고급
369
277
 
370
- ```ts
371
- const relations = ledger.extractRelations(parsed);
372
- ```
278
+ | 메서드 | 반환 타입 | 설명 |
279
+ |--------|-----------|------|
280
+ | `findPattern(pattern, opts?)` | `Promise<Result<PatternMatch[]>>` | AST 구조적 검색 (ast-grep) |
281
+ | `resolveSymbol(name, filePath)` | `Result<ResolvedSymbol>` | re-export 체인을 따라 원본 추적 |
282
+ | `getHeritageChain(name, filePath)` | `Promise<Result<HeritageNode>>` | extends/implements 트리 |
283
+ | `indexExternalPackages(packages)` | `Promise<Result<IndexResult[]>>` | `node_modules`의 `.d.ts` 인덱싱 |
284
+ | `batchParse(filePaths)` | `Promise<Result<Map>>` | 다중 파일 동시 파싱 |
373
285
 
374
- **반환**: `CodeRelation[]`
286
+ ### 라이프사이클 & 저수준
375
287
 
376
- ---
288
+ | 메서드 | 반환 타입 | 설명 |
289
+ |--------|-----------|------|
290
+ | `reindex()` | `Promise<Result<IndexResult>>` | 강제 전체 재인덱싱 (owner만 가능) |
291
+ | `onIndexed(callback)` | `() => void` | 인덱싱 완료 이벤트 구독 |
292
+ | `parseSource(filePath, src)` | `Result<ParsedFile>` | 단일 파일 파싱 & 캐시 |
293
+ | `extractSymbols(parsed)` | `Result<ExtractedSymbol[]>` | 파싱된 AST에서 심볼 추출 |
294
+ | `extractRelations(parsed)` | `Result<CodeRelation[]>` | 파싱된 AST에서 관계 추출 |
295
+ | `getParsedAst(filePath)` | `ParsedFile \| undefined` | 캐시된 AST 조회 (읽기 전용) |
296
+ | `getFileInfo(filePath)` | `Result<FileRecord \| null>` | 파일 메타데이터 (해시, mtime, 크기) |
297
+ | `getStats(project?)` | `Result<SymbolStats>` | 심볼/파일 통계 |
298
+ | `projects` | `ProjectBoundary[]` | 탐지된 프로젝트 경계 |
299
+ | `close(opts?)` | `Promise<Result<void>>` | 종료 (`{ cleanup: true }`로 DB 삭제 가능) |
300
+
301
+ <br>
377
302
 
378
- ### `ledger.getParsedAst(filePath)`
303
+ <details>
304
+ <summary><strong>타입 정의</strong></summary>
379
305
 
380
- 내부 LRU 캐시에서 이전에 파싱된 AST조회합니다.
306
+ 상세 TypeScript 타입 정의는 영문 README참고하세요 → [README.md — Type Definitions](./README.md#type-definitions)
381
307
 
382
- 파일이 아직 파싱되지 않았거나 캐시에서 제거된 경우 `undefined`를 반환합니다.
383
- 반환된 객체는 내부 캐시와 공유됩니다 — **읽기 전용**으로 취급하세요.
308
+ 주요 타입 요약:
384
309
 
385
310
  ```ts
386
- const ast = ledger.getParsedAst('/absolute/path/to/src/app.ts');
387
- if (ast) {
388
- console.log(ast.program.body.length, '개의 AST 노드');
311
+ interface SymbolSearchQuery {
312
+ text?: string; // FTS5 전문 검색
313
+ exact?: boolean; // 정확한 이름 일치
314
+ kind?: SymbolKind; // 심볼 종류 필터
315
+ filePath?: string; // 파일 경로 필터
316
+ isExported?: boolean; // export 여부
317
+ project?: string; // 프로젝트 이름
318
+ limit?: number; // 최대 결과 수 (기본값: 100)
319
+ decorator?: string; // 데코레이터 이름 필터
320
+ regex?: string; // 정규식 패턴 필터
389
321
  }
390
- ```
391
322
 
392
- **반환**: `ParsedFile | undefined`
393
-
394
- ---
395
-
396
- ### `ledger.getFileInfo(filePath, project?)`
397
-
398
- 인덱싱된 파일의 메타데이터를 조회합니다.
323
+ interface CodeRelation {
324
+ type: 'imports' | 'type-references' | 're-exports' | 'calls' | 'extends' | 'implements';
325
+ srcFilePath: string;
326
+ srcSymbolName: string | null;
327
+ dstFilePath: string;
328
+ dstSymbolName: string | null;
329
+ meta?: Record<string, unknown>;
330
+ }
399
331
 
400
- content hash, mtime, size 등이 포함된 `FileRecord`를 반환합니다.
401
- 파일이 아직 인덱싱되지 않은 경우 `null`을 반환합니다.
332
+ interface IndexResult {
333
+ indexedFiles: number;
334
+ removedFiles: number;
335
+ totalSymbols: number;
336
+ totalRelations: number;
337
+ durationMs: number;
338
+ changedFiles: string[];
339
+ deletedFiles: string[];
340
+ failedFiles: string[];
341
+ changedSymbols: {
342
+ added: Array<{ name: string; filePath: string; kind: string }>;
343
+ modified: Array<{ name: string; filePath: string; kind: string }>;
344
+ removed: Array<{ name: string; filePath: string; kind: string }>;
345
+ };
346
+ }
402
347
 
403
- ```ts
404
- const info = ledger.getFileInfo('src/app.ts');
405
- if (!isErr(info) && info !== null) {
406
- console.log(`해시: ${info.contentHash}, 크기: ${info.size}`);
348
+ interface GildashError {
349
+ type: GildashErrorType;
350
+ message: string;
351
+ cause?: unknown;
407
352
  }
408
353
  ```
409
354
 
410
- **반환**: `Result<FileRecord | null, GildashError>`
411
-
412
- ---
413
-
414
- ### `ledger.getSymbolsByFile(filePath, project?)`
355
+ </details>
415
356
 
416
- 특정 파일에 선언된 모든 심볼을 조회합니다. `searchSymbols`에 `filePath` 필터를 적용한 편의 래퍼입니다.
357
+ <br>
417
358
 
418
- ```ts
419
- const symbols = ledger.getSymbolsByFile('src/app.ts');
420
- if (!isErr(symbols)) {
421
- for (const sym of symbols) {
422
- console.log(`${sym.kind}: ${sym.name}`);
423
- }
424
- }
425
- ```
359
+ ## ⚠️ 에러 타입
426
360
 
427
- **반환**: `Result<SymbolSearchResult[], GildashError>`
361
+ | 타입 | 발생 시점 |
362
+ |------|----------|
363
+ | `watcher` | 파일 워처 시작/중지 실패 |
364
+ | `parse` | AST 파싱 실패 |
365
+ | `extract` | 심볼/관계 추출 실패 |
366
+ | `index` | 인덱싱 파이프라인 실패 |
367
+ | `store` | DB 연산 실패 |
368
+ | `search` | 검색 쿼리 실패 |
369
+ | `closed` | 종료된 인스턴스에서 연산 시도 |
370
+ | `validation` | 잘못된 입력 (e.g. `node_modules`에 패키지 없음) |
371
+ | `close` | 종료 중 에러 |
428
372
 
429
373
  <br>
430
374
 
@@ -433,19 +377,19 @@ if (!isErr(symbols)) {
433
377
  ```
434
378
  Gildash (파사드)
435
379
  ├── Parser — oxc-parser 기반 TypeScript AST 파싱
436
- ├── Extractor — 심볼/관계 추출 (imports, calls, heritage)
437
- ├── Store — bun:sqlite + drizzle-orm (files, symbols, relations, FTS5)
438
- ├── Indexer — 변경 감지 → 파싱 → 추출 → 저장 파이프라인
439
- ├── Search — 심볼 검색, 관계 검색, 의존성 그래프
380
+ ├── Extractor — 심볼/관계 추출 (imports, re-exports, type-refs, calls, heritage)
381
+ ├── Store — bun:sqlite + drizzle-orm (files · symbols · relations · FTS5)
382
+ ├── Indexer — 파일 변경 → 파싱 → 추출 → 저장 파이프라인, 심볼 레벨 diff
383
+ ├── Search — FTS + regex + decorator 검색, 관계 쿼리, 의존성 그래프, ast-grep
440
384
  └── Watcher — @parcel/watcher + owner/reader 역할 관리
441
385
  ```
442
386
 
443
387
  ### Owner/Reader 패턴
444
388
 
445
- 동일 SQLite DB를 여러 프로세스가 공유할 때, 단일 writer를 보장합니다.
389
+ 동일 SQLite DB를 여러 프로세스가 공유할 때, 단일 writer를 보장합니다:
446
390
 
447
- - **Owner** — watcher 실행, 인덱싱 수행, heartbeat 전송 (30초 간격)
448
- - **Reader** — 읽기 전용 접근, 60초 간격으로 owner 상태 확인; owner가 stale 상태가 되면 reader 중 하나가 owner로 승격
391
+ - **Owner** — 파일 워처 실행, 인덱싱 수행, 30초 간격으로 heartbeat 전송
392
+ - **Reader** — 읽기 전용 접근; 60초 간격으로 owner 상태 확인, owner가 stale 상태가 되면 reader 중 하나가 owner로 승격
449
393
 
450
394
  <br>
451
395