@housekit/orm 0.1.25 → 0.1.27

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.md CHANGED
@@ -14,10 +14,10 @@ HouseKit ORM is a modern database toolkit designed specifically for ClickHouse.
14
14
  ## 🚀 Key Features
15
15
 
16
16
  - **🛡️ First-Class TypeScript**: Full type inference for every query. Schema definition acts as the single source of truth.
17
- - **🏎️ Automatic Turbo Mode**: Native `RowBinary` serialization is used automatically when possible. Bypasses the overhead of JSON parsing for **5-10x faster inserts**.
17
+ - **🏎️ Binary Inserts by Default**: Native `RowBinary` serialization is used automatically. **5-10x faster** than JSON.
18
18
  - **🏗️ ClickHouse Native Engines**: Fluent DSL for `MergeTree`, `ReplacingMergeTree`, `SummingMergeTree`, `Distributed`, `Buffer`, and more.
19
19
  - **🔍 Advanced Analytics**: Specialized support for `ASOF JOIN`, `ARRAY JOIN`, `PREWHERE`, and complex Window Functions.
20
- - **🤝 Smart Relational API**: Query relations using `groupArray` internally, preventing row duplication and keeping data transfer lean.
20
+ - **🤝 Smart Relational API**: Query relations using `groupArray` internally, preventing row duplication.
21
21
  - **📦 Background Batching**: Built-in buffering to collect small inserts into high-performance batches automatically.
22
22
 
23
23
  ---
@@ -25,9 +25,6 @@ HouseKit ORM is a modern database toolkit designed specifically for ClickHouse.
25
25
  ## 📦 Installation
26
26
 
27
27
  ```bash
28
- # HouseKit requires the official ClickHouse client as a peer dependency
29
- npm install @housekit/orm @clickhouse/client
30
- # or
31
28
  bun add @housekit/orm @clickhouse/client
32
29
  ```
33
30
 
@@ -35,68 +32,199 @@ bun add @housekit/orm @clickhouse/client
35
32
 
36
33
  ## ⚡️ Quick Start
37
34
 
38
- ### 1. Define your Table & Export Types
39
- Use the fluent `defineTable` API. You can export the inferred types directly from the schema definition thanks to **Phantom Types**.
35
+ ### 1. Define your Table
40
36
 
41
37
  ```typescript
42
38
  // schema.ts
43
- import { defineTable, t, Engine } from '@housekit/orm';
44
-
45
- export const webEvents = defineTable('web_events', {
46
- id: t.uuid('id').primaryKey(),
47
- eventType: t.string('event_type'),
48
- url: t.string('url'),
49
- revenue: t.decimal('revenue', 18, 4).default(0),
50
- tags: t.array(t.string('tag')),
51
- metadata: t.json('metadata'),
52
- at: t.datetime('at').default('now()'),
39
+ import { defineTable, t, Engine, relations } from '@housekit/orm';
40
+
41
+ export const users = defineTable('users', {
42
+ id: t.uuid('id').autoGenerate({ version: 7 }).primaryKey().default('generateUUIDv7()'),
43
+ email: t.string('email'),
44
+ role: t.enum('role', ['admin', 'user']),
45
+ ...t.timestamps(),
53
46
  }, {
54
47
  engine: Engine.MergeTree(),
55
- orderBy: 'at',
56
- partitionBy: 'toYYYYMM(at)',
57
- ttl: 'at + INTERVAL 1 MONTH'
48
+ orderBy: 'id'
58
49
  });
59
50
 
60
- // Export inferred types directly
61
- export type WebEvent = typeof webEvents.$inferSelect;
62
- export type NewWebEvent = typeof webEvents.$inferInsert;
51
+ export const posts = defineTable('posts', {
52
+ id: t.uuid('id').autoGenerate({ version: 7 }).primaryKey().default('generateUUIDv7()'),
53
+ userId: t.uuid('user_id'),
54
+ title: t.string('title'),
55
+ createdAt: t.timestamp('created_at').default('now()'),
56
+ }, {
57
+ engine: Engine.MergeTree(),
58
+ orderBy: 'createdAt'
59
+ });
60
+
61
+ relations(users, ({ many }) => ({
62
+ posts: many(posts, { fields: [users.id], references: [posts.userId] })
63
+ }));
64
+
65
+ export type User = typeof users.$type;
66
+ export type NewUser = typeof users.$insert;
63
67
  ```
