sqlite-hub 1.0.0 → 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.
Files changed (40) hide show
  1. package/README.md +47 -33
  2. package/bin/sqlite-hub.js +127 -47
  3. package/frontend/js/api.js +6 -0
  4. package/frontend/js/app.js +199 -34
  5. package/frontend/js/components/modal.js +17 -1
  6. package/frontend/js/components/queryChartRenderer.js +28 -3
  7. package/frontend/js/components/queryEditor.js +20 -24
  8. package/frontend/js/components/queryHistoryDetail.js +1 -1
  9. package/frontend/js/components/queryHistoryHeader.js +48 -0
  10. package/frontend/js/components/queryHistoryList.js +132 -0
  11. package/frontend/js/components/queryHistoryPanel.js +72 -136
  12. package/frontend/js/components/structureGraph.js +699 -89
  13. package/frontend/js/components/tableDesignerEditor.js +3 -5
  14. package/frontend/js/store.js +73 -7
  15. package/frontend/js/utils/exportFilenames.js +3 -1
  16. package/frontend/js/views/charts.js +320 -169
  17. package/frontend/js/views/connections.js +59 -64
  18. package/frontend/js/views/data.js +12 -20
  19. package/frontend/js/views/editor.js +0 -2
  20. package/frontend/js/views/settings.js +78 -0
  21. package/frontend/js/views/structure.js +27 -13
  22. package/frontend/styles/components.css +155 -0
  23. package/frontend/styles/structure-graph.css +140 -35
  24. package/frontend/styles/tailwind.generated.css +1 -1
  25. package/frontend/styles/views.css +12 -6
  26. package/package.json +3 -2
  27. package/server/routes/export.js +89 -22
  28. package/server/routes/externalApi.js +84 -2
  29. package/server/routes/settings.js +37 -28
  30. package/server/services/appInfoService.js +215 -0
  31. package/server/services/databaseCommandService.js +50 -6
  32. package/server/services/sqlite/exportService.js +307 -22
  33. package/tests/api-token-auth.test.js +110 -1
  34. package/tests/cli-args.test.js +16 -3
  35. package/tests/database-command-service.test.js +39 -2
  36. package/tests/export-blob.test.js +54 -1
  37. package/tests/export-filenames.test.js +4 -0
  38. package/tests/settings-api-tokens-route.test.js +22 -1
  39. package/tests/settings-metadata.test.js +99 -1
  40. package/tests/settings-view.test.js +28 -0
package/README.md CHANGED
@@ -19,7 +19,7 @@ SQLite Hub keeps that workflow sharp:
19
19
  - filter, sort, page through, and export table data
20
20
  - inspect schema, structure, and relationships
21
21
  - edit records in place with typed value previews and an SQL diff preview before saving
22
- - export tables and query results as CSV, TSV, Markdown, or duplicate them as a table
22
+ - export tables and query results as CSV, TSV, Markdown, JSON, Parquet, or duplicate them as a table
23
23
  - copy result columns with formatting, headers, first-10 previews, TXT export, and Markdown todo export
24
24
  - keep database-scoped Markdown documents with previews, autosave, imports, exports, and saved-query inserts
25
25
  - switch between recent databases with sidebar quick picks
@@ -58,13 +58,13 @@ The database overview combines operational and schema information for the active
58
58
  <a href="./frontend/assets/mockups/structure_2_inspector_1200.webp"><img src="./frontend/assets/mockups/structure_2_inspector_1200.webp" alt="SQLite Hub structure inspector" width="49%"></a>
59
59
  </p>
60
60
 
61
- Inspect tables, views, indexes, triggers, columns, declared types, primary keys, nullability, foreign keys, and DDL without losing pace. The searchable object list and relationship graph support fit, relayout, selection clearing, direct navigation to table data, and a hideable inspector/sidebar. SQLite Hub remembers the last selected table while you move between views, and DDL can be copied directly from the inspector.
61
+ Inspect tables, views, indexes, triggers, columns, declared types, primary keys, nullability, foreign keys, and DDL without losing pace. The searchable object list and relationship graph support fit, relayout, selection clearing, direct navigation to table data, and a hideable inspector/sidebar. Clicking a relationship edge opens a join preview with the mapped columns and a copyable SQL `JOIN` snippet. SQLite Hub remembers the last selected table while you move between views, and DDL can be copied directly from the inspector.
62
62
 
