@colbymchenry/codegraph 1.3.0 → 1.3.1

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.
@@ -73,8 +73,8 @@ export declare class DatabaseConnection {
73
73
  */
74
74
  optimize(): void;
75
75
  /**
76
- * Lightweight, non-blocking maintenance to run after bulk writes
77
- * (indexAll, sync). Two operations:
76
+ * Lightweight maintenance to run after bulk writes (indexAll, sync).
77
+ * Two operations:
78
78
  *
79
79
  * - `PRAGMA optimize` — incremental ANALYZE; SQLite only re-analyzes
80
80
  * tables whose row counts changed materially since the last
@@ -86,10 +86,22 @@ export declare class DatabaseConnection {
86
86
  * unboundedly between automatic checkpoints (auto-fires at 1000
87
87
  * pages by default; large indexAll runs blow past that).
88
88
  *
89
- * Both operations are silently swallowed on failure they're a
90
- * best-effort optimization, never load-bearing for correctness.
89
+ * Runs on a WORKER THREAD with its own connection: on a multi-GB index
90
+ * these pragmas are minutes of synchronous IO (a 95k-file kernel index
91
+ * left a 593MB WAL whose checkpoint alone blew the #850 watchdog's 60s
92
+ * window and got a COMPLETED index SIGKILLed at the finish line). WAL
93
+ * checkpointing from a second connection is standard SQLite; `PRAGMA
94
+ * optimize` persists its statistics in sqlite_stat tables, so the main
95
+ * connection benefits the same. The main thread just awaits a message,
96
+ * so the event loop — and the watchdog heartbeat — keep turning.
97
+ *
98
+ * Everything is silently swallowed on failure — best-effort
99
+ * optimization, never load-bearing for correctness. If worker threads
100
+ * are unavailable, falls back to a bounded in-line `PRAGMA optimize`
101
+ * and SKIPS the checkpoint (the final close() checkpoints after the
102
+ * CLI has already disarmed its watchdog).
91
103
  */
92
- runMaintenance(): void;
104
+ runMaintenance(): Promise<void>;
93
105
  /**
94
106
  * Close the database connection
95
107
  */
@@ -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
  */
@@ -198,6 +198,23 @@ export declare class QueryBuilder {
198
198
  * Get all nodes in the database
199
199
  */
200
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>;
201
218
  /**
202
219
  * Get nodes by exact name match (uses idx_nodes_name index)
203
220
  */
@@ -395,6 +412,13 @@ export declare class QueryBuilder {
395
412
  * Get all distinct node names (lightweight — just name strings for pre-filtering)
396
413
  */
397
414
  getAllNodeNames(): string[];
415
+ /**
416
+ * Stream the distinct node names one row at a time — the incremental
417
+ * counterpart to {@link getAllNodeNames} for callers that need to yield
418
+ * to the event loop mid-scan (resolver cache warm-up on multi-million-node
419
+ * indexes). Fresh statement per call: the iterator holds an open cursor.
420
+ */
421
+ iterateNodeNames(): IterableIterator<string>;
398
422
  /**
399
423
  * Get unresolved references scoped to specific file paths.
400
424
  * Uses the idx_unresolved_file_path index for efficient lookup.
@@ -81,6 +81,20 @@ export declare class QueryPool {
81
81
  * degrades to today's behavior instead of failing tool calls.
82
82
  */
83
83
  get healthy(): boolean;
84
+ /**
85
+ * True once at least one worker has completed its cold start (posted the
86
+ * 'ready' handshake). Until then the ToolHandler serves calls IN-PROCESS:
87
+ * a worker cold start is a full module load + DB open — seconds normally,
88
+ * tens of seconds on a loaded machine — and a call queued behind it gets
89
+ * nothing until the 45s busy backstop. The daemon's very first tool call
90
+ * hitting that window was the recurring #662 test flake (and a real
91
+ * first-call stall for agents). The pool exists for CONCURRENT load, which
92
+ * by definition arrives after warm-up; the pre-pool in-process path is
93
+ * strictly better while nothing is warm. Stays true for the pool's
94
+ * lifetime — later crash-respawn gaps are covered by retry + backstop.
95
+ */
96
+ get ready(): boolean;
97
+ private everReady;
84
98
  private spawnOne;
85
99
  private onMessage;
86
100
  private onWorkerGone;
@@ -1,5 +1,6 @@
1
1
  import type { Edge } from '../types';
2
2
  import type { QueryBuilder } from '../db/queries';
3
3
  import type { ResolutionContext } from './types';
4
- export declare function cFnPointerDispatchEdges(queries: QueryBuilder, ctx: ResolutionContext): Edge[];
4
+ import type { MaybeYield } from './cooperative-yield';
5
+ export declare function cFnPointerDispatchEdges(_queries: QueryBuilder, ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]>;
5
6
  //# sourceMappingURL=c-fnptr-synthesizer.d.ts.map
@@ -24,5 +24,6 @@
24
24
  */
25
25
  import type { Edge } from '../types';
26
26
  import type { ResolutionContext } from './types';
27
- export declare function goframeRouteEdges(ctx: ResolutionContext): Edge[];
27
+ import type { MaybeYield } from './cooperative-yield';
28
+ export declare function goframeRouteEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]>;
28
29
  //# sourceMappingURL=goframe-synthesizer.d.ts.map
@@ -6,6 +6,7 @@
6
6
  import { UnresolvedReference, Edge } from '../types';
7
7
  import { QueryBuilder } from '../db/queries';
8
8
  import { UnresolvedRef, ResolvedRef, ResolutionResult } from './types';
9
+ import { type MaybeYield } from './cooperative-yield';
9
10
  export * from './types';
10
11
  /**
11
12
  * Reference Resolver
@@ -57,6 +58,15 @@ export declare class ReferenceResolver {
57
58
  * We cache the set of known symbol names for fast pre-filtering.
58
59
  */
59
60
  warmCaches(): void;
61
+ /**
62
+ * warmCaches for the async resolution entry points: streams the distinct
63
+ * name set with periodic yields instead of one synchronous `.all()`. On a
64
+ * multi-million-node index the DISTINCT scan is a solid multi-second block
65
+ * (measured up to 28s inside `codegraph sync` on the Linux kernel index),
66
+ * long enough to matter to the #850 watchdog on slower hardware. Same
67
+ * result, same memory — only the event loop keeps turning.
68
+ */
69
+ warmCachesYielding(onYield: MaybeYield): Promise<void>;
60
70
  /**
61
71
  * Clear internal caches
62
72
  */
@@ -66,6 +66,15 @@ export interface ResolutionContext {
66
66
  getNodesByQualifiedName(qualifiedName: string): Node[];
67
67
  /** Get all nodes of a kind */
68
68
  getNodesByKind(kind: Node['kind']): Node[];
69
+ /**
70
+ * Stream nodes of a kind one at a time instead of materializing (and, unlike
71
+ * `getNodesByKind`, without populating the resolver's per-kind array cache).
72
+ * For unbounded kinds (`function`, `method`, `struct`) on a symbol-dense
73
+ * project the full array is gigabytes — the dynamic-edge synthesizers must
74
+ * use this so their memory stays O(1) in node count (#610, #1212). Optional
75
+ * so minimal test contexts compile; callers fall back to getNodesByKind.
76
+ */
77
+ iterateNodesByKind?(kind: Node['kind']): IterableIterator<Node>;
69
78
  /** Check if a file exists */
70
79
  fileExists(filePath: string): boolean;
71
80
  /** Read file content */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colbymchenry/codegraph",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
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.3.0",
19
- "@colbymchenry/codegraph-darwin-x64": "1.3.0",
20
- "@colbymchenry/codegraph-linux-arm64": "1.3.0",
21
- "@colbymchenry/codegraph-linux-x64": "1.3.0",
22
- "@colbymchenry/codegraph-win32-arm64": "1.3.0",
23
- "@colbymchenry/codegraph-win32-x64": "1.3.0"
18
+ "@colbymchenry/codegraph-darwin-arm64": "1.3.1",
19
+ "@colbymchenry/codegraph-darwin-x64": "1.3.1",
20
+ "@colbymchenry/codegraph-linux-arm64": "1.3.1",
21
+ "@colbymchenry/codegraph-linux-x64": "1.3.1",
22
+ "@colbymchenry/codegraph-win32-arm64": "1.3.1",
23
+ "@colbymchenry/codegraph-win32-x64": "1.3.1"
24
24
  },
25
25
  "files": [
26
26
  "npm-shim.js",