64
68
 
65
69
  ### 2. Connect and Query
66
- HouseKit automatically picks up configuration from your environment or `housekit.config.ts`.
67
70
 
68
71
  ```typescript
69
- import { createClient, sql } from '@housekit/orm';
70
- import { webEvents } from './schema';
71
-
72
- const db = await createClient();
73
-
74
- // Fully typed result inference.
75
- // No need to call .then() or .execute(), just await the builder!
76
- const results = await db.select({
77
- id: webEvents.id,
78
- path: webEvents.url,
79
- total: sql<number>`sum(${webEvents.revenue})`
80
- })
81
- .from(webEvents)
82
- .where({
83
- // Object syntax implicitly uses AND operator
84
- eventType: 'sale',
85
- url: '/checkout'
86
- })
87
- .groupBy(webEvents.id, webEvents.url)
88
- .limit(10);
72
+ import { housekit } from '@housekit/orm';
73
+ import * as schema from './schema';
74
+
75
+ const db = housekit({ url: 'http://localhost:8123' }, { schema });
76
+
77
+ // Binary insert (default, fastest)
78
+ await db.insert(schema.users).values({ email: 'a@b.com', role: 'admin' });
79
+
80
+ // JSON insert with returning data
81
+ const [user] = await db
82
+ .insert(schema.users)
83
+ .values({ email: 'a@b.com', role: 'admin' })
84
+ .returning();
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 🔍 Relational Query API
90
+
91
+ ### findMany / findFirst
92
+
93
+ ```typescript
94
+ const users = await db.query.users.findMany({
95
+ where: { role: 'admin', active: true },
96
+ columns: { id: true, email: true },
97
+ orderBy: (cols, { desc }) => desc(cols.createdAt),
98
+ limit: 10,
99
+ with: {
100
+ posts: { limit: 5 }
101
+ }
102
+ });
103
+ ```
104
+
105
+ ### findById
106
+
107
+ ```typescript
108
+ // Simple lookup
109
+ const user = await db.query.users.findById('uuid-here');
110
+
111
+ // With relations
112
+ const user = await db.query.users.findById('uuid-here', {
113
+ with: { posts: true }
114
+ });
115
+ ```
116
+
117
+ ### where syntax
118
+
119
+ ```typescript
120
+ // Object syntax (simplest)
121
+ where: { email: 'a@b.com' }
122
+ where: { role: 'admin', active: true } // AND implícito
123
+
124
+ // Direct expression
125
+ where: eq(users.role, 'admin')
126
+
127
+ // Callback for complex filters
128
+ where: (cols, { and, gt, inArray }) => and(
129
+ gt(cols.age, 18),
130
+ inArray(cols.role, ['admin', 'moderator'])
131
+ )
132
+ ```
133
+
134
+ Available operators: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `inArray`, `notInArray`, `between`, `notBetween`, `has`, `hasAll`, `hasAny`, `and`, `or`, `not`, `isNull`, `isNotNull`
135
+
136
+ ### columns selection
137
+
138
+ Select specific columns:
139
+
140
+ ```typescript
141
+ const users = await db.query.users.findMany({
142
+ columns: { id: true, email: true }
143
+ });
144
+ // Returns: [{ id: '...', email: '...' }]
145
+ ```
146
+
147
+ ### orderBy
148
+
149
+ ```typescript
150
+ // Callback (recommended)
151
+ orderBy: (cols, { desc }) => desc(cols.createdAt)
152
+
153
+ // Multiple columns
154
+ orderBy: (cols, { desc, asc }) => [desc(cols.createdAt), asc(cols.name)]
155
+
156
+ // Direct value
157
+ orderBy: desc(users.createdAt)
158
+
159
+ // Array
160
+ orderBy: [desc(users.createdAt), asc(users.name)]
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 🚀 High-Performance Inserts
166
+
167
+ ### Binary Insert (Default)
168
+
169
+ Binary format is used by default for maximum performance:
170
+
171
+ ```typescript
172
+ // Binary insert (no data returned)
173
+ await db.insert(events).values([
174
+ { type: 'click', userId: '...' },
175
+ { type: 'view', userId: '...' },
176
+ ]);
177
+ ```
178
+
179
+ ### JSON Insert with Returning
180
+
181
+ Use `.returningOne()` for single inserts or `.returning()` for multiple:
182
+
183
+ ```typescript
184
+ // Single insert
185
+ const user = await db
186
+ .insert(users)
187
+ .values({ email: 'a@b.com', role: 'admin' })
188
+ .returningOne();
189
+
190
+ console.log(user.id); // Generated UUID
191
+
192
+ // Multiple inserts
193
+ const [user1, user2] = await db
194
+ .insert(users)
195
+ .values([{ email: 'a@b.com' }, { email: 'b@c.com' }])
196
+ .returning();
197
+ ```
198
+
199
+ ### Force JSON Format
200
+
201
+ ```typescript
202
+ await db.insert(events).values(data).useJsonFormat();
203
+ ```
204
+
205
+ ### Background Batching
206
+
207
+ Collect small writes into efficient batches:
208
+
209
+ ```typescript
210
+ const builder = db.insert(events).batch({
211
+ maxRows: 10000,
212
+ flushIntervalMs: 5000
213
+ });
214
+
215
+ // Fire-and-forget
216
+ await builder.append(event1);
217
+ await builder.append(event2);
89
218
  ```
