@smallwebco/tinypivot-server 1.0.60 → 1.0.62
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/index.cjs +85 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +85 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -328,6 +328,8 @@ function createTinyPivotHandler(options = {}) {
|
|
|
328
328
|
return handleListTables(connectionString, schemas, tableOptions, descriptions, onError);
|
|
329
329
|
case "get-schema":
|
|
330
330
|
return handleGetSchema(body.tables || [], connectionString, schemas, tableOptions, onError);
|
|
331
|
+
case "get-all-schemas":
|
|
332
|
+
return handleGetAllSchemas(connectionString, schemas, tableOptions, onError);
|
|
331
333
|
case "query":
|
|
332
334
|
return handleQuery(
|
|
333
335
|
body.sql,
|
|
@@ -478,6 +480,89 @@ async function handleGetSchema(tables, connectionString, schemas, tableOptions,
|
|
|
478
480
|
await pool.end();
|
|
479
481
|
}
|
|
480
482
|
}
|
|
483
|
+
async function handleGetAllSchemas(connectionString, schemas, tableOptions, onError) {
|
|
484
|
+
let Pool;
|
|
485
|
+
try {
|
|
486
|
+
const pg = await import("pg");
|
|
487
|
+
Pool = pg.Pool;
|
|
488
|
+
} catch {
|
|
489
|
+
return createErrorResponse(
|
|
490
|
+
"PostgreSQL driver (pg) is not installed. Install it with: pnpm add pg",
|
|
491
|
+
500
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
if (!connectionString) {
|
|
495
|
+
return createErrorResponse(
|
|
496
|
+
"Database connection not configured. Set DATABASE_URL environment variable.",
|
|
497
|
+
500
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
const pool = new Pool({ connectionString });
|
|
501
|
+
try {
|
|
502
|
+
const schemaPlaceholders = schemas.map((_, i) => `$${i + 1}`).join(", ");
|
|
503
|
+
const tablesResult = await pool.query(
|
|
504
|
+
`
|
|
505
|
+
SELECT table_name
|
|
506
|
+
FROM information_schema.tables
|
|
507
|
+
WHERE table_schema IN (${schemaPlaceholders})
|
|
508
|
+
AND table_type = 'BASE TABLE'
|
|
509
|
+
ORDER BY table_name
|
|
510
|
+
`,
|
|
511
|
+
schemas
|
|
512
|
+
);
|
|
513
|
+
let tableNames = tablesResult.rows.map((row) => row.table_name);
|
|
514
|
+
tableNames = filterTables(tableNames, tableOptions);
|
|
515
|
+
if (tableNames.length === 0) {
|
|
516
|
+
const response2 = { schemas: [] };
|
|
517
|
+
return new Response(JSON.stringify(response2), {
|
|
518
|
+
status: 200,
|
|
519
|
+
headers: { "Content-Type": "application/json" }
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
const tablePlaceholders = tableNames.map((_, i) => `$${i + 1}`).join(", ");
|
|
523
|
+
const columnsResult = await pool.query(
|
|
524
|
+
`
|
|
525
|
+
SELECT
|
|
526
|
+
table_name,
|
|
527
|
+
column_name,
|
|
528
|
+
data_type,
|
|
529
|
+
is_nullable
|
|
530
|
+
FROM information_schema.columns
|
|
531
|
+
WHERE table_name IN (${tablePlaceholders})
|
|
532
|
+
AND table_schema = ANY($${tableNames.length + 1}::text[])
|
|
533
|
+
ORDER BY table_name, ordinal_position
|
|
534
|
+
`,
|
|
535
|
+
[...tableNames, schemas]
|
|
536
|
+
);
|
|
537
|
+
const tableMap = /* @__PURE__ */ new Map();
|
|
538
|
+
for (const row of columnsResult.rows) {
|
|
539
|
+
const tableName = row.table_name;
|
|
540
|
+
if (!tableMap.has(tableName)) {
|
|
541
|
+
tableMap.set(tableName, []);
|
|
542
|
+
}
|
|
543
|
+
tableMap.get(tableName).push({
|
|
544
|
+
name: row.column_name,
|
|
545
|
+
type: PG_TYPE_MAP[row.data_type.toLowerCase()] || "unknown",
|
|
546
|
+
nullable: row.is_nullable === "YES"
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
const tableSchemas = [];
|
|
550
|
+
for (const [tableName, columns] of tableMap) {
|
|
551
|
+
tableSchemas.push({ table: tableName, columns });
|
|
552
|
+
}
|
|
553
|
+
const response = { schemas: tableSchemas };
|
|
554
|
+
return new Response(JSON.stringify(response), {
|
|
555
|
+
status: 200,
|
|
556
|
+
headers: { "Content-Type": "application/json" }
|
|
557
|
+
});
|
|
558
|
+
} catch (error) {
|
|
559
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
560
|
+
onError?.(err);
|
|
561
|
+
return createErrorResponse(`Failed to get all schemas: ${err.message}`, 500);
|
|
562
|
+
} finally {
|
|
563
|
+
await pool.end();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
481
566
|
async function handleQuery(sql, table, connectionString, schemas, tableOptions, maxRows, timeout, onError) {
|
|
482
567
|
if (!sql || typeof sql !== "string") {
|
|
483
568
|
return createErrorResponse("Missing or invalid SQL query", 400);
|
package/dist/index.d.cts
CHANGED
|
@@ -99,7 +99,7 @@ interface TinyPivotHandlerOptions {
|
|
|
99
99
|
*/
|
|
100
100
|
interface TinyPivotRequest {
|
|
101
101
|
/** Action to perform */
|
|
102
|
-
action: 'list-tables' | 'get-schema' | 'query' | 'chat';
|
|
102
|
+
action: 'list-tables' | 'get-schema' | 'get-all-schemas' | 'query' | 'chat';
|
|
103
103
|
tables?: string[];
|
|
104
104
|
sql?: string;
|
|
105
105
|
table?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -99,7 +99,7 @@ interface TinyPivotHandlerOptions {
|
|
|
99
99
|
*/
|
|
100
100
|
interface TinyPivotRequest {
|
|
101
101
|
/** Action to perform */
|
|
102
|
-
action: 'list-tables' | 'get-schema' | 'query' | 'chat';
|
|
102
|
+
action: 'list-tables' | 'get-schema' | 'get-all-schemas' | 'query' | 'chat';
|
|
103
103
|
tables?: string[];
|
|
104
104
|
sql?: string;
|
|
105
105
|
table?: string;
|
package/dist/index.js
CHANGED
|
@@ -288,6 +288,8 @@ function createTinyPivotHandler(options = {}) {
|
|
|
288
288
|
return handleListTables(connectionString, schemas, tableOptions, descriptions, onError);
|
|
289
289
|
case "get-schema":
|
|
290
290
|
return handleGetSchema(body.tables || [], connectionString, schemas, tableOptions, onError);
|
|
291
|
+
case "get-all-schemas":
|
|
292
|
+
return handleGetAllSchemas(connectionString, schemas, tableOptions, onError);
|
|
291
293
|
case "query":
|
|
292
294
|
return handleQuery(
|
|
293
295
|
body.sql,
|
|
@@ -438,6 +440,89 @@ async function handleGetSchema(tables, connectionString, schemas, tableOptions,
|
|
|
438
440
|
await pool.end();
|
|
439
441
|
}
|
|
440
442
|
}
|
|
443
|
+
async function handleGetAllSchemas(connectionString, schemas, tableOptions, onError) {
|
|
444
|
+
let Pool;
|
|
445
|
+
try {
|
|
446
|
+
const pg = await import("pg");
|
|
447
|
+
Pool = pg.Pool;
|
|
448
|
+
} catch {
|
|
449
|
+
return createErrorResponse(
|
|
450
|
+
"PostgreSQL driver (pg) is not installed. Install it with: pnpm add pg",
|
|
451
|
+
500
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
if (!connectionString) {
|
|
455
|
+
return createErrorResponse(
|
|
456
|
+
"Database connection not configured. Set DATABASE_URL environment variable.",
|
|
457
|
+
500
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
const pool = new Pool({ connectionString });
|
|
461
|
+
try {
|
|
462
|
+
const schemaPlaceholders = schemas.map((_, i) => `$${i + 1}`).join(", ");
|
|
463
|
+
const tablesResult = await pool.query(
|
|
464
|
+
`
|
|
465
|
+
SELECT table_name
|
|
466
|
+
FROM information_schema.tables
|
|
467
|
+
WHERE table_schema IN (${schemaPlaceholders})
|
|
468
|
+
AND table_type = 'BASE TABLE'
|
|
469
|
+
ORDER BY table_name
|
|
470
|
+
`,
|
|
471
|
+
schemas
|
|
472
|
+
);
|
|
473
|
+
let tableNames = tablesResult.rows.map((row) => row.table_name);
|
|
474
|
+
tableNames = filterTables(tableNames, tableOptions);
|
|
475
|
+
if (tableNames.length === 0) {
|
|
476
|
+
const response2 = { schemas: [] };
|
|
477
|
+
return new Response(JSON.stringify(response2), {
|
|
478
|
+
status: 200,
|
|
479
|
+
headers: { "Content-Type": "application/json" }
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
const tablePlaceholders = tableNames.map((_, i) => `$${i + 1}`).join(", ");
|
|
483
|
+
const columnsResult = await pool.query(
|
|
484
|
+
`
|
|
485
|
+
SELECT
|
|
486
|
+
table_name,
|
|
487
|
+
column_name,
|
|
488
|
+
data_type,
|
|
489
|
+
is_nullable
|
|
490
|
+
FROM information_schema.columns
|
|
491
|
+
WHERE table_name IN (${tablePlaceholders})
|
|
492
|
+
AND table_schema = ANY($${tableNames.length + 1}::text[])
|
|
493
|
+
ORDER BY table_name, ordinal_position
|
|
494
|
+
`,
|
|
495
|
+
[...tableNames, schemas]
|
|
496
|
+
);
|
|
497
|
+
const tableMap = /* @__PURE__ */ new Map();
|
|
498
|
+
for (const row of columnsResult.rows) {
|
|
499
|
+
const tableName = row.table_name;
|
|
500
|
+
if (!tableMap.has(tableName)) {
|
|
501
|
+
tableMap.set(tableName, []);
|
|
502
|
+
}
|
|
503
|
+
tableMap.get(tableName).push({
|
|
504
|
+
name: row.column_name,
|
|
505
|
+
type: PG_TYPE_MAP[row.data_type.toLowerCase()] || "unknown",
|
|
506
|
+
nullable: row.is_nullable === "YES"
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
const tableSchemas = [];
|
|
510
|
+
for (const [tableName, columns] of tableMap) {
|
|
511
|
+
tableSchemas.push({ table: tableName, columns });
|
|
512
|
+
}
|
|
513
|
+
const response = { schemas: tableSchemas };
|
|
514
|
+
return new Response(JSON.stringify(response), {
|
|
515
|
+
status: 200,
|
|
516
|
+
headers: { "Content-Type": "application/json" }
|
|
517
|
+
});
|
|
518
|
+
} catch (error) {
|
|
519
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
520
|
+
onError?.(err);
|
|
521
|
+
return createErrorResponse(`Failed to get all schemas: ${err.message}`, 500);
|
|
522
|
+
} finally {
|
|
523
|
+
await pool.end();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
441
526
|
async function handleQuery(sql, table, connectionString, schemas, tableOptions, maxRows, timeout, onError) {
|
|
442
527
|
if (!sql || typeof sql !== "string") {
|
|
443
528
|
return createErrorResponse("Missing or invalid SQL query", 400);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smallwebco/tinypivot-server",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.62",
|
|
5
5
|
"description": "Server handlers for TinyPivot Embedded AI Data Analyst - PostgreSQL integration, AI chat proxy, and natural language to SQL",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://tiny-pivot.com",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@smallwebco/tinypivot-core": "1.0.
|
|
52
|
+
"@smallwebco/tinypivot-core": "1.0.62"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/pg": "^8.11.6",
|