@housekit/orm 0.1.25 → 0.1.26

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,192 @@ 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)]
89
161
  ```
90
162
 
91
163
  ---
92
164
 
93
- ## 🧠 Advanced Schema Definitions
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 `.returning()` when you need the inserted data back:
182
+
183
+ ```typescript
184
+ const [user] = await db
185
+ .insert(users)
186
+ .values({ email: 'a@b.com', role: 'admin' })
187
+ .returning();
188
+
189
+ console.log(user.id); // Generated UUID
190
+ ```
191
+
192
+ ### Force JSON Format
193
+
194
+ ```typescript
195
+ await db.insert(events).values(data).useJsonFormat();
196
+ ```
197
+
198
+ ### Background Batching
199
+
200
+ Collect small writes into efficient batches:
201
+
202
+ ```typescript
203
+ const builder = db.insert(events).batch({
204
+ maxRows: 10000,
205
+ flushIntervalMs: 5000
206
+ });
207
+
208
+ // Fire-and-forget
209
+ await builder.append(event1);
210
+ await builder.append(event2);
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 🧠 Advanced Schema
94
216
 
95
217
  ### Complex Engines
96
- HouseKit supports specialized ClickHouse engines with strict type checking for their parameters.
97
218
 
98
219
  ```typescript
99
- // SummingMergeTree: Automatically aggregates numeric columns
220
+ // SummingMergeTree
100
221
  export const dailyRevenue = defineTable('daily_revenue', {
101
222
  day: t.date('day'),
102
223
  revenue: t.float64('revenue'),
@@ -105,23 +226,19 @@ export const dailyRevenue = defineTable('daily_revenue', {
105
226
  orderBy: 'day'
106
227
  });
107
228
 
108
- // ReplacingMergeTree: Deduplicates data by version
229
+ // ReplacingMergeTree
109
230
  export const users = defineTable('users', {
110
231
  id: t.uint64('id'),
111
232
  email: t.string('email'),
112
233
  version: t.uint64('version'),
113
234
  }, {
114
235
  engine: Engine.ReplacingMergeTree('version'),
115
-
116
- // Portability: '{cluster}' references the server-side macro.
117
- onCluster: '{cluster}',
118
-
236
+ onCluster: '{cluster}',
119
237
  orderBy: 'id'
120
238
  });
121
239
  ```
122
240
 
123
241
  ### Dictionaries
124
- Map external data or internal tables to fast in-memory dictionaries for ultra-low latency lookups.
125
242
 
126
243
  ```typescript
127
244
  import { defineDictionary } from '@housekit/orm';
@@ -138,130 +255,128 @@ export const userCache = defineDictionary('user_dict', {
138
255
 
139
256
  ---
140
257
 
141
- ## 🚀 High-Performance Data Ingestion
258
+ ## 🔍 Specialized Joins
142
259
 
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.
260
+ ### ASOF JOIN
145
261
 
146
262
  ```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)
263
+ const matched = await db.select()
264
+ .from(trades)
265
+ .asofJoin(quotes, sql`${trades.symbol} = ${quotes.symbol} AND ${trades.at} >= ${quotes.at}`)
266
+ .limit(100);
153
267
  ```
154
268
 
155
- ### Background Batching
156
- Collect small, frequent writes into large batches to prevent the "too many parts" error in ClickHouse.
269
+ ### GLOBAL JOIN
157
270
 
158
271
  ```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);
272
+ await db.select()
273
+ .from(distributedTable)
274
+ .globalJoin(rightTable, condition);
170
275
  ```
171
276
 
172
277
  ---
173
278
 
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.
279
+ ## 🛠 SQL Utilities
177
280
 
178
- ### In Repository Functions
281
+ ### Dynamic Queries
179
282
 
180
283
  ```typescript
181
- import { webEvents, type NewWebEvent } from './schema';
284
+ const conditions = [
285
+ eq(users.active, true),
286
+ gte(users.age, 18)
287
+ ];
182
288
 
183
- async function logEvents(events: NewWebEvent[]) {
184
- // Types match automatically
185
- return await db.insert(webEvents).values(events);
186
- }
289
+ const query = await db.select()
290
+ .from(users)
291
+ .where(sql.join(conditions, sql` AND `));
187
292
  ```
188
293
 
189
294
  ---
190
295
 
191
- ## 🤝 Smart Relational API
296
+ ## Performance Optimizations
297
+
298
+ HouseKit includes several optimizations for maximum throughput in production environments.
192
299
 
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.
300
+ ### Connection Pooling
301
+
302
+ Reuse HTTP connections across requests:
194
303
 
195
304
  ```typescript