90
219
 
91
220
  ---
92
221
 
93
- ## 🧠 Advanced Schema Definitions
222
+ ## 🧠 Advanced Schema
94
223
 
95
224
  ### Complex Engines
96
- HouseKit supports specialized ClickHouse engines with strict type checking for their parameters.
97
225
 
98
226
  ```typescript
99
- // SummingMergeTree: Automatically aggregates numeric columns
227
+ // SummingMergeTree
100
228
  export const dailyRevenue = defineTable('daily_revenue', {
101
229
  day: t.date('day'),
102
230
  revenue: t.float64('revenue'),
@@ -105,23 +233,19 @@ export const dailyRevenue = defineTable('daily_revenue', {
105
233
  orderBy: 'day'
106
234
  });
107
235
 
108
- // ReplacingMergeTree: Deduplicates data by version
236
+ // ReplacingMergeTree
109
237
  export const users = defineTable('users', {
110
238
  id: t.uint64('id'),
111
239
  email: t.string('email'),
112
240
  version: t.uint64('version'),
113
241
  }, {
114
242
  engine: Engine.ReplacingMergeTree('version'),
115
-
116
- // Portability: '{cluster}' references the server-side macro.
117
- onCluster: '{cluster}',
118
-
243
+ onCluster: '{cluster}',
119
244
  orderBy: 'id'
120
245
  });
121
246
  ```
122
247
 
123
248
  ### Dictionaries
124
- Map external data or internal tables to fast in-memory dictionaries for ultra-low latency lookups.
125
249
 
126
250
  ```typescript
127
251
  import { defineDictionary } from '@housekit/orm';
@@ -138,130 +262,128 @@ export const userCache = defineDictionary('user_dict', {
138
262
 
139
263
  ---
140
264
 
141
- ## 🚀 High-Performance Data Ingestion
265
+ ## 🔍 Specialized Joins
142
266
 
143
- ### Automatic Turbo Mode (RowBinary)
144
- When you call `db.insert()`, HouseKit analyzes your schema. If types are compatible, it automatically switches to **Turbo Mode**, using native binary serialization instead of JSON.
267
+ ### ASOF JOIN
145
268
 
146
269
  ```typescript
147
- // Clean syntax: No .execute() needed. Just await.
148
- await db.insert(webEvents).values([
149
- { id: '...', eventType: 'click', revenue: 0, metadata: { browser: 'chrome' } },
150
- { id: '...', eventType: 'purchase', revenue: 99.90, metadata: { browser: 'safari' } },
151
- ]);
152
- // Logic: Object -> Buffer (Binary) -> ClickHouse Stream (Zero-copy)
270
+ const matched = await db.select()
271
+ .from(trades)
272
+ .asofJoin(quotes, sql`${trades.symbol} = ${quotes.symbol} AND ${trades.at} >= ${quotes.at}`)
273
+ .limit(100);
153
274
  ```
154
275
 
155
- ### Background Batching
156
- Collect small, frequent writes into large batches to prevent the "too many parts" error in ClickHouse.
276
+ ### GLOBAL JOIN
157
277
 
158
278
  ```typescript
