@aroman22/codegraph-vba 1.5.2 → 1.6.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 +1 -1
- package/dist/bin/codegraph.d.ts +1 -1
- package/dist/bin/command-supervision.d.ts +38 -1
- package/dist/db/index.d.ts +84 -5
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/queries.d.ts +116 -4
- package/dist/db/wal-valve.d.ts +106 -0
- package/dist/directory.d.ts +9 -5
- package/dist/extraction/cfml-extractor.d.ts +107 -0
- package/dist/extraction/grammars.d.ts +25 -1
- package/dist/extraction/index.d.ts +47 -2
- package/dist/extraction/languages/arkts.d.ts +3 -0
- package/dist/extraction/languages/c-cpp.d.ts +98 -0
- package/dist/extraction/languages/cfquery.d.ts +12 -0
- package/dist/extraction/languages/cfscript.d.ts +3 -0
- package/dist/extraction/languages/cobol.d.ts +33 -0
- package/dist/extraction/languages/erlang.d.ts +3 -0
- package/dist/extraction/languages/nix.d.ts +3 -0
- package/dist/extraction/languages/solidity.d.ts +3 -0
- package/dist/extraction/languages/terraform.d.ts +3 -0
- package/dist/extraction/languages/vbnet.d.ts +11 -0
- package/dist/extraction/mybatis-extractor.d.ts +30 -10
- package/dist/extraction/parse-pool.d.ts +28 -1
- package/dist/extraction/tree-sitter-types.d.ts +20 -1
- package/dist/extraction/tree-sitter.d.ts +60 -1
- package/dist/extraction/vba/call-sweep.d.ts +3 -0
- package/dist/extraction/vba/calls.d.ts +55 -0
- package/dist/extraction/vba/constants.d.ts +42 -0
- package/dist/extraction/vba/context.d.ts +175 -0
- package/dist/extraction/vba/controls.d.ts +24 -0
- package/dist/extraction/vba/declarations.d.ts +3 -0
- package/dist/extraction/vba/dims.d.ts +3 -0
- package/dist/extraction/vba/docmd.d.ts +16 -0
- package/dist/extraction/vba/enums-consts.d.ts +15 -0
- package/dist/extraction/vba/implements.d.ts +3 -0
- package/dist/extraction/vba/procedures.d.ts +3 -0
- package/dist/extraction/vba/sql-wrapper.d.ts +17 -0
- package/dist/extraction/vba/tempvars.d.ts +10 -0
- package/dist/extraction/vba/text-utils.d.ts +73 -0
- package/dist/extraction/vba-extractor.d.ts +3 -951
- package/dist/extraction/vba-preprocess.d.ts +1 -1
- package/dist/index.d.ts +81 -1
- package/dist/installer/index.d.ts +42 -0
- package/dist/mcp/daemon.d.ts +35 -3
- package/dist/mcp/early-ppid.d.ts +26 -0
- package/dist/mcp/liveness-watchdog.d.ts +18 -1
- package/dist/mcp/query-pool.d.ts +14 -0
- package/dist/mcp/session.d.ts +14 -0
- package/dist/mcp/startup-handshake.d.ts +44 -0
- package/dist/mcp/tools.d.ts +22 -0
- package/dist/project-config.d.ts +44 -0
- package/dist/resolution/c-fnptr-synthesizer.d.ts +2 -1
- package/dist/resolution/callback-synthesizer.d.ts +1 -1
- package/dist/resolution/cooperative-yield.d.ts +32 -0
- package/dist/resolution/frameworks/cics.d.ts +20 -0
- package/dist/resolution/frameworks/terraform.d.ts +38 -0
- package/dist/resolution/goframe-synthesizer.d.ts +2 -1
- package/dist/resolution/import-resolver.d.ts +7 -0
- package/dist/resolution/index.d.ts +64 -2
- package/dist/resolution/name-matcher.d.ts +22 -3
- package/dist/resolution/strip-comments.d.ts +1 -1
- package/dist/resolution/types.d.ts +29 -0
- package/dist/resolution/workspace-packages.d.ts +10 -0
- package/dist/search/identifier-segments.d.ts +60 -0
- package/dist/sync/watcher.d.ts +10 -5
- package/dist/sync/worktree.d.ts +9 -0
- package/dist/types.d.ts +25 -2
- package/dist/upgrade/index.d.ts +32 -0
- package/dist/upgrade/remove-binary.d.ts +87 -0
- package/dist/upgrade/update-check.d.ts +92 -0
- package/npm-shim.js +32 -3
- package/package.json +7 -7
- package/dist/reasoning/config.d.ts +0 -45
- package/dist/reasoning/credentials.d.ts +0 -5
- package/dist/reasoning/login.d.ts +0 -21
- package/dist/reasoning/reasoner.d.ts +0 -43
package/README.md
CHANGED
|
@@ -281,7 +281,7 @@ The reliable, universal payoff is **surgical context and speed**: CodeGraph coll
|
|
|
281
281
|
| **Full-Text Search** | Find code by name instantly across your entire codebase, powered by FTS5 |
|
|
282
282
|
| **Impact Analysis** | Trace callers, callees, and the full impact radius of any symbol before making changes |
|
|
283
283
|
| **Always Fresh** | File watcher uses native OS events (FSEvents/inotify/ReadDirectoryChangesW) with debounced auto-sync — the graph stays current as you code, zero config |
|
|
284
|
-
| **
|
|
284
|
+
| **30+ Languages** | TypeScript, JavaScript, ArkTS, Python, Go, Rust, Java, C#, VB.NET, PHP, Ruby, C, C++, CUDA, Objective-C, Metal, Swift, Kotlin, Scala, Dart, Lua, Luau, R, Nix, Erlang, CFML, COBOL, Solidity, Terraform/OpenTofu, Svelte, Vue, Astro, Liquid, Pascal/Delphi, **VBA / Access** |
|
|
285
285
|
| **Framework-aware Routes** | Recognizes web-framework routing files and links URL patterns to their handlers across 17 frameworks |
|
|
286
286
|
| **Mixed iOS / React Native / Expo** | Closes cross-language flows that static parsing misses: Swift ↔ ObjC bridging, React Native legacy bridge + TurboModules + Fabric view components, native → JS event emitters, Expo Modules |
|
|
287
287
|
| **VBA / Access + Dysflow** | Indexes the Dysflow-exported source tree (`.bas`/`.cls` + `.form.txt`/`.report.txt`) of Microsoft Access projects so agents can navigate VBA code the same way they navigate TypeScript or Python |
|
package/dist/bin/codegraph.d.ts
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process supervision for long-running CLI commands (`index` / `init --index`).
|
|
3
|
+
*
|
|
4
|
+
* Indexing a large repo can run for a while on the main thread, and #999
|
|
5
|
+
* surfaced two ways that goes wrong when nothing is watching it:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Orphaned worker.** `index` runs in a child re-exec'd with
|
|
8
|
+
* `--liftoff-only` (the WASM-flag relaunch). Its parent blocks in
|
|
9
|
+
* `spawnSync`, so when the parent shim is killed it cannot forward the
|
|
10
|
+
* signal — the child keeps running, now orphaned, pinning a core. The PPID
|
|
11
|
+
* watchdog (#277) notices the parent/host went away and exits the child.
|
|
12
|
+
* 2. **Wedged indexer.** The `#850` main-thread liveness watchdog — which
|
|
13
|
+
* SIGKILLs a process whose event loop stops turning — was wired only into
|
|
14
|
+
* the MCP `serve` path, so a wedged `index`/`init` was never auto-killed.
|
|
15
|
+
*
|
|
16
|
+
* Both reuse the exact mechanisms `serve` already uses; this just makes them
|
|
17
|
+
* available to a one-shot command. Best-effort and self-disabling: a missing
|
|
18
|
+
* watchdog never blocks the command from running. Both honour the same env
|
|
19
|
+
* switches as `serve` (`CODEGRAPH_NO_WATCHDOG`, `CODEGRAPH_PPID_POLL_MS=0`).
|
|
20
|
+
*
|
|
21
|
+
* Unlike the daemon — whose main thread only does fast, bounded work — the
|
|
22
|
+
* `index`/`init` path runs reference resolution and dynamic-edge synthesis
|
|
23
|
+
* SYNCHRONOUSLY on this thread, and on a large repo that is legitimately many
|
|
24
|
+
* seconds of work. So those spans yield cooperatively to the event loop
|
|
25
|
+
* (`src/resolution/cooperative-yield.ts`) to keep the heartbeat alive; without
|
|
26
|
+
* that the watchdog would SIGKILL a valid, in-progress index (#1091). The
|
|
27
|
+
* distinction it must preserve — kill a TRUE wedge, spare slow-but-progressing
|
|
28
|
+
* work — is exactly what cooperative yielding buys: a genuinely stuck span never
|
|
29
|
+
* reaches its next yield, so it still trips the timeout.
|
|
30
|
+
*/
|
|
31
|
+
import { WatchdogOptions } from '../mcp/liveness-watchdog';
|
|
1
32
|
export interface CommandSupervision {
|
|
2
33
|
/** Tear down both watchdogs. Idempotent; call when the command finishes. */
|
|
3
34
|
stop(): void;
|
|
@@ -7,6 +38,12 @@ export interface CommandSupervision {
|
|
|
7
38
|
* `label` is used in the shutdown notice (e.g. `"index"`). Returns a handle
|
|
8
39
|
* whose `stop()` must be called when the command completes so neither watchdog
|
|
9
40
|
* outlives it.
|
|
41
|
+
*
|
|
42
|
+
* Pass `watchdog.progressPaths` (the project's SQLite DB + `-wal`) so the
|
|
43
|
+
* liveness watchdog can tell a slow-but-progressing store on degraded storage
|
|
44
|
+
* (files advancing) from a true wedge (they aren't) — one long synchronous
|
|
45
|
+
* SQLite statement on a 150-IOPS disk otherwise gets a healthy index
|
|
46
|
+
* SIGKILLed (#1231).
|
|
10
47
|
*/
|
|
11
|
-
export declare function installCommandSupervision(label: string): CommandSupervision;
|
|
48
|
+
export declare function installCommandSupervision(label: string, watchdog?: WatchdogOptions): CommandSupervision;
|
|
12
49
|
//# sourceMappingURL=command-supervision.d.ts.map
|
package/dist/db/index.d.ts
CHANGED
|
@@ -68,13 +68,51 @@ export declare class DatabaseConnection {
|
|
|
68
68
|
* Get database file size in bytes
|
|
69
69
|
*/
|
|
70
70
|
getSize(): number;
|
|
71
|
+
/**
|
|
72
|
+
* Size of the `-wal` sidecar file in bytes. 0 when it doesn't exist (non-WAL
|
|
73
|
+
* journal mode, in-memory DB, or no write since the last checkpoint+reset).
|
|
74
|
+
*/
|
|
75
|
+
getWalSizeBytes(): number;
|
|
76
|
+
/** Current `wal_autocheckpoint` interval in pages (0 = disabled). */
|
|
77
|
+
getWalAutocheckpoint(): number;
|
|
78
|
+
/**
|
|
79
|
+
* Set the connection's `wal_autocheckpoint` interval (pages; 0 disables).
|
|
80
|
+
* Bulk indexing defers checkpoints entirely (#1231): the default 1000-page
|
|
81
|
+
* auto-checkpoint re-writes hot B-tree/FTS pages into the main DB file over
|
|
82
|
+
* and over — measured at ~95% of ALL disk I/O during a bulk index, and the
|
|
83
|
+
* difference between 45s and 19+ minutes on HDD-class storage. During
|
|
84
|
+
* deferral a {@link WalCheckpointValve} bounds WAL growth off-thread.
|
|
85
|
+
*/
|
|
86
|
+
setWalAutocheckpoint(pages: number): void;
|
|
87
|
+
/**
|
|
88
|
+
* `PRAGMA wal_checkpoint(PASSIVE)` on a worker thread with its own
|
|
89
|
+
* connection. PASSIVE never blocks the writer, and running it off-thread
|
|
90
|
+
* means the main thread — and the #850 watchdog heartbeat — keep turning
|
|
91
|
+
* even when the backfill is minutes of I/O on slow storage (a synchronous
|
|
92
|
+
* checkpoint that exceeds the watchdog's 60s window gets a healthy index
|
|
93
|
+
* SIGKILLed — observed in the #1231 repro).
|
|
94
|
+
*
|
|
95
|
+
* Returns SQLite's checkpoint result row — `log === checkpointed` with
|
|
96
|
+
* `busy === 0` means the ENTIRE WAL was backfilled, so the writer's next
|
|
97
|
+
* commit restarts the WAL from the top and the file stops growing. The
|
|
98
|
+
* WAL valve needs that signal because a WAL file's SIZE never shrinks:
|
|
99
|
+
* after the first wrap, raw file size says nothing about the un-backfilled
|
|
100
|
+
* backlog. Best-effort: returns null on any failure (including worker
|
|
101
|
+
* threads being unavailable — a potentially minutes-long checkpoint must
|
|
102
|
+
* never run inline on the main thread).
|
|
103
|
+
*/
|
|
104
|
+
checkpointWalPassive(): Promise<{
|
|
105
|
+
busy: number;
|
|
106
|
+
log: number;
|
|
107
|
+
checkpointed: number;
|
|
108
|
+
} | null>;
|
|
71
109
|
/**
|
|
72
110
|
* Optimize database (vacuum and analyze)
|
|
73
111
|
*/
|
|
74
112
|
optimize(): void;
|
|
75
113
|
/**
|
|
76
|
-
* Lightweight
|
|
77
|
-
*
|
|
114
|
+
* Lightweight maintenance to run after bulk writes (indexAll, sync).
|
|
115
|
+
* Two operations:
|
|
78
116
|
*
|
|
79
117
|
* - `PRAGMA optimize` — incremental ANALYZE; SQLite only re-analyzes
|
|
80
118
|
* tables whose row counts changed materially since the last
|
|
@@ -86,10 +124,31 @@ export declare class DatabaseConnection {
|
|
|
86
124
|
* unboundedly between automatic checkpoints (auto-fires at 1000
|
|
87
125
|
* pages by default; large indexAll runs blow past that).
|
|
88
126
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
127
|
+
* Runs on a WORKER THREAD with its own connection: on a multi-GB index
|
|
128
|
+
* these pragmas are minutes of synchronous IO (a 95k-file kernel index
|
|
129
|
+
* left a 593MB WAL whose checkpoint alone blew the #850 watchdog's 60s
|
|
130
|
+
* window and got a COMPLETED index SIGKILLed at the finish line). WAL
|
|
131
|
+
* checkpointing from a second connection is standard SQLite; `PRAGMA
|
|
132
|
+
* optimize` persists its statistics in sqlite_stat tables, so the main
|
|
133
|
+
* connection benefits the same. The main thread just awaits a message,
|
|
134
|
+
* so the event loop — and the watchdog heartbeat — keep turning.
|
|
135
|
+
*
|
|
136
|
+
* Everything is silently swallowed on failure — best-effort
|
|
137
|
+
* optimization, never load-bearing for correctness. If worker threads
|
|
138
|
+
* are unavailable, falls back to a bounded in-line `PRAGMA optimize`
|
|
139
|
+
* and SKIPS the checkpoint (the final close() checkpoints after the
|
|
140
|
+
* CLI has already disarmed its watchdog).
|
|
141
|
+
*/
|
|
142
|
+
runMaintenance(): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Run pragmas on a worker thread against its own connection to this DB
|
|
145
|
+
* (shared machinery for {@link runMaintenance} and
|
|
146
|
+
* {@link checkpointWalPassive}). Each pragma is individually best-effort;
|
|
147
|
+
* the whole call is best-effort. `inlineFallback` (if any) runs on THIS
|
|
148
|
+
* connection only when worker threads are unavailable — keep it to pragmas
|
|
149
|
+
* that are safe to run synchronously on the main thread.
|
|
91
150
|
*/
|
|
92
|
-
|
|
151
|
+
private runPragmasOffThread;
|
|
93
152
|
/**
|
|
94
153
|
* Close the database connection
|
|
95
154
|
*/
|
|
@@ -119,4 +178,24 @@ export declare const DATABASE_FILENAME = "codegraph.db";
|
|
|
119
178
|
* Get the default database path for a project
|
|
120
179
|
*/
|
|
121
180
|
export declare function getDatabasePath(projectRoot: string): string;
|
|
181
|
+
/**
|
|
182
|
+
* Delete a database file and its WAL sidecars (`-wal`/`-shm`).
|
|
183
|
+
*
|
|
184
|
+
* This is how a FULL re-index discards an existing database — rather than
|
|
185
|
+
* opening the old graph and DELETE-ing every row. On a large or pre-fix
|
|
186
|
+
* poisoned index (e.g. an old graph that scanned an ignored gitlink corpus into
|
|
187
|
+
* ~1.6M nodes with a multi-GB WAL, #1065) the per-row `nodes_fts` delete-trigger
|
|
188
|
+
* churn blocks the main thread long enough to trip the #850 liveness watchdog
|
|
189
|
+
* before indexing even starts, so the rebuild could never recover the bad state
|
|
190
|
+
* (#1067). Unlinking is O(1) regardless of DB size and also reclaims the disk
|
|
191
|
+
* the bloated WAL would otherwise keep.
|
|
192
|
+
*
|
|
193
|
+
* POSIX removes the directory entry even while another process (a daemon/MCP
|
|
194
|
+
* server) still holds the file open; that holder heals via `reopenIfReplaced`
|
|
195
|
+
* (#925). On Windows a live holder can make the unlink fail with EBUSY/EPERM —
|
|
196
|
+
* that is thrown for the caller to surface ("stop the other process and retry").
|
|
197
|
+
* The `-wal`/`-shm` sidecars are best-effort: SQLite recreates them on the next
|
|
198
|
+
* open, so a leftover sidecar is harmless.
|
|
199
|
+
*/
|
|
200
|
+
export declare function removeDatabaseFiles(dbPath: string): void;
|
|
122
201
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/db/migrations.d.ts
CHANGED
package/dist/db/queries.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Prepared statements for CRUD operations on the knowledge graph.
|
|
5
5
|
*/
|
|
6
6
|
import { SqliteDatabase } from './sqlite-adapter';
|
|
7
|
-
import { Node, Edge, FileRecord, UnresolvedReference, NodeKind, EdgeKind, GraphStats, SearchOptions, SearchResult } from '../types';
|
|
7
|
+
import { Node, Edge, FileRecord, UnresolvedReference, NodeKind, EdgeKind, Language, GraphStats, SearchOptions, SearchResult } from '../types';
|
|
8
8
|
/**
|
|
9
9
|
* Query builder for the knowledge graph database
|
|
10
10
|
*/
|
|
@@ -14,6 +14,8 @@ export declare class QueryBuilder {
|
|
|
14
14
|
private nodeCache;
|
|
15
15
|
private readonly maxCacheSize;
|
|
16
16
|
private stmts;
|
|
17
|
+
private segmentedNames;
|
|
18
|
+
private static readonly MAX_SEGMENTED_NAMES;
|
|
17
19
|
constructor(db: SqliteDatabase);
|
|
18
20
|
/** Set the normalized project-name tokens used to down-weight non-discriminative
|
|
19
21
|
* query words in path scoring (#720). Called once when the project opens. */
|
|
@@ -24,6 +26,12 @@ export declare class QueryBuilder {
|
|
|
24
26
|
* Insert a new node
|
|
25
27
|
*/
|
|
26
28
|
insertNode(node: Node): void;
|
|
29
|
+
/** Which node kinds contribute their name to the segment vocabulary — the
|
|
30
|
+
* single gate shared by insertNode, updateNode, and the rebuild page query
|
|
31
|
+
* (getDistinctNodeNames), so the write paths can't drift apart. */
|
|
32
|
+
private isSegmentableKind;
|
|
33
|
+
/** Write `name`'s segments into name_segment_vocab (idempotent). */
|
|
34
|
+
private insertNameSegments;
|
|
27
35
|
/**
|
|
28
36
|
* Insert multiple nodes in a transaction
|
|
29
37
|
*/
|
|
@@ -40,6 +48,44 @@ export declare class QueryBuilder {
|
|
|
40
48
|
* Delete all nodes for a file
|
|
41
49
|
*/
|
|
42
50
|
deleteNodesByFile(filePath: string): void;
|
|
51
|
+
/** Wipe the segment vocabulary. A full index calls this at its start; the
|
|
52
|
+
* node write path repopulates it as files (re-)index, so the end state is
|
|
53
|
+
* exactly the current names with no orphan rows. */
|
|
54
|
+
clearNameSegmentVocab(): void;
|
|
55
|
+
/** True when the vocab has no rows — an index built before the table existed.
|
|
56
|
+
* `sync` uses this to heal such databases (see rebuildNameSegmentVocabFrom). */
|
|
57
|
+
isNameSegmentVocabEmpty(): boolean;
|
|
58
|
+
/** One page of distinct segmentable node names, for batched vocab rebuilds
|
|
59
|
+
* (file basenames and import specifiers are excluded from the vocab — see
|
|
60
|
+
* insertNode). */
|
|
61
|
+
getDistinctNodeNames(limit: number, offset: number): string[];
|
|
62
|
+
/** Insert segments for a batch of names in one transaction (vocab heal path). */
|
|
63
|
+
insertNameSegmentsBatch(names: string[]): void;
|
|
64
|
+
/**
|
|
65
|
+
* Names whose segments cover at least `minWords` distinct PROMPT WORDS —
|
|
66
|
+
* the co-occurrence probe behind the prompt hook's medium tier: the words
|
|
67
|
+
* "state" and "machine" both being segments of `OrderStateMachine` is strong
|
|
68
|
+
* evidence the prompt names that symbol in prose. Ordered by coverage.
|
|
69
|
+
*
|
|
70
|
+
* Takes (segment variant → original word) pairs and folds variants back to
|
|
71
|
+
* their word INSIDE the SQL: a name matching both `service` and `services`
|
|
72
|
+
* counts ONE word, not two. Counting raw variants let plural-variant pairs
|
|
73
|
+
* of a single word tie with genuine two-word matches and — because ORDER
|
|
74
|
+
* BY/LIMIT run here, before any JS-side re-check — crowd a real match past
|
|
75
|
+
* the LIMIT on vocab-heavy repos (#1146).
|
|
76
|
+
*/
|
|
77
|
+
getSegmentCoOccurrence(variants: Array<{
|
|
78
|
+
segment: string;
|
|
79
|
+
word: string;
|
|
80
|
+
}>, minWords: number, limit: number): Array<{
|
|
81
|
+
name: string;
|
|
82
|
+
matches: number;
|
|
83
|
+
}>;
|
|
84
|
+
/** How many distinct names each segment appears in — the rarity signal that
|
|
85
|
+
* separates a discriminative word ("checkout") from a ubiquitous one ("state"). */
|
|
86
|
+
getSegmentNameCounts(segments: string[]): Map<string, number>;
|
|
87
|
+
/** Names containing the given segment (rare-single-word tier). */
|
|
88
|
+
getNamesForSegment(segment: string, limit: number): string[];
|
|
43
89
|
/**
|
|
44
90
|
* Get a node by ID
|
|
45
91
|
*/
|
|
@@ -152,10 +198,32 @@ export declare class QueryBuilder {
|
|
|
152
198
|
* Get all nodes in the database
|
|
153
199
|
*/
|
|
154
200
|
getAllNodes(): Node[];
|
|
201
|
+
/**
|
|
202
|
+
* Stream nodes of one language whose `decorators` JSON array contains
|
|
203
|
+
* `decorator`. The LIKE on the JSON text is a cheap index-free pre-filter
|
|
204
|
+
* (a decorator name can appear as a substring of another), so callers must
|
|
205
|
+
* still exact-check `node.decorators.includes(decorator)`. Exists so the
|
|
206
|
+
* kotlin expect/actual synthesizer never materializes the whole node table
|
|
207
|
+
* the way `getAllNodes().filter(...)` did — that array alone OOM'd Node's
|
|
208
|
+
* default heap on a 2M-node graph (#1212).
|
|
209
|
+
*/
|
|
210
|
+
iterateNodesByLanguageWithDecorator(language: Language, decorator: string): IterableIterator<Node>;
|
|
211
|
+
/**
|
|
212
|
+
* Distinct languages present in the files table. One indexed aggregate —
|
|
213
|
+
* lets the dynamic-edge synthesizers skip passes for languages the project
|
|
214
|
+
* doesn't contain at all (a Kotlin pass has no work on a pure-C repo), so
|
|
215
|
+
* their cost is zero rather than a full-graph scan that finds nothing (#1212).
|
|
216
|
+
*/
|
|
217
|
+
getDistinctFileLanguages(): Set<string>;
|
|
155
218
|
/**
|
|
156
219
|
* Get nodes by exact name match (uses idx_nodes_name index)
|
|
157
220
|
*/
|
|
158
221
|
getNodesByName(name: string): Node[];
|
|
222
|
+
/**
|
|
223
|
+
* Nodes whose name starts with `prefix`, by index range scan (a LIKE would
|
|
224
|
+
* skip idx_nodes_name under SQLite's default case-insensitive LIKE).
|
|
225
|
+
*/
|
|
226
|
+
getNodesByNamePrefix(prefix: string, limit?: number): Node[];
|
|
159
227
|
/**
|
|
160
228
|
* Get nodes by exact qualified name match (uses idx_nodes_qualified_name index)
|
|
161
229
|
*/
|
|
@@ -326,6 +394,8 @@ export declare class QueryBuilder {
|
|
|
326
394
|
getCrossFileIncomingEdgesWithTarget(filePath: string): Array<Edge & {
|
|
327
395
|
targetName: string;
|
|
328
396
|
targetKind: NodeKind;
|
|
397
|
+
sourceFilePath: string;
|
|
398
|
+
sourceLanguage: Language;
|
|
329
399
|
}>;
|
|
330
400
|
/**
|
|
331
401
|
* Insert or update a file record
|
|
@@ -373,12 +443,18 @@ export declare class QueryBuilder {
|
|
|
373
443
|
*/
|
|
374
444
|
getUnresolvedReferences(): UnresolvedReference[];
|
|
375
445
|
/**
|
|
376
|
-
* Get the count of
|
|
446
|
+
* Get the count of PENDING (never-attempted) references without loading
|
|
447
|
+
* them into memory. Rows marked status='failed' — attempted by a completed
|
|
448
|
+
* pass, no match — are excluded: they are not outstanding work, only retry
|
|
449
|
+
* candidates for the #1240 sweep, so they must not trip the #1187 orphan
|
|
450
|
+
* sweep or the `status` pending-refs warning.
|
|
377
451
|
*/
|
|
378
452
|
getUnresolvedReferencesCount(): number;
|
|
379
453
|
/**
|
|
380
|
-
* Get a batch of unresolved references using LIMIT/OFFSET
|
|
381
|
-
* Used to process references in bounded memory chunks
|
|
454
|
+
* Get a batch of PENDING unresolved references using LIMIT/OFFSET
|
|
455
|
+
* pagination. Used to process references in bounded memory chunks; failed
|
|
456
|
+
* rows are excluded so the batched drain loop terminates once every row
|
|
457
|
+
* has been attempted.
|
|
382
458
|
*/
|
|
383
459
|
getUnresolvedReferencesBatch(offset: number, limit: number): UnresolvedReference[];
|
|
384
460
|
/**
|
|
@@ -389,6 +465,13 @@ export declare class QueryBuilder {
|
|
|
389
465
|
* Get all distinct node names (lightweight — just name strings for pre-filtering)
|
|
390
466
|
*/
|
|
391
467
|
getAllNodeNames(): string[];
|
|
468
|
+
/**
|
|
469
|
+
* Stream the distinct node names one row at a time — the incremental
|
|
470
|
+
* counterpart to {@link getAllNodeNames} for callers that need to yield
|
|
471
|
+
* to the event loop mid-scan (resolver cache warm-up on multi-million-node
|
|
472
|
+
* indexes). Fresh statement per call: the iterator holds an open cursor.
|
|
473
|
+
*/
|
|
474
|
+
iterateNodeNames(): IterableIterator<string>;
|
|
392
475
|
/**
|
|
393
476
|
* Get unresolved references scoped to specific file paths.
|
|
394
477
|
* Uses the idx_unresolved_file_path index for efficient lookup.
|
|
@@ -411,6 +494,35 @@ export declare class QueryBuilder {
|
|
|
411
494
|
referenceName: string;
|
|
412
495
|
referenceKind: string;
|
|
413
496
|
}>): void;
|
|
497
|
+
/**
|
|
498
|
+
* Mark refs a completed resolution pass could not resolve as status='failed'
|
|
499
|
+
* instead of deleting them (#1240). Failed rows are invisible to the pending
|
|
500
|
+
* count/batch readers (so drain loops and the #1187 orphan sweep still
|
|
501
|
+
* terminate) but stay queryable by name_tail so a later sync can retry them
|
|
502
|
+
* when a changed file introduces a symbol that could satisfy them. name_tail
|
|
503
|
+
* is (re)written here so rows inserted before the v8 migration get their
|
|
504
|
+
* tail the first time they're attempted.
|
|
505
|
+
*/
|
|
506
|
+
markReferencesFailed(refs: Array<{
|
|
507
|
+
fromNodeId: string;
|
|
508
|
+
referenceName: string;
|
|
509
|
+
referenceKind: string;
|
|
510
|
+
}>): void;
|
|
511
|
+
/**
|
|
512
|
+
* Failed refs whose name tail matches one of the given symbol names — the
|
|
513
|
+
* candidates a sync should retry after files carrying those names changed
|
|
514
|
+
* (#1240). Names matching more than `perNameCeiling` failed refs are
|
|
515
|
+
* skipped entirely: at that population a name is external/builtin noise
|
|
516
|
+
* (`get`, `map`, …) that one new definition won't resolve — the same
|
|
517
|
+
* rationale as resolution's AMBIGUOUS_NAME_CEILING (#999) — and retrying an
|
|
518
|
+
* arbitrary subset would be both wasted work and incoherent coverage.
|
|
519
|
+
*/
|
|
520
|
+
getRetryableFailedReferences(names: string[], perNameCeiling?: number): UnresolvedReference[];
|
|
521
|
+
/**
|
|
522
|
+
* Distinct node names present in the given files — the symbol names a sync
|
|
523
|
+
* pass uses to look up retryable failed refs after those files changed.
|
|
524
|
+
*/
|
|
525
|
+
getNodeNamesByFiles(filePaths: string[]): string[];
|
|
414
526
|
/**
|
|
415
527
|
* Lightweight (nodes, edges) count snapshot. Used around an index/sync
|
|
416
528
|
* run to compute true additions across extraction + resolution +
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WAL checkpoint valve — bounds WAL growth while auto-checkpointing is
|
|
3
|
+
* deferred during a bulk index (#1231).
|
|
4
|
+
*
|
|
5
|
+
* Why deferral: SQLite's default `wal_autocheckpoint` (1000 pages) re-writes
|
|
6
|
+
* hot B-tree/FTS pages into the main DB file over and over during a bulk
|
|
7
|
+
* index — measured at ~95% of ALL disk I/O, and the difference between 45s
|
|
8
|
+
* and 19+ minutes on HDD-class storage (150 random IOPS). Deferring
|
|
9
|
+
* checkpoints turns the store into pure sequential WAL appends; each backfill
|
|
10
|
+
* pass writes distinct pages once, in page order (≈ sequential).
|
|
11
|
+
*
|
|
12
|
+
* Why a valve: unbounded deferral is its own failure mode, both measured in
|
|
13
|
+
* the #1231 repro. The WAL duplicates hot pages per COMMIT, so it grows far
|
|
14
|
+
* faster than the DB (5.9GB WAL for a ~340MB DB on a 3.3k-file index) —
|
|
15
|
+
* filling the disk, and poisoning every subsequent read that must page
|
|
16
|
+
* through it (the first resolution-phase read blocked the main thread >60s
|
|
17
|
+
* and the #850 liveness watchdog killed the healthy index). The valve
|
|
18
|
+
* watches WAL growth on a timer and, past a soft threshold, backfills with
|
|
19
|
+
* `PRAGMA wal_checkpoint(PASSIVE)` on a worker-thread connection — PASSIVE
|
|
20
|
+
* never blocks the writer, and off-thread means the main thread (and the
|
|
21
|
+
* watchdog heartbeat) keep turning regardless of how long a backfill takes.
|
|
22
|
+
*
|
|
23
|
+
* The load-bearing subtlety: a WAL file's SIZE never shrinks. After a full
|
|
24
|
+
* backfill, the writer's next commit RESTARTS the WAL from the top and the
|
|
25
|
+
* frames recycle inside the same file — so raw size says nothing about the
|
|
26
|
+
* un-backfilled backlog, and a size-triggered valve degenerates into firing
|
|
27
|
+
* (and pausing the writer) forever once the file passes its threshold
|
|
28
|
+
* (measured: guava crawled at ~9min per 160 files). Instead the valve
|
|
29
|
+
* tracks `sizeAtLastFullBackfill` — refreshed whenever a checkpoint reports
|
|
30
|
+
* `log === checkpointed` (everything backfilled) — and triggers on GROWTH
|
|
31
|
+
* beyond that baseline, which only happens when genuinely un-backfilled
|
|
32
|
+
* frames push past the file's high-water mark.
|
|
33
|
+
*
|
|
34
|
+
* Backpressure: if the writer outruns the checkpointer past a hard cap of
|
|
35
|
+
* growth (2× soft), {@link backpressure} pauses the writer (at a safe,
|
|
36
|
+
* between-transactions boundary) until a FULL backfill lands. One in-flight
|
|
37
|
+
* pass is not enough: on a disk saturated by the writer, every concurrent
|
|
38
|
+
* PASSIVE pass is already stale by the time it finishes (the writer appended
|
|
39
|
+
* past its snapshot), so neither SQLite's WAL wrap nor the baseline ever
|
|
40
|
+
* trigger and the WAL grows without bound (measured: 5.9GB on guava at 150
|
|
41
|
+
* IOPS, then a >60s read stall and a watchdog kill). With the writer parked,
|
|
42
|
+
* the next pass covers everything, the WAL wraps on the following commit,
|
|
43
|
+
* and the pause is the disk's honest catch-up cost — the correct terminal
|
|
44
|
+
* mode when hardware genuinely can't keep up with the append rate.
|
|
45
|
+
*/
|
|
46
|
+
import type { DatabaseConnection } from './index';
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the valve's soft threshold from the `CODEGRAPH_WAL_VALVE_MB`
|
|
49
|
+
* override; non-numeric / non-positive values fall back to the default.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveWalValveMb(envVal: string | undefined): number;
|
|
52
|
+
export declare class WalCheckpointValve {
|
|
53
|
+
private readonly db;
|
|
54
|
+
private readonly intervalMs;
|
|
55
|
+
private readonly log;
|
|
56
|
+
private timer;
|
|
57
|
+
private inflight;
|
|
58
|
+
/** Writer pause in progress (hard cap breached): passes loop until a full backfill. */
|
|
59
|
+
private pause;
|
|
60
|
+
/**
|
|
61
|
+
* WAL file size observed when a checkpoint last reported the ENTIRE WAL
|
|
62
|
+
* backfilled. Growth is measured against this baseline — see the header
|
|
63
|
+
* comment for why absolute size cannot be used.
|
|
64
|
+
*/
|
|
65
|
+
private sizeAtLastFullBackfill;
|
|
66
|
+
private readonly softBytes;
|
|
67
|
+
private readonly hardBytes;
|
|
68
|
+
constructor(db: DatabaseConnection, softMb?: number, intervalMs?: number, log?: (msg: string) => void);
|
|
69
|
+
private mb;
|
|
70
|
+
/** Un-backfilled growth estimate: bytes the WAL has grown past the last full backfill. */
|
|
71
|
+
private growthBytes;
|
|
72
|
+
/** Begin watching the WAL. Idempotent; the timer never holds the loop open. */
|
|
73
|
+
start(): void;
|
|
74
|
+
/** Stop watching. Any in-flight checkpoint keeps running — await drain(). */
|
|
75
|
+
stop(): void;
|
|
76
|
+
/** One poll: fire an off-thread passive checkpoint when growth passes the soft threshold. */
|
|
77
|
+
check(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Writer-side backstop, called at a between-transactions boundary. Returns
|
|
80
|
+
* null (no wait) while growth is under the hard cap; past it, returns a
|
|
81
|
+
* promise that resolves only once a FULL backfill has landed — see the
|
|
82
|
+
* header comment for why a single pass is not enough on a saturated disk.
|
|
83
|
+
*/
|
|
84
|
+
backpressure(): Promise<void> | null;
|
|
85
|
+
/** Await any in-flight checkpoint and writer pause. */
|
|
86
|
+
drain(): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Phase-boundary fold: backfill the ENTIRE WAL now (off-thread, awaited).
|
|
89
|
+
* Called between bulk phases — e.g. after parsing, before resolution's
|
|
90
|
+
* first reads — so the next phase never pages a bulk-write-sized WAL on
|
|
91
|
+
* the main thread (the post-parse read against a multi-GB WAL is what
|
|
92
|
+
* blew the #850 watchdog's 60s window in the #1231 repro). The await
|
|
93
|
+
* keeps the event loop (and the watchdog heartbeat) turning.
|
|
94
|
+
*/
|
|
95
|
+
foldNow(): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* With the writer parked on the returned promise, loop passive passes until
|
|
98
|
+
* one reports the entire WAL backfilled (typically the second: the first
|
|
99
|
+
* drains the pass that was already running against a stale snapshot). Gives
|
|
100
|
+
* up after a bounded number of passes — e.g. a reader pinning the WAL —
|
|
101
|
+
* because unbounded WAL growth degrades; a wedged writer never recovers.
|
|
102
|
+
*/
|
|
103
|
+
private backfillFully;
|
|
104
|
+
private fire;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=wal-valve.d.ts.map
|
package/dist/directory.d.ts
CHANGED
|
@@ -86,10 +86,14 @@ export declare function findIndexedSubprojectRoots(root: string, opts?: {
|
|
|
86
86
|
max?: number;
|
|
87
87
|
}): string[];
|
|
88
88
|
/**
|
|
89
|
-
* Does `prompt` contain an explicit structural keyword
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
89
|
+
* Does `prompt` contain an explicit structural keyword? A keyword is a strong,
|
|
90
|
+
* self-contained signal, so the front-load hook fires on it directly — no graph
|
|
91
|
+
* check needed. (A *code-token* match, by contrast, is only a candidate the
|
|
92
|
+
* hook verifies against the graph first; see {@link extractCodeTokens}.)
|
|
93
|
+
* Coverage is multilingual (#994, #1126): the ~29 languages with the largest
|
|
94
|
+
* developer populations, across Latin, Cyrillic, Greek, CJK, Hangul, Arabic,
|
|
95
|
+
* Hebrew, Thai, and Devanagari scripts. Languages beyond the keyword table
|
|
96
|
+
* still fire through the language-agnostic code-token path.
|
|
93
97
|
*/
|
|
94
98
|
export declare function hasStructuralKeyword(prompt: string): boolean;
|
|
95
99
|
/**
|
|
@@ -110,7 +114,7 @@ export declare function extractCodeTokens(prompt: string): string[];
|
|
|
110
114
|
/**
|
|
111
115
|
* Cheap, graph-free candidate gate for the front-load hook: could `prompt` be a
|
|
112
116
|
* structural / flow / impact / "where-how" question worth front-loading context
|
|
113
|
-
* for? True on an explicit keyword
|
|
117
|
+
* for? True on an explicit keyword in any covered language (#994, #1126) OR an
|
|
114
118
|
* identifier-shaped token. A keyword is sufficient to fire on its own; a
|
|
115
119
|
* token-only match is only a candidate the hook then verifies against the graph
|
|
116
120
|
* (a brand name like `JavaScript` is token-shaped but isn't a symbol). Every
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ExtractionResult, Language } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* CfmlExtractor - Extracts code relationships from CFML source (.cfc/.cfm).
|
|
4
|
+
*
|
|
5
|
+
* tree-sitter-cfml splits CFML into two related grammars: `cfml` (tag-based —
|
|
6
|
+
* `<cfcomponent>`/`<cffunction>`/HTML) and `cfscript` (modern bare-script
|
|
7
|
+
* `component { ... }` syntax). The `cfml` grammar's own injections.scm treats
|
|
8
|
+
* bare-script content as an opaque blob meant to be re-parsed by `cfscript` —
|
|
9
|
+
* that re-parsing only happens at the editor/highlighting layer, not in the
|
|
10
|
+
* raw AST, so this extractor replicates it: a file whose first real token
|
|
11
|
+
* isn't `<` is delegated wholesale to the cfscript grammar (the dominant
|
|
12
|
+
* modern style); otherwise the file is walked tag-by-tag with the cfml
|
|
13
|
+
* grammar, delegating any `<cfscript>` tag bodies the same way.
|
|
14
|
+
*/
|
|
15
|
+
export declare class CfmlExtractor {
|
|
16
|
+
private filePath;
|
|
17
|
+
private source;
|
|
18
|
+
private language;
|
|
19
|
+
private nodes;
|
|
20
|
+
private edges;
|
|
21
|
+
private unresolvedReferences;
|
|
22
|
+
private errors;
|
|
23
|
+
/** `language` is the file's detected language — `'cfml'` for `.cfc`/`.cfm`, `'cfscript'` for `.cfs`. Both dialect-switch internally; this only controls the language tag stamped onto emitted nodes/refs. */
|
|
24
|
+
constructor(filePath: string, source: string, language?: Language);
|
|
25
|
+
extract(): ExtractionResult;
|
|
26
|
+
/** Modern bare-script `.cfc`/`.cfm`: delegate the whole file to the cfscript grammar. */
|
|
27
|
+
private extractBareScript;
|
|
28
|
+
/** Legacy tag-based CFML: walk `<cfcomponent>`/`<cffunction>`, delegating `<cfscript>` bodies. */
|
|
29
|
+
private extractTagBased;
|
|
30
|
+
/** Build the file's own `kind:'file'` node, spanning the whole source. Tag-based files need this explicitly — unlike `extractBareScript` (which delegates the whole file to `TreeSitterExtractor` and inherits its file node), `extractTagBased` walks the tree itself and has no other source of one. */
|
|
31
|
+
private createFileNode;
|
|
32
|
+
/**
|
|
33
|
+
* Walks `program`'s named children with a single forward cursor (not an
|
|
34
|
+
* index loop) — `extractComponent` consumes a variable run of FOLLOWING
|
|
35
|
+
* siblings as the component body (see its doc comment), so this must
|
|
36
|
+
* resume from whatever it last consumed rather than revisiting those same
|
|
37
|
+
* cffunction/cfscript siblings a second time as bogus top-level symbols.
|
|
38
|
+
*/
|
|
39
|
+
private walkProgram;
|
|
40
|
+
/**
|
|
41
|
+
* `<cfcomponent extends="Base" implements="IFoo,IBar">...</cfcomponent>`.
|
|
42
|
+
* The grammar's implicit-end-tag scanner means component body content
|
|
43
|
+
* (cffunction tags, cfscript tags, etc.) appears as the open tag's FOLLOWING
|
|
44
|
+
* siblings in `program`, not nested children — walk forward to the matching
|
|
45
|
+
* cf_component_close_tag.
|
|
46
|
+
*/
|
|
47
|
+
private extractComponent;
|
|
48
|
+
/**
|
|
49
|
+
* `<cffunction name="..." access="..." returntype="...">...</cffunction>`.
|
|
50
|
+
* `parentClassId` decides `method` vs top-level `function`; `containerId` is
|
|
51
|
+
* the `contains`-edge target (the class when inside one, otherwise the file
|
|
52
|
+
* node for a bare top-level cffunction) — kept separate so a top-level
|
|
53
|
+
* function still gets a containment edge without being misclassified as a
|
|
54
|
+
* method of the file. A method's qualifiedName is scoped under
|
|
55
|
+
* `parentClassName` (`TagService::save`, the same `Class::member` shape the
|
|
56
|
+
* generic extractor produces) so type-validated method resolution can match.
|
|
57
|
+
*/
|
|
58
|
+
private extractFunctionTag;
|
|
59
|
+
/**
|
|
60
|
+
* Recursively delegates any `cf_script_tag`/`cf_query_tag` found within
|
|
61
|
+
* `node`'s subtree — e.g. a `<cfscript>`/`<cfquery>` nested inside
|
|
62
|
+
* `<cfif>`/`<cfloop>`/`<cftry>` control-flow tags, which (unlike
|
|
63
|
+
* `<cfcomponent>`'s body — see the implicit-end-tag note on `extractComponent`)
|
|
64
|
+
* ARE normal children, just possibly several levels deep, so a direct-children
|
|
65
|
+
* check misses them. Does not descend into a nested `cf_function_tag` — that
|
|
66
|
+
* has its own scope and is walked separately. `parentClassName` rides along
|
|
67
|
+
* so a `<cfscript>` at component scope classifies its functions as methods
|
|
68
|
+
* scoped under the component.
|
|
69
|
+
*/
|
|
70
|
+
private delegateNestedTags;
|
|
71
|
+
/**
|
|
72
|
+
* Delegate a `<cfscript>...</cfscript>` tag body to the cfscript grammar.
|
|
73
|
+
* With `parentClassName` set (the block sits at component scope), functions
|
|
74
|
+
* declared at the script's top level are the component's methods
|
|
75
|
+
* (`<cfcomponent><cfscript>function configure(){}` — the standard ColdBox
|
|
76
|
+
* ModuleConfig shape): they're re-kinded `function` → `method`, and every
|
|
77
|
+
* merged symbol's qualifiedName is prefixed with the component scope
|
|
78
|
+
* (`configure` → `ModuleConfig::configure`) so type-validated method
|
|
79
|
+
* resolution can match them. Functions nested inside another function
|
|
80
|
+
* (closures) keep kind `function`.
|
|
81
|
+
*/
|
|
82
|
+
private delegateScriptTag;
|
|
83
|
+
/**
|
|
84
|
+
* Delegate a `<cfquery>...</cfquery>` tag's SQL body to the `cfquery` grammar.
|
|
85
|
+
* `#hash#` expressions inside the SQL (e.g. `#getCurrentUser().getId()#` in a
|
|
86
|
+
* WHERE clause) are real CFML calls/references — tree-sitter-cfml's `cfquery`
|
|
87
|
+
* grammar parses them structurally (same `call_expression`/`member_expression`
|
|
88
|
+
* shape as cfscript), so without this delegation they're silently dropped as
|
|
89
|
+
* opaque SQL text. The grammar models no other symbols, so only call/reference
|
|
90
|
+
* extraction is relevant here — unlike `delegateScriptTag`, there are no nodes
|
|
91
|
+
* or contains-edges to merge.
|
|
92
|
+
*/
|
|
93
|
+
private delegateQueryTag;
|
|
94
|
+
/** Read a `cf_attribute`'s value by name from a tag node's direct `cf_attribute`/`cf_tag_attributes` children. */
|
|
95
|
+
private tagAttr;
|
|
96
|
+
private componentNameFromPath;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Sniff whether CFML source is bare-script (`component { ... }`, modern style)
|
|
100
|
+
* vs tag-based (`<cfcomponent>`, `<cfif>`, HTML). Skips a leading UTF-8 BOM
|
|
101
|
+
* (endemic in CFML's Windows-editor history — 17% of ColdBox's files carry
|
|
102
|
+
* one; both grammars parse fine with it once routed correctly), whitespace,
|
|
103
|
+
* and `//`/`/* *\/` comments to find the first real token; tag-based files
|
|
104
|
+
* start with `<`, script-based files don't.
|
|
105
|
+
*/
|
|
106
|
+
export declare function isBareScriptCfml(source: string): boolean;
|
|
107
|
+
//# sourceMappingURL=cfml-extractor.d.ts.map
|