sql-dashboard 1.0.0 → 1.0.1
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/dist/chunk-ABIHDUB7.mjs +76 -0
- package/dist/{chunk-TNHUK2FI.mjs → chunk-RU3R3RGI.mjs} +203 -5
- package/dist/demo/index.d.mts +2 -0
- package/dist/demo/index.d.ts +2 -0
- package/dist/demo/index.js +91360 -0
- package/dist/demo/index.mjs +339 -0
- package/dist/drivers/mysql.driver.mjs +2 -2
- package/dist/export/index.mjs +7 -67
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/dist/middleware/express.js +3 -3
- package/dist/middleware/express.mjs +3 -3
- package/dist/middleware/fastify.js +3 -3
- package/dist/middleware/fastify.mjs +3 -3
- package/package.json +8 -2
- package/dist/{chunk-7YLO3OSN.mjs → chunk-62XBCHG5.mjs} +3 -3
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__require
|
|
3
|
+
} from "./chunk-OCL5Y3AH.mjs";
|
|
4
|
+
|
|
5
|
+
// src/export/csv.ts
|
|
6
|
+
function toCSV(result, options = {}) {
|
|
7
|
+
const {
|
|
8
|
+
delimiter = ",",
|
|
9
|
+
includeHeader = true,
|
|
10
|
+
quoteAll = true,
|
|
11
|
+
nullValue = "NULL"
|
|
12
|
+
} = options;
|
|
13
|
+
const lines = [];
|
|
14
|
+
if (includeHeader && result.columns.length > 0) {
|
|
15
|
+
lines.push(result.columns.map((col) => formatCSVField(col, delimiter, quoteAll)).join(delimiter));
|
|
16
|
+
}
|
|
17
|
+
for (const row of result.rows) {
|
|
18
|
+
const line = result.columns.map((col) => {
|
|
19
|
+
const value = row[col];
|
|
20
|
+
if (value === null || value === void 0) return nullValue;
|
|
21
|
+
return formatCSVField(String(value), delimiter, quoteAll);
|
|
22
|
+
});
|
|
23
|
+
lines.push(line.join(delimiter));
|
|
24
|
+
}
|
|
25
|
+
return lines.join("\r\n");
|
|
26
|
+
}
|
|
27
|
+
function formatCSVField(value, delimiter, quoteAll) {
|
|
28
|
+
const needsQuoting = quoteAll || value.includes(delimiter) || value.includes('"') || value.includes("\n") || value.includes("\r");
|
|
29
|
+
if (needsQuoting) {
|
|
30
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
function toCSVStream(result, options = {}) {
|
|
35
|
+
const { Readable } = __require("stream");
|
|
36
|
+
const csv = toCSV(result, options);
|
|
37
|
+
const stream = new Readable();
|
|
38
|
+
stream.push(csv);
|
|
39
|
+
stream.push(null);
|
|
40
|
+
return stream;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/export/json.ts
|
|
44
|
+
function toJSON(result, options = {}) {
|
|
45
|
+
const {
|
|
46
|
+
pretty = true,
|
|
47
|
+
indent = 2,
|
|
48
|
+
includeMeta = false
|
|
49
|
+
} = options;
|
|
50
|
+
if (includeMeta) {
|
|
51
|
+
return JSON.stringify({
|
|
52
|
+
meta: {
|
|
53
|
+
rowCount: result.rowCount,
|
|
54
|
+
columns: result.columns,
|
|
55
|
+
duration: result.duration,
|
|
56
|
+
status: result.status
|
|
57
|
+
},
|
|
58
|
+
data: result.rows
|
|
59
|
+
}, null, pretty ? indent : void 0);
|
|
60
|
+
}
|
|
61
|
+
return JSON.stringify(result.rows, null, pretty ? indent : void 0);
|
|
62
|
+
}
|
|
63
|
+
function toJSONLines(result) {
|
|
64
|
+
return result.rows.map((row) => JSON.stringify(row)).join("\n");
|
|
65
|
+
}
|
|
66
|
+
function toJSONArray(result) {
|
|
67
|
+
return JSON.stringify(result.rows);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
toCSV,
|
|
72
|
+
toCSVStream,
|
|
73
|
+
toJSON,
|
|
74
|
+
toJSONLines,
|
|
75
|
+
toJSONArray
|
|
76
|
+
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-MTCZXLV5.mjs";
|
|
4
4
|
import {
|
|
5
5
|
MySQLDriver
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-62XBCHG5.mjs";
|
|
7
7
|
import {
|
|
8
8
|
PostgresDriver
|
|
9
9
|
} from "./chunk-YGKUVVJT.mjs";
|
|
@@ -308,6 +308,12 @@ var SchemaBrowser = class {
|
|
|
308
308
|
};
|
|
309
309
|
|
|
310
310
|
// src/utils/pagination.ts
|
|
311
|
+
function calculatePagination(params) {
|
|
312
|
+
const page = Math.max(1, params.page ?? 1);
|
|
313
|
+
const pageSize = Math.min(Math.max(1, params.pageSize ?? 50), 1e3);
|
|
314
|
+
const offset = params.offset ?? (page - 1) * pageSize;
|
|
315
|
+
return { limit: pageSize, offset };
|
|
316
|
+
}
|
|
311
317
|
function createPaginatedResult(data, total, params) {
|
|
312
318
|
const page = Math.max(1, params.page ?? 1);
|
|
313
319
|
const pageSize = Math.min(Math.max(1, params.pageSize ?? 50), 1e3);
|
|
@@ -657,7 +663,7 @@ function extractTableNames(sql) {
|
|
|
657
663
|
}
|
|
658
664
|
function validateQuery(sql, security) {
|
|
659
665
|
const errors = [];
|
|
660
|
-
|
|
666
|
+
let trimmedSql = sql.trim();
|
|
661
667
|
if (!trimmedSql) {
|
|
662
668
|
errors.push({
|
|
663
669
|
code: "EMPTY_QUERY",
|
|
@@ -667,7 +673,7 @@ function validateQuery(sql, security) {
|
|
|
667
673
|
return { valid: false, errors, normalizedQuery: sql, statementType: "UNKNOWN", isReadOnly: true, tables: [] };
|
|
668
674
|
}
|
|
669
675
|
if (!trimmedSql.endsWith(";")) {
|
|
670
|
-
trimmedSql + ";";
|
|
676
|
+
trimmedSql = trimmedSql + ";";
|
|
671
677
|
}
|
|
672
678
|
const statementType = detectStatementType(trimmedSql);
|
|
673
679
|
const detectedTables = extractTableNames(trimmedSql);
|
|
@@ -761,6 +767,9 @@ function detectInjection(sql) {
|
|
|
761
767
|
}
|
|
762
768
|
return false;
|
|
763
769
|
}
|
|
770
|
+
function sanitizeIdentifier(name) {
|
|
771
|
+
return name.replace(/[^a-zA-Z0-9_$]/g, "").replace(/^(\d)/, "_$1");
|
|
772
|
+
}
|
|
764
773
|
function prepareBatchQueries(sql) {
|
|
765
774
|
const sanitized = sanitizeComment(sql);
|
|
766
775
|
const queries = sanitized.split(";").map((q) => q.trim()).filter((q) => q.length > 0).map((q) => q + ";");
|
|
@@ -768,6 +777,180 @@ function prepareBatchQueries(sql) {
|
|
|
768
777
|
}
|
|
769
778
|
|
|
770
779
|
// src/core/formatter.ts
|
|
780
|
+
function formatSQL(sql, options = {}) {
|
|
781
|
+
const {
|
|
782
|
+
uppercase = true,
|
|
783
|
+
indent = " "
|
|
784
|
+
} = options;
|
|
785
|
+
let formatted = sql.trim();
|
|
786
|
+
if (uppercase) {
|
|
787
|
+
formatted = uppercaseKeywords(formatted);
|
|
788
|
+
}
|
|
789
|
+
formatted = formatClauses(formatted, indent);
|
|
790
|
+
formatted = formatParentheses(formatted, indent);
|
|
791
|
+
formatted = formatCommas(formatted);
|
|
792
|
+
formatted = formatOperators(formatted);
|
|
793
|
+
return formatted;
|
|
794
|
+
}
|
|
795
|
+
var KEYWORDS = [
|
|
796
|
+
"SELECT",
|
|
797
|
+
"FROM",
|
|
798
|
+
"WHERE",
|
|
799
|
+
"AND",
|
|
800
|
+
"OR",
|
|
801
|
+
"NOT",
|
|
802
|
+
"IN",
|
|
803
|
+
"IS",
|
|
804
|
+
"NULL",
|
|
805
|
+
"LIKE",
|
|
806
|
+
"BETWEEN",
|
|
807
|
+
"EXISTS",
|
|
808
|
+
"HAVING",
|
|
809
|
+
"GROUP BY",
|
|
810
|
+
"ORDER BY",
|
|
811
|
+
"LIMIT",
|
|
812
|
+
"OFFSET",
|
|
813
|
+
"JOIN",
|
|
814
|
+
"LEFT JOIN",
|
|
815
|
+
"RIGHT JOIN",
|
|
816
|
+
"INNER JOIN",
|
|
817
|
+
"OUTER JOIN",
|
|
818
|
+
"FULL JOIN",
|
|
819
|
+
"CROSS JOIN",
|
|
820
|
+
"ON",
|
|
821
|
+
"AS",
|
|
822
|
+
"INSERT INTO",
|
|
823
|
+
"VALUES",
|
|
824
|
+
"UPDATE",
|
|
825
|
+
"SET",
|
|
826
|
+
"DELETE FROM",
|
|
827
|
+
"CREATE TABLE",
|
|
828
|
+
"ALTER TABLE",
|
|
829
|
+
"DROP TABLE",
|
|
830
|
+
"TRUNCATE TABLE",
|
|
831
|
+
"CREATE INDEX",
|
|
832
|
+
"DROP INDEX",
|
|
833
|
+
"CREATE VIEW",
|
|
834
|
+
"DROP VIEW",
|
|
835
|
+
"UNION",
|
|
836
|
+
"UNION ALL",
|
|
837
|
+
"INTERSECT",
|
|
838
|
+
"EXCEPT",
|
|
839
|
+
"CASE",
|
|
840
|
+
"WHEN",
|
|
841
|
+
"THEN",
|
|
842
|
+
"ELSE",
|
|
843
|
+
"END",
|
|
844
|
+
"ASC",
|
|
845
|
+
"DESC",
|
|
846
|
+
"DISTINCT",
|
|
847
|
+
"ALL",
|
|
848
|
+
"TOP",
|
|
849
|
+
"WITH",
|
|
850
|
+
"RECURSIVE",
|
|
851
|
+
"RETURNING",
|
|
852
|
+
"BEGIN",
|
|
853
|
+
"COMMIT",
|
|
854
|
+
"ROLLBACK",
|
|
855
|
+
"SAVEPOINT"
|
|
856
|
+
];
|
|
857
|
+
function uppercaseKeywords(sql) {
|
|
858
|
+
const pattern = new RegExp(`\\b(${KEYWORDS.join("|")})\\b`, "gi");
|
|
859
|
+
return sql.replace(pattern, (match) => match.toUpperCase());
|
|
860
|
+
}
|
|
861
|
+
function formatClauses(sql, indent) {
|
|
862
|
+
const clausePatterns = [
|
|
863
|
+
"SELECT",
|
|
864
|
+
"FROM",
|
|
865
|
+
"WHERE",
|
|
866
|
+
"GROUP BY",
|
|
867
|
+
"HAVING",
|
|
868
|
+
"ORDER BY",
|
|
869
|
+
"LIMIT",
|
|
870
|
+
"OFFSET",
|
|
871
|
+
"INSERT INTO",
|
|
872
|
+
"VALUES",
|
|
873
|
+
"UPDATE",
|
|
874
|
+
"SET",
|
|
875
|
+
"DELETE FROM"
|
|
876
|
+
];
|
|
877
|
+
for (const clause of clausePatterns) {
|
|
878
|
+
const regex = new RegExp(`\\b${clause}\\b`, "i");
|
|
879
|
+
sql = sql.replace(regex, `
|
|
880
|
+
${clause}`);
|
|
881
|
+
}
|
|
882
|
+
const joinPatterns = [
|
|
883
|
+
"JOIN",
|
|
884
|
+
"LEFT JOIN",
|
|
885
|
+
"RIGHT JOIN",
|
|
886
|
+
"INNER JOIN",
|
|
887
|
+
"OUTER JOIN",
|
|
888
|
+
"FULL JOIN",
|
|
889
|
+
"CROSS JOIN",
|
|
890
|
+
"UNION",
|
|
891
|
+
"UNION ALL",
|
|
892
|
+
"INTERSECT",
|
|
893
|
+
"EXCEPT"
|
|
894
|
+
];
|
|
895
|
+
for (const clause of joinPatterns) {
|
|
896
|
+
const regex = new RegExp(`\\n${clause}\\b`, "i");
|
|
897
|
+
if (regex.test(sql)) continue;
|
|
898
|
+
const inlineRegex = new RegExp(`\\b${clause}\\b`, "i");
|
|
899
|
+
sql = sql.replace(inlineRegex, `
|
|
900
|
+
${clause}`);
|
|
901
|
+
}
|
|
902
|
+
const lines = sql.split("\n").map((line) => line.trim());
|
|
903
|
+
return lines.map((line, i) => {
|
|
904
|
+
if (i > 0 && !line.startsWith(indent)) {
|
|
905
|
+
const trimmed = line.trim();
|
|
906
|
+
if (trimmed.length > 0 && !KEYWORDS.some((kw) => trimmed.toUpperCase().startsWith(kw))) {
|
|
907
|
+
return indent + line;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return line || "";
|
|
911
|
+
}).join("\n").replace(/\n{3,}/g, "\n\n");
|
|
912
|
+
}
|
|
913
|
+
function formatParentheses(sql, indent) {
|
|
914
|
+
let depth = 0;
|
|
915
|
+
return sql.split("").map((char) => {
|
|
916
|
+
if (char === "(") {
|
|
917
|
+
depth++;
|
|
918
|
+
return `(
|
|
919
|
+
${indent.repeat(depth)}`;
|
|
920
|
+
}
|
|
921
|
+
if (char === ")") {
|
|
922
|
+
depth--;
|
|
923
|
+
return `
|
|
924
|
+
${indent.repeat(depth)})`;
|
|
925
|
+
}
|
|
926
|
+
return char;
|
|
927
|
+
}).join("");
|
|
928
|
+
}
|
|
929
|
+
function formatCommas(sql) {
|
|
930
|
+
return sql.replace(/,\s*/g, ", ");
|
|
931
|
+
}
|
|
932
|
+
function formatOperators(sql) {
|
|
933
|
+
return sql.replace(/\s*=\s*/g, " = ").replace(/\s*!=\s*/g, " != ").replace(/\s*<>\s*/g, " <> ").replace(/\s*>\s*/g, " > ").replace(/\s*<\s*/g, " < ").replace(/\s*>=\s*/g, " >= ").replace(/\s*<=\s*/g, " <= ").replace(/\s*\+\s*/g, " + ").replace(/\s*-\s*/g, " - ");
|
|
934
|
+
}
|
|
935
|
+
function formatResultRow(row, columns) {
|
|
936
|
+
const formatted = {};
|
|
937
|
+
const cols = columns || Object.keys(row);
|
|
938
|
+
for (const col of cols) {
|
|
939
|
+
const value = row[col];
|
|
940
|
+
if (value === null || value === void 0) {
|
|
941
|
+
formatted[col] = "NULL";
|
|
942
|
+
} else if (Buffer.isBuffer(value)) {
|
|
943
|
+
formatted[col] = `[Buffer ${value.length} bytes]`;
|
|
944
|
+
} else if (value instanceof Date) {
|
|
945
|
+
formatted[col] = value.toISOString();
|
|
946
|
+
} else if (typeof value === "object") {
|
|
947
|
+
formatted[col] = JSON.stringify(value);
|
|
948
|
+
} else {
|
|
949
|
+
formatted[col] = value;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return formatted;
|
|
953
|
+
}
|
|
771
954
|
function truncateResult(rows, maxRows) {
|
|
772
955
|
if (rows.length <= maxRows) {
|
|
773
956
|
return { rows, truncated: false };
|
|
@@ -779,7 +962,7 @@ function truncateResult(rows, maxRows) {
|
|
|
779
962
|
var SQLDashboard = class extends EventEmitter {
|
|
780
963
|
constructor(options) {
|
|
781
964
|
super();
|
|
782
|
-
this.version = "1.0.
|
|
965
|
+
this.version = "1.0.1";
|
|
783
966
|
this.connected = false;
|
|
784
967
|
this.connectionPromise = null;
|
|
785
968
|
this.config = {
|
|
@@ -1027,7 +1210,22 @@ var SQLDashboard = class extends EventEmitter {
|
|
|
1027
1210
|
this.removeAllListeners();
|
|
1028
1211
|
}
|
|
1029
1212
|
};
|
|
1213
|
+
function createDashboard(options) {
|
|
1214
|
+
return new SQLDashboard(options);
|
|
1215
|
+
}
|
|
1030
1216
|
|
|
1031
1217
|
export {
|
|
1032
|
-
|
|
1218
|
+
calculatePagination,
|
|
1219
|
+
createPaginatedResult,
|
|
1220
|
+
ReadOnlyGuard,
|
|
1221
|
+
RateLimiter,
|
|
1222
|
+
Logger,
|
|
1223
|
+
detectStatementType,
|
|
1224
|
+
validateQuery,
|
|
1225
|
+
detectInjection,
|
|
1226
|
+
sanitizeIdentifier,
|
|
1227
|
+
formatSQL,
|
|
1228
|
+
formatResultRow,
|
|
1229
|
+
SQLDashboard,
|
|
1230
|
+
createDashboard
|
|
1033
1231
|
};
|