196
- // Define relations in your schema first
197
- import { relations } from '@housekit/orm';
305
+ const db = housekit({
306
+ url: 'http://localhost:8123',
307
+ pool: {
308
+ maxSockets: 200, // Max concurrent connections
309
+ keepAlive: true, // Reuse connections
310
+ timeout: 30000 // Socket timeout (ms)
311
+ }
312
+ }, { schema });
313
+ ```
198
314
 
199
- relations(users, ({ many }) => ({
200
- posts: many(posts, { fields: [users.id], references: [posts.userId] })
201
- }));
315
+ ### Skip Validation
202
316
 
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
- });
317
+ Bypass enum validation in production when you trust your data source:
214
318
 
215
- // Result structure:
216
- // [{ id: 1, name: 'Alice', posts: [{ title: '...', ... }] }]
319
+ ```typescript
320
+ // Global (all inserts)
321
+ const db = housekit({
322
+ url: 'http://localhost:8123',
323
+ skipValidation: true
324
+ }, { schema });
325
+
326
+ // Per-insert
327
+ await db.insert(events).values(data).skipValidation();
217
328
  ```
218
329
 
219
- ---
220
-
221
- ## 🛠 SQL Utilities
330
+ ### Object Pooling
222
331
 
223
- ### Dynamic Queries with `sql.join`
224
- Easily build complex queries by joining SQL fragments with separators.
332
+ HouseKit automatically pools `BinaryWriter` instances to reduce GC pressure. For advanced use cases:
225
333
 
226
334
  ```typescript
227
- const conditions = [
228
- eq(users.active, true),
229
- gte(users.age, 18)
230
- ];
335
+ import { acquireWriter, releaseWriter } from '@housekit/orm';
231
336
 
232
- const query = await db.select()
233
- .from(users)
234
- .where(sql.join(conditions, sql` AND `));
337
+ const writer = acquireWriter();
338
+ try {
339
+ // Use writer for custom serialization
340
+ } finally {
341
+ releaseWriter(writer);
342
+ }
235
343
  ```
236
344
 
237
- ---
345
+ ### Optimized Serialization
238
346
 
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).
347
+ For maximum performance with large batches:
243
348
 
244
349
  ```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);
350
+ import {
351
+ buildOptimizedBinaryConfig,
352
+ serializeRowsOptimized
353
+ } from '@housekit/orm';
354
+
355
+ // Pre-compile accessors once
356
+ const config = buildOptimizedBinaryConfig(columns, {
357
+ skipValidation: true
358
+ });
359
+
360
+ // Serialize batches with pooled writer + pre-compiled accessors
361
+ const buffer = serializeRowsOptimized(rows, config);
249
362
  ```
250
363
 
251
- ### GLOBAL JOIN
252
- Essential for distributed setups to avoid local-data-only results on sharded clusters.
364
+ ### TypedArray for Numeric Data
365
+
366
+ For schemas with mostly numeric columns:
253
367
 
254
368
  ```typescript
255
- await db.select()
256
- .from(distributedTable)
257
- .globalJoin(rightTable, condition);
369
+ import { isNumericHeavySchema, serializeNumericBatch } from '@housekit/orm';
370
+
371
+ if (isNumericHeavySchema(columns)) {
372
+ // Uses Float64Array for better memory layout
373
+ const { numericBuffer, otherData } = serializeNumericBatch(rows, config, numericIndices);
374
+ }
258
375
  ```
259
376
 
260
377
  ---
261
378
 
262
- ## 🛠 Observability & Logging
263
-
264
- Inject a custom logger to monitor query performance, throughput, and error rates.
379
+ ## 🛠 Observability
265
380
 
266
381
  ```typescript
267
382
  const db = await createClient({
@@ -278,4 +393,4 @@ const db = await createClient({
278
393
 
279
394
  ## License
280
395
 
281
- MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
396
+ MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
@@ -3,40 +3,43 @@ 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>;
39
37
  returning(): ClickHouseInsertBuilder<TTable, CleanSelect<TTable>[]>;
38
+ /**
39
+ * Disable the default returning() behavior.
40
+ * Useful when you don't need the inserted data back and want to avoid the overhead.
41
+ */
42
+ noReturning(): ClickHouseInsertBuilder<TTable, void>;
40
43
  /**
41
44
  * Force synchronous insert (disables async_insert).
42
45
  * Use when you need immediate durability guarantee.
@@ -131,6 +134,7 @@ export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, an
131
134
  private executeJsonInsert;
132
135
  /**
133
136
  * Execute insert using RowBinary format (fastest)
137
+ * Uses direct HTTP request since the official client doesn't support RowBinary yet
134
138
  */
135
139
  private executeBinaryInsert;
136
140
  /**
@@ -142,4 +146,3 @@ export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, an
142
146
  private resolveClientDefaultExpr;
143
147
  then<TResult1 = TReturn, TResult2 = never>(onfulfilled?: ((value: TReturn) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
144
148
  }
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
  }