63
63
  ### Data browser
64
64
 
65
65
  [![SQLite Hub data browser](./frontend/assets/mockups/data_1_1200.webp)](./frontend/assets/mockups/data_1_1200.webp)
66
66
 
67
- Scan rows, sort columns, move through local data quickly, and export full tables as CSV, TSV, or Markdown. The Data browser also supports duplicating exports as a new table, searchable and hideable table navigation, page sizes up to 250 rows, and advanced filters with column/operator/value controls. Text filters support case-insensitive `contains`, `not contains`, and exact `equals` matching.
67
+ Scan rows, sort columns, move through local data quickly, and export full tables as CSV, TSV, Markdown, JSON, or Parquet. The Data browser also supports duplicating exports as a new table, searchable and hideable table navigation, page sizes up to 250 rows, and advanced filters with column/operator/value controls. Text filters support case-insensitive `contains`, `not contains`, and exact `equals` matching.
68
68
 
69
69
  Wide tables keep their horizontal scroll position when sorting causes the grid to re-render. Cells use compact previews for long values, BLOBs, and detected file paths, while exports retain complete BLOB content.
70
70
 
@@ -89,9 +89,9 @@ The Row Editor adds contextual previews without changing the stored raw value:
89
89
 
90
90
  [![SQLite Hub SQL editor](./frontend/assets/mockups/sql_editor_1_1200.webp)](./frontend/assets/mockups/sql_editor_1_1200.webp)
91
91
 
92
- Write queries in a syntax-highlighted editor, execute them with the Run button or `Shift + Enter`, format SQL with the editor Format button, inspect results in the same workflow, and export result sets as CSV, TSV, Markdown, or duplicate them as a table. Query drafts survive reloads, query history can be searched and saved, and direct single-table `SELECT` results can be edited or deleted from the result grid when a stable row identity is available.
92
+ Write queries in a syntax-highlighted editor, execute them with the Run button or `Shift + Enter`, format SQL with the editor Format button, inspect results in the same workflow, and export result sets as CSV, TSV, Markdown, JSON, Parquet, or duplicate them as a table. Query drafts survive reloads, query history can be searched and saved, and direct single-table `SELECT` results can be edited or deleted from the result grid when a stable row identity is available.
93
93
 
94
- Interactive result sets are limited to the first 5,000 rows to keep the application responsive. A visible notice and Messages entry indicate truncation; CSV, TSV, Markdown, and duplicate-table exports execute without that interactive row limit and include complete BLOB values. Sorting wide results preserves the horizontal scroll position.
94
+ Interactive result sets are limited to the first 5,000 rows to keep the application responsive. A visible notice and Messages entry indicate truncation; CSV, TSV, Markdown, JSON, Parquet, and duplicate-table exports execute without that interactive row limit and include complete BLOB values. Sorting wide results preserves the horizontal scroll position.
95
95
 
96
96
  [![SQLite Hub query export](./frontend/assets/mockups/sql_editor_5_export_query_result_1200.webp)](./frontend/assets/mockups/sql_editor_5_export_query_result_1200.webp)
97
97
 
@@ -215,7 +215,7 @@ SQLite Hub ships with a built-in CLI that lets you start the app or query inform
215
215
  sqlite-hub # start on default port 4173
216
216
  sqlite-hub --port:4174 # start on a custom port
217
217
  sqlite-hub --open # open SQLite Hub in the browser
218
- sqlite-hub --config # show port, URL, app version, and SQLite version
218
+ sqlite-hub --info # show port, URL, versions, and update status
219
219
  sqlite-hub --help # show help text
220
220
  sqlite-hub --version # show version number
