sqlite-hub 0.16.0 → 0.17.2

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 (53) hide show
  1. package/README.md +106 -19
  2. package/bin/sqlite-hub.js +1041 -224
  3. package/frontend/index.html +1 -0
  4. package/frontend/js/api.js +34 -0
  5. package/frontend/js/app.js +481 -32
  6. package/frontend/js/components/modal.js +270 -50
  7. package/frontend/js/components/pageHeader.js +14 -16
  8. package/frontend/js/components/queryHistoryPanel.js +126 -131
  9. package/frontend/js/components/queryResults.js +18 -1
  10. package/frontend/js/components/rowEditorPanel.js +42 -34
  11. package/frontend/js/components/sidebar.js +1 -0
  12. package/frontend/js/router.js +8 -0
  13. package/frontend/js/store.js +655 -2
  14. package/frontend/js/utils/jsonPreview.js +31 -0
  15. package/frontend/js/utils/markdownDocuments.js +248 -0
  16. package/frontend/js/utils/rowEditorValues.js +41 -0
  17. package/frontend/js/utils/tableScrollState.js +39 -0
  18. package/frontend/js/views/charts.js +8 -0
  19. package/frontend/js/views/data.js +4 -1
  20. package/frontend/js/views/documents.js +300 -0
  21. package/frontend/js/views/editor.js +2 -0
  22. package/frontend/js/views/settings.js +39 -3
  23. package/frontend/styles/components.css +19 -0
  24. package/frontend/styles/tailwind.generated.css +1 -1
  25. package/frontend/styles/views.css +422 -0
  26. package/package.json +2 -1
  27. package/server/middleware/localRequestSecurity.js +84 -0
  28. package/server/routes/connections.js +18 -1
  29. package/server/routes/documents.js +163 -0
  30. package/server/routes/settings.js +22 -6
  31. package/server/server.js +18 -1
  32. package/server/services/nativeFileDialogService.js +168 -0
  33. package/server/services/sqlite/dataBrowserService.js +0 -4
  34. package/server/services/sqlite/exportService.js +5 -1
  35. package/server/services/sqlite/sqlExecutor.js +51 -2
  36. package/server/services/storage/appStateStore.js +313 -0
  37. package/server/utils/sqliteTypes.js +17 -8
  38. package/tests/cli-args.test.js +100 -0
  39. package/tests/connections-file-dialog-route.test.js +46 -0
  40. package/tests/copy-column-modal.test.js +97 -0
  41. package/tests/database-documents.test.js +85 -0
  42. package/tests/export-blob.test.js +46 -0
  43. package/tests/json-preview.test.js +49 -0
  44. package/tests/local-request-security.test.js +85 -0
  45. package/tests/markdown-documents.test.js +79 -0
  46. package/tests/native-file-dialog.test.js +78 -0
  47. package/tests/query-results-truncation.test.js +20 -0
  48. package/tests/row-editor-null-values.test.js +131 -0
  49. package/tests/settings-metadata.test.js +16 -0
  50. package/tests/settings-view.test.js +32 -0
  51. package/tests/sql-identifier-safety.test.js +9 -3
  52. package/tests/sql-result-limit.test.js +66 -0
  53. package/tests/table-scroll-state.test.js +70 -0
