metal-orm 1.0.90 → 1.0.91

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,229 +1,229 @@
1
- /**
2
- * DTO (Data Transfer Object) type utilities for metal-orm.
3
- * Derives API types from TableDef/Entity metadata.
4
- */
5
-
6
- import type { TableDef } from '../schema/table.js';
7
- import type { ColumnDef } from '../schema/column-types.js';
8
- import type { ColumnToTs, InferRow } from '../schema/types.js';
9
- import type { EntityConstructor } from '../orm/entity-metadata.js';
10
-
11
- // ─────────────────────────────────────────────────────────────────────────────
12
- // Entity support: Extract row type from entity constructor
13
- // ─────────────────────────────────────────────────────────────────────────────
14
-
15
- /**
16
- * Checks if a type is a TableDef.
17
- */
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- type IsTableDef<T> = T extends { name: string; columns: any } ? true : false;
20
-
21
- /**
22
- * Extracts the row type from either a TableDef or EntityConstructor.
23
- */
24
- type ExtractRow<T> = IsTableDef<T> extends true
25
- ? InferRow<T extends TableDef<infer C> ? TableDef<C> : never>
26
- : T extends EntityConstructor<infer E>
27
- ? E
28
- : never;
29
-
30
- /**
31
- * Utility to flatten intersection types for better IDE display.
32
- */
33
- export type Simplify<T> = { [K in keyof T]: T[K] } & {};
34
-
35
- /**
36
- * Response DTO - excludes specified columns from the entity.
37
- * Use this to hide sensitive fields like passwordHash, apiKey, etc.
38
- *
39
- * @example
40
- * ```ts
41
- * // With TableDef
42
- * type UserResponse = Dto<typeof usersTable, 'passwordHash'>;
43
- *
44
- * // With Entity class
45
- * type UserResponse = Dto<User, 'passwordHash'>;
46
- *
47
- * // Exclude multiple fields
48
- * type UserPublic = Dto<User, 'passwordHash' | 'email'>;
49
- *
50
- * // Include all fields (no exclusions)
51
- * type UserFull = Dto<User>;
52
- * ```
53
- */
54
- export type Dto<
55
- T extends TableDef | EntityConstructor,
56
- TExclude extends keyof ExtractRow<T> = never
57
- > = Simplify<Omit<ExtractRow<T>, TExclude>>;
58
-
59
- /**
60
- * Compose a DTO with relations.
61
- *
62
- * @example
63
- * ```ts
64
- * type UserWithPosts = WithRelations<UserResponse, {
65
- * posts: PostResponse[]
66
- * }>;
67
- *
68
- * type PostWithAuthor = WithRelations<PostResponse, {
69
- * author: Dto<User, 'passwordHash' | 'email'>
70
- * }>;
71
- * ```
72
- */
73
- export type WithRelations<TBase, TRelations> = Simplify<TBase & TRelations>;
74
-
75
- // ─────────────────────────────────────────────────────────────────────────────
76
- // Column classification helpers for TableDef (for Create/Update DTOs)
77
- // ─────────────────────────────────────────────────────────────────────────────
78
-
79
- type ColumnMap<T extends TableDef> = T['columns'];
80
-
81
- /**
82
- * Checks if a column has a default value.
83
- */
84
- type HasDefault<TCol extends ColumnDef> =
85
- TCol['default'] extends undefined ? false : true;
86
-
87
- /**
88
- * Checks if a column is auto-generated (autoIncrement or generated always/byDefault).
89
- */
90
- type IsAutoGenerated<TCol extends ColumnDef> =
91
- TCol['autoIncrement'] extends true ? true :
92
- TCol['generated'] extends 'always' | 'byDefault' ? true :
93
- false;
94
-
95
- /**
96
- * Checks if a column is insertable (not auto-generated).
97
- */
98
- type IsInsertable<TCol extends ColumnDef> =
99
- IsAutoGenerated<TCol> extends true ? false : true;
100
-
101
- /**
102
- * Checks if a column is required for insert (notNull, no default, not auto-generated).
103
- */
104
- type IsRequiredInsert<TCol extends ColumnDef> =
105
- TCol['notNull'] extends true
106
- ? IsAutoGenerated<TCol> extends true ? false :
107
- HasDefault<TCol> extends true ? false :
108
- true
109
- : false;
110
-
111
- /**
112
- * Keys that are required for insert (notNull, no default, not auto-generated).
113
- */
114
- type RequiredInsertKeys<T extends TableDef> = {
115
- [K in keyof ColumnMap<T>]: ColumnMap<T>[K] extends ColumnDef
116
- ? IsInsertable<ColumnMap<T>[K]> extends true
117
- ? IsRequiredInsert<ColumnMap<T>[K]> extends true ? K : never
118
- : never
119
- : never;
120
- }[keyof ColumnMap<T>];
121
-
122
- /**
123
- * Keys that are optional for insert (nullable, has default, or optional).
124
- */
125
- type OptionalInsertKeys<T extends TableDef> = {
126
- [K in keyof ColumnMap<T>]: ColumnMap<T>[K] extends ColumnDef
127
- ? IsInsertable<ColumnMap<T>[K]> extends true
128
- ? IsRequiredInsert<ColumnMap<T>[K]> extends false ? K : never
129
- : never
130
- : never;
131
- }[keyof ColumnMap<T>];
132
-
133
- /**
134
- * Create DTO - includes only insertable columns with proper optionality.
135
- * Auto-generated columns (autoIncrement, generated) are excluded.
136
- * Columns with defaults or nullable are optional.
137
- *
138
- * Works with both TableDef and EntityConstructor:
139
- * - For TableDef: Uses column metadata to determine required/optional fields
140
- * - For EntityConstructor: All fields are optional (simpler type inference)
141
- *
142
- * @example
143
- * ```ts
144
- * // With TableDef - auto-excludes id (autoIncrement), createdAt (has default)
145
- * type CreateUserDto = CreateDto<typeof usersTable>;
146
- * // → { name: string; email: string; bio?: string }
147
- *
148
- * // With Entity class - simpler inference, all fields optional
149
- * type CreateUserDto = CreateDto<User>;
150
- *
151
- * // Exclude additional fields (e.g., authorId comes from context)
152
- * type CreatePostDto = CreateDto<typeof Post, 'authorId'>;
153
- * ```
154
- */
155
- export type CreateDto<
156
- T extends TableDef | EntityConstructor,
157
- TExclude extends keyof ExtractRow<T> = never
158
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
- > = T extends TableDef<any>
160
- ? Simplify<
161
- { [K in Exclude<RequiredInsertKeys<T>, TExclude>]: ColumnToTs<ColumnMap<T>[K]> } &
162
- { [K in Exclude<OptionalInsertKeys<T>, TExclude>]?: ColumnToTs<ColumnMap<T>[K]> }
163
- >
164
- : Simplify<{
165
- [K in Exclude<keyof ExtractRow<T>, TExclude>]?: ExtractRow<T>[K];
166
- }>;
167
-
168
- /**
169
- * Update DTO - all columns are optional (partial update).
170
- * Excludes specified columns (e.g., id, createdAt).
171
- *
172
- * @example
173
- * ```ts
174
- * // With TableDef
175
- * type UpdateUserDto = UpdateDto<typeof User>;
176
- * // → { name?: string; email?: string; bio?: string; ... }
177
- *
178
- * // With Entity class
179
- * type UpdateUserDto = UpdateDto<User>;
180
- *
181
- * // Exclude fields that shouldn't be updated
182
- * type UpdateUserDto = UpdateDto<typeof User, 'id' | 'createdAt'>;
183
- * ```
184
- */
185
- export type UpdateDto<
186
- T extends TableDef | EntityConstructor,
187
- TExclude extends keyof ExtractRow<T> = never
188
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
189
- > = T extends TableDef<any>
190
- ? Simplify<{
191
- [K in Exclude<keyof ColumnMap<T>, TExclude>]?: ColumnToTs<ColumnMap<T>[K]>;
192
- }>
193
- : Simplify<{
194
- [K in Exclude<keyof ExtractRow<T>, TExclude>]?: ExtractRow<T>[K];
195
- }>;
196
-
197
- // ─────────────────────────────────────────────────────────────────────────────
198
- // Enhanced Pagination DTO types
199
- // ─────────────────────────────────────────────────────────────────────────────
200
-
201
- /**
202
- * Enhanced paginated response with computed navigation metadata.
203
- * Extends the basic PaginatedResult with additional convenience fields.
204
- *
205
- * @example
206
- * ```ts
207
- * type UsersPagedResponse = PagedResponse<UserResponse>;
208
- *
209
- * // Response:
210
- * // {
211
- * // items: UserResponse[];
212
- * // totalItems: number;
213
- * // page: number;
214
- * // pageSize: number;
215
- * // totalPages: number;
216
- * // hasNextPage: boolean;
217
- * // hasPrevPage: boolean;
218
- * // }
219
- * ```
220
- */
221
- export type PagedResponse<T> = {
222
- items: T[];
223
- totalItems: number;
224
- page: number;
225
- pageSize: number;
226
- totalPages: number;
227
- hasNextPage: boolean;
228
- hasPrevPage: boolean;
229
- };
1
+ /**
2
+ * DTO (Data Transfer Object) type utilities for metal-orm.
3
+ * Derives API types from TableDef/Entity metadata.
4
+ */
5
+
6
+ import type { TableDef } from '../schema/table.js';
7
+ import type { ColumnDef } from '../schema/column-types.js';
8
+ import type { ColumnToTs, InferRow } from '../schema/types.js';
9
+ import type { EntityConstructor } from '../orm/entity-metadata.js';
10
+
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+ // Entity support: Extract row type from entity constructor
13
+ // ─────────────────────────────────────────────────────────────────────────────
14
+
15
+ /**
16
+ * Checks if a type is a TableDef.
17
+ */
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ type IsTableDef<T> = T extends { name: string; columns: any } ? true : false;
20
+
21
+ /**
22
+ * Extracts the row type from either a TableDef or EntityConstructor.
23
+ */
24
+ type ExtractRow<T> = IsTableDef<T> extends true
25
+ ? InferRow<T extends TableDef<infer C> ? TableDef<C> : never>
26
+ : T extends EntityConstructor<infer E>
27
+ ? E
28
+ : never;
29
+
30
+ /**
31
+ * Utility to flatten intersection types for better IDE display.
32
+ */
33
+ export type Simplify<T> = { [K in keyof T]: T[K] } & {};
34
+
35
+ /**
36
+ * Response DTO - excludes specified columns from the entity.
37
+ * Use this to hide sensitive fields like passwordHash, apiKey, etc.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * // With TableDef
42
+ * type UserResponse = Dto<typeof usersTable, 'passwordHash'>;
43
+ *
44
+ * // With Entity class
45
+ * type UserResponse = Dto<User, 'passwordHash'>;
46
+ *
47
+ * // Exclude multiple fields
48
+ * type UserPublic = Dto<User, 'passwordHash' | 'email'>;
49
+ *
50
+ * // Include all fields (no exclusions)
51
+ * type UserFull = Dto<User>;
52
+ * ```
53
+ */
54
+ export type Dto<
55
+ T extends TableDef | EntityConstructor,
56
+ TExclude extends keyof ExtractRow<T> = never
57
+ > = Simplify<Omit<ExtractRow<T>, TExclude>>;
58
+
59
+ /**
60
+ * Compose a DTO with relations.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * type UserWithPosts = WithRelations<UserResponse, {
65
+ * posts: PostResponse[]
66
+ * }>;
67
+ *
68
+ * type PostWithAuthor = WithRelations<PostResponse, {
69
+ * author: Dto<User, 'passwordHash' | 'email'>
70
+ * }>;
71
+ * ```
72
+ */
73
+ export type WithRelations<TBase, TRelations> = Simplify<TBase & TRelations>;
74
+
75
+ // ─────────────────────────────────────────────────────────────────────────────
76
+ // Column classification helpers for TableDef (for Create/Update DTOs)
77
+ // ─────────────────────────────────────────────────────────────────────────────
78
+
79
+ type ColumnMap<T extends TableDef> = T['columns'];
80
+
81
+ /**
82
+ * Checks if a column has a default value.
83
+ */
84
+ type HasDefault<TCol extends ColumnDef> =
85
+ TCol['default'] extends undefined ? false : true;
86
+
87
+ /**
88
+ * Checks if a column is auto-generated (autoIncrement or generated always/byDefault).
89
+ */
90
+ type IsAutoGenerated<TCol extends ColumnDef> =
91
+ TCol['autoIncrement'] extends true ? true :
92
+ TCol['generated'] extends 'always' | 'byDefault' ? true :
93
+ false;
94
+
95
+ /**
96
+ * Checks if a column is insertable (not auto-generated).
97
+ */
98
+ type IsInsertable<TCol extends ColumnDef> =
99
+ IsAutoGenerated<TCol> extends true ? false : true;
100
+
101
+ /**
102
+ * Checks if a column is required for insert (notNull, no default, not auto-generated).
103
+ */
104
+ type IsRequiredInsert<TCol extends ColumnDef> =
105
+ TCol['notNull'] extends true
106
+ ? IsAutoGenerated<TCol> extends true ? false :
107
+ HasDefault<TCol> extends true ? false :
108
+ true
109
+ : false;
110
+
111
+ /**
112
+ * Keys that are required for insert (notNull, no default, not auto-generated).
113
+ */
114
+ type RequiredInsertKeys<T extends TableDef> = {
115
+ [K in keyof ColumnMap<T>]: ColumnMap<T>[K] extends ColumnDef
116
+ ? IsInsertable<ColumnMap<T>[K]> extends true
117
+ ? IsRequiredInsert<ColumnMap<T>[K]> extends true ? K : never
118
+ : never
119
+ : never;
120
+ }[keyof ColumnMap<T>];
121
+
122
+ /**
123
+ * Keys that are optional for insert (nullable, has default, or optional).
124
+ */
125
+ type OptionalInsertKeys<T extends TableDef> = {
126
+ [K in keyof ColumnMap<T>]: ColumnMap<T>[K] extends ColumnDef
127
+ ? IsInsertable<ColumnMap<T>[K]> extends true
128
+ ? IsRequiredInsert<ColumnMap<T>[K]> extends false ? K : never
129
+ : never
130
+ : never;
131
+ }[keyof ColumnMap<T>];
132
+
133
+ /**
134
+ * Create DTO - includes only insertable columns with proper optionality.
135
+ * Auto-generated columns (autoIncrement, generated) are excluded.
136
+ * Columns with defaults or nullable are optional.
137
+ *
138
+ * Works with both TableDef and EntityConstructor:
139
+ * - For TableDef: Uses column metadata to determine required/optional fields
140
+ * - For EntityConstructor: All fields are optional (simpler type inference)
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * // With TableDef - auto-excludes id (autoIncrement), createdAt (has default)
145
+ * type CreateUserDto = CreateDto<typeof usersTable>;
146
+ * // → { name: string; email: string; bio?: string }
147
+ *
148
+ * // With Entity class - simpler inference, all fields optional
149
+ * type CreateUserDto = CreateDto<User>;
150
+ *
151
+ * // Exclude additional fields (e.g., authorId comes from context)
152
+ * type CreatePostDto = CreateDto<typeof Post, 'authorId'>;
153
+ * ```
154
+ */
155
+ export type CreateDto<
156
+ T extends TableDef | EntityConstructor,
157
+ TExclude extends keyof ExtractRow<T> = never
158
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
+ > = T extends TableDef<any>
160
+ ? Simplify<
161
+ { [K in Exclude<RequiredInsertKeys<T>, TExclude>]: ColumnToTs<ColumnMap<T>[K]> } &
162
+ { [K in Exclude<OptionalInsertKeys<T>, TExclude>]?: ColumnToTs<ColumnMap<T>[K]> }
163
+ >
164
+ : Simplify<{
165
+ [K in Exclude<keyof ExtractRow<T>, TExclude>]?: ExtractRow<T>[K];
166
+ }>;
167
+
168
+ /**
169
+ * Update DTO - all columns are optional (partial update).
170
+ * Excludes specified columns (e.g., id, createdAt).
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * // With TableDef
175
+ * type UpdateUserDto = UpdateDto<typeof User>;
176
+ * // → { name?: string; email?: string; bio?: string; ... }
177
+ *
178
+ * // With Entity class
179
+ * type UpdateUserDto = UpdateDto<User>;
180
+ *
181
+ * // Exclude fields that shouldn't be updated
182
+ * type UpdateUserDto = UpdateDto<typeof User, 'id' | 'createdAt'>;
183
+ * ```
184
+ */
185
+ export type UpdateDto<
186
+ T extends TableDef | EntityConstructor,
187
+ TExclude extends keyof ExtractRow<T> = never
188
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
189
+ > = T extends TableDef<any>
190
+ ? Simplify<{
191
+ [K in Exclude<keyof ColumnMap<T>, TExclude>]?: ColumnToTs<ColumnMap<T>[K]>;
192
+ }>
193
+ : Simplify<{
194
+ [K in Exclude<keyof ExtractRow<T>, TExclude>]?: ExtractRow<T>[K];
195
+ }>;
196
+
197
+ // ─────────────────────────────────────────────────────────────────────────────
198
+ // Enhanced Pagination DTO types
199
+ // ─────────────────────────────────────────────────────────────────────────────
200
+
201
+ /**
202
+ * Enhanced paginated response with computed navigation metadata.
203
+ * Extends the basic PaginatedResult with additional convenience fields.
204
+ *
205
+ * @example
206
+ * ```ts
207
+ * type UsersPagedResponse = PagedResponse<UserResponse>;
208
+ *
209
+ * // Response:
210
+ * // {
211
+ * // items: UserResponse[];
212
+ * // totalItems: number;
213
+ * // page: number;
214
+ * // pageSize: number;
215
+ * // totalPages: number;
216
+ * // hasNextPage: boolean;
217
+ * // hasPrevPage: boolean;
218
+ * // }
219
+ * ```
220
+ */
221
+ export type PagedResponse<T> = {
222
+ items: T[];
223
+ totalItems: number;
224
+ page: number;
225
+ pageSize: number;
226
+ totalPages: number;
227
+ hasNextPage: boolean;
228
+ hasPrevPage: boolean;
229
+ };