@dbsp/cli 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,1454 @@
1
+ import {
2
+ ReplEngine,
3
+ getDatabaseName
4
+ } from "./chunk-ZSGVJFWG.js";
5
+ import {
6
+ config
7
+ } from "./chunk-U5DSGBS2.js";
8
+
9
+ // src/repl/index.tsx
10
+ import { Box as Box10, render, useApp, useInput as useInput3 } from "ink";
11
+ import React10, {
12
+ useCallback as useCallback2,
13
+ useEffect as useEffect3,
14
+ useMemo as useMemo2,
15
+ useRef as useRef2,
16
+ useState as useState3
17
+ } from "react";
18
+
19
+ // src/repl/components/CompletionDisplay.tsx
20
+ import React from "react";
21
+ import { Box, Text } from "ink";
22
+ var TYPE_ICONS = {
23
+ table: "\u{1F5C3}\uFE0F",
24
+ column: "\u{1F4CB}",
25
+ keyword: "\u{1F511}",
26
+ command: "\u26A1",
27
+ relation: "\u{1F517}"
28
+ };
29
+ var TYPE_COLORS = {
30
+ table: "cyan",
31
+ column: "blue",
32
+ keyword: "yellow",
33
+ command: "magenta",
34
+ relation: "green"
35
+ };
36
+ function CompletionDisplay({
37
+ suggestions,
38
+ maxItems = 8,
39
+ selectedIndex = -1
40
+ }) {
41
+ if (suggestions.length === 0) {
42
+ return null;
43
+ }
44
+ let startIndex = 0;
45
+ if (selectedIndex >= 0) {
46
+ const halfWindow = Math.floor(maxItems / 2);
47
+ startIndex = Math.max(
48
+ 0,
49
+ Math.min(selectedIndex - halfWindow, suggestions.length - maxItems)
50
+ );
51
+ }
52
+ startIndex = Math.max(0, startIndex);
53
+ const endIndex = Math.min(startIndex + maxItems, suggestions.length);
54
+ const items = suggestions.slice(startIndex, endIndex);
55
+ const hasBefore = startIndex > 0;
56
+ const hasAfter = endIndex < suggestions.length;
57
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", flexWrap: "wrap", marginTop: 0 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "[Tab]", " "), hasBefore && /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "\u25C0", " "), items.map((suggestion, idx) => {
58
+ const globalIndex = startIndex + idx;
59
+ const isSelected = globalIndex === selectedIndex;
60
+ return /* @__PURE__ */ React.createElement(Box, { key: globalIndex, marginRight: 1 }, /* @__PURE__ */ React.createElement(Text, null, TYPE_ICONS[suggestion.type], " "), /* @__PURE__ */ React.createElement(
61
+ Text,
62
+ {
63
+ color: isSelected ? "white" : TYPE_COLORS[suggestion.type],
64
+ ...isSelected && { backgroundColor: "blue" },
65
+ bold: isSelected
66
+ },
67
+ suggestion.label
68
+ ));
69
+ }), hasAfter && /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "\u25B6 +", suggestions.length - endIndex));
70
+ }
71
+
72
+ // src/repl/components/Header.tsx
73
+ import React2 from "react";
74
+ import { Box as Box2, Text as Text2 } from "ink";
75
+ var DIALECT_DISPLAY = {
76
+ postgresql: "PG",
77
+ mysql: "MySQL",
78
+ sqlite: "SQLite",
79
+ mssql: "MSSQL",
80
+ duckdb: "DuckDB"
81
+ };
82
+ var STRATEGY_DISPLAY = {
83
+ auto: "auto",
84
+ join: "join",
85
+ subquery: "sep",
86
+ cte: "cte",
87
+ lateral: "lat",
88
+ json_agg: "json"
89
+ };
90
+ function Header({
91
+ schemaPath,
92
+ mode,
93
+ tableCount,
94
+ relationCount,
95
+ dialect,
96
+ includeStrategy,
97
+ aliasingMode,
98
+ connected,
99
+ execMode,
100
+ databaseName,
101
+ schemaName,
102
+ parseMode,
103
+ explainMode
104
+ }) {
105
+ return /* @__PURE__ */ React2.createElement(
106
+ Box2,
107
+ {
108
+ borderStyle: "round",
109
+ borderColor: "cyan",
110
+ paddingX: 2,
111
+ marginBottom: 1,
112
+ flexDirection: "column"
113
+ },
114
+ /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "\u{1F50D} db-semantic-planner REPL"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | "), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, ".help for commands"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | "), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "Ctrl+C to exit")),
115
+ /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "Schema: "), /* @__PURE__ */ React2.createElement(Text2, { color: "white" }, schemaPath), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " ", "(", tableCount, " tables, ", relationCount, " relations)"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | Mode: "), /* @__PURE__ */ React2.createElement(Text2, { color: mode === "natural" ? "green" : "yellow" }, mode), schemaName && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | Schema: "), /* @__PURE__ */ React2.createElement(Text2, { color: "cyan" }, schemaName))),
116
+ /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "Dialect: "), /* @__PURE__ */ React2.createElement(Text2, { color: "blue" }, DIALECT_DISPLAY[dialect]), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | Strategy: "), /* @__PURE__ */ React2.createElement(Text2, { color: "magenta" }, STRATEGY_DISPLAY[includeStrategy]), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | Alias: "), /* @__PURE__ */ React2.createElement(Text2, { color: aliasingMode === "always" ? "cyan" : "yellow" }, aliasingMode === "always" ? "all" : "collision"), parseMode && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | "), /* @__PURE__ */ React2.createElement(Text2, { color: "blue" }, "[parse]")), explainMode && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | "), /* @__PURE__ */ React2.createElement(Text2, { color: "yellow" }, "[explain]")), connected && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | "), /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "DB: ", databaseName)), connected && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " | "), /* @__PURE__ */ React2.createElement(Text2, { color: execMode ? "green" : "gray" }, "[exec: ", execMode ? "ON" : "OFF", "]")))
117
+ );
118
+ }
119
+
120
+ // src/repl/components/HelpDisplay.tsx
121
+ import React3 from "react";
122
+ import { Box as Box3, Text as Text3 } from "ink";
123
+ var DOT_COMMANDS = [
124
+ { command: ".help", description: "Show this help" },
125
+ { command: ".tables", description: "List all tables in schema" },
126
+ {
127
+ command: ".schema [table]",
128
+ description: "Show schema info (or specific table)"
129
+ },
130
+ { command: ".relations", description: "List all relations" },
131
+ {
132
+ command: ".history",
133
+ description: "Show command history (\u2191/\u2193 to navigate, Ctrl+R to search)"
134
+ },
135
+ {
136
+ command: ".sql",
137
+ description: "SQL mode: input is raw SQL, ! escapes to natural"
138
+ },
139
+ {
140
+ command: ".natural",
141
+ description: "Natural mode: input is parsed, ! escapes to SQL"
142
+ },
143
+ {
144
+ command: ".use [schema]",
145
+ description: "Set schema for queries (empty to clear)"
146
+ },
147
+ { command: ".split", description: "Toggle split view (schema | query)" },
148
+ {
149
+ command: ".aliasing",
150
+ description: "Toggle column aliasing mode (always/onCollision)"
151
+ },
152
+ {
153
+ command: ".strategy [mode]",
154
+ description: "Show/set include strategy (auto|join|separate|cte|lateral|json_agg)"
155
+ },
156
+ {
157
+ command: ".dialect [name]",
158
+ description: "Show/set SQL dialect (postgresql|mysql|sqlite|mssql|duckdb)"
159
+ },
160
+ {
161
+ command: ".exec [on|off]",
162
+ description: "Toggle execution mode (requires --db connection)"
163
+ },
164
+ {
165
+ command: ".import <file>",
166
+ description: "Execute SQL file (DDL, seed data)"
167
+ },
168
+ {
169
+ command: ".parse [on|off]",
170
+ description: "Toggle parse tree (AST) output for queries"
171
+ },
172
+ {
173
+ command: ".explain [on|off]",
174
+ description: "Toggle EXPLAIN prefix for queries"
175
+ },
176
+ {
177
+ command: ".table [option] [value]",
178
+ description: "Configure table display (borders, overflow, headers, padding)"
179
+ },
180
+ { command: ".clear", description: "Clear screen and output" },
181
+ { command: ".exit / .quit", description: "Exit REPL" }
182
+ ];
183
+ var NATURAL_SYNTAX = [
184
+ {
185
+ pattern: "<table>",
186
+ example: "users",
187
+ description: "Select all from table"
188
+ },
189
+ {
190
+ pattern: "<table> where <cond>",
191
+ example: "users where active = true",
192
+ description: "Filter results"
193
+ },
194
+ {
195
+ pattern: "<table> include <rel>",
196
+ example: "users include posts",
197
+ description: "Include related data"
198
+ },
199
+ {
200
+ pattern: "<table> limit <n>",
201
+ example: "users limit 10",
202
+ description: "Limit results"
203
+ },
204
+ {
205
+ pattern: "!<query>",
206
+ example: "!SELECT... or !users",
207
+ description: "Mode escape: SQL in natural mode, natural in SQL mode"
208
+ }
209
+ ];
210
+ function SimpleTable({
211
+ data,
212
+ columns
213
+ }) {
214
+ return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, data.map((row, i) => /* @__PURE__ */ React3.createElement(Box3, { key: i }, columns.map((col, j) => /* @__PURE__ */ React3.createElement(Box3, { key: col, width: col === "description" ? 50 : 25 }, /* @__PURE__ */ React3.createElement(Text3, { color: j === 0 ? "cyan" : "white" }, row[col]))))));
215
+ }
216
+ function HelpDisplay() {
217
+ return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "yellow" }, "\u{1F4DA} Dot Commands:"), /* @__PURE__ */ React3.createElement(Box3, { marginTop: 1, marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(SimpleTable, { data: DOT_COMMANDS, columns: ["command", "description"] })), /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "\u{1F4DD} Natural Query Syntax:"), /* @__PURE__ */ React3.createElement(Box3, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(
218
+ SimpleTable,
219
+ {
220
+ data: NATURAL_SYNTAX,
221
+ columns: ["pattern", "example", "description"]
222
+ }
223
+ )), /* @__PURE__ */ React3.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, "Tip: Use .sql to switch to raw SQL mode, .natural to return")));
224
+ }
225
+
226
+ // src/repl/components/InputPrompt.tsx
227
+ import React5 from "react";
228
+ import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
229
+ import { useCallback, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
230
+
231
+ // src/repl/components/EnhancedTextInput.tsx
232
+ import { Box as Box4, Text as Text4, useInput, useStdin, useStdout } from "ink";
233
+ import React4, { useEffect, useRef, useState } from "react";
234
+ function findPrevWordStart(text, cursor) {
235
+ if (cursor <= 0) return 0;
236
+ let pos = cursor - 1;
237
+ while (pos > 0 && text[pos] === " ") {
238
+ pos--;
239
+ }
240
+ while (pos > 0 && text[pos - 1] !== " ") {
241
+ pos--;
242
+ }
243
+ return pos;
244
+ }
245
+ function findNextWordEnd(text, cursor) {
246
+ if (cursor >= text.length) return text.length;
247
+ let pos = cursor;
248
+ while (pos < text.length && text[pos] === " ") {
249
+ pos++;
250
+ }
251
+ while (pos < text.length && text[pos] !== " ") {
252
+ pos++;
253
+ }
254
+ return pos;
255
+ }
256
+ function EnhancedTextInput({
257
+ value: controlledValue,
258
+ defaultValue = "",
259
+ placeholder = "",
260
+ onChange,
261
+ onSubmit,
262
+ isDisabled = false,
263
+ isFocused = true
264
+ }) {
265
+ const [internalValue, setInternalValue] = useState(defaultValue);
266
+ const [cursor, setCursor] = useState(defaultValue.length);
267
+ const valueRef = useRef(defaultValue);
268
+ const cursorRef = useRef(defaultValue.length);
269
+ const { stdout } = useStdout();
270
+ const lastRawInputRef = useRef("");
271
+ const { stdin } = useStdin();
272
+ useEffect(() => {
273
+ if (!stdin || isDisabled || !isFocused) return;
274
+ const handleRawData = (data) => {
275
+ lastRawInputRef.current = data.toString();
276
+ };
277
+ stdin.on("data", handleRawData);
278
+ return () => {
279
+ stdin.off("data", handleRawData);
280
+ };
281
+ }, [stdin, isDisabled, isFocused]);
282
+ const value = controlledValue ?? internalValue;
283
+ useEffect(() => {
284
+ if (controlledValue !== void 0) {
285
+ const synced = Math.min(cursorRef.current, controlledValue.length);
286
+ cursorRef.current = synced;
287
+ setCursor(synced);
288
+ valueRef.current = controlledValue;
289
+ }
290
+ }, [controlledValue]);
291
+ useEffect(() => {
292
+ if (controlledValue === void 0) {
293
+ setInternalValue(defaultValue);
294
+ setCursor(defaultValue.length);
295
+ valueRef.current = defaultValue;
296
+ cursorRef.current = defaultValue.length;
297
+ }
298
+ }, [defaultValue, controlledValue]);
299
+ const updateValue = (newValue, newCursor) => {
300
+ const nc = newCursor ?? newValue.length;
301
+ valueRef.current = newValue;
302
+ cursorRef.current = nc;
303
+ if (controlledValue === void 0) {
304
+ setInternalValue(newValue);
305
+ }
306
+ setCursor(nc);
307
+ onChange?.(newValue);
308
+ };
309
+ useInput(
310
+ (input, key) => {
311
+ if (isDisabled || !isFocused) return;
312
+ const val = valueRef.current;
313
+ const cur = cursorRef.current;
314
+ if (key.return) {
315
+ if (key.shift) {
316
+ const newValue = `${val.slice(0, cur)}
317
+ ${val.slice(cur)}`;
318
+ updateValue(newValue, cur + 1);
319
+ return;
320
+ }
321
+ if (val.endsWith("\\")) {
322
+ const newValue = `${val.slice(0, -1)}
323
+ `;
324
+ updateValue(newValue, newValue.length);
325
+ return;
326
+ }
327
+ const submitted = val;
328
+ valueRef.current = "";
329
+ cursorRef.current = 0;
330
+ onSubmit?.(submitted);
331
+ return;
332
+ }
333
+ if (key.escape) {
334
+ return;
335
+ }
336
+ if (key.home) {
337
+ cursorRef.current = 0;
338
+ setCursor(0);
339
+ return;
340
+ }
341
+ if (key.end) {
342
+ cursorRef.current = val.length;
343
+ setCursor(val.length);
344
+ return;
345
+ }
346
+ if (key.leftArrow) {
347
+ if (key.ctrl || key.meta) {
348
+ const nc = findPrevWordStart(val, cur);
349
+ cursorRef.current = nc;
350
+ setCursor(nc);
351
+ } else {
352
+ const nc = Math.max(0, cur - 1);
353
+ cursorRef.current = nc;
354
+ setCursor(nc);
355
+ }
356
+ return;
357
+ }
358
+ if (key.rightArrow) {
359
+ if (key.ctrl || key.meta) {
360
+ const nc = findNextWordEnd(val, cur);
361
+ cursorRef.current = nc;
362
+ setCursor(nc);
363
+ } else {
364
+ const nc = Math.min(val.length, cur + 1);
365
+ cursorRef.current = nc;
366
+ setCursor(nc);
367
+ }
368
+ return;
369
+ }
370
+ if (key.ctrl && input === "a") {
371
+ cursorRef.current = 0;
372
+ setCursor(0);
373
+ return;
374
+ }
375
+ if (key.ctrl && input === "e") {
376
+ cursorRef.current = val.length;
377
+ setCursor(val.length);
378
+ return;
379
+ }
380
+ if (key.meta && input === "b") {
381
+ const nc = findPrevWordStart(val, cur);
382
+ cursorRef.current = nc;
383
+ setCursor(nc);
384
+ return;
385
+ }
386
+ if (key.meta && input === "f") {
387
+ const nc = findNextWordEnd(val, cur);
388
+ cursorRef.current = nc;
389
+ setCursor(nc);
390
+ return;
391
+ }
392
+ const rawInput = lastRawInputRef.current;
393
+ if (rawInput === "\x1B[3~" || rawInput.startsWith("\x1B[3~")) {
394
+ if (cur < val.length) {
395
+ const newValue = val.slice(0, cur) + val.slice(cur + 1);
396
+ updateValue(newValue, cur);
397
+ }
398
+ return;
399
+ }
400
+ const isBackspace = key.backspace || rawInput === "\x7F" || rawInput === "\b" || input === "\x7F" || input === "\b";
401
+ if (isBackspace) {
402
+ if (cur > 0) {
403
+ const newValue = val.slice(0, cur - 1) + val.slice(cur);
404
+ updateValue(newValue, cur - 1);
405
+ }
406
+ return;
407
+ }
408
+ if (key.ctrl && input === "w") {
409
+ const wordStart = findPrevWordStart(val, cur);
410
+ const newValue = val.slice(0, wordStart) + val.slice(cur);
411
+ updateValue(newValue, wordStart);
412
+ return;
413
+ }
414
+ if (key.ctrl && input === "u") {
415
+ const newValue = val.slice(cur);
416
+ updateValue(newValue, 0);
417
+ return;
418
+ }
419
+ if (key.ctrl && input === "k") {
420
+ const newValue = val.slice(0, cur);
421
+ updateValue(newValue, cur);
422
+ return;
423
+ }
424
+ if (key.ctrl && input === "h") {
425
+ if (cur > 0) {
426
+ const newValue = val.slice(0, cur - 1) + val.slice(cur);
427
+ updateValue(newValue, cur - 1);
428
+ }
429
+ return;
430
+ }
431
+ if (key.tab) {
432
+ return;
433
+ }
434
+ if (input && !key.ctrl && !key.meta) {
435
+ if (input.includes("\n") || input.includes("\r")) {
436
+ const fullText = val.slice(0, cur) + input + val.slice(cur);
437
+ const parts = fullText.split(/\r\n|\r|\n/);
438
+ const toSubmit = parts.map((l) => l.trim()).filter(Boolean);
439
+ if (toSubmit.length > 0) {
440
+ valueRef.current = "";
441
+ cursorRef.current = 0;
442
+ for (const line of toSubmit) {
443
+ onSubmit?.(line);
444
+ }
445
+ return;
446
+ }
447
+ updateValue("", 0);
448
+ return;
449
+ }
450
+ const newValue = val.slice(0, cur) + input + val.slice(cur);
451
+ updateValue(newValue, cur + input.length);
452
+ }
453
+ },
454
+ { isActive: isFocused && !isDisabled }
455
+ );
456
+ const showPlaceholder = value.length === 0;
457
+ const isMultiline = value.includes("\n");
458
+ if (showPlaceholder) {
459
+ return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { inverse: true }, " "), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, placeholder));
460
+ }
461
+ if (isMultiline) {
462
+ const lines = value.split("\n");
463
+ let charsSoFar = 0;
464
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, lines.map((line, lineIdx) => {
465
+ const lineStart = charsSoFar;
466
+ charsSoFar += line.length + 1;
467
+ const cursorInThisLine = cursor >= lineStart && cursor < lineStart + line.length + 1;
468
+ if (!cursorInThisLine) {
469
+ return /* @__PURE__ */ React4.createElement(Text4, { key: lineIdx }, lineIdx > 0 && /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "... "), line);
470
+ }
471
+ const cursorInLine = cursor - lineStart;
472
+ const before2 = line.slice(0, cursorInLine);
473
+ const at2 = line[cursorInLine] ?? " ";
474
+ const after2 = line.slice(cursorInLine + 1);
475
+ return /* @__PURE__ */ React4.createElement(Text4, { key: lineIdx }, lineIdx > 0 && /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "... "), before2, /* @__PURE__ */ React4.createElement(Text4, { inverse: true }, at2), after2);
476
+ }));
477
+ }
478
+ const termWidth = (stdout?.columns ?? 80) - 4;
479
+ const len = value.length;
480
+ if (len <= termWidth) {
481
+ const beforeCursor = value.slice(0, cursor);
482
+ const atCursor = value[cursor] ?? " ";
483
+ const afterCursor = value.slice(cursor + 1);
484
+ return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, null, beforeCursor), /* @__PURE__ */ React4.createElement(Text4, { inverse: true }, atCursor), /* @__PURE__ */ React4.createElement(Text4, null, afterCursor));
485
+ }
486
+ const ellipsis = "\u2026";
487
+ const usable = termWidth - 2;
488
+ let windowStart = cursor - Math.floor(usable / 2);
489
+ if (windowStart < 0) windowStart = 0;
490
+ if (windowStart + usable > len) windowStart = Math.max(0, len - usable);
491
+ const windowEnd = Math.min(windowStart + usable, len);
492
+ const showLeftEllipsis = windowStart > 0;
493
+ const showRightEllipsis = windowEnd < len;
494
+ const visibleText = value.slice(windowStart, windowEnd);
495
+ const cursorInWindow = cursor - windowStart;
496
+ const before = visibleText.slice(0, cursorInWindow);
497
+ const at = visibleText[cursorInWindow] ?? " ";
498
+ const after = visibleText.slice(cursorInWindow + 1);
499
+ return /* @__PURE__ */ React4.createElement(Box4, null, showLeftEllipsis && /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, ellipsis), /* @__PURE__ */ React4.createElement(Text4, null, before), /* @__PURE__ */ React4.createElement(Text4, { inverse: true }, at), /* @__PURE__ */ React4.createElement(Text4, null, after), showRightEllipsis && /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, ellipsis));
500
+ }
501
+
502
+ // src/repl/components/InputPrompt.tsx
503
+ function InputPrompt({
504
+ onSubmit,
505
+ mode,
506
+ resetKey = 0,
507
+ history,
508
+ onInputChange,
509
+ selectedCompletion,
510
+ onCompletionAccepted,
511
+ applyCompletion
512
+ }) {
513
+ const promptSymbol = mode === "natural" ? ">" : "sql>";
514
+ const promptColor = mode === "natural" ? "green" : "yellow";
515
+ const [currentInput, setCurrentInput] = useState2("");
516
+ const [historyKey, setHistoryKey] = useState2(0);
517
+ const [historyValue, setHistoryValue] = useState2(
518
+ void 0
519
+ );
520
+ const [searchMode, setSearchMode] = useState2(false);
521
+ const [searchQuery, setSearchQuery] = useState2("");
522
+ const [searchMatchIndex, setSearchMatchIndex] = useState2(0);
523
+ const searchMatches = useMemo(() => {
524
+ if (!history || !searchQuery) return [];
525
+ return history.reverseSearch(searchQuery);
526
+ }, [history, searchQuery]);
527
+ const currentMatch = searchMatches[searchMatchIndex] ?? "";
528
+ useEffect2(() => {
529
+ setCurrentInput("");
530
+ setHistoryValue(void 0);
531
+ setHistoryKey((k) => k + 1);
532
+ setSearchMode(false);
533
+ setSearchQuery("");
534
+ setSearchMatchIndex(0);
535
+ history?.resetIndex();
536
+ }, [resetKey, history]);
537
+ useInput2((input, key) => {
538
+ if (!history) return;
539
+ if (key.ctrl && input === "r") {
540
+ if (searchMode) {
541
+ if (searchMatches.length > 0) {
542
+ setSearchMatchIndex((i) => (i + 1) % searchMatches.length);
543
+ }
544
+ } else {
545
+ setSearchMode(true);
546
+ setSearchQuery("");
547
+ setSearchMatchIndex(0);
548
+ }
549
+ return;
550
+ }
551
+ if (searchMode) {
552
+ if (key.escape) {
553
+ setSearchMode(false);
554
+ setSearchQuery("");
555
+ setSearchMatchIndex(0);
556
+ return;
557
+ }
558
+ if (key.return) {
559
+ if (currentMatch) {
560
+ setHistoryValue(currentMatch);
561
+ setHistoryKey((k) => k + 1);
562
+ setCurrentInput(currentMatch);
563
+ onInputChange?.(currentMatch);
564
+ }
565
+ setSearchMode(false);
566
+ setSearchQuery("");
567
+ setSearchMatchIndex(0);
568
+ return;
569
+ }
570
+ return;
571
+ }
572
+ if (key.upArrow) {
573
+ const prev = history.previous(currentInput);
574
+ if (prev !== void 0) {
575
+ setHistoryValue(prev);
576
+ setHistoryKey((k) => k + 1);
577
+ }
578
+ } else if (key.downArrow) {
579
+ const next = history.next();
580
+ if (next !== void 0) {
581
+ setHistoryValue(next);
582
+ setHistoryKey((k) => k + 1);
583
+ }
584
+ }
585
+ });
586
+ const handleSubmit = useCallback(
587
+ (value) => {
588
+ if (selectedCompletion) {
589
+ const newValue = applyCompletion ? applyCompletion(value, selectedCompletion) : selectedCompletion;
590
+ setHistoryValue(newValue);
591
+ setHistoryKey((k) => k + 1);
592
+ setCurrentInput(newValue);
593
+ onInputChange?.(newValue);
594
+ onCompletionAccepted?.();
595
+ return;
596
+ }
597
+ if (history && value.trim()) {
598
+ history.add(value);
599
+ }
600
+ setCurrentInput("");
601
+ setHistoryValue(void 0);
602
+ onSubmit(value);
603
+ },
604
+ [
605
+ history,
606
+ onSubmit,
607
+ selectedCompletion,
608
+ onCompletionAccepted,
609
+ onInputChange,
610
+ applyCompletion
611
+ ]
612
+ );
613
+ const handleChange = useCallback(
614
+ (value) => {
615
+ setCurrentInput(value);
616
+ onInputChange?.(value);
617
+ },
618
+ [onInputChange]
619
+ );
620
+ const combinedKey = `${resetKey}-${historyKey}`;
621
+ const handleSearchChange = useCallback((value) => {
622
+ setSearchQuery(value);
623
+ setSearchMatchIndex(0);
624
+ }, []);
625
+ if (searchMode) {
626
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, currentMatch && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "\u2192 "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, currentMatch)), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta", bold: true }, "(reverse-i-search)`"), /* @__PURE__ */ React5.createElement(
627
+ EnhancedTextInput,
628
+ {
629
+ key: `search-${combinedKey}`,
630
+ defaultValue: searchQuery,
631
+ onChange: handleSearchChange,
632
+ placeholder: ""
633
+ }
634
+ ), /* @__PURE__ */ React5.createElement(Text5, { color: "magenta", bold: true }, "':"), searchMatches.length > 0 && /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, " ", "(", searchMatchIndex + 1, "/", searchMatches.length, ")")));
635
+ }
636
+ return /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: promptColor, bold: true }, promptSymbol, " "), /* @__PURE__ */ React5.createElement(
637
+ EnhancedTextInput,
638
+ {
639
+ key: combinedKey,
640
+ defaultValue: historyValue ?? "",
641
+ onSubmit: handleSubmit,
642
+ onChange: handleChange,
643
+ placeholder: mode === "natural" ? "Enter query (e.g., users where active = true) or .help" : "Enter SQL query or .natural to switch mode"
644
+ }
645
+ ));
646
+ }
647
+
648
+ // src/repl/components/InspectionPanel.tsx
649
+ import { Box as Box8, Text as Text8 } from "ink";
650
+ import React8 from "react";
651
+
652
+ // src/repl/result-formatter.tsx
653
+ import { makeTable } from "@oclif/table";
654
+ import { Box as Box6, Text as Text6 } from "ink";
655
+ import React6 from "react";
656
+ function formatValue(value) {
657
+ if (value === null) return "NULL";
658
+ if (value === void 0) return "";
659
+ if (typeof value === "string") return value;
660
+ if (typeof value === "number") return String(value);
661
+ if (typeof value === "boolean") return value ? "true" : "false";
662
+ if (value instanceof Date) return value.toISOString();
663
+ if (Array.isArray(value)) return JSON.stringify(value);
664
+ if (typeof value === "object") return JSON.stringify(value);
665
+ return String(value);
666
+ }
667
+ function ExecutionResultDisplay({
668
+ result
669
+ }) {
670
+ if (result.error) {
671
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "red" }, "\u274C Database Error"), /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, result.error), /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true }, "(", result.executionTimeMs, "ms)"));
672
+ }
673
+ if (result.rows.length === 0) {
674
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray" }, "0 rows (", result.executionTimeMs, "ms)"));
675
+ }
676
+ const { columns, rows, rowCount, executionTimeMs, truncated } = result;
677
+ const tableConfig = config.getTable();
678
+ const formattedData = rows.map((row) => {
679
+ const formatted = {};
680
+ for (const col of columns) {
681
+ formatted[col] = formatValue(row[col]);
682
+ }
683
+ return formatted;
684
+ });
685
+ const columnConfig = columns.map((col) => ({
686
+ key: col,
687
+ name: col,
688
+ overflow: tableConfig.overflow
689
+ }));
690
+ const headerFormatter = tableConfig.headerFormatter === "none" ? (h) => h : tableConfig.headerFormatter;
691
+ const tableOutput = makeTable({
692
+ data: formattedData,
693
+ columns: columnConfig,
694
+ borderStyle: tableConfig.borderStyle,
695
+ headerOptions: {
696
+ formatter: headerFormatter
697
+ },
698
+ padding: tableConfig.padding
699
+ });
700
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React6.createElement(Text6, null, tableOutput), /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, rowCount, " row", rowCount !== 1 ? "s" : "", " (", executionTimeMs, "ms)", truncated && /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " (truncated, showing first 100)")));
701
+ }
702
+
703
+ // src/repl/components/OutputDisplay.tsx
704
+ import React7 from "react";
705
+ import { Box as Box7, Text as Text7 } from "ink";
706
+ function SqlOutput({
707
+ sql,
708
+ params,
709
+ label = "\u{1F4DD} Generated SQL:"
710
+ }) {
711
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "yellow" }, label), /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, sql)), params.length > 0 && /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "Parameters: "), /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, JSON.stringify(params))));
712
+ }
713
+ function SeparateQueriesOutput({
714
+ queries
715
+ }) {
716
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginY: 1 }, queries.map((q, i) => /* @__PURE__ */ React7.createElement(Box7, { key: i, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, "\u{1F4CE} Separate Query (", q.relation, "):"), /* @__PURE__ */ React7.createElement(
717
+ Box7,
718
+ {
719
+ borderStyle: "single",
720
+ borderColor: "cyan",
721
+ paddingX: 1,
722
+ marginTop: 1
723
+ },
724
+ /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, q.sql)
725
+ ), q.params.length > 0 && /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "Parameters: "), /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, JSON.stringify(q.params))))));
726
+ }
727
+ function PlanOutput({
728
+ plan,
729
+ verbosity = "normal"
730
+ }) {
731
+ const hasDecisions = plan.decisions.length > 0;
732
+ const hasWarnings = plan.warnings.length > 0;
733
+ const hasCtes = plan.cteCount > 0;
734
+ if (verbosity === "compact" || !hasDecisions && !hasCtes && !hasWarnings) {
735
+ return /* @__PURE__ */ React7.createElement(Box7, { marginY: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "magenta" }, "\u{1F4CB} Plan:", " "), /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, plan.rootTable || plan.strategy, hasDecisions ? ` (${plan.decisions.length} decision${plan.decisions.length > 1 ? "s" : ""}${hasWarnings ? `, ${plan.warnings.length} warning${plan.warnings.length > 1 ? "s" : ""}` : ""})` : " (no decisions)"), plan.planningTimeMs > 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " \u23F1 ", plan.planningTimeMs.toFixed(1), "ms"));
736
+ }
737
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "magenta" }, "\u{1F4CB} Query Plan:"), /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, plan.rootTable && /* @__PURE__ */ React7.createElement(Text7, null, "Root: ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, plan.rootTable)), plan.tables.length > 0 && /* @__PURE__ */ React7.createElement(Text7, null, "Tables: ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, plan.tables.join(", "))), hasDecisions && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Decisions:"), plan.decisions.map((d, i) => /* @__PURE__ */ React7.createElement(Box7, { key: i, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " \u2022 "), /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, d.type), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, ": "), /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, d.context), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " \u2014 "), /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, d.choice), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " (", d.reasoning, ")")), verbosity === "verbose" && /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 4, flexDirection: "column" }, d.alternatives && d.alternatives.length > 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "\u251C Alternatives: ", d.alternatives.join(", ")), d.foreignKey && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "\u251C FK:", " ", Array.isArray(d.foreignKey) ? d.foreignKey.join(", ") : d.foreignKey), d.relationType && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "\u251C Relation: ", d.relationType), (d.intentPath || d.relationPath) && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "\u2514 Path: ", d.intentPath ?? d.relationPath))))), verbosity === "verbose" && plan.ctes && plan.ctes.length > 0 ? /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "CTEs:"), plan.ctes.map((c, i) => /* @__PURE__ */ React7.createElement(Box7, { key: i, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " \u2022 "), /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, c.name), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " ", "\u2014 ", c.purpose, c.recursive ? " (recursive)" : "")), c.referencedBy && c.referencedBy.length > 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " \u2514 Referenced by: ", c.referencedBy.join(", "))))) : hasCtes && /* @__PURE__ */ React7.createElement(Text7, null, "CTEs: ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, plan.cteCount, " extracted")), hasWarnings && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, "\u26A0\uFE0F Warnings:"), plan.warnings.map((w, i) => /* @__PURE__ */ React7.createElement(Box7, { key: i, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, " ", "\u2022 ", w.code ? `${w.code}: ` : "", w.message), w.suggestion && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " \u2192 ", w.suggestion), verbosity === "verbose" && w.relatedDecision && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " ", "\u2514 Related decision: ", w.relatedDecision)))), verbosity === "verbose" && plan.metadata && /* @__PURE__ */ React7.createElement(Text7, { color: "gray", dimColor: true }, "\u{1F4CA} Metadata: ", plan.metadata.relationsAnalyzed, " relation", plan.metadata.relationsAnalyzed !== 1 ? "s" : "", " analyzed", plan.metadata.isAmbiguous ? ` | ambiguous (${plan.metadata.ambiguousOptions?.join(", ")})` : " | not ambiguous"), plan.planningTimeMs > 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "\u23F1 Planning: ", plan.planningTimeMs.toFixed(1), "ms")));
738
+ }
739
+ function ErrorOutput({ message }) {
740
+ return /* @__PURE__ */ React7.createElement(Box7, { marginY: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "\u274C Error: ", message));
741
+ }
742
+ function formatParseTree(parsed) {
743
+ const lines = [];
744
+ const formatValue2 = (value, indent = 2) => {
745
+ const pad = " ".repeat(indent);
746
+ if (value === null) return "null";
747
+ if (value === void 0) return "undefined";
748
+ if (typeof value === "string") return `"${value}"`;
749
+ if (typeof value === "number" || typeof value === "boolean")
750
+ return String(value);
751
+ if (Array.isArray(value)) {
752
+ if (value.length === 0) return "[]";
753
+ const items = value.map((v) => formatValue2(v, indent + 2)).join(", ");
754
+ return `[${items}]`;
755
+ }
756
+ if (typeof value === "object") {
757
+ const entries = Object.entries(value);
758
+ if (entries.length === 0) return "{}";
759
+ const formatted = entries.map(([k, v]) => `${pad} ${k}: ${formatValue2(v, indent + 2)}`).join("\n");
760
+ return `{
761
+ ${formatted}
762
+ ${pad}}`;
763
+ }
764
+ return String(value);
765
+ };
766
+ lines.push("ParsedQuery {");
767
+ const obj = parsed;
768
+ for (const [key, value] of Object.entries(obj)) {
769
+ if (value !== void 0) {
770
+ lines.push(` ${key}: ${formatValue2(value)}`);
771
+ }
772
+ }
773
+ lines.push("}");
774
+ return lines.join("\n");
775
+ }
776
+ function ParseTreeOutput({ parsed }) {
777
+ const treeText = formatParseTree(parsed);
778
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "blue" }, "\u{1F333} Parse Tree (AST):"), /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "blue", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "white" }, treeText)));
779
+ }
780
+ function OutputDisplay({ result, planVerbosity }) {
781
+ if (!result) return null;
782
+ if (result.error) {
783
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(ErrorOutput, { message: result.error }), result.parsedQuery !== void 0 && /* @__PURE__ */ React7.createElement(ParseTreeOutput, { parsed: result.parsedQuery }));
784
+ }
785
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, result.parsedQuery !== void 0 && /* @__PURE__ */ React7.createElement(ParseTreeOutput, { parsed: result.parsedQuery }), /* @__PURE__ */ React7.createElement(SqlOutput, { sql: result.sql, params: result.params, label: "\u{1F4DD} Main SQL:" }), result.separateQueries && result.separateQueries.length > 0 && /* @__PURE__ */ React7.createElement(SeparateQueriesOutput, { queries: result.separateQueries }), result.plan && /* @__PURE__ */ React7.createElement(PlanOutput, { plan: result.plan, verbosity: planVerbosity ?? "normal" }));
786
+ }
787
+
788
+ // src/repl/components/InspectionPanel.tsx
789
+ var ALL_VIEWS = ["sql", "plan", "results", "params", "dump"];
790
+ var TAB_LABELS = {
791
+ sql: "SQL",
792
+ plan: "Plan",
793
+ results: "Results",
794
+ params: "Params",
795
+ dump: "Dump"
796
+ };
797
+ function TabBar({
798
+ activeView,
799
+ execMode
800
+ }) {
801
+ return /* @__PURE__ */ React8.createElement(Box8, null, ALL_VIEWS.map((v, i) => {
802
+ const isActive = v === activeView;
803
+ const isDimmed = v === "results" && !execMode;
804
+ return /* @__PURE__ */ React8.createElement(React8.Fragment, { key: v }, i > 0 && /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, " "), isActive ? /* @__PURE__ */ React8.createElement(Text8, { bold: true, inverse: true, color: "blue" }, ` ${TAB_LABELS[v]} `) : /* @__PURE__ */ React8.createElement(Text8, { color: isDimmed ? "gray" : "white", dimColor: isDimmed }, ` ${TAB_LABELS[v]} `));
805
+ }), /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, " Tab\u21B9 cycle \xB7 Esc close"));
806
+ }
807
+ function PanelContent({
808
+ view,
809
+ queryResult,
810
+ executionResult,
811
+ execMode
812
+ }) {
813
+ if (!queryResult && !executionResult) {
814
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "No query result to inspect.");
815
+ }
816
+ switch (view) {
817
+ case "sql": {
818
+ if (!queryResult) return /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "No SQL available.");
819
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(
820
+ SqlOutput,
821
+ {
822
+ sql: queryResult.sql,
823
+ params: queryResult.params,
824
+ label: "Main SQL:"
825
+ }
826
+ ), queryResult.separateQueries && queryResult.separateQueries.length > 0 && /* @__PURE__ */ React8.createElement(SeparateQueriesOutput, { queries: queryResult.separateQueries }));
827
+ }
828
+ case "plan": {
829
+ if (!queryResult?.plan)
830
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "No query plan available.");
831
+ return /* @__PURE__ */ React8.createElement(PlanOutput, { plan: queryResult.plan, verbosity: "verbose" });
832
+ }
833
+ case "results": {
834
+ if (!execMode)
835
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "Not in execution mode. Use .exec to enable.");
836
+ if (!executionResult)
837
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "No execution results yet. Run a query with .exec enabled.");
838
+ return /* @__PURE__ */ React8.createElement(ExecutionResultDisplay, { result: executionResult });
839
+ }
840
+ case "params": {
841
+ if (!queryResult)
842
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "No parameters available.");
843
+ const params = queryResult.params;
844
+ if (params.length === 0) {
845
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "No parameters (static query).");
846
+ }
847
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, params.map((p, i) => /* @__PURE__ */ React8.createElement(Text8, { key: i }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "$", i + 1), /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, " = "), /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, JSON.stringify(p)))));
848
+ }
849
+ case "dump": {
850
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, queryResult && /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
851
+ SqlOutput,
852
+ {
853
+ sql: queryResult.sql,
854
+ params: queryResult.params,
855
+ label: "Main SQL:"
856
+ }
857
+ ), queryResult.separateQueries && queryResult.separateQueries.length > 0 && /* @__PURE__ */ React8.createElement(
858
+ SeparateQueriesOutput,
859
+ {
860
+ queries: queryResult.separateQueries
861
+ }
862
+ ), queryResult.plan && /* @__PURE__ */ React8.createElement(PlanOutput, { plan: queryResult.plan, verbosity: "verbose" }), queryResult.parsedQuery !== void 0 && /* @__PURE__ */ React8.createElement(ParseTreeOutput, { parsed: queryResult.parsedQuery })), executionResult && /* @__PURE__ */ React8.createElement(ExecutionResultDisplay, { result: executionResult }));
863
+ }
864
+ }
865
+ }
866
+ function InspectionPanel(props) {
867
+ return /* @__PURE__ */ React8.createElement(
868
+ Box8,
869
+ {
870
+ flexDirection: "column",
871
+ borderStyle: "single",
872
+ borderColor: "blue",
873
+ paddingX: 1,
874
+ marginTop: 1
875
+ },
876
+ /* @__PURE__ */ React8.createElement(TabBar, { activeView: props.view, execMode: props.execMode }),
877
+ /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React8.createElement(PanelContent, { ...props }))
878
+ );
879
+ }
880
+
881
+ // src/repl/conversation/ConversationView.tsx
882
+ import { Box as Box9, Text as Text9 } from "ink";
883
+ import React9 from "react";
884
+ function QuerySummary({
885
+ queryResult,
886
+ execResult
887
+ }) {
888
+ if (!queryResult && !execResult) return null;
889
+ const parts = [];
890
+ if (execResult) {
891
+ if (execResult.error) {
892
+ return /* @__PURE__ */ React9.createElement(Text9, { color: "red" }, "\u2717 ", execResult.error);
893
+ }
894
+ parts.push(
895
+ `${execResult.rowCount} row${execResult.rowCount !== 1 ? "s" : ""}`
896
+ );
897
+ parts.push(`${execResult.executionTimeMs.toFixed(0)}ms`);
898
+ if (execResult.truncated) parts.push("truncated");
899
+ }
900
+ if (queryResult) {
901
+ if (queryResult.error) {
902
+ return /* @__PURE__ */ React9.createElement(Text9, { color: "red" }, "\u2717 ", queryResult.error);
903
+ }
904
+ if (!execResult) {
905
+ const plan = queryResult.plan;
906
+ if (plan?.rootTable) parts.push(plan.rootTable);
907
+ if (plan && plan.cteCount > 0)
908
+ parts.push(`${plan.cteCount} CTE${plan.cteCount !== 1 ? "s" : ""}`);
909
+ if (queryResult.separateQueries && queryResult.separateQueries.length > 0) {
910
+ parts.push(
911
+ `+${queryResult.separateQueries.length} quer${queryResult.separateQueries.length !== 1 ? "ies" : "y"}`
912
+ );
913
+ }
914
+ if (plan && plan.planningTimeMs > 0)
915
+ parts.push(`${plan.planningTimeMs.toFixed(1)}ms`);
916
+ if (plan?.warnings && plan.warnings.length > 0)
917
+ parts.push(
918
+ `${plan.warnings.length} warning${plan.warnings.length !== 1 ? "s" : ""}`
919
+ );
920
+ }
921
+ }
922
+ return /* @__PURE__ */ React9.createElement(Text9, null, /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, "\u2713 "), /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, parts.join(" \xB7 ")));
923
+ }
924
+ function ConversationEntryView({
925
+ entry,
926
+ outputLayout,
927
+ planVerbosity
928
+ }) {
929
+ let queryResult = null;
930
+ let execResult = null;
931
+ let infoMessage = null;
932
+ let errorMessage = null;
933
+ for (const event of entry.events) {
934
+ switch (event.type) {
935
+ case "query-result":
936
+ queryResult = event.result;
937
+ break;
938
+ case "execution-result":
939
+ execResult = event.result;
940
+ break;
941
+ case "info":
942
+ infoMessage = event.message;
943
+ break;
944
+ case "error":
945
+ errorMessage = event.message;
946
+ break;
947
+ }
948
+ }
949
+ const hasQueryOutput = queryResult !== null || execResult !== null;
950
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, entry.input && /* @__PURE__ */ React9.createElement(Text9, null, /* @__PURE__ */ React9.createElement(Text9, { color: "blue", bold: true }, "\u276F "), /* @__PURE__ */ React9.createElement(Text9, { bold: true }, entry.input)), infoMessage && /* @__PURE__ */ React9.createElement(Text9, null, infoMessage), errorMessage && /* @__PURE__ */ React9.createElement(Text9, { color: "red" }, errorMessage), hasQueryOutput && outputLayout === "compact" && /* @__PURE__ */ React9.createElement(QuerySummary, { queryResult, execResult }), hasQueryOutput && outputLayout === "sql" && /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(QuerySummary, { queryResult, execResult }), queryResult && /* @__PURE__ */ React9.createElement(OutputDisplay, { result: queryResult, planVerbosity })), hasQueryOutput && outputLayout === "results" && /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(QuerySummary, { queryResult, execResult }), execResult && /* @__PURE__ */ React9.createElement(ExecutionResultDisplay, { result: execResult })), hasQueryOutput && outputLayout === "full" && /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, queryResult && /* @__PURE__ */ React9.createElement(OutputDisplay, { result: queryResult, planVerbosity }), execResult && /* @__PURE__ */ React9.createElement(ExecutionResultDisplay, { result: execResult })));
951
+ }
952
+ function ConversationView({
953
+ entries,
954
+ outputLayout,
955
+ planVerbosity
956
+ }) {
957
+ if (entries.length === 0) return null;
958
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, entries.map((entry) => /* @__PURE__ */ React9.createElement(
959
+ ConversationEntryView,
960
+ {
961
+ key: entry.id,
962
+ entry,
963
+ outputLayout,
964
+ planVerbosity
965
+ }
966
+ )));
967
+ }
968
+
969
+ // src/repl/conversation/conversation-model.ts
970
+ var MAX_ENTRIES = 100;
971
+ var ConversationManager = class {
972
+ entries = [];
973
+ nextId = 1;
974
+ /** Create a new entry for user input. Returns the entry for event appending. */
975
+ addEntry(input) {
976
+ const entry = {
977
+ id: this.nextId++,
978
+ timestamp: /* @__PURE__ */ new Date(),
979
+ input,
980
+ events: []
981
+ };
982
+ this.entries.push(entry);
983
+ if (this.entries.length > MAX_ENTRIES) {
984
+ this.entries.splice(0, this.entries.length - MAX_ENTRIES);
985
+ }
986
+ return entry;
987
+ }
988
+ /** Append an event to an existing entry. */
989
+ appendEvent(entryId, event) {
990
+ const entry = this.entries.find((e) => e.id === entryId);
991
+ if (entry) {
992
+ entry.events.push(event);
993
+ }
994
+ }
995
+ /** Get all entries (readonly). */
996
+ getEntries() {
997
+ return this.entries;
998
+ }
999
+ /** Clear all entries. */
1000
+ clear() {
1001
+ this.entries = [];
1002
+ }
1003
+ };
1004
+
1005
+ // src/repl/history.ts
1006
+ import {
1007
+ chmodSync,
1008
+ existsSync,
1009
+ mkdirSync,
1010
+ readFileSync,
1011
+ writeFileSync
1012
+ } from "fs";
1013
+ import { homedir } from "os";
1014
+ import { dirname, join } from "path";
1015
+ var HISTORY_FILE = join(homedir(), ".dbsp_history");
1016
+ var MAX_HISTORY_SIZE = 1e3;
1017
+ function unescapeHistoryLine(line) {
1018
+ let result = "";
1019
+ for (let i = 0; i < line.length; i++) {
1020
+ if (line[i] === "\\" && i + 1 < line.length) {
1021
+ const next = line[i + 1];
1022
+ if (next === "\\") {
1023
+ result += "\\";
1024
+ i++;
1025
+ } else if (next === "n") {
1026
+ result += "\n";
1027
+ i++;
1028
+ } else {
1029
+ result += line[i];
1030
+ }
1031
+ } else {
1032
+ result += line[i];
1033
+ }
1034
+ }
1035
+ return result;
1036
+ }
1037
+ var CommandHistory = class {
1038
+ history = [];
1039
+ index = -1;
1040
+ currentInput = "";
1041
+ constructor() {
1042
+ this.load();
1043
+ }
1044
+ /**
1045
+ * Load history from file
1046
+ */
1047
+ load() {
1048
+ try {
1049
+ if (existsSync(HISTORY_FILE)) {
1050
+ try {
1051
+ chmodSync(HISTORY_FILE, 384);
1052
+ } catch {
1053
+ }
1054
+ const content = readFileSync(HISTORY_FILE, "utf-8");
1055
+ this.history = content.split("\n").filter((line) => line.trim().length > 0).map((line) => unescapeHistoryLine(line));
1056
+ }
1057
+ } catch {
1058
+ }
1059
+ }
1060
+ /**
1061
+ * Save history to file
1062
+ */
1063
+ save() {
1064
+ try {
1065
+ const dir = dirname(HISTORY_FILE);
1066
+ if (!existsSync(dir)) {
1067
+ mkdirSync(dir, { recursive: true });
1068
+ }
1069
+ const encoded = this.history.map((entry) => entry.replace(/\\/g, "\\\\").replace(/\n/g, "\\n")).join("\n");
1070
+ writeFileSync(HISTORY_FILE, encoded, {
1071
+ encoding: "utf-8",
1072
+ mode: 384
1073
+ });
1074
+ try {
1075
+ chmodSync(HISTORY_FILE, 384);
1076
+ } catch {
1077
+ }
1078
+ } catch {
1079
+ }
1080
+ }
1081
+ /**
1082
+ * Add a command to history.
1083
+ *
1084
+ * @param command - Command string to record
1085
+ * @param persist - Whether to persist to disk (default: true). Pass false for
1086
+ * batch-mode queries that should not be saved to ~/.dbsp_history (SEC-13).
1087
+ */
1088
+ add(command, persist = true) {
1089
+ const trimmed = command.trim();
1090
+ if (!trimmed) return;
1091
+ if (this.history[this.history.length - 1] === trimmed) return;
1092
+ this.history.push(trimmed);
1093
+ if (this.history.length > MAX_HISTORY_SIZE) {
1094
+ this.history = this.history.slice(-MAX_HISTORY_SIZE);
1095
+ }
1096
+ this.resetIndex();
1097
+ if (persist) {
1098
+ this.save();
1099
+ }
1100
+ }
1101
+ /**
1102
+ * Reset the navigation index
1103
+ */
1104
+ resetIndex() {
1105
+ this.index = -1;
1106
+ this.currentInput = "";
1107
+ }
1108
+ /**
1109
+ * Navigate to previous command (up arrow)
1110
+ * Returns the command to display, or undefined if at start of history
1111
+ */
1112
+ previous(currentInput) {
1113
+ if (this.index === -1) {
1114
+ this.currentInput = currentInput;
1115
+ }
1116
+ if (this.history.length === 0) return void 0;
1117
+ if (this.index < this.history.length - 1) {
1118
+ this.index++;
1119
+ }
1120
+ return this.history[this.history.length - 1 - this.index];
1121
+ }
1122
+ /**
1123
+ * Navigate to next command (down arrow)
1124
+ * Returns the command to display, or the saved current input if past history
1125
+ */
1126
+ next() {
1127
+ if (this.index <= 0) {
1128
+ this.index = -1;
1129
+ return this.currentInput;
1130
+ }
1131
+ this.index--;
1132
+ return this.history[this.history.length - 1 - this.index];
1133
+ }
1134
+ /**
1135
+ * Search history for commands containing the query
1136
+ */
1137
+ search(query) {
1138
+ if (!query) return this.history.slice(-10);
1139
+ const lower = query.toLowerCase();
1140
+ return this.history.filter((cmd) => cmd.toLowerCase().includes(lower));
1141
+ }
1142
+ /**
1143
+ * CLI-MUT: Reverse incremental search (Ctrl+R functionality)
1144
+ * Returns matches from most recent to oldest
1145
+ */
1146
+ reverseSearch(query) {
1147
+ if (!query) return [];
1148
+ const lower = query.toLowerCase();
1149
+ return this.history.filter((cmd) => cmd.toLowerCase().includes(lower)).reverse();
1150
+ }
1151
+ /**
1152
+ * Get all history entries
1153
+ */
1154
+ getAll() {
1155
+ return this.history;
1156
+ }
1157
+ /**
1158
+ * Get recent history (last N entries)
1159
+ */
1160
+ getRecent(count = 10) {
1161
+ return this.history.slice(-count);
1162
+ }
1163
+ /**
1164
+ * Clear all history
1165
+ */
1166
+ clear() {
1167
+ this.history = [];
1168
+ this.resetIndex();
1169
+ this.save();
1170
+ }
1171
+ /**
1172
+ * Get total count
1173
+ */
1174
+ get length() {
1175
+ return this.history.length;
1176
+ }
1177
+ };
1178
+ var historyInstance = null;
1179
+ function getHistory() {
1180
+ if (!historyInstance) {
1181
+ historyInstance = new CommandHistory();
1182
+ }
1183
+ return historyInstance;
1184
+ }
1185
+
1186
+ // src/repl/index.tsx
1187
+ function ReplApp({ config: config2 }) {
1188
+ const { exit } = useApp();
1189
+ const engineRef = useRef2(null);
1190
+ if (!engineRef.current) {
1191
+ engineRef.current = new ReplEngine({
1192
+ schema: config2.schema,
1193
+ schemaPath: config2.schemaPath,
1194
+ ...config2.databaseUrl !== void 0 && {
1195
+ databaseUrl: config2.databaseUrl
1196
+ },
1197
+ ...config2.initialSchemaName !== void 0 && {
1198
+ initialSchemaName: config2.initialSchemaName
1199
+ },
1200
+ ...config2.initialParseMode !== void 0 && {
1201
+ initialParseMode: config2.initialParseMode
1202
+ },
1203
+ ...config2.initialExecMode !== void 0 && {
1204
+ initialExecMode: config2.initialExecMode
1205
+ },
1206
+ ...config2.dbCasing !== void 0 && { dbCasing: config2.dbCasing }
1207
+ });
1208
+ }
1209
+ const engine = engineRef.current;
1210
+ const conversationRef = useRef2(null);
1211
+ if (!conversationRef.current) {
1212
+ conversationRef.current = new ConversationManager();
1213
+ }
1214
+ const conversation = conversationRef.current;
1215
+ const [engineState, setEngineState] = useState3(
1216
+ engine.getState()
1217
+ );
1218
+ const [entries, setEntries] = useState3(conversation.getEntries());
1219
+ const [showHelp, setShowHelp] = useState3(false);
1220
+ const [inputKey, setInputKey] = useState3(0);
1221
+ const [panelView, setPanelView] = useState3(null);
1222
+ const [lastQueryResult, setLastQueryResult] = useState3(
1223
+ null
1224
+ );
1225
+ const [lastExecResult, setLastExecResult] = useState3(
1226
+ null
1227
+ );
1228
+ const completionProvider = useMemo2(
1229
+ () => engine.getCompletionProvider(),
1230
+ [engine]
1231
+ );
1232
+ const [completions, setCompletions] = useState3([]);
1233
+ const [selectedCompletionIndex, setSelectedCompletionIndex] = useState3(-1);
1234
+ const history = useMemo2(() => getHistory(), []);
1235
+ useEffect3(() => {
1236
+ engine.init();
1237
+ return () => {
1238
+ engine.destroy();
1239
+ };
1240
+ }, [engine]);
1241
+ const currentEntryIdRef = useRef2(null);
1242
+ useEffect3(() => {
1243
+ const refreshEntries = () => setEntries([...conversation.getEntries()]);
1244
+ const appendToCurrentOrCreate = (event) => {
1245
+ if (currentEntryIdRef.current !== null) {
1246
+ conversation.appendEvent(currentEntryIdRef.current, event);
1247
+ } else {
1248
+ const entry = conversation.addEntry("");
1249
+ conversation.appendEvent(entry.id, event);
1250
+ }
1251
+ refreshEntries();
1252
+ };
1253
+ const unsub = engine.on((event) => {
1254
+ switch (event.type) {
1255
+ case "state-change":
1256
+ setEngineState(event.state);
1257
+ break;
1258
+ case "exit":
1259
+ exit();
1260
+ break;
1261
+ case "clear":
1262
+ conversation.clear();
1263
+ setShowHelp(false);
1264
+ setPanelView(null);
1265
+ setLastQueryResult(null);
1266
+ setLastExecResult(null);
1267
+ refreshEntries();
1268
+ break;
1269
+ case "info":
1270
+ if (event.message === "SHOW_HELP") {
1271
+ setShowHelp(true);
1272
+ break;
1273
+ }
1274
+ appendToCurrentOrCreate(event);
1275
+ break;
1276
+ case "error":
1277
+ appendToCurrentOrCreate(event);
1278
+ break;
1279
+ case "query-result":
1280
+ if (currentEntryIdRef.current !== null) {
1281
+ conversation.appendEvent(currentEntryIdRef.current, event);
1282
+ refreshEntries();
1283
+ }
1284
+ setLastQueryResult(event.result);
1285
+ break;
1286
+ case "execution-result":
1287
+ if (currentEntryIdRef.current !== null) {
1288
+ conversation.appendEvent(currentEntryIdRef.current, event);
1289
+ refreshEntries();
1290
+ }
1291
+ setLastExecResult(event.result);
1292
+ setLastQueryResult(event.query);
1293
+ break;
1294
+ case "show-history": {
1295
+ const recent = history.getRecent(20);
1296
+ const msg = recent.length === 0 ? "No command history yet." : `\u{1F4DC} Recent Commands (${recent.length}):
1297
+ ${recent.map((cmd, i) => ` ${i + 1}. ${cmd}`).join("\n")}`;
1298
+ appendToCurrentOrCreate({ type: "info", message: msg });
1299
+ break;
1300
+ }
1301
+ case "show-panel":
1302
+ setPanelView(event.view);
1303
+ break;
1304
+ case "close-panel":
1305
+ setPanelView(null);
1306
+ break;
1307
+ case "layout-change":
1308
+ break;
1309
+ }
1310
+ });
1311
+ return unsub;
1312
+ }, [engine, conversation, exit]);
1313
+ useInput3((inputChar, key) => {
1314
+ if (key.ctrl && inputChar === "c") {
1315
+ exit();
1316
+ }
1317
+ if (key.escape && panelView !== null) {
1318
+ setPanelView(null);
1319
+ return;
1320
+ }
1321
+ if (key.tab) {
1322
+ if (panelView !== null) {
1323
+ const views = ["sql", "plan", "results", "params", "dump"];
1324
+ const currentIdx = views.indexOf(panelView);
1325
+ const nextIdx = (currentIdx + 1) % views.length;
1326
+ const nextView = views[nextIdx];
1327
+ if (nextView) setPanelView(nextView);
1328
+ return;
1329
+ }
1330
+ if (completions.length > 0 && engineState.mode === "natural") {
1331
+ const nextIndex = selectedCompletionIndex < 0 ? 0 : (selectedCompletionIndex + 1) % completions.length;
1332
+ setSelectedCompletionIndex(nextIndex);
1333
+ }
1334
+ }
1335
+ });
1336
+ const handleInputChange = useCallback2(
1337
+ (value) => {
1338
+ setSelectedCompletionIndex(-1);
1339
+ if (engineState.mode === "natural") {
1340
+ const suggestions = completionProvider.complete(value);
1341
+ setCompletions(suggestions);
1342
+ } else {
1343
+ setCompletions([]);
1344
+ }
1345
+ },
1346
+ [completionProvider, engineState.mode]
1347
+ );
1348
+ const handleCompletionAccepted = useCallback2(() => {
1349
+ setSelectedCompletionIndex(-1);
1350
+ setCompletions([]);
1351
+ }, []);
1352
+ const handleApplyCompletion = useCallback2(
1353
+ (currentInput, completionText) => {
1354
+ return completionProvider.applyCompletion(currentInput, completionText);
1355
+ },
1356
+ [completionProvider]
1357
+ );
1358
+ const selectedCompletion = selectedCompletionIndex >= 0 ? completions[selectedCompletionIndex]?.text : void 0;
1359
+ const submitQueueRef = useRef2([]);
1360
+ const isProcessingRef = useRef2(false);
1361
+ const processQueue = useCallback2(async () => {
1362
+ if (isProcessingRef.current) return;
1363
+ isProcessingRef.current = true;
1364
+ while (submitQueueRef.current.length > 0) {
1365
+ const trimmed = submitQueueRef.current.shift();
1366
+ if (trimmed === void 0) break;
1367
+ history.add(trimmed);
1368
+ const entry = conversation.addEntry(trimmed);
1369
+ currentEntryIdRef.current = entry.id;
1370
+ setEntries([...conversation.getEntries()]);
1371
+ await engine.submit(trimmed);
1372
+ currentEntryIdRef.current = null;
1373
+ }
1374
+ isProcessingRef.current = false;
1375
+ }, [engine, conversation, history]);
1376
+ const handleSubmit = useCallback2(
1377
+ (value) => {
1378
+ const trimmed = value.trim();
1379
+ if (!trimmed) return;
1380
+ setInputKey((k) => k + 1);
1381
+ setCompletions([]);
1382
+ setShowHelp(false);
1383
+ submitQueueRef.current.push(trimmed);
1384
+ processQueue();
1385
+ },
1386
+ [processQueue]
1387
+ );
1388
+ const tableCount = config2.schema.tableNames.length;
1389
+ const relationCount = config2.schema.model.relations.size;
1390
+ const contentArea = /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, showHelp && /* @__PURE__ */ React10.createElement(HelpDisplay, null), /* @__PURE__ */ React10.createElement(
1391
+ ConversationView,
1392
+ {
1393
+ entries,
1394
+ outputLayout: engineState.outputLayout,
1395
+ planVerbosity: engineState.planVerbosity
1396
+ }
1397
+ ), engineState.mode === "natural" && completions.length > 0 && !showHelp && /* @__PURE__ */ React10.createElement(
1398
+ CompletionDisplay,
1399
+ {
1400
+ suggestions: completions,
1401
+ selectedIndex: selectedCompletionIndex
1402
+ }
1403
+ ), /* @__PURE__ */ React10.createElement(
1404
+ InputPrompt,
1405
+ {
1406
+ onSubmit: handleSubmit,
1407
+ mode: engineState.mode,
1408
+ resetKey: inputKey,
1409
+ history,
1410
+ onInputChange: handleInputChange,
1411
+ ...selectedCompletion !== void 0 && { selectedCompletion },
1412
+ onCompletionAccepted: handleCompletionAccepted,
1413
+ applyCompletion: handleApplyCompletion
1414
+ }
1415
+ ), panelView !== null && /* @__PURE__ */ React10.createElement(
1416
+ InspectionPanel,
1417
+ {
1418
+ view: panelView,
1419
+ queryResult: lastQueryResult,
1420
+ executionResult: lastExecResult,
1421
+ execMode: engineState.execMode,
1422
+ onViewChange: setPanelView
1423
+ }
1424
+ ));
1425
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React10.createElement(
1426
+ Header,
1427
+ {
1428
+ schemaPath: config2.schemaPath,
1429
+ mode: engineState.mode,
1430
+ tableCount,
1431
+ relationCount,
1432
+ dialect: engineState.dialect,
1433
+ includeStrategy: engineState.includeStrategy,
1434
+ aliasingMode: engineState.aliasingMode,
1435
+ connected: engineState.connected,
1436
+ execMode: engineState.execMode,
1437
+ parseMode: engineState.parseMode,
1438
+ explainMode: engineState.explainMode,
1439
+ ...engineState.schemaName && { schemaName: engineState.schemaName },
1440
+ ...config2.databaseUrl && {
1441
+ databaseName: getDatabaseName(config2.databaseUrl)
1442
+ }
1443
+ }
1444
+ ), contentArea);
1445
+ }
1446
+ async function startRepl(config2) {
1447
+ console.log("Starting REPL...\n");
1448
+ const instance = render(/* @__PURE__ */ React10.createElement(ReplApp, { config: config2 }));
1449
+ await instance.waitUntilExit();
1450
+ }
1451
+ export {
1452
+ startRepl
1453
+ };
1454
+ //# sourceMappingURL=repl-4OFERLKZ.js.map