221
221
  ```
@@ -261,7 +261,18 @@ sqlite-hub --database:Billly --table:companies
261
261
 
262
262
  Prints table metadata such as columns, primary keys, foreign keys, indexes, row count, and row identity strategy.
263
263
 
264
- ### SQL Editor - Saved Queries
264
+ ### SQL Editor - Raw SQL And Saved Queries
265
+
266
+ Execute raw SQL through the same SQL Editor execution path used by the app:
267
+
268
+ ```bash
269
+ sqlite-hub --database:Unit-00 --query:"SELECT * FROM companies LIMIT 10"
270
+ sqlite-hub --database:Unit-00 --query:"SELECT * FROM companies LIMIT 10" --store:"Company Sample"
271
+ ```
272
+
273
+ Raw CLI queries are recorded in Query History. Add `--store:"name"` to title the
274
+ history item and mark it as saved. Raw SQL execution is rejected when the target
275
+ database is marked read-only.
265
276
 
266
277
  List all saved queries for a database:
267
278
 
@@ -280,7 +291,7 @@ This searches the query history for the given database, finds the matching saved
280
291
  Show the saved query SQL without executing it:
281
292
 
282
293
  ```bash
283
- sqlite-hub --database:Unit-00 --query:"Stock Winners"
294
+ sqlite-hub --database:Unit-00 --saved-query:"Stock Winners"
284
295
  ```
285
296
 
286
297
  Show the saved notes for a query:
@@ -289,12 +300,13 @@ Show the saved notes for a query:
289
300
  sqlite-hub --database:Unit-00 --notes:"TOP25 Loser and Winner EOD, T1, T3, T5"
290
301
  ```
291
302
 
292
- Export a saved query using the same CSV, TSV, and Markdown export logic as the SQL Editor:
303
+ Export a saved query using the same CSV, TSV, Markdown, and JSON export logic as the SQL Editor:
293
304
 
294
305
  ```bash
295
306
  sqlite-hub --database:Unit-00 --export:"Stock Winners" --format:csv
296
307
  sqlite-hub --database:Unit-00 --export:"Stock Winners" --format:tsv
297
308
  sqlite-hub --database:Unit-00 --export:"Stock Winners" --format:md
309
+ sqlite-hub --database:Unit-00 --export:"Stock Winners" --format:json
298
310
  ```
299
311
 
300
312
  The export is written to the current working directory using the generated query export filename.
@@ -332,29 +344,31 @@ sqlite-hub --database:Unit-00 --table:companies --export:0a754aba373d34972998792
332
344
 
333
345
  ### Available flags
334
346
 
335
- | Flag | Description |
336
- | -------------------------------------------------------- | ----------------------------------------------- |
337
- | `--help`, `-h` | Show help text |
338
- | `--version`, `-v` | Show version number |
339
- | `--config` | Show port, URL, app version, and SQLite version |
340
- | `--open` | Open SQLite Hub in the browser |
341
- | `--port:PORT` | Start the server on a custom port |
342
- | `--database`, `-d` | List all imported databases |
343
- | `--database:name` | Select a database by name or id |
344
- | `--database:name --path` | Get the file path of a database |
345
- | `--database:name --size` | Get the size of a database |
346
- | `--database:name --lastopened` | Get the last opened timestamp |
347
- | `--database:name --tables` | Get all table names from a database |
348
- | `--database:name --queries` | List saved queries for a database |
349
- | `--database:name --execute:"query"` | Execute a saved query by name |
350
- | `--database:name --query:"query"` | Print a saved query by name |
351
- | `--database:name --notes:"query"` | Print saved notes for a query |
352
- | `--database:name --export:"query" --format:csv\|tsv\|md` | Set query export format |
353
- | `--database:name --documents` | List Markdown documents for a database |
354
- | `--database:name --documents:"document"` | Print a document's Markdown content |
355
- | `--database:name --documents:"document" --export` | Export a document as Markdown |
356
- | `--database:name --table:"table"` | Print table metadata |
357
- | `--database:name --table:"table" --export:"pk"` | Export one row as JSON |
347
+ | Flag | Description |
348
+ | --------------------------------------------------------------- | ----------------------------------------------- |
349
+ | `--help`, `-h` | Show help text |
350
+ | `--version`, `-v` | Show version number |
351
+ | `--info` | Show port, URL, versions, and update status |
352
+ | `--open` | Open SQLite Hub in the browser |
353
+ | `--port:PORT` | Start the server on a custom port |
354
+ | `--database`, `-d` | List all imported databases |
355
+ | `--database:name` | Select a database by name or id |
356
+ | `--database:name --path` | Get the file path of a database |
357
+ | `--database:name --size` | Get the size of a database |
358
+ | `--database:name --lastopened` | Get the last opened timestamp |
359
+ | `--database:name --tables` | Get all table names from a database |
360
+ | `--database:name --queries` | List saved queries for a database |
361
+ | `--database:name --query:"sql"` | Execute raw SQL and record it in Query History |
362
+ | `--database:name --query:"sql" --store:"name"` | Save a raw query in Query History with a name |
363
+ | `--database:name --execute:"query"` | Execute a saved query by name |
364
+ | `--database:name --saved-query:"query"` | Print a saved query by name |
365
+ | `--database:name --notes:"query"` | Print saved notes for a query |
366
+ | `--database:name --export:"query" --format:csv\|tsv\|md\|json` | Set query export format |
367
+ | `--database:name --documents` | List Markdown documents for a database |
368
+ | `--database:name --documents:"document"` | Print a document's Markdown content |
369
+ | `--database:name --documents:"document" --export` | Export a document as Markdown |
370
+ | `--database:name --table:"table"` | Print table metadata |
371
+ | `--database:name --table:"table" --export:"pk"` | Export one row as JSON |
358
372
 