159
- const builder = db.insert(webEvents)
160
- .batch({
161
- maxRows: 10000,
162
- flushIntervalMs: 5000
163
- });
164
-
165
- // Add rows to the background queue.
166
- // Processing and flushing happen automatically.
167
- // Returns immediately (Fire-and-forget).
168
- await builder.append(row1);
169
- await builder.append(row2);
279
+ await db.select()
280
+ .from(distributedTable)
281
+ .globalJoin(rightTable, condition);
170
282
  ```
171
283
 
172
284
  ---
173
285
 
174
- ## 🛠️ Zero-Config Type Safety
175
-
176
- Because we use **Phantom Types**, you don't need to import generic helpers like `Infer<T>` in your application code. You can use `typeof table.$infer...` or the types you exported from your schema file.
286
+ ## 🛠 SQL Utilities
177
287
 
178
- ### In Repository Functions
288
+ ### Dynamic Queries
179
289
 
180
290
  ```typescript
181
- import { webEvents, type NewWebEvent } from './schema';
291
+ const conditions = [
292
+ eq(users.active, true),
293
+ gte(users.age, 18)
294
+ ];
182
295
 
183
- async function logEvents(events: NewWebEvent[]) {
184
- // Types match automatically
185
- return await db.insert(webEvents).values(events);
186
- }
296
+ const query = await db.select()
297
+ .from(users)
298
+ .where(sql.join(conditions, sql` AND `));
187
299
  ```
188
300
 
189
301
  ---
190
302
 
191
- ## 🤝 Smart Relational API
303
+ ## Performance Optimizations
304
+
305
+ HouseKit includes several optimizations for maximum throughput in production environments.
192
306
 
193
- Traditional ORMs produce "Flat Joins" that duplicate data (the Cartesian Product problem). HouseKit's Relational API uses ClickHouse's `groupArray` internally to fetch related data as nested arrays in a single, efficient query.
307
+ ### Connection Pooling
308
+
309
+ Reuse HTTP connections across requests:
194
310
 
195
311
  ```typescript
196
- // Define relations in your schema first
197
- import { relations } from '@housekit/orm';
312
+ const db = housekit({
313
+ url: 'http://localhost:8123',
314
+ pool: {
315
+ maxSockets: 200, // Max concurrent connections
316
+ keepAlive: true, // Reuse connections
317
+ timeout: 30000 // Socket timeout (ms)
318
+ }
319
+ }, { schema });
320
+ ```
198
321
 
199
- relations(users, ({ many }) => ({
200
- posts: many(posts, { fields: [users.id], references: [posts.userId] })
201
- }));
322
+ ### Skip Validation
202
323
 
203
- // Query with nested data
204
- const usersWithData = await db.query.users.findMany({
205
- where: { country: 'US' }, // Object syntax
206
- with: {
207
- posts: {
208
- limit: 5,
209
- orderBy: { col: posts.createdAt, dir: 'DESC' }
210
- }
211
- },
212
- limit: 10
213
- });
324
+ Bypass enum validation in production when you trust your data source:
214
325
 
215
- // Result structure:
216
- // [{ id: 1, name: 'Alice', posts: [{ title: '...', ... }] }]
326
+ ```typescript
327
+ // Global (all inserts)
328
+ const db = housekit({
329
+ url: 'http://localhost:8123',
330
+ skipValidation: true
331
+ }, { schema });
332
+
333
+ // Per-insert
334
+ await db.insert(events).values(data).skipValidation();
217
335
  ```
218
336
 
219
- ---
220
-
221
- ## 🛠 SQL Utilities
337
+ ### Object Pooling
222
338
 
223
- ### Dynamic Queries with `sql.join`
224
- Easily build complex queries by joining SQL fragments with separators.
339
+ HouseKit automatically pools `BinaryWriter` instances to reduce GC pressure. For advanced use cases:
225
340
 
226
341
  ```typescript
227
- const conditions = [
228
- eq(users.active, true),
229
- gte(users.age, 18)
230
- ];
342
+ import { acquireWriter, releaseWriter } from '@housekit/orm';
231
343
 
232
- const query = await db.select()
233
- .from(users)
234
- .where(sql.join(conditions, sql` AND `));
344
+ const writer = acquireWriter();
345
+ try {
346
+ // Use writer for custom serialization
347
+ } finally {
348
+ releaseWriter(writer);
349
+ }
235
350
  ```
236
351
 
237
- ---
352
+ ### Optimized Serialization
238
353
 
239
- ## 🔍 Specialized ClickHouse Joins
240
-
241
- ### ASOF JOIN
242
- The industry standard for time-series matches (e.g., matching a trade with the closest price quote).
354
+ For maximum performance with large batches:
243
355
 
244
356
  ```typescript
