@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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ElkinDev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,317 @@
1
+ # dbgraph
2
+
3
+ > A codegraph for your database. dbgraph indexes a database catalog into a local
4
+ > graph of tables, columns, constraints, views, procedures, functions, triggers and
5
+ > their relationships, and serves it to AI agents over MCP — so one tool call answers
6
+ > what would otherwise cost several exploratory queries.
7
+
8
+ dbgraph reads a database's catalog (never its data), normalizes it into an
9
+ engine-agnostic graph, and persists that graph in a local SQLite + FTS5 index. AI
10
+ agents then query the graph over the Model Context Protocol (MCP) instead of
11
+ re-discovering the schema by hand.
12
+
13
+ - **Read-only by construction.** No command issues any write, DDL or DML against the
14
+ target database — only catalog `SELECT`s through the adapter port
15
+ (`openspec/specs/cli-config/spec.md` — "Read-only-against-target is INVIOLABLE").
16
+ - **100% local.** The graph lives in a local SQLite + FTS5 index; nothing leaves the
17
+ machine (`openspec/specs/graph-storage/spec.md`).
18
+ - **Served over MCP.** The eight tools `dbgraph_explore`, `dbgraph_search`,
19
+ `dbgraph_object`, `dbgraph_related`, `dbgraph_impact`, `dbgraph_path`,
20
+ `dbgraph_precheck` and `dbgraph_status` expose the graph to agents over the official
21
+ `@modelcontextprotocol/sdk` stdio transport
22
+ (`openspec/specs/mcp-server/spec.md` — Purpose).
23
+
24
+ > **Status.** This repository runs **from source today**. There is no published npm
25
+ > package or binary yet — the release workflow exists but has never been fired (see
26
+ > [Limitations](#limitations)). Standalone win-x64 / linux-x64 binaries are planned for
27
+ > v1.0.
28
+
29
+ ## What it models
30
+
31
+ Every catalog object becomes a node; every relationship becomes an edge.
32
+
33
+ - **Nodes** (`openspec/specs/graph-model/spec.md` — Node taxonomy): `database`,
34
+ `schema`, `table`, `column`, `view`, `trigger`, `procedure`, `function`, `index`,
35
+ and `field` (for document stores).
36
+ - **Edges** (`openspec/specs/graph-model/spec.md` — Edge taxonomy): `references`
37
+ (declared foreign keys, plus an aggregated table→table form), `depends_on` (view
38
+ dependencies), `reads_from` / `writes_to` (parsed from module bodies), `fires_on`
39
+ (triggers, carrying the DML event), `indexes`, and `inferred_reference` (opt-in,
40
+ carrying `confidence: inferred` and a numeric `score`).
41
+
42
+ Every edge is classified by `confidence` — `declared`, `parsed`, or `inferred` — so
43
+ the graph never hides how it knows something. A module whose body cannot be analyzed
44
+ is flagged `has_dynamic_sql: true` rather than guessing its edges
45
+ (`openspec/specs/graph-normalization/spec.md` — "Dynamic SQL declares blindness").
46
+
47
+ ## Feature matrix
48
+
49
+ Five engines are supported. Each cell below is transcribed from the engine's canonical
50
+ extraction spec and its truthful `CapabilityMatrix` — not from memory.
51
+
52
+ | Engine | Tables & columns | Views | Indexes | Constraints / FKs | Procedures / functions | Triggers | Inferred relationships | Connectivity |
53
+ |--------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---|
54
+ | **SQLite** | ✓ | ✓ | ✓ | FK¹ | — | ✓ | ○ | local file (`better-sqlite3` / `node:sqlite`) |
55
+ | **SQL Server** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ○ | SQL auth · NTLM · integrated (via `sqlcmd`) |
56
+ | **PostgreSQL** | ✓ | ✓² | ✓ | ✓ | ✓ | ✓ | ○ | host/port/db/user/password · optional SSL |
57
+ | **MySQL / MariaDB** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ○ | host/port/db/user/password · optional SSL |
58
+ | **MongoDB** | collections + sampled fields³ | — | ✓ | —⁴ | — | — | ✓⁵ | `${env:VAR}` URI · database · sampleSize · TLS |
59
+
60
+ **Legend:** ✓ extracted · ○ opt-in, off by default · — not supported by the engine
61
+ (explicitly declared unsupported in its `CapabilityMatrix`).
62
+
63
+ **Per-cell sources:**
64
+
65
+ - **SQLite** — tables/columns, foreign keys (incl. composite), indexes
66
+ (unique/partial/expression), views and triggers per
67
+ `openspec/specs/sqlite-extraction/spec.md`. Its `CapabilityMatrix` declares
68
+ procedures, functions, sequences and collections **unsupported** (same spec —
69
+ "Truthful SQLite CapabilityMatrix"; `openspec/specs/cli-config/spec.md` — "SQLite
70
+ offers no procedures").
71
+ ¹ SQLite exposes foreign keys (including composite) and primary-key membership on
72
+ columns; it has no separate named CHECK/UNIQUE constraint objects.
73
+ - **SQL Server** — tables/columns (types, nullability, defaults, computed); PK, FK
74
+ (composite), unique and check constraints; indexes (clustered/nonclustered, filtered,
75
+ included); views, procedures, functions and triggers with bodies per level; trigger
76
+ `fires_on` plus its read/write effects — per
77
+ `openspec/specs/mssql-extraction/spec.md`. Connectivity via SQL auth and NTLM, with
78
+ integrated security handled through an external `sqlcmd` strategy (same spec; Kerberos
79
+ SSO is unsupported).
80
+ - **PostgreSQL** — tables/columns (types, nullability, defaults, identity, generated);
81
+ PK, FK (composite), unique and CHECK constraints; indexes (partial, expression,
82
+ included); functions and procedures; triggers (event + timing) — per
83
+ `openspec/specs/pg-extraction/spec.md`.
84
+ ² Views include materialized views.
85
+ - **MySQL / MariaDB** — tables/columns (types, nullability, defaults, AUTO_INCREMENT);
86
+ PK, FK (composite), unique and CHECK constraints; indexes (composite, uniqueness);
87
+ views, functions, procedures and triggers — per
88
+ `openspec/specs/mysql-extraction/spec.md`.
89
+ - **MongoDB** — collections via `listCollections`, with field structure **inferred by
90
+ sampling** documents through `$sample` (dotted paths, union BSON types, presence
91
+ frequency); indexes via `listIndexes`; `$jsonSchema` validators carried in `extra` —
92
+ per `openspec/specs/mongodb-extraction/spec.md`. Its `CapabilityMatrix` declares
93
+ `table`, `column`, `constraint`, `view`, `procedure`, `function`, `trigger` and
94
+ `sequence` **unsupported** (`supportsBodies: false`; same spec — "Truthful MongoDB
95
+ CapabilityMatrix"; `openspec/specs/cli-config/spec.md` — "MongoDB offers no
96
+ triggers").
97
+ ³ Field structure is a statistical inference over a finite sample — honest only about
98
+ what the sample observed. Sampled **values are never persisted** (only field names,
99
+ types and frequencies).
100
+ ⁴ MongoDB has no foreign keys; top-level `$jsonSchema` validators are carried in the
101
+ collection's `extra`.
102
+ ⁵ For MongoDB, `inferred_reference` edges are the **only** relationships produced (an
103
+ `<entity>_id` field infers a reference to the target collection's `_id`) and inference
104
+ is auto-enabled by the presence of collection/field nodes.
105
+
106
+ **Inferred relationships** (the `○` cells) come from an opt-in, pure-core inference
107
+ engine that is **off by default** for the SQL engines. When enabled it emits
108
+ `inferred_reference` edges from column names and declared types alone — `<entity>_id`,
109
+ `<entity>Id`, `id_<entity>` resolved against real target tables and gated by type
110
+ compatibility — never by reading data values
111
+ (`openspec/specs/graph-normalization/spec.md` — "Opt-in structural inference of
112
+ references", "Inference is opt-in and OFF by default").
113
+
114
+ ## Quickstart (from source)
115
+
116
+ There is no published package yet, so run from source. Requires **Node.js >= 22**.
117
+
118
+ ```bash
119
+ git clone <this-repo> dbgraph
120
+ cd dbgraph
121
+ npm ci
122
+ npm run build
123
+ ```
124
+
125
+ Then point dbgraph at a database and build its graph. Using SQLite as an example:
126
+
127
+ ```bash
128
+ # 1. Initialize: validates the connection, writes dbgraph.config.json at the project
129
+ # root (with ${env:VAR} references only — never plaintext secrets), gitignores the
130
+ # local .dbgraph index, and runs the first sync.
131
+ dbgraph init --dialect sqlite --file ./app.db
132
+
133
+ # 2. Re-sync after schema changes (incremental — skips extraction when the catalog
134
+ # fingerprint is unchanged; --full forces a rebuild).
135
+ dbgraph sync
136
+
137
+ # 3. Query the graph.
138
+ dbgraph query orders # ranked hits, each with its type and qualified name
139
+ dbgraph query orders --json # deterministic, machine-parseable JSON
140
+ dbgraph explore main.orders # a node and its grouped neighbors (use the QUALIFIED name)
141
+ dbgraph status # counts, last snapshot, and live drift
142
+ ```
143
+
144
+ `init` writes an env-only config: every connection-identity field (host, port, user,
145
+ password, URL) is stored as a `${env:VAR}` reference and resolved from `process.env` at
146
+ runtime — a plaintext credential is rejected
147
+ (`openspec/specs/cli-config/spec.md` — "Plaintext credentials are rejected").
148
+
149
+ ## CLI reference
150
+
151
+ `dbgraph <command> [options]`. Run `dbgraph --help` for the full banner, or
152
+ `dbgraph <command> --help` for per-command options.
153
+
154
+ | Command | What it does |
155
+ |---------|--------------|
156
+ | `init` | Initialize the graph index for a database (writes config, gitignores `.dbgraph`, runs first sync). |
157
+ | `sync` | Synchronize the graph with the database — incremental by fingerprint; `--full` forces a rebuild. |
158
+ | `status` | Show current graph counts, the last snapshot, and whether the live schema has drifted. |
159
+ | `query <term>` | Full-text search the graph; prints type + qualified name per hit. Exits 1 on zero hits. |
160
+ | `explore <qname>` | Show a node and its grouped neighbors. `--detail brief\|normal\|full`. |
161
+ | `diff <a> <b>` | Compare two snapshots per object (added/removed/modified). `--last` compares the two most recent. Exits 1 when changes exist (CI-gate). |
162
+ | `affected <script.sql>` | Analyze a DDL script and report impacted objects. `--json` for machine output; exits 1 when anything is affected. |
163
+ | `doctor` | Run a content-free connectivity self-test (safe to share). |
164
+ | `install` | Wire `dbgraph-mcp` into supported MCP agents. `--project` for project scope, `--remove` to undo. |
165
+
166
+ Global flags: `--json` (machine-parseable output where supported), `--quiet` / `-q`
167
+ (suppress info/progress, keep warnings/errors), `--help` / `-h`, `--version` / `-v`.
168
+ `--json` payloads on stdout are deterministic and byte-identical for the same graph and
169
+ input (`openspec/specs/cli-config/spec.md`; `src/cli/cli.ts` `USAGE_TEXT`;
170
+ `src/cli/dispatch.ts` `COMMAND_TABLE`).
171
+
172
+ Exit codes map to a fixed contract (`openspec/specs/cli-config/spec.md`): `0` success ·
173
+ `1` a "negative" result (zero query hits, or diff/affected found changes) · `2`
174
+ connection failure · `3` permission failure · `4` unsupported dialect.
175
+
176
+ ## MCP integration
177
+
178
+ `dbgraph install` wires an idempotent `dbgraph-mcp` server entry into every supported
179
+ agent's MCP config, driven by a single `AGENT_TABLE` source of truth
180
+ (`openspec/specs/mcp-server/spec.md` — "dbgraph install idempotently wires the agent MCP
181
+ config"; `src/cli/commands/install.ts`).
182
+
183
+ ```bash
184
+ dbgraph install # wire user-level config for every detected agent
185
+ dbgraph install --project # write project-scoped config in the current directory
186
+ dbgraph install --remove # remove the dbgraph-mcp entry from every detected agent
187
+ ```
188
+
189
+ The written entry is `command` + `args` only (`npx -y dbgraph-mcp`) — never a secret.
190
+ Global install configures an agent **only if** its config file already exists;
191
+ `--project` (US-038) re-roots resolution at the current directory and **creates** absent
192
+ project files for the supported agents.
193
+
194
+ ### Supported agents
195
+
196
+ | Agent | Config key & shape | Project-scoped file (`--project`) |
197
+ |-------|--------------------|-----------------------------------|
198
+ | Claude Code | `mcpServers.dbgraph-mcp = { command, args }` | `.mcp.json` |
199
+ | Cursor | `mcpServers.dbgraph-mcp = { command, args }` | `.cursor/mcp.json` |
200
+ | Gemini CLI | `mcpServers.dbgraph-mcp = { command, args }` | `.gemini/settings.json` |
201
+ | VS Code | `servers.dbgraph-mcp = { type: 'stdio', command, args }` | `.vscode/mcp.json` |
202
+ | opencode | `mcp.dbgraph-mcp = { type: 'local', command: [...] }` | `opencode.json` |
203
+ | Codex CLI | TOML `[mcp_servers.dbgraph-mcp]` | `.codex/config.toml` |
204
+
205
+ **Codex is trust-gated.** Codex loads project-scoped MCP servers only for **trusted**
206
+ projects, so `--project` reports it with a caveat verbatim:
207
+
208
+ ```text
209
+ codex → written (requires trusted project: set trust_level in ~/.codex/config.toml)
210
+ ```
211
+
212
+ ### Env-var interpolation per agent
213
+
214
+ The `dbgraph-mcp` entry itself uses no interpolation (only `command` + `args`). But if
215
+ you hand-edit a config to add another server that needs a secret, each agent has its own
216
+ native env syntax — reference variables, never inline a credential:
217
+
218
+ | Agent | Config file | Env-var interpolation syntax |
219
+ |-------|-------------|------------------------------|
220
+ | Claude Code | `.mcp.json` | `${VAR}` |
221
+ | Cursor | `.cursor/mcp.json` | `${env:VAR}` |
222
+ | VS Code | `.vscode/mcp.json` | `${env:VAR}` + `inputs` |
223
+ | Gemini CLI | `.gemini/settings.json` | `$VAR` / `${VAR}` |
224
+ | opencode | `opencode.json` | `{env:VAR}` |
225
+ | Codex CLI | `.codex/config.toml` | TOML `env` tables — no string interpolation |
226
+
227
+ If no agent config is detected, `dbgraph install` prints a manual snippet for all six
228
+ formats and exits successfully.
229
+
230
+ ### Serve over HTTP
231
+
232
+ `dbgraph mcp` speaks **stdio** by default. Add `--http` to serve the same read-only
233
+ 8-tool surface over **Streamable HTTP** from one host, for several remote agents:
234
+
235
+ ```bash
236
+ dbgraph mcp --http # endpoint at http://127.0.0.1:7423/mcp (loopback default)
237
+ dbgraph mcp --http --host 0.0.0.0 # bind all interfaces — prints a no-auth warning
238
+ ```
239
+
240
+ **No authentication ships in v1** and the default bind is loopback (`127.0.0.1`) — that
241
+ loopback default is the primary containment. For any non-loopback exposure, front the
242
+ endpoint with a reverse proxy (TLS + auth) or network controls. HTTP mode adds no write
243
+ path and diagnostics stay content-free.
244
+
245
+ HTTP client config is **not** auto-wired (`dbgraph install` wires the stdio entry only) —
246
+ add the server by hand. Two shapes bite if copied across agents: **Gemini CLI** needs
247
+ `httpUrl` (a plain `url` silently selects deprecated SSE), and **Cursor** takes **no**
248
+ `type` field (it is inferred from the `url`). The verified 6/6 per-agent matrix, the
249
+ reverse-proxy model, and the pinned `--host 0.0.0.0` warning are in
250
+ [`docs/mcp-http.md`](docs/mcp-http.md).
251
+
252
+ ## Troubleshooting
253
+
254
+ Run `dbgraph doctor` first — it prints a **content-free** capability report (engine,
255
+ native-driver presence, detected CLI tools with versions, ODBC presence, resolved
256
+ environment profile, and the strategy it would choose). It contains no schema name,
257
+ identifier, query result, or secret, so it is safe to paste into a bug report
258
+ (`openspec/specs/connectivity-diagnostics/spec.md` — "dbgraph doctor reports diagnostics
259
+ content-free"; `src/core/present/doctor.ts` `DoctorView`).
260
+
261
+ The real-world SQL Server connectivity breaks below are documented in
262
+ `docs/findings/connectivity-environments.md` (F-1..F-7) and handled by the
263
+ connectivity-strategy system (`openspec/specs/connectivity/spec.md`,
264
+ `openspec/specs/connectivity-diagnostics/spec.md`):
265
+
266
+ | # | Symptom | `doctor` field | Fix |
267
+ |---|---------|----------------|-----|
268
+ | F-1 | `sync` fails on an integrated-security SQL Server (no SQL login; the pure-JS driver can't do SSPI) | `cliTools` (sqlcmd absent) / `chosenStrategy: unavailable` | Install `sqlcmd`; dbgraph shells out with `sqlcmd -E` for integrated auth. |
269
+ | F-2 | Wrong `sqlcmd` flags for the installed variant/version | `resolvedProfile: <variant>@<versionRange>` | dbgraph selects flags per detected variant; if it reports an unrecognized profile, share the `doctor` output. |
270
+ | F-3 | Truncated / failed `FOR JSON` on a legacy `sqlcmd` | `resolvedProfile` (legacy) | dbgraph adapts flags per profile (e.g. `-y 0` alone on legacy 15.x). |
271
+ | F-4 | JSON parse fails at a chunk boundary | `resolvedProfile` (output shape) | dbgraph reassembles the 2033-char `FOR JSON` chunks without trimming. |
272
+ | F-5 | Non-ASCII characters corrupt the JSON stream | `resolvedProfile` (encoding) | dbgraph forces the `sqlcmd` output to codepage 65001 (`-f o:65001`). |
273
+ | F-6 | Malformed / partial tool output | actionable error (what was received, first N chars) — never a raw stack trace | Run `dbgraph doctor` and paste the content-free report. |
274
+ | F-7 | These `sqlcmd` transport breaks aren't caught by CI | — | Covered by recorded fixtures and an opt-in `sqlcmd` CI lane — see [CONTRIBUTING](CONTRIBUTING.md) and [Limitations](#limitations). |
275
+
276
+ A missing database driver surfaces the exact install command, not a stack trace —
277
+ `Required driver '<name>' is not installed. Run: npm i <name>`
278
+ (`openspec/specs/binary-distribution/spec.md`). A connection failure yields a typed,
279
+ non-blocking outcome offering at least three actionable options (run the catalog
280
+ `SELECT`s yourself, consented install, or import a manual dump) — never an unhandled
281
+ exception (`openspec/specs/connectivity-diagnostics/spec.md`).
282
+
283
+ ## Limitations
284
+
285
+ Stated plainly, and by design:
286
+
287
+ - **No published release yet.** The package version is `0.0.0` and the `release.yml`
288
+ workflow, though written and trigger-guarded, has never been fired — no tag has been
289
+ pushed (`openspec/specs/binary-distribution/spec.md` — "release.yml is trigger-guarded
290
+ ... and never fired"). Run from source today; standalone win-x64 / linux-x64 binaries
291
+ are planned for v1.0.
292
+ - **MongoDB structure is inferred, not declared.** Field structure comes from `$sample`;
293
+ it is honest only about what the sample observed. Sampled document **values are never
294
+ persisted** — only field names, types and presence frequencies survive
295
+ (`openspec/specs/cli-config/spec.md` — sampling is "structural only ... values never
296
+ stored"; `openspec/specs/mongodb-extraction/spec.md`).
297
+ - **Inferred relationships are opt-in.** For the SQL engines, inference is off by
298
+ default and must be enabled explicitly; it never reads data values
299
+ (`openspec/specs/graph-normalization/spec.md`).
300
+ - **SQL Server integrated auth needs `sqlcmd`.** The pure-JS driver cannot do
301
+ SSPI/Kerberos; integrated security is handled by shelling out to `sqlcmd -E`
302
+ (`openspec/specs/mssql-extraction/spec.md`; `docs/findings/connectivity-environments.md`
303
+ F-1).
304
+ - **The `sqlcmd` transport has a CI coverage gap.** Flag combinations, output shape,
305
+ chunking and encoding only surface on a real `sqlcmd` run; they are covered by recorded
306
+ fixtures and an opt-in CI lane, not the default unit matrix
307
+ (`docs/findings/connectivity-environments.md` F-7).
308
+
309
+ ## Development
310
+
311
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the per-batch quality gate, the strict
312
+ TDD and spec-driven workflow, and the test tiers. Security posture and disclosure are in
313
+ [SECURITY.md](SECURITY.md).
314
+
315
+ ## License
316
+
317
+ MIT
@@ -0,0 +1,48 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
16
+ var __commonJS = (cb, mod) => function __require2() {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
22
+ };
23
+ var __copyProps = (to, from, except, desc) => {
24
+ if (from && typeof from === "object" || typeof from === "function") {
25
+ for (let key of __getOwnPropNames(from))
26
+ if (!__hasOwnProp.call(to, key) && key !== except)
27
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
28
+ }
29
+ return to;
30
+ };
31
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
32
+ // If the importer is in node compatibility mode or this is not an ESM
33
+ // file that has been converted to a CommonJS file using a Babel-
34
+ // compatible transform (i.e. "__esModule" has not been set), then set
35
+ // "default" to the CommonJS "module.exports" for node compatibility.
36
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
37
+ mod
38
+ ));
39
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
+
41
+ export {
42
+ __require,
43
+ __esm,
44
+ __commonJS,
45
+ __export,
46
+ __toESM,
47
+ __toCommonJS
48
+ };
@@ -0,0 +1,31 @@
1
+ // src/adapters/engines/_shared/connectivity-outcome.ts
2
+ function buildConnectivityOutcome(args) {
3
+ const options = [
4
+ {
5
+ kind: "run-it-yourself",
6
+ description: "Run these read-only catalog SELECT statements in your own client to extract the schema.",
7
+ queries: args.runItYourselfQueries
8
+ },
9
+ {
10
+ kind: "consented-install",
11
+ description: `Install the '${args.installTool}' driver or tool from the official source. Nothing is installed automatically \u2014 explicit consent required.`,
12
+ tool: args.installTool,
13
+ docUrl: args.installDocUrl
14
+ },
15
+ {
16
+ kind: "manual-dump",
17
+ description: `Import a combined JSON dump you produced externally. Place the file at: ${args.dumpPath}`,
18
+ outputPath: args.dumpPath
19
+ }
20
+ ];
21
+ return {
22
+ engine: args.engine,
23
+ summary: args.summary,
24
+ attempts: args.attempts,
25
+ options
26
+ };
27
+ }
28
+
29
+ export {
30
+ buildConnectivityOutcome
31
+ };
@@ -0,0 +1,231 @@
1
+ import {
2
+ ConfigError,
3
+ UnsupportedDialectError
4
+ } from "./chunk-TH4OVS4W.js";
5
+
6
+ // src/infra/config/schema.ts
7
+ var VALID_LEVELS = ["off", "metadata", "full"];
8
+ var SUPPORTED_DIALECTS = ["sqlite", "mssql", "pg", "mysql", "mongodb"];
9
+
10
+ // src/infra/config/parse-config.ts
11
+ function isRecord(v) {
12
+ return typeof v === "object" && v !== null && !Array.isArray(v);
13
+ }
14
+ function requireString(obj, field, context) {
15
+ const v = obj[field];
16
+ if (typeof v !== "string" || v.length === 0) {
17
+ throw new ConfigError(
18
+ `${context}: field "${field}" is required and must be a non-empty string.`
19
+ );
20
+ }
21
+ return v;
22
+ }
23
+ function optionalString(obj, field, context) {
24
+ const v = obj[field];
25
+ if (v === void 0) return void 0;
26
+ if (typeof v !== "string") {
27
+ throw new ConfigError(`${context}: field "${field}" must be a string when provided.`);
28
+ }
29
+ return v;
30
+ }
31
+ function parseLevels(raw) {
32
+ if (raw === void 0) return void 0;
33
+ if (!isRecord(raw)) {
34
+ throw new ConfigError(
35
+ 'Config: "levels" must be an object mapping object type names to level strings.'
36
+ );
37
+ }
38
+ const result = {};
39
+ for (const [key, val] of Object.entries(raw)) {
40
+ if (!VALID_LEVELS.includes(val)) {
41
+ throw new ConfigError(
42
+ `Config: levels."${key}" must be one of ${VALID_LEVELS.join(", ")} \u2014 got "${String(val)}".`
43
+ );
44
+ }
45
+ result[key] = val;
46
+ }
47
+ return result;
48
+ }
49
+ function parseSqliteSource(raw) {
50
+ if (!isRecord(raw)) {
51
+ throw new ConfigError('Config: "source" must be an object for dialect "sqlite".');
52
+ }
53
+ const file = requireString(raw, "file", "source (sqlite)");
54
+ return { file };
55
+ }
56
+ function parseMssqlSource(raw) {
57
+ if (!isRecord(raw)) {
58
+ throw new ConfigError('Config: "source" must be an object for dialect "mssql".');
59
+ }
60
+ const server = requireString(raw, "server", "source (mssql)");
61
+ const database = requireString(raw, "database", "source (mssql)");
62
+ const port = optionalString(raw, "port", "source (mssql)");
63
+ const authRaw = raw["auth"];
64
+ let auth;
65
+ if (authRaw !== void 0) {
66
+ if (authRaw !== "sql" && authRaw !== "ntlm" && authRaw !== "integrated") {
67
+ throw new ConfigError(
68
+ 'source (mssql): field "auth" must be one of "sql", "ntlm", or "integrated".'
69
+ );
70
+ }
71
+ auth = authRaw;
72
+ }
73
+ if (auth === "integrated") {
74
+ const result2 = { server, database, auth };
75
+ if (port !== void 0) result2.port = port;
76
+ return result2;
77
+ }
78
+ const user = requireString(raw, "user", "source (mssql)");
79
+ const password = requireString(raw, "password", "source (mssql)");
80
+ const domain = optionalString(raw, "domain", "source (mssql)");
81
+ const resolvedAuth = auth ?? (domain !== void 0 ? "ntlm" : "sql");
82
+ const result = { server, database, user, password, auth: resolvedAuth };
83
+ if (port !== void 0) result.port = port;
84
+ if (domain !== void 0) result.domain = domain;
85
+ return result;
86
+ }
87
+ var ENV_REF_RE = /^\$\{env:[A-Z_][A-Z0-9_]*\}$/;
88
+ function isEnvRef(value) {
89
+ return ENV_REF_RE.test(value);
90
+ }
91
+ function parsePgSource(raw) {
92
+ if (!isRecord(raw)) {
93
+ throw new ConfigError('Config: "source" must be an object for dialect "pg".');
94
+ }
95
+ const host = requireString(raw, "host", "source (pg)");
96
+ const database = requireString(raw, "database", "source (pg)");
97
+ const user = requireString(raw, "user", "source (pg)");
98
+ const password = requireString(raw, "password", "source (pg)");
99
+ if (!isEnvRef(password)) {
100
+ throw new ConfigError(
101
+ 'source (pg): field "password" must be a ${env:VAR} reference, not a literal value. Use an environment variable reference such as ${env:PG_PASSWORD}.'
102
+ );
103
+ }
104
+ const port = optionalString(raw, "port", "source (pg)");
105
+ const ssl = optionalString(raw, "ssl", "source (pg)");
106
+ const schema = optionalString(raw, "schema", "source (pg)");
107
+ const result = { host, database, user, password };
108
+ if (port !== void 0) result.port = port;
109
+ if (ssl !== void 0) result.ssl = ssl;
110
+ if (schema !== void 0) result.schema = schema;
111
+ return result;
112
+ }
113
+ function parseMysqlSource(raw) {
114
+ if (!isRecord(raw)) {
115
+ throw new ConfigError('Config: "source" must be an object for dialect "mysql".');
116
+ }
117
+ const host = requireString(raw, "host", "source (mysql)");
118
+ const database = requireString(raw, "database", "source (mysql)");
119
+ const user = requireString(raw, "user", "source (mysql)");
120
+ const password = requireString(raw, "password", "source (mysql)");
121
+ if (!isEnvRef(password)) {
122
+ throw new ConfigError(
123
+ 'source (mysql): field "password" must be a ${env:VAR} reference, not a literal value. Use an environment variable reference such as ${env:MYSQL_PASSWORD}.'
124
+ );
125
+ }
126
+ const port = optionalString(raw, "port", "source (mysql)");
127
+ const ssl = optionalString(raw, "ssl", "source (mysql)");
128
+ const result = { host, database, user, password };
129
+ if (port !== void 0) result.port = port;
130
+ if (ssl !== void 0) result.ssl = ssl;
131
+ return result;
132
+ }
133
+ function parseMongodbSource(raw) {
134
+ if (!isRecord(raw)) {
135
+ throw new ConfigError('Config: "source" must be an object for dialect "mongodb".');
136
+ }
137
+ const uri = requireString(raw, "uri", "source (mongodb)");
138
+ const database = requireString(raw, "database", "source (mongodb)");
139
+ if (!isEnvRef(uri)) {
140
+ throw new ConfigError(
141
+ 'source (mongodb): field "uri" must be a ${env:VAR} reference, not a literal value. Use an environment variable reference such as ${env:MONGODB_URI}.'
142
+ );
143
+ }
144
+ const sampleSizeRaw = raw["sampleSize"];
145
+ let sampleSize;
146
+ if (sampleSizeRaw !== void 0) {
147
+ if (typeof sampleSizeRaw !== "number" || !Number.isFinite(sampleSizeRaw) || sampleSizeRaw <= 0) {
148
+ throw new ConfigError(
149
+ 'source (mongodb): field "sampleSize" must be a positive number when provided.'
150
+ );
151
+ }
152
+ sampleSize = sampleSizeRaw;
153
+ }
154
+ const tlsRaw = raw["tls"];
155
+ let tls;
156
+ if (tlsRaw !== void 0) {
157
+ if (typeof tlsRaw !== "boolean") {
158
+ throw new ConfigError(
159
+ 'source (mongodb): field "tls" must be a boolean when provided.'
160
+ );
161
+ }
162
+ tls = tlsRaw;
163
+ }
164
+ const result = { uri, database };
165
+ if (sampleSize !== void 0) result.sampleSize = sampleSize;
166
+ if (tls !== void 0) result.tls = tls;
167
+ return result;
168
+ }
169
+ function parseConfig(raw) {
170
+ if (!isRecord(raw)) {
171
+ throw new ConfigError(
172
+ "Config: root must be a JSON object. Got " + (raw === null ? "null" : typeof raw) + "."
173
+ );
174
+ }
175
+ const dialectRaw = raw["dialect"];
176
+ if (typeof dialectRaw !== "string" || dialectRaw.length === 0) {
177
+ throw new ConfigError(
178
+ `Config: field "dialect" is required and must be a non-empty string (supported: ${SUPPORTED_DIALECTS.join(", ")}).`
179
+ );
180
+ }
181
+ if (!SUPPORTED_DIALECTS.includes(dialectRaw)) {
182
+ throw new UnsupportedDialectError(dialectRaw);
183
+ }
184
+ if (!("source" in raw)) {
185
+ throw new ConfigError('Config: field "source" is required.');
186
+ }
187
+ const levels = parseLevels(raw["levels"]);
188
+ switch (dialectRaw) {
189
+ case "sqlite": {
190
+ const source = parseSqliteSource(raw["source"]);
191
+ const driverRaw = raw["driver"];
192
+ let driver;
193
+ if (driverRaw !== void 0) {
194
+ if (driverRaw !== "better-sqlite3" && driverRaw !== "node:sqlite") {
195
+ throw new ConfigError(
196
+ 'Config: "driver" must be one of "better-sqlite3" or "node:sqlite".'
197
+ );
198
+ }
199
+ driver = driverRaw;
200
+ }
201
+ const cfg = levels !== void 0 ? driver !== void 0 ? { dialect: "sqlite", source, levels, driver } : { dialect: "sqlite", source, levels } : driver !== void 0 ? { dialect: "sqlite", source, driver } : { dialect: "sqlite", source };
202
+ return cfg;
203
+ }
204
+ case "mssql": {
205
+ const source = parseMssqlSource(raw["source"]);
206
+ const cfg = levels !== void 0 ? { dialect: "mssql", source, levels } : { dialect: "mssql", source };
207
+ return cfg;
208
+ }
209
+ case "pg": {
210
+ const source = parsePgSource(raw["source"]);
211
+ const cfg = levels !== void 0 ? { dialect: "pg", source, levels } : { dialect: "pg", source };
212
+ return cfg;
213
+ }
214
+ case "mysql": {
215
+ const source = parseMysqlSource(raw["source"]);
216
+ const cfg = levels !== void 0 ? { dialect: "mysql", source, levels } : { dialect: "mysql", source };
217
+ return cfg;
218
+ }
219
+ case "mongodb": {
220
+ const source = parseMongodbSource(raw["source"]);
221
+ const cfg = levels !== void 0 ? { dialect: "mongodb", source, levels } : { dialect: "mongodb", source };
222
+ return cfg;
223
+ }
224
+ default:
225
+ throw new UnsupportedDialectError(dialectRaw);
226
+ }
227
+ }
228
+
229
+ export {
230
+ parseConfig
231
+ };