planche 0.7.8 → 0.7.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/app/assets/{AboutDialog-8g4nv7oI.js → AboutDialog-CNjQdT5u.js} +1 -1
- package/app/assets/ConnectDialog-JmGFmUJv.js +1 -0
- package/app/assets/{CopyDatabaseDialog-DFHhTGzA.js → CopyDatabaseDialog-BeIaHJEE.js} +1 -1
- package/app/assets/{CopyTableDialog-Ai65hTaM.js → CopyTableDialog-RTJBBuHd.js} +1 -1
- package/app/assets/{CreateDatabaseDialog-BaddFu5_.js → CreateDatabaseDialog-EQk84TRf.js} +1 -1
- package/app/assets/DataDiffDialog-Bsk7fEYT.js +1 -0
- package/app/assets/{DownloadToCSVDialog-A3GD6diN.js → DownloadToCSVDialog-fZOrfJCY.js} +2 -2
- package/app/assets/{EditSchemaDialog-CfYXbsMs.js → EditSchemaDialog-C8sd3QZp.js} +1 -1
- package/app/assets/{FindReplaceDialog-DsafIyXL.js → FindReplaceDialog-Q-kOCd30.js} +1 -1
- package/app/assets/{FlushDialog-BJ7t14hx.js → FlushDialog-DuPXbGV8.js} +1 -1
- package/app/assets/LinkColumnDialog-Vfx3YnVz.js +1 -0
- package/app/assets/{LocalHistoryDialog-DnRAW0Yc.js → LocalHistoryDialog-D7PbCc1T.js} +1 -1
- package/app/assets/ProcessListDialog-BcrS--N5.js +1 -0
- package/app/assets/QuickCommandPalette-DgmR-AEB.js +1 -0
- package/app/assets/RowFormDialog-BeY3wZoM.js +2 -0
- package/app/assets/{SafetySettingsDialog-CSJgC2S8.js → SafetySettingsDialog-COTjlwgg.js} +1 -1
- package/app/assets/SchemaDiffDialog-DShgozcV.js +7 -0
- package/app/assets/{SchemaToHTMLDialog-De2prqOE.js → SchemaToHTMLDialog-6ZXb_qWR.js} +1 -1
- package/app/assets/{StatusDialog-DwZ3fiLH.js → StatusDialog-DYnmWcrB.js} +1 -1
- package/app/assets/{UserAddDialog-6SxCG4hP.js → UserAddDialog-WJfSy3UY.js} +1 -1
- package/app/assets/UserManagerDialog-Bp3YK2PA.js +1 -0
- package/app/assets/{VariablesDialog-C9tOMsRa.js → VariablesDialog-CofNmdTr.js} +1 -1
- package/app/assets/database-C07SXivf.js +1 -0
- package/app/assets/de-BHXb0E0M.js +2 -0
- package/app/assets/fr-vqo4FVRK.js +2 -0
- package/app/assets/{i18n-BBZ4HyyF.js → i18n-ByO9kvGG.js} +2 -2
- package/app/assets/ja-BosgFbQC.js +2 -0
- package/app/assets/ko-CvSx-uAq.js +2 -0
- package/app/assets/main-BKsXOMU1.css +2 -0
- package/app/assets/{main-DwGQ4Tb3.js → main-DKTBYrT9.js} +184 -184
- package/app/assets/{query-execution-DNBwLTK8.js → query-execution-Bqix04tS.js} +3 -3
- package/app/assets/{select-CYih1UpS.js → select-teWP1vRl.js} +1 -1
- package/app/assets/table-copy-NGGO-qDb.js +1 -0
- package/app/assets/zh-B0EkNPpR.js +2 -0
- package/app/index.html +7 -6
- package/dist/index.js +366 -34
- package/package.json +2 -2
- package/app/assets/ConnectDialog-DaEC7cZs.js +0 -1
- package/app/assets/DataDiffDialog-DHLDDTLx.js +0 -1
- package/app/assets/LinkColumnDialog-CcIHoa0p.js +0 -1
- package/app/assets/ProcessListDialog-D4kPbazL.js +0 -1
- package/app/assets/QuickCommandPalette-BPYRYAsk.js +0 -1
- package/app/assets/RowFormDialog-C2OsE9vc.js +0 -2
- package/app/assets/SchemaDiffDialog-C8v-22Oj.js +0 -7
- package/app/assets/UserManagerDialog-o0TErGmM.js +0 -1
- package/app/assets/database-DT05D7dO.js +0 -1
- package/app/assets/de-CC5Txb37.js +0 -2
- package/app/assets/fr-DuwjnnAw.js +0 -2
- package/app/assets/ja-B8MYtGo3.js +0 -2
- package/app/assets/ko-lJaJ15rE.js +0 -2
- package/app/assets/main-DCJnT21X.css +0 -2
- package/app/assets/table-copy-CDK_Gn8v.js +0 -1
- package/app/assets/zh-DXC_ymYM.js +0 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5
|
-
import { createRequire } from "module";
|
|
5
|
+
import { createRequire as createRequire2 } from "module";
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import { parseArgs } from "util";
|
|
8
8
|
|
|
@@ -344,7 +344,7 @@ var postgresDriverFactory = {
|
|
|
344
344
|
};
|
|
345
345
|
|
|
346
346
|
// src/drivers/sqlite.ts
|
|
347
|
-
import
|
|
347
|
+
import { createRequire } from "module";
|
|
348
348
|
import { existsSync, realpathSync } from "fs";
|
|
349
349
|
import { homedir } from "os";
|
|
350
350
|
import { basename, dirname, join, relative, resolve, sep } from "path";
|
|
@@ -403,13 +403,18 @@ function resolveFile(file, forCreate = false) {
|
|
|
403
403
|
}
|
|
404
404
|
return resolved;
|
|
405
405
|
}
|
|
406
|
+
var ctor = null;
|
|
407
|
+
function sqliteCtor() {
|
|
408
|
+
ctor ??= createRequire(import.meta.url)("better-sqlite3");
|
|
409
|
+
return ctor;
|
|
410
|
+
}
|
|
406
411
|
function openDatabase(config) {
|
|
407
412
|
const file = resolveFile(config.file, config.createIfMissing === true);
|
|
408
413
|
const isMemory = file === ":memory:";
|
|
409
414
|
if (!isMemory && !config.createIfMissing && !existsSync(file)) {
|
|
410
415
|
throw new Error(`SQLite file not found: ${config.file}`);
|
|
411
416
|
}
|
|
412
|
-
const db = new
|
|
417
|
+
const db = new (sqliteCtor())(file, {
|
|
413
418
|
readonly: config.readonly ?? false,
|
|
414
419
|
fileMustExist: !isMemory && !config.createIfMissing
|
|
415
420
|
});
|
|
@@ -614,8 +619,8 @@ var MssqlDriver = class {
|
|
|
614
619
|
* decide whether it is editable and address rows by primary key.
|
|
615
620
|
*/
|
|
616
621
|
async describe(text) {
|
|
617
|
-
const
|
|
618
|
-
if (
|
|
622
|
+
const cached2 = this.sources.get(text);
|
|
623
|
+
if (cached2 !== void 0) return cached2;
|
|
619
624
|
let result = null;
|
|
620
625
|
try {
|
|
621
626
|
const req = this.pool.request();
|
|
@@ -760,8 +765,8 @@ var OracleDriver = class {
|
|
|
760
765
|
}
|
|
761
766
|
async tableColumns(schema, table) {
|
|
762
767
|
const key = `${schema}.${table}`;
|
|
763
|
-
const
|
|
764
|
-
if (
|
|
768
|
+
const cached2 = this.columns.get(key);
|
|
769
|
+
if (cached2 !== void 0) return cached2;
|
|
765
770
|
let out = null;
|
|
766
771
|
try {
|
|
767
772
|
const res = await this.conn.execute(
|
|
@@ -929,8 +934,8 @@ var D1Driver = class {
|
|
|
929
934
|
return result;
|
|
930
935
|
}
|
|
931
936
|
async tableColumns(table) {
|
|
932
|
-
const
|
|
933
|
-
if (
|
|
937
|
+
const cached2 = this.columns.get(table);
|
|
938
|
+
if (cached2 !== void 0) return cached2;
|
|
934
939
|
let out = null;
|
|
935
940
|
try {
|
|
936
941
|
const info = await this.raw(`PRAGMA table_info("${table.replaceAll('"', '""')}")`);
|
|
@@ -1369,8 +1374,8 @@ async function openSshTunnel(requested, target) {
|
|
|
1369
1374
|
verified(false);
|
|
1370
1375
|
});
|
|
1371
1376
|
};
|
|
1372
|
-
await new Promise((
|
|
1373
|
-
client.once("ready",
|
|
1377
|
+
await new Promise((resolve5, reject) => {
|
|
1378
|
+
client.once("ready", resolve5);
|
|
1374
1379
|
client.once("error", (err) => reject(verifyError ?? describeSshError(err, ssh)));
|
|
1375
1380
|
try {
|
|
1376
1381
|
client.connect(connectConfig);
|
|
@@ -1400,22 +1405,22 @@ async function openSshTunnel(requested, target) {
|
|
|
1400
1405
|
socket.once("close", () => stream.destroy());
|
|
1401
1406
|
});
|
|
1402
1407
|
});
|
|
1403
|
-
await new Promise((
|
|
1408
|
+
await new Promise((resolve5, reject) => {
|
|
1404
1409
|
server.once("error", reject);
|
|
1405
|
-
server.listen(0, "127.0.0.1", () =>
|
|
1410
|
+
server.listen(0, "127.0.0.1", () => resolve5());
|
|
1406
1411
|
}).catch((err) => {
|
|
1407
1412
|
client.end();
|
|
1408
1413
|
throw new TunnelError(`Could not open local tunnel port: ${err.message}`, 400);
|
|
1409
1414
|
});
|
|
1410
1415
|
const { port } = server.address();
|
|
1411
1416
|
let closed = false;
|
|
1412
|
-
const close = () => new Promise((
|
|
1413
|
-
if (closed) return
|
|
1417
|
+
const close = () => new Promise((resolve5) => {
|
|
1418
|
+
if (closed) return resolve5();
|
|
1414
1419
|
closed = true;
|
|
1415
1420
|
for (const s of sockets) s.destroy();
|
|
1416
1421
|
server.close(() => {
|
|
1417
1422
|
client.end();
|
|
1418
|
-
|
|
1423
|
+
resolve5();
|
|
1419
1424
|
});
|
|
1420
1425
|
});
|
|
1421
1426
|
client.once("close", () => void close());
|
|
@@ -1429,6 +1434,116 @@ async function openSshTunnel(requested, target) {
|
|
|
1429
1434
|
};
|
|
1430
1435
|
}
|
|
1431
1436
|
|
|
1437
|
+
// src/usage.ts
|
|
1438
|
+
import { mkdir as mkdir2, readFile as readFile3, rename, writeFile } from "fs/promises";
|
|
1439
|
+
import { homedir as homedir4 } from "os";
|
|
1440
|
+
import { join as join4 } from "path";
|
|
1441
|
+
|
|
1442
|
+
// ../shared/contract.ts
|
|
1443
|
+
var PROTOCOL_VERSION = 2;
|
|
1444
|
+
var PRO_FEATURES = [
|
|
1445
|
+
"ssh_tunnel",
|
|
1446
|
+
"compare_schemas",
|
|
1447
|
+
"compare_data",
|
|
1448
|
+
"copy_table",
|
|
1449
|
+
"federated",
|
|
1450
|
+
"scratchpad",
|
|
1451
|
+
"column_link"
|
|
1452
|
+
];
|
|
1453
|
+
|
|
1454
|
+
// src/usage.ts
|
|
1455
|
+
var PLANCHE_DIR2 = join4(homedir4(), ".planche");
|
|
1456
|
+
var USAGE_FILE = join4(PLANCHE_DIR2, "usage.json");
|
|
1457
|
+
var WRITE_DEBOUNCE_MS = 2e3;
|
|
1458
|
+
var KNOWN = new Set(PRO_FEATURES);
|
|
1459
|
+
var state = null;
|
|
1460
|
+
var loading = null;
|
|
1461
|
+
var timer = null;
|
|
1462
|
+
var writes = Promise.resolve();
|
|
1463
|
+
function isProFeature(value) {
|
|
1464
|
+
return typeof value === "string" && KNOWN.has(value);
|
|
1465
|
+
}
|
|
1466
|
+
var isoNow = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1467
|
+
function empty() {
|
|
1468
|
+
return { version: 1, since: isoNow(), features: {} };
|
|
1469
|
+
}
|
|
1470
|
+
function parse(raw) {
|
|
1471
|
+
const data = JSON.parse(raw);
|
|
1472
|
+
if (!data || typeof data !== "object") return empty();
|
|
1473
|
+
const features = {};
|
|
1474
|
+
for (const [key, value] of Object.entries(data.features ?? {})) {
|
|
1475
|
+
if (!isProFeature(key) || !value || typeof value !== "object") continue;
|
|
1476
|
+
const entry = value;
|
|
1477
|
+
const count = Number(entry.count);
|
|
1478
|
+
if (!Number.isFinite(count) || count < 0) continue;
|
|
1479
|
+
features[key] = {
|
|
1480
|
+
count: Math.floor(count),
|
|
1481
|
+
first: typeof entry.first === "string" ? entry.first : isoNow(),
|
|
1482
|
+
last: typeof entry.last === "string" ? entry.last : isoNow()
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
return { version: 1, since: typeof data.since === "string" ? data.since : isoNow(), features };
|
|
1486
|
+
}
|
|
1487
|
+
async function load() {
|
|
1488
|
+
if (state) return state;
|
|
1489
|
+
loading ??= (async () => {
|
|
1490
|
+
try {
|
|
1491
|
+
state = parse(await readFile3(USAGE_FILE, "utf8"));
|
|
1492
|
+
} catch {
|
|
1493
|
+
state = empty();
|
|
1494
|
+
}
|
|
1495
|
+
return state;
|
|
1496
|
+
})();
|
|
1497
|
+
return loading;
|
|
1498
|
+
}
|
|
1499
|
+
async function persist() {
|
|
1500
|
+
if (!state) return;
|
|
1501
|
+
const body = `${JSON.stringify(state, null, 2)}
|
|
1502
|
+
`;
|
|
1503
|
+
await mkdir2(PLANCHE_DIR2, { recursive: true, mode: 448 });
|
|
1504
|
+
const tmp = `${USAGE_FILE}.${process.pid}.tmp`;
|
|
1505
|
+
await writeFile(tmp, body, { mode: 384 });
|
|
1506
|
+
await rename(tmp, USAGE_FILE);
|
|
1507
|
+
}
|
|
1508
|
+
function schedule() {
|
|
1509
|
+
if (timer) return;
|
|
1510
|
+
timer = setTimeout(() => {
|
|
1511
|
+
timer = null;
|
|
1512
|
+
void flushUsage();
|
|
1513
|
+
}, WRITE_DEBOUNCE_MS);
|
|
1514
|
+
timer.unref?.();
|
|
1515
|
+
}
|
|
1516
|
+
function recordUsage(feature) {
|
|
1517
|
+
void (async () => {
|
|
1518
|
+
try {
|
|
1519
|
+
const file = await load();
|
|
1520
|
+
const now = isoNow();
|
|
1521
|
+
const entry = file.features[feature];
|
|
1522
|
+
if (entry) {
|
|
1523
|
+
entry.count += 1;
|
|
1524
|
+
entry.last = now;
|
|
1525
|
+
} else {
|
|
1526
|
+
file.features[feature] = { count: 1, first: now, last: now };
|
|
1527
|
+
}
|
|
1528
|
+
schedule();
|
|
1529
|
+
} catch {
|
|
1530
|
+
}
|
|
1531
|
+
})();
|
|
1532
|
+
}
|
|
1533
|
+
function flushUsage() {
|
|
1534
|
+
if (timer) {
|
|
1535
|
+
clearTimeout(timer);
|
|
1536
|
+
timer = null;
|
|
1537
|
+
}
|
|
1538
|
+
writes = writes.then(persist).catch(() => {
|
|
1539
|
+
});
|
|
1540
|
+
return writes;
|
|
1541
|
+
}
|
|
1542
|
+
async function usageSnapshot() {
|
|
1543
|
+
const file = await load().catch(empty);
|
|
1544
|
+
return { success: true, since: file.since, features: { ...file.features } };
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1432
1547
|
// src/drivers/index.ts
|
|
1433
1548
|
var factories = {
|
|
1434
1549
|
mysql: mysqlDriverFactory,
|
|
@@ -1454,6 +1569,7 @@ function parseDriverConfig(body) {
|
|
|
1454
1569
|
async function withTunnel(config, run) {
|
|
1455
1570
|
if (config.driver === "sqlite" || config.driver === "d1" || !config.ssh) return run(config, void 0);
|
|
1456
1571
|
const tunnel = await openSshTunnel(config.ssh, { host: config.host, port: config.port });
|
|
1572
|
+
recordUsage("ssh_tunnel");
|
|
1457
1573
|
try {
|
|
1458
1574
|
return await run({ ...config, host: tunnel.host, port: tunnel.port, ssh: void 0 }, tunnel);
|
|
1459
1575
|
} catch (err) {
|
|
@@ -1565,17 +1681,207 @@ function describeProfile(entry) {
|
|
|
1565
1681
|
}
|
|
1566
1682
|
|
|
1567
1683
|
// src/index.ts
|
|
1568
|
-
import { existsSync as
|
|
1569
|
-
import { join as
|
|
1684
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1685
|
+
import { join as join7, resolve as resolve4 } from "path";
|
|
1686
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1687
|
+
|
|
1688
|
+
// src/install.ts
|
|
1689
|
+
import { spawnSync } from "child_process";
|
|
1690
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
|
|
1691
|
+
import { homedir as homedir5 } from "os";
|
|
1692
|
+
import { createInterface } from "readline/promises";
|
|
1693
|
+
import { dirname as dirname3, join as join5, resolve as resolve3 } from "path";
|
|
1570
1694
|
import { fileURLToPath } from "url";
|
|
1695
|
+
var MANIFEST = "install.json";
|
|
1696
|
+
var PLANCHE_DIR3 = join5(homedir5(), ".planche");
|
|
1697
|
+
var UPDATE_CACHE = join5(PLANCHE_DIR3, "update-check.json");
|
|
1698
|
+
var REGISTRY = "https://registry.npmjs.org/planche/latest";
|
|
1699
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1700
|
+
var cached = null;
|
|
1701
|
+
function locate() {
|
|
1702
|
+
if (cached) return cached;
|
|
1703
|
+
const candidates = [];
|
|
1704
|
+
if (process.env.PLANCHE_INSTALL_ROOT) candidates.push(resolve3(process.env.PLANCHE_INSTALL_ROOT));
|
|
1705
|
+
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
1706
|
+
for (let i = 0; i < 6; i += 1) {
|
|
1707
|
+
candidates.push(dir);
|
|
1708
|
+
const parent = dirname3(dir);
|
|
1709
|
+
if (parent === dir) break;
|
|
1710
|
+
dir = parent;
|
|
1711
|
+
}
|
|
1712
|
+
for (const root of candidates) {
|
|
1713
|
+
try {
|
|
1714
|
+
const raw = readFileSync2(join5(root, MANIFEST), "utf8");
|
|
1715
|
+
const manifest = JSON.parse(raw);
|
|
1716
|
+
if (manifest && manifest.version === 1) {
|
|
1717
|
+
cached = { root, manifest };
|
|
1718
|
+
return cached;
|
|
1719
|
+
}
|
|
1720
|
+
} catch {
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
cached = { root: null, manifest: null };
|
|
1724
|
+
return cached;
|
|
1725
|
+
}
|
|
1726
|
+
function installKind() {
|
|
1727
|
+
return locate().manifest ? "standalone" : "npx";
|
|
1728
|
+
}
|
|
1729
|
+
function abiWarning() {
|
|
1730
|
+
const { manifest } = locate();
|
|
1731
|
+
if (!manifest?.modules || manifest.modules === process.versions.modules) return null;
|
|
1732
|
+
return `This install was built for Node ABI ${manifest.modules}, but it is running on ${process.version} (ABI ${process.versions.modules}).
|
|
1733
|
+
SQLite connections will fail until the native bindings are rebuilt \u2014 run: planche upgrade`;
|
|
1734
|
+
}
|
|
1735
|
+
function npmCommand(manifest) {
|
|
1736
|
+
const node = manifest.node && existsSync2(manifest.node) ? manifest.node : process.execPath;
|
|
1737
|
+
if (manifest.npmCli && existsSync2(manifest.npmCli)) return { cmd: node, args: [manifest.npmCli], shell: false };
|
|
1738
|
+
if (manifest.npm && existsSync2(manifest.npm)) {
|
|
1739
|
+
return { cmd: manifest.npm, args: [], shell: process.platform === "win32" };
|
|
1740
|
+
}
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1743
|
+
var VERSION_SPEC = /^(latest|next|\d+\.\d+\.\d+(-[0-9A-Za-z.]+)?)$/;
|
|
1744
|
+
function runUpgrade(target) {
|
|
1745
|
+
if (!VERSION_SPEC.test(target)) {
|
|
1746
|
+
console.error(`Not a version: ${target}. Use \`planche upgrade\` or \`planche upgrade 1.2.3\`.`);
|
|
1747
|
+
return 2;
|
|
1748
|
+
}
|
|
1749
|
+
const { root, manifest } = locate();
|
|
1750
|
+
if (!root || !manifest) {
|
|
1751
|
+
console.error("This copy of planche was not installed by planche.dev/install.sh.");
|
|
1752
|
+
console.error("Run the latest version with: npx planche@latest");
|
|
1753
|
+
return 1;
|
|
1754
|
+
}
|
|
1755
|
+
const npm = npmCommand(manifest);
|
|
1756
|
+
if (!npm) {
|
|
1757
|
+
console.error(`Cannot find npm for this install (see ${join5(root, MANIFEST)}).`);
|
|
1758
|
+
console.error("Re-run the installer: curl -fsSL https://planche.dev/install.sh | sh");
|
|
1759
|
+
return 1;
|
|
1760
|
+
}
|
|
1761
|
+
const prefix = join5(root, "pkg");
|
|
1762
|
+
console.log(`Installing planche@${target} into ${prefix} ...`);
|
|
1763
|
+
const result = spawnSync(
|
|
1764
|
+
npm.cmd,
|
|
1765
|
+
[...npm.args, "install", "--prefix", prefix, "--omit=dev", "--no-audit", "--no-fund", "--loglevel=error", `planche@${target}`],
|
|
1766
|
+
{ stdio: "inherit", shell: npm.shell }
|
|
1767
|
+
);
|
|
1768
|
+
if (result.status !== 0) {
|
|
1769
|
+
console.error("Upgrade failed. Re-run the installer to repair the install:");
|
|
1770
|
+
console.error(" curl -fsSL https://planche.dev/install.sh | sh");
|
|
1771
|
+
return result.status ?? 1;
|
|
1772
|
+
}
|
|
1773
|
+
let installed = target;
|
|
1774
|
+
try {
|
|
1775
|
+
const pkg = JSON.parse(readFileSync2(join5(prefix, "node_modules", "planche", "package.json"), "utf8"));
|
|
1776
|
+
installed = pkg.version;
|
|
1777
|
+
} catch {
|
|
1778
|
+
}
|
|
1779
|
+
try {
|
|
1780
|
+
writeFileSync(join5(root, MANIFEST), `${JSON.stringify({ ...manifest, modules: process.versions.modules, installedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
1781
|
+
`);
|
|
1782
|
+
} catch {
|
|
1783
|
+
}
|
|
1784
|
+
cached = null;
|
|
1785
|
+
clearUpdateCache();
|
|
1786
|
+
console.log(`planche ${installed} installed. Start it with: planche`);
|
|
1787
|
+
return 0;
|
|
1788
|
+
}
|
|
1789
|
+
async function runUninstall(assumeYes) {
|
|
1790
|
+
const { root, manifest } = locate();
|
|
1791
|
+
if (!root || !manifest) {
|
|
1792
|
+
console.error("This copy of planche was not installed by planche.dev/install.sh; there is nothing to uninstall.");
|
|
1793
|
+
console.error("An `npx planche` download lives in the npm cache and is cleared with: npm cache clean --force");
|
|
1794
|
+
return 1;
|
|
1795
|
+
}
|
|
1796
|
+
const targets = [root, ...manifest.bin && existsSync2(manifest.bin) ? [manifest.bin] : []];
|
|
1797
|
+
console.log("This will remove:");
|
|
1798
|
+
for (const t of targets) console.log(` ${t}`);
|
|
1799
|
+
console.log(`
|
|
1800
|
+
Your settings stay: ${PLANCHE_DIR3} (known_hosts, usage counts) is left untouched.`);
|
|
1801
|
+
if (!assumeYes) {
|
|
1802
|
+
if (!process.stdin.isTTY) {
|
|
1803
|
+
console.error("\nRefusing to delete without a confirmation. Re-run with: planche uninstall --yes");
|
|
1804
|
+
return 1;
|
|
1805
|
+
}
|
|
1806
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1807
|
+
const answer = (await rl.question("\nRemove them? [y/N] ")).trim().toLowerCase();
|
|
1808
|
+
rl.close();
|
|
1809
|
+
if (answer !== "y" && answer !== "yes") {
|
|
1810
|
+
console.log("Nothing was removed.");
|
|
1811
|
+
return 1;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
for (const t of targets) {
|
|
1815
|
+
try {
|
|
1816
|
+
rmSync(t, { recursive: true, force: true });
|
|
1817
|
+
} catch (err) {
|
|
1818
|
+
console.error(`Could not remove ${t}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1819
|
+
return 1;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
console.log("planche has been uninstalled.");
|
|
1823
|
+
return 0;
|
|
1824
|
+
}
|
|
1825
|
+
function readUpdateCache() {
|
|
1826
|
+
try {
|
|
1827
|
+
const data = JSON.parse(readFileSync2(UPDATE_CACHE, "utf8"));
|
|
1828
|
+
return typeof data.checkedAt === "number" && typeof data.latest === "string" ? data : null;
|
|
1829
|
+
} catch {
|
|
1830
|
+
return null;
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function writeUpdateCache(latest) {
|
|
1834
|
+
try {
|
|
1835
|
+
mkdirSync(PLANCHE_DIR3, { recursive: true, mode: 448 });
|
|
1836
|
+
writeFileSync(UPDATE_CACHE, `${JSON.stringify({ checkedAt: Date.now(), latest })}
|
|
1837
|
+
`, { mode: 384 });
|
|
1838
|
+
} catch {
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
function clearUpdateCache() {
|
|
1842
|
+
try {
|
|
1843
|
+
rmSync(UPDATE_CACHE, { force: true });
|
|
1844
|
+
} catch {
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
function isNewer(candidate, current) {
|
|
1848
|
+
const parse2 = (v) => /^\d+\.\d+\.\d+$/.test(v) ? v.split(".").map(Number) : null;
|
|
1849
|
+
const a = parse2(candidate);
|
|
1850
|
+
const b = parse2(current);
|
|
1851
|
+
if (!a || !b) return false;
|
|
1852
|
+
for (let i = 0; i < 3; i += 1) {
|
|
1853
|
+
if (a[i] !== b[i]) return a[i] > b[i];
|
|
1854
|
+
}
|
|
1855
|
+
return false;
|
|
1856
|
+
}
|
|
1857
|
+
async function updateNotice(current) {
|
|
1858
|
+
if (installKind() !== "standalone") return null;
|
|
1859
|
+
if (process.env.PLANCHE_NO_UPDATE_CHECK) return null;
|
|
1860
|
+
const cache = readUpdateCache();
|
|
1861
|
+
let latest = cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS ? cache.latest : null;
|
|
1862
|
+
if (!latest) {
|
|
1863
|
+
const controller = new AbortController();
|
|
1864
|
+
const timer2 = setTimeout(() => controller.abort(), 2500);
|
|
1865
|
+
try {
|
|
1866
|
+
const response = await fetch(REGISTRY, { signal: controller.signal, headers: { accept: "application/json" } });
|
|
1867
|
+
if (!response.ok) return null;
|
|
1868
|
+
const data = await response.json();
|
|
1869
|
+
if (typeof data.version !== "string" || !/^[\w.+-]{1,32}$/.test(data.version)) return null;
|
|
1870
|
+
latest = data.version;
|
|
1871
|
+
writeUpdateCache(latest);
|
|
1872
|
+
} catch {
|
|
1873
|
+
return null;
|
|
1874
|
+
} finally {
|
|
1875
|
+
clearTimeout(timer2);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
return isNewer(latest, current) ? `planche ${latest} is available (you have ${current}) \u2014 update with: planche upgrade` : null;
|
|
1879
|
+
}
|
|
1571
1880
|
|
|
1572
1881
|
// src/server.ts
|
|
1573
1882
|
import express from "express";
|
|
1574
1883
|
import { createHash as createHash2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
1575
1884
|
|
|
1576
|
-
// ../shared/contract.ts
|
|
1577
|
-
var PROTOCOL_VERSION = 2;
|
|
1578
|
-
|
|
1579
1885
|
// src/tunneling.ts
|
|
1580
1886
|
import { randomUUID } from "crypto";
|
|
1581
1887
|
|
|
@@ -1742,8 +2048,8 @@ var ConnectionManager = class {
|
|
|
1742
2048
|
/** Connections opened for a read-only gateway profile: writes are refused before reaching the driver. */
|
|
1743
2049
|
readOnly = /* @__PURE__ */ new Set();
|
|
1744
2050
|
constructor() {
|
|
1745
|
-
const
|
|
1746
|
-
|
|
2051
|
+
const timer2 = setInterval(() => void this.closeIdle(), 6e4);
|
|
2052
|
+
timer2.unref();
|
|
1747
2053
|
}
|
|
1748
2054
|
/** Close connections idle for longer than `IDLE_TIMEOUT_MS`. */
|
|
1749
2055
|
async closeIdle() {
|
|
@@ -1852,8 +2158,8 @@ var ConnectionManager = class {
|
|
|
1852
2158
|
};
|
|
1853
2159
|
|
|
1854
2160
|
// src/server.ts
|
|
1855
|
-
import { existsSync as
|
|
1856
|
-
import { join as
|
|
2161
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
2162
|
+
import { join as join6 } from "path";
|
|
1857
2163
|
function errorStatus(err) {
|
|
1858
2164
|
const status = err?.status;
|
|
1859
2165
|
return typeof status === "number" && status >= 400 && status < 600 ? status : 400;
|
|
@@ -1985,6 +2291,7 @@ function createAgent(options) {
|
|
|
1985
2291
|
activeConnections: manager.activeConnections,
|
|
1986
2292
|
uptime: process.uptime()
|
|
1987
2293
|
};
|
|
2294
|
+
if (options.install) body.install = options.install;
|
|
1988
2295
|
if (options.gateway) body.gateway = { name: options.gateway.name, profiles: options.gateway.profiles.length };
|
|
1989
2296
|
if (req.headers.authorization !== void 0) {
|
|
1990
2297
|
if (throttle.locked(peerOf(req))) {
|
|
@@ -2084,6 +2391,15 @@ function createAgent(options) {
|
|
|
2084
2391
|
fail(res, 422, message);
|
|
2085
2392
|
}
|
|
2086
2393
|
});
|
|
2394
|
+
app.post("/usage", (req, res) => {
|
|
2395
|
+
const feature = (req.body ?? {}).feature;
|
|
2396
|
+
if (isProFeature(feature)) recordUsage(feature);
|
|
2397
|
+
res.json({ success: true });
|
|
2398
|
+
});
|
|
2399
|
+
app.get("/usage", async (_req, res) => {
|
|
2400
|
+
await flushUsage();
|
|
2401
|
+
res.json(await usageSnapshot());
|
|
2402
|
+
});
|
|
2087
2403
|
app.post("/disconnect", async (req, res) => {
|
|
2088
2404
|
try {
|
|
2089
2405
|
const { connectionId } = req.body ?? {};
|
|
@@ -2138,8 +2454,8 @@ function serveApp(app, appDir) {
|
|
|
2138
2454
|
"form-action 'none'",
|
|
2139
2455
|
"frame-ancestors 'none'"
|
|
2140
2456
|
].join("; ");
|
|
2141
|
-
const indexFile =
|
|
2142
|
-
const indexHtml =
|
|
2457
|
+
const indexFile = join6(appDir, "index.html");
|
|
2458
|
+
const indexHtml = existsSync3(indexFile) ? readFileSync3(indexFile, "utf8").replace("<head>", '<head>\n <meta name="planche-agent" content="same-origin" />') : null;
|
|
2143
2459
|
const headers = (res) => {
|
|
2144
2460
|
res.setHeader("Content-Security-Policy", csp);
|
|
2145
2461
|
res.setHeader("X-Frame-Options", "DENY");
|
|
@@ -2164,7 +2480,7 @@ function serveApp(app, appDir) {
|
|
|
2164
2480
|
}
|
|
2165
2481
|
|
|
2166
2482
|
// src/index.ts
|
|
2167
|
-
var { version: VERSION } =
|
|
2483
|
+
var { version: VERSION } = createRequire2(import.meta.url)("../package.json");
|
|
2168
2484
|
var DEFAULT_APP_URL = "https://planche.dev";
|
|
2169
2485
|
var DEV_ORIGINS = [
|
|
2170
2486
|
"http://localhost:5173",
|
|
@@ -2173,8 +2489,8 @@ var DEV_ORIGINS = [
|
|
|
2173
2489
|
"http://127.0.0.1:4173"
|
|
2174
2490
|
];
|
|
2175
2491
|
function bundledAppDir() {
|
|
2176
|
-
const dir =
|
|
2177
|
-
return
|
|
2492
|
+
const dir = fileURLToPath2(new URL("../app/", import.meta.url));
|
|
2493
|
+
return existsSync4(join7(dir, "index.html")) ? dir : null;
|
|
2178
2494
|
}
|
|
2179
2495
|
function isLoopbackUrl(url) {
|
|
2180
2496
|
try {
|
|
@@ -2185,19 +2501,19 @@ function isLoopbackUrl(url) {
|
|
|
2185
2501
|
}
|
|
2186
2502
|
async function appIsReachable(url, timeoutMs = 2500) {
|
|
2187
2503
|
const controller = new AbortController();
|
|
2188
|
-
const
|
|
2504
|
+
const timer2 = setTimeout(() => controller.abort(), timeoutMs);
|
|
2189
2505
|
try {
|
|
2190
2506
|
const response = await fetch(url, { signal: controller.signal, redirect: "follow", headers: { "cache-control": "no-store" } });
|
|
2191
2507
|
return response.ok;
|
|
2192
2508
|
} catch {
|
|
2193
2509
|
return false;
|
|
2194
2510
|
} finally {
|
|
2195
|
-
clearTimeout(
|
|
2511
|
+
clearTimeout(timer2);
|
|
2196
2512
|
}
|
|
2197
2513
|
}
|
|
2198
2514
|
function resolveAppDir(explicit) {
|
|
2199
|
-
const dir = explicit ?
|
|
2200
|
-
if (!
|
|
2515
|
+
const dir = explicit ? resolve4(explicit) : fileURLToPath2(new URL("../app/", import.meta.url));
|
|
2516
|
+
if (!existsSync4(join7(dir, "index.html"))) {
|
|
2201
2517
|
console.error(explicit ? `--app-dir has no index.html: ${dir}` : `This build does not include the web app (${dir}). Install the published package, or pass --app-dir <dir>.`);
|
|
2202
2518
|
process.exit(2);
|
|
2203
2519
|
}
|
|
@@ -2207,6 +2523,8 @@ function usage() {
|
|
|
2207
2523
|
return `planche ${VERSION} \u2014 local agent for the Planche SQL client
|
|
2208
2524
|
|
|
2209
2525
|
Usage: planche [options]
|
|
2526
|
+
planche upgrade [version] Update a standalone install in place
|
|
2527
|
+
planche uninstall [--yes] Remove it again (settings in ~/.planche are kept)
|
|
2210
2528
|
|
|
2211
2529
|
Options:
|
|
2212
2530
|
-p, --port <n> Port to listen on (default: 8888, env PLANCHE_PORT)
|
|
@@ -2254,12 +2572,21 @@ function openInBrowser(url) {
|
|
|
2254
2572
|
}
|
|
2255
2573
|
}
|
|
2256
2574
|
async function main() {
|
|
2575
|
+
const [subcommand] = process.argv.slice(2);
|
|
2576
|
+
if (subcommand === "upgrade") {
|
|
2577
|
+
process.exit(runUpgrade(process.argv[3] ?? "latest"));
|
|
2578
|
+
}
|
|
2579
|
+
if (subcommand === "uninstall") {
|
|
2580
|
+
process.exit(await runUninstall(process.argv.includes("--yes") || process.argv.includes("-y")));
|
|
2581
|
+
}
|
|
2257
2582
|
const major = Number(process.versions.node.split(".")[0]);
|
|
2258
2583
|
if (major < 22) {
|
|
2259
2584
|
console.error(`planche needs Node.js 22 or newer (you are running ${process.version}).`);
|
|
2260
2585
|
console.error("Try `nvm use 22` or `npx -p node@22 planche`.");
|
|
2261
2586
|
process.exit(1);
|
|
2262
2587
|
}
|
|
2588
|
+
const abi = abiWarning();
|
|
2589
|
+
if (abi) console.warn(`WARNING: ${abi}`);
|
|
2263
2590
|
let values;
|
|
2264
2591
|
try {
|
|
2265
2592
|
values = parseArgs({
|
|
@@ -2360,6 +2687,7 @@ async function main() {
|
|
|
2360
2687
|
bodyLimit,
|
|
2361
2688
|
version: VERSION,
|
|
2362
2689
|
logRequests: flags.verbose ?? false,
|
|
2690
|
+
install: installKind(),
|
|
2363
2691
|
// Browsers omit a default port from `Host`, so the bare names are allowed too.
|
|
2364
2692
|
allowedHosts: loopback ? ["127.0.0.1", "localhost", "[::1]", `127.0.0.1:${port}`, `localhost:${port}`, `[::1]:${port}`] : void 0,
|
|
2365
2693
|
pairingCode,
|
|
@@ -2387,6 +2715,9 @@ async function main() {
|
|
|
2387
2715
|
}
|
|
2388
2716
|
console.log(line);
|
|
2389
2717
|
if (shouldOpen) openInBrowser(pairUrl);
|
|
2718
|
+
void updateNotice(VERSION).then((notice) => {
|
|
2719
|
+
if (notice) console.log(notice);
|
|
2720
|
+
});
|
|
2390
2721
|
});
|
|
2391
2722
|
server.on("error", (err) => {
|
|
2392
2723
|
if (err.code === "EADDRINUSE") {
|
|
@@ -2399,6 +2730,7 @@ async function main() {
|
|
|
2399
2730
|
const shutdown = async () => {
|
|
2400
2731
|
console.log("\nShutting down...");
|
|
2401
2732
|
await manager.disconnectAll();
|
|
2733
|
+
await flushUsage();
|
|
2402
2734
|
process.exit(0);
|
|
2403
2735
|
};
|
|
2404
2736
|
process.on("SIGINT", shutdown);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "planche",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "Local agent for Planche
|
|
3
|
+
"version": "0.7.10",
|
|
4
|
+
"description": "Local agent for Planche \u2014 lets the browser-based Planche SQL client reach MySQL/MariaDB/PostgreSQL/SQLite/SQL Server/Oracle/Neon/Supabase/Cloudflare D1 databases from your machine.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://planche.dev",
|
|
7
7
|
"repository": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,m as t,n,t as r}from"./ui-store-VGM_km-l.js";import{C as i,K as a,M as o,N as s,S as c,a as l,b as u,c as d,d as f,i as p,j as m,l as h,n as g,o as _,r as v,s as y,u as b,w as x,x as S,y as C}from"./main-DwGQ4Tb3.js";import{l as w,m as T,t as E}from"./button-BRD_vJ8s.js";import{a as D,i as O,n as k,r as A,t as j}from"./select-CYih1UpS.js";import{t as M}from"./connection-store-Cn79Ey1H.js";import{n as ee}from"./query-store-Cu277RoH.js";import{O as N,S as te,T as ne,b as re,j as P,k as F,l as I,m as ie,n as ae,s as L,t as oe,w as se}from"./database-DT05D7dO.js";import{n as R,t as z}from"./dbms-Bsjvyvsp.js";import{a as ce,i as le,n as ue,o as de,t as fe}from"./dialog-1MXGAorC.js";var B=e(t(),1),V=new Set([`postgres:`,`postgresql:`]);function H(e){let t=e.trim().replace(/^psql\s+(['"])?/,``).replace(/['"]$/,``);if(!/^postgres(ql)?:\/\//i.test(t))return null;let n;try{n=new URL(t)}catch{return null}if(!V.has(n.protocol.toLowerCase())||!n.hostname)return null;let r=e=>{try{return decodeURIComponent(e)}catch{return e}},i={host:n.hostname};n.port&&(i.port=Number(n.port)),n.username&&(i.user=r(n.username)),n.password&&(i.pass=r(n.password));let a=r(n.pathname.replace(/^\//,``));return a&&(i.database=a),i}var U=w(),W=[`utf8`,`utf8mb4`,`latin1`,`ascii`,`euckr`,`sjis`,`big5`],G={enabled:!1,host:``,port:22,user:``,auth:`key`};function K({label:e,htmlFor:t,children:n}){return(0,U.jsxs)(`div`,{className:`grid grid-cols-[120px_minmax(0,1fr)] items-center gap-2`,children:[(0,U.jsx)(h,{htmlFor:t,children:e}),n]})}function pe({value:e,onChange:t}){let{t:n}=T();function r(n,r){t({...e,[n]:r})}let i=e.dbms===`sqlite`,a=e.transport??`browser`,c=i&&a===`browser`,l=F.has(e.dbms),u=e.dbms===`d1`,d=z.find(t=>t.id===e.dbms)?.capabilities,p=e.ssh??G,[m,h]=(0,B.useState)(``),g=m.trim()===``?`empty`:H(m)?`applied`:`invalid`,v=e.dbms===`supabase`&&e.port===6543;function x(n){h(n);let r=H(n);r&&t({...e,host:r.host??e.host,port:r.port??z.find(t=>t.id===e.dbms)?.defaultPort??e.port,user:r.user??e.user,pass:r.pass??e.pass,database:r.database??e.database})}function S(n){t({...e,ssh:{...p,...n}})}async function C(e){e&&S({privateKey:await e.text(),privateKeyName:e.name})}return(0,U.jsxs)(`div`,{className:`flex flex-col gap-3 p-4`,children:[(0,U.jsx)(K,{label:n(`connection.form.hostName`),htmlFor:`conn-hostName`,children:(0,U.jsx)(b,{id:`conn-hostName`,placeholder:n(`connection.form.hostNamePlaceholder`),value:e.hostName,onChange:e=>r(`hostName`,e.target.value)})}),(0,U.jsx)(K,{label:n(`connection.form.dataSource`),children:(0,U.jsxs)(j,{value:e.dbms,onValueChange:n=>{let r=z.find(e=>e.id===n);if(!r)return;let i=r.id,a=F.has(i)||i===`d1`;h(``),t({...e,dbms:i,transactionMode:r.capabilities.transactions?e.transactionMode:`auto`,port:r.defaultPort??e.port,host:a&&e.host===`localhost`?``:!a&&e.host===``?`localhost`:e.host,user:a||l||u?y(i):e.user,database:e.database&&!a&&!l&&!u?e.database:_(i)??e.database})},children:[(0,U.jsx)(O,{id:`conn-dbms`,className:`w-full`,children:(0,U.jsx)(D,{})}),(0,U.jsx)(k,{children:z.map(e=>(0,U.jsx)(A,{value:e.id,children:(0,U.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,U.jsx)(f,{dbms:e.id}),e.label]})},e.id))})]})}),l&&(0,U.jsx)(K,{label:n(`connection.form.connectionString`),htmlFor:`conn-uri`,children:(0,U.jsxs)(`div`,{className:`min-w-0`,children:[(0,U.jsx)(b,{id:`conn-uri`,type:`password`,autoComplete:`off`,spellCheck:!1,className:`font-mono text-xs`,placeholder:e.dbms===`neon`?`postgresql://neondb_owner:npg_…@ep-….neon.tech/neondb?sslmode=require`:`postgresql://postgres.[ref]:[password]@aws-0-….pooler.supabase.com:5432/postgres`,value:m,onChange:e=>x(e.target.value)}),(0,U.jsx)(`p`,{className:`mt-1 text-xs leading-relaxed ${g===`invalid`?`text-destructive`:`text-muted-foreground`}`,children:n(g===`invalid`?`connection.form.connectionStringInvalid`:g===`applied`?`connection.form.connectionStringApplied`:e.dbms===`neon`?`connection.form.neonHint`:`connection.form.supabaseHint`)})]})}),i&&(0,U.jsxs)(K,{label:n(`connection.form.openVia`),children:[(0,U.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,U.jsxs)(`label`,{className:`flex items-center gap-1.5 text-sm cursor-pointer`,children:[(0,U.jsx)(`input`,{type:`radio`,name:`transport`,value:`browser`,checked:a===`browser`,onChange:()=>r(`transport`,`browser`),className:`accent-primary`}),n(`connection.form.viaBrowser`)]}),(0,U.jsxs)(`label`,{className:`flex items-center gap-1.5 text-sm cursor-pointer`,children:[(0,U.jsx)(`input`,{type:`radio`,name:`transport`,value:`tunnel`,checked:a===`tunnel`,onChange:()=>r(`transport`,`tunnel`),className:`accent-primary`}),n(`connection.form.viaAgent`)]})]}),(0,U.jsx)(`p`,{className:`col-start-2 text-xs text-muted-foreground leading-relaxed`,children:n(a===`browser`?`connection.form.viaBrowserHint`:`connection.form.viaAgentHint`)})]}),c?(0,U.jsx)(K,{label:n(`connection.form.databaseFile`),children:(0,U.jsxs)(`div`,{className:`min-w-0 text-xs text-muted-foreground leading-relaxed`,children:[e.file?(0,U.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-3 gap-y-1`,children:[(0,U.jsx)(`span`,{className:`min-w-0 break-all text-foreground`,title:e.file,children:e.file}),(0,U.jsx)(E,{type:`button`,variant:`outline`,size:`sm`,className:`h-6 shrink-0 px-2 text-xs`,onClick:()=>{L(e.id),r(`file`,``)},children:n(`connection.form.changeFile`)})]}):(0,U.jsx)(`p`,{children:n(`connection.form.filePickerHint`)}),(0,U.jsx)(`p`,{className:`mt-1`,children:n(`connection.form.fileRememberedHint`)})]})}):null,!c&&(0,U.jsx)(K,{label:n(`connection.form.authToken`),htmlFor:`conn-authToken`,children:(0,U.jsx)(b,{id:`conn-authToken`,type:`password`,placeholder:n(`connection.form.authTokenPlaceholder`),value:e.authToken,onChange:e=>r(`authToken`,e.target.value.trim())})}),c?null:u?(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(K,{label:n(`connection.form.d1AccountId`),htmlFor:`conn-d1-account`,children:(0,U.jsx)(b,{id:`conn-d1-account`,className:`font-mono text-xs`,spellCheck:!1,placeholder:`023e105f4ecef8ad9ca31a8372d0c353`,value:e.accountId??``,onChange:e=>r(`accountId`,e.target.value.trim())})}),(0,U.jsx)(K,{label:n(`connection.form.d1DatabaseId`),htmlFor:`conn-d1-database`,children:(0,U.jsx)(b,{id:`conn-d1-database`,className:`font-mono text-xs`,spellCheck:!1,placeholder:`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`,value:e.databaseId??``,onChange:e=>r(`databaseId`,e.target.value.trim())})}),(0,U.jsx)(K,{label:n(`connection.form.d1ApiToken`),htmlFor:`conn-pass`,children:(0,U.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,U.jsx)(b,{id:`conn-pass`,type:`password`,value:e.pass,onChange:e=>r(`pass`,e.target.value.trim())}),(0,U.jsxs)(`label`,{className:`flex items-center gap-1.5 text-xs whitespace-nowrap cursor-pointer`,title:n(`connection.form.savePasswordHint`),children:[(0,U.jsx)(`input`,{type:`checkbox`,checked:e.savePassword??!0,onChange:e=>r(`savePassword`,e.target.checked)}),n(`connection.form.savePassword`)]})]})}),(0,U.jsx)(`p`,{className:`col-start-2 text-xs text-muted-foreground leading-relaxed`,children:n(`connection.form.d1Hint`)}),(0,U.jsx)(`p`,{className:`col-start-2 text-xs text-muted-foreground leading-relaxed`,children:n(`connection.form.d1LimitsHint`)})]}):i?(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(K,{label:n(`connection.form.databaseFile`),htmlFor:`conn-file`,children:(0,U.jsx)(b,{id:`conn-file`,placeholder:n(`connection.form.filePathPlaceholder`),value:e.file??``,onChange:e=>r(`file`,e.target.value)})}),(0,U.jsx)(K,{label:n(`connection.form.createIfMissing`),htmlFor:`conn-create`,children:(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,U.jsx)(`input`,{id:`conn-create`,type:`checkbox`,checked:e.createIfMissing??!1,onChange:e=>r(`createIfMissing`,e.target.checked),className:`size-4 accent-primary cursor-pointer`}),(0,U.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:n(`connection.form.createIfMissingHint`)})]})})]}):(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(K,{label:n(`connection.form.host`),htmlFor:`conn-host`,children:(0,U.jsx)(b,{id:`conn-host`,placeholder:e.dbms===`neon`?`ep-….aws.neon.tech`:e.dbms===`supabase`?`aws-0-….pooler.supabase.com`:`localhost`,value:e.host,onChange:e=>r(`host`,e.target.value)})}),(0,U.jsx)(K,{label:n(`connection.form.port`),htmlFor:`conn-port`,children:(0,U.jsx)(b,{id:`conn-port`,type:`number`,value:e.port,onChange:t=>r(`port`,Number(t.target.value)||z.find(t=>t.id===e.dbms)?.defaultPort||3306)})}),v&&(0,U.jsx)(`p`,{className:`col-start-2 rounded-md border border-yellow-500/40 bg-yellow-500/10 px-2 py-1.5 text-xs leading-relaxed text-foreground`,children:n(`connection.form.supabasePoolerWarning`)}),(P(e.dbms)||e.dbms===`mssql`||e.dbms===`oracle`)&&(0,U.jsx)(K,{label:e.dbms===`oracle`?n(`connection.form.serviceName`):n(`connection.form.database`),htmlFor:`conn-database`,children:(0,U.jsx)(b,{id:`conn-database`,placeholder:_(e.dbms),value:e.database??``,onChange:e=>r(`database`,e.target.value.trim())})}),(0,U.jsx)(K,{label:n(`connection.form.user`),htmlFor:`conn-user`,children:(0,U.jsx)(b,{id:`conn-user`,placeholder:y(e.dbms),value:e.user,onChange:e=>r(`user`,e.target.value)})}),(0,U.jsx)(K,{label:n(`connection.form.password`),htmlFor:`conn-pass`,children:(0,U.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,U.jsx)(b,{id:`conn-pass`,type:`password`,value:e.pass,onChange:e=>r(`pass`,e.target.value)}),(0,U.jsxs)(`label`,{className:`flex items-center gap-1.5 text-xs whitespace-nowrap cursor-pointer`,title:n(`connection.form.savePasswordHint`),children:[(0,U.jsx)(`input`,{type:`checkbox`,checked:e.savePassword??!0,onChange:e=>r(`savePassword`,e.target.checked)}),n(`connection.form.savePassword`)]})]})}),l&&(0,U.jsx)(`p`,{className:`col-start-2 text-xs text-muted-foreground leading-relaxed`,children:n(`connection.form.managedTlsHint`)}),!l&&e.dbms!==`oracle`&&(0,U.jsx)(K,{label:n(`connection.form.tls`),children:(0,U.jsxs)(`div`,{className:`flex flex-wrap items-center gap-4`,children:[(0,U.jsxs)(`label`,{className:`flex items-center gap-1.5 text-xs cursor-pointer`,children:[(0,U.jsx)(`input`,{type:`checkbox`,checked:e.ssl??!1,onChange:e=>r(`ssl`,e.target.checked),className:`size-4 accent-primary`}),n(`connection.form.tlsEnable`)]}),(0,U.jsxs)(`label`,{className:`flex items-center gap-1.5 text-xs cursor-pointer ${e.ssl?``:`opacity-50`}`,title:n(`connection.form.tlsVerifyHint`),children:[(0,U.jsx)(`input`,{type:`checkbox`,disabled:!e.ssl,checked:e.sslVerify??!1,onChange:e=>r(`sslVerify`,e.target.checked),className:`size-4 accent-primary`}),n(`connection.form.tlsVerify`)]})]})}),!l&&(0,U.jsx)(K,{label:n(`connection.form.charset`),children:(0,U.jsxs)(j,{value:e.charset,onValueChange:e=>r(`charset`,e),children:[(0,U.jsx)(O,{className:`w-full`,children:(0,U.jsx)(D,{})}),(0,U.jsx)(k,{children:W.map(e=>(0,U.jsx)(A,{value:e,children:e},e))})]})})]}),!i&&!l&&!u&&(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(`div`,{className:`mt-1 border-t pt-3 text-xs font-medium text-muted-foreground`,children:n(`connection.form.ssh`)}),(0,U.jsx)(K,{label:n(`connection.form.sshTunnel`),htmlFor:`conn-ssh`,children:(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,U.jsx)(`input`,{id:`conn-ssh`,type:`checkbox`,checked:p.enabled,onChange:e=>S({enabled:e.target.checked}),className:`size-4 accent-primary cursor-pointer`}),(0,U.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:n(`connection.form.sshTunnelHint`)})]})}),p.enabled&&(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(K,{label:n(`connection.form.sshHost`),htmlFor:`conn-ssh-host`,children:(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,U.jsx)(b,{id:`conn-ssh-host`,placeholder:n(`connection.form.sshHostPlaceholder`),value:p.host,onChange:e=>S({host:e.target.value.trim()})}),(0,U.jsx)(b,{"aria-label":n(`connection.form.sshPort`),type:`number`,className:`w-24 shrink-0`,value:p.port,onChange:e=>S({port:Number(e.target.value)||22})})]})}),(0,U.jsx)(K,{label:n(`connection.form.sshUser`),htmlFor:`conn-ssh-user`,children:(0,U.jsx)(b,{id:`conn-ssh-user`,placeholder:n(`connection.form.sshUserPlaceholder`),value:p.user,onChange:e=>S({user:e.target.value.trim()})})}),(0,U.jsx)(K,{label:n(`connection.form.sshAuth`),children:(0,U.jsxs)(j,{value:p.auth,onValueChange:e=>S({auth:e}),children:[(0,U.jsx)(O,{className:`w-full`,children:(0,U.jsx)(D,{})}),(0,U.jsxs)(k,{children:[(0,U.jsx)(A,{value:`key`,children:n(`connection.form.sshAuthKey`)}),(0,U.jsx)(A,{value:`password`,children:n(`connection.form.sshAuthPassword`)}),(0,U.jsx)(A,{value:`agent`,children:n(`connection.form.sshAuthAgent`)})]})]})}),p.auth===`password`&&(0,U.jsx)(K,{label:n(`connection.form.sshPassword`),htmlFor:`conn-ssh-pass`,children:(0,U.jsx)(b,{id:`conn-ssh-pass`,type:`password`,value:p.password??``,onChange:e=>S({password:e.target.value})})}),p.auth===`key`&&(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(K,{label:n(`connection.form.sshKey`),htmlFor:`conn-ssh-key`,children:(0,U.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,U.jsx)(`input`,{id:`conn-ssh-key`,type:`file`,className:`hidden`,onChange:e=>{C(e.target.files?.[0]),e.target.value=``}}),(0,U.jsx)(E,{type:`button`,variant:`outline`,size:`sm`,className:`h-7 shrink-0 px-2 text-xs`,onClick:()=>document.getElementById(`conn-ssh-key`)?.click(),children:p.privateKey?n(`connection.form.changeFile`):n(`connection.form.sshKeyPick`)}),(0,U.jsx)(`span`,{className:`min-w-0 truncate text-xs text-muted-foreground`,title:p.privateKeyName,children:p.privateKey?p.privateKeyName||n(`connection.form.sshKeyLoaded`):n(`connection.form.sshKeyHint`)})]})}),(0,U.jsx)(K,{label:n(`connection.form.sshPassphrase`),htmlFor:`conn-ssh-passphrase`,children:(0,U.jsx)(b,{id:`conn-ssh-passphrase`,type:`password`,placeholder:n(`connection.form.sshPassphrasePlaceholder`),value:p.passphrase??``,onChange:e=>S({passphrase:e.target.value})})})]}),p.auth===`agent`&&(0,U.jsx)(`p`,{className:`col-start-2 text-xs text-muted-foreground leading-relaxed`,children:n(`connection.form.sshAuthAgentHint`)}),(0,U.jsx)(`p`,{className:`text-xs text-muted-foreground leading-relaxed`,children:n(`connection.form.sshTargetHint`)}),(0,U.jsx)(`p`,{className:`text-xs text-muted-foreground leading-relaxed`,children:n(`connection.form.sshSecretsHint`)})]})]}),(0,U.jsx)(K,{label:n(`connection.form.autoConnection`),htmlFor:`conn-auto`,children:(0,U.jsx)(`div`,{className:`flex items-center`,children:(0,U.jsx)(`input`,{id:`conn-auto`,type:`checkbox`,checked:e.autoConnection,onChange:e=>r(`autoConnection`,e.target.checked),className:`size-4 accent-primary cursor-pointer`})})}),(0,U.jsx)(`div`,{className:`mt-1 border-t pt-3 text-xs font-medium text-muted-foreground`,children:n(`connection.form.safety`)}),(0,U.jsx)(K,{label:n(`connection.form.environment`),children:(0,U.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5`,children:[o.map(t=>{let n=(e.environmentColor??`none`)===t.id;return(0,U.jsx)(`button`,{type:`button`,title:s(t.id),"aria-label":s(t.id),"aria-pressed":n,onClick:()=>r(`environmentColor`,t.id),className:`size-6 rounded-full border-2 transition-transform ${n?`scale-110 border-foreground`:`border-transparent hover:scale-105`}`,style:{background:t.id===`none`?`repeating-linear-gradient(45deg, transparent, transparent 3px, var(--muted-foreground) 3px, var(--muted-foreground) 4px)`:t.hex}},t.id)}),(0,U.jsx)(`span`,{className:`ml-1 text-xs text-muted-foreground`,children:n(`connection.form.environmentHint`)})]})}),(0,U.jsx)(K,{label:n(`connection.form.readOnly`),htmlFor:`conn-readonly`,children:(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,U.jsx)(`input`,{id:`conn-readonly`,type:`checkbox`,checked:e.readOnly??!1,onChange:e=>r(`readOnly`,e.target.checked),className:`size-4 accent-primary cursor-pointer`}),(0,U.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:n(`connection.form.readOnlyHint`)})]})}),d?.transactions!==!1&&(0,U.jsx)(K,{label:n(`connection.form.transactions`),children:(0,U.jsxs)(j,{value:e.transactionMode??`auto`,onValueChange:e=>r(`transactionMode`,e),children:[(0,U.jsx)(O,{className:`w-full`,children:(0,U.jsx)(D,{})}),(0,U.jsxs)(k,{children:[(0,U.jsx)(A,{value:`auto`,children:n(`connection.form.txAuto`)}),(0,U.jsx)(A,{value:`manual`,children:n(`connection.form.txManual`)})]})]})})]})}function q(){let{t:e}=T(),{activeDialog:t,closeDialog:o,showMessage:s}=n(),[h,_]=(0,B.useState)(!1),{savedConnections:y,addSavedConnection:b,updateSavedConnection:w,deleteSavedConnection:D,openConnection:O}=M(),k=r(`connect`)?.profileId,[A,j]=(0,B.useState)(()=>k??null),[P,F]=(0,B.useState)(()=>{let e=k?y.find(e=>e.id===k):void 0;return e?{...e}:null}),L=t===`connect`,[z,V]=(0,B.useState)(null),[H,W]=(0,B.useState)(null),G=d(e=>e.url),K=d(e=>e.token);(0,B.useEffect)(()=>{if(!L)return;let e=!1;return(async()=>{try{let t=await te();if(e||t.state!==`online`||!t.gateway){e||V(null);return}let n=await se();e||(V({name:n.gateway,profiles:n.profiles}),W(null))}catch(t){e||W(t instanceof Error?t.message:String(t))}})(),()=>{e=!0}},[L,G,K]);function q(t){let r=y.find(e=>e.gatewayProfile===t.name);if(r){Y(r);return}let i=v(t);b(i),Y(i),n.getState().setFooterMessage(e(`connection.dialog.gatewayAdded`,{name:t.name}))}function J(e){e||(o(),j(null),F(null))}function Y(e){j(e.id),F({...e})}function me(e){F(e),w(e.id,e)}function he(){let e=l();b(e),j(e.id),F(e)}function ge(){A&&(D(A),j(null),F(null))}let[_e,X]=(0,B.useState)(!1),ve=ee(e=>e.addQueryTab);function Z(t){let n=M.getState().activeConnections.some(e=>e.id===t.id);if(O(t),n){re.getState().removeTree(t.id),o(),j(null),F(null);return}ve(t.id,{id:`tab-${Date.now()}`,title:e(`connection.dialog.firstQueryTab`),query:``,database:``,isExecuting:!1,results:[],error:null,executionTime:null}),o(),j(null),F(null)}async function ye(t){let r=await oe(t.id,{interactive:!0});if(!r)return;let i={...t,file:r.fileName,serverConnectionId:r.connectionId};w(t.id,{file:r.fileName}),Z(i),M.getState().setFileDetached(t.id,r.fileDetached),r.restoredUnsaved?n.getState().setFooterMessage(e(r.diskChanged?`connection.dialog.restoredUnsavedDiskChanged`:`connection.dialog.restoredUnsaved`,{file:r.fileName})):r.wal&&!r.walApplied?n.getState().setFooterMessage(e(`connection.dialog.walPending`,{file:r.fileName})):r.walApplied&&n.getState().setFooterMessage(e(`connection.dialog.walApplied`,{file:r.fileName}))}async function Q(t,r){try{return await r(t)}catch(i){let a=ne(i);if(!a||!(`ssh`in t)||!t.ssh||!await n.getState().requestDestructiveConfirm({title:e(`connection.dialog.hostKeyTitle`),description:e(`connection.dialog.hostKeyDescription`,a),confirmLabel:e(`connection.dialog.hostKeyAccept`)}))throw i;return r({...t,ssh:{...t.ssh,acceptHostKey:a.fingerprint}})}}async function $(){if(P){X(!0);try{if(I(P)===`browser`){await ye(P);return}let t=await Q(N(P),e=>ae(e,P.authToken||void 0));t.success?Z({...P,serverConnectionId:t.connectionId}):s({tone:`error`,title:e(`connection.dialog.connectFailedTitle`),description:t.message??e(`connection.dialog.unknownError`)})}catch(t){s({tone:`error`,title:e(`connection.dialog.connectFailedTitle`),description:t instanceof Error?t.message:String(t)})}finally{X(!1)}}}async function be(){if(P){if(I(P)===`browser`){s({tone:`info`,title:e(`connection.dialog.testConnection`),description:e(`connection.dialog.browserTestHint`)});return}_(!0);try{let t=await Q(N(P),e=>ie(e,P.authToken||void 0));t.success?s({tone:`success`,title:e(`connection.dialog.testSuccess`),description:e(`connection.dialog.testSuccessDescription`)}):s({tone:`error`,title:e(`connection.dialog.testFailedTitle`),description:t.message??e(`connection.dialog.unknownError`)})}catch(t){s({tone:`error`,title:e(`connection.dialog.testFailedTitle`),description:t instanceof Error?t.message:String(t)})}finally{_(!1)}}}return(0,U.jsx)(fe,{open:L,onOpenChange:J,children:(0,U.jsxs)(ue,{className:`sm:max-w-[1100px] h-[85vh] max-h-[85vh] flex flex-col`,children:[(0,U.jsx)(ce,{children:(0,U.jsx)(de,{children:e(`connection.dialog.title`)})}),(0,U.jsxs)(`p`,{className:`flex items-start gap-2 rounded-md border border-catppuccin-green/40 bg-catppuccin-green/10 px-3 py-2 text-xs leading-relaxed text-foreground`,children:[(0,U.jsx)(a,{className:`mt-0.5 size-4 shrink-0 text-catppuccin-green`,"aria-hidden":!0}),(0,U.jsx)(`span`,{children:e(`connection.dialog.privacy`)})]}),(0,U.jsxs)(`div`,{className:`flex flex-1 gap-4 overflow-hidden min-h-0`,children:[(0,U.jsxs)(`div`,{className:`w-[460px] shrink-0 border rounded-md overflow-hidden flex flex-col`,children:[(z||H)&&(0,U.jsxs)(`div`,{className:`border-b p-2 text-xs space-y-1 max-h-40 overflow-auto`,children:[(0,U.jsxs)(`p`,{className:`font-medium`,children:[e(`connection.dialog.gatewayTitle`),z?` — ${z.name}`:``]}),H&&(0,U.jsx)(`p`,{className:`text-destructive`,children:e(`connection.dialog.gatewayLoadFailed`,{error:H})}),z?.profiles.map(t=>(0,U.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,U.jsxs)(`span`,{className:`flex items-center gap-1.5 truncate`,children:[(0,U.jsx)(f,{dbms:g[t.driver],className:`size-3.5`}),(0,U.jsx)(`span`,{className:`truncate`,children:t.name}),(0,U.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,t.readOnly?e(`connection.dialog.gatewayReadOnly`):e(`connection.dialog.gatewayWritable`),`)`]})]}),(0,U.jsx)(E,{size:`sm`,variant:`outline`,className:`h-6 px-2 text-xs`,onClick:()=>q(t),children:e(`connection.dialog.gatewayAdd`)})]},t.name))]}),(0,U.jsx)(m,{className:`h-full`,children:(0,U.jsxs)(C,{className:`table-fixed`,children:[(0,U.jsx)(i,{children:(0,U.jsxs)(x,{children:[(0,U.jsx)(c,{className:`w-[32%]`,children:e(`connection.dialog.colHostName`)}),(0,U.jsx)(c,{className:`w-[40%]`,children:e(`connection.dialog.colTarget`)}),(0,U.jsx)(c,{className:`w-[28%]`,children:e(`connection.dialog.colDbms`)})]})}),(0,U.jsx)(u,{children:y.length===0?(0,U.jsx)(x,{children:(0,U.jsx)(S,{colSpan:3,className:`text-center text-muted-foreground`,children:e(`connection.dialog.noSaved`)})}):y.map(t=>(0,U.jsxs)(x,{"data-state":A===t.id?`selected`:void 0,className:`cursor-pointer`,onClick:()=>Y(t),onDoubleClick:()=>{Y(t),$()},children:[(0,U.jsx)(S,{className:`truncate font-medium`,title:t.hostName,children:t.hostName||e(`connection.dialog.unnamed`)}),(0,U.jsx)(S,{className:`truncate`,title:t.dbms===`sqlite`?t.file:t.dbms===`d1`?t.databaseId:`${t.user}@${t.host}:${t.port}`,children:t.dbms===`sqlite`?t.file||(I(t)===`browser`?e(`connection.dialog.pickOnConnect`):e(`connection.dialog.noFile`)):t.dbms===`d1`?t.databaseId||`—`:`${t.user}@${t.host}:${t.port}`}),(0,U.jsx)(S,{className:`truncate`,title:R(t.dbms).label,children:(0,U.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,U.jsx)(f,{dbms:t.dbms,className:`size-3.5`}),(0,U.jsx)(`span`,{className:`truncate`,children:R(t.dbms).label})]})})]},t.id))})]})})]}),(0,U.jsx)(`div`,{className:`flex-1 border rounded-md overflow-auto`,children:P?(0,U.jsxs)(U.Fragment,{children:[I(P)!==`browser`&&(0,U.jsx)(`div`,{className:`p-4 pb-0`,children:(0,U.jsx)(p,{})}),P.gatewayProfile?(0,U.jsxs)(`div`,{className:`p-4 space-y-2 text-sm`,children:[(0,U.jsxs)(`p`,{className:`font-medium flex items-center gap-2`,children:[(0,U.jsx)(f,{dbms:P.dbms,className:`size-4`}),P.hostName]}),(0,U.jsx)(`p`,{className:`text-muted-foreground`,children:e(`connection.dialog.gatewayProfileHint`,{name:P.gatewayProfile})}),(0,U.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[P.readOnly?e(`connection.dialog.gatewayReadOnly`):e(`connection.dialog.gatewayWritable`),P.database?` · ${P.database}`:``]})]}):(0,U.jsx)(pe,{value:P,onChange:me})]}):(0,U.jsx)(`div`,{className:`flex items-center justify-center h-full text-muted-foreground text-sm`,children:e(`connection.dialog.selectPrompt`)})})]}),(0,U.jsxs)(le,{className:`sm:justify-between`,children:[(0,U.jsxs)(`div`,{className:`flex gap-2`,children:[(0,U.jsx)(E,{variant:`outline`,onClick:he,children:e(`connection.dialog.addNew`)}),(0,U.jsx)(E,{variant:`destructive`,onClick:ge,disabled:!A,children:e(`connection.dialog.delete`)})]}),(0,U.jsxs)(`div`,{className:`flex gap-2`,children:[(0,U.jsx)(E,{variant:`outline`,onClick:be,disabled:!P||h,children:e(`connection.dialog.testConnection`)}),(0,U.jsx)(E,{onClick:$,disabled:!P||_e,children:e(`connection.dialog.connect`)}),(0,U.jsx)(E,{variant:`outline`,onClick:()=>J(!1),children:e(`connection.dialog.close`)})]})]})]})})}export{q as ConnectDialog};
|