245
- const matched = await db.select()
246
- .from(trades)
247
- .asofJoin(quotes, sql`${trades.symbol} = ${quotes.symbol} AND ${trades.at} >= ${quotes.at}`)
248
- .limit(100);
357
+ import {
358
+ buildOptimizedBinaryConfig,
359
+ serializeRowsOptimized
360
+ } from '@housekit/orm';
361
+
362
+ // Pre-compile accessors once
363
+ const config = buildOptimizedBinaryConfig(columns, {
364
+ skipValidation: true
365
+ });
366
+
367
+ // Serialize batches with pooled writer + pre-compiled accessors
368
+ const buffer = serializeRowsOptimized(rows, config);
249
369
  ```
250
370
 
251
- ### GLOBAL JOIN
252
- Essential for distributed setups to avoid local-data-only results on sharded clusters.
371
+ ### TypedArray for Numeric Data
372
+
373
+ For schemas with mostly numeric columns:
253
374
 
254
375
  ```typescript
255
- await db.select()
256
- .from(distributedTable)
257
- .globalJoin(rightTable, condition);
376
+ import { isNumericHeavySchema, serializeNumericBatch } from '@housekit/orm';
377
+
378
+ if (isNumericHeavySchema(columns)) {
379
+ // Uses Float64Array for better memory layout
380
+ const { numericBuffer, otherData } = serializeNumericBatch(rows, config, numericIndices);
381
+ }
258
382
  ```
259
383
 
260
384
  ---
261
385
 
262
- ## 🛠 Observability & Logging
263
-
264
- Inject a custom logger to monitor query performance, throughput, and error rates.
386
+ ## 🛠 Observability
265
387
 
266
388
  ```typescript
