sqlite-hub 0.5.0 → 0.6.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 +2 -2
- package/changelog.md +8 -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_querydetail.png +0 -0
- package/frontend/assets/mockups/structure.png +0 -0
- package/frontend/assets/mockups/structure_inspector.png +0 -0
- package/frontend/js/api.js +15 -0
- package/frontend/js/app.js +279 -8
- package/frontend/js/components/queryEditor.js +2 -2
- package/frontend/js/components/sidebar.js +1 -0
- package/frontend/js/components/tableDesignerEditor.js +356 -0
- package/frontend/js/components/tableDesignerSidebar.js +126 -0
- package/frontend/js/components/tableDesignerSqlPreview.js +40 -0
- package/frontend/js/router.js +10 -0
- package/frontend/js/store.js +296 -1
- package/frontend/js/utils/tableDesigner.js +1192 -0
- package/frontend/js/views/data.js +253 -263
- package/frontend/js/views/tableDesigner.js +37 -0
- package/frontend/styles/base.css +87 -73
- package/frontend/styles/components.css +798 -251
- package/frontend/styles/views.css +40 -0
- package/package.json +1 -1
- package/server/routes/tableDesigner.js +60 -0
- package/server/server.js +4 -0
- package/server/services/sqlite/tableDesigner/changeAnalysis.js +295 -0
- package/server/services/sqlite/tableDesigner/schemaMapping.js +233 -0
- package/server/services/sqlite/tableDesigner/sql.js +63 -0
- package/server/services/sqlite/tableDesigner/validation.js +245 -0
- package/server/services/sqlite/tableDesignerService.js +181 -0
- package/frontend/assets/mockups/data_edit.png +0 -0
- package/frontend/assets/mockups/graph_visualize.png +0 -0
- package/frontend/assets/mockups/overview.png +0 -0
package/frontend/js/store.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import * as api from "./api.js";
|
|
2
2
|
import { formatCellValue, inferStatusTone, truncateMiddle } from "./utils/format.js";
|
|
3
|
+
import {
|
|
4
|
+
addTableDesignerColumn,
|
|
5
|
+
createTableDesignerDraftFromCsvImport,
|
|
6
|
+
createNewTableDesignerDraft,
|
|
7
|
+
hydrateTableDesignerDraft,
|
|
8
|
+
removeTableDesignerColumn,
|
|
9
|
+
updateTableDesignerColumnField,
|
|
10
|
+
updateTableDesignerDraftField,
|
|
11
|
+
} from "./utils/tableDesigner.js";
|
|
3
12
|
|
|
4
13
|
const listeners = new Set();
|
|
5
14
|
const DEFAULT_SETTINGS = {
|
|
@@ -92,6 +101,19 @@ const state = {
|
|
|
92
101
|
deleting: false,
|
|
93
102
|
saveError: null,
|
|
94
103
|
},
|
|
104
|
+
tableDesigner: {
|
|
105
|
+
tables: [],
|
|
106
|
+
selectedTableName: null,
|
|
107
|
+
draft: null,
|
|
108
|
+
pendingImportedDraft: null,
|
|
109
|
+
loading: false,
|
|
110
|
+
detailLoading: false,
|
|
111
|
+
saving: false,
|
|
112
|
+
searchQuery: "",
|
|
113
|
+
supportedTypes: [],
|
|
114
|
+
error: null,
|
|
115
|
+
saveError: null,
|
|
116
|
+
},
|
|
95
117
|
structure: {
|
|
96
118
|
data: null,
|
|
97
119
|
selectedName: null,
|
|
@@ -145,7 +167,14 @@ function clearQueryHistoryDetailState() {
|
|
|
145
167
|
}
|
|
146
168
|
|
|
147
169
|
function requiresActiveDatabase(routeName) {
|
|
148
|
-
return [
|
|
170
|
+
return [
|
|
171
|
+
"overview",
|
|
172
|
+
"data",
|
|
173
|
+
"editor",
|
|
174
|
+
"editorResults",
|
|
175
|
+
"structure",
|
|
176
|
+
"tableDesigner",
|
|
177
|
+
].includes(routeName);
|
|
149
178
|
}
|
|
150
179
|
|
|
151
180
|
function normalizeDataPageSize(value, fallback = 50) {
|
|
@@ -270,6 +299,22 @@ function getCurrentStructureEntry(snapshot = state) {
|
|
|
270
299
|
return entries.find((entry) => entry.name === snapshot.structure.selectedName) ?? null;
|
|
271
300
|
}
|
|
272
301
|
|
|
302
|
+
function getTableDesignerContext(snapshot = state) {
|
|
303
|
+
return {
|
|
304
|
+
catalogTables: snapshot.tableDesigner.tables ?? [],
|
|
305
|
+
supportedTypes: snapshot.tableDesigner.supportedTypes ?? [],
|
|
306
|
+
readOnly: Boolean(snapshot.connections.active?.readOnly),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function decorateTableDesignerDraft(draft, snapshot = state) {
|
|
311
|
+
if (!draft) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return hydrateTableDesignerDraft(draft, getTableDesignerContext(snapshot));
|
|
316
|
+
}
|
|
317
|
+
|
|
273
318
|
function buildDeleteRowPreview(fields = []) {
|
|
274
319
|
return fields
|
|
275
320
|
.filter((field) => field && field.label)
|
|
@@ -358,6 +403,8 @@ function clearRouteSlices() {
|
|
|
358
403
|
state.overview.error = null;
|
|
359
404
|
state.dataBrowser.error = null;
|
|
360
405
|
state.dataBrowser.saveError = null;
|
|
406
|
+
state.tableDesigner.error = null;
|
|
407
|
+
state.tableDesigner.saveError = null;
|
|
361
408
|
state.structure.error = null;
|
|
362
409
|
}
|
|
363
410
|
|
|
@@ -387,6 +434,18 @@ function setMissingDatabaseState() {
|
|
|
387
434
|
state.structure.detail = null;
|
|
388
435
|
state.structure.error = error;
|
|
389
436
|
|
|
437
|
+
state.tableDesigner.loading = false;
|
|
438
|
+
state.tableDesigner.detailLoading = false;
|
|
439
|
+
state.tableDesigner.tables = [];
|
|
440
|
+
state.tableDesigner.selectedTableName = null;
|
|
441
|
+
state.tableDesigner.draft = null;
|
|
442
|
+
state.tableDesigner.pendingImportedDraft = null;
|
|
443
|
+
state.tableDesigner.saving = false;
|
|
444
|
+
state.tableDesigner.searchQuery = "";
|
|
445
|
+
state.tableDesigner.supportedTypes = [];
|
|
446
|
+
state.tableDesigner.error = error;
|
|
447
|
+
state.tableDesigner.saveError = null;
|
|
448
|
+
|
|
390
449
|
resetQueryHistoryState({ preserveSearch: false });
|
|
391
450
|
}
|
|
392
451
|
|
|
@@ -423,6 +482,10 @@ function syncRouteContext() {
|
|
|
423
482
|
state.structure.detail = null;
|
|
424
483
|
state.structure.selectedName = null;
|
|
425
484
|
}
|
|
485
|
+
|
|
486
|
+
if (route.name !== "tableDesigner") {
|
|
487
|
+
state.tableDesigner.saveError = null;
|
|
488
|
+
}
|
|
426
489
|
}
|
|
427
490
|
|
|
428
491
|
async function refreshConnectionsState() {
|
|
@@ -824,6 +887,103 @@ async function loadStructure(version) {
|
|
|
824
887
|
}
|
|
825
888
|
}
|
|
826
889
|
|
|
890
|
+
async function loadTableDesignerDetail(version, tableName) {
|
|
891
|
+
if (!tableName) {
|
|
892
|
+
state.tableDesigner.draft = null;
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
state.tableDesigner.detailLoading = true;
|
|
897
|
+
state.tableDesigner.saveError = null;
|
|
898
|
+
emitChange();
|
|
899
|
+
|
|
900
|
+
try {
|
|
901
|
+
const response = await api.getTableDesignerTable(tableName);
|
|
902
|
+
|
|
903
|
+
if (version !== routeLoadVersion) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
state.tableDesigner.selectedTableName = tableName;
|
|
908
|
+
state.tableDesigner.draft = decorateTableDesignerDraft(response.data?.draft ?? null);
|
|
909
|
+
} catch (error) {
|
|
910
|
+
if (version !== routeLoadVersion) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
state.tableDesigner.error = normalizeError(error);
|
|
915
|
+
state.tableDesigner.draft = null;
|
|
916
|
+
} finally {
|
|
917
|
+
if (version === routeLoadVersion) {
|
|
918
|
+
state.tableDesigner.detailLoading = false;
|
|
919
|
+
emitChange();
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
async function loadTableDesigner(version, route) {
|
|
925
|
+
state.tableDesigner.loading = true;
|
|
926
|
+
state.tableDesigner.error = null;
|
|
927
|
+
emitChange();
|
|
928
|
+
|
|
929
|
+
try {
|
|
930
|
+
const response = await api.getTableDesignerOverview();
|
|
931
|
+
|
|
932
|
+
if (version !== routeLoadVersion) {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
state.tableDesigner.tables = response.data?.tables ?? [];
|
|
937
|
+
state.tableDesigner.supportedTypes = response.data?.supportedTypes ?? [];
|
|
938
|
+
|
|
939
|
+
if (route.params?.isNew) {
|
|
940
|
+
const importedDraft = state.tableDesigner.pendingImportedDraft;
|
|
941
|
+
state.tableDesigner.selectedTableName = null;
|
|
942
|
+
state.tableDesigner.detailLoading = false;
|
|
943
|
+
state.tableDesigner.pendingImportedDraft = null;
|
|
944
|
+
state.tableDesigner.draft = decorateTableDesignerDraft(
|
|
945
|
+
importedDraft ?? createNewTableDesignerDraft()
|
|
946
|
+
);
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const requestedTableName = route.params?.tableName ?? null;
|
|
951
|
+
const tableName =
|
|
952
|
+
requestedTableName &&
|
|
953
|
+
state.tableDesigner.tables.some((table) => table.name === requestedTableName)
|
|
954
|
+
? requestedTableName
|
|
955
|
+
: state.tableDesigner.selectedTableName &&
|
|
956
|
+
state.tableDesigner.tables.some(
|
|
957
|
+
(table) => table.name === state.tableDesigner.selectedTableName
|
|
958
|
+
)
|
|
959
|
+
? state.tableDesigner.selectedTableName
|
|
960
|
+
: state.tableDesigner.tables[0]?.name ?? null;
|
|
961
|
+
|
|
962
|
+
if (!tableName) {
|
|
963
|
+
state.tableDesigner.selectedTableName = null;
|
|
964
|
+
state.tableDesigner.detailLoading = false;
|
|
965
|
+
state.tableDesigner.draft = null;
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
await loadTableDesignerDetail(version, tableName);
|
|
970
|
+
} catch (error) {
|
|
971
|
+
if (version !== routeLoadVersion) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
state.tableDesigner.tables = [];
|
|
976
|
+
state.tableDesigner.selectedTableName = null;
|
|
977
|
+
state.tableDesigner.draft = null;
|
|
978
|
+
state.tableDesigner.error = normalizeError(error);
|
|
979
|
+
} finally {
|
|
980
|
+
if (version === routeLoadVersion) {
|
|
981
|
+
state.tableDesigner.loading = false;
|
|
982
|
+
emitChange();
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
827
987
|
function invalidateDatabaseCaches() {
|
|
828
988
|
state.overview.data = null;
|
|
829
989
|
state.dataBrowser.tables = [];
|
|
@@ -835,6 +995,15 @@ function invalidateDatabaseCaches() {
|
|
|
835
995
|
state.dataBrowser.exportLoading = false;
|
|
836
996
|
state.dataBrowser.error = null;
|
|
837
997
|
state.dataBrowser.saveError = null;
|
|
998
|
+
state.tableDesigner.tables = [];
|
|
999
|
+
state.tableDesigner.selectedTableName = null;
|
|
1000
|
+
state.tableDesigner.draft = null;
|
|
1001
|
+
state.tableDesigner.pendingImportedDraft = null;
|
|
1002
|
+
state.tableDesigner.saving = false;
|
|
1003
|
+
state.tableDesigner.searchQuery = "";
|
|
1004
|
+
state.tableDesigner.supportedTypes = [];
|
|
1005
|
+
state.tableDesigner.error = null;
|
|
1006
|
+
state.tableDesigner.saveError = null;
|
|
838
1007
|
state.structure.data = null;
|
|
839
1008
|
state.structure.detail = null;
|
|
840
1009
|
}
|
|
@@ -869,6 +1038,9 @@ async function loadRouteData(route) {
|
|
|
869
1038
|
case "structure":
|
|
870
1039
|
await loadStructure(version);
|
|
871
1040
|
return;
|
|
1041
|
+
case "tableDesigner":
|
|
1042
|
+
await loadTableDesigner(version, route);
|
|
1043
|
+
return;
|
|
872
1044
|
case "settings":
|
|
873
1045
|
await refreshSettingsState();
|
|
874
1046
|
return;
|
|
@@ -1450,6 +1622,125 @@ export async function selectStructureEntry(name) {
|
|
|
1450
1622
|
await loadStructureDetail(++routeLoadVersion);
|
|
1451
1623
|
}
|
|
1452
1624
|
|
|
1625
|
+
export function setTableDesignerSearchQuery(query) {
|
|
1626
|
+
state.tableDesigner.searchQuery = String(query ?? "");
|
|
1627
|
+
emitChange();
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
export function updateCurrentTableDesignerField(field, value) {
|
|
1631
|
+
if (!state.tableDesigner.draft) {
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
state.tableDesigner.draft = updateTableDesignerDraftField(
|
|
1636
|
+
state.tableDesigner.draft,
|
|
1637
|
+
field,
|
|
1638
|
+
value,
|
|
1639
|
+
getTableDesignerContext()
|
|
1640
|
+
);
|
|
1641
|
+
state.tableDesigner.saveError = null;
|
|
1642
|
+
emitChange();
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
export function updateCurrentTableDesignerColumnField(columnId, field, value) {
|
|
1646
|
+
if (!state.tableDesigner.draft) {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
state.tableDesigner.draft = updateTableDesignerColumnField(
|
|
1651
|
+
state.tableDesigner.draft,
|
|
1652
|
+
columnId,
|
|
1653
|
+
field,
|
|
1654
|
+
value,
|
|
1655
|
+
getTableDesignerContext()
|
|
1656
|
+
);
|
|
1657
|
+
state.tableDesigner.saveError = null;
|
|
1658
|
+
emitChange();
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
export function addCurrentTableDesignerColumn() {
|
|
1662
|
+
if (!state.tableDesigner.draft) {
|
|
1663
|
+
return null;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
const previousColumnIds = new Set(state.tableDesigner.draft.columns.map((column) => column.id));
|
|
1667
|
+
state.tableDesigner.draft = addTableDesignerColumn(
|
|
1668
|
+
state.tableDesigner.draft,
|
|
1669
|
+
getTableDesignerContext()
|
|
1670
|
+
);
|
|
1671
|
+
state.tableDesigner.saveError = null;
|
|
1672
|
+
emitChange();
|
|
1673
|
+
|
|
1674
|
+
const nextColumn = state.tableDesigner.draft.columns.find(
|
|
1675
|
+
(column) => !previousColumnIds.has(column.id)
|
|
1676
|
+
);
|
|
1677
|
+
|
|
1678
|
+
return nextColumn?.id ?? null;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
export function queueTableDesignerCsvImport(fileName, csvText) {
|
|
1682
|
+
try {
|
|
1683
|
+
const imported = createTableDesignerDraftFromCsvImport(
|
|
1684
|
+
{ fileName, csvText },
|
|
1685
|
+
getTableDesignerContext()
|
|
1686
|
+
);
|
|
1687
|
+
|
|
1688
|
+
state.tableDesigner.pendingImportedDraft = imported.draft;
|
|
1689
|
+
state.tableDesigner.selectedTableName = null;
|
|
1690
|
+
state.tableDesigner.saveError = null;
|
|
1691
|
+
state.tableDesigner.error = null;
|
|
1692
|
+
emitChange();
|
|
1693
|
+
return imported;
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
pushToast(error?.message || "CSV import failed.", "alert");
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
export function removeCurrentTableDesignerColumn(columnId) {
|
|
1701
|
+
if (!state.tableDesigner.draft) {
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
state.tableDesigner.draft = removeTableDesignerColumn(
|
|
1706
|
+
state.tableDesigner.draft,
|
|
1707
|
+
columnId,
|
|
1708
|
+
getTableDesignerContext()
|
|
1709
|
+
);
|
|
1710
|
+
state.tableDesigner.saveError = null;
|
|
1711
|
+
emitChange();
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
export async function saveCurrentTableDesignerDraft() {
|
|
1715
|
+
if (!state.tableDesigner.draft) {
|
|
1716
|
+
return null;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
state.tableDesigner.saving = true;
|
|
1720
|
+
state.tableDesigner.saveError = null;
|
|
1721
|
+
emitChange();
|
|
1722
|
+
|
|
1723
|
+
try {
|
|
1724
|
+
const response = await api.saveTableDesignerDraft({
|
|
1725
|
+
draft: state.tableDesigner.draft,
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
state.tableDesigner.tables = response.data?.tables ?? state.tableDesigner.tables;
|
|
1729
|
+
state.tableDesigner.selectedTableName =
|
|
1730
|
+
response.data?.savedTableName ?? state.tableDesigner.selectedTableName;
|
|
1731
|
+
state.tableDesigner.draft = decorateTableDesignerDraft(response.data?.draft ?? null);
|
|
1732
|
+
pushToast(response.message || "Table schema saved.", "success");
|
|
1733
|
+
return response.data?.savedTableName ?? null;
|
|
1734
|
+
} catch (error) {
|
|
1735
|
+
state.tableDesigner.saveError = normalizeError(error);
|
|
1736
|
+
emitChange();
|
|
1737
|
+
return null;
|
|
1738
|
+
} finally {
|
|
1739
|
+
state.tableDesigner.saving = false;
|
|
1740
|
+
emitChange();
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1453
1744
|
export function selectDataRow(index) {
|
|
1454
1745
|
const numericIndex = Number(index);
|
|
1455
1746
|
|
|
@@ -1845,6 +2136,10 @@ export async function refreshCurrentRoute() {
|
|
|
1845
2136
|
await loadRouteData(state.route);
|
|
1846
2137
|
}
|
|
1847
2138
|
|
|
2139
|
+
export function showToast(message, tone = "muted") {
|
|
2140
|
+
pushToast(message, tone);
|
|
2141
|
+
}
|
|
2142
|
+
|
|
1848
2143
|
export function getCurrentConnection(snapshot = state) {
|
|
1849
2144
|
return snapshot.connections.active;
|
|
1850
2145
|
}
|