359
373
  Legacy aliases such as `--database-path:name`, `--database-size:name`, `--database-lastopened:name`, `--database-tables:name`, and `--database:name --sqleditor:"query"` still work.
360
374
 
@@ -388,7 +402,7 @@ Results:
388
402
 
389
403
  ## API
390
404
 
391
- SQLite Hub also provides a local JSON API for database metadata, tables, saved queries, exports, and documents. Access is protected by database-specific API tokens created in Settings. See the [API documentation](./docs/API.md) for authentication, endpoints, and examples.
405
+ SQLite Hub also provides a local JSON API for app info, database metadata, tables, saved queries, exports, and documents. `/api/v1/info` returns the same app/version status as `sqlite-hub --info`; database data is protected by database-specific API tokens created in Settings. See the [API documentation](./docs/API.md) for authentication, endpoints, and examples.
392
406
 
393
407
  ## Changelog
394
408
 
package/bin/sqlite-hub.js CHANGED
@@ -3,13 +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 {
7
- DatabaseCommandService,
8
- getQueryTitle,
9
- } = require('../server/services/databaseCommandService');
6
+ const { DatabaseCommandService, getQueryTitle } = require('../server/services/databaseCommandService');
7
+ const { buildAppInfo } = require('../server/services/appInfoService');
10
8
 
11
9
  const DEFAULT_PORT = 4173;
12
- const EXPORT_FORMATS = new Set(['csv', 'tsv', 'md']);
10
+ const EXPORT_FORMATS = new Set(['csv', 'tsv', 'md', 'json']);
13
11
 
14
12
  function printHelp() {
15
13
  console.log(`SQLite Hub CLI
@@ -18,8 +16,10 @@ Usage:
18
16
  sqlite-hub [--port:4173]
19
17
  sqlite-hub --database
20
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"
21
21
  sqlite-hub --database:"name" --execute:"Saved Query"
22
- sqlite-hub --database:"name" --query:"Saved Query"
22
+ sqlite-hub --database:"name" --saved-query:"Saved Query"
23
23
  sqlite-hub --database:"name" --notes:"Saved Query"
24
24
  sqlite-hub --database:"name" --export:"Saved Query" --format:csv
25
25
  sqlite-hub --database:"name" --documents
@@ -31,7 +31,7 @@ Usage:
31
31
  Options:
32
32
  --help, -h Show this help text.
33
33
  --version, -v Show the version number.
34
- --config Show CLI port, URL, app version, and SQLite version.
34
+ --info Show port, URL, app version, SQLite version, and update status.
35
35
  --open Start/open SQLite Hub in the browser.
36
36
  --port:PORT Start the server on a custom port.
37
37
  --database, -d List all imported databases.
@@ -41,20 +41,18 @@ Options:
41
41
  --lastopened Print the selected database last-opened timestamp.
42
42
  --tables List tables in the selected database.
43
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.
44
46
  --execute:"query" Execute a saved SQL Editor query by name.
45
- --query:"query" Print a saved SQL Editor query by name.
47
+ --saved-query:"query" Print a saved SQL Editor query by name.
46
48
  --notes:"query" Print notes for a saved SQL Editor query by name.
47
49
  --export:"query" Export a saved query when --table is not set.
48
- --format:csv|tsv|md Export format for query exports. Defaults to csv.
50
+ --format:csv|tsv|md|json Export format for query exports. Defaults to csv.
49
51
  --documents List Markdown documents for the selected database.
50
52
  --documents:"name" Print a document's Markdown content.
51
53
  --documents:"name" --export Export a document as a Markdown file.
52
54
  --table:"table" Print table metadata.
53
55
  --table:"table" --export:"pk" Export one row as JSON by primary key or rowid.
54
-
55
- Legacy aliases still work:
56
- --database-path:name, --database-size:name, --database-lastopened:name
57
- --database-tables:name, --database:name --sqleditor, --database:name --sqleditor:"query"
58
56
  `);
59
57
  }
