@colbymchenry/codegraph 1.1.5 → 1.2.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/db/index.d.ts +20 -0
- package/dist/extraction/languages/c-cpp.d.ts +17 -0
- package/dist/extraction/tree-sitter-types.d.ts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/resolution/callback-synthesizer.d.ts +1 -1
- package/dist/resolution/cooperative-yield.d.ts +32 -0
- package/dist/resolution/index.d.ts +11 -2
- package/dist/resolution/name-matcher.d.ts +22 -0
- package/npm-shim.js +25 -3
- package/package.json +7 -7
package/README.md
CHANGED
package/dist/db/index.d.ts
CHANGED
|
@@ -119,4 +119,24 @@ export declare const DATABASE_FILENAME = "codegraph.db";
|
|
|
119
119
|
* Get the default database path for a project
|
|
120
120
|
*/
|
|
121
121
|
export declare function getDatabasePath(projectRoot: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* Delete a database file and its WAL sidecars (`-wal`/`-shm`).
|
|
124
|
+
*
|
|
125
|
+
* This is how a FULL re-index discards an existing database — rather than
|
|
126
|
+
* opening the old graph and DELETE-ing every row. On a large or pre-fix
|
|
127
|
+
* poisoned index (e.g. an old graph that scanned an ignored gitlink corpus into
|
|
128
|
+
* ~1.6M nodes with a multi-GB WAL, #1065) the per-row `nodes_fts` delete-trigger
|
|
129
|
+
* churn blocks the main thread long enough to trip the #850 liveness watchdog
|
|
130
|
+
* before indexing even starts, so the rebuild could never recover the bad state
|
|
131
|
+
* (#1067). Unlinking is O(1) regardless of DB size and also reclaims the disk
|
|
132
|
+
* the bloated WAL would otherwise keep.
|
|
133
|
+
*
|
|
134
|
+
* POSIX removes the directory entry even while another process (a daemon/MCP
|
|
135
|
+
* server) still holds the file open; that holder heals via `reopenIfReplaced`
|
|
136
|
+
* (#925). On Windows a live holder can make the unlink fail with EBUSY/EPERM —
|
|
137
|
+
* that is thrown for the caller to surface ("stop the other process and retry").
|
|
138
|
+
* The `-wal`/`-shm` sidecars are best-effort: SQLite recreates them on the next
|
|
139
|
+
* open, so a leftover sidecar is harmless.
|
|
140
|
+
*/
|
|
141
|
+
export declare function removeDatabaseFiles(dbPath: string): void;
|
|
122
142
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -47,5 +47,22 @@ export declare const cExtractor: LanguageExtractor;
|
|
|
47
47
|
* so C's heavier use of `struct TAG var;` never reaches it.
|
|
48
48
|
*/
|
|
49
49
|
export declare function blankCppExportMacros(source: string): string;
|
|
50
|
+
export declare function blankCppInlineMacros(source: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Universal fallback (any macro, no list) for a C/C++ function name still mangled
|
|
53
|
+
* because a macro we don't blank sat in front of the return type: `MACRO Ret
|
|
54
|
+
* name(…)` / `Ret MACRO name(…)` misparse so the return type is glued onto the
|
|
55
|
+
* name ("Ret name", "char_t* to_str(double v)"). Recover the real identifier —
|
|
56
|
+
* the token immediately before the parameter list (or the last token). This runs
|
|
57
|
+
* AFTER the curated pre-parse blank, so it only ever sees the residual tail that
|
|
58
|
+
* blanking didn't already fix cleanly (which also recovers the return type).
|
|
59
|
+
*
|
|
60
|
+
* Safe by construction: only touches an ALREADY-mangled name — one with an
|
|
61
|
+
* internal space that isn't a legit `operator …`/destructor — so a well-formed
|
|
62
|
+
* name is returned unchanged. Guarded against the two ways it could mis-pick:
|
|
63
|
+
* the `Ret (name)` parenthesized-name idiom (left as-is, ambiguous), and a token
|
|
64
|
+
* that is a bare primitive/keyword rather than a real identifier.
|
|
65
|
+
*/
|
|
66
|
+
export declare function recoverMangledCppName(name: string): string;
|
|
50
67
|
export declare const cppExtractor: LanguageExtractor;
|
|
51
68
|
//# sourceMappingURL=c-cpp.d.ts.map
|
|
@@ -114,6 +114,15 @@ export interface LanguageExtractor {
|
|
|
114
114
|
returnField?: string;
|
|
115
115
|
/** Override symbol name extraction (e.g. ObjC multi-part selectors). */
|
|
116
116
|
resolveName?: (node: SyntaxNode, source: string) => string | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* Post-process an already-extracted name to recover a real identifier from a
|
|
119
|
+
* name still mangled by a macro the pre-parse didn't blank (C/C++:
|
|
120
|
+
* `MACRO Ret name(` misparses to the name "Ret name"). Applied to every name
|
|
121
|
+
* this extractor produces, so it MUST be a no-op on a well-formed name — only
|
|
122
|
+
* C/C++ set it, because a mangled name there is unambiguous (an internal space),
|
|
123
|
+
* whereas e.g. Kotlin/Scala backtick identifiers legitimately contain spaces.
|
|
124
|
+
*/
|
|
125
|
+
recoverMangledName?: (name: string) => string;
|
|
117
126
|
/** Extract property name when the generic name walk fails (e.g. ObjC @property). */
|
|
118
127
|
extractPropertyName?: (node: SyntaxNode, source: string) => string | null;
|
|
119
128
|
/** Extract signature from node */
|
|
@@ -140,6 +149,14 @@ export interface LanguageExtractor {
|
|
|
140
149
|
extraClassNodeTypes?: string[];
|
|
141
150
|
/** Whether methods can be top-level without enclosing class (Go: true) */
|
|
142
151
|
methodsAreTopLevel?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Skip a bodiless class node as a forward declaration / elaborated type,
|
|
154
|
+
* mirroring the bodiless-struct/enum skip. Set only for languages where a
|
|
155
|
+
* bodiless `class` specifier is NOT a complete definition — C/C++
|
|
156
|
+
* (`class Foo;` is a forward decl). Leave unset for languages where a
|
|
157
|
+
* bodiless class IS complete (Kotlin `class Empty`, Scala `case object`). (#1093)
|
|
158
|
+
*/
|
|
159
|
+
skipBodilessClass?: boolean;
|
|
143
160
|
/** NodeKind to use for interface-like declarations (Rust: 'trait'). Default: 'interface' */
|
|
144
161
|
interfaceKind?: NodeKind;
|
|
145
162
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -110,6 +110,23 @@ export declare class CodeGraph {
|
|
|
110
110
|
* @returns A CodeGraph instance
|
|
111
111
|
*/
|
|
112
112
|
static open(projectRoot: string, options?: OpenOptions): Promise<CodeGraph>;
|
|
113
|
+
/**
|
|
114
|
+
* Rebuild the project's database from scratch and return a fresh, empty
|
|
115
|
+
* instance — the "same result as a fresh init" semantics that `codegraph
|
|
116
|
+
* index` documents.
|
|
117
|
+
*
|
|
118
|
+
* Unlike `open()` followed by `clear()`, this DISCARDS the existing
|
|
119
|
+
* `.codegraph/codegraph.db` (and its `-wal`/`-shm` sidecars) before
|
|
120
|
+
* re-initializing, instead of opening the old database and DELETE-ing every
|
|
121
|
+
* row. On a large or pre-fix poisoned index — e.g. an old graph that scanned
|
|
122
|
+
* an ignored gitlink corpus (#1065) into ~1.6M nodes with a multi-GB WAL —
|
|
123
|
+
* the per-row `nodes_fts` delete-trigger churn blocks the main thread long
|
|
124
|
+
* enough to trip the #850 liveness watchdog before indexing even starts, so a
|
|
125
|
+
* full re-index could never recover the bad state (#1067). Discarding the
|
|
126
|
+
* files is O(1) regardless of size, reclaims the disk, and sidesteps opening
|
|
127
|
+
* (and running migrations against) the poisoned database entirely.
|
|
128
|
+
*/
|
|
129
|
+
static recreate(projectRoot: string): Promise<CodeGraph>;
|
|
113
130
|
/**
|
|
114
131
|
* Open synchronously (without sync)
|
|
115
132
|
*/
|
|
@@ -11,5 +11,5 @@ import type { ResolutionContext } from './types';
|
|
|
11
11
|
* Sidekiq Worker.perform_async → #perform + Laravel event(new X) → listener handle).
|
|
12
12
|
* Returns the count added. Never throws into indexing — callers wrap in try/catch.
|
|
13
13
|
*/
|
|
14
|
-
export declare function synthesizeCallbackEdges(queries: QueryBuilder, ctx: ResolutionContext): number
|
|
14
|
+
export declare function synthesizeCallbackEdges(queries: QueryBuilder, ctx: ResolutionContext): Promise<number>;
|
|
15
15
|
//# sourceMappingURL=callback-synthesizer.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cooperative yielding for long synchronous resolution spans.
|
|
3
|
+
*
|
|
4
|
+
* Reference resolution and callback-edge synthesis run on the indexer's MAIN
|
|
5
|
+
* thread — unlike parsing, which is off-thread in the parse worker. The #850
|
|
6
|
+
* liveness watchdog (armed on `index`/`init` since #999) SIGKILLs the process
|
|
7
|
+
* when that thread doesn't turn its event loop for the timeout window (default
|
|
8
|
+
* 60s), because its heartbeat is a `setInterval` on that same thread. On a large
|
|
9
|
+
* repo, resolving refs + synthesizing dynamic-dispatch edges legitimately runs
|
|
10
|
+
* for minutes, so a span that never yields starves the heartbeat and the
|
|
11
|
+
* watchdog kills a VALID, in-progress index — the exact symptom of #1091 (the
|
|
12
|
+
* progress bar freezes at wherever it last rendered — 88% / 100% — then the
|
|
13
|
+
* process is killed).
|
|
14
|
+
*
|
|
15
|
+
* `createYielder` returns a `maybeYield()` that yields (via `setImmediate`) only
|
|
16
|
+
* once more than `budgetMs` of wall-clock has elapsed since the last yield, so
|
|
17
|
+
* fast repos pay essentially nothing while slow ones give the heartbeat a
|
|
18
|
+
* regular window to fire. Call it at natural boundaries in a long loop (between
|
|
19
|
+
* batches, between synthesis passes).
|
|
20
|
+
*
|
|
21
|
+
* This does NOT weaken the watchdog. A genuinely wedged loop — an infinite or
|
|
22
|
+
* non-terminating span, the case the watchdog exists to catch — never reaches a
|
|
23
|
+
* yield point, so the heartbeat still stops and the SIGKILL still fires. We only
|
|
24
|
+
* stop killing work that is demonstrably making progress.
|
|
25
|
+
*/
|
|
26
|
+
/** Yield when more than `budgetMs` of wall-clock has passed since the last yield. */
|
|
27
|
+
export type MaybeYield = () => Promise<void>;
|
|
28
|
+
/** Default budget: well under the watchdog's minimum heartbeat cadence (~1s), so
|
|
29
|
+
* a heartbeat byte always has a chance to land between yields. */
|
|
30
|
+
export declare const DEFAULT_YIELD_BUDGET_MS = 250;
|
|
31
|
+
export declare function createYielder(budgetMs?: number): MaybeYield;
|
|
32
|
+
//# sourceMappingURL=cooperative-yield.d.ts.map
|
|
@@ -103,7 +103,16 @@ export declare class ReferenceResolver {
|
|
|
103
103
|
* (re-resolving an already-resolved ref is a no-op since it's been deleted).
|
|
104
104
|
* Returns the number of newly-created edges.
|
|
105
105
|
*/
|
|
106
|
-
resolveChainedCallsViaConformance(): number
|
|
106
|
+
resolveChainedCallsViaConformance(): Promise<number>;
|
|
107
|
+
/**
|
|
108
|
+
* Resolve one batch in smaller sub-chunks, yielding to the event loop between
|
|
109
|
+
* them so the #850 liveness heartbeat can fire on a slow/dense batch (#1091).
|
|
110
|
+
* Behaviourally identical to a single `resolveAll(batch)`: `warmCaches()` is
|
|
111
|
+
* idempotent (guarded) and `resolveOne` is independent per ref, so splitting
|
|
112
|
+
* and re-merging changes only timing, never which edges get created. Falls
|
|
113
|
+
* through to a plain `resolveAll` when the batch is already small.
|
|
114
|
+
*/
|
|
115
|
+
private resolveBatchYielding;
|
|
107
116
|
/**
|
|
108
117
|
* Resolve and persist in batches to keep memory bounded.
|
|
109
118
|
* Processes unresolved references in chunks, persisting edges and cleaning
|
|
@@ -172,7 +181,7 @@ export declare class ReferenceResolver {
|
|
|
172
181
|
* Mirrors resolveChainedCallsViaConformance's lifecycle. Returns the number
|
|
173
182
|
* of newly-created edges.
|
|
174
183
|
*/
|
|
175
|
-
resolveDeferredThisMemberRefs(): number
|
|
184
|
+
resolveDeferredThisMemberRefs(): Promise<number>;
|
|
176
185
|
private gateLanguage;
|
|
177
186
|
/**
|
|
178
187
|
* Drop a FRAMEWORK-strategy resolution that crosses two *known* language
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles symbol name matching for reference resolution.
|
|
5
5
|
*/
|
|
6
|
+
import { Node } from '../types';
|
|
6
7
|
import { UnresolvedRef, ResolvedRef, ResolutionContext } from './types';
|
|
7
8
|
/**
|
|
8
9
|
* Try to resolve a path-like reference (e.g., "snippets/drawer-menu.liquid")
|
|
@@ -47,6 +48,27 @@ export declare function matchByExactName(ref: UnresolvedRef, context: Resolution
|
|
|
47
48
|
* Try to resolve by qualified name
|
|
48
49
|
*/
|
|
49
50
|
export declare function matchByQualifiedName(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null;
|
|
51
|
+
/**
|
|
52
|
+
* When a symbol name is ambiguous across files, prefer the candidate(s) declared
|
|
53
|
+
* in the call site's own file, keeping the rest in their original order (#1079).
|
|
54
|
+
* A same-file definition is the strongest language-agnostic signal for which of
|
|
55
|
+
* several same-named symbols a call means; without it, resolution collapses onto
|
|
56
|
+
* whichever was indexed first, so a call in `b/svc` wrongly targets `a/svc`.
|
|
57
|
+
* No-op when there are <2 candidates or none share the call site's file.
|
|
58
|
+
*/
|
|
59
|
+
export declare function preferCallSiteFile(nodes: Node[], callSiteFile: string): Node[];
|
|
60
|
+
export declare function resolveMethodOnType(typeName: string, methodName: string, ref: UnresolvedRef, context: ResolutionContext, confidence: number, resolvedBy: ResolvedRef['resolvedBy'],
|
|
61
|
+
/**
|
|
62
|
+
* Optional FQN that identifies WHICH class declaration `typeName`
|
|
63
|
+
* refers to in the caller's file. When multiple candidates share
|
|
64
|
+
* the same qualifiedName (`FooConverter::convert` in both
|
|
65
|
+
* `dao/converter/` and `service/converter/`), the FQN's
|
|
66
|
+
* file-path-suffix picks the right one — the disambiguation
|
|
67
|
+
* signal Java imports carry but the call site doesn't (#314).
|
|
68
|
+
*/
|
|
69
|
+
preferredFqn?: string,
|
|
70
|
+
/** Recursion guard for the supertype/conformance walk. */
|
|
71
|
+
depth?: number): ResolvedRef | null;
|
|
50
72
|
/**
|
|
51
73
|
* Resolve a C++ chained call whose receiver is itself a call — encoded by the
|
|
52
74
|
* extractor as `<innerCallee>().<method>` (#645). The receiver's type is what
|
package/npm-shim.js
CHANGED
|
@@ -45,7 +45,7 @@ async function main() {
|
|
|
45
45
|
// Happy path: the npm-installed optional dependency. Fall back to a download
|
|
46
46
|
// when the registry didn't deliver it.
|
|
47
47
|
var resolved = resolveInstalledBundle() || (await selfHealBundle());
|
|
48
|
-
var res = childProcess.spawnSync(resolved.command, resolved.args, { stdio: 'inherit' });
|
|
48
|
+
var res = childProcess.spawnSync(resolved.command, resolved.args, { stdio: 'inherit', windowsHide: true });
|
|
49
49
|
if (res.error) {
|
|
50
50
|
process.stderr.write('codegraph: ' + res.error.message + '\n');
|
|
51
51
|
process.exit(1);
|
|
@@ -105,7 +105,7 @@ async function selfHealBundle() {
|
|
|
105
105
|
// Already downloaded by a previous run? Use it even when downloads are
|
|
106
106
|
// disabled — CODEGRAPH_NO_DOWNLOAD blocks fetching, not a cached bundle.
|
|
107
107
|
var cached = launcherIn(dest);
|
|
108
|
-
if (cached) return cached;
|
|
108
|
+
if (cached) { pruneOldBundles(bundlesDir, dest); return cached; }
|
|
109
109
|
|
|
110
110
|
if (process.env.CODEGRAPH_NO_DOWNLOAD) {
|
|
111
111
|
fail('the network fallback is disabled (CODEGRAPH_NO_DOWNLOAD is set).');
|
|
@@ -149,6 +149,7 @@ async function selfHealBundle() {
|
|
|
149
149
|
|
|
150
150
|
var ready = launcherIn(dest);
|
|
151
151
|
if (!ready) fail('downloaded bundle is missing its launcher under ' + dest + '.');
|
|
152
|
+
pruneOldBundles(bundlesDir, dest);
|
|
152
153
|
process.stderr.write('codegraph: bundle ready.\n');
|
|
153
154
|
return ready;
|
|
154
155
|
}
|
|
@@ -221,7 +222,7 @@ function extract(archive, destDir) {
|
|
|
221
222
|
var args = isWindows
|
|
222
223
|
? ['-xf', archive, '-C', destDir, '--strip-components=1']
|
|
223
224
|
: ['-xzf', archive, '-C', destDir, '--strip-components=1'];
|
|
224
|
-
var res = childProcess.spawnSync('tar', args, { stdio: 'ignore' });
|
|
225
|
+
var res = childProcess.spawnSync('tar', args, { stdio: 'ignore', windowsHide: true });
|
|
225
226
|
if (res.error) throw new Error('tar unavailable: ' + res.error.message);
|
|
226
227
|
if (res.status !== 0) throw new Error('tar exited ' + res.status);
|
|
227
228
|
}
|
|
@@ -230,6 +231,27 @@ function rmrf(p) {
|
|
|
230
231
|
try { fs.rmSync(p, { recursive: true, force: true }); } catch (e) { /* best effort */ }
|
|
231
232
|
}
|
|
232
233
|
|
|
234
|
+
// Drop sibling bundles for OTHER versions of this same platform target, keeping
|
|
235
|
+
// only keepDir. The self-heal cache otherwise accumulates a full ~50 MB bundle
|
|
236
|
+
// per version forever (issue #1074). Best-effort: a locked/busy dir (a
|
|
237
|
+
// concurrent run still mapping an older node.exe on Windows) just stays — rmrf
|
|
238
|
+
// already swallows its own errors, and the readdir is guarded — so cleanup can
|
|
239
|
+
// never break a working command. Only this target's "<target>-<version>" dirs
|
|
240
|
+
// are touched; other platforms' bundles and the ".dl-*" staging dirs are left
|
|
241
|
+
// alone.
|
|
242
|
+
function pruneOldBundles(bundlesDir, keepDir) {
|
|
243
|
+
var keep = path.basename(keepDir);
|
|
244
|
+
try {
|
|
245
|
+
var names = fs.readdirSync(bundlesDir);
|
|
246
|
+
for (var i = 0; i < names.length; i++) {
|
|
247
|
+
var name = names[i];
|
|
248
|
+
if (name === keep) continue;
|
|
249
|
+
if (name.indexOf(target + '-') !== 0) continue;
|
|
250
|
+
rmrf(path.join(bundlesDir, name));
|
|
251
|
+
}
|
|
252
|
+
} catch (e) { /* best effort — never break a working run over cleanup */ }
|
|
253
|
+
}
|
|
254
|
+
|
|
233
255
|
function fail(reason) {
|
|
234
256
|
process.stderr.write(
|
|
235
257
|
'codegraph: no prebuilt bundle for ' + target + '.\n' +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colbymchenry/codegraph",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Local-first code intelligence for AI agents (MCP). Self-contained — bundles its own runtime.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codegraph": "npm-shim.js"
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"./package.json": "./package.json"
|
|
16
16
|
},
|
|
17
17
|
"optionalDependencies": {
|
|
18
|
-
"@colbymchenry/codegraph-darwin-arm64": "1.
|
|
19
|
-
"@colbymchenry/codegraph-darwin-x64": "1.
|
|
20
|
-
"@colbymchenry/codegraph-linux-arm64": "1.
|
|
21
|
-
"@colbymchenry/codegraph-linux-x64": "1.
|
|
22
|
-
"@colbymchenry/codegraph-win32-arm64": "1.
|
|
23
|
-
"@colbymchenry/codegraph-win32-x64": "1.
|
|
18
|
+
"@colbymchenry/codegraph-darwin-arm64": "1.2.0",
|
|
19
|
+
"@colbymchenry/codegraph-darwin-x64": "1.2.0",
|
|
20
|
+
"@colbymchenry/codegraph-linux-arm64": "1.2.0",
|
|
21
|
+
"@colbymchenry/codegraph-linux-x64": "1.2.0",
|
|
22
|
+
"@colbymchenry/codegraph-win32-arm64": "1.2.0",
|
|
23
|
+
"@colbymchenry/codegraph-win32-x64": "1.2.0"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"npm-shim.js",
|