267
389
  const db = await createClient({
@@ -278,4 +400,4 @@ const db = await createClient({
278
400
 
279
401
  ## License
280
402
 
281
- MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
403
+ MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
@@ -3,40 +3,55 @@ import { type TableRuntime, type CleanInsert, type CleanSelect } from '../core';
3
3
  import { type BatchTransformOptions } from '../utils/batch-transform';
4
4
  import { Readable } from 'stream';
5
5
  import { type BatchConfig } from '../utils/background-batcher';
6
- /**
7
- * Insert format strategy:
8
- * - 'auto': Automatically choose best format (default - uses binary when possible)
9
- * - 'binary': Force RowBinary format (fastest)
10
- * - 'json': Force JSON format (for debugging/compatibility)
11
- * - 'compact': Force JSONCompactEachRow (smaller than JSON, faster than JSON)
12
- */
13
- type InsertFormat = 'auto' | 'binary' | 'json' | 'compact';
14
- export interface InsertOptions {
15
- /**
16
- * Format strategy for serialization.
17
- * Default: 'auto' (uses RowBinary when possible, falls back to JSON)
18
- */
19
- format?: InsertFormat;
20
- /** Rows per batch when streaming */
21
- batchSize?: number;
6
+ export interface BinaryInsertConfig {
7
+ url: string;
8
+ username: string;
9
+ password: string;
10
+ database: string;
11
+ /** Skip validation for maximum performance */
12
+ skipValidation?: boolean;
22
13
  }
23
- export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, any>, TReturn = void> {
14
+ export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, any>, TReturn = any> {
24
15
  private client;
25
16
  private table;
17
+ private connectionConfig?;
26
18
  private _values;
27
19
  private _async;
28
20
  private _waitForAsync;
29
21
  private _batchOptions;
30
22
  private _format;
31
- private _batchSize;
32
23
  private _batchConfig;
33
24
  private _forceJson;
34
25
  private _returning;
35
- constructor(client: ClickHouseClient, table: TTable);
36
- values(value: CleanInsert<TTable> | Array<CleanInsert<TTable>> | Iterable<CleanInsert<TTable>> | AsyncIterable<CleanInsert<TTable>> | Readable): this;
26
+ private _isSingle;
27
+ private _skipValidation;
28
+ constructor(client: ClickHouseClient, table: TTable, connectionConfig?: BinaryInsertConfig | undefined);
29
+ /**
30
+ * Skip enum validation for maximum performance.
31
+ * Use in production when you trust your data source.
32
+ */
33
+ skipValidation(): this;
34
+ values(value: CleanInsert<TTable> | Array<CleanInsert<TTable>> | Iterable<CleanInsert<TTable>> | AsyncIterable<CleanInsert<TTable>> | Readable): ClickHouseInsertBuilder<TTable, TReturn>;
37
35
  /** @template [T = CleanInsert<TTable>] */
38
- insert(data: CleanInsert<TTable> | CleanInsert<TTable>[]): Promise<this>;
36
+ insert(data: CleanInsert<TTable> | CleanInsert<TTable>[]): Promise<TReturn>;
37
+ /**
38
+ * Return inserted data as an array.
39
+ * Use when inserting multiple rows.
40
+ */
39
41
  returning(): ClickHouseInsertBuilder<TTable, CleanSelect<TTable>[]>;
42
+ /**
43
+ * Return the single inserted row directly (not wrapped in array).
44
+ * Use when inserting a single value for cleaner syntax.
45
+ *
46
+ * @example
47
+ * const user = await db.insert(users).values({ email: 'a@b.com' }).returningOne();
48
+ */
49
+ returningOne(): ClickHouseInsertBuilder<TTable, CleanSelect<TTable>>;
50
+ /**
51
+ * Disable the default returning() behavior.
52
+ * Useful when you don't need the inserted data back and want to avoid the overhead.
53
+ */
54
+ noReturning(): ClickHouseInsertBuilder<TTable, void>;
40
55
  /**
41
56
  * Force synchronous insert (disables async_insert).
42
57
  * Use when you need immediate durability guarantee.
@@ -131,6 +146,7 @@ export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, an
131
146
  private executeJsonInsert;
132
147
  /**
133
148
  * Execute insert using RowBinary format (fastest)
149
+ * Uses direct HTTP request since the official client doesn't support RowBinary yet
134
150
  */
135
151
  private executeBinaryInsert;
136
152
  /**
@@ -142,4 +158,3 @@ export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, an
142
158
  private resolveClientDefaultExpr;
143
159
  then<TResult1 = TReturn, TResult2 = never>(onfulfilled?: ((value: TReturn) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
144
160
  }
145
- export {};
@@ -221,7 +221,9 @@ export declare class ClickHouseQueryBuilder<TTable extends TableDefinition<any>
221
221
  }>;
222
222
  readonly $inferSelect: { [K_2 in keyof (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_2 ? T_2 extends TSelection ? T_2 extends SelectionShape ? SelectResult<T_2> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_3 ? T_3 extends TSelection ? T_3 extends SelectionShape ? SelectResult<T_3> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)]: (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_3 ? T_3 extends TSelection ? T_3 extends SelectionShape ? SelectResult<T_3> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_4 ? T_4 extends TSelection ? T_4 extends SelectionShape ? SelectResult<T_4> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_2] extends infer T_4 ? T_4 extends (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_5 ? T_5 extends TSelection ? T_5 extends SelectionShape ? SelectResult<T_5> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_6 ? T_6 extends TSelection ? T_6 extends SelectionShape ? SelectResult<T_6> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_2] ? T_4 extends ClickHouseColumn<infer Type, infer IsNotNull extends boolean, any> ? IsNotNull extends true ? Type : Type | null : never : never : never; } extends infer T_1 ? { [K_1 in keyof T_1]: T_1[K_1]; } : never;
223
223
  readonly $inferInsert: import("..").TableInsert<TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_4 ? T_4 extends TSelection ? T_4 extends SelectionShape ? SelectResult<T_4> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_5 ? T_5 extends TSelection ? T_5 extends SelectionShape ? SelectResult<T_5> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>> extends infer T_3 ? { [K_3 in keyof T_3]: T_3[K_3]; } : never;
224
- } & (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_5 ? T_5 extends TSelection ? T_5 extends SelectionShape ? SelectResult<T_5> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_6 ? T_6 extends TSelection ? T_6 extends SelectionShape ? SelectResult<T_6> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>);
224
+ readonly $type: { [K_5 in keyof (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_6 ? T_6 extends TSelection ? T_6 extends SelectionShape ? SelectResult<T_6> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_7 ? T_7 extends TSelection ? T_7 extends SelectionShape ? SelectResult<T_7> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)]: (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_7 ? T_7 extends TSelection ? T_7 extends SelectionShape ? SelectResult<T_7> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_8 ? T_8 extends TSelection ? T_8 extends SelectionShape ? SelectResult<T_8> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_5] extends infer T_8 ? T_8 extends (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_9 ? T_9 extends TSelection ? T_9 extends SelectionShape ? SelectResult<T_9> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_10 ? T_10 extends TSelection ? T_10 extends SelectionShape ? SelectResult<T_10> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_5] ? T_8 extends ClickHouseColumn<infer Type, infer IsNotNull extends boolean, any> ? IsNotNull extends true ? Type : Type | null : never : never : never; } extends infer T_5 ? { [K_4 in keyof T_5]: T_5[K_4]; } : never;
225
+ readonly $insert: import("..").TableInsert<TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_8 ? T_8 extends TSelection ? T_8 extends SelectionShape ? SelectResult<T_8> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_9 ? T_9 extends TSelection ? T_9 extends SelectionShape ? SelectResult<T_9> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>> extends infer T_7 ? { [K_3 in keyof T_7]: T_7[K_3]; } : never;
226
+ } & (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_9 ? T_9 extends TSelection ? T_9 extends SelectionShape ? SelectResult<T_9> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_10 ? T_10 extends TSelection ? T_10 extends SelectionShape ? SelectResult<T_10> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>);
225
227
  register: (mainQuery: ClickHouseQueryBuilder<any, any, any>) => ClickHouseQueryBuilder<any, any, any>;
226
228
  };
227
229
  final(): this;
package/dist/client.d.ts CHANGED
@@ -6,9 +6,23 @@ import { ClickHouseUpdateBuilder } from './builders/update';
6
6
  import { type TableDefinition, type TableRuntime, type CleanInsert } from './core';
7
7
  import { type HousekitLogger } from './logger';
8
8
  import { type RelationalAPI } from './relational';
9
+ interface ConnectionPoolConfig {
10
+ /** Maximum concurrent sockets (default: 100) */
11
+ maxSockets?: number;
12
+ /** Keep connections alive (default: true) */
13
+ keepAlive?: boolean;
14
+ /** Keep-alive initial delay in ms (default: 1000) */
15
+ keepAliveInitialDelay?: number;
16
+ /** Socket timeout in ms (default: 30000) */
17
+ timeout?: number;
18
+ }
9
19
  export type HousekitClientConfig = ClickHouseClientConfigOptions & {
10
20
  schema?: Record<string, TableDefinition<any>>;
11
21
  logger?: HousekitLogger;
22
+ /** Connection pool configuration */
23
+ pool?: ConnectionPoolConfig;
24
+ /** Skip validation for maximum insert performance */
25
+ skipValidation?: boolean;
12
26
  };
13
27
  export type ClientConfigWithSchema = HousekitClientConfig;
14
28
  export interface HousekitClient<TSchema extends Record<string, TableDefinition<any>> | undefined = any> {
@@ -45,3 +59,4 @@ export declare function createHousekitClient<TSchema extends Record<string, Tabl
45
59
  schema?: TSchema;
46
60
  }): HousekitClient<TSchema>;
47
61
  export declare const createClientFromConfigObject: typeof createHousekitClient;
62
+ export {};
package/dist/column.d.ts CHANGED
@@ -44,7 +44,7 @@ export declare class ClickHouseColumn<TType = any, TNotNull extends boolean = tr
44
44
  * Define a default value for the column.
45
45
  * Can be a static value or a SQL expression like 'now()'.
46
46
  */
47
- default(value: any): ClickHouseColumn<TType, TNotNull, TAutoGenerated>;
47
+ default(value: any): ClickHouseColumn<TType, TNotNull, true>;
48
48
  references(getColumn: () => ClickHouseColumn, options?: {
49
49
  onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
50
50
  onUpdate?: 'cascade' | 'set null' | 'restrict' | 'no action';
@@ -71,6 +71,6 @@ export declare class ClickHouseColumn<TType = any, TNotNull extends boolean = tr
71
71
  * Calculates the column value on the client side before insertion.
72
72
  * Useful for UUIDs, hashes, or computations based on other fields.
73
73
  */
74
- $defaultFn(fn: (row: Record<string, any>) => any): ClickHouseColumn<TType, TNotNull, TAutoGenerated>;
74
+ $defaultFn(fn: (row: Record<string, any>) => any): ClickHouseColumn<TType, TNotNull, true>;
75
75
  toSQL(): string;
76
76
  }