@housekit/orm 0.1.18 → 0.1.21

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.
@@ -1,5 +1,13 @@
1
- import { type TableDefinition } from './core';
1
+ import { type RelationDefinition, type TableDefinition, type CleanSelect } from './core';
2
2
  import type { SQLExpression } from './expressions';
3
+ type Simplify<T> = {
4
+ [K in keyof T]: T[K];
5
+ } & {};
6
+ type RelationsOf<TTable> = TTable extends {
7
+ $relations: infer R;
8
+ } ? R : {};
9
+ type NormalizedRelations<TTable> = RelationsOf<TTable> extends Record<string, RelationDefinition<any>> ? RelationsOf<TTable> : {};
10
+ type RelationTarget<T> = T extends RelationDefinition<infer TTarget> ? TTarget : never;
3
11
  /**
4
12
  * Join strategy for relational queries.
5
13
  *
@@ -10,6 +18,23 @@ export type JoinStrategy = 'auto' | 'standard' | 'global' | 'any' | 'global_any'
10
18
  /**
11
19
  * Configuration options for the Relational Query API (`findMany`, `findFirst`).
12
20
  */
21
+ export type RelationalWith<TTable> = [
22
+ keyof NormalizedRelations<TTable>
23
+ ] extends [never] ? Record<string, boolean | RelationalFindOptions<any>> : {
24
+ [K in keyof NormalizedRelations<TTable>]?: boolean | RelationalFindOptions<RelationTarget<NormalizedRelations<TTable>[K]>>;
25
+ };
26
+ type NestedWith<T> = T extends RelationalFindOptions<any> ? T['with'] : undefined;
27
+ type RelationValue<TRel, TWith> = TRel extends {
28
+ relation: 'one';
29
+ table: infer TRelTable;
30
+ } ? RelationalResult<TRelTable, TWith> | null : TRel extends {
31
+ relation: 'many';
32
+ table: infer TRelTable;
33
+ } ? Array<RelationalResult<TRelTable, TWith>> : never;
34
+ type RelationValueForKey<TTable, K, TWithValue> = K extends keyof NormalizedRelations<TTable> ? TWithValue extends boolean ? RelationValue<NormalizedRelations<TTable>[K], undefined> : TWithValue extends RelationalFindOptions<any> ? RelationValue<NormalizedRelations<TTable>[K], NestedWith<TWithValue>> : never : any;
35
+ export type RelationalResult<TTable, TWith = undefined> = Simplify<CleanSelect<TTable> & (TWith extends RelationalWith<TTable> ? {
36
+ [K in keyof TWith]: RelationValueForKey<TTable, K, TWith[K]>;
37
+ } : {})>;
13
38
  export type RelationalFindOptions<TTable = any> = {
14
39
  /**
15
40
  * Filter conditions for the main query.
@@ -32,7 +57,7 @@ export type RelationalFindOptions<TTable = any> = {
32
57
  *
33
58
  * @example with: { posts: { limit: 5 }, profile: true }
34
59
  */
35
- with?: Record<string, boolean | RelationalFindOptions>;
60
+ with?: RelationalWith<TTable>;
36
61
  /**
37
62
  * Join strategy for related data.
38
63
  *
@@ -46,6 +71,12 @@ export type RelationalFindOptions<TTable = any> = {
46
71
  */
47
72
  joinStrategy?: JoinStrategy;
48
73
  };
74
+ export type RelationalAPI<TSchema extends Record<string, TableDefinition<any>>> = {
75
+ [K in keyof TSchema]: {
76
+ findMany: <TOpts extends RelationalFindOptions<TSchema[K]> | undefined>(opts?: TOpts) => Promise<Array<RelationalResult<TSchema[K], TOpts extends RelationalFindOptions<TSchema[K]> ? TOpts['with'] : undefined>>>;
77
+ findFirst: <TOpts extends RelationalFindOptions<TSchema[K]> | undefined>(opts?: TOpts) => Promise<RelationalResult<TSchema[K], TOpts extends RelationalFindOptions<TSchema[K]> ? TOpts['with'] : undefined> | null>;
78
+ };
79
+ };
49
80
  /**
50
81
  * Build a modern relational API with ClickHouse-specific optimizations.
51
82
  *
@@ -58,4 +89,5 @@ export type RelationalFindOptions<TTable = any> = {
58
89
  * @param client - The ClickHouse client instance.
59
90
  * @param schema - The shared schema definition containing all table and relation metadata.
60
91
  */
