metal-orm 1.0.89 → 1.0.90

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.
Files changed (49) hide show
  1. package/dist/index.cjs +2968 -2983
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +765 -246
  4. package/dist/index.d.ts +765 -246
  5. package/dist/index.js +2913 -2975
  6. package/dist/index.js.map +1 -1
  7. package/package.json +3 -2
  8. package/src/codegen/typescript.ts +29 -40
  9. package/src/core/ast/expression-builders.ts +34 -53
  10. package/src/core/ast/expression-nodes.ts +51 -72
  11. package/src/core/ast/expression-visitor.ts +219 -252
  12. package/src/core/ast/expression.ts +20 -21
  13. package/src/core/dialect/abstract.ts +55 -81
  14. package/src/core/execution/db-executor.ts +4 -5
  15. package/src/core/execution/executors/mysql-executor.ts +7 -9
  16. package/src/decorators/bootstrap.ts +11 -8
  17. package/src/dto/apply-filter.ts +281 -0
  18. package/src/dto/dto-types.ts +229 -0
  19. package/src/dto/filter-types.ts +193 -0
  20. package/src/dto/index.ts +97 -0
  21. package/src/dto/openapi/generators/base.ts +29 -0
  22. package/src/dto/openapi/generators/column.ts +34 -0
  23. package/src/dto/openapi/generators/dto.ts +94 -0
  24. package/src/dto/openapi/generators/filter.ts +74 -0
  25. package/src/dto/openapi/generators/nested-dto.ts +532 -0
  26. package/src/dto/openapi/generators/pagination.ts +111 -0
  27. package/src/dto/openapi/generators/relation-filter.ts +210 -0
  28. package/src/dto/openapi/index.ts +17 -0
  29. package/src/dto/openapi/type-mappings.ts +191 -0
  30. package/src/dto/openapi/types.ts +83 -0
  31. package/src/dto/openapi/utilities.ts +45 -0
  32. package/src/dto/pagination-utils.ts +150 -0
  33. package/src/dto/transform.ts +193 -0
  34. package/src/index.ts +67 -65
  35. package/src/orm/unit-of-work.ts +13 -25
  36. package/src/query-builder/query-ast-service.ts +287 -300
  37. package/src/query-builder/relation-filter-utils.ts +159 -160
  38. package/src/query-builder/select.ts +137 -192
  39. package/src/core/ast/ast-validation.ts +0 -19
  40. package/src/core/ast/param-proxy.ts +0 -47
  41. package/src/core/ast/query-visitor.ts +0 -273
  42. package/src/openapi/index.ts +0 -4
  43. package/src/openapi/query-parameters.ts +0 -207
  44. package/src/openapi/schema-extractor-input.ts +0 -193
  45. package/src/openapi/schema-extractor-output.ts +0 -427
  46. package/src/openapi/schema-extractor-utils.ts +0 -110
  47. package/src/openapi/schema-extractor.ts +0 -120
  48. package/src/openapi/schema-types.ts +0 -187
  49. package/src/openapi/type-mappers.ts +0 -227
