crushdataai 1.2.17 → 1.2.18

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 (29) hide show
  1. package/README.md +4 -4
  2. package/assets/.agent/workflows/data-analyst.md +30 -2
  3. package/assets/.claude/skills/data-analyst/SKILL.md +37 -0
  4. package/assets/.cursor/commands/data-analyst.md +30 -2
  5. package/assets/.github/prompts/data-analyst.prompt.md +30 -2
  6. package/assets/.kiro/steering/data-analyst.md +30 -2
  7. package/assets/.windsurf/workflows/data-analyst.md +30 -2
  8. package/dist/connectors/clickhouse/index.d.ts +12 -0
  9. package/dist/connectors/clickhouse/index.js +140 -0
  10. package/dist/connectors/databricks/index.d.ts +12 -0
  11. package/dist/connectors/databricks/index.js +105 -0
  12. package/dist/connectors/mongodb/index.d.ts +12 -0
  13. package/dist/connectors/mongodb/index.js +156 -0
  14. package/dist/connectors/mssql/index.d.ts +12 -0
  15. package/dist/connectors/mssql/index.js +181 -0
  16. package/dist/connectors/redshift/index.d.ts +12 -0
  17. package/dist/connectors/redshift/index.js +178 -0
  18. package/dist/routes/dashboard.js +84 -2
  19. package/dist/services/query-executor.js +67 -16
  20. package/dist/types/dashboard.d.ts +2 -1
  21. package/package.json +9 -3
  22. package/ui-dashboard-dist/assets/index-Ch04z44X.css +1 -0
  23. package/ui-dashboard-dist/assets/{index-SkyAs8Zl.js → index-DHr1UjyZ.js} +148 -148
  24. package/ui-dashboard-dist/index.html +2 -2
  25. package/assets/images/crushdataai-dashboard-simple-charts.png +0 -0
  26. package/assets/images/crushdataai-dashboard.png +0 -0
  27. package/assets/images/crushdataai-data-connection-ui.png +0 -0
  28. package/assets/images/crushdataai-landing-page.png +0 -0
  29. package/ui-dashboard-dist/assets/index-uepFwkLY.css +0 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  An AI skill that provides structured, professional data analysis workflows with built-in validation - helping AI coding assistants perform data analysis like a careful human analyst.
6
6
 
