@hypequery/clickhouse 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-CLI.md +123 -0
- package/README.md +276 -0
- package/dist/cli/bin.js +151 -0
- package/dist/cli/generate-types.d.ts +5 -0
- package/dist/cli/generate-types.js +91 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +2 -0
- package/dist/core/connection.d.ts.map +1 -0
- package/dist/core/connection.js +34 -0
- package/dist/core/cross-filter.d.ts.map +1 -0
- package/dist/core/cross-filter.js +218 -0
- package/dist/core/features/aggregations.d.ts.map +1 -0
- package/dist/core/features/aggregations.js +35 -0
- package/dist/core/features/analytics.d.ts.map +1 -0
- package/dist/core/features/analytics.js +35 -0
- package/dist/core/features/executor.d.ts.map +1 -0
- package/dist/core/features/executor.js +136 -0
- package/dist/core/features/filtering.d.ts.map +1 -0
- package/dist/core/features/filtering.js +30 -0
- package/dist/core/features/joins.d.ts.map +1 -0
- package/dist/core/features/joins.js +16 -0
- package/dist/core/features/pagination.d.ts.map +1 -0
- package/dist/core/features/pagination.js +190 -0
- package/dist/core/features/query-modifiers.d.ts.map +1 -0
- package/dist/core/features/query-modifiers.js +50 -0
- package/dist/core/formatters/sql-formatter.d.ts.map +1 -0
- package/dist/core/formatters/sql-formatter.js +69 -0
- package/dist/core/join-relationships.d.ts.map +1 -0
- package/dist/core/join-relationships.js +56 -0
- package/dist/core/query-builder.d.ts.map +1 -0
- package/dist/core/query-builder.js +372 -0
- package/dist/core/tests/index.d.ts.map +1 -0
- package/dist/core/tests/index.js +1 -0
- package/dist/core/tests/integration/setup.d.ts.map +1 -0
- package/dist/core/tests/integration/setup.js +274 -0
- package/dist/core/tests/test-utils.d.ts.map +1 -0
- package/dist/core/tests/test-utils.js +32 -0
- package/dist/core/utils/logger.d.ts.map +1 -0
- package/dist/core/utils/logger.js +98 -0
- package/dist/core/utils/sql-expressions.d.ts.map +1 -0
- package/dist/core/utils/sql-expressions.js +73 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +29 -0
- package/dist/core/validators/filter-validator.d.ts.map +1 -0
- package/dist/core/validators/filter-validator.js +19 -0
- package/dist/core/validators/value-validator.d.ts.map +1 -0
- package/dist/core/validators/value-validator.js +47 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/types/base.d.ts.map +1 -0
- package/dist/types/base.js +1 -0
- package/dist/types/clickhouse-types.d.ts.map +1 -0
- package/dist/types/clickhouse-types.js +1 -0
- package/dist/types/filters.d.ts.map +1 -0
- package/dist/types/filters.js +1 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/package.json +67 -0
package/README-CLI.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# HypeQuery TypeScript Generator
|
|
2
|
+
|
|
3
|
+
This tool automatically generates TypeScript type definitions from your ClickHouse database schema.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
The TypeScript generator is included with the `@hypequery/clickhouse` package:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @hypequery/clickhouse
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
Generate TypeScript types for your ClickHouse database:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx hypequery-generate-types
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This will:
|
|
22
|
+
1. Connect to your ClickHouse database using environment variables
|
|
23
|
+
2. Introspect all tables in your database
|
|
24
|
+
3. Generate TypeScript definitions in `./generated-schema.ts`
|
|
25
|
+
|
|
26
|
+
## Example Usage
|
|
27
|
+
|
|
28
|
+
**Generate types with a custom output path:**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx hypequery-generate-types ./src/types/db-schema.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Specify database connection directly:**
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
CLICKHOUSE_HOST=http://clickhouse.example.com:8123 \
|
|
38
|
+
CLICKHOUSE_USER=myuser \
|
|
39
|
+
CLICKHOUSE_PASSWORD=mypassword \
|
|
40
|
+
CLICKHOUSE_DATABASE=analytics \
|
|
41
|
+
npx hypequery-generate-types
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
Configure the connection using environment variables:
|
|
47
|
+
|
|
48
|
+
| Variable | Description | Default |
|
|
49
|
+
|----------|-------------|---------|
|
|
50
|
+
| `CLICKHOUSE_HOST` | ClickHouse server URL | `http://localhost:8123` |
|
|
51
|
+
| `CLICKHOUSE_USER` | ClickHouse username | `default` |
|
|
52
|
+
| `CLICKHOUSE_PASSWORD` | ClickHouse password | _(empty)_ |
|
|
53
|
+
| `CLICKHOUSE_DATABASE` | ClickHouse database name | `default` |
|
|
54
|
+
|
|
55
|
+
You can set these in:
|
|
56
|
+
- A `.env` file in your project root
|
|
57
|
+
- Your system environment
|
|
58
|
+
- Directly when running the command
|
|
59
|
+
|
|
60
|
+
## Using Generated Types
|
|
61
|
+
|
|
62
|
+
Import the generated types in your code:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { createQueryBuilder } from '@hypequery/clickhouse';
|
|
66
|
+
import { IntrospectedSchema } from './generated-schema';
|
|
67
|
+
|
|
68
|
+
// Create a type-safe query builder
|
|
69
|
+
const db = createQueryBuilder<IntrospectedSchema>({
|
|
70
|
+
host: process.env.CLICKHOUSE_HOST,
|
|
71
|
+
username: process.env.CLICKHOUSE_USER,
|
|
72
|
+
password: process.env.CLICKHOUSE_PASSWORD,
|
|
73
|
+
database: process.env.CLICKHOUSE_DATABASE,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Enjoy complete type safety!
|
|
77
|
+
const results = await db
|
|
78
|
+
.table('users') // TypeScript checks table exists
|
|
79
|
+
.select(['id', 'name']) // TypeScript checks columns exist
|
|
80
|
+
.where('id', 'gt', 10) // TypeScript validates types
|
|
81
|
+
.execute();
|
|
82
|
+
|
|
83
|
+
// Results are properly typed
|
|
84
|
+
results.forEach(user => {
|
|
85
|
+
console.log(user.name); // TypeScript knows this is a string
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Adding to Your Workflow
|
|
90
|
+
|
|
91
|
+
Add it to your npm scripts:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"scripts": {
|
|
96
|
+
"generate-types": "hypequery-generate-types ./src/types/db-schema.ts",
|
|
97
|
+
"prebuild": "npm run generate-types",
|
|
98
|
+
"build": "tsc"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Troubleshooting
|
|
104
|
+
|
|
105
|
+
If you encounter issues:
|
|
106
|
+
|
|
107
|
+
1. **Connection problems**
|
|
108
|
+
- Make sure ClickHouse is running and accessible
|
|
109
|
+
- Check your firewall settings
|
|
110
|
+
|
|
111
|
+
2. **Authentication failures**
|
|
112
|
+
- Verify your username and password
|
|
113
|
+
- Ensure the user has sufficient permissions
|
|
114
|
+
|
|
115
|
+
3. **Missing tables**
|
|
116
|
+
- Confirm you're connecting to the correct database
|
|
117
|
+
- Verify the tables exist in your ClickHouse instance
|
|
118
|
+
|
|
119
|
+
For more help, run:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx hypequery-generate-types --help
|
|
123
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# HypeQuery
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<img src="https://hypequery.dev/img/logo.svg" alt="HypeQuery Logo" width="200"/>
|
|
5
|
+
<h1>@hypequery/clickhouse</h1>
|
|
6
|
+
<p>A typescript-first library for building type-safe dashboards with ClickHouse</p>
|
|
7
|
+
|
|
8
|
+
[](https://github.com/lukejreilly/hypequery/blob/main/LICENSE)
|
|
9
|
+
[](https://badge.fury.io/js/@hypequery%2Fcore)
|
|
10
|
+
[](https://github.com/lukejreilly/hypequery/stargazers)
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
> **Note:** This package is published on npm as `@hypequery/core`. The unscoped package `hypequery-core` is unrelated and should not be used.
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
hypequery is a typescript-first query builder for ClickHouse designed specifically for building real-time, type-safe analytics dashboards. Unlike generic SQL query builders, HypeQuery understands your ClickHouse schema and provides full type checking throughout your codebase, making it ideal for data-intensive applications.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- 🎯 **Type-Safe**: Full TypeScript support with inferred types from your ClickHouse schema
|
|
22
|
+
- 🚀 **Performant**: Built for real-time analytics with optimized query generation
|
|
23
|
+
- 🔍 **Cross Filtering**: Powerful cross-filtering capabilities for interactive dashboards
|
|
24
|
+
- 📊 **Dashboard Ready**: Built-in support for pagination, sorting, and filtering
|
|
25
|
+
- 🛠️ **Developer Friendly**: Fluent API design for an intuitive development experience
|
|
26
|
+
- 📱 **Platform Agnostic**: Works in both Node.js and browser environments
|
|
27
|
+
- 🔄 **Schema Generation**: CLI tool to generate TypeScript types from your ClickHouse schema
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# npm
|
|
33
|
+
npm install @hypequery/core
|
|
34
|
+
|
|
35
|
+
# yarn
|
|
36
|
+
yarn add @hypequery/core
|
|
37
|
+
|
|
38
|
+
# pnpm
|
|
39
|
+
pnpm add @hypequery/core
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { createQueryBuilder } from '@hypequery/core';
|
|
46
|
+
import type { Schema } from './generated-schema';
|
|
47
|
+
|
|
48
|
+
// Initialize the query builder
|
|
49
|
+
const db = createQueryBuilder<Schema>({
|
|
50
|
+
host: 'your-clickhouse-host',
|
|
51
|
+
username: 'default',
|
|
52
|
+
password: '',
|
|
53
|
+
database: 'default'
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Build and execute a query
|
|
57
|
+
const results = await db
|
|
58
|
+
.table('trips')
|
|
59
|
+
.select(['pickup_datetime', 'dropoff_datetime', 'total_amount'])
|
|
60
|
+
.where('total_amount', '>', 50)
|
|
61
|
+
.orderBy('pickup_datetime', 'DESC')
|
|
62
|
+
.limit(10)
|
|
63
|
+
.execute();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Schema Generation
|
|
67
|
+
|
|
68
|
+
HypeQuery provides a CLI tool to generate TypeScript types from your ClickHouse schema:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Install globally (optional)
|
|
72
|
+
npm install -g @hypequery/core
|
|
73
|
+
|
|
74
|
+
# Generate schema types
|
|
75
|
+
npx hypequery-generate --host your-clickhouse-host --database your-database
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This creates a `generated-schema.ts` file that you can import in your application:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { createQueryBuilder } from '@hypequery/core';
|
|
82
|
+
import type { IntrospectedSchema } from './generated-schema';
|
|
83
|
+
|
|
84
|
+
const db = createQueryBuilder<IntrospectedSchema>({
|
|
85
|
+
// connection details
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Core Features
|
|
90
|
+
|
|
91
|
+
### Type-Safe Queries
|
|
92
|
+
|
|
93
|
+
HypeQuery provides full TypeScript support, ensuring your queries are type-safe:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// Column names are type-checked
|
|
97
|
+
const query = db.table('trips')
|
|
98
|
+
.select(['pickup_datetime', 'total_amount'])
|
|
99
|
+
.where('total_amount', '>', 50)
|
|
100
|
+
.execute();
|
|
101
|
+
|
|
102
|
+
// Type error if column doesn't exist
|
|
103
|
+
db.table('trips').select(['non_existent_column']); // TypeScript error
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Cross Filtering
|
|
107
|
+
|
|
108
|
+
Implement interactive dashboards with cross-filtering support:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { CrossFilter } from '@hypequery/core';
|
|
112
|
+
|
|
113
|
+
// Create a filter
|
|
114
|
+
const filter = new CrossFilter()
|
|
115
|
+
.add({
|
|
116
|
+
column: 'pickup_datetime',
|
|
117
|
+
operator: 'gte',
|
|
118
|
+
value: '2024-01-01'
|
|
119
|
+
})
|
|
120
|
+
.add({
|
|
121
|
+
column: 'total_amount',
|
|
122
|
+
operator: 'gt',
|
|
123
|
+
value: 20
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Apply to multiple queries
|
|
127
|
+
const query1 = db.table('trips')
|
|
128
|
+
.applyCrossFilters(filter)
|
|
129
|
+
.execute();
|
|
130
|
+
|
|
131
|
+
const query2 = db.table('drivers')
|
|
132
|
+
.applyCrossFilters(filter)
|
|
133
|
+
.execute();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Pagination
|
|
137
|
+
|
|
138
|
+
Built-in cursor-based pagination for efficient data loading:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// First page
|
|
142
|
+
const firstPage = await db.table('trips')
|
|
143
|
+
.select(['pickup_datetime', 'total_amount'])
|
|
144
|
+
.orderBy('pickup_datetime', 'DESC')
|
|
145
|
+
.paginate({
|
|
146
|
+
pageSize: 10
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Next page
|
|
150
|
+
const nextPage = await db.table('trips')
|
|
151
|
+
.select(['pickup_datetime', 'total_amount'])
|
|
152
|
+
.orderBy('pickup_datetime', 'DESC')
|
|
153
|
+
.paginate({
|
|
154
|
+
pageSize: 10,
|
|
155
|
+
after: firstPage.pageInfo.endCursor
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Previous page
|
|
159
|
+
const prevPage = await db.table('trips')
|
|
160
|
+
.select(['pickup_datetime', 'total_amount'])
|
|
161
|
+
.orderBy('pickup_datetime', 'DESC')
|
|
162
|
+
.paginate({
|
|
163
|
+
pageSize: 10,
|
|
164
|
+
before: nextPage.pageInfo.startCursor
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Advanced Queries
|
|
169
|
+
|
|
170
|
+
HypeQuery supports complex queries including joins, aggregations, and subqueries:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Aggregations
|
|
174
|
+
const stats = await db.table('trips')
|
|
175
|
+
.avg('total_amount')
|
|
176
|
+
.max('trip_distance')
|
|
177
|
+
.count('trip_id')
|
|
178
|
+
.where('pickup_datetime', '>=', '2024-01-01')
|
|
179
|
+
.execute();
|
|
180
|
+
|
|
181
|
+
// Joins
|
|
182
|
+
const tripsWithDrivers = await db.table('trips')
|
|
183
|
+
.select(['trips.trip_id', 'trips.total_amount', 'drivers.name'])
|
|
184
|
+
.join('drivers', 'trips.driver_id', '=', 'drivers.id')
|
|
185
|
+
.execute();
|
|
186
|
+
|
|
187
|
+
// Raw SQL when needed
|
|
188
|
+
const customQuery = await db.table('trips')
|
|
189
|
+
.select([
|
|
190
|
+
db.raw('toStartOfDay(pickup_datetime) as day'),
|
|
191
|
+
'count() as trip_count'
|
|
192
|
+
])
|
|
193
|
+
.groupBy(db.raw('toStartOfDay(pickup_datetime)'))
|
|
194
|
+
.execute();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Environment Support
|
|
198
|
+
|
|
199
|
+
### Browser Environment
|
|
200
|
+
|
|
201
|
+
For browser usage, you'll typically need to set up a proxy server to avoid CORS issues:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const db = createQueryBuilder<Schema>({
|
|
205
|
+
host: '/api/clickhouse', // Proxy through your API route
|
|
206
|
+
username: 'default',
|
|
207
|
+
password: '',
|
|
208
|
+
database: 'default'
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Node.js Environment
|
|
213
|
+
|
|
214
|
+
For server-side applications, you can connect directly to ClickHouse:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
const db = createQueryBuilder<Schema>({
|
|
218
|
+
host: 'http://your-clickhouse-server:8123',
|
|
219
|
+
username: 'default',
|
|
220
|
+
password: 'your-password',
|
|
221
|
+
database: 'default'
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Versioning and Release Channels
|
|
226
|
+
|
|
227
|
+
HypeQuery follows semantic versioning and provides multiple release channels:
|
|
228
|
+
|
|
229
|
+
- **Latest**: Stable releases (`npm install @hypequery/core`)
|
|
230
|
+
- **Beta**: Pre-release versions (`npm install @hypequery/core@beta`)
|
|
231
|
+
|
|
232
|
+
## Documentation
|
|
233
|
+
|
|
234
|
+
For detailed documentation and examples, visit our [documentation site](https://hypequery.dev/docs).
|
|
235
|
+
|
|
236
|
+
- [Getting Started](https://hypequery.dev/docs/installation)
|
|
237
|
+
- [Query Building](https://hypequery.dev/docs/guides/query-building)
|
|
238
|
+
- [Filtering](https://hypequery.dev/docs/guides/filtering)
|
|
239
|
+
- [Pagination](https://hypequery.dev/docs/features/pagination)
|
|
240
|
+
- [API Reference](https://hypequery.dev/docs/reference/api)
|
|
241
|
+
|
|
242
|
+
## Examples
|
|
243
|
+
|
|
244
|
+
Check out our example implementations:
|
|
245
|
+
|
|
246
|
+
- [Example Dashboard](https://github.com/lukejreilly/hypequery/tree/main/examples/example-dashboard): A complete Next.js dashboard with HypeQuery
|
|
247
|
+
- [React Query Integration](https://hypequery.dev/docs/guides/integrations/react-query): Using HypeQuery with React Query
|
|
248
|
+
- [Time Series Analysis](https://hypequery.dev/docs/guides/timeseries): Building time series analytics
|
|
249
|
+
|
|
250
|
+
## Troubleshooting
|
|
251
|
+
|
|
252
|
+
### Common Issues
|
|
253
|
+
|
|
254
|
+
- **Connection Errors**: Ensure your ClickHouse server is running and accessible
|
|
255
|
+
- **CORS Issues**: Use a proxy server for browser environments
|
|
256
|
+
- **Type Errors**: Make sure to regenerate your schema types after schema changes
|
|
257
|
+
|
|
258
|
+
## Contributing
|
|
259
|
+
|
|
260
|
+
We welcome contributions! Please see our [contributing guide](CONTRIBUTING.md) for details.
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
265
|
+
|
|
266
|
+
## Support
|
|
267
|
+
|
|
268
|
+
- 📚 [Documentation](https://hypequery.dev/docs)
|
|
269
|
+
- 🐛 [Issue Tracker](https://github.com/lukejreilly/hypequery/issues)
|
|
270
|
+
- 💬 [Discussions](https://github.com/lukejreilly/hypequery/discussions)
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
<div align="center">
|
|
275
|
+
<sub>Built with ❤️ by the HypeQuery team</sub>
|
|
276
|
+
</div>
|
package/dist/cli/bin.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { ClickHouseConnection } from '../core/connection.js';
|
|
4
|
+
import { generateTypes } from './generate-types.js';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
|
|
9
|
+
// Load environment variables from the current directory
|
|
10
|
+
dotenv.config();
|
|
11
|
+
|
|
12
|
+
// ANSI color codes for prettier output
|
|
13
|
+
const colors = {
|
|
14
|
+
reset: '\x1b[0m',
|
|
15
|
+
bright: '\x1b[1m',
|
|
16
|
+
dim: '\x1b[2m',
|
|
17
|
+
green: '\x1b[32m',
|
|
18
|
+
yellow: '\x1b[33m',
|
|
19
|
+
blue: '\x1b[34m',
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
cyan: '\x1b[36m'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Display a colorful banner with the tool name
|
|
26
|
+
*/
|
|
27
|
+
function showBanner() {
|
|
28
|
+
console.log(`
|
|
29
|
+
${colors.bright}${colors.cyan}HypeQuery TypeScript Generator${colors.reset}
|
|
30
|
+
${colors.dim}Generate TypeScript types from your ClickHouse database schema${colors.reset}
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Show help information for the CLI
|
|
36
|
+
*/
|
|
37
|
+
function showHelp() {
|
|
38
|
+
console.log(`
|
|
39
|
+
${colors.bright}Usage:${colors.reset}
|
|
40
|
+
npx hypequery-generate-types [output-path] [options]
|
|
41
|
+
|
|
42
|
+
${colors.bright}Arguments:${colors.reset}
|
|
43
|
+
output-path Path where TypeScript definitions will be saved (default: "./generated-schema.ts")
|
|
44
|
+
|
|
45
|
+
${colors.bright}Environment variables:${colors.reset}
|
|
46
|
+
CLICKHOUSE_HOST ClickHouse server URL (default: http://localhost:8123)
|
|
47
|
+
CLICKHOUSE_USER ClickHouse username (default: default)
|
|
48
|
+
CLICKHOUSE_PASSWORD ClickHouse password
|
|
49
|
+
CLICKHOUSE_DATABASE ClickHouse database name (default: default)
|
|
50
|
+
|
|
51
|
+
${colors.bright}Examples:${colors.reset}
|
|
52
|
+
npx hypequery-generate-types
|
|
53
|
+
npx hypequery-generate-types ./src/types/db-schema.ts
|
|
54
|
+
CLICKHOUSE_HOST=http://my-clickhouse:8123 npx hypequery-generate-types
|
|
55
|
+
|
|
56
|
+
${colors.bright}Options:${colors.reset}
|
|
57
|
+
--help, -h Show this help text
|
|
58
|
+
`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Main CLI function
|
|
63
|
+
*/
|
|
64
|
+
async function main() {
|
|
65
|
+
showBanner();
|
|
66
|
+
|
|
67
|
+
// Process command line arguments
|
|
68
|
+
const args = process.argv.slice(2);
|
|
69
|
+
|
|
70
|
+
// Check for help flag
|
|
71
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
72
|
+
showHelp();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get output path (default or from args)
|
|
77
|
+
const outputPath = args.length > 0 && !args[0].startsWith('-')
|
|
78
|
+
? args[0]
|
|
79
|
+
: './generated-schema.ts';
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Display connection info
|
|
83
|
+
const host = process.env.VITE_CLICKHOUSE_HOST || process.env.CLICKHOUSE_HOST || 'http://localhost:8123';
|
|
84
|
+
const database = process.env.VITE_CLICKHOUSE_DATABASE || process.env.CLICKHOUSE_DATABASE || 'default';
|
|
85
|
+
|
|
86
|
+
console.log(`${colors.dim}Connecting to ClickHouse at ${colors.reset}${colors.bright}${host}${colors.reset}`);
|
|
87
|
+
console.log(`${colors.dim}Database: ${colors.reset}${colors.bright}${database}${colors.reset}`);
|
|
88
|
+
|
|
89
|
+
// Initialize connection from env vars
|
|
90
|
+
ClickHouseConnection.initialize({
|
|
91
|
+
host,
|
|
92
|
+
username: process.env.VITE_CLICKHOUSE_USER || process.env.CLICKHOUSE_USER || 'default',
|
|
93
|
+
password: process.env.VITE_CLICKHOUSE_PASSWORD || process.env.CLICKHOUSE_PASSWORD,
|
|
94
|
+
database,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
console.log(`${colors.dim}Generating TypeScript definitions...${colors.reset}`);
|
|
98
|
+
|
|
99
|
+
// Ensure directory exists
|
|
100
|
+
const dir = path.dirname(path.resolve(outputPath));
|
|
101
|
+
await fs.mkdir(dir, { recursive: true });
|
|
102
|
+
|
|
103
|
+
// Generate types
|
|
104
|
+
await generateTypes(outputPath);
|
|
105
|
+
|
|
106
|
+
console.log(`${colors.green}✓ Success! ${colors.reset}Types generated at ${colors.bright}${path.resolve(outputPath)}${colors.reset}`);
|
|
107
|
+
console.log(`
|
|
108
|
+
${colors.dim}To use these types in your project:${colors.reset}
|
|
109
|
+
|
|
110
|
+
import { createQueryBuilder } from '@hypequery/clickhouse';
|
|
111
|
+
import { IntrospectedSchema } from '${outputPath.replace(/\.ts$/, '')}';
|
|
112
|
+
|
|
113
|
+
const db = createQueryBuilder<IntrospectedSchema>({
|
|
114
|
+
host: process.env.CLICKHOUSE_HOST,
|
|
115
|
+
username: process.env.CLICKHOUSE_USER,
|
|
116
|
+
password: process.env.CLICKHOUSE_PASSWORD,
|
|
117
|
+
database: process.env.CLICKHOUSE_DATABASE,
|
|
118
|
+
});
|
|
119
|
+
`);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`${colors.red}✗ Error generating types: ${colors.reset}${error.message}`);
|
|
122
|
+
|
|
123
|
+
// Provide more helpful error messages for common issues
|
|
124
|
+
if (error.message && error.message.includes('ECONNREFUSED')) {
|
|
125
|
+
console.error(`
|
|
126
|
+
${colors.yellow}Connection refused.${colors.reset} Please check:
|
|
127
|
+
- Is ClickHouse running at ${process.env.CLICKHOUSE_HOST || 'http://localhost:8123'}?
|
|
128
|
+
- Do you need to provide authentication credentials?
|
|
129
|
+
- Are there any network/firewall restrictions?
|
|
130
|
+
`);
|
|
131
|
+
} else if (error.message && error.message.includes('Authentication failed')) {
|
|
132
|
+
console.error(`
|
|
133
|
+
${colors.yellow}Authentication failed.${colors.reset} Please check:
|
|
134
|
+
- Are your CLICKHOUSE_USER and CLICKHOUSE_PASSWORD environment variables set correctly?
|
|
135
|
+
- Does the user have sufficient permissions?
|
|
136
|
+
`);
|
|
137
|
+
} else if (error.message && error.message.includes('database does not exist')) {
|
|
138
|
+
console.error(`
|
|
139
|
+
${colors.yellow}Database not found.${colors.reset} Please check:
|
|
140
|
+
- Is the CLICKHOUSE_DATABASE environment variable set correctly?
|
|
141
|
+
- Does the database exist in your ClickHouse instance?
|
|
142
|
+
`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.error(`${colors.dim}For more information, use --help flag.${colors.reset}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Execute the main function
|
|
151
|
+
main();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ClickHouseConnection } from '../core/connection.js';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
|
|
6
|
+
// Load environment variables from the current directory
|
|
7
|
+
dotenv.config();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} ColumnInfo
|
|
11
|
+
* @property {string} name - The name of the column
|
|
12
|
+
* @property {string} type - The ClickHouse type of the column
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Converts ClickHouse types to TypeScript types
|
|
17
|
+
* @param {string} type - The ClickHouse type to convert
|
|
18
|
+
* @returns {string} - The corresponding TypeScript type
|
|
19
|
+
*/
|
|
20
|
+
const clickhouseToTsType = (type) => {
|
|
21
|
+
if (type.startsWith('Array(')) {
|
|
22
|
+
const innerType = type.slice(6, -1);
|
|
23
|
+
return `Array(${clickhouseToTsType(innerType)})`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
switch (type.toLowerCase()) {
|
|
27
|
+
case 'string':
|
|
28
|
+
case 'fixedstring':
|
|
29
|
+
return 'String';
|
|
30
|
+
case 'int8':
|
|
31
|
+
case 'int16':
|
|
32
|
+
case 'int32':
|
|
33
|
+
return 'Int32';
|
|
34
|
+
case 'int64':
|
|
35
|
+
return 'Int64';
|
|
36
|
+
case 'float32':
|
|
37
|
+
case 'float64':
|
|
38
|
+
return 'Float64';
|
|
39
|
+
case 'datetime':
|
|
40
|
+
return 'DateTime';
|
|
41
|
+
case 'date':
|
|
42
|
+
return 'Date';
|
|
43
|
+
default:
|
|
44
|
+
return 'String';
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generates TypeScript type definitions from the ClickHouse database schema
|
|
50
|
+
* @param {string} outputPath - The file path where the type definitions will be written
|
|
51
|
+
* @returns {Promise<void>}
|
|
52
|
+
*/
|
|
53
|
+
export async function generateTypes(outputPath) {
|
|
54
|
+
const client = ClickHouseConnection.getClient();
|
|
55
|
+
|
|
56
|
+
// Get all tables
|
|
57
|
+
const tablesQuery = await client.query({
|
|
58
|
+
query: 'SHOW TABLES',
|
|
59
|
+
format: 'JSONEachRow'
|
|
60
|
+
});
|
|
61
|
+
const tables = await tablesQuery.json();
|
|
62
|
+
|
|
63
|
+
let typeDefinitions = `// Generated by @hypequery/clickhouse
|
|
64
|
+
import { ColumnType } from '@hypequery/clickhouse';
|
|
65
|
+
|
|
66
|
+
export interface IntrospectedSchema {`;
|
|
67
|
+
|
|
68
|
+
// Get columns for each table
|
|
69
|
+
for (const table of tables) {
|
|
70
|
+
const columnsQuery = await client.query({
|
|
71
|
+
query: `DESCRIBE ${table.name}`,
|
|
72
|
+
format: 'JSONEachRow'
|
|
73
|
+
});
|
|
74
|
+
const columns = await columnsQuery.json();
|
|
75
|
+
|
|
76
|
+
typeDefinitions += `\n ${table.name}: {`;
|
|
77
|
+
for (const column of columns) {
|
|
78
|
+
typeDefinitions += `\n ${column.name}: '${clickhouseToTsType(column.type)}';`;
|
|
79
|
+
}
|
|
80
|
+
typeDefinitions += '\n };';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
typeDefinitions += '\n}\n';
|
|
84
|
+
|
|
85
|
+
// Ensure the output directory exists
|
|
86
|
+
const outputDir = path.dirname(path.resolve(outputPath));
|
|
87
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
88
|
+
|
|
89
|
+
// Write the file
|
|
90
|
+
await fs.writeFile(path.resolve(outputPath), typeDefinitions);
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/core/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAWjE,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE;QACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;IAEF,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,mBAAmB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAkC;IAEzD,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI;IAqB5D,MAAM,CAAC,SAAS,IAAI,UAAU,CAAC,OAAO,YAAY,CAAC;CAMpD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createClient } from '@clickhouse/client-web';
|
|
2
|
+
export class ClickHouseConnection {
|
|
3
|
+
static initialize(config) {
|
|
4
|
+
// Create a client config object with only the standard options
|
|
5
|
+
const clientConfig = {
|
|
6
|
+
host: config.host,
|
|
7
|
+
username: config.username,
|
|
8
|
+
password: config.password,
|
|
9
|
+
database: config.database,
|
|
10
|
+
};
|
|
11
|
+
// Add the extended options if provided
|
|
12
|
+
if (config.http_headers)
|
|
13
|
+
clientConfig.http_headers = config.http_headers;
|
|
14
|
+
if (config.request_timeout)
|
|
15
|
+
clientConfig.request_timeout = config.request_timeout;
|
|
16
|
+
if (config.compression)
|
|
17
|
+
clientConfig.compression = config.compression;
|
|
18
|
+
if (config.application)
|
|
19
|
+
clientConfig.application = config.application;
|
|
20
|
+
if (config.keep_alive)
|
|
21
|
+
clientConfig.keep_alive = config.keep_alive;
|
|
22
|
+
if (config.log)
|
|
23
|
+
clientConfig.log = config.log;
|
|
24
|
+
if (config.clickhouse_settings)
|
|
25
|
+
clientConfig.clickhouse_settings = config.clickhouse_settings;
|
|
26
|
+
this.instance = createClient(clientConfig);
|
|
27
|
+
}
|
|
28
|
+
static getClient() {
|
|
29
|
+
if (!this.instance) {
|
|
30
|
+
throw new Error('ClickHouse connection not initialized');
|
|
31
|
+
}
|
|
32
|
+
return this.instance;
|
|
33
|
+
}
|
|
34
|
+
}
|