@@ -0,0 +1,193 @@
1
+ /**
2
+ * DTO transformation utilities for working with DTO instances.
3
+ *
4
+ * These helpers make it easier to convert between DTO types, apply defaults,
5
+ * and handle auto-generated fields without manual object construction.
6
+ */
7
+
8
+ // ─────────────────────────────────────────────────────────────────────────────
9
+ // Transform utilities
10
+ // ─────────────────────────────────────────────────────────────────────────────
11
+
12
+ /**
13
+ * Transforms a CreateDto or UpdateDto into a ResponseDto by merging with auto-generated fields.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const newUser: UserResponse = toResponse(body, {
18
+ * id: newId,
19
+ * createdAt: new Date().toISOString()
20
+ * });
21
+ *
22
+ * // Or use the curried form for reuse
23
+ * const createUserResponse = toResponseBuilder<UserCreateDto, UserResponse>({
24
+ * id: () => nextId++,
25
+ * createdAt: () => new Date().toISOString()
26
+ * });
27
+ * const user = createUserResponse(body);
28
+ * ```
29
+ */
30
+ export function toResponse<TInput, TOutput>(
31
+ input: TInput,
32
+ autoFields: Partial<TOutput>
33
+ ): TOutput {
34
+ return {
35
+ ...input,
36
+ ...autoFields
37
+ } as unknown as TOutput;
38
+ }
39
+
40
+ /**
41
+ * Creates a toResponse function with pre-configured auto-fields.
42
+ * Useful for reusing same transformation logic across multiple endpoints.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const userResponseBuilder = toResponseBuilder<CreateUserDto, UserResponse>({
47
+ * id: () => generateId(),
48
+ * createdAt: () => new Date().toISOString(),
49
+ * active: true
50
+ * });
51
+ *
52
+ * app.post('/users', (req, res) => {
53
+ * const user = userResponseBuilder(req.body);
54
+ * res.status(201).json(user);
55
+ * });
56
+ * ```
57
+ */
58
+ export function toResponseBuilder<TInput, TOutput>(
59
+ autoFields: Partial<TOutput> | (() => Partial<TOutput>)
60
+ ): (input: TInput) => TOutput {
61
+ return (input: TInput) => {
62
+ const fields: Partial<TOutput> =
63
+ typeof autoFields === 'function' ? (autoFields as () => Partial<TOutput>)() : autoFields;
64
+ return {
65
+ ...input,
66
+ ...fields
67
+ } as unknown as TOutput;
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Merges default values into a DTO. Returns a complete object with all defaults applied.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const user = withDefaults(body, {
77
+ * active: true,
78
+ * createdAt: new Date().toISOString()
79
+ * });
80
+ * ```
81
+ */
82
+ export function withDefaults<T>(dto: Partial<T>, defaults: T): T {
83
+ return {
84
+ ...defaults,
85
+ ...dto
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Creates a withDefaults function with pre-configured defaults.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const userWithDefaults = withDefaultsBuilder<CreateUserDto>({
95
+ * active: true
96
+ * });
97
+ *
98
+ * app.post('/users', (req, res) => {
99
+ * const userInput = userWithDefaults(req.body);
100
+ * // ...
101
+ * });
102
+ * ```
103
+ */
104
+ export function withDefaultsBuilder<T>(
105
+ defaults: T | (() => T)
106
+ ): (dto: Partial<T>) => T {
107
+ return (dto: Partial<T>) => {
108
+ const resolvedDefaults: T =
109
+ typeof defaults === 'function' ? (defaults as () => T)() : defaults;
110
+ return {
111
+ ...resolvedDefaults,
112
+ ...dto
113
+ };
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Excludes specified fields from an object. Useful for removing sensitive fields
119
+ * from database results before sending to the client.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * const userFromDb = await db.select().from(users).where(...).first();
124
+ * const userResponse = exclude(userFromDb, 'passwordHash', 'apiKey');
125
+ * ```
126
+ */
127
+ export function exclude<T extends object, K extends keyof T>(
128
+ obj: T,
129
+ ...keys: K[]
130
+ ): Omit<T, K> {
131
+ const result = { ...obj };
132
+ for (const key of keys) {
133
+ delete result[key];
134
+ }
135
+ return result as Omit<T, K>;
136
+ }
137
+
138
+ /**
139
+ * Picks only specified fields from an object. Useful for creating DTOs from
140
+ * larger database entities.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * const userFromDb = await db.select().from(users).where(...).first();
145
+ * const userResponse = pick(userFromDb, 'id', 'name', 'email');
146
+ * ```
147
+ */
148
+ export function pick<T extends object, K extends keyof T>(
149
+ obj: T,
150
+ ...keys: K[]
151
+ ): Pick<T, K> {
152
+ const result = {} as Pick<T, K>;
153
+ for (const key of keys) {
154
+ result[key] = obj[key];
155
+ }
156
+ return result;
157
+ }
158
+
159
+ /**
160
+ * Maps field names from one DTO to another. Useful when your DTO has different
161
+ * field names than your entity (e.g., camelCase vs snake_case).
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * type ApiUser = { firstName: string; lastName: string };
166
+ * type DbUser = { first_name: string; last_name: string };
167
+ *
168
+ * const dbUser = mapFields(apiUser, {
169
+ * firstName: 'first_name',
170
+ * lastName: 'last_name'
171
+ * });
172
+ * ```
173
+ */
174
+ export function mapFields<T extends object>(
175
+ obj: T,
176
+ fieldMap: Partial<Record<keyof T, string>>
177
+ ): Record<string, unknown> {
178
+ const result: Record<string, unknown> = {};
179
+ const keys = Object.keys(fieldMap) as (keyof T)[];
180
+ for (const sourceKey of keys) {
181
+ const targetKey = fieldMap[sourceKey];
182
+ if (targetKey) {
183
+ result[targetKey] = obj[sourceKey];
184
+ }
185
+ }
186
+ // Copy any unmapped fields
187
+ for (const [key, value] of Object.entries(obj)) {
188
+ if (!(key in fieldMap)) {
189
+ result[key] = value;
190
+ }
191
+ }
192
+ return result;
193
+ }
package/src/index.ts CHANGED
@@ -1,67 +1,69 @@
1
- /**
2
- * MetalORM core exports.
3
- * Provides schema definition, query building, and ORM capabilities.
4
- */
5
- export * from './schema/table.js';
6
- export * from './schema/column-types.js';
7
- export * from './schema/relation.js';
8
- export * from './schema/types.js';
9
- export * from './query-builder/select.js';
10
- export * from './query-builder/select-helpers.js';
11
- export * from './query-builder/insert.js';
12
- export * from './query-builder/update.js';
13
- export * from './query-builder/delete.js';
14
- export * from './query/index.js';
15
- export * from './core/ast/expression.js';
16
- export * from './core/ast/window-functions.js';
17
- export * from './core/hydration/types.js';
18
- export * from './core/dialect/mysql/index.js';
19
- export * from './core/dialect/mssql/index.js';
20
- export * from './core/dialect/sqlite/index.js';
21
- export * from './core/dialect/postgres/index.js';
22
- export * from './core/ddl/schema-generator.js';
23
- export * from './core/ddl/schema-types.js';
24
- export * from './core/ddl/schema-diff.js';
25
- export * from './core/ddl/schema-introspect.js';
26
- export * from './core/ddl/introspect/registry.js';
27
- export * from './core/functions/text.js';
28
- export * from './core/functions/numeric.js';
29
- export * from './core/functions/datetime.js';
30
- export * from './core/functions/control-flow.js';
31
- export * from './core/functions/json.js';
32
- export * from './core/functions/array.js';
33
- export * from './orm/als.js';
34
- export * from './orm/hydration.js';
35
- export * from './codegen/typescript.js';
36
- export * from './orm/orm-session.js';
37
- export * from './orm/orm.js';
38
- export * from './orm/entity.js';
39
- export * from './orm/lazy-batch.js';
40
- export * from './orm/relations/has-many.js';
41
- export * from './orm/relations/belongs-to.js';
42
- export * from './orm/relations/many-to-many.js';
43
- export * from './orm/execute.js';
44
- export type { EntityContext } from './orm/entity-context.js';
45
- export type { PrimaryKey as EntityPrimaryKey } from './orm/entity-context.js';
46
- export * from './orm/execution-context.js';
47
- export * from './orm/hydration-context.js';
48
- export * from './orm/domain-event-bus.js';
49
- export * from './orm/runtime-types.js';
50
- export * from './orm/query-logger.js';
51
- export * from './orm/interceptor-pipeline.js';
52
- export * from './orm/jsonify.js';
1
+ /**
2
+ * MetalORM core exports.
3
+ * Provides schema definition, query building, and ORM capabilities.
4
+ */
5
+ export * from './schema/table.js';
6
+ export * from './schema/column-types.js';
7
+ export * from './schema/relation.js';
8
+ export * from './schema/types.js';
9
+ export * from './query-builder/select.js';
10
+ export * from './query-builder/select-helpers.js';
11
+ export * from './query-builder/insert.js';
12
+ export * from './query-builder/update.js';
13
+ export * from './query-builder/delete.js';
14
+ export * from './query/index.js';
15
+ export * from './core/ast/expression.js';
16
+ export * from './core/ast/window-functions.js';
17
+ export * from './core/hydration/types.js';
18
+ export * from './core/dialect/mysql/index.js';
19
+ export * from './core/dialect/mssql/index.js';
20
+ export * from './core/dialect/sqlite/index.js';
21
+ export * from './core/dialect/postgres/index.js';
22
+ export * from './core/ddl/schema-generator.js';
23
+ export * from './core/ddl/schema-types.js';
24
+ export * from './core/ddl/schema-diff.js';
25
+ export * from './core/ddl/schema-introspect.js';
26
+ export * from './core/ddl/introspect/registry.js';
27
+ export * from './core/functions/text.js';
28
+ export * from './core/functions/numeric.js';
29
+ export * from './core/functions/datetime.js';
30
+ export * from './core/functions/control-flow.js';
31
+ export * from './core/functions/json.js';
32
+ export * from './core/functions/array.js';
33
+ export * from './orm/als.js';
34
+ export * from './orm/hydration.js';
35
+ export * from './codegen/typescript.js';
36
+ export * from './orm/orm-session.js';
37
+ export * from './orm/orm.js';
38
+ export * from './orm/entity.js';
39
+ export * from './orm/lazy-batch.js';
40
+ export * from './orm/relations/has-many.js';
41
+ export * from './orm/relations/belongs-to.js';
42
+ export * from './orm/relations/many-to-many.js';
43
+ export * from './orm/execute.js';
44
+ export type { EntityContext } from './orm/entity-context.js';
45
+ export type { PrimaryKey as EntityPrimaryKey } from './orm/entity-context.js';
46
+ export * from './orm/execution-context.js';
47
+ export * from './orm/hydration-context.js';
48
+ export * from './orm/domain-event-bus.js';
49
+ export * from './orm/runtime-types.js';
50
+ export * from './orm/query-logger.js';
51
+ export * from './orm/interceptor-pipeline.js';
52
+ export * from './orm/jsonify.js';
53
53
  export * from './orm/save-graph-types.js';
54
54
  export * from './decorators/index.js';
55
- export * from './openapi/index.js';
56
-
57
- // NEW: execution abstraction + helpers
58
- export * from './core/execution/db-executor.js';
59
- export * from './core/execution/pooling/pool-types.js';
60
- export * from './core/execution/pooling/pool.js';
61
- export * from './core/execution/executors/postgres-executor.js';
62
- export * from './core/execution/executors/mysql-executor.js';
63
- export * from './core/execution/executors/sqlite-executor.js';
64
- export * from './core/execution/executors/mssql-executor.js';
65
-
66
- // NEW: first-class pooling integration
67
- export * from './orm/pooled-executor-factory.js';
55
+
56
+ // NEW: execution abstraction + helpers
57
+ export * from './core/execution/db-executor.js';
58
+ export * from './core/execution/pooling/pool-types.js';
59
+ export * from './core/execution/pooling/pool.js';
60
+ export * from './core/execution/executors/postgres-executor.js';
61
+ export * from './core/execution/executors/mysql-executor.js';
62
+ export * from './core/execution/executors/sqlite-executor.js';
63
+ export * from './core/execution/executors/mssql-executor.js';
64
+
65
+ // NEW: first-class pooling integration
66
+ export * from './orm/pooled-executor-factory.js';
67
+
68
+ // DTO module for REST API integration
69
+ export * from './dto/index.js';
@@ -198,13 +198,12 @@ export class UnitOfWork {
198
198
 
199
199
  const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
200
200
  let builder = new InsertQueryBuilder(tracked.table).values(payload as Record<string, ValueOperandInput>);
201
- if (this.dialect.supportsReturning()) {
202
- builder = builder.returning(...this.getReturningColumns(tracked.table));
203
- }
204
- const compiled = builder.compile(this.dialect);
205
- const results = await this.executeCompiled(compiled);
206
- this.applyReturningResults(tracked, results);
207
- this.applyInsertId(tracked, results);
201
+ if (this.dialect.supportsReturning()) {
202
+ builder = builder.returning(...this.getReturningColumns(tracked.table));
203
+ }
204
+ const compiled = builder.compile(this.dialect);
205
+ const results = await this.executeCompiled(compiled);
206
+ this.applyReturningResults(tracked, results);
208
207
 
209
208
  tracked.status = EntityStatus.Managed;
210
209
  tracked.original = this.createSnapshot(tracked.table, tracked.entity as Record<string, unknown>);
@@ -345,29 +344,18 @@ export class UnitOfWork {
345
344
  * @param tracked - The tracked entity
346
345
  * @param results - Query results
347
346
  */
348
- private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
349
- if (!this.dialect.supportsReturning()) return;
350
- const first = results[0];
351
- if (!first || first.values.length === 0) return;
347
+ private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
348
+ if (!this.dialect.supportsReturning()) return;
349
+ const first = results[0];
350
+ if (!first || first.values.length === 0) return;
352
351
 
353
352
  const row = first.values[0];
354
353
  for (let i = 0; i < first.columns.length; i++) {
355
354
  const columnName = this.normalizeColumnName(first.columns[i]);
356
355
  if (!(columnName in tracked.table.columns)) continue;
357
- (tracked.entity as Record<string, unknown>)[columnName] = row[i];
358
- }
359
- }
360
-
361
- private applyInsertId(tracked: TrackedEntity, results: QueryResult[]): void {
362
- if (this.dialect.supportsReturning()) return;
363
- if (tracked.pk != null) return;
364
- const pkName = findPrimaryKey(tracked.table);
365
- const pkColumn = tracked.table.columns[pkName];
366
- if (!pkColumn?.autoIncrement) return;
367
- const insertId = results.find(result => typeof result.insertId === 'number')?.insertId;
368
- if (insertId == null) return;
369
- (tracked.entity as Record<string, unknown>)[pkName] = insertId;
370
- }
356
+ (tracked.entity as Record<string, unknown>)[columnName] = row[i];
357
+ }
358
+ }
371
359
 
372
360
  /**
373
361
  * Normalizes a column name by removing quotes and table prefixes.