61
- export declare function buildRelationalAPI(client: any, schema?: Record<string, TableDefinition<any>>): Record<string, any> | undefined;
92
+ export declare function buildRelationalAPI<TSchema extends Record<string, TableDefinition<any>>>(client: any, schema?: TSchema): RelationalAPI<TSchema> | undefined;
93
+ export {};
@@ -1,4 +1,4 @@
1
- import type { ClickHouseColumn, RelationDefinition, TableDefinition } from './core';
1
+ import type { ClickHouseColumn, RelationDefinition, TableColumns, TableDefinition } from './core';
2
2
  type OneConfig = {
3
3
  fields: ClickHouseColumn[];
4
4
  references: ClickHouseColumn[];
@@ -8,8 +8,11 @@ type ManyConfig = {
8
8
  references?: ClickHouseColumn[];
9
9
  };
10
10
  type RelationBuilderHelpers = {
11
- one: (table: TableDefinition<any>, config: OneConfig) => RelationDefinition;
12
- many: (table: TableDefinition<any>, config?: ManyConfig) => RelationDefinition;
11
+ one: <TTarget extends TableDefinition<TableColumns>>(table: TTarget, config: OneConfig) => RelationDefinition<TTarget>;
12
+ many: <TTarget extends TableDefinition<TableColumns>>(table: TTarget, config?: ManyConfig) => RelationDefinition<TTarget>;
13
13
  };
14
- export declare function relations<TTable extends TableDefinition<any>, TRelations extends Record<string, RelationDefinition>>(table: TTable, callback: (helpers: RelationBuilderHelpers) => TRelations): TRelations;
14
+ export declare function relations<TTable extends TableDefinition<TableColumns>, TRelations extends Record<string, RelationDefinition<any>>>(table: TTable, callback: (helpers: RelationBuilderHelpers) => TRelations): asserts table is TTable & {
15
+ $relations: TRelations;
16
+ };
17
+ export declare function relations<TTable extends TableDefinition<TableColumns>, TRelations extends Record<string, RelationDefinition<any>>>(table: TTable, callback: (helpers: RelationBuilderHelpers) => TRelations): TRelations;
15
18
  export {};
@@ -132,6 +132,24 @@ export declare const t: {
132
132
  polygon: (name: string) => ClickHouseColumn<[number, number][][], true, false>;
133
133
  multiPolygon: (name: string) => ClickHouseColumn<[number, number][][][], true, false>;
134
134
  enum: (name: string, values: readonly string[]) => ClickHouseColumn<string, false, false>;
135
+ /**
136
+ * Adds 'created_at' and 'updated_at' columns with default now().
137
+ */
138
+ timestamps: () => {
139
+ created_at: ClickHouseColumn<string | Date, true, false>;
140
+ updated_at: ClickHouseColumn<string | Date, true, false>;
141
+ };
142
+ /**
143
+ * Adds a standard UUID primary key column (default: 'id').
144
+ */
145
+ primaryUuid: <TName extends string = "id">(name?: TName) => { [K in TName]: ClickHouseColumn<string>; };
146
+ /**
147
+ * Adds 'is_deleted' and 'deleted_at' columns for soft deletes.
148
+ */
149
+ softDeletes: () => {
150
+ is_deleted: ClickHouseColumn<boolean, true, false>;
151
+ deleted_at: ClickHouseColumn<string | Date, false, false>;
152
+ };
135
153
  };
136
154
  export type ColumnBuilder = typeof t;
137
155
  /**
@@ -160,13 +178,25 @@ export { chProjection as defineProjection };
160
178
  /**
161
179
  * Define relations for a table using a callback pattern.
162
180
  */
163
- export declare function relations<TTable extends TableDefinition<any>>(table: TTable, relationsBuilder: (helpers: {
164
- one: (table: TableDefinition<any>, config: {
181
+ export declare function relations<TTable extends TableDefinition<any>, TRelations extends Record<string, RelationDefinition<any>>>(table: TTable, relationsBuilder: (helpers: {
182
+ one: <TTarget extends TableDefinition<any>>(table: TTarget, config: {
183
+ fields: ClickHouseColumn<any, any, any>[];
184
+ references: ClickHouseColumn<any, any, any>[];
185
+ }) => RelationDefinition<TTarget>;
186
+ many: <TTarget extends TableDefinition<any>>(table: TTarget, config?: {
187
+ fields?: ClickHouseColumn<any, any, any>[];
188
+ references?: ClickHouseColumn<any, any, any>[];
189
+ }) => RelationDefinition<TTarget>;
190
+ }) => TRelations): asserts table is TTable & {
191
+ $relations: TRelations;
192
+ };
193
+ export declare function relations<TTable extends TableDefinition<any>, TRelations extends Record<string, RelationDefinition<any>>>(table: TTable, relationsBuilder: (helpers: {
194
+ one: <TTarget extends TableDefinition<any>>(table: TTarget, config: {
165
195
  fields: ClickHouseColumn<any, any, any>[];
166
196
  references: ClickHouseColumn<any, any, any>[];
167
- }) => RelationDefinition;
168
- many: (table: TableDefinition<any>, config?: {
197
+ }) => RelationDefinition<TTarget>;
198
+ many: <TTarget extends TableDefinition<any>>(table: TTarget, config?: {
169
199
  fields?: ClickHouseColumn<any, any, any>[];
170
200
  references?: ClickHouseColumn<any, any, any>[];
171
- }) => RelationDefinition;
172
- }) => Record<string, RelationDefinition>): Record<string, RelationDefinition>;
201
+ }) => RelationDefinition<TTarget>;
202
+ }) => TRelations): TRelations;
package/dist/table.d.ts CHANGED
@@ -53,16 +53,16 @@ export declare const index: (name: string) => {
53
53
  };
54
54
  };