7
- ![CrushData AI Landing Page](assets/images/crushdataai-landing-page.png)
7
+ ![CrushData AI Landing Page](https://github.com/SankaiAI/crushdataai-agent/raw/main/assets/crushdataai-landing-page.png)
8
8
 
9
9
  ## 🎯 What It Does
10
10
 
@@ -83,7 +83,7 @@ crushdataai connect
83
83
  - **Supported Types**: CSV, MySQL, PostgreSQL, Shopify, BigQuery, Snowflake
84
84
  - **Private & Secure**: Credentials are stored **locally** on your machine (`~/.crushdataai/connections.json`). They are **never** uploaded to any server or included in the npm package.
85
85
 
86
- ![CrushData AI Connection Manager](assets/images/crushdataai-data-connection-ui.png)
86
+ ![CrushData AI Connection Manager](https://github.com/SankaiAI/crushdataai-agent/raw/main/assets/crushdataai-data-connection-ui.png)
87
87
 
88
88
  > [!NOTE]
89
89
  > **Persistence**: Once you add a connection, you can **close the UI** (Ctrl+C). The AI IDE reads the saved connection details directly from your local config file, so the server does NOT need to keep running.
@@ -108,10 +108,10 @@ crushdataai dashboard
108
108
  npx crushdataai dashboard
109
109
  ```
110
110
 
111
- ![Advanced Dashboard](assets/images/crushdataai-dashboard.png)
111
+ ![Advanced Dashboard](https://github.com/SankaiAI/crushdataai-agent/raw/main/assets/crushdataai-dashboard.png)
112
112
  *Advanced charts visualization (Funnel, Gauge, Radar, etc.)*
113
113
 
114
- ![Simple Dashboard](assets/images/crushdataai-dashboard-simple-charts.png)
114
+ ![Simple Dashboard](https://github.com/SankaiAI/crushdataai-agent/raw/main/assets/crushdataai-dashboard-simple-charts.png)
115
115
  *Standard charts visualization (Line, Bar, Pie, etc.)*
116
116
 
117
117
  ### 2. Features
@@ -130,10 +130,38 @@ dashboard = {
130
130
  "charts": [{"id": "chart-1", "type": "line", "title": "Trend", "data": {"labels": ["Jan","Feb"], "datasets": [{"label": "Revenue", "values": [10000,20000]}]}}]
131
131
  }
132
132
 
133
- with open("reports/dashboards/dashboard.json", "w") as f:
133
+ with open("reports/dashboards/dashboard.json", "w") as f:
134
134
  json.dump(dashboard, f, indent=2)
135
135
  ```
136
- Tell user: "Run `npx crushdataai dashboard` to view."
136
+
137
+ ### 5b. Making Charts Refreshable (Recommended)
138
+
139
+ To allow the user to refresh data directly from the dashboard:
140
+ 1. Include a `query` object in the chart definition.
141
+ 2. Run `npx crushdataai connections` to list available connection names (secure - no passwords shown).
142
+ 3. Set `connection` to one of the listed names.
143
+ 4. Set `sql` to the query used to generate the data.
144
+
145
+ > **SECURITY**: Never read `.env` directly to find connection names. Always use `npx crushdataai connections`.
146
+
147
+ ```json
148
+ "query": {
149
+ "connection": "my_postgres_db",
150
+ "sql": "SELECT date, revenue FROM sales WHERE..."
151
+ }
152
+ ```
153
+
154
+ **Database Specifics:**
155
+ - **SQL/Databases**: Provide the full SQL query.
156
+ - **Shopify**: Provide the resource name (e.g. `orders`).
157
+ - **CSV**: Provide the connection name. `sql` is ignored but required (set to "default").
158
+ - **MongoDB**: Provide the collection name in the `sql` field.
159
+
160
+ **Script-Based Refresh (for Python-aggregated charts):**
161
+ ```json
162
+ "query": { "script": "analysis/my_dashboard_script.py" }
163
+ ```
164
+ Use `script` for Shopify/API charts that need aggregation. CLI re-runs the script on Refresh.
137
165
 
138
166
  ---
139
167
 
@@ -213,6 +213,43 @@ FROM table;
213
213
  json.dump(dashboard, f, indent=2)
214
214
  ```
215
215
 
216
+ ### 5b. Making Charts Refreshable (Recommended)
217
+
218
+ To allow the user to refresh data directly from the dashboard:
219
+ 1. Include a `query` object in the chart definition.
220
+ 2. Run `npx crushdataai connections` to list available connection names (this is secure - no passwords shown).
221
+ 3. Set `connection` to one of the listed names.
222
+ 4. Set `sql` to the query used to generate the data.
223
+
224
+ > **SECURITY**: Never read `.env` directly to find connection names. Always use `npx crushdataai connections`.
225
+
226
+ ```json
227
+ "query": {
228
+ "connection": "my_postgres_db",
229
+ "sql": "SELECT date, revenue FROM sales WHERE..."
230
+ }
231
+ ```
232
+
233
+ **Database Specifics:**
234
+ - **SQL/Databases**: Provide the full SQL query.
235
+ - **Shopify**: Provide the resource name (e.g. `orders`).
236
+ - **CSV**: Provide the connection name. `sql` is ignored but required (set to "default").
237
+ - **MongoDB**: Provide the collection name in the `sql` field.
238
+
239
+ **Script-Based Refresh (for Python-aggregated charts):**
240
+
241
+ If your chart requires Python aggregation (e.g., grouping, custom calculations), use `script` instead of `connection`:
242
+
243
+ ```json
244
+ "query": {
245
+ "script": "analysis/my_dashboard_script.py"
246
+ }
247
+ ```
248
+
249
+ When the user clicks Refresh, the CLI will **re-run your Python script**. The script should update the dashboard JSON file with fresh aggregated data.
250
+
251
+ > **TIP**: Use `script` for Shopify/API charts that need aggregation. Use `connection` + `sql` only for SQL databases where the query returns pre-formatted chart data.
252
+
216
253
  3. **Tell user:**
217
254
  > "Dashboard ready! Run `npx crushdataai dashboard` to view."
218
255
 
@@ -75,10 +75,38 @@ dashboard = {
75
75
  "charts": [{"id": "chart-1", "type": "line", "title": "Trend", "data": {"labels": ["Jan","Feb"], "datasets": [{"label": "Revenue", "values": [10000,20000]}]}}]
76
76
  }
77
77
 
78
- with open("reports/dashboards/dashboard.json", "w") as f:
78
+ with open("reports/dashboards/dashboard.json", "w") as f:
79
79
  json.dump(dashboard, f, indent=2)
80
80
  ```
81
- Tell user: "Run `npx crushdataai dashboard` to view."
81
+
82
+ ### 5b. Making Charts Refreshable (Recommended)
83
+
84
+ To allow the user to refresh data directly from the dashboard:
85
+ 1. Include a `query` object in the chart definition.
86
+ 2. Run `npx crushdataai connections` to list available connection names (secure - no passwords shown).
87
+ 3. Set `connection` to one of the listed names.
88
+ 4. Set `sql` to the query used to generate the data.
89
+
90
+ > **SECURITY**: Never read `.env` directly to find connection names. Always use `npx crushdataai connections`.
91
+
92
+ ```json
93
+ "query": {
94
+ "connection": "my_postgres_db",
95
+ "sql": "SELECT date, revenue FROM sales WHERE..."
96
+ }
97
+ ```
98
+
99
+ **Database Specifics:**
100
+ - **SQL/Databases**: Provide the full SQL query.
101
+ - **Shopify**: Provide the resource name (e.g. `orders`).
102
+ - **CSV**: Provide the connection name. `sql` is ignored but required (set to "default").
103
+ - **MongoDB**: Provide the collection name in the `sql` field.
104
+
105
+ **Script-Based Refresh (for Python-aggregated charts):**
106
+ ```json
107
+ "query": { "script": "analysis/my_dashboard_script.py" }
108
+ ```
109
+ Use `script` for Shopify/API charts that need aggregation. CLI re-runs the script on Refresh.
82
110
 
83
111
  ---
84
112
 
@@ -72,7 +72,35 @@ dashboard = {
72
72
  "kpis": [{"id": "kpi-1", "label": "Total", "value": "$50K", "trend": "+12%"}],
73
73
  "charts": [{"id": "chart-1", "type": "line", "title": "Trend", "data": {"labels": ["Jan","Feb"], "datasets": [{"label": "Revenue", "values": [10000,20000]}]}}]
74
74
  }
75
- with open("reports/dashboards/dashboard.json", "w") as f:
75
+ with open("reports/dashboards/dashboard.json", "w") as f:
76
76
  json.dump(dashboard, f, indent=2)
77
77
  ```
78
- Tell user: "Run `npx crushdataai dashboard` to view."
78
+
79
+ ### 5b. Making Charts Refreshable (Recommended)
80
+
81
+ To allow the user to refresh data directly from the dashboard:
82
+ 1. Include a `query` object in the chart definition.
83
+ 2. Run `npx crushdataai connections` to list available connection names (secure - no passwords shown).
84
+ 3. Set `connection` to one of the listed names.
85
+ 4. Set `sql` to the query used to generate the data.
86
+
87
+ > **SECURITY**: Never read `.env` directly to find connection names. Always use `npx crushdataai connections`.
88
+
89
+ ```json
90
+ "query": {
91
+ "connection": "my_postgres_db",
92
+ "sql": "SELECT date, revenue FROM sales WHERE..."
93
+ }
94
+ ```
95
+
96
+ **Database Specifics:**
97
+ - **SQL/Databases**: Provide the full SQL query.
98
+ - **Shopify**: Provide the resource name (e.g. `orders`).
99
+ - **CSV**: Provide the connection name. `sql` is ignored but required (set to "default").
100
+ - **MongoDB**: Provide the collection name in the `sql` field.
101
+
102
+ **Script-Based Refresh (for Python-aggregated charts):**
103
+ ```json
104
+ "query": { "script": "analysis/my_dashboard_script.py" }
105
+ ```
106
+ Use `script` for Shopify/API charts that need aggregation. CLI re-runs the script on Refresh.
@@ -74,7 +74,35 @@ dashboard = {
74
74
  "kpis": [{"id": "kpi-1", "label": "Total", "value": "$50K", "trend": "+12%"}],
75
75
  "charts": [{"id": "chart-1", "type": "line", "title": "Trend", "data": {"labels": ["Jan","Feb"], "datasets": [{"label": "Revenue", "values": [10000,20000]}]}}]
76
76
  }
77
- with open("reports/dashboards/dashboard.json", "w") as f:
77
+ with open("reports/dashboards/dashboard.json", "w") as f:
78
78
  json.dump(dashboard, f, indent=2)
79
79
  ```
80
- Tell user: "Run `npx crushdataai dashboard` to view."
80
+
81
+ ### 5b. Making Charts Refreshable (Recommended)
82
+
83
+ To allow the user to refresh data directly from the dashboard:
84
+ 1. Include a `query` object in the chart definition.
85
+ 2. Run `npx crushdataai connections` to list available connection names (secure - no passwords shown).
86
+ 3. Set `connection` to one of the listed names.
87
+ 4. Set `sql` to the query used to generate the data.
88
+
89
+ > **SECURITY**: Never read `.env` directly to find connection names. Always use `npx crushdataai connections`.
90
+
91
+ ```json
92
+ "query": {
93
+ "connection": "my_postgres_db",
94
+ "sql": "SELECT date, revenue FROM sales WHERE..."
95
+ }
96
+ ```
97
+
98
+ **Database Specifics:**
99
+ - **SQL/Databases**: Provide the full SQL query.
100
+ - **Shopify**: Provide the resource name (e.g. `orders`).
101
+ - **CSV**: Provide the connection name. `sql` is ignored but required (set to "default").
102
+ - **MongoDB**: Provide the collection name in the `sql` field.
103
+
104
+ **Script-Based Refresh (for Python-aggregated charts):**
105
+ ```json
106
+ "query": { "script": "analysis/my_dashboard_script.py" }
107
+ ```
108
+ Use `script` for Shopify/API charts that need aggregation. CLI re-runs the script on Refresh.
@@ -72,7 +72,35 @@ dashboard = {
72
72
  "kpis": [{"id": "kpi-1", "label": "Total", "value": "$50K", "trend": "+12%"}],
73
73
  "charts": [{"id": "chart-1", "type": "line", "title": "Trend", "data": {"labels": ["Jan","Feb"], "datasets": [{"label": "Revenue", "values": [10000,20000]}]}}]
74
74
  }
75
- with open("reports/dashboards/dashboard.json", "w") as f:
75
+ with open("reports/dashboards/dashboard.json", "w") as f:
76
76
  json.dump(dashboard, f, indent=2)
77
77
  ```
78
- Tell user: "Run `npx crushdataai dashboard` to view."
78
+
79
+ ### 5b. Making Charts Refreshable (Recommended)
80
+
81
+ To allow the user to refresh data directly from the dashboard:
82
+ 1. Include a `query` object in the chart definition.
83
+ 2. Run `npx crushdataai connections` to list available connection names (secure - no passwords shown).
84
+ 3. Set `connection` to one of the listed names.
85
+ 4. Set `sql` to the query used to generate the data.
86
+
87
+ > **SECURITY**: Never read `.env` directly to find connection names. Always use `npx crushdataai connections`.
88
+
89
+ ```json
90
+ "query": {
91
+ "connection": "my_postgres_db",
92
+ "sql": "SELECT date, revenue FROM sales WHERE..."
93
+ }
94
+ ```
95
+
96
+ **Database Specifics:**
97
+ - **SQL/Databases**: Provide the full SQL query.
98
+ - **Shopify**: Provide the resource name (e.g. `orders`).
99
+ - **CSV**: Provide the connection name. `sql` is ignored but required (set to "default").
100
+ - **MongoDB**: Provide the collection name in the `sql` field.
101
+
102
+ **Script-Based Refresh (for Python-aggregated charts):**
103
+ ```json
104
+ "query": { "script": "analysis/my_dashboard_script.py" }
105
+ ```
106
+ Use `script` for Shopify/API charts that need aggregation. CLI re-runs the script on Refresh.
@@ -0,0 +1,12 @@
1
+ import { Connector, Table, TableData } from '../index';
2
+ import { Connection } from '../../connections';
3
+ export declare class ClickHouseConnector implements Connector {
4
+ type: string;
5
+ private createClient;
6
+ test(connection: Connection): Promise<boolean>;
7
+ getTables(connection: Connection): Promise<Table[]>;
8
+ getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
9
+ getSchema(connection: Connection, tableName: string): Promise<import('../index').ColumnInfo[]>;
10
+ getSnippet(connection: Connection, lang: string): string;
11
+ executeQuery(connection: Connection, query: string): Promise<any[]>;
12
+ }
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClickHouseConnector = void 0;
4
+ const client_1 = require("@clickhouse/client");
5
+ class ClickHouseConnector {
6
+ constructor() {
7
+ this.type = 'clickhouse';
8
+ }
9
+ createClient(connection) {
10
+ let url = connection.host || 'http://localhost:8123';
11
+ if (!url.startsWith('http')) {
12
+ url = `http://${url}`;
13
+ }
14
+ return (0, client_1.createClient)({
15
+ url,
16
+ username: connection.user || 'default',
17
+ password: connection.password || '',
18
+ database: connection.database || 'default',
19
+ request_timeout: 10000,
20
+ });
21
+ }
22
+ async test(connection) {
23
+ console.log(`[ClickHouse] Testing connection for ${connection.name}`);
24
+ try {
25
+ const client = this.createClient(connection);
26
+ await client.query({ query: 'SELECT 1' });
27
+ console.log(`[ClickHouse] Connection test successful`);
28
+ await client.close();
29
+ return true;
30
+ }
31
+ catch (error) {
32
+ console.error(`[ClickHouse] Connection test failed:`, error.message);
33
+ throw new Error(`ClickHouse connection failed: ${error.message}`);
34
+ }
35
+ }
36
+ async getTables(connection) {
37
+ console.log(`[ClickHouse] getTables called for ${connection.name}`);
38
+ const client = this.createClient(connection);
39
+ try {
40
+ const resultSet = await client.query({
41
+ query: `
42
+ SELECT name, 'table' as type
43
+ FROM system.tables
44
+ WHERE database = '${connection.database || 'default'}'
45
+ `,
46
+ format: 'JSONEachRow'
47
+ });
48
+ const rows = await resultSet.json();
49
+ return rows.map((row) => ({
50
+ name: row.name,
51
+ type: 'table',
52
+ rowCount: null
53
+ }));
54
+ }
55
+ catch (error) {
56
+ throw new Error(`Failed to fetch tables: ${error.message}`);
57
+ }
58
+ finally {
59
+ await client.close();
60
+ }
61
+ }
62
+ async getData(connection, tableName, page, limit) {
63
+ console.log(`[ClickHouse] getData called for ${connection.name}`);
64
+ const client = this.createClient(connection);
65
+ try {
66
+ const offset = (page - 1) * limit;
67
+ // Get total count
68
+ const countResult = await client.query({
69
+ query: `SELECT count() as total FROM "${tableName}"`,
70
+ format: 'JSONEachRow'
71
+ });
72
+ const countRows = await countResult.json();
73
+ const totalRows = parseInt(countRows[0]?.total || '0', 10);
74
+ // Get data
75
+ const resultSet = await client.query({
76
+ query: `SELECT * FROM "${tableName}" LIMIT ${limit} OFFSET ${offset}`,
77
+ format: 'JSONEachRow'
78
+ });
79
+ const rows = await resultSet.json();
80
+ const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
81
+ const totalPages = Math.ceil(totalRows / limit) || 1;
82
+ return {
83
+ columns,
84
+ rows,
85
+ pagination: {
86
+ page, limit, totalRows, totalPages,
87
+ startIdx: offset + 1, endIdx: offset + rows.length
88
+ }
89
+ };
90
+ }
91
+ catch (error) {
92
+ throw new Error(`Failed to fetch data: ${error.message}`);
93
+ }
94
+ finally {
95
+ await client.close();
96
+ }
97
+ }
98
+ async getSchema(connection, tableName) {
99
+ const client = this.createClient(connection);
100
+ try {
101
+ const resultSet = await client.query({
102
+ query: `DESCRIBE "${tableName}"`,
103
+ format: 'JSONEachRow'
104
+ });
105
+ const rows = await resultSet.json();
106
+ return rows.map((row) => ({
107
+ name: row.name,
108
+ type: row.type,
109
+ nullable: false // ClickHouse nullable is explicit type wrapper usually
110
+ }));
111
+ }
112
+ catch (error) {
113
+ throw new Error(`Failed to fetch schema: ${error.message}`);
114
+ }
115
+ finally {
116
+ await client.close();
117
+ }
118
+ }
119
+ getSnippet(connection, lang) {
120
+ // ... ClickHouse python snippet ...
121
+ return '';
122
+ }
123
+ async executeQuery(connection, query) {
124
+ const client = this.createClient(connection);
125
+ try {
126
+ const resultSet = await client.query({
127
+ query: query,
128
+ format: 'JSONEachRow'
129
+ });
130
+ return await resultSet.json();
131
+ }
132
+ catch (error) {
133
+ throw new Error(`Failed to execute query: ${error.message}`);
134
+ }
135
+ finally {
136
+ await client.close();
137
+ }
138
+ }
139
+ }
140
+ exports.ClickHouseConnector = ClickHouseConnector;
@@ -0,0 +1,12 @@
1
+ import { Connector, Table, TableData } from '../index';
2
+ import { Connection } from '../../connections';
3
+ export declare class DatabricksConnector implements Connector {
4
+ type: string;
5
+ private createSession;
6
+ test(connection: Connection): Promise<boolean>;
7
+ getTables(connection: Connection): Promise<Table[]>;
8
+ getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
9
+ getSchema(connection: Connection, tableName: string): Promise<import('../index').ColumnInfo[]>;
10
+ getSnippet(connection: Connection, lang: string): string;
11
+ executeQuery(connection: Connection, query: string): Promise<any[]>;
12
+ }
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DatabricksConnector = void 0;
4
+ const sql_1 = require("@databricks/sql");
5
+ class DatabricksConnector {
6
+ constructor() {
7
+ this.type = 'databricks';
8
+ }
9
+ async createSession(connection) {
10
+ const client = new sql_1.DBSQLClient();
11
+ const token = connection.apiKey || connection.password;
12
+ const host = connection.host;
13
+ const path = connection.connectionString; // mapped from HTTP Path
14
+ if (!token || !host || !path) {
15
+ throw new Error('Host, Token (API Key), and HTTP Path (Connection String) are required for Databricks');
16
+ }
17
+ await client.connect({ token, host, path });
18
+ const session = await client.openSession();
19
+ return { client, session };
20
+ }
21
+ async test(connection) {
22
+ console.log(`[Databricks] Testing connection for ${connection.name}`);
23
+ try {
24
+ const { client, session } = await this.createSession(connection);
25
+ const op = await session.executeStatement('SELECT 1');
26
+ await op.close();
27
+ await session.close();
28
+ await client.close();
29
+ return true;
30
+ }
31
+ catch (error) {
32
+ throw new Error(`Databricks connection failed: ${error.message}`);
33
+ }
34
+ }
35
+ async getTables(connection) {
36
+ console.log(`[Databricks] getTables called for ${connection.name}`);
37
+ try {
38
+ const { client, session } = await this.createSession(connection);
39
+ const op = await session.executeStatement('SHOW TABLES');
40
+ const rows = await op.fetchAll();
41
+ await op.close();
42
+ await session.close();
43
+ await client.close();
44
+ return rows.map((row) => ({
45
+ name: row.tableName || row.name,
46
+ type: 'table',
47
+ rowCount: null
48
+ }));
49
+ }
50
+ catch (error) {
51
+ throw new Error(`Failed to fetch tables: ${error.message}`);
52
+ }
53
+ }
54
+ async getData(connection, tableName, page, limit) {
55
+ console.log(`[Databricks] getData called for ${connection.name}`);
56
+ try {
57
+ const { client, session } = await this.createSession(connection);
58
+ // Count
59
+ const countOp = await session.executeStatement(`SELECT COUNT(*) as total FROM ${tableName}`);
60
+ const countRows = await countOp.fetchAll();
61
+ await countOp.close();
62
+ const totalRows = parseInt(countRows[0]?.total || '0', 10);
63
+ // Data
64
+ const dataOp = await session.executeStatement(`SELECT * FROM ${tableName} LIMIT ${limit}`);
65
+ const rows = await dataOp.fetchAll();
66
+ await dataOp.close();
67
+ await session.close();
68
+ await client.close();
69
+ const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
70
+ const totalPages = Math.ceil(totalRows / limit) || 1;
71
+ return {
72
+ columns,
73
+ rows,
74
+ pagination: {
75
+ page, limit, totalRows, totalPages,
76
+ startIdx: 1, endIdx: rows.length
77
+ }
78
+ };
79
+ }
80
+ catch (error) {
81
+ throw new Error(`Failed to fetch data: ${error.message}`);
82
+ }
83
+ }
84
+ async getSchema(connection, tableName) {
85
+ return [];
86
+ }
87
+ getSnippet(connection, lang) {
88
+ return '';
89
+ }
90
+ async executeQuery(connection, query) {
91
+ try {
92
+ const { client, session } = await this.createSession(connection);
93
+ const op = await session.executeStatement(query);
94
+ const rows = await op.fetchAll();
95
+ await op.close();
96
+ await session.close();
97
+ await client.close();
98
+ return rows;
99
+ }
100
+ catch (error) {
101
+ throw new Error(`Failed to execute query: ${error.message}`);
102
+ }
103
+ }
104
+ }
105
+ exports.DatabricksConnector = DatabricksConnector;
@@ -0,0 +1,12 @@
1
+ import { Connector, Table, TableData } from '../index';
2
+ import { Connection } from '../../connections';
3
+ export declare class MongoDBConnector implements Connector {
4
+ type: string;
5
+ private getUrl;
6
+ test(connection: Connection): Promise<boolean>;
7
+ getTables(connection: Connection): Promise<Table[]>;
8
+ getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
9
+ getSchema(connection: Connection, tableName: string): Promise<import('../index').ColumnInfo[]>;
10
+ getSnippet(connection: Connection, lang: string): string;
11
+ executeQuery(connection: Connection, query: string): Promise<any[]>;
12
+ }