sqlite-hub 0.17.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -47
- package/bin/sqlite-hub.js +206 -444
- package/examples/api/queries.js +31 -0
- package/examples/api/rows.js +27 -0
- package/frontend/assets/mockups/charts_1_bars_1200.webp +0 -0
- package/frontend/assets/mockups/charts_2_pie_1200.webp +0 -0
- package/frontend/assets/mockups/charts_3_scatter_plot_1200.webp +0 -0
- package/frontend/assets/mockups/connections_1200.webp +0 -0
- package/frontend/assets/mockups/data_1_1200.webp +0 -0
- package/frontend/assets/mockups/data_2_row_editor_1200.webp +0 -0
- package/frontend/assets/mockups/documents_1200.webp +0 -0
- package/frontend/assets/mockups/media_tagging_1_setup_1200.webp +0 -0
- package/frontend/assets/mockups/media_tagging_2_tagging_queue_1200.webp +0 -0
- package/frontend/assets/mockups/media_tagging_3_media_viewer_1200.webp +0 -0
- package/frontend/assets/mockups/overview_1200.webp +0 -0
- package/frontend/assets/mockups/settings_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_1_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_2_query_details_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_3_export_column_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_4_export_column_as_markdown_1200.webp +0 -0
- package/frontend/assets/mockups/sql_editor_5_export_query_result_1200.webp +0 -0
- package/frontend/assets/mockups/structure_1_1200.webp +0 -0
- package/frontend/assets/mockups/structure_2_inspector_1200.webp +0 -0
- package/frontend/assets/mockups/table_designer_1_1200.webp +0 -0
- package/frontend/assets/mockups/table_designer_2_checks_1200.webp +0 -0
- package/frontend/js/api.js +25 -0
- package/frontend/js/app.js +263 -34
- package/frontend/js/components/formControls.js +38 -0
- package/frontend/js/components/modal.js +77 -21
- package/frontend/js/components/queryChartRenderer.js +28 -3
- package/frontend/js/components/queryEditor.js +20 -24
- package/frontend/js/components/queryHistoryDetail.js +1 -1
- package/frontend/js/components/queryHistoryHeader.js +48 -0
- package/frontend/js/components/queryHistoryList.js +132 -0
- package/frontend/js/components/queryHistoryPanel.js +72 -136
- package/frontend/js/components/structureGraph.js +699 -89
- package/frontend/js/components/tableDesignerEditor.js +3 -5
- package/frontend/js/store.js +188 -7
- package/frontend/js/utils/exportFilenames.js +3 -1
- package/frontend/js/views/charts.js +320 -169
- package/frontend/js/views/connections.js +59 -64
- package/frontend/js/views/data.js +12 -20
- package/frontend/js/views/editor.js +0 -2
- package/frontend/js/views/settings.js +219 -5
- package/frontend/js/views/structure.js +27 -13
- package/frontend/styles/components.css +155 -0
- package/frontend/styles/structure-graph.css +140 -35
- package/frontend/styles/tailwind.generated.css +2 -2
- package/frontend/styles/views.css +12 -6
- package/package.json +7 -6
- package/server/middleware/apiTokenAuth.js +30 -0
- package/server/routes/connections.js +17 -0
- package/server/routes/export.js +89 -22
- package/server/routes/externalApi.js +304 -0
- package/server/routes/settings.js +90 -21
- package/server/server.js +22 -1
- package/server/services/apiTokenService.js +101 -0
- package/server/services/appInfoService.js +215 -0
- package/server/services/databaseCommandService.js +443 -0
- package/server/services/nativeFileDialogService.js +93 -1
- package/server/services/sqlite/exportService.js +307 -22
- package/server/services/storage/appStateStore.js +113 -0
- package/server/utils/errors.js +7 -0
- package/tests/api-token-auth.test.js +236 -0
- package/tests/cli-args.test.js +16 -3
- package/tests/cli-service-delegation.test.js +43 -0
- package/tests/connections-file-dialog-route.test.js +43 -0
- package/tests/copy-column-modal.test.js +34 -0
- package/tests/database-command-service.test.js +139 -0
- package/tests/export-blob.test.js +54 -1
- package/tests/export-filenames.test.js +4 -0
- package/tests/form-controls.test.js +34 -0
- package/tests/native-file-dialog.test.js +27 -0
- package/tests/settings-api-tokens-route.test.js +97 -0
- package/tests/settings-metadata.test.js +99 -1
- package/tests/settings-view.test.js +75 -0
- package/frontend/assets/mockups/connections.png +0 -0
- package/frontend/assets/mockups/data.png +0 -0
- package/frontend/assets/mockups/data_row_editor.png +0 -0
- package/frontend/assets/mockups/home.png +0 -0
- package/frontend/assets/mockups/sql_editor.png +0 -0
- package/frontend/assets/mockups/sql_editor_croped.png +0 -0
- package/frontend/assets/mockups/sql_editor_querydetail.png +0 -0
- package/frontend/assets/mockups/structure.png +0 -0
- package/frontend/assets/mockups/structure_inspector.png +0 -0
package/bin/sqlite-hub.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { spawn } = require('node:child_process');
|
|
6
|
+
const { DatabaseCommandService, getQueryTitle } = require('../server/services/databaseCommandService');
|
|
7
|
+
const { buildAppInfo } = require('../server/services/appInfoService');
|
|
6
8
|
|
|
7
9
|
const DEFAULT_PORT = 4173;
|
|
8
|
-
const EXPORT_FORMATS = new Set(['csv', 'tsv', 'md']);
|
|
10
|
+
const EXPORT_FORMATS = new Set(['csv', 'tsv', 'md', 'json']);
|
|
9
11
|
|
|
10
12
|
function printHelp() {
|
|
11
13
|
console.log(`SQLite Hub CLI
|
|
@@ -14,8 +16,10 @@ Usage:
|
|
|
14
16
|
sqlite-hub [--port:4173]
|
|
15
17
|
sqlite-hub --database
|
|
16
18
|
sqlite-hub --database:"name" --tables
|
|
19
|
+
sqlite-hub --database:"name" --query:"SELECT * FROM table_name"
|
|
20
|
+
sqlite-hub --database:"name" --query:"SELECT * FROM table_name" --store:"Query Name"
|
|
17
21
|
sqlite-hub --database:"name" --execute:"Saved Query"
|
|
18
|
-
sqlite-hub --database:"name" --query:"Saved Query"
|
|
22
|
+
sqlite-hub --database:"name" --saved-query:"Saved Query"
|
|
19
23
|
sqlite-hub --database:"name" --notes:"Saved Query"
|
|
20
24
|
sqlite-hub --database:"name" --export:"Saved Query" --format:csv
|
|
21
25
|
sqlite-hub --database:"name" --documents
|
|
@@ -27,7 +31,7 @@ Usage:
|
|
|
27
31
|
Options:
|
|
28
32
|
--help, -h Show this help text.
|
|
29
33
|
--version, -v Show the version number.
|
|
30
|
-
--
|
|
34
|
+
--info Show port, URL, app version, SQLite version, and update status.
|
|
31
35
|
--open Start/open SQLite Hub in the browser.
|
|
32
36
|
--port:PORT Start the server on a custom port.
|
|
33
37
|
--database, -d List all imported databases.
|
|
@@ -37,20 +41,18 @@ Options:
|
|
|
37
41
|
--lastopened Print the selected database last-opened timestamp.
|
|
38
42
|
--tables List tables in the selected database.
|
|
39
43
|
--queries List saved SQL Editor queries for the selected database.
|
|
44
|
+
--query:"sql" Execute raw SQL and record it in query history.
|
|
45
|
+
--store:"name" Save a raw --query history item with this name.
|
|
40
46
|
--execute:"query" Execute a saved SQL Editor query by name.
|
|
41
|
-
--query:"query"
|
|
47
|
+
--saved-query:"query" Print a saved SQL Editor query by name.
|
|
42
48
|
--notes:"query" Print notes for a saved SQL Editor query by name.
|
|
43
49
|
--export:"query" Export a saved query when --table is not set.
|
|
44
|
-
--format:csv|tsv|md
|
|
50
|
+
--format:csv|tsv|md|json Export format for query exports. Defaults to csv.
|
|
45
51
|
--documents List Markdown documents for the selected database.
|
|
46
52
|
--documents:"name" Print a document's Markdown content.
|
|
47
53
|
--documents:"name" --export Export a document as a Markdown file.
|
|
48
54
|
--table:"table" Print table metadata.
|
|
49
55
|
--table:"table" --export:"pk" Export one row as JSON by primary key or rowid.
|
|
50
|
-
|
|
51
|
-
Legacy aliases still work:
|
|
52
|
-
--database-path:name, --database-size:name, --database-lastopened:name
|
|
53
|
-
--database-tables:name, --database:name --sqleditor, --database:name --sqleditor:"query"
|
|
54
56
|
`);
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -142,7 +144,7 @@ function normalizeExportFormat(format) {
|
|
|
142
144
|
const normalized = String(format ?? 'csv').toLowerCase();
|
|
143
145
|
|
|
144
146
|
if (!EXPORT_FORMATS.has(normalized)) {
|
|
145
|
-
throw new Error(`Unsupported export format: ${format}. Use csv, tsv, or
|
|
147
|
+
throw new Error(`Unsupported export format: ${format}. Use csv, tsv, md, or json.`);
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
return normalized;
|
|
@@ -152,7 +154,7 @@ function parseCliArguments(argv) {
|
|
|
152
154
|
const options = {
|
|
153
155
|
help: false,
|
|
154
156
|
version: false,
|
|
155
|
-
|
|
157
|
+
info: false,
|
|
156
158
|
open: false,
|
|
157
159
|
port: undefined,
|
|
158
160
|
databaseList: false,
|
|
@@ -163,6 +165,8 @@ function parseCliArguments(argv) {
|
|
|
163
165
|
tables: false,
|
|
164
166
|
queries: false,
|
|
165
167
|
executeQuery: null,
|
|
168
|
+
rawQuery: null,
|
|
169
|
+
storeName: null,
|
|
166
170
|
showQuery: null,
|
|
167
171
|
showNotes: null,
|
|
168
172
|
exportTarget: null,
|
|
@@ -187,8 +191,8 @@ function parseCliArguments(argv) {
|
|
|
187
191
|
continue;
|
|
188
192
|
}
|
|
189
193
|
|
|
190
|
-
if (flag === '--config') {
|
|
191
|
-
options.
|
|
194
|
+
if (flag === '--info' || flag === '--config') {
|
|
195
|
+
options.info = true;
|
|
192
196
|
continue;
|
|
193
197
|
}
|
|
194
198
|
|
|
@@ -295,6 +299,20 @@ function parseCliArguments(argv) {
|
|
|
295
299
|
}
|
|
296
300
|
|
|
297
301
|
if (flag === '--query') {
|
|
302
|
+
const parsed = takeFlagValue(flag, value, argv, index);
|
|
303
|
+
options.rawQuery = parsed.value;
|
|
304
|
+
index = parsed.nextIndex;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (flag === '--store') {
|
|
309
|
+
const parsed = takeFlagValue(flag, value, argv, index);
|
|
310
|
+
options.storeName = parsed.value;
|
|
311
|
+
index = parsed.nextIndex;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (flag === '--saved-query') {
|
|
298
316
|
const parsed = takeFlagValue(flag, value, argv, index);
|
|
299
317
|
options.showQuery = parsed.value;
|
|
300
318
|
index = parsed.nextIndex;
|
|
@@ -375,18 +393,20 @@ function parseCliArguments(argv) {
|
|
|
375
393
|
function hasDatabaseOperation(options) {
|
|
376
394
|
return Boolean(
|
|
377
395
|
options.pathInfo ||
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
396
|
+
options.sizeInfo ||
|
|
397
|
+
options.lastOpenedInfo ||
|
|
398
|
+
options.tables ||
|
|
399
|
+
options.queries ||
|
|
400
|
+
options.executeQuery ||
|
|
401
|
+
options.rawQuery ||
|
|
402
|
+
options.storeName ||
|
|
403
|
+
options.showQuery ||
|
|
404
|
+
options.showNotes ||
|
|
405
|
+
options.documents ||
|
|
406
|
+
options.documentName ||
|
|
407
|
+
options.documentExport ||
|
|
408
|
+
options.exportTarget ||
|
|
409
|
+
options.tableName,
|
|
390
410
|
);
|
|
391
411
|
}
|
|
392
412
|
|
|
@@ -421,14 +441,6 @@ function openInDefaultBrowser(url) {
|
|
|
421
441
|
child.unref();
|
|
422
442
|
}
|
|
423
443
|
|
|
424
|
-
function findDatabaseByName(connections, name) {
|
|
425
|
-
const normalizedName = String(name ?? '').toLowerCase();
|
|
426
|
-
|
|
427
|
-
return connections.find(
|
|
428
|
-
conn => conn.label.toLowerCase() === normalizedName || conn.id.toLowerCase() === normalizedName,
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
444
|
function formatSize(bytes) {
|
|
433
445
|
if (!bytes) return 'N/A';
|
|
434
446
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -436,20 +448,6 @@ function formatSize(bytes) {
|
|
|
436
448
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
437
449
|
}
|
|
438
450
|
|
|
439
|
-
function sanitizeFilenameBase(value, fallback = 'export') {
|
|
440
|
-
const sanitized = String(value ?? '')
|
|
441
|
-
.replace(/[<>:"/\\|?*\u0000-\u001f]/g, ' ')
|
|
442
|
-
.replace(/\s+/g, ' ')
|
|
443
|
-
.trim()
|
|
444
|
-
.replace(/[. ]+$/g, '');
|
|
445
|
-
|
|
446
|
-
if (!sanitized) {
|
|
447
|
-
return fallback;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return sanitized.slice(0, 120);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
451
|
function createAppStateStore() {
|
|
454
452
|
const { resolveAppStatePaths } = require('../server/utils/appPaths');
|
|
455
453
|
const { AppStateStore } = require('../server/services/storage/appStateStore');
|
|
@@ -459,39 +457,6 @@ function createAppStateStore() {
|
|
|
459
457
|
return new AppStateStore(appStateDbPath);
|
|
460
458
|
}
|
|
461
459
|
|
|
462
|
-
function createReadOnlyRuntime(conn, appStateStore) {
|
|
463
|
-
const { ConnectionManager } = require('../server/services/sqlite/connectionManager');
|
|
464
|
-
const { DataBrowserService } = require('../server/services/sqlite/dataBrowserService');
|
|
465
|
-
const { ExportService } = require('../server/services/sqlite/exportService');
|
|
466
|
-
const { SqlExecutor } = require('../server/services/sqlite/sqlExecutor');
|
|
467
|
-
|
|
468
|
-
const connectionManager = new ConnectionManager({ appStateStore });
|
|
469
|
-
|
|
470
|
-
connectionManager.openConnection({
|
|
471
|
-
filePath: conn.path,
|
|
472
|
-
label: conn.label,
|
|
473
|
-
id: conn.id,
|
|
474
|
-
logoPath: conn.logoPath ?? null,
|
|
475
|
-
makeActive: false,
|
|
476
|
-
readOnly: true,
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
const sqlExecutor = new SqlExecutor({ connectionManager, appStateStore });
|
|
480
|
-
const exportService = new ExportService({ appStateStore, connectionManager, sqlExecutor });
|
|
481
|
-
const dataBrowserService = new DataBrowserService({ connectionManager });
|
|
482
|
-
|
|
483
|
-
return {
|
|
484
|
-
connectionManager,
|
|
485
|
-
db: connectionManager.getActiveDatabase(),
|
|
486
|
-
dataBrowserService,
|
|
487
|
-
exportService,
|
|
488
|
-
sqlExecutor,
|
|
489
|
-
close() {
|
|
490
|
-
connectionManager.closeCurrent();
|
|
491
|
-
},
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
|
|
495
460
|
function printDatabaseList(connections) {
|
|
496
461
|
if (connections.length === 0) {
|
|
497
462
|
console.log('No databases imported yet.');
|
|
@@ -538,72 +503,8 @@ function printTables(conn, tables) {
|
|
|
538
503
|
console.log('');
|
|
539
504
|
}
|
|
540
505
|
|
|
541
|
-
function
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
function findQueryByName(appStateStore, conn, queryName) {
|
|
546
|
-
const normalizedQueryName = String(queryName ?? '').trim().toLowerCase();
|
|
547
|
-
|
|
548
|
-
if (!normalizedQueryName) {
|
|
549
|
-
throw new Error('Query name is required.');
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
const collection = appStateStore.buildQueryHistoryCollection({
|
|
553
|
-
databaseKey: conn.id,
|
|
554
|
-
search: queryName,
|
|
555
|
-
onlySaved: false,
|
|
556
|
-
limit: 100,
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
return (
|
|
560
|
-
collection.items.find(item => {
|
|
561
|
-
const candidates = [item.id, item.title, item.displayTitle].filter(Boolean);
|
|
562
|
-
|
|
563
|
-
return candidates.some(candidate => String(candidate).toLowerCase() === normalizedQueryName);
|
|
564
|
-
}) ||
|
|
565
|
-
collection.items.find(item => String(item.rawSql ?? '').toLowerCase().includes(normalizedQueryName)) ||
|
|
566
|
-
null
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function getSavedQueries(appStateStore, conn, limit = 100) {
|
|
571
|
-
return appStateStore.buildQueryHistoryCollection({
|
|
572
|
-
databaseKey: conn.id,
|
|
573
|
-
onlySaved: true,
|
|
574
|
-
limit,
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function printAvailableQueries(appStateStore, conn) {
|
|
579
|
-
const allQueries = getSavedQueries(appStateStore, conn);
|
|
580
|
-
|
|
581
|
-
console.error('\nAvailable saved queries:');
|
|
582
|
-
|
|
583
|
-
if (allQueries.items.length > 0) {
|
|
584
|
-
allQueries.items.forEach(query => {
|
|
585
|
-
console.error(` - ${getQueryTitle(query)}`);
|
|
586
|
-
});
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
console.error(' (none)');
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function requireMatchingQuery(appStateStore, conn, queryName) {
|
|
594
|
-
const matchingQuery = findQueryByName(appStateStore, conn, queryName);
|
|
595
|
-
|
|
596
|
-
if (!matchingQuery) {
|
|
597
|
-
console.error(`Saved query not found: ${queryName}`);
|
|
598
|
-
printAvailableQueries(appStateStore, conn);
|
|
599
|
-
process.exit(1);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
return matchingQuery;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
function listSavedQueries(appStateStore, conn) {
|
|
606
|
-
const savedQueries = getSavedQueries(appStateStore, conn);
|
|
506
|
+
function listSavedQueries(databaseService, conn) {
|
|
507
|
+
const savedQueries = databaseService.listSavedQueries(conn.id);
|
|
607
508
|
|
|
608
509
|
if (savedQueries.items.length === 0) {
|
|
609
510
|
console.log(`No saved queries found for ${conn.label}.`);
|
|
@@ -664,28 +565,40 @@ function printExecutionResult(result) {
|
|
|
664
565
|
});
|
|
665
566
|
}
|
|
666
567
|
|
|
667
|
-
function executeSavedQuery({
|
|
668
|
-
const matchingQuery =
|
|
568
|
+
function executeSavedQuery({ databaseService, conn, queryName }) {
|
|
569
|
+
const { query: matchingQuery, result } = databaseService.executeSavedQuery(conn.id, queryName);
|
|
669
570
|
|
|
670
571
|
console.log(`\nExecuting: ${getQueryTitle(matchingQuery)}`);
|
|
671
572
|
console.log(`SQL: ${matchingQuery.previewSql || matchingQuery.rawSql}`);
|
|
672
573
|
console.log('─'.repeat(60));
|
|
673
574
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
575
|
+
printExecutionResult(result);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function executeRawQuery({ databaseService, conn, sql, storeName = null }) {
|
|
579
|
+
const { result, storedQuery } = databaseService.executeRawQuery(conn.id, sql, { storeName });
|
|
677
580
|
|
|
581
|
+
console.log(`\nExecuting raw SQL against: ${conn.label}`);
|
|
582
|
+
console.log('─'.repeat(60));
|
|
678
583
|
printExecutionResult(result);
|
|
584
|
+
|
|
585
|
+
if (result.historyId) {
|
|
586
|
+
console.log(`\nHistory ID: ${result.historyId}`);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (storedQuery) {
|
|
590
|
+
console.log(`Stored query: ${getQueryTitle(storedQuery)}`);
|
|
591
|
+
}
|
|
679
592
|
}
|
|
680
593
|
|
|
681
|
-
function showSavedQuery({
|
|
682
|
-
const matchingQuery =
|
|
594
|
+
function showSavedQuery({ databaseService, conn, queryName }) {
|
|
595
|
+
const matchingQuery = databaseService.getSavedQuery(conn.id, queryName);
|
|
683
596
|
|
|
684
597
|
console.log(matchingQuery.rawSql);
|
|
685
598
|
}
|
|
686
599
|
|
|
687
|
-
function showSavedQueryNotes({
|
|
688
|
-
const matchingQuery =
|
|
600
|
+
function showSavedQueryNotes({ databaseService, conn, queryName }) {
|
|
601
|
+
const matchingQuery = databaseService.getSavedQuery(conn.id, queryName);
|
|
689
602
|
const notes = String(matchingQuery.notes ?? '').trim();
|
|
690
603
|
|
|
691
604
|
if (notes) {
|
|
@@ -696,9 +609,8 @@ function showSavedQueryNotes({ appStateStore, conn, queryName }) {
|
|
|
696
609
|
console.log(`No notes saved for: ${getQueryTitle(matchingQuery)}`);
|
|
697
610
|
}
|
|
698
611
|
|
|
699
|
-
function exportSavedQuery({
|
|
700
|
-
const matchingQuery =
|
|
701
|
-
const result = exportService.exportQuery(matchingQuery.rawSql, { format });
|
|
612
|
+
function exportSavedQuery({ databaseService, conn, queryName, format }) {
|
|
613
|
+
const { query: matchingQuery, result } = databaseService.exportSavedQuery(conn.id, queryName, format);
|
|
702
614
|
const outputPath = path.resolve(process.cwd(), result.filename);
|
|
703
615
|
|
|
704
616
|
fs.writeFileSync(outputPath, result.content, 'utf8');
|
|
@@ -709,66 +621,8 @@ function exportSavedQuery({ appStateStore, conn, exportService, queryName, forma
|
|
|
709
621
|
console.log(`File: ${outputPath}`);
|
|
710
622
|
}
|
|
711
623
|
|
|
712
|
-
function
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
function printAvailableDocuments(appStateStore, conn) {
|
|
717
|
-
const documents = appStateStore.listDatabaseDocuments(conn.id);
|
|
718
|
-
|
|
719
|
-
console.error('\nAvailable documents:');
|
|
720
|
-
|
|
721
|
-
if (documents.length > 0) {
|
|
722
|
-
documents.forEach(document => {
|
|
723
|
-
console.error(` - ${getDocumentTitle(document)}`);
|
|
724
|
-
});
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
console.error(' (none)');
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
function findDocumentByName(appStateStore, conn, documentName) {
|
|
732
|
-
const normalizedDocumentName = String(documentName ?? '').trim().toLowerCase();
|
|
733
|
-
|
|
734
|
-
if (!normalizedDocumentName) {
|
|
735
|
-
throw new Error('Document name is required.');
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
const documents = appStateStore.listDatabaseDocuments(conn.id);
|
|
739
|
-
const exactMatch = documents.find(document => {
|
|
740
|
-
const candidates = [document.id, document.filename, document.title].filter(Boolean);
|
|
741
|
-
|
|
742
|
-
return candidates.some(candidate => String(candidate).toLowerCase() === normalizedDocumentName);
|
|
743
|
-
});
|
|
744
|
-
|
|
745
|
-
if (exactMatch) {
|
|
746
|
-
return appStateStore.getDatabaseDocument(conn.id, exactMatch.id);
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
const partialMatch = documents.find(document => {
|
|
750
|
-
const candidates = [document.filename, document.title].filter(Boolean);
|
|
751
|
-
|
|
752
|
-
return candidates.some(candidate => String(candidate).toLowerCase().includes(normalizedDocumentName));
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
return partialMatch ? appStateStore.getDatabaseDocument(conn.id, partialMatch.id) : null;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
function requireMatchingDocument(appStateStore, conn, documentName) {
|
|
759
|
-
const matchingDocument = findDocumentByName(appStateStore, conn, documentName);
|
|
760
|
-
|
|
761
|
-
if (!matchingDocument) {
|
|
762
|
-
console.error(`Document not found: ${documentName}`);
|
|
763
|
-
printAvailableDocuments(appStateStore, conn);
|
|
764
|
-
process.exit(1);
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
return matchingDocument;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
function listDocuments(appStateStore, conn) {
|
|
771
|
-
const documents = appStateStore.listDatabaseDocuments(conn.id);
|
|
624
|
+
function listDocuments(databaseService, conn) {
|
|
625
|
+
const documents = databaseService.listDocuments(conn.id);
|
|
772
626
|
|
|
773
627
|
if (documents.length === 0) {
|
|
774
628
|
console.log(`No documents found for ${conn.label}.`);
|
|
@@ -787,156 +641,27 @@ function listDocuments(appStateStore, conn) {
|
|
|
787
641
|
console.log('');
|
|
788
642
|
}
|
|
789
643
|
|
|
790
|
-
function showDocumentMarkdown({
|
|
791
|
-
const matchingDocument =
|
|
644
|
+
function showDocumentMarkdown({ databaseService, conn, documentName }) {
|
|
645
|
+
const matchingDocument = databaseService.getDocument(conn.id, documentName);
|
|
792
646
|
|
|
793
647
|
console.log(matchingDocument.content ?? '');
|
|
794
648
|
}
|
|
795
649
|
|
|
796
|
-
function
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
.replace(/[<>:"/\\|?*]+/g, ' ')
|
|
800
|
-
.replace(/\s+/g, ' ')
|
|
801
|
-
.trim()
|
|
802
|
-
.replace(/^\.+/, '')
|
|
803
|
-
.replace(/[. ]+$/g, '');
|
|
804
|
-
|
|
805
|
-
if (!normalizedFilename) {
|
|
806
|
-
normalizedFilename = fallback;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (!/\.md$/i.test(normalizedFilename)) {
|
|
810
|
-
normalizedFilename = `${normalizedFilename}.md`;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
if (normalizedFilename.length > 160) {
|
|
814
|
-
normalizedFilename = `${normalizedFilename.slice(0, 157)}.md`;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
return normalizedFilename;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
function exportDocumentMarkdown({ appStateStore, conn, documentName }) {
|
|
821
|
-
const matchingDocument = requireMatchingDocument(appStateStore, conn, documentName);
|
|
822
|
-
const filename = normalizeMarkdownExportFilename(matchingDocument.filename, `${matchingDocument.title || 'document'}.md`);
|
|
823
|
-
const outputPath = path.resolve(process.cwd(), filename);
|
|
650
|
+
function exportDocumentMarkdown({ databaseService, conn, documentName }) {
|
|
651
|
+
const result = databaseService.exportDocument(conn.id, documentName);
|
|
652
|
+
const outputPath = path.resolve(process.cwd(), result.filename);
|
|
824
653
|
|
|
825
|
-
fs.writeFileSync(outputPath,
|
|
654
|
+
fs.writeFileSync(outputPath, result.content, 'utf8');
|
|
826
655
|
|
|
827
|
-
console.log(`Exported document: ${
|
|
828
|
-
console.log(`Characters: ${
|
|
656
|
+
console.log(`Exported document: ${result.document.filename}`);
|
|
657
|
+
console.log(`Characters: ${result.document.contentLength}`);
|
|
829
658
|
console.log(`File: ${outputPath}`);
|
|
830
659
|
}
|
|
660
|
+
function exportTableRowAsJson({ databaseService, conn, tableName, exportTarget }) {
|
|
661
|
+
const result = databaseService.getTableRow(conn.id, tableName, exportTarget);
|
|
662
|
+
const outputPath = path.resolve(process.cwd(), result.filename);
|
|
831
663
|
|
|
832
|
-
|
|
833
|
-
const text = String(value ?? '');
|
|
834
|
-
const affinity = String(column?.affinity ?? '').toUpperCase();
|
|
835
|
-
|
|
836
|
-
if ((affinity === 'INTEGER' || affinity === 'REAL' || affinity === 'NUMERIC') && text.trim() !== '') {
|
|
837
|
-
const numberValue = Number(text);
|
|
838
|
-
|
|
839
|
-
if (Number.isFinite(numberValue)) {
|
|
840
|
-
return numberValue;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
return value;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
function parseCompositePrimaryKeyValue(rawValue) {
|
|
848
|
-
try {
|
|
849
|
-
const parsed = JSON.parse(rawValue);
|
|
850
|
-
|
|
851
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
852
|
-
return parsed;
|
|
853
|
-
}
|
|
854
|
-
} catch (error) {
|
|
855
|
-
// Fall through to the clearer error below.
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
throw new Error('Composite primary key export requires a JSON object, for example --export:\'{"id":1,"locale":"en"}\'.');
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
function buildIdentityFromExportTarget(tableDetail, exportTarget) {
|
|
862
|
-
if (tableDetail.identityStrategy?.type === 'rowid') {
|
|
863
|
-
const numberValue = Number(exportTarget);
|
|
864
|
-
const rowid = Number.isInteger(numberValue) ? numberValue : exportTarget;
|
|
865
|
-
|
|
866
|
-
return {
|
|
867
|
-
kind: 'rowid',
|
|
868
|
-
values: {
|
|
869
|
-
rowid,
|
|
870
|
-
},
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
if (tableDetail.identityStrategy?.type === 'primaryKey') {
|
|
875
|
-
const columns = tableDetail.identityStrategy.columns ?? [];
|
|
876
|
-
|
|
877
|
-
if (columns.length === 1) {
|
|
878
|
-
const columnName = columns[0];
|
|
879
|
-
const column = tableDetail.columns.find(candidate => candidate.name === columnName);
|
|
880
|
-
|
|
881
|
-
return {
|
|
882
|
-
kind: 'primaryKey',
|
|
883
|
-
columns,
|
|
884
|
-
values: {
|
|
885
|
-
[columnName]: coerceIdentityValue(column, exportTarget),
|
|
886
|
-
},
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
const parsed = parseCompositePrimaryKeyValue(exportTarget);
|
|
891
|
-
|
|
892
|
-
return {
|
|
893
|
-
kind: 'primaryKey',
|
|
894
|
-
columns,
|
|
895
|
-
values: Object.fromEntries(
|
|
896
|
-
columns.map(columnName => {
|
|
897
|
-
if (!Object.prototype.hasOwnProperty.call(parsed, columnName)) {
|
|
898
|
-
throw new Error(`Missing primary key value for ${columnName}.`);
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
const column = tableDetail.columns.find(candidate => candidate.name === columnName);
|
|
902
|
-
|
|
903
|
-
return [columnName, coerceIdentityValue(column, parsed[columnName])];
|
|
904
|
-
}),
|
|
905
|
-
),
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
throw new Error(`Table ${tableDetail.name} has no stable row identity.`);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
function buildDataRowEditorJsonObject({ row, columns = [] } = {}) {
|
|
913
|
-
const names = columns
|
|
914
|
-
.map(column => String(typeof column === 'object' ? column?.name : column ?? '').trim())
|
|
915
|
-
.filter(name => name && name !== '__identity');
|
|
916
|
-
const sourceNames = names.length
|
|
917
|
-
? names
|
|
918
|
-
: Object.keys(row ?? {}).filter(name => name !== '__identity');
|
|
919
|
-
|
|
920
|
-
return Object.fromEntries(
|
|
921
|
-
sourceNames
|
|
922
|
-
.map(name => [name, Object.prototype.hasOwnProperty.call(row ?? {}, name) ? row[name] : undefined])
|
|
923
|
-
.filter(([, value]) => value !== undefined),
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
function exportTableRowAsJson({ dataBrowserService, db, tableName, exportTarget }) {
|
|
928
|
-
const { getTableDetail } = require('../server/services/sqlite/introspection');
|
|
929
|
-
const tableDetail = getTableDetail(db, tableName, { includeRowCount: false });
|
|
930
|
-
const identity = buildIdentityFromExportTarget(tableDetail, exportTarget);
|
|
931
|
-
const { row } = dataBrowserService.getTableRow(tableName, { identity });
|
|
932
|
-
const rowObject = buildDataRowEditorJsonObject({
|
|
933
|
-
row,
|
|
934
|
-
columns: tableDetail.columns.filter(column => column.visible),
|
|
935
|
-
});
|
|
936
|
-
const filenameBase = sanitizeFilenameBase(`${tableName}-${exportTarget}`, `${tableName}-row`);
|
|
937
|
-
const outputPath = path.resolve(process.cwd(), `${filenameBase}.json`);
|
|
938
|
-
|
|
939
|
-
fs.writeFileSync(outputPath, `${JSON.stringify(rowObject, null, 2)}\n`, 'utf8');
|
|
664
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(result.data, null, 2)}\n`, 'utf8');
|
|
940
665
|
|
|
941
666
|
console.log(`Exported row: ${tableName}`);
|
|
942
667
|
console.log(`Key: ${exportTarget}`);
|
|
@@ -997,28 +722,51 @@ function printTableInfo(tableDetail) {
|
|
|
997
722
|
console.log(`Indexes: ${tableDetail.indexes.length}`);
|
|
998
723
|
tableDetail.indexes.forEach(index => {
|
|
999
724
|
const unique = index.unique ? ' UNIQUE' : '';
|
|
1000
|
-
const columns =
|
|
725
|
+
const columns =
|
|
726
|
+
index.columns
|
|
727
|
+
.map(column => column.name)
|
|
728
|
+
.filter(Boolean)
|
|
729
|
+
.join(', ') || 'expression';
|
|
1001
730
|
console.log(` - ${index.name}${unique}: ${columns}`);
|
|
1002
731
|
});
|
|
1003
732
|
}
|
|
1004
733
|
}
|
|
1005
734
|
|
|
1006
|
-
function
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
735
|
+
function formatVersionStatus(versionCheck) {
|
|
736
|
+
if (!versionCheck || versionCheck.status === 'unknown') {
|
|
737
|
+
return `unknown${versionCheck?.error?.message ? ` (${versionCheck.error.message})` : ''}`;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (versionCheck.updateAvailable) {
|
|
741
|
+
return `update available (${versionCheck.currentVersion} -> ${versionCheck.latestVersion})`;
|
|
742
|
+
}
|
|
1010
743
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
744
|
+
if (versionCheck.status === 'ahead') {
|
|
745
|
+
return `ahead of npm latest (${versionCheck.currentVersion} > ${versionCheck.latestVersion})`;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return `current (${versionCheck.currentVersion})`;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
async function printInfo(port, options = {}) {
|
|
752
|
+
const infoService = options.appInfoService ?? buildAppInfo;
|
|
753
|
+
const url = `http://127.0.0.1:${port}`;
|
|
754
|
+
const info = await infoService({ port, url });
|
|
755
|
+
|
|
756
|
+
console.log('SQLite Hub info');
|
|
757
|
+
console.log(`Port: ${info.port}`);
|
|
758
|
+
console.log(`URL: ${info.url}`);
|
|
759
|
+
console.log(`Package: ${info.packageName}`);
|
|
760
|
+
console.log(`App version: ${info.appVersion}`);
|
|
761
|
+
console.log(`SQLite version: ${info.sqliteVersion}`);
|
|
762
|
+
console.log(`Version status: ${formatVersionStatus(info.versionCheck)}`);
|
|
763
|
+
|
|
764
|
+
if (info.versionCheck?.latestVersion) {
|
|
765
|
+
console.log(`Latest version: ${info.versionCheck.latestVersion}`);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (info.versionCheck?.releaseUrl) {
|
|
769
|
+
console.log(`Release URL: ${info.versionCheck.releaseUrl}`);
|
|
1022
770
|
}
|
|
1023
771
|
}
|
|
1024
772
|
|
|
@@ -1049,8 +797,8 @@ function requireDatabaseName(options) {
|
|
|
1049
797
|
return options.databaseName;
|
|
1050
798
|
}
|
|
1051
799
|
|
|
1052
|
-
async function main() {
|
|
1053
|
-
const options = parseCliArguments(
|
|
800
|
+
async function main(argv = process.argv.slice(2), dependencies = {}) {
|
|
801
|
+
const options = parseCliArguments(argv);
|
|
1054
802
|
const port = options.port ?? DEFAULT_PORT;
|
|
1055
803
|
|
|
1056
804
|
if (options.help) {
|
|
@@ -1064,8 +812,8 @@ async function main() {
|
|
|
1064
812
|
return;
|
|
1065
813
|
}
|
|
1066
814
|
|
|
1067
|
-
if (options.
|
|
1068
|
-
|
|
815
|
+
if (options.info) {
|
|
816
|
+
await printInfo(port, dependencies);
|
|
1069
817
|
return;
|
|
1070
818
|
}
|
|
1071
819
|
|
|
@@ -1074,8 +822,16 @@ async function main() {
|
|
|
1074
822
|
return;
|
|
1075
823
|
}
|
|
1076
824
|
|
|
1077
|
-
|
|
1078
|
-
|
|
825
|
+
if (options.storeName && !options.rawQuery) {
|
|
826
|
+
throw new Error('--store requires --query:"sql".');
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const databaseService =
|
|
830
|
+
dependencies.databaseService ??
|
|
831
|
+
new DatabaseCommandService({
|
|
832
|
+
appStateStore: dependencies.appStateStore ?? createAppStateStore(),
|
|
833
|
+
});
|
|
834
|
+
const connections = databaseService.listDatabases();
|
|
1079
835
|
|
|
1080
836
|
if (options.databaseList && !options.databaseName && !hasDatabaseOperation(options)) {
|
|
1081
837
|
printDatabaseList(connections);
|
|
@@ -1084,24 +840,19 @@ async function main() {
|
|
|
1084
840
|
|
|
1085
841
|
if (options.databaseName || hasDatabaseOperation(options)) {
|
|
1086
842
|
const dbName = requireDatabaseName(options);
|
|
1087
|
-
const conn =
|
|
1088
|
-
|
|
1089
|
-
if (!conn) {
|
|
1090
|
-
console.error(`Database not found: ${dbName}`);
|
|
1091
|
-
process.exit(1);
|
|
1092
|
-
}
|
|
843
|
+
const conn = databaseService.getDatabase(dbName);
|
|
1093
844
|
|
|
1094
845
|
if (options.documents) {
|
|
1095
846
|
if (options.documentName) {
|
|
1096
847
|
if (options.documentExport) {
|
|
1097
848
|
exportDocumentMarkdown({
|
|
1098
|
-
|
|
849
|
+
databaseService,
|
|
1099
850
|
conn,
|
|
1100
851
|
documentName: options.documentName,
|
|
1101
852
|
});
|
|
1102
853
|
} else {
|
|
1103
854
|
showDocumentMarkdown({
|
|
1104
|
-
|
|
855
|
+
databaseService,
|
|
1105
856
|
conn,
|
|
1106
857
|
documentName: options.documentName,
|
|
1107
858
|
});
|
|
@@ -1109,73 +860,82 @@ async function main() {
|
|
|
1109
860
|
return;
|
|
1110
861
|
}
|
|
1111
862
|
|
|
1112
|
-
listDocuments(
|
|
863
|
+
listDocuments(databaseService, conn);
|
|
1113
864
|
return;
|
|
1114
865
|
}
|
|
1115
866
|
|
|
1116
|
-
if (
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
tableName: options.tableName,
|
|
1128
|
-
exportTarget: options.exportTarget,
|
|
1129
|
-
});
|
|
1130
|
-
} else {
|
|
1131
|
-
printTableInfo(getTableDetail(runtime.db, options.tableName));
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
867
|
+
if (
|
|
868
|
+
options.tableName ||
|
|
869
|
+
options.tables ||
|
|
870
|
+
options.queries ||
|
|
871
|
+
options.executeQuery ||
|
|
872
|
+
options.rawQuery ||
|
|
873
|
+
options.showQuery ||
|
|
874
|
+
options.showNotes ||
|
|
875
|
+
options.exportTarget
|
|
876
|
+
) {
|
|
877
|
+
if (options.tableName) {
|
|
1137
878
|
if (options.exportTarget) {
|
|
1138
|
-
|
|
1139
|
-
|
|
879
|
+
exportTableRowAsJson({
|
|
880
|
+
databaseService,
|
|
1140
881
|
conn,
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
format: options.exportFormat,
|
|
882
|
+
tableName: options.tableName,
|
|
883
|
+
exportTarget: options.exportTarget,
|
|
1144
884
|
});
|
|
1145
|
-
|
|
885
|
+
} else {
|
|
886
|
+
printTableInfo(databaseService.getTable(conn.id, options.tableName));
|
|
1146
887
|
}
|
|
1147
888
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
appStateStore,
|
|
1151
|
-
conn,
|
|
1152
|
-
sqlExecutor: runtime.sqlExecutor,
|
|
1153
|
-
queryName: options.executeQuery,
|
|
1154
|
-
});
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
1157
891
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
892
|
+
if (options.exportTarget) {
|
|
893
|
+
exportSavedQuery({
|
|
894
|
+
databaseService,
|
|
895
|
+
conn,
|
|
896
|
+
queryName: options.exportTarget,
|
|
897
|
+
format: options.exportFormat,
|
|
898
|
+
});
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
1162
901
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
902
|
+
if (options.executeQuery) {
|
|
903
|
+
executeSavedQuery({
|
|
904
|
+
databaseService,
|
|
905
|
+
conn,
|
|
906
|
+
queryName: options.executeQuery,
|
|
907
|
+
});
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
1167
910
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
911
|
+
if (options.rawQuery) {
|
|
912
|
+
executeRawQuery({
|
|
913
|
+
databaseService,
|
|
914
|
+
conn,
|
|
915
|
+
sql: options.rawQuery,
|
|
916
|
+
storeName: options.storeName,
|
|
917
|
+
});
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
1172
920
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
921
|
+
if (options.showQuery) {
|
|
922
|
+
showSavedQuery({ databaseService, conn, queryName: options.showQuery });
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (options.showNotes) {
|
|
927
|
+
showSavedQueryNotes({ databaseService, conn, queryName: options.showNotes });
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
if (options.queries) {
|
|
932
|
+
listSavedQueries(databaseService, conn);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if (options.tables) {
|
|
937
|
+
printTables(conn, databaseService.listTables(conn.id));
|
|
938
|
+
return;
|
|
1179
939
|
}
|
|
1180
940
|
}
|
|
1181
941
|
|
|
@@ -1215,7 +975,9 @@ if (require.main === module) {
|
|
|
1215
975
|
|
|
1216
976
|
module.exports = {
|
|
1217
977
|
main,
|
|
978
|
+
formatVersionStatus,
|
|
1218
979
|
normalizeExportFormat,
|
|
1219
980
|
openInDefaultBrowser,
|
|
1220
981
|
parseCliArguments,
|
|
982
|
+
printInfo,
|
|
1221
983
|
};
|