55
55
  export declare const projection: (name: string, query: string) => ProjectionDefinition;
56
- export type RelationDefinition = {
56
+ export type RelationDefinition<TTarget extends TableDefinition<any> = TableDefinition<any>> = {
57
57
  relation: 'one';
58
58
  name: string;
59
- table: TableDefinition<any>;
59
+ table: TTarget;
60
60
  fields: ClickHouseColumn[];
61
61
  references: ClickHouseColumn[];
62
62
  } | {
63
63
  relation: 'many';
64
64
  name: string;
65
- table: TableDefinition<any>;
65
+ table: TTarget;
66
66
  fields?: ClickHouseColumn[];
67
67
  references?: ClickHouseColumn[];
68
68
  };
@@ -81,21 +81,21 @@ export type InferSelectModel<T extends {
81
81
  };
82
82
  export type InferInsertModel<T extends {
83
83
  $columns: TableColumns;
84
- }> = TableInsert<T['$columns']> & {
85
- (): TableInsert<T['$columns']>;
86
- (): TableInsert<T['$columns']>[];
87
- (): TableInsert<T['$columns']>;
88
- };
89
- export type CleanInsert<T extends {
90
- $inferInsert?: any;
91
- }> = T extends {
92
- $inferInsert?: infer I;
93
- } ? I : never;
94
- export type CleanSelect<T extends {
95
- $inferSelect?: any;
96
- }> = T extends {
97
- $inferSelect?: infer S;
98
- } ? S : never;
84
+ }> = TableInsert<T['$columns']>;
85
+ type InferInsertFromColumns<T> = T extends {
86
+ $columns: infer TCols extends TableColumns;
87
+ } ? TableInsert<TCols> : never;
88
+ type InferSelectFromColumns<T> = T extends {
89
+ $columns: infer TCols extends TableColumns;
90
+ } ? InferSelectModel<{
91
+ $columns: TCols;
92
+ }> : never;
93
+ export type CleanInsert<T> = T extends {
94
+ $inferInsert: infer I;
95
+ } ? I : InferInsertFromColumns<T>;
96
+ export type CleanSelect<T> = T extends {
97
+ $inferSelect: infer S;
98
+ } ? S : InferSelectFromColumns<T>;
99
99
  export type InferInsertValue<TCols extends TableColumns, K extends keyof TCols> = TCols[K] extends ClickHouseColumn<infer Type, infer NotNull, any> ? NotNull extends true ? Type : Type | undefined | null : never;
