sqlite-hub 0.4.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 +15 -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 +114 -5
- package/frontend/js/app.js +368 -18
- package/frontend/js/components/bottomTabs.js +1 -1
- package/frontend/js/components/dataGrid.js +3 -3
- package/frontend/js/components/queryEditor.js +33 -55
- package/frontend/js/components/queryHistoryDetail.js +263 -0
- package/frontend/js/components/queryHistoryPanel.js +228 -0
- package/frontend/js/components/queryResults.js +32 -46
- package/frontend/js/components/rowEditorPanel.js +73 -14
- 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 +841 -22
- package/frontend/js/utils/format.js +23 -0
- package/frontend/js/utils/tableDesigner.js +1192 -0
- package/frontend/js/views/data.js +273 -250
- package/frontend/js/views/editor.js +34 -10
- package/frontend/js/views/overview.js +15 -0
- package/frontend/js/views/tableDesigner.js +37 -0
- package/frontend/styles/base.css +87 -73
- package/frontend/styles/components.css +841 -188
- package/frontend/styles/views.css +40 -0
- package/package.json +1 -1
- package/server/routes/data.js +2 -0
- package/server/routes/export.js +4 -1
- package/server/routes/overview.js +12 -0
- package/server/routes/sql.js +163 -5
- package/server/routes/tableDesigner.js +60 -0
- package/server/server.js +5 -1
- package/server/services/sqlite/dataBrowserService.js +4 -16
- package/server/services/sqlite/exportService.js +4 -16
- package/server/services/sqlite/overviewService.js +34 -0
- package/server/services/sqlite/sqlExecutor.js +83 -63
- 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/server/services/sqlite/tableSort.js +63 -0
- package/server/services/storage/appStateStore.js +674 -1
- package/server/services/storage/queryHistoryUtils.js +169 -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
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
function stripLineComments(sql = "") {
|
|
2
|
+
return String(sql)
|
|
3
|
+
.split(/\r?\n/)
|
|
4
|
+
.map((line) => line.replace(/--.*$/g, ""))
|
|
5
|
+
.join("\n");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function stripBlockComments(sql = "") {
|
|
9
|
+
return String(sql).replace(/\/\*[\s\S]*?\*\//g, " ");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function compactWhitespace(value = "") {
|
|
13
|
+
return String(value).replace(/\s+/g, " ").trim();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function truncateText(value = "", maxLength = 80) {
|
|
17
|
+
const text = String(value ?? "").trim();
|
|
18
|
+
|
|
19
|
+
if (text.length <= maxLength) {
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return `${text.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function normalizeSql(sql = "") {
|
|
27
|
+
const withoutComments = stripBlockComments(stripLineComments(sql));
|
|
28
|
+
const compact = compactWhitespace(withoutComments).replace(/;+\s*$/g, "").trim();
|
|
29
|
+
|
|
30
|
+
return compact.toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getLeadingKeywords(sql = "", limit = 3) {
|
|
34
|
+
const stripped = stripBlockComments(stripLineComments(sql));
|
|
35
|
+
const matches = stripped.match(/[A-Za-z]+/g) ?? [];
|
|
36
|
+
|
|
37
|
+
return matches.slice(0, limit).map((token) => token.toLowerCase());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveEffectiveKeyword(sql = "") {
|
|
41
|
+
const [firstKeyword, secondKeyword, thirdKeyword] = getLeadingKeywords(sql, 3);
|
|
42
|
+
|
|
43
|
+
if (firstKeyword === "with") {
|
|
44
|
+
return secondKeyword === "recursive" ? thirdKeyword ?? "with" : secondKeyword ?? "with";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (firstKeyword === "explain") {
|
|
48
|
+
return secondKeyword ?? "explain";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return firstKeyword ?? "";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function detectQueryType(sql = "") {
|
|
55
|
+
const keyword = resolveEffectiveKeyword(sql);
|
|
56
|
+
|
|
57
|
+
if (keyword === "select") {
|
|
58
|
+
return "select";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (keyword === "insert") {
|
|
62
|
+
return "insert";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (keyword === "update") {
|
|
66
|
+
return "update";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (keyword === "delete") {
|
|
70
|
+
return "delete";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (keyword === "pragma") {
|
|
74
|
+
return "pragma";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (["create", "alter", "drop", "rename", "truncate"].includes(keyword)) {
|
|
78
|
+
return "ddl";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return "other";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isDestructiveQuery(sql = "") {
|
|
85
|
+
const keyword = resolveEffectiveKeyword(sql);
|
|
86
|
+
return ["insert", "update", "delete", "alter", "drop", "replace"].includes(keyword);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeTableName(candidate = "") {
|
|
90
|
+
const trimmed = String(candidate ?? "")
|
|
91
|
+
.trim()
|
|
92
|
+
.replace(/[;,)]*$/g, "");
|
|
93
|
+
|
|
94
|
+
if (!trimmed || trimmed.startsWith("(")) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const segments = trimmed.split(".").filter(Boolean);
|
|
99
|
+
const finalSegment = segments.at(-1) ?? trimmed;
|
|
100
|
+
const normalized = finalSegment.replace(/^["`[]|["`\]]$/g, "");
|
|
101
|
+
|
|
102
|
+
if (!normalized || ["select", "with", "pragma"].includes(normalized.toLowerCase())) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return normalized;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function collectTableMatches(sql = "", patterns = []) {
|
|
110
|
+
const text = stripBlockComments(stripLineComments(sql));
|
|
111
|
+
const tableNames = [];
|
|
112
|
+
|
|
113
|
+
patterns.forEach((pattern) => {
|
|
114
|
+
let match;
|
|
115
|
+
|
|
116
|
+
while ((match = pattern.exec(text))) {
|
|
117
|
+
const tableName = normalizeTableName(match[1]);
|
|
118
|
+
|
|
119
|
+
if (tableName) {
|
|
120
|
+
tableNames.push(tableName);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return Array.from(new Set(tableNames));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function detectTables(sql = "") {
|
|
129
|
+
return collectTableMatches(sql, [
|
|
130
|
+
/\bfrom\s+([^\s,;()]+)/gi,
|
|
131
|
+
/\bjoin\s+([^\s,;()]+)/gi,
|
|
132
|
+
/\bupdate(?:\s+or\s+\w+)?\s+([^\s,;()]+)/gi,
|
|
133
|
+
/\binto\s+([^\s,;()]+)/gi,
|
|
134
|
+
/\btable\s+(?:if\s+(?:not\s+)?exists\s+)?([^\s,;()]+)/gi,
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function buildAutoTitle(rawSql = "", { queryType = "other", tablesDetected = [] } = {}) {
|
|
139
|
+
const firstMeaningfulLine = String(rawSql)
|
|
140
|
+
.split(/\r?\n/)
|
|
141
|
+
.map((line) => line.replace(/--.*$/g, "").trim())
|
|
142
|
+
.find(Boolean);
|
|
143
|
+
|
|
144
|
+
if (firstMeaningfulLine) {
|
|
145
|
+
return truncateText(firstMeaningfulLine.replace(/;+\s*$/g, ""), 80);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const primaryTable = tablesDetected[0] ? ` on ${tablesDetected[0]}` : "";
|
|
149
|
+
|
|
150
|
+
if (queryType && queryType !== "other") {
|
|
151
|
+
return `${queryType.toUpperCase()}${primaryTable}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return primaryTable ? `Query${primaryTable}` : "SQL query";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function buildSqlPreview(rawSql = "", maxLength = 140) {
|
|
158
|
+
const compact = compactWhitespace(stripLineComments(rawSql));
|
|
159
|
+
return truncateText(compact, maxLength) || "SQL query";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
buildAutoTitle,
|
|
164
|
+
buildSqlPreview,
|
|
165
|
+
detectQueryType,
|
|
166
|
+
detectTables,
|
|
167
|
+
isDestructiveQuery,
|
|
168
|
+
normalizeSql,
|
|
169
|
+
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|