@@ -0,0 +1,66 @@
1
+ const Database = require("better-sqlite3");
2
+ const assert = require("node:assert/strict");
3
+ const test = require("node:test");
4
+ const {
5
+ DEFAULT_RESULT_ROW_LIMIT,
6
+ SqlExecutor,
7
+ } = require("../server/services/sqlite/sqlExecutor");
8
+
9
+ function createExecutor(db) {
10
+ return new SqlExecutor({
11
+ connectionManager: {
12
+ getActiveDatabase: () => db,
13
+ getActiveConnection: () => ({ id: "result-limit-test" }),
14
+ },
15
+ appStateStore: {
16
+ recordQueryExecution: () => 1,
17
+ },
18
+ });
19
+ }
20
+
21
+ test("interactive SQL results stop at the configured row limit", () => {
22
+ const db = new Database(":memory:");
23
+
24
+ try {
25
+ db.exec(`
26
+ CREATE TABLE items (id INTEGER PRIMARY KEY, label TEXT);
27
+ INSERT INTO items (label) VALUES ('one'), ('two'), ('three'), ('four'), ('five');
28
+ `);
29
+
30
+ const result = createExecutor(db).execute("SELECT id, label FROM items ORDER BY id", {
31
+ maxRows: 3,
32
+ });
33
+
34
+ assert.equal(DEFAULT_RESULT_ROW_LIMIT, 5000);
35
+ assert.equal(result.rows.length, 3);
36
+ assert.deepEqual(result.rows.map((row) => row.id), [1, 2, 3]);
37
+ assert.equal(result.truncated, true);
38
+ assert.equal(result.rowLimit, 3);
39
+ assert.equal(result.statements[0].truncated, true);
40
+ assert.equal(result.statements[0].rowCount, 3);
41
+ } finally {
42
+ db.close();
43
+ }
44
+ });
45
+
46
+ test("unlimited SQL execution remains available for export services", () => {
47
+ const db = new Database(":memory:");
48
+
49
+ try {
50
+ db.exec(`
51
+ CREATE TABLE items (id INTEGER PRIMARY KEY);
52
+ INSERT INTO items VALUES (1), (2), (3), (4), (5);
53
+ `);
54
+
55
+ const result = createExecutor(db).execute("SELECT id FROM items ORDER BY id", {
56
+ maxRows: null,
57
+ persistHistory: false,
58
+ });
59
+
60
+ assert.equal(result.rows.length, 5);
61
+ assert.equal(result.truncated, false);
62
+ assert.equal(result.rowLimit, null);
63
+ } finally {
64
+ db.close();
65
+ }
66
+ });
@@ -0,0 +1,70 @@
1
+ const assert = require("node:assert/strict");
2
+ const { readFileSync } = require("node:fs");
3
+ const path = require("node:path");
4
+ const test = require("node:test");
5
+
6
+ let tableScrollStateModulePromise = null;
7
+
8
+ function loadTableScrollStateModule() {
9
+ if (!tableScrollStateModulePromise) {
10
+ const source = readFileSync(
11
+ path.resolve(__dirname, "../frontend/js/utils/tableScrollState.js"),
12
+ "utf8"
13
+ );
14
+ const url = `data:text/javascript;base64,${Buffer.from(source).toString("base64")}`;
15
+
16
+ tableScrollStateModulePromise = import(url);
17
+ }
18
+
19
+ return tableScrollStateModulePromise;
20
+ }
21
+
22
+ test("table scroll state restores horizontal position for the matching grid", async () => {
23
+ const { captureTableHorizontalScrollState, restoreTableHorizontalScrollState } =
24
+ await loadTableScrollStateModule();
25
+ const key = 'data:events "archive"';
26
+ const previousGrid = { dataset: { tableScrollKey: key }, scrollLeft: 640 };
27
+ const nextGrid = { dataset: { tableScrollKey: key }, scrollLeft: 0 };
28
+ const snapshot = captureTableHorizontalScrollState({
29
+ routeName: "data",
30
+ scrollNodes: [previousGrid],
31
+ });
32
+
33
+ assert.equal(
34
+ restoreTableHorizontalScrollState({
35
+ snapshot,
36
+ routeName: "data",
37
+ scrollNodes: [nextGrid],
38
+ }),
39
+ true
40
+ );
41
+ assert.equal(nextGrid.scrollLeft, 640);
42
+ });
43
+
44
+ test("table scroll state is not restored across routes or different grids", async () => {
45
+ const { captureTableHorizontalScrollState, restoreTableHorizontalScrollState } =
46
+ await loadTableScrollStateModule();
47
+ const snapshot = captureTableHorizontalScrollState({
48
+ routeName: "editorResults",
49
+ scrollNodes: [{ dataset: { tableScrollKey: "editor:12" }, scrollLeft: 320 }],
50
+ });
51
+ const nextGrid = { dataset: { tableScrollKey: "editor:13" }, scrollLeft: 0 };
52
+
53
+ assert.equal(
54
+ restoreTableHorizontalScrollState({
55
+ snapshot,
56
+ routeName: "data",
57
+ scrollNodes: [nextGrid],
58
+ }),
59
+ false
60
+ );
61
+ assert.equal(
62
+ restoreTableHorizontalScrollState({
63
+ snapshot,
64
+ routeName: "editorResults",
65
+ scrollNodes: [nextGrid],
66
+ }),
67
+ false
68
+ );
69
+ assert.equal(nextGrid.scrollLeft, 0);
70
+ });