100
100
  export type TableInsertArray<T extends TableDefinition<TableColumns>> = T['$inferInsert'][];
101
101
  export type TableModel<T extends TableDefinition<TableColumns>> = PublicSelectModel<T>;
@@ -112,13 +112,24 @@ export type TableDefinition<TCols extends TableColumns, TOptions = TableOptions>
112
112
  toSQL(): string;
113
113
  toSQLs?(): string[];
114
114
  as(alias: string): TableDefinition<TCols, TOptions>;
115
- $inferSelect?: InferSelectModel<{
115
+ $inferSelect: InferSelectModel<{
116
116
  $columns: TCols;
117
117
  }>;
118
- $inferInsert?: InferInsertModel<{
118
+ $inferInsert: InferInsertModel<{
119
119
  $columns: TCols;
120
120
  }>;
121
121
  } & TCols;
122
+ export type TableRuntime<TInsert = any, TSelect = any, TOptions = TableOptions> = {
123
+ $table: string;
124
+ $columns: TableColumns;
125
+ $options: TOptions;
126
+ $relations?: Record<string, RelationDefinition>;
127
+ toSQL(): string;
128
+ toSQLs?(): string[];
129
+ as(alias: string): TableRuntime<TInsert, TSelect, TOptions>;
130
+ $inferSelect: TSelect;
131
+ $inferInsert: TInsert;
132
+ };
122
133
  export interface VersionedMeta {
123
134
  baseName: string;
124
135
  version: string | number;
@@ -1,5 +1,5 @@
1
1
  import type { ClickHouseClient } from '@clickhouse/client';
2
- import type { TableDefinition } from '../core';
2
+ import type { TableRuntime } from '../core';
3
3
  export interface BatchConfig {
4
4
  maxRows: number;
5
5
  flushIntervalMs: number;
@@ -12,7 +12,7 @@ declare class BackgroundBatcher {
12
12
  private serializers;
13
13
  constructor(client: ClickHouseClient);
14
14
  private getSerializer;
15
- add(table: TableDefinition<any>, row: any, config: BatchConfig): Promise<void>;
15
+ add(table: TableRuntime<any, any>, row: any, config: BatchConfig): Promise<void>;
16
16
  flush(tableName: string): Promise<void>;
17
17
  flushAll(): Promise<void>;
18
18
  }
@@ -1,4 +1,4 @@
1
- import { ClickHouseColumn, type TableDefinition } from '../core';
1
+ import { ClickHouseColumn, type TableRuntime } from '../core';
2
2
  type UUIDVersion = 1 | 3 | 4 | 5 | 6 | 7;
3
3
  type PreparedInsertColumn = {
4
4
  propKey: string;
@@ -17,7 +17,7 @@ export type InsertPlan = {
17
17
  columnNames: string[];
18
18
  useCompact: boolean;
19
19
  };
20
- export declare function buildInsertPlan(table: TableDefinition<any>): InsertPlan;
20
+ export declare function buildInsertPlan(table: TableRuntime<any, any>): InsertPlan;
21
21
  export declare function processRowWithPlan(row: Record<string, any>, plan: InsertPlan, mode?: 'compact' | 'json'): Record<string, any> | any[];
22
22
  export declare function processRowsStream(rows: AsyncIterable<Record<string, any>> | Iterable<Record<string, any>>, plan: InsertPlan, mode?: 'compact' | 'json'): AsyncGenerator<any[] | Record<string, any>, void, unknown>;
23
23
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@housekit/orm",
3
- "version": "0.1.18",
3
+ "version": "0.1.21",
4
4
  "description": "Type-safe ClickHouse ORM with modern DX and ClickHouse-specific optimizations. Features Turbo Mode (RowBinary), full engine support, and advanced query capabilities.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",