@elkindev/dbgraph 1.0.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.
@@ -0,0 +1,2692 @@
1
+ import { SpawnSyncOptions, SpawnSyncReturns } from 'node:child_process';
2
+
3
+ /**
4
+ * Logger port — design §7.2.
5
+ * Core uses the injected Logger; never console.log (dbgraph-conventions).
6
+ * A no-op default logger is exported so functions can take an optional logger
7
+ * without null checks.
8
+ */
9
+ interface Logger {
10
+ debug(msg: string, meta?: Record<string, unknown>): void;
11
+ info(msg: string, meta?: Record<string, unknown>): void;
12
+ warn(msg: string, meta?: Record<string, unknown>): void;
13
+ error(msg: string, meta?: Record<string, unknown>): void;
14
+ }
15
+ /** No-op default logger — used when the caller does not inject one. */
16
+ declare const noopLogger: Logger;
17
+
18
+ /**
19
+ * open-connections — composition-root utility (phase-5-mcp-server task 2.1).
20
+ *
21
+ * Relocated from src/cli/config/open-connections.ts to a NEUTRAL composition
22
+ * layer so both the CLI and MCP adapter can consume it through the barrel
23
+ * (src/index.ts) without either side importing the other's layer.
24
+ *
25
+ * Design Decision 4 (phase-5-mcp-server): infra MAY import adapter/store
26
+ * factories — it is the composition seam. Both CLI and MCP import the barrel;
27
+ * the barrel re-exports this module.
28
+ *
29
+ * ADR-004: imports from the barrel (../../index.js) for adapter/store factories;
30
+ * config helpers imported from ./config/ (src/infra/config/) — NEVER from
31
+ * src/cli/** (that direction violates the cli↔mcp decoupling, ADR-004).
32
+ * Security: resolved secrets are NEVER logged; the resolved config is ephemeral.
33
+ */
34
+
35
+ type AdapterAndStore = {
36
+ adapter: Awaited<ReturnType<typeof createSqliteSchemaAdapter>> | Awaited<ReturnType<typeof createMssqlSchemaAdapter>> | Awaited<ReturnType<typeof createPgSchemaAdapter>> | Awaited<ReturnType<typeof createMysqlSchemaAdapter>> | Awaited<ReturnType<typeof createMongodbSchemaAdapter>>;
37
+ store: Awaited<ReturnType<typeof createSqliteGraphStore>>;
38
+ };
39
+ /** Injectable seams for {@link openConnections} (phase-9.5c, design D2). */
40
+ interface OpenConnectionsDeps {
41
+ /**
42
+ * SEA detection override — default reads `node:sea`.isSea via createRequire
43
+ * (NOT a static node:sea import). Inside a SEA binary this is true → the local
44
+ * store flips to `node:sqlite`; off-SEA it is false → default better-sqlite3.
45
+ */
46
+ readonly isSea?: () => boolean;
47
+ }
48
+ /**
49
+ * Reads dbgraph.config.json from projectRoot, resolves ${env:VAR} secrets,
50
+ * creates the appropriate schema adapter, ensures .dbgraph/ exists, and
51
+ * opens the SQLite graph store.
52
+ *
53
+ * Callers are responsible for calling adapter.close() and store.close() in a
54
+ * finally block after use.
55
+ *
56
+ * @param projectRoot - The directory containing dbgraph.config.json.
57
+ * @param logger - Optional Logger for strategy-selection transparency.
58
+ * Defaults to noopLogger (back-compat — existing callers
59
+ * that do not pass a logger are unaffected).
60
+ *
61
+ * Throws ConfigError if:
62
+ * - dbgraph.config.json is missing or malformed
63
+ * - any ${env:VAR} reference is unresolved
64
+ */
65
+ declare function openConnections(projectRoot: string, logger?: Logger, deps?: OpenConnectionsDeps): Promise<AdapterAndStore>;
66
+
67
+ /**
68
+ * Node kinds, graph nodes, indexing levels, and per-type level configuration.
69
+ * Design §4.1 — engine-agnostic domain types.
70
+ */
71
+ type NodeKind = 'database' | 'schema' | 'table' | 'column' | 'constraint' | 'index' | 'view' | 'procedure' | 'function' | 'trigger' | 'sequence' | 'collection' | 'field';
72
+ /** Runtime-accessible tuple of all NodeKind values (used for validation and tests). */
73
+ declare const NODE_KINDS: readonly NodeKind[];
74
+ type IndexLevel = 'off' | 'metadata' | 'full';
75
+ /** Runtime-accessible tuple of all IndexLevel values. */
76
+ declare const INDEX_LEVELS: readonly IndexLevel[];
77
+ /**
78
+ * Per-object-type level configuration.
79
+ * ADR-003 defaults: triggers full; procedures/functions metadata; statistics/sampling off.
80
+ */
81
+ interface ObjectTypeLevels {
82
+ tables: IndexLevel;
83
+ columns: IndexLevel;
84
+ constraints: IndexLevel;
85
+ indexes: IndexLevel;
86
+ views: IndexLevel;
87
+ procedures: IndexLevel;
88
+ functions: IndexLevel;
89
+ triggers: IndexLevel;
90
+ sequences: IndexLevel;
91
+ collections: IndexLevel;
92
+ fields: IndexLevel;
93
+ statistics: IndexLevel;
94
+ sampling: IndexLevel;
95
+ }
96
+ /**
97
+ * Opaque per-kind JSON payload. Adapters may add engine-specific keys.
98
+ * Typed accessor views (TablePayload, etc.) provide compile-time ergonomics.
99
+ */
100
+ type NodePayload = Readonly<Record<string, unknown>>;
101
+ interface TablePayload {
102
+ rowCountEstimate?: number;
103
+ comment?: string;
104
+ }
105
+ interface ColumnPayload {
106
+ dataType: string;
107
+ nullable: boolean;
108
+ default?: string | null;
109
+ ordinal: number;
110
+ comment?: string;
111
+ }
112
+ interface ConstraintPayload {
113
+ type: 'PK' | 'FK' | 'UNIQUE' | 'CHECK';
114
+ definition?: string;
115
+ columns: readonly string[];
116
+ }
117
+ interface IndexPayload {
118
+ unique: boolean;
119
+ columns: readonly string[];
120
+ method?: string;
121
+ }
122
+ interface RoutinePayload {
123
+ signature?: string;
124
+ returns?: string;
125
+ body?: string;
126
+ hasDynamicSql: boolean;
127
+ comment?: string;
128
+ }
129
+ interface TriggerPayload {
130
+ timing?: 'BEFORE' | 'AFTER' | 'INSTEAD OF';
131
+ events: readonly ('INSERT' | 'UPDATE' | 'DELETE')[];
132
+ body?: string;
133
+ hasDynamicSql: boolean;
134
+ comment?: string;
135
+ }
136
+ /**
137
+ * Accessor view for a 'field' node (MongoDB sampled field).
138
+ * Mirrors ColumnPayload structurally so inferReferences (which reads payload.dataType
139
+ * as a string) consumes a 'field' node identically to a 'column' node (Design §D1).
140
+ * dataType is a union string like 'int|string' (sorted) — NOT a types[] array.
141
+ */
142
+ interface FieldPayload {
143
+ dataType: string;
144
+ frequency: number;
145
+ nullable?: boolean;
146
+ }
147
+ interface GraphNode {
148
+ readonly id: string;
149
+ readonly kind: NodeKind;
150
+ readonly schema: string | null;
151
+ readonly name: string;
152
+ readonly qname: string;
153
+ readonly level: IndexLevel;
154
+ readonly missing: boolean;
155
+ readonly excluded: boolean;
156
+ readonly bodyHash: string | null;
157
+ readonly payload: NodePayload;
158
+ }
159
+
160
+ /**
161
+ * Edge kinds, confidence classification, edge attributes, and graph edges.
162
+ * Design §4.2 — engine-agnostic domain types.
163
+ */
164
+ type EdgeKind = 'references' | 'depends_on' | 'reads_from' | 'writes_to' | 'fires_on' | 'has_column' | 'has_index' | 'has_constraint' | 'in_index' | 'inferred_reference';
165
+ /** Runtime-accessible tuple of all EdgeKind values. */
166
+ declare const EDGE_KINDS: readonly EdgeKind[];
167
+ type EdgeConfidence = 'declared' | 'parsed' | 'inferred';
168
+ /** Runtime-accessible tuple of all EdgeConfidence values. */
169
+ declare const EDGE_CONFIDENCE_VALUES: readonly EdgeConfidence[];
170
+ interface EdgeAttrs {
171
+ readonly srcColumn?: string;
172
+ readonly dstColumn?: string;
173
+ readonly event?: 'INSERT' | 'UPDATE' | 'DELETE';
174
+ readonly aggregate?: boolean;
175
+ readonly ordinal?: number;
176
+ readonly constraintName?: string;
177
+ }
178
+ interface GraphEdge {
179
+ readonly id: string;
180
+ readonly kind: EdgeKind;
181
+ readonly src: string;
182
+ readonly dst: string;
183
+ readonly confidence: EdgeConfidence;
184
+ readonly score: number | null;
185
+ readonly attrs: EdgeAttrs;
186
+ }
187
+
188
+ /**
189
+ * RawCatalog — the durable adapter→core contract.
190
+ * Design §4.5 — what every engine adapter produces and feeds to normalizeCatalog.
191
+ * This file imports NOTHING from adapters, drivers, mcp, or cli (ADR-004).
192
+ */
193
+
194
+ interface RawCatalog {
195
+ readonly engine: string;
196
+ readonly engineVersion?: string;
197
+ readonly schemas: readonly string[];
198
+ readonly objects: readonly RawObject[];
199
+ }
200
+ interface RawField {
201
+ readonly name: string;
202
+ readonly dataType: string;
203
+ readonly frequency: number;
204
+ readonly nullable?: boolean;
205
+ }
206
+ interface RawObject {
207
+ readonly kind: NodeKind;
208
+ readonly schema: string | null;
209
+ readonly name: string;
210
+ readonly columns?: readonly RawColumn[];
211
+ readonly constraints?: readonly RawConstraint[];
212
+ readonly indexes?: readonly RawIndex[];
213
+ readonly fields?: readonly RawField[];
214
+ readonly signature?: string;
215
+ readonly returns?: string;
216
+ readonly body?: string;
217
+ readonly hasDynamicSql?: boolean;
218
+ readonly trigger?: RawTriggerInfo;
219
+ readonly dependencies?: readonly RawDependency[];
220
+ readonly comment?: string;
221
+ readonly extra?: Readonly<Record<string, unknown>>;
222
+ }
223
+ interface RawColumn {
224
+ readonly name: string;
225
+ readonly dataType: string;
226
+ readonly nullable: boolean;
227
+ readonly default?: string | null;
228
+ readonly ordinal: number;
229
+ readonly comment?: string;
230
+ }
231
+ interface RawConstraint {
232
+ readonly name: string;
233
+ readonly type: 'PK' | 'FK' | 'UNIQUE' | 'CHECK';
234
+ readonly columns: readonly string[];
235
+ readonly references?: {
236
+ schema: string | null;
237
+ table: string;
238
+ columns: readonly string[];
239
+ };
240
+ readonly definition?: string;
241
+ }
242
+ interface RawIndex {
243
+ readonly name: string;
244
+ readonly unique: boolean;
245
+ readonly columns: readonly string[];
246
+ readonly method?: string;
247
+ }
248
+ interface RawTriggerInfo {
249
+ readonly timing: 'BEFORE' | 'AFTER' | 'INSTEAD OF';
250
+ readonly events: readonly ('INSERT' | 'UPDATE' | 'DELETE')[];
251
+ readonly table: {
252
+ schema: string | null;
253
+ name: string;
254
+ };
255
+ }
256
+ interface RawDependency {
257
+ readonly target: {
258
+ schema: string | null;
259
+ name: string;
260
+ kind?: NodeKind;
261
+ };
262
+ readonly access: 'read' | 'write';
263
+ readonly confidence: 'declared' | 'parsed';
264
+ }
265
+
266
+ /**
267
+ * CapabilityMatrix, ExtractionScope, and DEFAULT_LEVELS.
268
+ * Design §4.3 — what an engine supports and the normalizer's second argument.
269
+ * No adapter, driver, mcp, or cli imports (ADR-004).
270
+ */
271
+
272
+ interface CapabilityMatrix {
273
+ readonly engine: string;
274
+ readonly supported: ReadonlySet<NodeKind>;
275
+ readonly defaultLevels: ObjectTypeLevels;
276
+ readonly supportsBodies: boolean;
277
+ readonly supportsDependencyHints: boolean;
278
+ }
279
+ interface ExtractionScope {
280
+ readonly levels: ObjectTypeLevels;
281
+ readonly include?: readonly string[];
282
+ readonly exclude?: readonly string[];
283
+ /**
284
+ * Opt-in gate for structural inference (US-008).
285
+ * When `true`, the normalizer calls `inferReferences` after step 4c.
286
+ * When absent or `false` (the default), inference is skipped and the normalizer
287
+ * output is byte-identical to pre-Phase-9a behavior (the four shipped SQL engines
288
+ * stay unaffected). Design D3.
289
+ */
290
+ readonly inferRelationships?: boolean;
291
+ }
292
+
293
+ /**
294
+ * NormalizedGraph, NormalizationResult, OmittedKindInfo, and StubInfo.
295
+ * Design §4.6 — the output contract of normalizeCatalog.
296
+ * No adapter, driver, mcp, or cli imports (ADR-004).
297
+ */
298
+
299
+ interface NormalizedGraph {
300
+ readonly nodes: readonly GraphNode[];
301
+ readonly edges: readonly GraphEdge[];
302
+ }
303
+ interface StubInfo {
304
+ readonly id: string;
305
+ readonly qname: string;
306
+ readonly kind: NodeKind;
307
+ readonly reason: 'missing' | 'excluded';
308
+ readonly referencedBy: string;
309
+ }
310
+ /**
311
+ * Records an object type that was omitted because its level is 'off' in the scope.
312
+ * US-003 / graph-model spec "off level is an absence, not silence":
313
+ * "a queryable absence reason is representable".
314
+ */
315
+ interface OmittedKindInfo {
316
+ /** The object kind that was configured off. */
317
+ readonly kind: NodeKind;
318
+ /** Human-readable reason: always "not indexed by configuration" (US-003 spec wording). */
319
+ readonly reason: string;
320
+ }
321
+ interface NormalizationResult {
322
+ readonly graph: NormalizedGraph;
323
+ readonly stubs: readonly StubInfo[];
324
+ readonly warnings: readonly string[];
325
+ /** US-003 / W-1: off-level kinds with queryable absence reason (spec: "not indexed by configuration"). */
326
+ readonly omitted: readonly OmittedKindInfo[];
327
+ }
328
+
329
+ /**
330
+ * GraphStore port — the driven port for graph persistence.
331
+ * Design §11 — async signatures so the port is the seam for node:sqlite / bun:sqlite (ADR-005).
332
+ * The SQLite adapter wraps synchronous calls in resolved Promises (zero runtime cost).
333
+ * This file imports NOTHING from adapters, drivers, mcp, or cli (ADR-004).
334
+ */
335
+
336
+ interface UpsertResult {
337
+ readonly nodes: number;
338
+ readonly edges: number;
339
+ }
340
+ interface SearchHit {
341
+ readonly id: string;
342
+ readonly kind: NodeKind;
343
+ readonly qname: string;
344
+ readonly column: 'qname' | 'comment' | 'body';
345
+ readonly score: number;
346
+ }
347
+ interface SnapshotRecord {
348
+ readonly id: string;
349
+ readonly takenAt: string;
350
+ readonly engine: string;
351
+ readonly engineVersion?: string;
352
+ readonly fingerprint: string;
353
+ readonly counts: Readonly<Record<string, number>>;
354
+ }
355
+ /** One row in the snapshot_objects manifest (phase-4-cli-config Batch F). */
356
+ interface SnapshotObjectRow {
357
+ readonly snapshotId: string;
358
+ readonly nodeId: string;
359
+ readonly kind: string;
360
+ readonly qname: string;
361
+ readonly bodyHash: string | null;
362
+ }
363
+ interface NeighborQuery {
364
+ readonly nodeId: string;
365
+ readonly kinds?: readonly EdgeKind[];
366
+ }
367
+ interface NeighborGroups {
368
+ readonly [kind: string]: {
369
+ readonly out: readonly {
370
+ node: GraphNode;
371
+ edge: GraphEdge;
372
+ }[];
373
+ readonly in: readonly {
374
+ node: GraphNode;
375
+ edge: GraphEdge;
376
+ }[];
377
+ };
378
+ }
379
+ interface ImpactQuery {
380
+ readonly nodeId: string;
381
+ readonly depth?: number;
382
+ }
383
+ interface ImpactChain {
384
+ readonly nodes: readonly string[];
385
+ readonly edges: readonly EdgeKind[];
386
+ }
387
+ interface ImpactResult {
388
+ readonly readImpact: readonly ImpactChain[];
389
+ readonly writeImpact: readonly ImpactChain[];
390
+ readonly truncated: boolean;
391
+ readonly dynamicSqlWarning: boolean;
392
+ }
393
+ interface PathQuery {
394
+ readonly from: string;
395
+ readonly to: string;
396
+ readonly allowInferred?: boolean;
397
+ }
398
+ interface JoinHop {
399
+ readonly fromTable: string;
400
+ readonly toTable: string;
401
+ readonly joinColumns: readonly {
402
+ from: string;
403
+ to: string;
404
+ }[];
405
+ }
406
+ interface PathResult {
407
+ readonly found: boolean;
408
+ readonly hops?: readonly JoinHop[];
409
+ readonly inferred?: boolean;
410
+ readonly nearest?: {
411
+ from: readonly string[];
412
+ to: readonly string[];
413
+ };
414
+ }
415
+ interface SearchQuery {
416
+ readonly term: string;
417
+ readonly limit?: number;
418
+ readonly offset?: number;
419
+ }
420
+ interface GraphStore {
421
+ close(): Promise<void>;
422
+ schemaVersion(): Promise<number>;
423
+ upsertGraph(graph: NormalizedGraph): Promise<UpsertResult>;
424
+ deleteNodes(ids: readonly string[]): Promise<number>;
425
+ getNode(id: string): Promise<GraphNode | null>;
426
+ getNodesByKind(kind: NodeKind): Promise<readonly GraphNode[]>;
427
+ getNodeByQName(kind: NodeKind, qname: string): Promise<GraphNode | null>;
428
+ getEdgesFrom(nodeId: string, kinds?: readonly EdgeKind[]): Promise<readonly GraphEdge[]>;
429
+ getEdgesTo(nodeId: string, kinds?: readonly EdgeKind[]): Promise<readonly GraphEdge[]>;
430
+ searchFts(query: string, opts?: {
431
+ limit?: number;
432
+ offset?: number;
433
+ }): Promise<{
434
+ hits: readonly SearchHit[];
435
+ total: number;
436
+ }>;
437
+ putSnapshot(s: SnapshotRecord): Promise<void>;
438
+ listSnapshots(): Promise<readonly SnapshotRecord[]>;
439
+ /** Returns the snapshot_objects manifest for the given snapshot (phase-4-cli-config Batch F). */
440
+ getSnapshotObjects(snapshotId: string): Promise<readonly SnapshotObjectRow[]>;
441
+ getMeta(key: string): Promise<string | null>;
442
+ setMeta(key: string, value: string): Promise<void>;
443
+ }
444
+
445
+ /**
446
+ * SchemaAdapter port — the driving port for source-database schema extraction.
447
+ * Design §1 "SchemaAdapter port shape" — async lifecycle symmetric with GraphStore.
448
+ * This file imports NOTHING from adapters, drivers, mcp, or cli (ADR-004).
449
+ * The concrete join to a driver lives in src/adapters/engines/<engine>/.
450
+ *
451
+ * US-026 (first concrete adapter), US-031 (read-only by construction),
452
+ * US-009 (per-engine fingerprint).
453
+ */
454
+
455
+ /**
456
+ * Configuration for the SQLite schema adapter.
457
+ * `file` is the path to the source .db file (or ':memory:' for tests).
458
+ * `driver` selects the driver explicitly; defaults to `better-sqlite3`.
459
+ * NO silent auto-fallback — design §2 "Driver selection is explicit".
460
+ */
461
+ interface SqliteAdapterConfig {
462
+ readonly file: string;
463
+ readonly driver?: 'better-sqlite3' | 'node:sqlite';
464
+ }
465
+ /**
466
+ * Configuration for the Microsoft SQL Server schema adapter.
467
+ * Structural union member — distinguished from SqliteAdapterConfig by the
468
+ * presence of `server`, `database`, and `authentication` (no `dialect` field
469
+ * needed; each engine keeps its own factory taking its concrete config type).
470
+ *
471
+ * authentication.type 'sql' — SQL Server authentication (user + password).
472
+ * authentication.type 'ntlm' — Windows/NTLM authentication (domain + user + password).
473
+ * authentication.type 'integrated' — Windows Integrated Security; NO credentials required.
474
+ * The current OS session identity is used (ADR-006 note:
475
+ * tedious cannot handle this; the strategy registry will
476
+ * skip the native strategy and use an external tool instead).
477
+ *
478
+ * US-027 (SQL Server adapter), ADR-007 (no new generic config shape).
479
+ * connectivity-strategies A1.4: integrated member added additively.
480
+ */
481
+ interface MssqlAdapterConfig {
482
+ readonly server: string;
483
+ readonly port?: number;
484
+ readonly database: string;
485
+ readonly authentication: {
486
+ readonly type: 'sql';
487
+ readonly user: string;
488
+ readonly password: string;
489
+ } | {
490
+ readonly type: 'ntlm';
491
+ readonly domain: string;
492
+ readonly user: string;
493
+ readonly password: string;
494
+ } | {
495
+ readonly type: 'integrated';
496
+ };
497
+ readonly encrypt?: boolean;
498
+ readonly trustServerCertificate?: boolean;
499
+ }
500
+ /**
501
+ * Configuration for the PostgreSQL schema adapter.
502
+ *
503
+ * IMPORTANT — union discriminability: These config shapes are NOT discriminable
504
+ * by structure alone (both PgAdapterConfig and MysqlAdapterConfig carry `host`).
505
+ * The union is a plain structural union for typing only; runtime dispatch is by
506
+ * the EXPLICIT config `dialect` field in parse-config.ts / open-connections.ts,
507
+ * and each engine factory takes its own concrete config type directly.
508
+ * No `dialect` discriminant is added to the union members.
509
+ *
510
+ * `password` MUST be supplied as a `${env:VAR}` reference — never a literal.
511
+ * `port` defaults to 5432 when omitted.
512
+ * `schema` is optional: omit to extract all non-system schemas; supply to scope
513
+ * extraction to a single schema.
514
+ *
515
+ * US-028 (PostgreSQL adapter), pg-extraction spec "Connectivity via host/port".
516
+ */
517
+ interface PgAdapterConfig {
518
+ readonly host: string;
519
+ readonly port?: number;
520
+ readonly database: string;
521
+ readonly user: string;
522
+ readonly password: string;
523
+ readonly ssl?: boolean | {
524
+ readonly rejectUnauthorized?: boolean;
525
+ };
526
+ readonly schema?: string;
527
+ }
528
+ /**
529
+ * Configuration for the MySQL schema adapter.
530
+ *
531
+ * IMPORTANT — union discriminability: Both PgAdapterConfig and MysqlAdapterConfig
532
+ * carry `host`; the union is intentionally NON-discriminable by structural shape.
533
+ * Runtime dispatch keys on the EXPLICIT config `dialect` field in parse-config.ts
534
+ * and open-connections.ts. Each engine factory takes its own concrete config type
535
+ * directly. No `dialect` discriminant is added to union members.
536
+ *
537
+ * `password` MUST be supplied as a `${env:VAR}` reference — never a literal.
538
+ * `port` defaults to 3306 when omitted.
539
+ * NO `schema?` field: the connected `database` IS the extraction scope
540
+ * (schema == database in MySQL — there is no schema-vs-database distinction).
541
+ *
542
+ * US-029 (MySQL adapter, Phase 8b), mysql-extraction spec "Connectivity via host/port".
543
+ */
544
+ interface MysqlAdapterConfig {
545
+ readonly host: string;
546
+ readonly port?: number;
547
+ readonly database: string;
548
+ readonly user: string;
549
+ readonly password: string;
550
+ readonly ssl?: boolean | {
551
+ readonly rejectUnauthorized?: boolean;
552
+ };
553
+ }
554
+ /**
555
+ * Configuration for the MongoDB schema adapter.
556
+ *
557
+ * URI-based: the full connection string is carried in `uri` — NO host/port/user/
558
+ * password/schema decomposition. The password (embedded in the URI) MUST be
559
+ * supplied as a `${env:VAR}` reference; literals are REJECTED by the parser.
560
+ *
561
+ * IMPORTANT — union discriminability: Runtime dispatch keys on the EXPLICIT config
562
+ * `dialect` field in parse-config.ts / open-connections.ts, NOT on shape.
563
+ * No `dialect` discriminant is added to union members.
564
+ *
565
+ * `database` names the extraction scope (also used as the schema name in the
566
+ * normalized catalog — schema == database for MongoDB).
567
+ * `sampleSize` controls how many documents per collection are sampled to infer
568
+ * field types; defaults to 100 at runtime.
569
+ * `tls` enables TLS/SSL for the connection when true.
570
+ *
571
+ * US-030 (MongoDB adapter, Phase 9b), mongodb-extraction spec
572
+ * "Connectivity via a URI reference".
573
+ */
574
+ interface MongodbAdapterConfig {
575
+ readonly uri: string;
576
+ readonly database: string;
577
+ readonly sampleSize?: number;
578
+ readonly tls?: boolean;
579
+ }
580
+ /**
581
+ * Union of all engine-specific config shapes.
582
+ *
583
+ * This is a STRUCTURAL union for typing only — it is intentionally
584
+ * NON-discriminable by shape (pg and mysql both carry `host`; sqlite uses `file`;
585
+ * mssql uses `server`; mongodb uses `uri`). Runtime dispatch is by the EXPLICIT
586
+ * config `dialect` field in parse-config.ts / open-connections.ts; each engine
587
+ * factory takes its own concrete config type directly. No `dialect` discriminant
588
+ * is added to members.
589
+ *
590
+ * Future engines add their own member without touching existing members.
591
+ */
592
+ type SchemaAdapterConfig = SqliteAdapterConfig | MssqlAdapterConfig | PgAdapterConfig | MysqlAdapterConfig | MongodbAdapterConfig;
593
+ /**
594
+ * The driven port every source-database adapter MUST implement.
595
+ *
596
+ * Lifecycle:
597
+ * createXxxSchemaAdapter(config) → already-open SchemaAdapter
598
+ * extract(scope) / fingerprint() ← may be called any number of times
599
+ * close() ← idempotent; a second call MUST NOT throw
600
+ *
601
+ * The `open` step is handled by the FACTORY (mirrors createSqliteGraphStore),
602
+ * so this port exposes no `open()` method.
603
+ */
604
+ interface SchemaAdapter {
605
+ /** Stable engine identifier, e.g. 'sqlite', 'mssql', 'pg'. */
606
+ readonly dialect: string;
607
+ /**
608
+ * Truthful capability matrix for this engine.
609
+ * Declares which NodeKind types are extractable, whether bodies are available,
610
+ * and whether dependency hints are supported.
611
+ */
612
+ readonly capabilities: CapabilityMatrix;
613
+ /**
614
+ * Extract the source database's schema into a RawCatalog.
615
+ * Honours the ExtractionScope levels (off / metadata / full).
616
+ * Returns a value consumable by normalizeCatalog without any adapter import.
617
+ */
618
+ extract(scope: ExtractionScope): Promise<RawCatalog>;
619
+ /**
620
+ * Compute a cheap drift fingerprint for the current schema.
621
+ * MUST change on DDL changes; MUST be stable across data-only changes (US-009).
622
+ * Issues exactly ONE catalog query — does NOT walk all objects.
623
+ */
624
+ fingerprint(): Promise<string>;
625
+ /**
626
+ * Release the connection. Idempotent — a second call MUST NOT throw.
627
+ */
628
+ close(): Promise<void>;
629
+ }
630
+
631
+ /**
632
+ * CapabilityProbe port — engine-agnostic capability detection contract.
633
+ * Design §"probe() is OPTIONAL on the strategy port; per-engine probe in adapters".
634
+ * Resilient-connectivity Batch 1, task 1.1.
635
+ *
636
+ * This file imports ONLY core types. It MUST NOT import any driver,
637
+ * external-tool, node:child_process, adapter, cli, or mcp symbol (ADR-004).
638
+ * Concrete probe implementations live under src/adapters/engines/<engine>/probe.ts.
639
+ *
640
+ * The probe contract:
641
+ * - MUST NOT throw — any detection failure reports as a negative result
642
+ * - MUST NOT open a database connection to determine availability
643
+ * - MUST NOT issue any write
644
+ */
645
+ /**
646
+ * Information about a single CLI tool found (or not found) on PATH.
647
+ * `version` and `path` are null when the tool is absent or its version is
648
+ * unparseable from the tool's own output.
649
+ */
650
+ interface CliToolInfo {
651
+ /** Stable tool name, e.g. 'sqlcmd', 'psql', 'mysql'. */
652
+ readonly tool: string;
653
+ /** Parsed version string, or null if absent or unparseable. */
654
+ readonly version: string | null;
655
+ /** Resolved absolute path, or null if not found on PATH. */
656
+ readonly path: string | null;
657
+ }
658
+ /**
659
+ * The result of running a capability probe for a given engine.
660
+ * All fields are non-throwing — unavailability is a false/null/empty value.
661
+ */
662
+ interface ProbeResult {
663
+ /** Whether the engine's native driver package is importable (no DB connection attempted). */
664
+ readonly nativeDriver: boolean;
665
+ /** Zero or more CLI tools scanned on PATH — one entry per relevant tool for this engine. */
666
+ readonly cliTools: readonly CliToolInfo[];
667
+ /** Whether an ODBC driver is present (applicable to mssql; false for other engines). */
668
+ readonly odbc: boolean;
669
+ }
670
+ /**
671
+ * The driver port every per-engine capability probe MUST implement.
672
+ *
673
+ * Implementations live in src/adapters/engines/<engine>/probe.ts.
674
+ * This interface is consumed driver-free by:
675
+ * - core/present/doctor.ts (content-free report)
676
+ * - adapters/engines/mssql/strategies/profiles.ts (resolveProfile)
677
+ * - cli/commands/doctor.ts (runDoctor)
678
+ *
679
+ * ZERO driver, tool, or node:child_process imports are permitted here.
680
+ */
681
+ interface CapabilityProbe {
682
+ /**
683
+ * Stable engine identifier, e.g. 'mssql', 'pg', 'mysql', 'sqlite'.
684
+ */
685
+ readonly engine: string;
686
+ /**
687
+ * Run a full capability detection for this engine and return the result.
688
+ *
689
+ * MUST resolve — never reject.
690
+ * MUST NOT open a database connection.
691
+ * MUST NOT issue any write.
692
+ * A timed-out or failed detection step MUST be reported as a negative result.
693
+ */
694
+ probe(): Promise<ProbeResult>;
695
+ }
696
+
697
+ /**
698
+ * ConnectivityStrategy port — engine-agnostic strategy contract for schema extraction.
699
+ * Design §"ConnectivityStrategy port in core, driver-free (ADR-004)".
700
+ *
701
+ * This file imports ONLY core model types (RawCatalog, ExtractionScope) and the Logger
702
+ * port. It MUST NOT import any driver, external-tool, or node:child_process symbol.
703
+ * Concrete strategies live under src/adapters/engines/<engine>/strategies/ (ADR-004).
704
+ *
705
+ * connectivity-strategies Batch A, task A1.1.
706
+ */
707
+
708
+ /**
709
+ * Result of a strategy's availability probe.
710
+ * `available: false` when the prerequisite (tool on PATH, dump file, credentials) is absent.
711
+ * `detail` carries human-readable discovery metadata (version, path, reason).
712
+ */
713
+ interface DetectResult {
714
+ readonly available: boolean;
715
+ /** Optional human-readable detail: tool version, file path, or reason unavailable. */
716
+ readonly detail?: string;
717
+ }
718
+ /**
719
+ * Records why a strategy was skipped or failed during selection.
720
+ * Carried by StrategyExhaustionError to enumerate what was tried and why.
721
+ */
722
+ interface StrategyAttempt {
723
+ /** The stable strategy identifier, e.g. 'native-tedious', 'sqlcmd', 'manual-dump'. */
724
+ readonly id: string;
725
+ /** Human-readable reason the strategy could not be used. */
726
+ readonly reason: string;
727
+ }
728
+ /**
729
+ * The driven port every connectivity strategy MUST implement.
730
+ *
731
+ * Lifecycle (per extraction):
732
+ * detect() — is the prerequisite available? (no DB connection opened)
733
+ * canConnect() — cheap SELECT 1 / file-read probe
734
+ * runCatalog(scope) — full catalog extraction
735
+ * close?() — release any held resources (idempotent)
736
+ *
737
+ * The port is implementable by any engine without changing core (ADR-004).
738
+ * ZERO driver, tool, or node:child_process imports are permitted here.
739
+ */
740
+ interface ConnectivityStrategy {
741
+ /**
742
+ * Stable identifier for logging and error reporting.
743
+ * Examples: 'native-tedious', 'sqlcmd', 'manual-dump', 'consented-install'.
744
+ */
745
+ readonly id: string;
746
+ /**
747
+ * Probe whether the strategy's prerequisite is present on this machine.
748
+ * MUST NOT open a database connection.
749
+ * A failed or timed-out probe MUST resolve { available: false }, never reject.
750
+ */
751
+ detect(): Promise<DetectResult>;
752
+ /**
753
+ * Cheap connectivity probe (SELECT 1 / file readable).
754
+ * Called only when detect() resolved available: true.
755
+ * Returns true when the strategy can likely run a full extraction.
756
+ */
757
+ canConnect(): Promise<boolean>;
758
+ /**
759
+ * Execute the full catalog extraction and return a RawCatalog.
760
+ * MUST issue ONLY catalog SELECT statements (read-only inviolable, US-031).
761
+ */
762
+ runCatalog(scope: ExtractionScope): Promise<RawCatalog>;
763
+ /**
764
+ * Run a capability detection for this strategy's engine and return the result.
765
+ * Optional — existing strategies that omit it remain valid (back-compat).
766
+ * When present:
767
+ * MUST resolve — never reject.
768
+ * MUST NOT open a database connection.
769
+ * MUST NOT issue any write.
770
+ * Consumed by adapters/engines/<engine>/strategies/registry.ts (selectStrategy)
771
+ * and cli/commands/doctor.ts (runDoctor) via the core port shape.
772
+ * resilient-connectivity Batch 1, task 1.4.
773
+ */
774
+ probe?(): Promise<ProbeResult>;
775
+ /**
776
+ * Compute a cheap DDL-sensitive fingerprint for the current schema.
777
+ * Optional. When absent, StrategyBackedSchemaAdapter falls back to a
778
+ * content-hash over the extracted catalog (if available in the future).
779
+ * When present, MUST change on DDL changes and be stable on DML (US-009).
780
+ * Issues exactly ONE catalog query — does NOT walk all objects.
781
+ */
782
+ fingerprint?(): Promise<string>;
783
+ /**
784
+ * Release any held resources (connection pool, file handle, etc.).
785
+ * Optional. When present, MUST be idempotent — a second call MUST NOT throw.
786
+ */
787
+ close?(): Promise<void>;
788
+ }
789
+
790
+ /**
791
+ * Deterministic node/edge ID derivation and canonical qualified-name helpers.
792
+ * Design §3.4, ADR-008 — IDs are pure functions of semantic identity.
793
+ * Uses Node's built-in crypto (ADR-007: no new dependencies).
794
+ * This file imports NOTHING from adapters, drivers, mcp, or cli (ADR-004).
795
+ */
796
+ /**
797
+ * Returns the canonical qualified name for a node: case-folded, quote-stripped,
798
+ * segments joined by '.'. If schema is null, returns just the name.
799
+ *
800
+ * @example
801
+ * canonicalQName('DBO', 'Orders') // → 'dbo.orders'
802
+ * canonicalQName('[dbo]', '[Orders]') // → 'dbo.orders'
803
+ * canonicalQName(null, 'my_db') // → 'my_db'
804
+ */
805
+ declare function canonicalQName(schema: string | null, name: string): string;
806
+ /**
807
+ * Derives a deterministic node ID from a node kind and its canonical qualified name.
808
+ * Formula: sha1(kind + ' ' + canonicalQName)
809
+ *
810
+ * The kind prefix prevents collisions between e.g. a table and a view sharing
811
+ * a name in different namespaces. The qname is case-folded by the caller
812
+ * or via canonicalQName(), so IDs are case-insensitive.
813
+ *
814
+ * Stubs reuse the SAME derivation from the referenced qname — a later real
815
+ * extraction of that object lands on the SAME node ID (idempotent upsert).
816
+ */
817
+ declare function nodeId(kind: string, qname: string): string;
818
+ /**
819
+ * Derives a deterministic edge ID from edge kind, source id, destination id,
820
+ * and a discriminator string that makes parallel edges distinct.
821
+ *
822
+ * Discriminator usage:
823
+ * - 'references' per-column: 'srcColumn>dstColumn'
824
+ * - 'references' aggregated: 'aggregate'
825
+ * - 'fires_on': the event ('INSERT' | 'UPDATE' | 'DELETE')
826
+ * - edges that cannot duplicate: '' (empty string)
827
+ *
828
+ * Formula: sha1(kind + ' ' + src_id + ' ' + dst_id + ' ' + discriminator)
829
+ */
830
+ declare function edgeId(kind: string, srcId: string, dstId: string, discriminator: string): string;
831
+ /**
832
+ * Serializes a value to JSON with recursively sorted object keys so that the
833
+ * output is byte-identical for the same structural value regardless of the
834
+ * property insertion order. Arrays preserve their element order.
835
+ *
836
+ * Used in the normalizer to ensure payload/attrs JSON is deterministic (§5.6).
837
+ */
838
+ declare function stableStringify(value: unknown): string;
839
+
840
+ /**
841
+ * normalizeCatalog — the main normalizer entry point.
842
+ * Design §5 — pipeline: validate → filter → primary nodes → references → stubs → ordering.
843
+ * Pure function; no I/O. ADR-004: imports only from src/core.
844
+ * ADR-008: deterministic output (sorted by kind/qname/id; stableStringify for JSON).
845
+ */
846
+
847
+ /**
848
+ * Converts a RawCatalog into a deterministic NormalizationResult.
849
+ * Design §5.1 pipeline order is strictly followed.
850
+ */
851
+ declare function normalizeCatalog(raw: RawCatalog, scope: ExtractionScope): NormalizationResult;
852
+
853
+ /**
854
+ * Level application (off / metadata / full) and body normalization.
855
+ * Design §5.4, §5.5 — centralizes level logic so it is applied identically everywhere.
856
+ * US-003: metadata keeps node+edges but no body; full includes normalized body + FTS.
857
+ * ADR-005/008: body whitespace normalization ensures bodyHash is stable across environments.
858
+ * This file imports NOTHING from adapters, drivers, mcp, or cli (ADR-004).
859
+ */
860
+
861
+ /**
862
+ * Normalizes a procedure/trigger body for stable hashing and storage:
863
+ * 1. Normalizes CRLF → LF.
864
+ * 2. Trims trailing whitespace on each line.
865
+ * 3. Drops a trailing newline from the result.
866
+ *
867
+ * Does NOT re-format SQL or alter semantics — purely whitespace canonicalization.
868
+ * Idempotent: normalizing twice yields the same output.
869
+ */
870
+ declare function normalizeBody(body: string): string;
871
+ /** Result of applying a level to a node's body/comment context. */
872
+ interface LevelResult {
873
+ /** Normalized body — present only at level='full' and only when body was supplied. */
874
+ readonly body?: string;
875
+ /** SHA-1 hash of normalized body (ADR-005); null when body is absent or level≠'full'. */
876
+ readonly bodyHash: string | null;
877
+ /** FTS body text: normalized body at 'full', empty string otherwise (US-003). */
878
+ readonly ftsBody: string;
879
+ /** Comment/description preserved from input. */
880
+ readonly comment?: string;
881
+ }
882
+ /**
883
+ * Applies an indexing level to a node's body/comment context.
884
+ *
885
+ * - `off` → returns null (caller MUST NOT produce a node or FTS row).
886
+ * - `metadata` → returns LevelResult with no body, null bodyHash, empty ftsBody.
887
+ * - `full` → returns LevelResult with normalized body (if supplied), sha1 bodyHash,
888
+ * and ftsBody populated with the normalized body for FTS indexing.
889
+ *
890
+ * @param input - The level and optional body/comment for the node.
891
+ * @returns LevelResult for metadata/full, or null for off.
892
+ */
893
+ declare function applyLevel(input: {
894
+ level: IndexLevel;
895
+ body?: string;
896
+ comment?: string;
897
+ }): LevelResult | null;
898
+
899
+ /**
900
+ * getNeighbors — US-013: direct neighbors grouped by edge kind and direction.
901
+ * Design §6.1 — pure orchestration over the GraphStore port.
902
+ * ADR-004: imports only core model and ports, never adapters/drivers/mcp/cli.
903
+ * ADR-008: deterministic output (groups in fixed kind order; neighbors sorted by qname, id).
904
+ */
905
+
906
+ /**
907
+ * Returns all direct neighbors of a node grouped by edge kind and direction.
908
+ *
909
+ * - Outbound edges (getEdgesFrom): the neighbour is the edge's `dst`.
910
+ * - Inbound edges (getEdgesTo): the neighbour is the edge's `src`.
911
+ * - If `q.kinds` is provided, only edges matching those kinds are included.
912
+ * - Inferred edges appear under their own kind group (`inferred_reference`)
913
+ * — they are NOT mixed with declared/parsed edges.
914
+ * - Deterministic: within each (kind, direction) group, neighbours are sorted
915
+ * by (qname, id).
916
+ */
917
+ declare function getNeighbors(store: GraphStore, q: NeighborQuery): Promise<NeighborGroups>;
918
+
919
+ /**
920
+ * getImpact — US-014: depth-limited blast-radius traversal separating read/write impact.
921
+ * Design §6.2 — BFS over inbound edges, read/write split, visible chains a→b→c,
922
+ * depth cap + truncation warning, cycle safety (visited set), dynamic-SQL propagation.
923
+ * ADR-004: imports only core. ADR-008: deterministic output.
924
+ */
925
+
926
+ /**
927
+ * Computes the transitive impact closure of a node as visible dependency chains.
928
+ *
929
+ * Algorithm (BFS):
930
+ * 1. Start from `q.nodeId`; add it to `visited`.
931
+ * 2. For each frontier node, fetch inbound impact edges (who points AT it).
932
+ * 3. For each new (unvisited) neighbour create a new chain by extending the
933
+ * predecessor chain and enqueue.
934
+ * 4. Stop expanding when `depth` is reached; set `truncated` if any cut-off
935
+ * node had unexpanded inbound edges.
936
+ * 5. After BFS, classify each chain into readImpact or writeImpact based on
937
+ * the last hop's edge kind. A chain may contribute to both if its edges mix
938
+ * (the terminal hop decides the bucket).
939
+ * 6. Set `dynamicSqlWarning` if any node in any surfaced chain has
940
+ * `payload.hasDynamicSql === true`.
941
+ * 7. Sort chains and their nodes deterministically.
942
+ */
943
+ declare function getImpact(store: GraphStore, q: ImpactQuery): Promise<ImpactResult>;
944
+
945
+ /**
946
+ * findJoinPath — US-015: shortest join path over references edges with exact join columns.
947
+ * Design §6.3 — BFS at table grain over aggregated references edges; per-column edges
948
+ * supply the exact join columns for each hop. No-route → nearest neighbors suggestion.
949
+ * ADR-004: imports only core. ADR-008: deterministic (BFS in sorted order; ties by qname/id).
950
+ */
951
+
952
+ /**
953
+ * Finds the shortest join path between two table nodes over `references` edges.
954
+ *
955
+ * Algorithm:
956
+ * 1. BFS over aggregated references edges (attrs.aggregate === true) in both directions.
957
+ * Using ONLY aggregated edges avoids traversing per-column edges during BFS;
958
+ * they are resolved separately for join columns.
959
+ * 2. When the target node is found, reconstruct the path from the predecessor map.
960
+ * 3. For each hop, collect the matching per-column references edges to emit exact join columns.
961
+ * 4. If `allowInferred` is false (default), skip `inferred_reference` edges.
962
+ * 5. No route: return `found:false` with the nearest one-hop neighbours of each endpoint.
963
+ * 6. Same node (from === to): return `found:true` with empty hops array.
964
+ *
965
+ * Determinism (ADR-008): BFS explores neighbours in sorted (qname, id) order so the
966
+ * chosen shortest path is stable across runs. Ties are broken by (qname, id).
967
+ */
968
+ declare function findJoinPath(store: GraphStore, q: PathQuery): Promise<PathResult>;
969
+
970
+ /**
971
+ * search — US-011: full-text search over FTS-indexed graph nodes.
972
+ * Design §6.4 — delegates to port.searchFts (FTS5 via adapter), with a TS-side
973
+ * Levenshtein fallback when FTS returns no results (typo tolerance).
974
+ *
975
+ * GOLDEN-PINNED constants (ADR-008 — determinism):
976
+ * LEVENSHTEIN_THRESHOLD = 2 (max edit distance for fallback match)
977
+ * TYPO_CAP = 5 (max results returned by fallback)
978
+ *
979
+ * These MUST NOT be changed without updating goldens and the apply-progress log.
980
+ *
981
+ * ADR-004: imports only core. ADR-007: no new dependencies (Levenshtein in-lined here).
982
+ */
983
+
984
+ /** Maximum Levenshtein edit distance for a typo-fallback match. */
985
+ declare const LEVENSHTEIN_THRESHOLD: 2;
986
+ /** Maximum number of results returned by the typo-fallback path. */
987
+ declare const TYPO_CAP: 5;
988
+ interface SearchResult {
989
+ readonly hits: readonly SearchHit[];
990
+ readonly total: number;
991
+ }
992
+ /**
993
+ * Searches the graph by full-text term.
994
+ *
995
+ * 1. Build an FTS5-compatible prefix query from the term tokens and call
996
+ * `store.searchFts`. Results are ranked by BM25 (the adapter handles that).
997
+ * 2. If FTS returns zero results, run the TS-side Levenshtein fallback:
998
+ * - Gather candidate qnames from all non-stub nodes (fetched per-kind via port).
999
+ * - Compute Levenshtein distance between the term and each qname's local name.
1000
+ * - Return up to TYPO_CAP results with distance ≤ LEVENSHTEIN_THRESHOLD,
1001
+ * sorted by (distance ASC, qname ASC) for determinism (ADR-008).
1002
+ * 3. Propagate `limit`/`offset` to the FTS call; the fallback respects TYPO_CAP.
1003
+ */
1004
+ declare function search(store: GraphStore, q: SearchQuery): Promise<SearchResult>;
1005
+
1006
+ /**
1007
+ * formatExplore — task 5.1 (phase-4-cli-config).
1008
+ * Spec: cli-config "explore output comes from a pure formatter shared with the MCP tool"
1009
+ * Design: PURE, core-types-only, golden-pinned. Lives in src/core/present/ per orchestrator
1010
+ * override (supersedes design.md Decision 2 which placed it in src/cli/format/).
1011
+ * Reused by CLI (phase-4) AND Phase-5 MCP tool — "same source, same golden" (US-021).
1012
+ *
1013
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1014
+ * The existing test/core/boundaries.test.ts enforces this at CI.
1015
+ * ADR-008: deterministic output — same (ExploreView, ExploreDetail) → byte-identical string.
1016
+ * No process.env / Date.now() / Math.random() / I/O anywhere in this file.
1017
+ *
1018
+ * --detail levels:
1019
+ * brief — qname + kind + 1-line neighbor-kind counts
1020
+ * normal — brief + grouped neighbors (edge kind / direction / qname-sorted)
1021
+ * full — normal + bodyHash + level + dynamic-SQL warning when payload.hasDynamicSql=true
1022
+ */
1023
+
1024
+ /** The detail level requested by the caller (--detail flag). */
1025
+ type ExploreDetail = 'brief' | 'normal' | 'full';
1026
+ /**
1027
+ * Input bundle for formatExplore.
1028
+ * Assembled by the caller from getNeighbors() + GraphNode (both core types).
1029
+ * Design intentionally mirrors the future MCP ExploreToolInput.
1030
+ */
1031
+ interface ExploreView {
1032
+ readonly node: GraphNode;
1033
+ readonly neighbors: NeighborGroups;
1034
+ }
1035
+ /**
1036
+ * Formats an ExploreView into a human-readable string at the requested detail level.
1037
+ *
1038
+ * Output contract (ADR-008):
1039
+ * - Same (view, detail) → always the SAME bytes
1040
+ * - Trailing newline guaranteed
1041
+ * - No Date.now(), no process.env, no I/O
1042
+ */
1043
+ declare function formatExplore(view: ExploreView, detail: ExploreDetail): string;
1044
+
1045
+ /**
1046
+ * formatSearch — task 1.2 (phase-5-mcp-server).
1047
+ * Spec: dbgraph_search ranked paginated hits; Pagination.
1048
+ * Design: PURE, core-types-only, golden-pinned. Lives in src/core/present/ per ADR-004.
1049
+ *
1050
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1051
+ * The existing test/core/boundaries.test.ts enforces this at CI.
1052
+ * ADR-008: deterministic output — same (SearchView, SearchDetail) → byte-identical string.
1053
+ * No process.env / Date.now() / Math.random() / I/O anywhere in this file.
1054
+ *
1055
+ * --detail levels:
1056
+ * brief — type + qname per hit, pagination footer
1057
+ * normal — brief + match column indicator
1058
+ * full — normal + match column (body/comment/qname) explicitly labeled
1059
+ */
1060
+
1061
+ /** The detail level for search results. */
1062
+ type SearchDetail = 'brief' | 'normal' | 'full';
1063
+ /**
1064
+ * Input bundle for formatSearch.
1065
+ * Assembled by the caller from the search() result.
1066
+ */
1067
+ interface SearchView {
1068
+ readonly hits: readonly SearchHit[];
1069
+ readonly total: number;
1070
+ readonly offset: number;
1071
+ readonly limit: number;
1072
+ }
1073
+ /**
1074
+ * Formats a SearchView into a human-readable string at the requested detail level.
1075
+ *
1076
+ * Output contract (ADR-008):
1077
+ * - Same (view, detail) → always the SAME bytes
1078
+ * - Trailing newline guaranteed
1079
+ * - No Date.now(), no process.env, no I/O
1080
+ */
1081
+ declare function formatSearch(view: SearchView, detail: SearchDetail): string;
1082
+
1083
+ /**
1084
+ * formatObject — task 1.3 (phase-5-mcp-server).
1085
+ * Spec: dbgraph_object assembles full detail; Metadata-level states body omitted.
1086
+ * Design: PURE, core-types-only, golden-pinned. Lives in src/core/present/ per ADR-004.
1087
+ *
1088
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1089
+ * ADR-008: deterministic output — same (ObjectView, ObjectDetail) → byte-identical string.
1090
+ *
1091
+ * --detail levels:
1092
+ * brief — header + annotation counts (idx/trg counts)
1093
+ * normal — brief + columns (type/null/default), PK/FK/check constraints
1094
+ * full — normal + indexes (cols+kind), triggers (event), body for full-level modules
1095
+ */
1096
+
1097
+ /** The detail level for object output. */
1098
+ type ObjectDetail = 'brief' | 'normal' | 'full';
1099
+ /**
1100
+ * Input bundle for formatObject.
1101
+ * Assembled by the caller from getNodeByQName + getNeighbors.
1102
+ */
1103
+ interface ObjectView {
1104
+ readonly node: GraphNode;
1105
+ readonly neighbors: NeighborGroups;
1106
+ }
1107
+ /**
1108
+ * Formats an ObjectView into a human-readable string at the requested detail level.
1109
+ *
1110
+ * Output contract (ADR-008):
1111
+ * - Same (view, detail) → always the SAME bytes
1112
+ * - Trailing newline guaranteed
1113
+ * - No Date.now(), no process.env, no I/O
1114
+ */
1115
+ declare function formatObject(view: ObjectView, detail: ObjectDetail): string;
1116
+
1117
+ /**
1118
+ * payload.ts — change explore-payloads (US-036).
1119
+ * Spec: mcp-server "One shared payload-render helper backs explore and object";
1120
+ * cli-config "explore output comes from a pure formatter shared with the MCP tool".
1121
+ *
1122
+ * ONE pure module rendering the per-kind node payload as section-body lines. Both
1123
+ * `formatObject` and `formatExplore` consume these renderers so the section bytes are
1124
+ * byte-identical across surfaces (no per-surface branch, no drift — design D1/D2).
1125
+ *
1126
+ * Contract (design D1):
1127
+ * - Each renderer returns the section HEADER + one body row per entry, WITHOUT a
1128
+ * leading blank separator. Callers keep the inter-section `push('')` cadence, so
1129
+ * today's exact bytes are reproduced (refactor transparency).
1130
+ * - Each renderer returns [] when its group is empty.
1131
+ * - deriveColumnAnnotations computes the PK set + FK colname→target map ONCE. FK
1132
+ * target precedence (design D8): constraint payload `definition` when present,
1133
+ * else reconstructed from the `references` edges when unambiguous (Batch B),
1134
+ * else omitted — never guessed.
1135
+ *
1136
+ * ADR-004: imports ONLY core model types — NO adapters, NO cli, NO mcp, NO drivers.
1137
+ * ADR-008: deterministic — same input → byte-identical string[]. No Date/env/random/I/O.
1138
+ */
1139
+
1140
+ /** A neighbor entry as returned by getNeighbors — the node plus its connecting edge. */
1141
+ interface NeighborEntry {
1142
+ readonly node: GraphNode;
1143
+ readonly edge?: GraphEdge;
1144
+ }
1145
+ /** Column-level annotations derived once from a node's constraint + references neighbors. */
1146
+ interface ColumnAnnotations {
1147
+ /** Column names that participate in the primary key. */
1148
+ readonly pk: ReadonlySet<string>;
1149
+ /** FK column name → rendered target (payload definition, or reconstructed table-level qname). */
1150
+ readonly fk: ReadonlyMap<string, string>;
1151
+ }
1152
+ /**
1153
+ * Computes the PK column set and the FK colname→target map from a node's constraint
1154
+ * neighbors. FK target precedence (design D8):
1155
+ * 1. constraint payload `definition` present → rendered verbatim (column-level target).
1156
+ * 2. (Batch B) payload target absent → reconstructed table-level target from the
1157
+ * `references` edges when unambiguous.
1158
+ * 3. ambiguous / no resolvable target → omitted (honest degradation, never guessed).
1159
+ */
1160
+ declare function deriveColumnAnnotations(constraints: readonly NeighborEntry[], references: readonly NeighborEntry[]): ColumnAnnotations;
1161
+ /** COLUMNS section: one row per column with type + PK/FK/NN markers and DEFAULT. */
1162
+ declare function renderColumns(columns: readonly NeighborEntry[], a: ColumnAnnotations): string[];
1163
+ /** CONSTRAINTS section: one row per constraint, FK column→target mapping via `a.fk`. */
1164
+ declare function renderConstraints(constraints: readonly NeighborEntry[], a: ColumnAnnotations): string[];
1165
+ /** INDEXES section: one row per index with UNIQUE + columns + optional [method]. */
1166
+ declare function renderIndexes(indexes: readonly NeighborEntry[]): string[];
1167
+ /** TRIGGERS section: one row per trigger with timing + events (from the fires_on.in group). */
1168
+ declare function renderTriggers(triggers: readonly NeighborEntry[]): string[];
1169
+ /**
1170
+ * Renders a single non-container focus node's OWN per-kind payload line(s) using the
1171
+ * SAME per-kind grammar as the section renderers (design D2). PK/FK markers appear on a
1172
+ * column focus ONLY when parent-context annotations `a` are supplied; a bare column focus
1173
+ * still shows type/nullable/default. Returns [] for a kind that has no focus payload line
1174
+ * (e.g. a table/view — those render their full sections instead).
1175
+ */
1176
+ declare function renderFocusPayload(node: GraphNode, a?: ColumnAnnotations): string[];
1177
+
1178
+ /**
1179
+ * formatRelated — task 1.4 (phase-5-mcp-server).
1180
+ * Spec: dbgraph_related grouped by edge kind and direction; inferred edges separate group.
1181
+ * Design: PURE, core-types-only, reuses ExploreView. Lives in src/core/present/ per ADR-004.
1182
+ *
1183
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1184
+ * ADR-008: deterministic output — same (ExploreView, RelatedDetail) → byte-identical string.
1185
+ *
1186
+ * --detail levels:
1187
+ * brief — grouped edge kinds with in+out counts
1188
+ * normal — brief + qnames per group (sorted)
1189
+ * full — normal + inferred score for inferred edges
1190
+ *
1191
+ * Reuses ExploreView (node + neighbors map) — same shape as formatExplore input.
1192
+ * Inferred edges (confidence='inferred') appear in a SEPARATE group with score.
1193
+ */
1194
+
1195
+ /** The detail level for related output. */
1196
+ type RelatedDetail = 'brief' | 'normal' | 'full';
1197
+
1198
+ /**
1199
+ * Formats an ExploreView into a related-neighbors string at the requested detail level.
1200
+ *
1201
+ * Output contract (ADR-008):
1202
+ * - Same (view, detail) → always the SAME bytes
1203
+ * - Trailing newline guaranteed
1204
+ * - No Date.now(), no process.env, no I/O
1205
+ */
1206
+ declare function formatRelated(view: ExploreView, detail: RelatedDetail): string;
1207
+
1208
+ /**
1209
+ * formatImpact — task 1.5 (phase-5-mcp-server).
1210
+ * Spec: dbgraph_impact read/write blast radius; Depth truncation and dynamic-SQL warn.
1211
+ * Design: PURE; ImpactView { node; result: ImpactResult; resolve(id)→qname };
1212
+ * visible chain a→b→c, READ separated from WRITE, depth-truncation warning,
1213
+ * "impact possibly incomplete" when any node has_dynamic_sql.
1214
+ *
1215
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1216
+ * ADR-008: deterministic output — same (ImpactView, ImpactDetail) → byte-identical string.
1217
+ *
1218
+ * --detail levels:
1219
+ * brief — chain summary counts only
1220
+ * normal — full chain (a→b→c), read/write split
1221
+ * full — normal + node kinds, dynamic-SQL / truncation warnings
1222
+ */
1223
+
1224
+ /** The detail level for impact output. */
1225
+ type ImpactDetail = 'brief' | 'normal' | 'full';
1226
+ /**
1227
+ * Input bundle for formatImpact.
1228
+ * Assembled by the caller from getImpact().
1229
+ * resolve(id) maps node IDs in the chains to their qualified names.
1230
+ */
1231
+ interface ImpactView {
1232
+ readonly node: GraphNode;
1233
+ readonly result: ImpactResult;
1234
+ readonly resolve: (id: string) => string;
1235
+ }
1236
+ /**
1237
+ * Formats an ImpactView into a human-readable string at the requested detail level.
1238
+ *
1239
+ * Output contract (ADR-008):
1240
+ * - Same (view, detail) → always the SAME bytes
1241
+ * - Trailing newline guaranteed
1242
+ * - No Date.now(), no process.env, no I/O
1243
+ */
1244
+ declare function formatImpact(view: ImpactView, detail: ImpactDetail): string;
1245
+
1246
+ /**
1247
+ * formatPath — task 1.6 (phase-5-mcp-server).
1248
+ * Spec: dbgraph_path shortest join path or suggests neighbors; No route reports neighbors.
1249
+ * Design: PURE; PathView (PathResult + endpoint qnames); shortest route with exact join
1250
+ * columns per hop; inferred-only route marked inferred; no-route message + closest neighbors.
1251
+ *
1252
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1253
+ * ADR-008: deterministic output — same (PathView) → byte-identical string.
1254
+ *
1255
+ * Note: formatPath has a single implicit detail level (path has no meaningful brief/full split).
1256
+ */
1257
+
1258
+ /**
1259
+ * Input bundle for formatPath.
1260
+ * Assembled by the caller from findJoinPath() + node qname resolution.
1261
+ */
1262
+ interface PathView {
1263
+ readonly from: string;
1264
+ readonly to: string;
1265
+ readonly result: PathResult;
1266
+ readonly resolveTable: (id: string) => string;
1267
+ }
1268
+ /**
1269
+ * Formats a PathView into a human-readable string.
1270
+ *
1271
+ * Output contract (ADR-008):
1272
+ * - Same (view) → always the SAME bytes
1273
+ * - Trailing newline guaranteed
1274
+ * - No Date.now(), no process.env, no I/O
1275
+ */
1276
+ declare function formatPath(view: PathView): string;
1277
+
1278
+ /**
1279
+ * formatStatus — task 1.7 (phase-5-mcp-server).
1280
+ * Spec: dbgraph_status index trust and live drift (connectionless half).
1281
+ * Design: PURE; McpStatusView; engine/version, last sync, per-type counts, configured levels,
1282
+ * excluded objects, drift line ("could not be checked live" when no connection).
1283
+ *
1284
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1285
+ * ADR-008: deterministic output — same (McpStatusView) → byte-identical string.
1286
+ *
1287
+ * This is the MCP variant of status, distinct from the CLI status formatter.
1288
+ *
1289
+ * --detail levels:
1290
+ * brief — engine/version + last sync timestamp
1291
+ * normal — brief + per-type counts, configured levels
1292
+ * full — normal + excluded objects, drift detail
1293
+ */
1294
+
1295
+ /** The detail level for status output. */
1296
+ type StatusDetail = 'brief' | 'normal' | 'full';
1297
+ /**
1298
+ * Input bundle for formatStatus — MCP variant.
1299
+ * Assembled by the caller from listSnapshots() + capabilitiesFor() + drift check.
1300
+ */
1301
+ interface McpStatusView {
1302
+ readonly engine: string;
1303
+ readonly engineVersion: string | undefined;
1304
+ readonly lastSync: string | null;
1305
+ readonly counts: Readonly<Record<string, number>>;
1306
+ readonly levels: Readonly<Partial<ObjectTypeLevels>>;
1307
+ readonly excludedObjects: readonly string[];
1308
+ readonly driftChecked: boolean;
1309
+ readonly driftDetected: boolean | null;
1310
+ }
1311
+ /**
1312
+ * Formats a McpStatusView into a human-readable string at the requested detail level.
1313
+ *
1314
+ * Output contract (ADR-008):
1315
+ * - Same (view) → always the SAME bytes
1316
+ * - Trailing newline guaranteed
1317
+ * - No Date.now(), no process.env, no I/O
1318
+ */
1319
+ declare function formatStatus(view: McpStatusView, detail: StatusDetail): string;
1320
+
1321
+ /**
1322
+ * Typed error classes for dbgraph core.
1323
+ * Design §7.1 — each error carries a stable code, extends DbgraphError,
1324
+ * and has an actionable message. Never bare strings; nothing is swallowed.
1325
+ */
1326
+ /**
1327
+ * Base error class for all dbgraph errors.
1328
+ * Carries a stable, machine-readable `code` for programmatic handling.
1329
+ */
1330
+ declare class DbgraphError extends Error {
1331
+ readonly code: string;
1332
+ constructor(message: string, code: string);
1333
+ }
1334
+ /**
1335
+ * Thrown when the normalizer encounters a structural violation in a RawCatalog.
1336
+ * Message identifies which object and which field failed, e.g.:
1337
+ * "object dbo.orders: constraint FK_x has misaligned columns (2 src, 3 dst)"
1338
+ */
1339
+ declare class NormalizationError extends DbgraphError {
1340
+ constructor(message: string);
1341
+ }
1342
+ /**
1343
+ * Thrown when the SQLite adapter encounters a driver-level failure.
1344
+ * Wraps the underlying error as `cause` so the driver's original stack is preserved.
1345
+ */
1346
+ declare class StorageError extends DbgraphError {
1347
+ constructor(message: string, cause?: unknown);
1348
+ }
1349
+ /**
1350
+ * Thrown when the on-disk database was written by a newer version of dbgraph
1351
+ * than the running binary supports (observed > supported).
1352
+ * Message instructs the user to re-sync to rebuild the index.
1353
+ */
1354
+ declare class SchemaVersionError extends DbgraphError {
1355
+ readonly observed: number;
1356
+ readonly supported: number;
1357
+ constructor(observed: number, supported: number);
1358
+ }
1359
+ /**
1360
+ * Thrown when a query is called with invalid parameters (e.g. depth < 1).
1361
+ * Message describes the invalid parameter and its expected range.
1362
+ */
1363
+ declare class QueryError extends DbgraphError {
1364
+ constructor(message: string);
1365
+ }
1366
+ /**
1367
+ * Thrown when a requested node id or qname is not found in the graph store.
1368
+ * Message identifies what was looked up and instructs the user to re-sync.
1369
+ */
1370
+ declare class NotFoundError extends DbgraphError {
1371
+ constructor(kind: string, identifier: string);
1372
+ }
1373
+ /**
1374
+ * Thrown when configuration is invalid, malformed, or unsafe.
1375
+ * Examples: missing required field, unknown dialect, inline plaintext credential,
1376
+ * or an unset env variable referenced by ${env:VAR}.
1377
+ * Message MUST be actionable — naming the offending field and corrective action.
1378
+ */
1379
+ declare class ConfigError extends DbgraphError {
1380
+ constructor(message: string);
1381
+ }
1382
+ /**
1383
+ * Thrown when a requested dialect has no registered adapter.
1384
+ * Message names the bad dialect and lists the available dialects.
1385
+ */
1386
+ declare class UnsupportedDialectError extends DbgraphError {
1387
+ constructor(dialect: string);
1388
+ }
1389
+
1390
+ /**
1391
+ * A discriminated union of the actionable options presented to the user when no
1392
+ * connectivity method can be established. Three variants MUST always be present.
1393
+ *
1394
+ * Design §"ConnectivityOutcome + options live in core" — these are pure data
1395
+ * (engine-neutral, driver-free). Adapters BUILD the value; present/ RENDERS it.
1396
+ */
1397
+ type ConnectivityOption = {
1398
+ /** Emit the exact read-only catalog SELECT queries for the user to run themselves. */
1399
+ readonly kind: 'run-it-yourself';
1400
+ readonly description: string;
1401
+ /** The EXACT read-only catalog SELECT statements, write-verb-free. */
1402
+ readonly queries: readonly string[];
1403
+ } | {
1404
+ /** Offer to install the missing driver/tool ONLY with explicit user consent. */
1405
+ readonly kind: 'consented-install';
1406
+ readonly description: string;
1407
+ readonly tool: string;
1408
+ readonly docUrl: string;
1409
+ } | {
1410
+ /** Import a combined JSON dump the user produced externally. */
1411
+ readonly kind: 'manual-dump';
1412
+ readonly description: string;
1413
+ readonly outputPath: string;
1414
+ };
1415
+ /**
1416
+ * The structured, engine-neutral outcome yielded when no connectivity method
1417
+ * can establish a connection. Carries at least three actionable options.
1418
+ *
1419
+ * Design §"typed throw, not return-type change" — thrown as ConnectivityUnavailableError.
1420
+ * The summary field MUST be content-free (no schema names, object identifiers, or secrets).
1421
+ */
1422
+ interface ConnectivityOutcome {
1423
+ /** Stable engine identifier, e.g. 'mssql', 'pg', 'mysql', 'sqlite'. */
1424
+ readonly engine: string;
1425
+ /** Content-free human-readable summary. MUST NOT contain schema/identifier/secret. */
1426
+ readonly summary: string;
1427
+ /** Ordered list of strategies attempted and the reason each was skipped. */
1428
+ readonly attempts: readonly StrategyAttempt[];
1429
+ /** The at-least-three options offered to the user. Length MUST be >= 3. */
1430
+ readonly options: readonly ConnectivityOption[];
1431
+ }
1432
+ /**
1433
+ * Thrown when all connectivity strategies for an engine have been exhausted —
1434
+ * none could both detect their prerequisite AND successfully probe a connection.
1435
+ * Carries the ordered list of attempts and their individual reasons so callers
1436
+ * and the CLI presenter can surface actionable guidance.
1437
+ *
1438
+ * Code: E_STRATEGY_EXHAUSTION.
1439
+ * connectivity-strategies A1.3.
1440
+ */
1441
+ declare class StrategyExhaustionError extends DbgraphError {
1442
+ readonly attempts: readonly StrategyAttempt[];
1443
+ constructor(attempts: readonly StrategyAttempt[]);
1444
+ }
1445
+ /**
1446
+ * Thrown when a transport, format, or parse failure occurs during mssql catalog
1447
+ * extraction via sqlcmd. The message is REDACTED — it never echoes raw stderr,
1448
+ * host names, or connection-string identifiers. The raw cause (if any) is
1449
+ * preserved as `error.cause` for debugging only and is never surfaced to the user.
1450
+ *
1451
+ * Code: E_TRANSPORT.
1452
+ * connectivity MODIFIED requirement: "a transport/format/parse failure is
1453
+ * redacted into a typed error".
1454
+ * resilient-connectivity R1 remediation (C2).
1455
+ */
1456
+ declare class TransportError extends DbgraphError {
1457
+ constructor(message: string, cause?: unknown);
1458
+ }
1459
+ /**
1460
+ * Thrown when the engine adapter cannot connect to the source database.
1461
+ * Covers: file not found, file not a valid database, database locked/busy,
1462
+ * and required driver package not installed.
1463
+ * Message MUST be actionable — naming the failing condition and the corrective action.
1464
+ * Wraps the underlying driver error as `cause` so the original stack is preserved.
1465
+ * US-026 / US-031.
1466
+ */
1467
+ declare class ConnectionError extends DbgraphError {
1468
+ constructor(message: string, cause?: unknown);
1469
+ }
1470
+ /**
1471
+ * Thrown when a write or otherwise disallowed operation is attempted on a
1472
+ * read-only source-database connection (US-031).
1473
+ * Wraps the underlying driver error as `cause`.
1474
+ */
1475
+ declare class PermissionError extends DbgraphError {
1476
+ constructor(message: string, cause?: unknown);
1477
+ }
1478
+ /**
1479
+ * Thrown when no connectivity method can be established for an engine.
1480
+ * Carries a structured, engine-neutral `ConnectivityOutcome` with at least
1481
+ * three actionable options (run-it-yourself, consented-install, manual-dump).
1482
+ *
1483
+ * Code: E_CONNECTIVITY_UNAVAILABLE.
1484
+ * Design §"typed throw, not return-type change" — bubbles to cli.ts catch boundary.
1485
+ * The message is content-free (carries outcome.summary only — no identifier/secret).
1486
+ * resilient-connectivity Batch 1, task 1.2.
1487
+ */
1488
+ declare class ConnectivityUnavailableError extends DbgraphError {
1489
+ readonly outcome: ConnectivityOutcome;
1490
+ constructor(outcome: ConnectivityOutcome);
1491
+ }
1492
+
1493
+ /**
1494
+ * formatOutcome — task 1.3 (resilient-connectivity Batch 1).
1495
+ * Spec: connectivity-diagnostics "Connection failure yields a typed non-blocking outcome
1496
+ * presenting at least three options".
1497
+ * Design: PURE; ConnectivityOutcome; engine + content-free summary, each attempt
1498
+ * (id — reason), ALL >= 3 options rendered deterministically.
1499
+ *
1500
+ * ADR-004: imports ONLY core types — NO adapters, NO cli, NO mcp, NO drivers.
1501
+ * ADR-008: deterministic output — same (ConnectivityOutcome) → byte-identical string.
1502
+ *
1503
+ * Mirrors the SHAPE of present/status.ts: lines[], push, join('\n') + '\n'.
1504
+ *
1505
+ * Rendering contract per option kind:
1506
+ * run-it-yourself — prints each query verbatim (paste-able, write-verb-free)
1507
+ * consented-install — prints tool + docUrl + explicit CONSENT notice (no auto-install)
1508
+ * manual-dump — prints outputPath
1509
+ */
1510
+
1511
+ /**
1512
+ * Formats a ConnectivityOutcome into a human-readable string.
1513
+ *
1514
+ * Output contract (ADR-008):
1515
+ * - Same (outcome) → always the SAME bytes
1516
+ * - Trailing newline guaranteed
1517
+ * - No Date.now(), no process.env, no I/O
1518
+ */
1519
+ declare function formatOutcome(outcome: ConnectivityOutcome): string;
1520
+
1521
+ /**
1522
+ * formatDoctor — task 5.1 (resilient-connectivity Batch 5).
1523
+ * Spec (US-043): connectivity-diagnostics "dbgraph doctor reports diagnostics content-free".
1524
+ * Design: PURE; DoctorView; engine, native-driver bool, CLI tools + versions,
1525
+ * ODBC bool, resolved profile NAME, chosen-strategy id — SHAPE ONLY.
1526
+ * ZERO schema / identifier / secret.
1527
+ *
1528
+ * ADR-004: imports ONLY core types — NO adapters, NO cli, NO mcp, NO drivers.
1529
+ * ADR-008: deterministic output — same (DoctorView) → byte-identical string.
1530
+ *
1531
+ * Mirrors the SHAPE of present/status.ts: lines[], push, join('\n') + '\n'.
1532
+ *
1533
+ * Content-free contract:
1534
+ * The renderer surfaces ONLY the capability-shape fields from DoctorView.
1535
+ * No schema names, object identifiers, connection-string values, or secrets
1536
+ * are ever surfaced — the output is safe to paste into a bug report.
1537
+ */
1538
+
1539
+ /**
1540
+ * Input bundle for formatDoctor — a content-free capability snapshot.
1541
+ *
1542
+ * Fields contain SHAPE data only (booleans, version strings, profile names,
1543
+ * strategy ids) — never schema names, object identifiers, or secrets.
1544
+ */
1545
+ interface DoctorView {
1546
+ /** Engine identifier (e.g. 'mssql', 'pg', 'mysql', 'sqlite'). */
1547
+ readonly engine: string;
1548
+ /**
1549
+ * Whether the native driver package is importable in the current environment.
1550
+ * True = driver present; false = absent.
1551
+ */
1552
+ readonly nativeDriver: boolean;
1553
+ /**
1554
+ * Detected CLI tools (e.g. sqlcmd, psql, mysql).
1555
+ * Each entry carries the tool name, detected version (null if absent), and
1556
+ * resolved PATH (null if absent).
1557
+ */
1558
+ readonly cliTools: readonly CliToolInfo[];
1559
+ /**
1560
+ * Whether an ODBC driver for this engine is registered in the OS.
1561
+ * Always false for pg/mysql/sqlite (N/A).
1562
+ */
1563
+ readonly odbc: boolean;
1564
+ /**
1565
+ * The resolved profile name from the strategy registry.
1566
+ * 'n/a' when no profile registry exists for this engine.
1567
+ * 'unknown@any' when no matching profile was found (conservative default).
1568
+ */
1569
+ readonly resolvedProfile: string;
1570
+ /**
1571
+ * The chosen (or recommended) strategy id.
1572
+ * 'unavailable' when no strategy can be selected.
1573
+ */
1574
+ readonly chosenStrategy: string;
1575
+ }
1576
+ /**
1577
+ * Formats a DoctorView into a human-readable, content-free diagnostic string.
1578
+ *
1579
+ * Output contract (ADR-008):
1580
+ * - Same (view) → always the SAME bytes
1581
+ * - Trailing newline guaranteed
1582
+ * - No Date.now(), no process.env, no I/O
1583
+ * - CONTENT-FREE: only capability booleans, version strings, profile names,
1584
+ * and strategy ids are emitted — no schema/identifier/secret
1585
+ */
1586
+ declare function formatDoctor(view: DoctorView): string;
1587
+
1588
+ /**
1589
+ * formatPrecheck — task 1.8 (phase-5-mcp-server).
1590
+ * Spec: dbgraph_precheck aggregates DDL impact (format half).
1591
+ * Design: PURE; PrecheckView; matched objects + aggregated impact sections
1592
+ * (triggers/writers/readers/constraints+indexes/what-to-test), confidence:'parsed' tags,
1593
+ * unmatched-identifier section.
1594
+ *
1595
+ * ADR-004: imports ONLY core model/port types — NO adapters, NO cli, NO mcp, NO drivers.
1596
+ * ADR-008: deterministic output — same (PrecheckView, PrecheckDetail) → byte-identical string.
1597
+ *
1598
+ * --detail levels:
1599
+ * brief — matched objects list only
1600
+ * normal — brief + aggregated impact sections
1601
+ * full — normal + confidence tags, unmatched identifiers
1602
+ */
1603
+ /** The detail level for precheck output. */
1604
+ type PrecheckDetail = 'brief' | 'normal' | 'full';
1605
+ /** An item in the precheck result — carries confidence and kind. */
1606
+ interface PrecheckItem {
1607
+ readonly qname: string;
1608
+ readonly kind: string;
1609
+ readonly confidence: 'parsed';
1610
+ }
1611
+ /**
1612
+ * Aggregated impact section for precheck output.
1613
+ */
1614
+ interface PrecheckImpactSection {
1615
+ readonly triggers: readonly PrecheckItem[];
1616
+ readonly writers: readonly PrecheckItem[];
1617
+ readonly readers: readonly PrecheckItem[];
1618
+ readonly constraintsAndIndexes: readonly PrecheckItem[];
1619
+ readonly whatToTest: readonly string[];
1620
+ }
1621
+ /**
1622
+ * Input bundle for formatPrecheck.
1623
+ * Assembled by the caller from DDL extraction + graph lookup + getImpact aggregation.
1624
+ */
1625
+ interface PrecheckView {
1626
+ readonly matchedObjects: readonly PrecheckItem[];
1627
+ readonly impact: PrecheckImpactSection;
1628
+ readonly unmatchedIdentifiers: readonly string[];
1629
+ }
1630
+ /**
1631
+ * Formats a PrecheckView into a human-readable string at the requested detail level.
1632
+ *
1633
+ * Output contract (ADR-008):
1634
+ * - Same (view, detail) → always the SAME bytes
1635
+ * - Trailing newline guaranteed
1636
+ * - No Date.now(), no process.env, no I/O
1637
+ */
1638
+ declare function formatPrecheck(view: PrecheckView, detail: PrecheckDetail): string;
1639
+
1640
+ /**
1641
+ * extractIdentifiers — task 4.3 / Batch D (phase-5-mcp-server).
1642
+ * Spec: dbgraph_precheck extractor unit — conservative regex tokenizer.
1643
+ * Design: PURE; reuses MSSQL tokenizer.ts [\w.]+ + bracket-strip patterns for
1644
+ * ALTER TABLE / CREATE|DROP INDEX / ADD|DROP COLUMN; case-insensitive, deduped.
1645
+ *
1646
+ * ADR-004: imports NOTHING from adapters, cli, mcp, or drivers. PURE function.
1647
+ * ADR-007: no node-sql-parser — conservative regex only.
1648
+ * ADR-008: deterministic — same ddl → same readonly string[].
1649
+ *
1650
+ * Placement: src/core/precheck/ — neutral module shared by BOTH MCP tool
1651
+ * (src/mcp/tools/precheck.ts) and CLI command (src/cli/commands/affected.ts)
1652
+ * via the barrel. Neither cli nor mcp imports the other (ADR-004 boundary).
1653
+ */
1654
+ /**
1655
+ * Extracts qualified identifiers from DDL statements using conservative
1656
+ * regex patterns (ALTER TABLE, CREATE|DROP INDEX, ADD|DROP COLUMN).
1657
+ *
1658
+ * Returns a deduped, stable sorted readonly array of lowercased identifiers.
1659
+ * Bracket delimiters are stripped. Case is normalized to lowercase.
1660
+ *
1661
+ * Only recognized statement patterns yield identifiers — unrecognized DDL
1662
+ * produces no identifiers (SELECT, INSERT, etc. are silently ignored).
1663
+ *
1664
+ * @param ddl Raw DDL string (one or more statements, separated by semicolons).
1665
+ * @returns Readonly, deduped, sorted array of extracted identifiers.
1666
+ */
1667
+ declare function extractIdentifiers(ddl: string): readonly string[];
1668
+
1669
+ /**
1670
+ * runPrecheck — precheck engine, task 4.3 / Batch D (phase-5-mcp-server).
1671
+ * Spec: match extracted identifiers to graph → aggregate getImpact → PrecheckView.
1672
+ * Design: PURE core (store, ddl) → PrecheckView; tags confidence:'parsed';
1673
+ * reports unmatched identifiers; deduplicates impact across multiple statements.
1674
+ *
1675
+ * ADR-004: imports ONLY core query fns + core ports. NO adapters, NO cli, NO mcp.
1676
+ * ADR-007: no node-sql-parser. ADR-008: deterministic output.
1677
+ *
1678
+ * Placement: src/core/precheck/ — neutral module consumed by BOTH
1679
+ * src/mcp/tools/precheck.ts AND src/cli/commands/affected.ts via the barrel.
1680
+ * Neither cli nor mcp imports the other (ADR-004 boundary preserved).
1681
+ */
1682
+
1683
+ /**
1684
+ * Extracts identifiers from `ddl`, matches them against the graph,
1685
+ * aggregates `getImpact` across all matched objects, and returns a
1686
+ * `PrecheckView` ready for `formatPrecheck`.
1687
+ *
1688
+ * Every matched item and impact item carries `confidence: 'parsed'`.
1689
+ * Identifiers that match no graph node appear in `unmatchedIdentifiers`.
1690
+ * Impact sections are deduplicated across all statements.
1691
+ *
1692
+ * @param store The open GraphStore (read-only; no writes issued).
1693
+ * @param ddl Raw DDL string (one or more statements).
1694
+ * @returns PrecheckView with matchedObjects, impact, unmatchedIdentifiers.
1695
+ */
1696
+ declare function runPrecheck(store: GraphStore, ddl: string): Promise<PrecheckView>;
1697
+
1698
+ /**
1699
+ * Factory for the SQLite GraphStore adapter.
1700
+ * Design §2 (factory join point) + §10 (sequence diagram).
1701
+ *
1702
+ * createSqliteGraphStore(opts) is the ONLY place where:
1703
+ * 1. The driver is selected and opened (ADR-004: driver never pulled into core).
1704
+ * 2. The raw DB is opened + migrations are run.
1705
+ * 3. A GraphStore-conforming adapter is returned.
1706
+ *
1707
+ * Phase 9.5b: the standalone `await import('better-sqlite3')` is REMOVED — the
1708
+ * dynamic import now lives inside `openRawDb`'s better-sqlite3 path in schema.ts.
1709
+ * factory.ts calls `openRawDb(path)` (now async, returns `WritableSqliteHandle`),
1710
+ * passes the handle to `runMigrations` and `new SqliteGraphStore`. Default driver
1711
+ * stays better-sqlite3; no `driver` option yet (added in Batch 2).
1712
+ *
1713
+ * Note: this adapter is under src/adapters/storage/sqlite/ — it is EXEMPT from
1714
+ * the write-verb security scan that targets src/adapters/engines/* (source-extraction
1715
+ * paths). The local index MUST write (CREATE TABLE, INSERT/UPSERT, DELETE) to its
1716
+ * own .dbgraph database file by design (ADR-005). This exemption is documented here
1717
+ * so Batch D boundary/security tests can scope correctly.
1718
+ *
1719
+ * Imports allowed: core types/ports only + storage-layer modules.
1720
+ */
1721
+
1722
+ interface SqliteGraphStoreOptions {
1723
+ /** Filesystem path to the SQLite database file, or ':memory:' for in-memory. */
1724
+ readonly path: string;
1725
+ /**
1726
+ * SQLite driver to use. Default: `'better-sqlite3'` (byte-identical default path).
1727
+ * Use `'node:sqlite'` to opt-in to the built-in Node.js SQLite driver (Node >= 22.5).
1728
+ *
1729
+ * Explicit selection only — NO silent fallback. Requesting `'node:sqlite'` on
1730
+ * Node < 22.5 throws an explicit error. Existing callers that pass no `driver`
1731
+ * continue using `'better-sqlite3'` unchanged (ADR-008, byte-identical guarantee).
1732
+ */
1733
+ readonly driver?: 'better-sqlite3' | 'node:sqlite';
1734
+ }
1735
+ /**
1736
+ * Opens the SQLite graph store at `opts.path`, runs forward migrations if needed,
1737
+ * and returns a GraphStore-conforming adapter.
1738
+ *
1739
+ * @param opts.path - ':memory:' or an absolute path to the .dbgraph/dbgraph.db file.
1740
+ * @throws SchemaVersionError if the on-disk schema is newer than supported.
1741
+ * @throws StorageError if the database cannot be opened.
1742
+ */
1743
+ declare function createSqliteGraphStore(opts: SqliteGraphStoreOptions): Promise<GraphStore>;
1744
+
1745
+ /**
1746
+ * createSqliteSchemaAdapter — factory for the SQLite schema extraction adapter.
1747
+ * Design §5.1 — the ONLY join point between core and the SQLite driver.
1748
+ *
1749
+ * Responsibilities:
1750
+ * 1. Explicit driver selection (default: better-sqlite3; NO silent auto-fallback).
1751
+ * 2. Dynamic import of the chosen driver (ADR-004: driver never pulled into core).
1752
+ * 3. Open read-only with fileMustExist flags.
1753
+ * 4. Map driver-level open errors to typed ConnectionError / PermissionError.
1754
+ * 5. Wrap the raw handle in ReadonlyDriver and return a SqliteSchemaAdapter.
1755
+ *
1756
+ * node:sqlite requires Node >= 22.5; requesting it on an older runtime throws
1757
+ * ConnectionError with an actionable message (design §2 — no silent downgrade).
1758
+ *
1759
+ * US-026 (first concrete adapter), US-031 (read-only by construction).
1760
+ */
1761
+
1762
+ /**
1763
+ * Opens a SQLite source database in read-only mode and returns a SchemaAdapter.
1764
+ * The adapter is already-open — no `open()` call is needed (mirrors createSqliteGraphStore).
1765
+ *
1766
+ * @param config.file - Absolute path to the source .db file.
1767
+ * @param config.driver - 'better-sqlite3' (default) | 'node:sqlite'. Explicit selection,
1768
+ * no silent fallback.
1769
+ * @throws ConnectionError if the file is missing, not a valid SQLite db, db is locked,
1770
+ * or the required driver package is not installed.
1771
+ * @throws ConnectionError if node:sqlite is requested on Node < 22.5.
1772
+ */
1773
+ declare function createSqliteSchemaAdapter(config: SqliteAdapterConfig): Promise<SchemaAdapter>;
1774
+
1775
+ /**
1776
+ * createMssqlSchemaAdapter — strategy-backed factory for the SQL Server schema adapter.
1777
+ * Design §"Registry lives in the factory; selection iterates in priority order".
1778
+ *
1779
+ * Responsibilities:
1780
+ * 1. Build ordered strategy list from config via buildMssqlStrategies.
1781
+ * 2. Probe strategies in order via selectStrategy (detect + canConnect).
1782
+ * 3. Wrap the winning strategy in StrategyBackedSchemaAdapter.
1783
+ * 4. Return the adapter — already-connected, no open() call needed.
1784
+ *
1785
+ * Back-compat:
1786
+ * - sql/ntlm configs → NativeTediousStrategy wins (same behavior as before).
1787
+ * - integrated config → NativeTediousStrategy is omitted; SqlcmdStrategy is tried.
1788
+ * - Missing 'mssql' driver for explicit-cred configs → ConnectionError('npm i mssql').
1789
+ *
1790
+ * US-027 (SQL Server adapter), ADR-004 (seam), ADR-006 (lazy optional import).
1791
+ * connectivity-strategies Batch C, task C3.3.
1792
+ */
1793
+
1794
+ /**
1795
+ * Optional dependencies for createMssqlSchemaAdapter.
1796
+ * All fields are optional — omit entirely for production use.
1797
+ */
1798
+ interface MssqlSchemaAdapterDeps {
1799
+ /** Logger port instance for strategy-selection transparency. Defaults to noopLogger. */
1800
+ readonly logger?: Logger;
1801
+ /** Override NativeTediousStrategy constructor — for testing only. */
1802
+ readonly NativeTedious?: new (config: MssqlAdapterConfig) => ConnectivityStrategy;
1803
+ /** Override SqlcmdStrategy constructor — for testing only. */
1804
+ readonly Sqlcmd?: new (config: MssqlAdapterConfig) => ConnectivityStrategy;
1805
+ /** Override ManualDumpStrategy constructor — for testing only. */
1806
+ readonly ManualDump?: new (config: MssqlAdapterConfig) => ConnectivityStrategy;
1807
+ /** Override ConsentedInstallStrategy constructor — for testing only. */
1808
+ readonly ConsentedInstall?: new (config: MssqlAdapterConfig, logger: Logger, os?: string) => ConnectivityStrategy;
1809
+ }
1810
+ /**
1811
+ * Opens a SQL Server connection (or prepares an external-tool strategy) and
1812
+ * returns a SchemaAdapter. The adapter is already-connected — no open() call needed.
1813
+ *
1814
+ * Strategy selection order (see registry.ts):
1815
+ * 1. NativeTediousStrategy — explicit-credential (sql/ntlm) configs only
1816
+ * 2. SqlcmdStrategy — integrated auth or fallback if native fails
1817
+ *
1818
+ * @param config - MssqlAdapterConfig: server/database/authentication/TLS options.
1819
+ * @param deps - Optional deps for selection transparency and testability.
1820
+ *
1821
+ * @throws ConnectionError if mssql is not installed for an explicit-cred config
1822
+ * (native strategy propagates the 'npm i mssql' message — back-compat).
1823
+ * @throws ConnectionError if native pool cannot connect (credentials, network, TLS).
1824
+ * @throws StrategyExhaustionError if all strategies fail to detect + connect.
1825
+ */
1826
+ declare function createMssqlSchemaAdapter(config: MssqlAdapterConfig, deps?: MssqlSchemaAdapterDeps): Promise<SchemaAdapter>;
1827
+
1828
+ /**
1829
+ * Minimal duck-typed interface for a pg.Client.
1830
+ * Typed locally so we do not need to import pg here — the client instance
1831
+ * is created and connected by the factory and passed in.
1832
+ *
1833
+ * pg.Client.query(sql, params?) returns { rows: Record<string, unknown>[] }
1834
+ * pg.Client.end releases the connection socket.
1835
+ */
1836
+ interface ClientLike {
1837
+ query(sql: string, params?: unknown[]): Promise<{
1838
+ rows: Record<string, unknown>[];
1839
+ }>;
1840
+ end(): Promise<void>;
1841
+ }
1842
+
1843
+ /**
1844
+ * createPgSchemaAdapter — factory for the PostgreSQL schema extraction adapter.
1845
+ * Design §"PG mirrors the SQLite adapter SHAPE — collapse lazy-import + connect
1846
+ * + fingerprint logic directly into factory.ts".
1847
+ *
1848
+ * Responsibilities:
1849
+ * 1. Lazy dynamic import('pg' as string) — ADR-006: NO top-level pg import.
1850
+ * 2. Build the pg connection config (host, port default 5432, database, user,
1851
+ * password already resolved from ${env:VAR} by the caller, optional ssl).
1852
+ * 3. new Client(connConfig) + client.connect() — one short-lived Client per run.
1853
+ * 4. Map connect failures via mapPgError to typed errors (ConnectionError /
1854
+ * PermissionError).
1855
+ * 5. Wrap the connected client in createPgReadonlyDriver → PgSchemaAdapter.
1856
+ * 6. Missing 'pg' package → ConnectionError('... npm i pg').
1857
+ *
1858
+ * NO strategy registry (that is SQL-Server-only machinery).
1859
+ * createPgSchemaAdapter is the ONLY join point (ADR-004).
1860
+ *
1861
+ * US-028 (PostgreSQL adapter), ADR-004 (seam), ADR-006 (lazy optional import),
1862
+ * pg-extraction spec "Absent pg driver names npm i pg".
1863
+ */
1864
+
1865
+ /**
1866
+ * Optional dependencies for createPgSchemaAdapter.
1867
+ * All fields are optional — omit entirely for production use.
1868
+ *
1869
+ * - `Client` — inject a fake pg.Client constructor in unit tests so no real
1870
+ * pg install is required. Production code uses the lazily-imported
1871
+ * pg.Client.
1872
+ * - `importPg` — inject a fake dynamic import function to test the missing-driver
1873
+ * error path without actually uninstalling pg.
1874
+ */
1875
+ interface PgSchemaAdapterDeps {
1876
+ /**
1877
+ * pg.Client constructor override — for unit testing only.
1878
+ * When provided, the factory skips the lazy import and uses this constructor.
1879
+ */
1880
+ readonly Client?: new (config: Record<string, unknown>) => ClientLike & {
1881
+ connect(): Promise<void>;
1882
+ };
1883
+ /**
1884
+ * Dynamic import override — for testing the MODULE_NOT_FOUND path only.
1885
+ * When provided, the factory calls this instead of `import('pg' as string)`.
1886
+ */
1887
+ readonly importPg?: () => unknown;
1888
+ }
1889
+ /**
1890
+ * Opens a PostgreSQL connection and returns a SchemaAdapter.
1891
+ * The adapter is already-connected — no open() call needed.
1892
+ *
1893
+ * @param config - PgAdapterConfig: host/port/database/user/password (resolved) + optional ssl/schema.
1894
+ * @param deps - Optional deps for testing (Client constructor / importPg override).
1895
+ *
1896
+ * @throws ConnectionError if pg is not installed (`npm i pg` in message — ADR-006).
1897
+ * @throws ConnectionError if client.connect() fails (network / db missing / auth).
1898
+ * @throws PermissionError if client.connect() fails with insufficient privilege.
1899
+ */
1900
+ declare function createPgSchemaAdapter(config: PgAdapterConfig, deps?: PgSchemaAdapterDeps): Promise<SchemaAdapter>;
1901
+
1902
+ /**
1903
+ * MysqlReadonlyDriver — async engine-local seam over mysql2/promise connection.
1904
+ * Design §driver.ts — "single duck-typed MysqlReadonlyDriver seam".
1905
+ *
1906
+ * The adapter and map.ts talk ONLY to this interface — never to mysql2 directly.
1907
+ * Mirrors pg/driver.ts but adapts to mysql2/promise createConnection (NOT a pool):
1908
+ * one short-lived connection per extraction run, then conn.end().
1909
+ *
1910
+ * mysql2/promise result shape: [rows, fields] (tuple)
1911
+ * vs PG shape: { rows: Record<string, unknown>[] }
1912
+ *
1913
+ * The seam normalizes [rows, fields] → rows so the adapter and map.ts consume
1914
+ * a uniform Record<string, unknown>[] shape — identical to the PG driver surface.
1915
+ * (Decision D2: the seam absorbs the dialect shape difference.)
1916
+ *
1917
+ * query() accepts an optional params array for parameterized queries (? params).
1918
+ * All catalog queries in mysql use DATABASE() (a function, not a bind param), so
1919
+ * params will generally be undefined — but the signature matches the pg seam.
1920
+ *
1921
+ * NO top-level mysql2 import anywhere (ADR-006). The lazy import lives in factory.ts.
1922
+ * The adapter talks ONLY to MysqlReadonlyDriver (ADR-004).
1923
+ *
1924
+ * US-029 (MySQL adapter, Phase 8b), ADR-004 (seam keeps mysql2 out of core),
1925
+ * ADR-006 (lazy optional import).
1926
+ */
1927
+ /**
1928
+ * Minimal duck-typed interface for a mysql2/promise connection.
1929
+ * Typed locally so we do NOT import mysql2 in this file at module level.
1930
+ * The connection instance is created and connected by the factory and passed in.
1931
+ *
1932
+ * mysql2/promise connection.query(sql, params?) returns [rows, fields].
1933
+ * connection.end() releases the connection socket.
1934
+ *
1935
+ * Decision D3: we use query() (text protocol, zero params for DATABASE() scoped
1936
+ * queries) rather than execute() (prepared/binary), matching the conservative
1937
+ * read-only posture and avoiding prepared-statement protocol restrictions.
1938
+ */
1939
+ interface ConnectionLike {
1940
+ query(sql: string, params?: unknown[]): Promise<[Record<string, unknown>[], unknown]>;
1941
+ end(): Promise<void>;
1942
+ }
1943
+
1944
+ /**
1945
+ * createMysqlSchemaAdapter — factory for the MySQL schema extraction adapter.
1946
+ * Design §"collapse lazy-import + connect + error mapping directly into factory.ts"
1947
+ * (pg/SQLite shape — no strategy registry; MySQL has flat host/port/user/password + ssl).
1948
+ *
1949
+ * Responsibilities:
1950
+ * 1. Lazy dynamic import('mysql2/promise' as string) — ADR-006: NO top-level mysql2 import.
1951
+ * 2. Build the mysql2 connection config (host, port default 3306, database, user,
1952
+ * password already resolved from ${env:VAR} by the caller, optional ssl).
1953
+ * 3. createConnection(connConfig) + connection.connect() — one short-lived connection per run.
1954
+ * 4. Map connect failures via mapMysqlError to typed errors (ConnectionError / PermissionError).
1955
+ * 5. Wrap the connected connection in createMysqlReadonlyDriver → MysqlSchemaAdapter.
1956
+ * 6. Missing 'mysql2' package → ConnectionError('... npm i mysql2').
1957
+ *
1958
+ * NO strategy registry (that is SQL-Server-only machinery).
1959
+ * createMysqlSchemaAdapter is the ONLY join point (ADR-004, ADR-006).
1960
+ *
1961
+ * US-029 (MySQL adapter, Phase 8b), ADR-004 (seam), ADR-006 (lazy optional import),
1962
+ * mysql-extraction spec "Absent mysql2 driver names npm i mysql2".
1963
+ */
1964
+
1965
+ /**
1966
+ * Optional dependencies for createMysqlSchemaAdapter.
1967
+ * All fields are optional — omit entirely for production use.
1968
+ *
1969
+ * - `createConnection` — inject a fake mysql2 createConnection function in unit
1970
+ * tests so no real mysql2 install is required. Production code
1971
+ * uses the lazily-imported mysql2/promise.createConnection.
1972
+ * - `importMysql` — inject a fake dynamic import function to test the missing-driver
1973
+ * error path without actually uninstalling mysql2.
1974
+ */
1975
+ interface MysqlSchemaAdapterDeps {
1976
+ /**
1977
+ * mysql2/promise createConnection override — for unit testing only.
1978
+ * When provided, the factory skips the lazy import and uses this function.
1979
+ *
1980
+ * Matches the real mysql2/promise.createConnection() signature:
1981
+ * createConnection(config) => Promise<Connection>
1982
+ * The Promise resolves with an already-connected ConnectionLike (no .connect() call needed).
1983
+ */
1984
+ readonly createConnection?: (config: Record<string, unknown>) => Promise<ConnectionLike>;
1985
+ /**
1986
+ * Dynamic import override — for testing the MODULE_NOT_FOUND path only.
1987
+ * When provided, the factory calls this instead of import('mysql2/promise' as string).
1988
+ */
1989
+ readonly importMysql?: () => Promise<unknown>;
1990
+ }
1991
+ /**
1992
+ * Opens a MySQL connection and returns a SchemaAdapter.
1993
+ * The adapter is already-connected — no open() call needed.
1994
+ *
1995
+ * @param config - MysqlAdapterConfig: host/port/database/user/password (resolved) + optional ssl.
1996
+ * @param deps - Optional deps for testing (createConnection override / importMysql override).
1997
+ *
1998
+ * @throws ConnectionError if mysql2 is not installed (`npm i mysql2` in message — ADR-006).
1999
+ * @throws ConnectionError if connection.connect() fails (network / db missing / auth).
2000
+ * @throws PermissionError if connection.connect() fails with insufficient privilege.
2001
+ */
2002
+ declare function createMysqlSchemaAdapter(config: MysqlAdapterConfig, deps?: MysqlSchemaAdapterDeps): Promise<SchemaAdapter>;
2003
+
2004
+ /**
2005
+ * Minimal cursor-like shape returned by mongodb collection operations.
2006
+ * Typed locally so we do NOT need to import mongodb in this file.
2007
+ */
2008
+ interface CursorLike<T = Record<string, unknown>> {
2009
+ toArray(): Promise<T[]>;
2010
+ }
2011
+ /**
2012
+ * Minimal duck-typed interface for a mongodb collection.
2013
+ * Typed locally — no top-level mongodb import.
2014
+ */
2015
+ interface CollectionLike {
2016
+ aggregate(pipeline: Record<string, unknown>[]): CursorLike;
2017
+ listIndexes(): CursorLike;
2018
+ }
2019
+ /**
2020
+ * Minimal duck-typed interface for a mongodb Db instance.
2021
+ * Typed locally — no top-level mongodb import.
2022
+ *
2023
+ * listCollections().toArray() returns collection info objects.
2024
+ * command(cmd) runs an administrative command (dbStats, etc.).
2025
+ * collection(name) accesses a named collection.
2026
+ */
2027
+ interface DbLike {
2028
+ listCollections(filter?: Record<string, unknown>, options?: Record<string, unknown>): CursorLike<{
2029
+ name: string;
2030
+ options?: Record<string, unknown>;
2031
+ }>;
2032
+ command(cmd: Record<string, unknown>): Promise<Record<string, unknown>>;
2033
+ collection(name: string): CollectionLike;
2034
+ }
2035
+ /**
2036
+ * Minimal duck-typed interface for a mongodb MongoClient.
2037
+ * Typed locally — no top-level mongodb import (ADR-006).
2038
+ *
2039
+ * connect() must be called before using the client.
2040
+ * db(name) returns a Db instance for the named database.
2041
+ * close() releases the connection.
2042
+ */
2043
+ interface MongoClientLike {
2044
+ connect(): Promise<void>;
2045
+ db(name: string): DbLike;
2046
+ close(): Promise<void>;
2047
+ }
2048
+
2049
+ /**
2050
+ * createMongodbSchemaAdapter — factory for the MongoDB schema extraction adapter.
2051
+ * Design §"collapse lazy-import + connect + error mapping directly into factory.ts"
2052
+ * (pg/mysql shape — no strategy registry; MongoDB uses URI-based config).
2053
+ *
2054
+ * Responsibilities:
2055
+ * 1. Lazy dynamic import('mongodb' as string) — ADR-006: NO top-level mongodb import.
2056
+ * 2. Instantiate MongoClient with the resolved URI + optional tls flag.
2057
+ * 3. client.connect() — one short-lived MongoClient per extraction run.
2058
+ * 4. Map connect failures via mapMongoError to typed errors
2059
+ * (ConnectionError / PermissionError / ConnectivityUnavailableError).
2060
+ * 5. Wrap the connected client in createMongodbReadonlyDriver → MongodbSchemaAdapter.
2061
+ * 6. Missing 'mongodb' package → ConnectivityUnavailableError with "npm i mongodb" in summary.
2062
+ *
2063
+ * NO strategy registry (that is SQL-Server-only machinery).
2064
+ * createMongodbSchemaAdapter is the ONLY join point (ADR-004, ADR-006).
2065
+ *
2066
+ * US-030 (MongoDB adapter), ADR-004 (seam), ADR-006 (lazy optional import),
2067
+ * mongodb-extraction spec "Absent mongodb driver names npm i mongodb".
2068
+ */
2069
+
2070
+ /**
2071
+ * Optional dependencies for createMongodbSchemaAdapter.
2072
+ * All fields are optional — omit entirely for production use.
2073
+ *
2074
+ * - `MongoClient` — inject a fake MongoClient constructor in unit tests so no real
2075
+ * mongodb install is required. Production code uses the lazily-imported
2076
+ * mongodb MongoClient.
2077
+ * - `importMongodb` — inject a fake dynamic import function to test the missing-driver
2078
+ * error path without actually uninstalling mongodb.
2079
+ */
2080
+ interface MongodbSchemaAdapterDeps {
2081
+ /**
2082
+ * MongoClient constructor override — for unit testing only.
2083
+ * When provided, the factory skips the lazy import and uses this constructor.
2084
+ */
2085
+ readonly MongoClient?: new (uri: string, options?: Record<string, unknown>) => MongoClientLike;
2086
+ /**
2087
+ * Dynamic import override — for testing the MODULE_NOT_FOUND path only.
2088
+ * When provided, the factory calls this instead of import('mongodb' as string).
2089
+ */
2090
+ readonly importMongodb?: () => Promise<unknown>;
2091
+ }
2092
+ /**
2093
+ * Opens a MongoDB connection and returns a SchemaAdapter.
2094
+ * The adapter is already-connected — no open() call needed.
2095
+ *
2096
+ * @param config - MongodbAdapterConfig: uri/database (resolved) + optional sampleSize/tls.
2097
+ * @param deps - Optional deps for testing (MongoClient constructor / importMongodb override).
2098
+ *
2099
+ * @throws ConnectivityUnavailableError if mongodb is not installed (summary has "npm i mongodb").
2100
+ * @throws ConnectivityUnavailableError if client.connect() fails (network / auth / role).
2101
+ */
2102
+ declare function createMongodbSchemaAdapter(config: MongodbAdapterConfig, deps?: MongodbSchemaAdapterDeps): Promise<SchemaAdapter>;
2103
+
2104
+ /**
2105
+ * MssqlCapabilityProbe — CapabilityProbe implementation for SQL Server.
2106
+ * Design §"probe() is OPTIONAL on the strategy port; per-engine probe in adapters".
2107
+ * Resilient-connectivity Batch 2, task 2.1.
2108
+ *
2109
+ * Detection strategy:
2110
+ * - nativeDriver: dynamic import('tedious') resolves → present (NO .connect())
2111
+ * - cliTools: where/which sqlcmd on PATH (cross-platform); version via sqlcmd -?
2112
+ * - odbc: Windows registry probe (mirrors OdbcDriverStrategy.detect())
2113
+ *
2114
+ * All detection failures, timeouts, and absent prerequisites yield a NEGATIVE
2115
+ * result — probe() MUST NEVER reject.
2116
+ *
2117
+ * Seams (all optional — omit for production use):
2118
+ * - spawnSync: injectable child_process.spawnSync (default: real spawnSync)
2119
+ * - importTedious: injectable import() for 'tedious' (default: real dynamic import)
2120
+ * - platform: injectable process.platform override (for cross-platform testing)
2121
+ *
2122
+ * ADR-004: this file MAY import node:child_process (adapter territory).
2123
+ * The core port (capability-probe.ts) imports nothing from adapters.
2124
+ */
2125
+
2126
+ /**
2127
+ * Injectable seam for node:child_process.spawnSync (Buffer overload).
2128
+ * Mirrors the existing SqlcmdStrategy / OdbcDriverStrategy seam pattern.
2129
+ */
2130
+ type SpawnSyncFn$3 = (command: string, args: readonly string[], options: SpawnSyncOptions) => SpawnSyncReturns<Buffer>;
2131
+ /**
2132
+ * Injectable seam for dynamic import('tedious').
2133
+ * Resolves with the module (any shape) when present; rejects on MODULE_NOT_FOUND.
2134
+ * MUST NOT call .connect() or instantiate any class.
2135
+ */
2136
+ type ImportTediousFn = () => Promise<unknown>;
2137
+ /**
2138
+ * Constructor options for MssqlCapabilityProbe.
2139
+ * All fields are optional — omit entirely for production use.
2140
+ */
2141
+ interface MssqlCapabilityProbeOptions {
2142
+ /** Injectable spawnSync seam. Defaults to node:child_process.spawnSync. */
2143
+ readonly spawnSync?: SpawnSyncFn$3;
2144
+ /** Injectable tedious import seam. Defaults to dynamic import('tedious'). */
2145
+ readonly importTedious?: ImportTediousFn;
2146
+ /** Injectable platform override for cross-platform PATH detection testing. */
2147
+ readonly platform?: NodeJS.Platform;
2148
+ }
2149
+ /**
2150
+ * Capability probe for SQL Server.
2151
+ * Implements CapabilityProbe — engine = 'mssql'.
2152
+ *
2153
+ * probe() MUST resolve, never reject.
2154
+ * probe() MUST NOT open a DB connection.
2155
+ * probe() MUST NOT issue any write.
2156
+ */
2157
+ declare class MssqlCapabilityProbe implements CapabilityProbe {
2158
+ readonly engine = "mssql";
2159
+ private readonly _spawnSync;
2160
+ private readonly _importTedious;
2161
+ private readonly _platform;
2162
+ constructor(opts?: MssqlCapabilityProbeOptions);
2163
+ /**
2164
+ * Run all three detections (driver, CLI, ODBC) and return the composite result.
2165
+ * Any detection failure is reported as unavailable — never throws.
2166
+ */
2167
+ probe(): Promise<ProbeResult>;
2168
+ /**
2169
+ * Checks whether the 'tedious' npm package is importable WITHOUT connecting.
2170
+ * Resolves true on success, false on any error (MODULE_NOT_FOUND, throws, etc.).
2171
+ */
2172
+ private _detectNativeDriver;
2173
+ /**
2174
+ * Scans for sqlcmd on PATH using where (Windows) / which (POSIX).
2175
+ * If found, parses the version from sqlcmd -?.
2176
+ * Always resolves with one CliToolInfo entry for 'sqlcmd'.
2177
+ */
2178
+ private _detectCliTools;
2179
+ private _detectSqlcmd;
2180
+ /**
2181
+ * Runs sqlcmd -? to extract the version string.
2182
+ * Returns null on any failure (timeout, non-zero exit, parse failure).
2183
+ */
2184
+ private _parseSqlcmdVersion;
2185
+ /**
2186
+ * Probes the Windows registry for an ODBC Driver for SQL Server installation.
2187
+ * Mirrors OdbcDriverStrategy.detect() — returns true/false, never throws.
2188
+ */
2189
+ private _detectOdbc;
2190
+ }
2191
+
2192
+ /**
2193
+ * PgCapabilityProbe — CapabilityProbe implementation for PostgreSQL.
2194
+ * Design §"probe() is OPTIONAL on the strategy port; per-engine probe in adapters".
2195
+ * Resilient-connectivity Batch 2, task 2.2.
2196
+ *
2197
+ * Detection strategy:
2198
+ * - nativeDriver: dynamic import('pg') resolves → present (NO Client.connect())
2199
+ * - cliTools: where/which psql on PATH (cross-platform); version via psql --version
2200
+ * - odbc: always false (N/A for PostgreSQL)
2201
+ *
2202
+ * All detection failures, timeouts, and absent prerequisites yield a NEGATIVE
2203
+ * result — probe() MUST NEVER reject.
2204
+ *
2205
+ * Seams (all optional — omit for production use):
2206
+ * - spawnSync: injectable child_process.spawnSync
2207
+ * - importPg: injectable import() for 'pg' (mirrors factory.ts PgSchemaAdapterDeps.importPg)
2208
+ * - platform: injectable process.platform override (for cross-platform testing)
2209
+ *
2210
+ * ADR-004: this file MAY import node:child_process (adapter territory).
2211
+ */
2212
+
2213
+ /**
2214
+ * Injectable seam for node:child_process.spawnSync (Buffer overload).
2215
+ * Mirrors the SqlcmdStrategy / OdbcDriverStrategy seam pattern.
2216
+ */
2217
+ type SpawnSyncFn$2 = (command: string, args: readonly string[], options: SpawnSyncOptions) => SpawnSyncReturns<Buffer>;
2218
+ /**
2219
+ * Injectable seam for dynamic import('pg').
2220
+ * Resolves with the module when present; rejects on MODULE_NOT_FOUND.
2221
+ * MUST NOT call Client.connect() or instantiate any class.
2222
+ */
2223
+ type ImportPgFn = () => Promise<unknown>;
2224
+ /**
2225
+ * Constructor options for PgCapabilityProbe.
2226
+ * All fields are optional — omit entirely for production use.
2227
+ */
2228
+ interface PgCapabilityProbeOptions {
2229
+ /** Injectable spawnSync seam. Defaults to node:child_process.spawnSync. */
2230
+ readonly spawnSync?: SpawnSyncFn$2;
2231
+ /** Injectable pg import seam. Defaults to dynamic import('pg'). */
2232
+ readonly importPg?: ImportPgFn;
2233
+ /** Injectable platform override for cross-platform PATH detection testing. */
2234
+ readonly platform?: NodeJS.Platform;
2235
+ }
2236
+ /**
2237
+ * Capability probe for PostgreSQL.
2238
+ * Implements CapabilityProbe — engine = 'pg'.
2239
+ *
2240
+ * probe() MUST resolve, never reject.
2241
+ * probe() MUST NOT open a DB connection.
2242
+ * probe() MUST NOT issue any write.
2243
+ */
2244
+ declare class PgCapabilityProbe implements CapabilityProbe {
2245
+ readonly engine = "pg";
2246
+ private readonly _spawnSync;
2247
+ private readonly _importPg;
2248
+ private readonly _platform;
2249
+ constructor(opts?: PgCapabilityProbeOptions);
2250
+ /**
2251
+ * Run all detections (driver, CLI) and return the composite result.
2252
+ * Any detection failure is reported as unavailable — never throws.
2253
+ * odbc is always false for PostgreSQL.
2254
+ */
2255
+ probe(): Promise<ProbeResult>;
2256
+ /**
2257
+ * Checks whether the 'pg' npm package is importable WITHOUT connecting.
2258
+ * Resolves true on success, false on any error.
2259
+ */
2260
+ private _detectNativeDriver;
2261
+ /**
2262
+ * Scans for psql on PATH using where (Windows) / which (POSIX).
2263
+ * If found, parses the version from psql --version.
2264
+ * Always resolves with one CliToolInfo entry for 'psql'.
2265
+ */
2266
+ private _detectCliTools;
2267
+ private _detectPsql;
2268
+ /**
2269
+ * Runs psql --version to extract the version string.
2270
+ * Returns null on any failure.
2271
+ */
2272
+ private _parsePsqlVersion;
2273
+ }
2274
+
2275
+ /**
2276
+ * MysqlCapabilityProbe — CapabilityProbe implementation for MySQL.
2277
+ * Design §"probe() is OPTIONAL on the strategy port; per-engine probe in adapters".
2278
+ * Resilient-connectivity Batch 2, task 2.3.
2279
+ *
2280
+ * Detection strategy:
2281
+ * - nativeDriver: dynamic import('mysql2') resolves → present (NO createConnection())
2282
+ * - cliTools: where/which mysql on PATH (cross-platform); version via mysql --version
2283
+ * - odbc: always false (N/A for MySQL)
2284
+ *
2285
+ * All detection failures, timeouts, and absent prerequisites yield a NEGATIVE
2286
+ * result — probe() MUST NEVER reject.
2287
+ *
2288
+ * Seams (all optional — omit for production use):
2289
+ * - spawnSync: injectable child_process.spawnSync
2290
+ * - importMysql: injectable import() for 'mysql2' (mirrors factory.ts MysqlSchemaAdapterDeps.importMysql)
2291
+ * - platform: injectable process.platform override (for cross-platform testing)
2292
+ *
2293
+ * ADR-004: this file MAY import node:child_process (adapter territory).
2294
+ */
2295
+
2296
+ /**
2297
+ * Injectable seam for node:child_process.spawnSync (Buffer overload).
2298
+ * Mirrors the SqlcmdStrategy / OdbcDriverStrategy seam pattern.
2299
+ */
2300
+ type SpawnSyncFn$1 = (command: string, args: readonly string[], options: SpawnSyncOptions) => SpawnSyncReturns<Buffer>;
2301
+ /**
2302
+ * Injectable seam for dynamic import('mysql2').
2303
+ * Resolves with the module when present; rejects on MODULE_NOT_FOUND.
2304
+ * MUST NOT call createConnection() or establish any connection.
2305
+ */
2306
+ type ImportMysqlFn = () => Promise<unknown>;
2307
+ /**
2308
+ * Constructor options for MysqlCapabilityProbe.
2309
+ * All fields are optional — omit entirely for production use.
2310
+ */
2311
+ interface MysqlCapabilityProbeOptions {
2312
+ /** Injectable spawnSync seam. Defaults to node:child_process.spawnSync. */
2313
+ readonly spawnSync?: SpawnSyncFn$1;
2314
+ /** Injectable mysql2 import seam. Defaults to dynamic import('mysql2'). */
2315
+ readonly importMysql?: ImportMysqlFn;
2316
+ /** Injectable platform override for cross-platform PATH detection testing. */
2317
+ readonly platform?: NodeJS.Platform;
2318
+ }
2319
+ /**
2320
+ * Capability probe for MySQL.
2321
+ * Implements CapabilityProbe — engine = 'mysql'.
2322
+ *
2323
+ * probe() MUST resolve, never reject.
2324
+ * probe() MUST NOT open a DB connection.
2325
+ * probe() MUST NOT issue any write.
2326
+ */
2327
+ declare class MysqlCapabilityProbe implements CapabilityProbe {
2328
+ readonly engine = "mysql";
2329
+ private readonly _spawnSync;
2330
+ private readonly _importMysql;
2331
+ private readonly _platform;
2332
+ constructor(opts?: MysqlCapabilityProbeOptions);
2333
+ /**
2334
+ * Run all detections (driver, CLI) and return the composite result.
2335
+ * Any detection failure is reported as unavailable — never throws.
2336
+ * odbc is always false for MySQL.
2337
+ */
2338
+ probe(): Promise<ProbeResult>;
2339
+ /**
2340
+ * Checks whether the 'mysql2' npm package is importable WITHOUT connecting.
2341
+ * Resolves true on success, false on any error.
2342
+ */
2343
+ private _detectNativeDriver;
2344
+ /**
2345
+ * Scans for the mysql CLI on PATH using where (Windows) / which (POSIX).
2346
+ * If found, parses the version from mysql --version.
2347
+ * Always resolves with one CliToolInfo entry for 'mysql'.
2348
+ */
2349
+ private _detectCliTools;
2350
+ private _detectMysqlCli;
2351
+ /**
2352
+ * Runs mysql --version to extract the version string.
2353
+ * Returns null on any failure.
2354
+ */
2355
+ private _parseMysqlVersion;
2356
+ }
2357
+
2358
+ /**
2359
+ * SqliteCapabilityProbe — CapabilityProbe implementation for SQLite.
2360
+ * Design §"probe() is OPTIONAL on the strategy port; per-engine probe in adapters".
2361
+ * Resilient-connectivity Batch 2, task 2.4.
2362
+ *
2363
+ * Detection strategy:
2364
+ * - nativeDriver: true if better-sqlite3 OR node:sqlite is importable (no DB opened)
2365
+ * - cliTools: [] (no separate CLI tool relevant to SQLite extraction)
2366
+ * - odbc: always false (N/A for SQLite)
2367
+ *
2368
+ * Driver selection mirrors the shipped sqlite/factory.ts logic:
2369
+ * 1. Try better-sqlite3 first (the default driver — ships as a prod dependency).
2370
+ * 2. Fall back to node:sqlite if better-sqlite3 is absent and Node >= 22.5.
2371
+ * The probe keeps the surface uniform: same ProbeResult shape across all four engines
2372
+ * so `doctor` and the engine-agnostic parity suite are engine-agnostic by construction.
2373
+ *
2374
+ * All detection failures yield a NEGATIVE result — probe() MUST NEVER reject.
2375
+ *
2376
+ * Seams (all optional — omit for production use):
2377
+ * - importBetterSqlite: injectable import() for 'better-sqlite3'
2378
+ * - importNodeSqlite: injectable import() for 'node:sqlite'
2379
+ *
2380
+ * ADR-004: no child_process here (no CLI tool for SQLite).
2381
+ */
2382
+
2383
+ /** Injectable seam for dynamic import('better-sqlite3'). */
2384
+ type ImportBetterSqliteFn = () => Promise<unknown>;
2385
+ /** Injectable seam for dynamic import('node:sqlite'). */
2386
+ type ImportNodeSqliteFn = () => Promise<unknown>;
2387
+ /**
2388
+ * Constructor options for SqliteCapabilityProbe.
2389
+ * All fields are optional — omit entirely for production use.
2390
+ */
2391
+ interface SqliteCapabilityProbeOptions {
2392
+ /** Injectable better-sqlite3 import seam. Defaults to dynamic import('better-sqlite3'). */
2393
+ readonly importBetterSqlite?: ImportBetterSqliteFn;
2394
+ /** Injectable node:sqlite import seam. Defaults to dynamic import('node:sqlite'). */
2395
+ readonly importNodeSqlite?: ImportNodeSqliteFn;
2396
+ }
2397
+ /**
2398
+ * Capability probe for SQLite.
2399
+ * Implements CapabilityProbe — engine = 'sqlite'.
2400
+ *
2401
+ * probe() MUST resolve, never reject.
2402
+ * probe() MUST NOT open a DB file.
2403
+ * probe() MUST NOT issue any write.
2404
+ *
2405
+ * cliTools is always empty — there is no separate CLI tool for SQLite extraction.
2406
+ * odbc is always false — N/A for SQLite.
2407
+ */
2408
+ declare class SqliteCapabilityProbe implements CapabilityProbe {
2409
+ readonly engine = "sqlite";
2410
+ private readonly _importBetterSqlite;
2411
+ private readonly _importNodeSqlite;
2412
+ constructor(opts?: SqliteCapabilityProbeOptions);
2413
+ /**
2414
+ * Detects whether at least one SQLite driver is available.
2415
+ * Checks better-sqlite3 first, then node:sqlite.
2416
+ * Never opens a database file.
2417
+ * Never throws.
2418
+ */
2419
+ probe(): Promise<ProbeResult>;
2420
+ /**
2421
+ * Returns true if better-sqlite3 or node:sqlite is importable.
2422
+ * Tries better-sqlite3 first (it is the default and shipped prod dep).
2423
+ * Falls back to node:sqlite if better-sqlite3 is absent.
2424
+ * Returns false if both are absent or both throw.
2425
+ */
2426
+ private _detectNativeDriver;
2427
+ }
2428
+
2429
+ /**
2430
+ * MongodbCapabilityProbe — CapabilityProbe implementation for MongoDB.
2431
+ * Design §"probe() NEVER rejects; per-engine probe in adapters".
2432
+ * Mirrors PgCapabilityProbe exactly in structure.
2433
+ *
2434
+ * Detection strategy:
2435
+ * - nativeDriver: dynamic import('mongodb') resolves → present (NO MongoClient.connect())
2436
+ * - cliTools: where/which mongosh on PATH (cross-platform); version via mongosh --version
2437
+ * - odbc: always false (N/A for MongoDB)
2438
+ *
2439
+ * All detection failures, timeouts, and absent prerequisites yield a NEGATIVE
2440
+ * result — probe() MUST NEVER reject.
2441
+ *
2442
+ * Seams (all optional — omit for production use):
2443
+ * - spawnSync: injectable child_process.spawnSync
2444
+ * - importMongodb: injectable import() for 'mongodb' (mirrors factory.ts seam)
2445
+ * - platform: injectable process.platform override (for cross-platform testing)
2446
+ *
2447
+ * ADR-004: this file MAY import node:child_process (adapter territory).
2448
+ * US-030 (MongoDB adapter), phase-9b-mongodb Batch 3 task 3.4.
2449
+ */
2450
+
2451
+ /**
2452
+ * Injectable seam for node:child_process.spawnSync (Buffer overload).
2453
+ * Mirrors PgCapabilityProbe seam pattern.
2454
+ */
2455
+ type SpawnSyncFn = (command: string, args: readonly string[], options: SpawnSyncOptions) => SpawnSyncReturns<Buffer>;
2456
+ /**
2457
+ * Injectable seam for dynamic import('mongodb').
2458
+ * Resolves with the module when present; rejects on MODULE_NOT_FOUND.
2459
+ * MUST NOT call MongoClient.connect() or instantiate any class.
2460
+ */
2461
+ type ImportMongodbFn = () => Promise<unknown>;
2462
+ /**
2463
+ * Constructor options for MongodbCapabilityProbe.
2464
+ * All fields are optional — omit entirely for production use.
2465
+ */
2466
+ interface MongodbCapabilityProbeOptions {
2467
+ /** Injectable spawnSync seam. Defaults to node:child_process.spawnSync. */
2468
+ readonly spawnSync?: SpawnSyncFn;
2469
+ /** Injectable mongodb import seam. Defaults to dynamic import('mongodb'). */
2470
+ readonly importMongodb?: ImportMongodbFn;
2471
+ /** Injectable platform override for cross-platform PATH detection testing. */
2472
+ readonly platform?: NodeJS.Platform;
2473
+ }
2474
+ /**
2475
+ * Capability probe for MongoDB.
2476
+ * Implements CapabilityProbe — engine = 'mongodb'.
2477
+ *
2478
+ * probe() MUST resolve, never reject.
2479
+ * probe() MUST NOT open a DB connection.
2480
+ * probe() MUST NOT issue any write.
2481
+ */
2482
+ declare class MongodbCapabilityProbe implements CapabilityProbe {
2483
+ readonly engine = "mongodb";
2484
+ private readonly _spawnSync;
2485
+ private readonly _importMongodb;
2486
+ private readonly _platform;
2487
+ constructor(opts?: MongodbCapabilityProbeOptions);
2488
+ /**
2489
+ * Run all detections (driver, CLI) and return the composite result.
2490
+ * Any detection failure is reported as unavailable — never throws.
2491
+ * odbc is always false for MongoDB.
2492
+ */
2493
+ probe(): Promise<ProbeResult>;
2494
+ /**
2495
+ * Checks whether the 'mongodb' npm package is importable WITHOUT connecting.
2496
+ * Resolves true on success, false on any error.
2497
+ */
2498
+ private _detectNativeDriver;
2499
+ /**
2500
+ * Scans for mongosh on PATH using where (Windows) / which (POSIX).
2501
+ * If found, parses the version from mongosh --version.
2502
+ * Always resolves with one CliToolInfo entry for 'mongosh'.
2503
+ */
2504
+ private _detectCliTools;
2505
+ private _detectMongosh;
2506
+ /**
2507
+ * Runs mongosh --version to extract the version string.
2508
+ * Returns null on any failure.
2509
+ */
2510
+ private _parseMongoshVersion;
2511
+ }
2512
+
2513
+ /**
2514
+ * profiles.ts — SqlcmdProfile registry for SQL Server connectivity.
2515
+ *
2516
+ * Encodes known sqlcmd variant/version quirks as DATA, not code branches.
2517
+ * Design Decision: "SqlcmdProfile registry is a data table in adapters".
2518
+ * Spec (US-040): "Variant/version profile registry encodes known quirks as data".
2519
+ *
2520
+ * F-3: Legacy sqlcmd 15.x flag mutual-exclusivities — -y 0 used ALONE (no -h/-W).
2521
+ * F-4: 2033-char chunk lines, NO column header, NO dashes separator.
2522
+ * F-5: -f o:65001 forces UTF-8 stdout codepage.
2523
+ * F-6: Reassembler must concatenate verbatim — no .trim() at chunk boundaries.
2524
+ *
2525
+ * Adding a new environment = adding one entry to SQLCMD_PROFILES.
2526
+ * No transport code changes required.
2527
+ *
2528
+ * ADR-004: this file lives in adapters territory; it may import core ports.
2529
+ * It MUST NOT import node:child_process or any driver.
2530
+ */
2531
+
2532
+ /**
2533
+ * Profile encoding a specific sqlcmd variant+version environment.
2534
+ *
2535
+ * Fields:
2536
+ * variant — tool variant identifier (e.g. 'legacy-odbc', 'go-sqlcmd')
2537
+ * versionRange — human-readable version range (e.g. '15.x', '16.x', 'any')
2538
+ * flags — extra argv flags to append to the sqlcmd invocation
2539
+ * (EXACT array — no -h, no -W for legacy-15.x per F-3)
2540
+ * outputShape — describes the FOR JSON output format for this variant+version
2541
+ * encoding — Buffer decoding encoding for stdout (e.g. 'utf8')
2542
+ */
2543
+ interface SqlcmdProfile {
2544
+ /** Stable tool variant identifier, e.g. 'legacy-odbc', 'go-sqlcmd'. */
2545
+ readonly variant: string;
2546
+ /** Human-readable version range string, e.g. '15.x'. */
2547
+ readonly versionRange: string;
2548
+ /**
2549
+ * Extra argv flags to pass to sqlcmd for catalog/fingerprint invocations.
2550
+ * For legacy-15.x: ['-y', '0', '-f', 'o:65001'] — -y 0 alone (F-3).
2551
+ */
2552
+ readonly flags: readonly string[];
2553
+ /** Describes the FOR JSON output format emitted by this variant+version. */
2554
+ readonly outputShape: {
2555
+ /** Byte width of each FOR JSON chunk line (2033 for legacy-15.x — F-4). */
2556
+ readonly chunkSize: number;
2557
+ /**
2558
+ * Whether the output has a column-header line before the JSON data.
2559
+ * False for legacy-15.x with -y 0 and SET NOCOUNT ON (F-4).
2560
+ */
2561
+ readonly hasHeader: boolean;
2562
+ };
2563
+ /**
2564
+ * Node.js Buffer encoding for stdout.toString().
2565
+ * 'utf8' when -f o:65001 is in flags (F-5).
2566
+ */
2567
+ readonly encoding: string;
2568
+ }
2569
+ /**
2570
+ * Resolves the SqlcmdProfile for the given probe result.
2571
+ *
2572
+ * Matching algorithm:
2573
+ * 1. Extract the version string from the first sqlcmd CliToolInfo entry.
2574
+ * 2. For each registered profile (in order), check if the version matches
2575
+ * the profile's versionRange (simple major-version prefix match).
2576
+ * 3. Return the first matching profile.
2577
+ * 4. If no entry matches → return DEFAULT_PROFILE (never throw).
2578
+ *
2579
+ * The conservative-default guarantee means an unrecognized environment
2580
+ * produces a profile, not an exception (US-040 requirement).
2581
+ */
2582
+ declare function resolveProfile(probe: ProbeResult): SqlcmdProfile;
2583
+
2584
+ /**
2585
+ * Truthful CapabilityMatrix for SQLite.
2586
+ * Design §3 "CapabilityMatrix (truthful SQLite)".
2587
+ * SQLite supports: schema, table, column, constraint, index, view, trigger.
2588
+ * SQLite does NOT support: procedure, function, sequence, collection, field,
2589
+ * statistics, sampling (these concepts do not exist in SQLite).
2590
+ * supportsBodies: true — view + trigger SQL available from sqlite_master.
2591
+ * supportsDependencyHints: false — the flag denotes cheap catalog hints (a dependency PRAGMA),
2592
+ * which SQLite does NOT expose; false here matches pg/mysql/mongodb. Dependency edges
2593
+ * (view depends_on, trigger writes_to/reads_from) are body-derived — parsed from the view/
2594
+ * trigger body SQL via the shared presence-gate tokenizer — so the flag gates no code path here.
2595
+ */
2596
+
2597
+ declare const SQLITE_CAPABILITIES: CapabilityMatrix;
2598
+
2599
+ /**
2600
+ * Truthful CapabilityMatrix for Microsoft SQL Server.
2601
+ * Design §CapabilityMatrix "CapabilityMatrix (truthful SQL Server)".
2602
+ *
2603
+ * SQL Server supports: schema, table, column, constraint, index, view,
2604
+ * procedure, function, trigger, sequence.
2605
+ * SQL Server does NOT support: collection, field (MongoDB-only concepts).
2606
+ *
2607
+ * supportsBodies: true — sys.sql_modules.definition provides bodies
2608
+ * for views, procedures, functions, triggers (level-gated).
2609
+ * supportsDependencyHints: true — sys.sql_expression_dependencies feeds the
2610
+ * conservative body tokenizer to classify read/write edges (US-007, ADR-007).
2611
+ *
2612
+ * US-027 (SQL Server adapter), E5 common criterion (truthful matrix).
2613
+ */
2614
+
2615
+ declare const MSSQL_CAPABILITIES: CapabilityMatrix;
2616
+
2617
+ /**
2618
+ * Truthful CapabilityMatrix for PostgreSQL.
2619
+ * Design §CapabilityMatrix "Truthful PostgreSQL CapabilityMatrix".
2620
+ *
2621
+ * PG supports: schema, table, column, constraint, index, view,
2622
+ * procedure, function, trigger, sequence.
2623
+ * PG does NOT support: collection, field (MongoDB-only concepts).
2624
+ *
2625
+ * supportsBodies: true — pg_get_functiondef/pg_get_viewdef provide bodies
2626
+ * for views, procedures, functions, triggers (level-gated).
2627
+ * supportsDependencyHints: false — Phase-8a derives edges from bodies ONLY
2628
+ * (no pg_depend OID-graph edge list; body tokenizer is the SOLE edge source).
2629
+ * pg_depend/pg_rewrite OID-graph mapping is recorded as a future enhancement.
2630
+ *
2631
+ * US-028 (PostgreSQL adapter), US-028b (matview without model change),
2632
+ * E5 common criterion (truthful matrix).
2633
+ */
2634
+
2635
+ declare const PG_CAPABILITIES: CapabilityMatrix;
2636
+
2637
+ /**
2638
+ * Truthful CapabilityMatrix for MySQL.
2639
+ * Design §MYSQL_CAPABILITIES.
2640
+ *
2641
+ * MySQL supports: table, column, constraint, index, view,
2642
+ * procedure, function, trigger.
2643
+ * MySQL does NOT support: sequence (AUTO_INCREMENT is a per-table column
2644
+ * property, not a first-class sequence object — MYSQL_CAPABILITIES omits
2645
+ * 'sequence'; golden asserts ZERO sequence objects).
2646
+ * MySQL does NOT expose a standalone schema kind: the connected database IS
2647
+ * the namespace (schema == database), surfaced via RawCatalog.schemas.
2648
+ *
2649
+ * supportsBodies: true — VIEW_DEFINITION + ROUTINE_DEFINITION provide bodies.
2650
+ * supportsDependencyHints: false — MySQL has no information_schema dependency
2651
+ * view (no pg_depend / sys.sql_expression_dependencies equivalent).
2652
+ * The body tokenizer is the SOLE edge source.
2653
+ *
2654
+ * US-029 (MySQL adapter, Phase 8b), E5 common criterion (truthful matrix).
2655
+ */
2656
+
2657
+ declare const MYSQL_CAPABILITIES: CapabilityMatrix;
2658
+
2659
+ /**
2660
+ * Truthful CapabilityMatrix for MongoDB.
2661
+ * Design §MONGODB_CAPABILITIES.
2662
+ *
2663
+ * MongoDB supports: collection, field, index ONLY.
2664
+ * MongoDB does NOT support: table, column, constraint, view, procedure,
2665
+ * function, trigger, sequence (SQL-only concepts irrelevant to MongoDB).
2666
+ *
2667
+ * supportsBodies: false — MongoDB has no procedure or view body to retrieve.
2668
+ * supportsDependencyHints: false — the body tokenizer is the SOLE edge source.
2669
+ * (MongoDB has no analog to pg_depend or sys.sql_expression_dependencies.)
2670
+ *
2671
+ * US-030 (MongoDB adapter, Phase 9b), E5 common criterion (truthful matrix).
2672
+ */
2673
+
2674
+ declare const MONGODB_CAPABILITIES: CapabilityMatrix;
2675
+
2676
+ /**
2677
+ * Package public API — design §2.
2678
+ * Re-exports everything from the core barrel plus the SQLite adapter factory.
2679
+ * The adapter factory is the ONLY place core and adapters are joined (ADR-004).
2680
+ *
2681
+ * Consumers: import from '@niklerk23/dbgraph' — never from internal sub-paths.
2682
+ */
2683
+ declare const DBGRAPH_VERSION = "1.0.0";
2684
+
2685
+ /**
2686
+ * Returns the static CapabilityMatrix for the given dialect WITHOUT opening
2687
+ * a database connection. Unknown dialect throws UnsupportedDialectError.
2688
+ * Design Decision 6 (phase-4-cli-config).
2689
+ */
2690
+ declare function capabilitiesFor(dialect: string): CapabilityMatrix;
2691
+
2692
+ export { type AdapterAndStore, type CapabilityMatrix, type CapabilityProbe, type CliToolInfo, type ColumnAnnotations, type ColumnPayload, ConfigError, ConnectionError, type ConnectivityOption, type ConnectivityOutcome, type ConnectivityStrategy, ConnectivityUnavailableError, type ConstraintPayload, DBGRAPH_VERSION, DbgraphError, type DetectResult, type DoctorView, EDGE_CONFIDENCE_VALUES, EDGE_KINDS, type EdgeAttrs, type EdgeConfidence, type EdgeKind, type ExploreDetail, type ExploreView, type ExtractionScope, type FieldPayload, type GraphEdge, type GraphNode, type GraphStore, INDEX_LEVELS, type ImpactChain, type ImpactDetail, type ImpactQuery, type ImpactResult, type ImpactView, type IndexLevel, type IndexPayload, type JoinHop, LEVENSHTEIN_THRESHOLD, type LevelResult, type Logger, MONGODB_CAPABILITIES, MSSQL_CAPABILITIES, MYSQL_CAPABILITIES, type McpStatusView, type MongodbAdapterConfig, MongodbCapabilityProbe, type MssqlAdapterConfig, MssqlCapabilityProbe, type MysqlAdapterConfig, MysqlCapabilityProbe, NODE_KINDS, type NeighborEntry, type NeighborGroups, type NeighborQuery, type NodeKind, type NodePayload, NormalizationError, type NormalizationResult, type NormalizedGraph, NotFoundError, type ObjectDetail, type ObjectTypeLevels, type ObjectView, PG_CAPABILITIES, type PathQuery, type PathResult, type PathView, PermissionError, type PgAdapterConfig, PgCapabilityProbe, type PrecheckDetail, type PrecheckImpactSection, type PrecheckItem, type PrecheckView, type ProbeResult, QueryError, type RawCatalog, type RawColumn, type RawConstraint, type RawDependency, type RawField, type RawIndex, type RawObject, type RawTriggerInfo, type RelatedDetail, type ExploreView as RelatedView, type RoutinePayload, SQLITE_CAPABILITIES, type SchemaAdapter, type SchemaAdapterConfig, SchemaVersionError, type SearchDetail, type SearchHit, type SearchQuery, type SearchResult, type SearchView, type SnapshotObjectRow, type SnapshotRecord, type SqlcmdProfile, type SqliteAdapterConfig, SqliteCapabilityProbe, type StatusDetail, StorageError, type StrategyAttempt, StrategyExhaustionError, type StubInfo, TYPO_CAP, type TablePayload, TransportError, type TriggerPayload, UnsupportedDialectError, type UpsertResult, applyLevel, canonicalQName, capabilitiesFor, createMongodbSchemaAdapter, createMssqlSchemaAdapter, createMysqlSchemaAdapter, createPgSchemaAdapter, createSqliteGraphStore, createSqliteSchemaAdapter, deriveColumnAnnotations, edgeId, extractIdentifiers, findJoinPath, formatDoctor, formatExplore, formatImpact, formatObject, formatOutcome, formatPath, formatPrecheck, formatRelated, formatSearch, formatStatus, getImpact, getNeighbors, nodeId, noopLogger, normalizeBody, normalizeCatalog, openConnections, renderColumns, renderConstraints, renderFocusPayload, renderIndexes, renderTriggers, resolveProfile, runPrecheck, search, stableStringify };