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.
Files changed (32) hide show
  1. package/dist/cli/chunk-3XB4P3G3.js +228 -0
  2. package/dist/cli/{chunk-5X2YZYYM.js → chunk-G7KSYREO.js} +43 -5
  3. package/dist/cli/chunk-Y3EHCZQX.js +220 -0
  4. package/dist/cli/{chunk-PTI2CUG6.js → chunk-Z2LV7EWL.js} +75 -4
  5. package/dist/cli/dev.js +317 -49
  6. package/dist/cli/index.js +317 -49
  7. package/dist/cli/{mssql-D7NDIUG7.js → mssql-AYS72PRQ.js} +1 -1
  8. package/dist/cli/{mssql-5VFXLOGV.js → mssql-HI3S4B7E.js} +3 -1
  9. package/dist/cli/{mssql-4KECNFPA.js → mssql-JB63TSGG.js} +4 -1
  10. package/dist/cli/mssql-Y5A62OYG.js +20 -0
  11. package/dist/client/assets/index-BhZ7NrhL.css +1 -0
  12. package/dist/client/assets/index-D-mash8_.js +370 -0
  13. package/dist/client/index.html +2 -2
  14. package/dist/server/{chunk-XFS5KTMI.js → chunk-JYZHE6GB.js} +43 -5
  15. package/dist/server/{chunk-7G2KHS5I.js → chunk-MJJGJZFC.js} +75 -4
  16. package/dist/server/chunk-VEX42Z2L.js +225 -0
  17. package/dist/server/chunk-VQPGGH2I.js +217 -0
  18. package/dist/server/dev.js +317 -49
  19. package/dist/server/index.js +317 -49
  20. package/dist/server/{mssql-UUT2IKOP.js → mssql-H6LKT5KN.js} +1 -1
  21. package/dist/server/mssql-JDRJT5H4.js +18 -0
  22. package/dist/server/mssql-LLTFKNX2.js +18 -0
  23. package/dist/server/mssql-UZUN227W.js +18 -0
  24. package/package.json +4 -3
  25. package/dist/cli/chunk-4IGBRCNN.js +0 -118
  26. package/dist/cli/chunk-IPR5JXB5.js +0 -108
  27. package/dist/cli/mssql-2REU4IX7.js +0 -17
  28. package/dist/client/assets/index-C32MC3if.js +0 -239
  29. package/dist/client/assets/index-piPKidzx.css +0 -1
  30. package/dist/server/chunk-XMPF5YCJ.js +0 -106
  31. package/dist/server/mssql-7KV6MCZK.js +0 -16
  32. 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-5X2YZYYM.js";
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-D7NDIUG7.js");
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-D7NDIUG7.js");
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-D7NDIUG7.js");
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-D7NDIUG7.js");
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 * FROM [${schema}].[${table}]
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
- generatedQuery = `SELECT * FROM (
315
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
316
- FROM [${schema}].[${table}]${whereClause ? "\n " + whereClause : ""}
317
- ) t
318
- WHERE rn > ${offset} AND rn <= ${offset + pageSize}
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 * FROM (
322
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
323
- FROM [${schema}].[${table}]
324
- ${whereClause}
325
- ) t
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-D7NDIUG7.js");
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
- res.status(500).json({
358
- error: errorMessage,
359
- details: errorDetails
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-D7NDIUG7.js");
668
+ const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
401
669
  await disconnect2();
402
670
  }
403
671
  res.status(500).json({
@@ -8,7 +8,7 @@ import {
8
8
  getConnection,
9
9
  parseConnectionString,
10
10
  testConnection
11
- } from "./chunk-5X2YZYYM.js";
11
+ } from "./chunk-3XB4P3G3.js";
12
12
  export {
13
13
  connect,
14
14
  disconnect,
@@ -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-PTI2CUG6.js";
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-4IGBRCNN.js";
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}}