60
58
 
@@ -146,7 +144,7 @@ function normalizeExportFormat(format) {
146
144
  const normalized = String(format ?? 'csv').toLowerCase();
147
145
 
148
146
  if (!EXPORT_FORMATS.has(normalized)) {
149
- throw new Error(`Unsupported export format: ${format}. Use csv, tsv, or md.`);
147
+ throw new Error(`Unsupported export format: ${format}. Use csv, tsv, md, or json.`);
150
148
  }
151
149
 
152
150
  return normalized;
@@ -156,7 +154,7 @@ function parseCliArguments(argv) {
156
154
  const options = {
157
155
  help: false,
158
156
  version: false,
159
- config: false,
157
+ info: false,
160
158
  open: false,
161
159
  port: undefined,
162
160
  databaseList: false,
@@ -167,6 +165,8 @@ function parseCliArguments(argv) {
167
165
  tables: false,
168
166
  queries: false,
169
167
  executeQuery: null,
168
+ rawQuery: null,
169
+ storeName: null,
170
170
  showQuery: null,
171
171
  showNotes: null,
172
172
  exportTarget: null,
@@ -191,8 +191,8 @@ function parseCliArguments(argv) {
191
191
  continue;
192
192
  }
193
193
 
194
- if (flag === '--config') {
195
- options.config = true;
194
+ if (flag === '--info' || flag === '--config') {
195
+ options.info = true;
196
196
  continue;
197
197
  }
198
198
 
@@ -299,6 +299,20 @@ function parseCliArguments(argv) {
299
299
  }
300
300
 
301
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') {
302
316
  const parsed = takeFlagValue(flag, value, argv, index);
303
317
  options.showQuery = parsed.value;
304
318
  index = parsed.nextIndex;
@@ -379,18 +393,20 @@ function parseCliArguments(argv) {
379
393
  function hasDatabaseOperation(options) {
380
394
  return Boolean(
381
395
  options.pathInfo ||
382
- options.sizeInfo ||
383
- options.lastOpenedInfo ||
384
- options.tables ||
385
- options.queries ||
386
- options.executeQuery ||
387
- options.showQuery ||
388
- options.showNotes ||
389
- options.documents ||
390
- options.documentName ||
391
- options.documentExport ||
392
- options.exportTarget ||
393
- options.tableName,
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,
394
410
  );
395
411
  }
396
412
 
@@ -559,6 +575,22 @@ function executeSavedQuery({ databaseService, conn, queryName }) {
559
575
  printExecutionResult(result);
560
576
  }
561
577
 
578
+ function executeRawQuery({ databaseService, conn, sql, storeName = null }) {
579
+ const { result, storedQuery } = databaseService.executeRawQuery(conn.id, sql, { storeName });
580
+
581
+ console.log(`\nExecuting raw SQL against: ${conn.label}`);
582
+ console.log('─'.repeat(60));
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
+ }
592
+ }
593
+
562
594
  function showSavedQuery({ databaseService, conn, queryName }) {
563
595
  const matchingQuery = databaseService.getSavedQuery(conn.id, queryName);
564
596
 
@@ -690,28 +722,51 @@ function printTableInfo(tableDetail) {
690
722
  console.log(`Indexes: ${tableDetail.indexes.length}`);
691
723
  tableDetail.indexes.forEach(index => {
692
724
  const unique = index.unique ? ' UNIQUE' : '';
693
- const columns = index.columns.map(column => column.name).filter(Boolean).join(', ') || 'expression';
725
+ const columns =
726
+ index.columns
727
+ .map(column => column.name)
728
+ .filter(Boolean)
729
+ .join(', ') || 'expression';
694
730
  console.log(` - ${index.name}${unique}: ${columns}`);
695
731
  });
696
732
  }
697
733
  }
698
734
 
699
- function printConfig(port) {
700
- const Database = require('better-sqlite3');
701
- const { version } = require('../package.json');
702
- const db = new Database(':memory:');
735
+ function formatVersionStatus(versionCheck) {
736
+ if (!versionCheck || versionCheck.status === 'unknown') {
737
+ return `unknown${versionCheck?.error?.message ? ` (${versionCheck.error.message})` : ''}`;
738
+ }
703
739
 
704
- try {
705
- const sqliteVersion = db.prepare('SELECT sqlite_version() AS version').get().version;
706
- const url = `http://127.0.0.1:${port}`;
740
+ if (versionCheck.updateAvailable) {
741
+ return `update available (${versionCheck.currentVersion} -> ${versionCheck.latestVersion})`;
742
+ }
743
+
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)}`);
707
763
 
708
- console.log('SQLite Hub config');
709
- console.log(`Port: ${port}`);
710
- console.log(`URL: ${url}`);
711
- console.log(`App version: ${version}`);
712
- console.log(`SQLite version: ${sqliteVersion}`);
713
- } finally {
714
- db.close();
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}`);
715
770
  }
716
771
  }
717
772
 
@@ -757,8 +812,8 @@ async function main(argv = process.argv.slice(2), dependencies = {}) {
757
812
  return;
758
813
  }
759
814
 
760
- if (options.config) {
761
- printConfig(port);
815
+ if (options.info) {
816
+ await printInfo(port, dependencies);
762
817
  return;
763
818
  }
764
819
 
@@ -767,6 +822,10 @@ async function main(argv = process.argv.slice(2), dependencies = {}) {
767
822
  return;
768
823
  }
769
824
 
825
+ if (options.storeName && !options.rawQuery) {
826
+ throw new Error('--store requires --query:"sql".');
827
+ }
828
+
770
829
  const databaseService =
771
830
  dependencies.databaseService ??
772
831
  new DatabaseCommandService({
@@ -805,7 +864,16 @@ async function main(argv = process.argv.slice(2), dependencies = {}) {
805
864
  return;
806
865
  }
807
866
 
808
- if (options.tableName || options.tables || options.queries || options.executeQuery || options.showQuery || options.showNotes || options.exportTarget) {
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
+ ) {
809
877
  if (options.tableName) {
810
878
  if (options.exportTarget) {
811
879
  exportTableRowAsJson({
@@ -840,6 +908,16 @@ async function main(argv = process.argv.slice(2), dependencies = {}) {
840
908
  return;
841
909
  }
842
910
 
911
+ if (options.rawQuery) {
912
+ executeRawQuery({
913
+ databaseService,
914
+ conn,
915
+ sql: options.rawQuery,
916
+ storeName: options.storeName,
917
+ });
918
+ return;
919
+ }
920
+
843
921
  if (options.showQuery) {
844
922
  showSavedQuery({ databaseService, conn, queryName: options.showQuery });
845
923
  return;
@@ -897,7 +975,9 @@ if (require.main === module) {
897
975
 
898
976
  module.exports = {
899
977
  main,
978
+ formatVersionStatus,
900
979
  normalizeExportFormat,
901
980
  openInDefaultBrowser,
902
981
  parseCliArguments,
982
+ printInfo,
903
983
  };
@@ -431,6 +431,10 @@ export function patchSettings(settings) {
431
431
  });
432
432
  }
433
433
 
434
+ export function checkAppVersion() {
435
+ return request("/api/settings/version-check");
436
+ }
437
+
434
438
  export function createApiToken(name) {
435
439
  return request("/api/settings/api-tokens", {
436
440
  method: "POST",
@@ -448,6 +452,8 @@ const TEXT_EXPORT_EXTENSIONS = {
448
452
  csv: "csv",
449
453
  tsv: "tsv",
450
454
  md: "md",
455
+ json: "json",
456
+ parquet: "parquet",
451
457
  };
452
458
 
453
459
  function normalizeTextExportFormat(format) {