datapeek 0.1.8 → 0.1.10
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/cli/chunk-3XB4P3G3.js +228 -0
- package/dist/cli/{chunk-5X2YZYYM.js → chunk-G7KSYREO.js} +43 -5
- package/dist/cli/chunk-Y3EHCZQX.js +220 -0
- package/dist/cli/{chunk-PTI2CUG6.js → chunk-Z2LV7EWL.js} +75 -4
- package/dist/cli/dev.js +317 -49
- package/dist/cli/index.js +317 -49
- package/dist/cli/{mssql-D7NDIUG7.js → mssql-AYS72PRQ.js} +1 -1
- package/dist/cli/{mssql-5VFXLOGV.js → mssql-HI3S4B7E.js} +3 -1
- package/dist/cli/{mssql-4KECNFPA.js → mssql-JB63TSGG.js} +4 -1
- package/dist/cli/mssql-Y5A62OYG.js +20 -0
- package/dist/client/assets/index-BhZ7NrhL.css +1 -0
- package/dist/client/assets/index-D-mash8_.js +370 -0
- package/dist/client/index.html +2 -2
- package/dist/server/{chunk-XFS5KTMI.js → chunk-JYZHE6GB.js} +43 -5
- package/dist/server/{chunk-7G2KHS5I.js → chunk-MJJGJZFC.js} +75 -4
- package/dist/server/chunk-VEX42Z2L.js +225 -0
- package/dist/server/chunk-VQPGGH2I.js +217 -0
- package/dist/server/dev.js +317 -49
- package/dist/server/index.js +317 -49
- package/dist/server/{mssql-UUT2IKOP.js → mssql-H6LKT5KN.js} +1 -1
- package/dist/server/mssql-JDRJT5H4.js +18 -0
- package/dist/server/mssql-LLTFKNX2.js +18 -0
- package/dist/server/mssql-UZUN227W.js +18 -0
- package/package.json +4 -3
- package/dist/cli/chunk-4IGBRCNN.js +0 -118
- package/dist/cli/chunk-IPR5JXB5.js +0 -108
- package/dist/cli/mssql-2REU4IX7.js +0 -17
- package/dist/client/assets/index-C32MC3if.js +0 -239
- package/dist/client/assets/index-piPKidzx.css +0 -1
- package/dist/server/chunk-XMPF5YCJ.js +0 -106
- package/dist/server/mssql-7KV6MCZK.js +0 -16
- package/dist/server/mssql-XRYTOVSF.js +0 -16
package/dist/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
executeQueryMultiple,
|
|
8
8
|
getConnection,
|
|
9
9
|
testConnection
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3XB4P3G3.js";
|
|
11
11
|
|
|
12
12
|
// src/cli/index.ts
|
|
13
13
|
import { Command } from "commander";
|
|
@@ -91,7 +91,7 @@ connectionRoutes.delete("/", async (req, res) => {
|
|
|
91
91
|
});
|
|
92
92
|
connectionRoutes.get("/status", async (req, res) => {
|
|
93
93
|
try {
|
|
94
|
-
const { getConnection: getConnection2, executeQuery: executeQuery3 } = await import("./mssql-
|
|
94
|
+
const { getConnection: getConnection2, executeQuery: executeQuery3 } = await import("./mssql-AYS72PRQ.js");
|
|
95
95
|
const pool = getConnection2();
|
|
96
96
|
if (pool && pool.connected) {
|
|
97
97
|
try {
|
|
@@ -101,7 +101,7 @@ connectionRoutes.get("/status", async (req, res) => {
|
|
|
101
101
|
} catch (error) {
|
|
102
102
|
const errorMessage = error.message || "";
|
|
103
103
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
104
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
104
|
+
const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
|
|
105
105
|
await disconnect2();
|
|
106
106
|
}
|
|
107
107
|
res.json({ connected: false });
|
|
@@ -137,7 +137,7 @@ tableRoutes.get("/", async (req, res) => {
|
|
|
137
137
|
} catch (error) {
|
|
138
138
|
const errorMessage = error.message || "";
|
|
139
139
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
140
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
140
|
+
const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
|
|
141
141
|
await disconnect2();
|
|
142
142
|
}
|
|
143
143
|
res.status(500).json({ error: error.message || "Failed to fetch tables" });
|
|
@@ -157,7 +157,10 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
|
|
|
157
157
|
c.CHARACTER_MAXIMUM_LENGTH as maxLength,
|
|
158
158
|
c.IS_NULLABLE as isNullable,
|
|
159
159
|
c.COLUMN_DEFAULT as defaultValue,
|
|
160
|
-
CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END as isPrimaryKey
|
|
160
|
+
CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END as isPrimaryKey,
|
|
161
|
+
fk.REFERENCED_TABLE_SCHEMA as referencedSchema,
|
|
162
|
+
fk.REFERENCED_TABLE_NAME as referencedTable,
|
|
163
|
+
fk.REFERENCED_COLUMN_NAME as referencedColumn
|
|
161
164
|
FROM INFORMATION_SCHEMA.COLUMNS c
|
|
162
165
|
LEFT JOIN (
|
|
163
166
|
SELECT ku.TABLE_SCHEMA, ku.TABLE_NAME, ku.COLUMN_NAME
|
|
@@ -168,6 +171,27 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
|
|
|
168
171
|
) pk ON c.TABLE_SCHEMA = pk.TABLE_SCHEMA
|
|
169
172
|
AND c.TABLE_NAME = pk.TABLE_NAME
|
|
170
173
|
AND c.COLUMN_NAME = pk.COLUMN_NAME
|
|
174
|
+
LEFT JOIN (
|
|
175
|
+
SELECT
|
|
176
|
+
kcu1.TABLE_SCHEMA,
|
|
177
|
+
kcu1.TABLE_NAME,
|
|
178
|
+
kcu1.COLUMN_NAME,
|
|
179
|
+
kcu2.TABLE_SCHEMA as REFERENCED_TABLE_SCHEMA,
|
|
180
|
+
kcu2.TABLE_NAME as REFERENCED_TABLE_NAME,
|
|
181
|
+
kcu2.COLUMN_NAME as REFERENCED_COLUMN_NAME
|
|
182
|
+
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
|
|
183
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
|
|
184
|
+
ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
|
|
185
|
+
AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
|
|
186
|
+
AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
|
|
187
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
|
|
188
|
+
ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
|
|
189
|
+
AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
|
|
190
|
+
AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
|
|
191
|
+
AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
|
|
192
|
+
) fk ON c.TABLE_SCHEMA = fk.TABLE_SCHEMA
|
|
193
|
+
AND c.TABLE_NAME = fk.TABLE_NAME
|
|
194
|
+
AND c.COLUMN_NAME = fk.COLUMN_NAME
|
|
171
195
|
WHERE c.TABLE_SCHEMA = @schema
|
|
172
196
|
AND c.TABLE_NAME = @table
|
|
173
197
|
ORDER BY c.ORDINAL_POSITION
|
|
@@ -180,7 +204,7 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
|
|
|
180
204
|
} catch (error) {
|
|
181
205
|
const errorMessage = error.message || "";
|
|
182
206
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
183
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
207
|
+
const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
|
|
184
208
|
await disconnect2();
|
|
185
209
|
}
|
|
186
210
|
res.status(500).json({ error: error.message || "Failed to fetch table structure" });
|
|
@@ -193,6 +217,7 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
|
|
|
193
217
|
const pageSize = Math.min(parseInt(req.query.pageSize) || 100, 1e3);
|
|
194
218
|
const sortColumn = req.query.sortColumn;
|
|
195
219
|
const sortDirection = req.query.sortDirection || "asc";
|
|
220
|
+
const fkDisplayMode = req.query.fkDisplayMode || "key-only";
|
|
196
221
|
const offset = (page - 1) * pageSize;
|
|
197
222
|
const filters = {};
|
|
198
223
|
Object.keys(req.query).forEach((key) => {
|
|
@@ -291,41 +316,179 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
|
|
|
291
316
|
} catch (e) {
|
|
292
317
|
}
|
|
293
318
|
}
|
|
319
|
+
const fkJoins = [];
|
|
320
|
+
const fkSelects = [];
|
|
321
|
+
const fkDisplayColumns = {};
|
|
322
|
+
if (fkDisplayMode === "key-display" || fkDisplayMode === "display-only") {
|
|
323
|
+
const fkQuery = `
|
|
324
|
+
SELECT
|
|
325
|
+
kcu1.COLUMN_NAME as fkColumnName,
|
|
326
|
+
kcu2.TABLE_SCHEMA as referencedSchema,
|
|
327
|
+
kcu2.TABLE_NAME as referencedTable,
|
|
328
|
+
kcu2.COLUMN_NAME as referencedColumn
|
|
329
|
+
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
|
|
330
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
|
|
331
|
+
ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
|
|
332
|
+
AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
|
|
333
|
+
AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
|
|
334
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
|
|
335
|
+
ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
|
|
336
|
+
AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
|
|
337
|
+
AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
|
|
338
|
+
AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
|
|
339
|
+
WHERE kcu1.TABLE_SCHEMA = @schema
|
|
340
|
+
AND kcu1.TABLE_NAME = @table
|
|
341
|
+
`;
|
|
342
|
+
console.log("Fetching foreign keys with query:", fkQuery);
|
|
343
|
+
const foreignKeys = await executeQuery(fkQuery, [
|
|
344
|
+
{ name: "schema", value: schema, type: sql.NVarChar },
|
|
345
|
+
{ name: "table", value: table, type: sql.NVarChar }
|
|
346
|
+
]);
|
|
347
|
+
console.log(`Found ${foreignKeys.length} foreign key(s)`);
|
|
348
|
+
if (foreignKeys.length > 0) {
|
|
349
|
+
const uniqueRefTables = Array.from(
|
|
350
|
+
new Set(foreignKeys.map((fk) => `${fk.referencedSchema}.${fk.referencedTable}`))
|
|
351
|
+
);
|
|
352
|
+
const tableConditions = uniqueRefTables.map((tableRef, idx) => {
|
|
353
|
+
const [refSchema, refTable] = tableRef.split(".");
|
|
354
|
+
return `(TABLE_SCHEMA = @refSchema${idx} AND TABLE_NAME = @refTable${idx})`;
|
|
355
|
+
}).join(" OR ");
|
|
356
|
+
const batchColumnsQuery = `
|
|
357
|
+
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, ORDINAL_POSITION
|
|
358
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
359
|
+
WHERE ${tableConditions}
|
|
360
|
+
ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION
|
|
361
|
+
`;
|
|
362
|
+
const batchParams = uniqueRefTables.flatMap((tableRef, idx) => {
|
|
363
|
+
const [refSchema, refTable] = tableRef.split(".");
|
|
364
|
+
return [
|
|
365
|
+
{ name: `refSchema${idx}`, value: refSchema, type: sql.NVarChar },
|
|
366
|
+
{ name: `refTable${idx}`, value: refTable, type: sql.NVarChar }
|
|
367
|
+
];
|
|
368
|
+
});
|
|
369
|
+
console.log("Fetching referenced table columns with query:", batchColumnsQuery);
|
|
370
|
+
console.log("Batch parameters:", batchParams.map((p) => ({ name: p.name, value: p.value })));
|
|
371
|
+
const allRefColumns = await executeQuery(batchColumnsQuery, batchParams);
|
|
372
|
+
console.log(`Found columns for ${uniqueRefTables.length} referenced table(s)`);
|
|
373
|
+
const columnsByTable = {};
|
|
374
|
+
allRefColumns.forEach((col) => {
|
|
375
|
+
const key = `${col.TABLE_SCHEMA}.${col.TABLE_NAME}`;
|
|
376
|
+
if (!columnsByTable[key]) {
|
|
377
|
+
columnsByTable[key] = [];
|
|
378
|
+
}
|
|
379
|
+
columnsByTable[key].push(col);
|
|
380
|
+
});
|
|
381
|
+
for (const fk of foreignKeys) {
|
|
382
|
+
const fkColumn = fk.fkColumnName;
|
|
383
|
+
const refSchema = fk.referencedSchema;
|
|
384
|
+
const refTable = fk.referencedTable;
|
|
385
|
+
const refColumn = fk.referencedColumn;
|
|
386
|
+
const tableKey = `${refSchema}.${refTable}`;
|
|
387
|
+
const refColumns = columnsByTable[tableKey] || [];
|
|
388
|
+
const preferredNames = ["name", "title", "description", "code"];
|
|
389
|
+
let displayColumn = null;
|
|
390
|
+
for (const preferredName of preferredNames) {
|
|
391
|
+
const found = refColumns.find(
|
|
392
|
+
(col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
|
|
393
|
+
);
|
|
394
|
+
if (found) {
|
|
395
|
+
displayColumn = found.COLUMN_NAME;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (!displayColumn) {
|
|
400
|
+
const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
|
|
401
|
+
const found = refColumns.find(
|
|
402
|
+
(col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
|
|
403
|
+
);
|
|
404
|
+
if (found) {
|
|
405
|
+
displayColumn = found.COLUMN_NAME;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (displayColumn) {
|
|
409
|
+
const alias = `fk_${fkColumn}`;
|
|
410
|
+
fkJoins.push({
|
|
411
|
+
alias,
|
|
412
|
+
refSchema,
|
|
413
|
+
refTable,
|
|
414
|
+
fkColumn,
|
|
415
|
+
refColumn,
|
|
416
|
+
displayColumn
|
|
417
|
+
});
|
|
418
|
+
fkSelects.push(`${alias}.[${displayColumn}] as [${fkColumn}_display]`);
|
|
419
|
+
fkDisplayColumns[fkColumn] = displayColumn;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const baseTableAlias = "t";
|
|
425
|
+
let baseSelects = `[${baseTableAlias}].*`;
|
|
426
|
+
if (fkDisplayMode === "display-only") {
|
|
427
|
+
const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
|
|
428
|
+
if (fkColumnNames.length > 0) {
|
|
429
|
+
baseSelects = `[${baseTableAlias}].*`;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const allSelects = `${baseSelects}${fkSelects.length > 0 ? ", " + fkSelects.join(", ") : ""}`;
|
|
433
|
+
const buildJoinClauses = (tableAlias) => {
|
|
434
|
+
return fkJoins.map(
|
|
435
|
+
(fk) => `LEFT JOIN [${fk.refSchema}].[${fk.refTable}] ${fk.alias} ON [${tableAlias}].[${fk.fkColumn}] = ${fk.alias}.[${fk.refColumn}]`
|
|
436
|
+
).join("\n ");
|
|
437
|
+
};
|
|
294
438
|
let data;
|
|
295
439
|
let generatedQuery = "";
|
|
296
440
|
if (orderByColumn) {
|
|
297
|
-
generatedQuery = `SELECT * FROM [${schema}].[${table}]${whereClause ? "\n" + whereClause : ""}
|
|
298
|
-
ORDER BY [${orderByColumn}] ${orderByDirection}
|
|
299
|
-
OFFSET ${offset} ROWS
|
|
300
|
-
FETCH NEXT ${pageSize} ROWS ONLY`;
|
|
301
441
|
const dataQuery = `
|
|
302
|
-
SELECT
|
|
442
|
+
SELECT ${allSelects}
|
|
443
|
+
FROM [${schema}].[${table}] ${baseTableAlias}
|
|
444
|
+
${buildJoinClauses(baseTableAlias)}
|
|
303
445
|
${whereClause}
|
|
304
|
-
ORDER BY [${orderByColumn}] ${orderByDirection}
|
|
446
|
+
ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
|
|
305
447
|
OFFSET @offset ROWS
|
|
306
448
|
FETCH NEXT @pageSize ROWS ONLY
|
|
307
449
|
`;
|
|
450
|
+
generatedQuery = `SELECT ${allSelects}
|
|
451
|
+
FROM [${schema}].[${table}] ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}${whereClause ? "\n" + whereClause : ""}
|
|
452
|
+
ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
|
|
453
|
+
OFFSET ${offset} ROWS
|
|
454
|
+
FETCH NEXT ${pageSize} ROWS ONLY`;
|
|
455
|
+
console.log("Executing SQL query:", dataQuery);
|
|
456
|
+
console.log("Query parameters:", {
|
|
457
|
+
offset,
|
|
458
|
+
pageSize,
|
|
459
|
+
filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
|
|
460
|
+
});
|
|
308
461
|
data = await executeQuery(dataQuery, [
|
|
309
462
|
{ name: "offset", value: offset, type: sql.Int },
|
|
310
463
|
{ name: "pageSize", value: pageSize, type: sql.Int },
|
|
311
464
|
...filterParams
|
|
312
465
|
]);
|
|
313
466
|
} else {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
ORDER BY rn`;
|
|
467
|
+
const innerQuery = `
|
|
468
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
|
|
469
|
+
FROM [${schema}].[${table}]
|
|
470
|
+
${whereClause}
|
|
471
|
+
`;
|
|
320
472
|
const dataQuery = `
|
|
321
|
-
SELECT
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
WHERE rn > @offset AND rn <= @offset + @pageSize
|
|
327
|
-
ORDER BY rn
|
|
473
|
+
SELECT ${allSelects}
|
|
474
|
+
FROM (${innerQuery}) ${baseTableAlias}
|
|
475
|
+
${buildJoinClauses(baseTableAlias)}
|
|
476
|
+
WHERE ${baseTableAlias}.rn > @offset AND ${baseTableAlias}.rn <= @offset + @pageSize
|
|
477
|
+
ORDER BY ${baseTableAlias}.rn
|
|
328
478
|
`;
|
|
479
|
+
generatedQuery = `SELECT ${allSelects}
|
|
480
|
+
FROM (
|
|
481
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
|
|
482
|
+
FROM [${schema}].[${table}]${whereClause ? "\n " + whereClause : ""}
|
|
483
|
+
) ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}
|
|
484
|
+
WHERE ${baseTableAlias}.rn > ${offset} AND ${baseTableAlias}.rn <= ${offset + pageSize}
|
|
485
|
+
ORDER BY ${baseTableAlias}.rn`;
|
|
486
|
+
console.log("Executing SQL query:", dataQuery);
|
|
487
|
+
console.log("Query parameters:", {
|
|
488
|
+
offset,
|
|
489
|
+
pageSize,
|
|
490
|
+
filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
|
|
491
|
+
});
|
|
329
492
|
data = await executeQuery(dataQuery, [
|
|
330
493
|
{ name: "offset", value: offset, type: sql.Int },
|
|
331
494
|
{ name: "pageSize", value: pageSize, type: sql.Int },
|
|
@@ -336,6 +499,21 @@ ORDER BY rn`;
|
|
|
336
499
|
return rest;
|
|
337
500
|
});
|
|
338
501
|
}
|
|
502
|
+
if (fkDisplayMode === "display-only") {
|
|
503
|
+
const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
|
|
504
|
+
data = data.map((row) => {
|
|
505
|
+
const filteredRow = { ...row };
|
|
506
|
+
fkColumnNames.forEach((fkCol) => {
|
|
507
|
+
delete filteredRow[fkCol];
|
|
508
|
+
const displayColName = `${fkCol}_display`;
|
|
509
|
+
if (displayColName in filteredRow) {
|
|
510
|
+
filteredRow[fkCol] = filteredRow[displayColName];
|
|
511
|
+
delete filteredRow[displayColName];
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
return filteredRow;
|
|
515
|
+
});
|
|
516
|
+
}
|
|
339
517
|
res.json({
|
|
340
518
|
data,
|
|
341
519
|
query: generatedQuery,
|
|
@@ -344,20 +522,124 @@ ORDER BY rn`;
|
|
|
344
522
|
pageSize,
|
|
345
523
|
total,
|
|
346
524
|
totalPages: Math.ceil(total / pageSize)
|
|
347
|
-
}
|
|
525
|
+
},
|
|
526
|
+
foreignKeyDisplays: fkDisplayColumns,
|
|
527
|
+
fkDisplayMode
|
|
348
528
|
});
|
|
349
529
|
} catch (error) {
|
|
350
530
|
console.error("Error fetching table data:", error);
|
|
351
531
|
const errorMessage = error.message || "";
|
|
352
532
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
353
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
533
|
+
const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
|
|
354
534
|
await disconnect2();
|
|
355
535
|
}
|
|
536
|
+
const isTimeout = error.code === "ETIMEOUT" || error.code === "ESOCKET" || error.message?.includes("timeout") || error.message?.includes("ETIMEDOUT") || error.originalError?.code === "ETIMEOUT" || error.originalError?.code === "ESOCKET";
|
|
356
537
|
const errorDetails = error.originalError?.message || error.originalError?.info?.message || "";
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
538
|
+
if (isTimeout) {
|
|
539
|
+
res.status(408).json({
|
|
540
|
+
error: errorMessage || "Query execution timeout",
|
|
541
|
+
details: errorDetails || "The query took too long to execute. Try disabling foreign key displays or reducing the page size.",
|
|
542
|
+
timeout: true
|
|
543
|
+
});
|
|
544
|
+
} else {
|
|
545
|
+
res.status(500).json({
|
|
546
|
+
error: errorMessage,
|
|
547
|
+
details: errorDetails
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
|
|
553
|
+
try {
|
|
554
|
+
const { schema, table } = req.params;
|
|
555
|
+
const { foreignKeyColumn, referencedSchema, referencedTable, referencedColumn, ids } = req.body;
|
|
556
|
+
if (!foreignKeyColumn || !referencedSchema || !referencedTable || !referencedColumn || !ids || !Array.isArray(ids) || ids.length === 0) {
|
|
557
|
+
return res.status(400).json({ error: "Missing required parameters" });
|
|
558
|
+
}
|
|
559
|
+
const pool = getConnection();
|
|
560
|
+
if (!pool || !pool.connected) {
|
|
561
|
+
return res.status(400).json({ error: "Not connected to database" });
|
|
562
|
+
}
|
|
563
|
+
const columnsQuery = `
|
|
564
|
+
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
|
|
565
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
566
|
+
WHERE TABLE_SCHEMA = @refSchema
|
|
567
|
+
AND TABLE_NAME = @refTable
|
|
568
|
+
ORDER BY ORDINAL_POSITION
|
|
569
|
+
`;
|
|
570
|
+
const columns = await executeQuery(columnsQuery, [
|
|
571
|
+
{ name: "refSchema", value: referencedSchema, type: sql.NVarChar },
|
|
572
|
+
{ name: "refTable", value: referencedTable, type: sql.NVarChar }
|
|
573
|
+
]);
|
|
574
|
+
const referencedColInfo = columns.find(
|
|
575
|
+
(col) => col.COLUMN_NAME === referencedColumn
|
|
576
|
+
);
|
|
577
|
+
if (!referencedColInfo) {
|
|
578
|
+
return res.status(400).json({ error: `Referenced column '${referencedColumn}' not found` });
|
|
579
|
+
}
|
|
580
|
+
const getSqlType = (dataType) => {
|
|
581
|
+
const dt = dataType.toLowerCase();
|
|
582
|
+
if (dt === "int" || dt === "integer") return sql.Int;
|
|
583
|
+
if (dt === "bigint") return sql.BigInt;
|
|
584
|
+
if (dt === "smallint") return sql.SmallInt;
|
|
585
|
+
if (dt === "tinyint") return sql.TinyInt;
|
|
586
|
+
if (dt === "bit") return sql.Bit;
|
|
587
|
+
if (dt === "float" || dt === "real" || dt === "double precision") return sql.Float;
|
|
588
|
+
if (dt === "decimal" || dt === "numeric" || dt === "money" || dt === "smallmoney") return sql.Decimal(18, 0);
|
|
589
|
+
if (dt === "datetime" || dt === "datetime2" || dt === "smalldatetime") return sql.DateTime;
|
|
590
|
+
if (dt === "date") return sql.Date;
|
|
591
|
+
if (dt === "time") return sql.Time;
|
|
592
|
+
if (dt === "uniqueidentifier") return sql.UniqueIdentifier;
|
|
593
|
+
return sql.NVarChar;
|
|
594
|
+
};
|
|
595
|
+
const referencedColumnType = getSqlType(referencedColInfo.DATA_TYPE);
|
|
596
|
+
const preferredNames = ["name", "title", "description", "code"];
|
|
597
|
+
let displayColumn = null;
|
|
598
|
+
for (const preferredName of preferredNames) {
|
|
599
|
+
const found = columns.find(
|
|
600
|
+
(col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
|
|
601
|
+
);
|
|
602
|
+
if (found) {
|
|
603
|
+
displayColumn = found.COLUMN_NAME;
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (!displayColumn) {
|
|
608
|
+
const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
|
|
609
|
+
const found = columns.find(
|
|
610
|
+
(col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
|
|
611
|
+
);
|
|
612
|
+
if (found) {
|
|
613
|
+
displayColumn = found.COLUMN_NAME;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const placeholders = ids.map((_, i) => `@id${i}`).join(", ");
|
|
617
|
+
const selectColumns = displayColumn ? `[${referencedColumn}], [${displayColumn}]` : `[${referencedColumn}]`;
|
|
618
|
+
const dataQuery = `
|
|
619
|
+
SELECT ${selectColumns}
|
|
620
|
+
FROM [${referencedSchema}].[${referencedTable}]
|
|
621
|
+
WHERE [${referencedColumn}] IN (${placeholders})
|
|
622
|
+
`;
|
|
623
|
+
const params = ids.map((id, i) => ({
|
|
624
|
+
name: `id${i}`,
|
|
625
|
+
value: id,
|
|
626
|
+
type: referencedColumnType
|
|
627
|
+
}));
|
|
628
|
+
const result = await executeQuery(dataQuery, params);
|
|
629
|
+
const dataMap = {};
|
|
630
|
+
result.forEach((row) => {
|
|
631
|
+
const key = String(row[referencedColumn]);
|
|
632
|
+
dataMap[key] = displayColumn ? row[displayColumn] : null;
|
|
360
633
|
});
|
|
634
|
+
res.json({ dataMap, displayColumn });
|
|
635
|
+
} catch (error) {
|
|
636
|
+
console.error("Error fetching related data:", error);
|
|
637
|
+
const errorMessage = error.message || "";
|
|
638
|
+
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
639
|
+
const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
|
|
640
|
+
await disconnect2();
|
|
641
|
+
}
|
|
642
|
+
res.status(500).json({ error: error.message || "Failed to fetch related data" });
|
|
361
643
|
}
|
|
362
644
|
});
|
|
363
645
|
|
|
@@ -370,34 +652,20 @@ queryRoutes.post("/", async (req, res) => {
|
|
|
370
652
|
if (!sqlQuery || typeof sqlQuery !== "string") {
|
|
371
653
|
return res.status(400).json({ error: "Query is required" });
|
|
372
654
|
}
|
|
373
|
-
const trimmedQuery = sqlQuery.trim().toUpperCase();
|
|
374
|
-
if (!trimmedQuery.startsWith("SELECT")) {
|
|
375
|
-
return res.status(400).json({
|
|
376
|
-
error: "Only SELECT queries are allowed (read-only mode)"
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
const dangerousKeywords = ["DROP", "DELETE", "INSERT", "UPDATE", "ALTER", "CREATE", "TRUNCATE", "EXEC", "EXECUTE"];
|
|
380
|
-
const hasDangerousKeyword = dangerousKeywords.some((keyword) => {
|
|
381
|
-
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
382
|
-
return regex.test(sqlQuery);
|
|
383
|
-
});
|
|
384
|
-
if (hasDangerousKeyword) {
|
|
385
|
-
return res.status(400).json({
|
|
386
|
-
error: "Query contains prohibited keywords. Only SELECT queries are allowed."
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
655
|
const startTime = Date.now();
|
|
390
|
-
const resultSets = await executeQueryMultiple(sqlQuery);
|
|
656
|
+
const { recordsets: resultSets, columnMetadata } = await executeQueryMultiple(sqlQuery);
|
|
391
657
|
const executionTime = Date.now() - startTime;
|
|
392
658
|
res.json({
|
|
393
659
|
data: resultSets[0] || [],
|
|
394
660
|
resultSets: resultSets.length > 0 ? resultSets : [],
|
|
395
|
-
executionTime
|
|
661
|
+
executionTime,
|
|
662
|
+
columnMetadata
|
|
663
|
+
// Include column metadata for empty result sets
|
|
396
664
|
});
|
|
397
665
|
} catch (error) {
|
|
398
666
|
const errorMessage = error.message || "";
|
|
399
667
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
400
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
668
|
+
const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
|
|
401
669
|
await disconnect2();
|
|
402
670
|
}
|
|
403
671
|
res.status(500).json({
|
|
@@ -4,14 +4,16 @@ import {
|
|
|
4
4
|
connect,
|
|
5
5
|
disconnect,
|
|
6
6
|
executeQuery,
|
|
7
|
+
executeQueryMultiple,
|
|
7
8
|
getConnection,
|
|
8
9
|
parseConnectionString,
|
|
9
10
|
testConnection
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-G7KSYREO.js";
|
|
11
12
|
export {
|
|
12
13
|
connect,
|
|
13
14
|
disconnect,
|
|
14
15
|
executeQuery,
|
|
16
|
+
executeQueryMultiple,
|
|
15
17
|
getConnection,
|
|
16
18
|
parseConnectionString,
|
|
17
19
|
testConnection
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
import {
|
|
3
4
|
connect,
|
|
4
5
|
disconnect,
|
|
5
6
|
executeQuery,
|
|
7
|
+
executeQueryMultiple,
|
|
6
8
|
getConnection,
|
|
7
9
|
parseConnectionString,
|
|
8
10
|
testConnection
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-Y3EHCZQX.js";
|
|
10
12
|
export {
|
|
11
13
|
connect,
|
|
12
14
|
disconnect,
|
|
13
15
|
executeQuery,
|
|
16
|
+
executeQueryMultiple,
|
|
14
17
|
getConnection,
|
|
15
18
|
parseConnectionString,
|
|
16
19
|
testConnection
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
connect,
|
|
5
|
+
disconnect,
|
|
6
|
+
executeQuery,
|
|
7
|
+
executeQueryMultiple,
|
|
8
|
+
getConnection,
|
|
9
|
+
parseConnectionString,
|
|
10
|
+
testConnection
|
|
11
|
+
} from "./chunk-Z2LV7EWL.js";
|
|
12
|
+
export {
|
|
13
|
+
connect,
|
|
14
|
+
disconnect,
|
|
15
|
+
executeQuery,
|
|
16
|
+
executeQueryMultiple,
|
|
17
|
+
getConnection,
|
|
18
|
+
parseConnectionString,
|
|
19
|
+
testConnection
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 100%;--foreground: 222.2 84% 4.9%;--card: 0 0% 100%;--card-foreground: 222.2 84% 4.9%;--popover: 0 0% 100%;--popover-foreground: 222.2 84% 4.9%;--primary: 222.2 47.4% 11.2%;--primary-foreground: 210 40% 98%;--secondary: 210 40% 96.1%;--secondary-foreground: 222.2 47.4% 11.2%;--muted: 210 40% 96.1%;--muted-foreground: 215.4 16.3% 46.9%;--accent: 210 40% 96.1%;--accent-foreground: 222.2 47.4% 11.2%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 210 40% 98%;--border: 214.3 31.8% 91.4%;--input: 214.3 31.8% 91.4%;--ring: 222.2 84% 4.9%;--radius: .5rem;--chart-1: 12 76% 61%;--chart-2: 173 58% 39%;--chart-3: 197 37% 24%;--chart-4: 43 74% 66%;--chart-5: 27 87% 67%;--header-bg: 0 0% 100%;--sidebar-bg: 0 0% 100%;--content-bg: 0 0% 100%;--tabs-bg: 0 0% 100%;--grid-bg: 0 0% 100%}.dark{--background: 222.2 84% 4.9%;--foreground: 210 40% 98%;--card: 222.2 84% 4.9%;--card-foreground: 210 40% 98%;--popover: 222.2 84% 4.9%;--popover-foreground: 210 40% 98%;--primary: 210 40% 98%;--primary-foreground: 222.2 47.4% 11.2%;--secondary: 217.2 32.6% 17.5%;--secondary-foreground: 210 40% 98%;--muted: 217.2 32.6% 17.5%;--muted-foreground: 215 20.2% 65.1%;--accent: 217.2 32.6% 17.5%;--accent-foreground: 210 40% 98%;--destructive: 0 70% 55%;--destructive-foreground: 210 40% 98%;--border: 217.2 32.6% 17.5%;--input: 217.2 32.6% 17.5%;--ring: 212.7 26.8% 83.9%;--chart-1: 220 70% 50%;--chart-2: 160 60% 45%;--chart-3: 30 80% 55%;--chart-4: 280 65% 60%;--chart-5: 340 75% 55%;--header-bg: 222.2 84% 6.5%;--sidebar-bg: 222.2 84% 5.5%;--content-bg: 222.2 84% 4.9%;--tabs-bg: 222.2 84% 5.2%;--grid-bg: 222.2 84% 5%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}code,pre{font-family:JetBrains Mono,Menlo,Monaco,Courier New,monospace}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.left-0{left:0}.left-2{left:.5rem}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.m-2{margin:.5rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1{height:.25rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-64{max-height:16rem}.max-h-96{max-height:24rem}.w-12{width:3rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-auto{width:auto}.w-full{width:100%}.w-px{width:1px}.min-w-\[8rem\]{min-width:8rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-cell{cursor:cell}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-border\/50{border-color:hsl(var(--border) / .5)}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-primary{border-color:hsl(var(--primary))}.bg-accent{background-color:hsl(var(--accent))}.bg-background{background-color:hsl(var(--background))}.bg-background\/80{background-color:hsl(var(--background) / .8)}.bg-black\/50{background-color:#00000080}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-content-bg{background-color:hsl(var(--content-bg))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive) / .1)}.bg-destructive\/90{background-color:hsl(var(--destructive) / .9)}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-grid-bg{background-color:hsl(var(--grid-bg))}.bg-header-bg{background-color:hsl(var(--header-bg))}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/30{background-color:hsl(var(--muted) / .3)}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/20{background-color:hsl(var(--primary) / .2)}.bg-primary\/90{background-color:hsl(var(--primary) / .9)}.bg-secondary{background-color:hsl(var(--secondary))}.bg-secondary\/80{background-color:hsl(var(--secondary) / .8)}.bg-sidebar-bg{background-color:hsl(var(--sidebar-bg))}.bg-tabs-bg{background-color:hsl(var(--tabs-bg))}.bg-transparent{background-color:transparent}.fill-yellow-500{fill:#eab308}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pl-8{padding-left:2rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--foreground))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:border-b-0:last-child{border-bottom-width:0px}.last\:border-r-0:last-child{border-right-width:0px}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-green-500\/20:hover{background-color:#22c55e33}.hover\:bg-muted\/30:hover{background-color:hsl(var(--muted) / .3)}.hover\:bg-muted\/70:hover{background-color:hsl(var(--muted) / .7)}.hover\:bg-primary\/50:hover{background-color:hsl(var(--primary) / .5)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:bg-transparent:hover{background-color:transparent}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:bg-destructive\/10:focus{background-color:hsl(var(--destructive) / .1)}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:text-destructive:focus{color:hsl(var(--destructive))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.dark\:bg-content-bg:is(.dark *){background-color:hsl(var(--content-bg))}.dark\:bg-grid-bg:is(.dark *){background-color:hsl(var(--grid-bg))}.dark\:bg-header-bg:is(.dark *){background-color:hsl(var(--header-bg))}.dark\:bg-sidebar-bg:is(.dark *){background-color:hsl(var(--sidebar-bg))}.dark\:bg-tabs-bg:is(.dark *){background-color:hsl(var(--tabs-bg))}.dark\:text-green-400:is(.dark *){--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}@media(min-width:640px){.sm\:max-w-\[500px\]{max-width:500px}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}
|