metal-orm 1.0.42 → 1.0.44

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 (122) hide show
  1. package/README.md +195 -37
  2. package/dist/index.cjs +1014 -538
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +1267 -371
  5. package/dist/index.d.ts +1267 -371
  6. package/dist/index.js +1012 -536
  7. package/dist/index.js.map +1 -1
  8. package/package.json +8 -2
  9. package/scripts/run-eslint.mjs +34 -0
  10. package/src/codegen/typescript.ts +32 -15
  11. package/src/core/ast/adapters.ts +8 -2
  12. package/src/core/ast/builders.ts +105 -76
  13. package/src/core/ast/expression-builders.ts +430 -392
  14. package/src/core/ast/expression-nodes.ts +14 -5
  15. package/src/core/ast/expression-visitor.ts +56 -14
  16. package/src/core/ast/helpers.ts +23 -0
  17. package/src/core/ast/join-node.ts +18 -2
  18. package/src/core/ast/query.ts +6 -6
  19. package/src/core/ast/window-functions.ts +10 -2
  20. package/src/core/ddl/dialects/base-schema-dialect.ts +37 -4
  21. package/src/core/ddl/dialects/index.ts +1 -0
  22. package/src/core/ddl/dialects/mssql-schema-dialect.ts +5 -0
  23. package/src/core/ddl/dialects/mysql-schema-dialect.ts +3 -0
  24. package/src/core/ddl/dialects/postgres-schema-dialect.ts +14 -1
  25. package/src/core/ddl/dialects/render-reference.test.ts +69 -0
  26. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +10 -0
  27. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  28. package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
  29. package/src/core/ddl/introspect/context.ts +6 -0
  30. package/src/core/ddl/introspect/functions/postgres.ts +13 -0
  31. package/src/core/ddl/introspect/mssql.ts +53 -8
  32. package/src/core/ddl/introspect/mysql.ts +32 -6
  33. package/src/core/ddl/introspect/postgres.ts +102 -34
  34. package/src/core/ddl/introspect/registry.ts +14 -0
  35. package/src/core/ddl/introspect/run-select.ts +19 -4
  36. package/src/core/ddl/introspect/sqlite.ts +78 -11
  37. package/src/core/ddl/introspect/types.ts +0 -1
  38. package/src/core/ddl/introspect/utils.ts +21 -3
  39. package/src/core/ddl/naming-strategy.ts +6 -0
  40. package/src/core/ddl/schema-dialect.ts +20 -6
  41. package/src/core/ddl/schema-diff.ts +22 -0
  42. package/src/core/ddl/schema-generator.ts +26 -12
  43. package/src/core/ddl/schema-plan-executor.ts +6 -0
  44. package/src/core/ddl/schema-types.ts +6 -0
  45. package/src/core/ddl/sql-writing.ts +4 -4
  46. package/src/core/dialect/abstract.ts +19 -7
  47. package/src/core/dialect/base/function-table-formatter.ts +3 -2
  48. package/src/core/dialect/base/join-compiler.ts +5 -3
  49. package/src/core/dialect/base/returning-strategy.ts +1 -0
  50. package/src/core/dialect/base/sql-dialect.ts +3 -3
  51. package/src/core/dialect/mssql/functions.ts +24 -25
  52. package/src/core/dialect/mssql/index.ts +1 -4
  53. package/src/core/dialect/mysql/functions.ts +0 -1
  54. package/src/core/dialect/postgres/functions.ts +33 -34
  55. package/src/core/dialect/postgres/index.ts +1 -0
  56. package/src/core/dialect/sqlite/functions.ts +18 -19
  57. package/src/core/dialect/sqlite/index.ts +2 -0
  58. package/src/core/execution/db-executor.ts +1 -1
  59. package/src/core/execution/executors/mysql-executor.ts +2 -2
  60. package/src/core/execution/executors/postgres-executor.ts +1 -1
  61. package/src/core/execution/pooling/pool.ts +12 -5
  62. package/src/core/functions/datetime.ts +58 -34
  63. package/src/core/functions/numeric.ts +96 -31
  64. package/src/core/functions/standard-strategy.ts +35 -0
  65. package/src/core/functions/text.ts +84 -23
  66. package/src/core/functions/types.ts +23 -8
  67. package/src/decorators/bootstrap.ts +42 -11
  68. package/src/decorators/column.ts +20 -11
  69. package/src/decorators/decorator-metadata.ts +30 -9
  70. package/src/decorators/entity.ts +29 -5
  71. package/src/decorators/index.ts +3 -0
  72. package/src/decorators/relations.ts +34 -11
  73. package/src/orm/als.ts +34 -9
  74. package/src/orm/entity-context.ts +62 -8
  75. package/src/orm/entity-meta.ts +8 -8
  76. package/src/orm/entity-metadata.ts +131 -16
  77. package/src/orm/entity.ts +28 -29
  78. package/src/orm/execute.ts +19 -4
  79. package/src/orm/hydration.ts +42 -39
  80. package/src/orm/identity-map.ts +1 -1
  81. package/src/orm/lazy-batch.ts +74 -104
  82. package/src/orm/orm-session.ts +24 -23
  83. package/src/orm/orm.ts +2 -5
  84. package/src/orm/relation-change-processor.ts +12 -11
  85. package/src/orm/relations/belongs-to.ts +11 -11
  86. package/src/orm/relations/has-many.ts +54 -10
  87. package/src/orm/relations/has-one.ts +8 -7
  88. package/src/orm/relations/many-to-many.ts +13 -13
  89. package/src/orm/runtime-types.ts +4 -4
  90. package/src/orm/save-graph.ts +31 -25
  91. package/src/orm/unit-of-work.ts +17 -17
  92. package/src/query/index.ts +74 -0
  93. package/src/query/target.ts +46 -0
  94. package/src/query-builder/delete-query-state.ts +30 -0
  95. package/src/query-builder/delete.ts +64 -18
  96. package/src/query-builder/hydration-manager.ts +52 -5
  97. package/src/query-builder/insert-query-state.ts +30 -0
  98. package/src/query-builder/insert.ts +58 -10
  99. package/src/query-builder/query-ast-service.ts +7 -2
  100. package/src/query-builder/query-resolution.ts +78 -0
  101. package/src/query-builder/raw-column-parser.ts +7 -1
  102. package/src/query-builder/relation-alias.ts +7 -0
  103. package/src/query-builder/relation-conditions.ts +61 -48
  104. package/src/query-builder/relation-service.ts +68 -63
  105. package/src/query-builder/relation-utils.ts +3 -0
  106. package/src/query-builder/select/cte-facet.ts +40 -0
  107. package/src/query-builder/select/from-facet.ts +80 -0
  108. package/src/query-builder/select/join-facet.ts +62 -0
  109. package/src/query-builder/select/predicate-facet.ts +103 -0
  110. package/src/query-builder/select/projection-facet.ts +69 -0
  111. package/src/query-builder/select/relation-facet.ts +81 -0
  112. package/src/query-builder/select/setop-facet.ts +36 -0
  113. package/src/query-builder/select-helpers.ts +15 -2
  114. package/src/query-builder/select-query-builder-deps.ts +19 -1
  115. package/src/query-builder/select-query-state.ts +2 -1
  116. package/src/query-builder/select.ts +795 -1163
  117. package/src/query-builder/update-query-state.ts +52 -0
  118. package/src/query-builder/update.ts +69 -18
  119. package/src/schema/column.ts +26 -26
  120. package/src/schema/table-guards.ts +31 -0
  121. package/src/schema/table.ts +47 -18
  122. package/src/schema/types.ts +22 -22
@@ -1,4 +1,10 @@
1
- import type { EntityInstance } from '../schema/types.js';
1
+ import type {
2
+ EntityInstance,
3
+ HasManyCollection,
4
+ HasOneReference,
5
+ BelongsToReference,
6
+ ManyToManyCollection
7
+ } from '../schema/types.js';
2
8
  import {
3
9
  RelationKinds,
4
10
  type BelongsToManyRelation,
@@ -19,12 +25,12 @@ export interface SaveGraphOptions {
19
25
  pruneMissing?: boolean;
20
26
  }
21
27
 
22
- type AnyEntity = Record<string, any>;
28
+ type AnyEntity = Record<string, unknown>;
23
29
 
24
30
  const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
25
31
 
26
- const pickColumns = (table: TableDef, payload: AnyEntity): Record<string, any> => {
27
- const columns: Record<string, any> = {};
32
+ const pickColumns = (table: TableDef, payload: AnyEntity): Record<string, unknown> => {
33
+ const columns: Record<string, unknown> = {};
28
34
  for (const key of Object.keys(table.columns)) {
29
35
  if (payload[key] !== undefined) {
30
36
  columns[key] = payload[key];
@@ -71,7 +77,7 @@ const isEntityInCollection = (items: AnyEntity[], pkName: string, entity: AnyEnt
71
77
  return items.some(item => toKey(item[pkName]) === toKey(entityPk));
72
78
  };
73
79
 
74
- const findInCollectionByPk = (items: AnyEntity[], pkName: string, pkValue: any): AnyEntity | undefined => {
80
+ const findInCollectionByPk = (items: AnyEntity[], pkName: string, pkValue: unknown): AnyEntity | undefined => {
75
81
  if (pkValue === undefined || pkValue === null) return undefined;
76
82
  return items.find(item => toKey(item[pkName]) === toKey(pkValue));
77
83
  };
@@ -85,12 +91,12 @@ const handleHasMany = async (
85
91
  options: SaveGraphOptions
86
92
  ): Promise<void> => {
87
93
  if (!Array.isArray(payload)) return;
88
- const collection = root[relationName];
94
+ const collection = root[relationName] as unknown as HasManyCollection<unknown>;
89
95
  await collection.load();
90
96
 
91
97
  const targetTable = relation.target;
92
98
  const targetPk = findPrimaryKey(targetTable);
93
- const existing = collection.getItems();
99
+ const existing = collection.getItems() as unknown as AnyEntity[];
94
100
  const seen = new Set<string>();
95
101
 
96
102
  for (const item of payload) {
@@ -103,10 +109,10 @@ const handleHasMany = async (
103
109
  (pkValue !== undefined && pkValue !== null ? session.getEntity(targetTable, pkValue) : undefined);
104
110
 
105
111
  const entity = current ?? ensureEntity(session, targetTable, asObj);
106
- assignColumns(targetTable, entity, asObj);
107
- await applyGraphToEntity(session, targetTable, entity, asObj, options);
112
+ assignColumns(targetTable, entity as AnyEntity, asObj);
113
+ await applyGraphToEntity(session, targetTable, entity as AnyEntity, asObj, options);
108
114
 
109
- if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
115
+ if (!isEntityInCollection(collection.getItems() as unknown as AnyEntity[], targetPk, entity as unknown as AnyEntity)) {
110
116
  collection.attach(entity);
111
117
  }
112
118
 
@@ -133,7 +139,7 @@ const handleHasOne = async (
133
139
  payload: unknown,
134
140
  options: SaveGraphOptions
135
141
  ): Promise<void> => {
136
- const ref = root[relationName];
142
+ const ref = root[relationName] as unknown as HasOneReference<unknown>;
137
143
  if (payload === undefined) return;
138
144
  if (payload === null) {
139
145
  ref.set(null);
@@ -143,13 +149,13 @@ const handleHasOne = async (
143
149
  if (typeof payload === 'number' || typeof payload === 'string') {
144
150
  const entity = ref.set({ [pk]: payload });
145
151
  if (entity) {
146
- await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
152
+ await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload }, options);
147
153
  }
148
154
  return;
149
155
  }
150
156
  const attached = ref.set(payload as AnyEntity);
151
157
  if (attached) {
152
- await applyGraphToEntity(session, relation.target, attached, payload as AnyEntity, options);
158
+ await applyGraphToEntity(session, relation.target, attached as AnyEntity, payload as AnyEntity, options);
153
159
  }
154
160
  };
155
161
 
@@ -161,7 +167,7 @@ const handleBelongsTo = async (
161
167
  payload: unknown,
162
168
  options: SaveGraphOptions
163
169
  ): Promise<void> => {
164
- const ref = root[relationName];
170
+ const ref = root[relationName] as unknown as BelongsToReference<unknown>;
165
171
  if (payload === undefined) return;
166
172
  if (payload === null) {
167
173
  ref.set(null);
@@ -171,13 +177,13 @@ const handleBelongsTo = async (
171
177
  if (typeof payload === 'number' || typeof payload === 'string') {
172
178
  const entity = ref.set({ [pk]: payload });
173
179
  if (entity) {
174
- await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
180
+ await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload }, options);
175
181
  }
176
182
  return;
177
183
  }
178
184
  const attached = ref.set(payload as AnyEntity);
179
185
  if (attached) {
180
- await applyGraphToEntity(session, relation.target, attached, payload as AnyEntity, options);
186
+ await applyGraphToEntity(session, relation.target, attached as AnyEntity, payload as AnyEntity, options);
181
187
  }
182
188
  };
183
189
 
@@ -190,7 +196,7 @@ const handleBelongsToMany = async (
190
196
  options: SaveGraphOptions
191
197
  ): Promise<void> => {
192
198
  if (!Array.isArray(payload)) return;
193
- const collection = root[relationName];
199
+ const collection = root[relationName] as unknown as ManyToManyCollection<unknown>;
194
200
  await collection.load();
195
201
 
196
202
  const targetTable = relation.target;
@@ -212,10 +218,10 @@ const handleBelongsToMany = async (
212
218
  ? session.getEntity(targetTable, pkValue) ?? ensureEntity(session, targetTable, asObj)
213
219
  : ensureEntity(session, targetTable, asObj);
214
220
 
215
- assignColumns(targetTable, entity, asObj);
216
- await applyGraphToEntity(session, targetTable, entity, asObj, options);
221
+ assignColumns(targetTable, entity as AnyEntity, asObj);
222
+ await applyGraphToEntity(session, targetTable, entity as AnyEntity, asObj, options);
217
223
 
218
- if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
224
+ if (!isEntityInCollection(collection.getItems() as unknown as AnyEntity[], targetPk, entity as unknown as AnyEntity)) {
219
225
  collection.attach(entity);
220
226
  }
221
227
 
@@ -225,7 +231,7 @@ const handleBelongsToMany = async (
225
231
  }
226
232
 
227
233
  if (options.pruneMissing) {
228
- for (const item of [...collection.getItems()]) {
234
+ for (const item of [...collection.getItems()] as unknown as AnyEntity[]) {
229
235
  const pkValue = item[targetPk];
230
236
  if (pkValue !== undefined && pkValue !== null && !seen.has(toKey(pkValue))) {
231
237
  collection.detach(item);
@@ -272,7 +278,7 @@ const applyGraphToEntity = async (
272
278
 
273
279
  export const saveGraph = async <TTable extends TableDef>(
274
280
  session: OrmSession,
275
- entityClass: EntityConstructor<any>,
281
+ entityClass: EntityConstructor,
276
282
  payload: AnyEntity,
277
283
  options: SaveGraphOptions = {}
278
284
  ): Promise<EntityInstance<TTable>> => {
@@ -282,11 +288,11 @@ export const saveGraph = async <TTable extends TableDef>(
282
288
  }
283
289
 
284
290
  const root = ensureEntity<TTable>(session, table as TTable, payload);
285
- await applyGraphToEntity(session, table, root, payload, options);
291
+ await applyGraphToEntity(session, table, root as AnyEntity, payload, options);
286
292
  return root;
287
293
  };
288
294
 
289
- export const saveGraphInternal = async <TCtor extends EntityConstructor<any>>(
295
+ export const saveGraphInternal = async <TCtor extends EntityConstructor>(
290
296
  session: OrmSession,
291
297
  entityClass: TCtor,
292
298
  payload: AnyEntity,
@@ -298,6 +304,6 @@ export const saveGraphInternal = async <TCtor extends EntityConstructor<any>>(
298
304
  }
299
305
 
300
306
  const root = ensureEntity(session, table, payload);
301
- await applyGraphToEntity(session, table, root, payload, options);
307
+ await applyGraphToEntity(session, table, root as AnyEntity, payload, options);
302
308
  return root as unknown as InstanceType<TCtor>;
303
309
  };
@@ -14,7 +14,7 @@ import type { TrackedEntity } from './runtime-types.js';
14
14
  * Unit of Work pattern implementation for tracking entity changes.
15
15
  */
16
16
  export class UnitOfWork {
17
- private readonly trackedEntities = new Map<any, TrackedEntity>();
17
+ private readonly trackedEntities = new Map<unknown, TrackedEntity>();
18
18
 
19
19
  /**
20
20
  * Creates a new UnitOfWork instance.
@@ -51,7 +51,7 @@ export class UnitOfWork {
51
51
  * @param pk - The primary key value
52
52
  * @returns The entity or undefined if not found
53
53
  */
54
- getEntity(table: TableDef, pk: string | number): any | undefined {
54
+ getEntity(table: TableDef, pk: string | number): unknown | undefined {
55
55
  return this.identityMap.getEntity(table, pk);
56
56
  }
57
57
 
@@ -69,7 +69,7 @@ export class UnitOfWork {
69
69
  * @param entity - The entity to find
70
70
  * @returns The tracked entity or undefined if not found
71
71
  */
72
- findTracked(entity: any): TrackedEntity | undefined {
72
+ findTracked(entity: unknown): TrackedEntity | undefined {
73
73
  return this.trackedEntities.get(entity);
74
74
  }
75
75
 
@@ -79,7 +79,7 @@ export class UnitOfWork {
79
79
  * @param pk - The primary key value
80
80
  * @param entity - The entity instance
81
81
  */
82
- setEntity(table: TableDef, pk: string | number, entity: any): void {
82
+ setEntity(table: TableDef, pk: string | number, entity: unknown): void {
83
83
  if (pk === null || pk === undefined) return;
84
84
  let tracked = this.trackedEntities.get(entity);
85
85
  if (!tracked) {
@@ -88,7 +88,7 @@ export class UnitOfWork {
88
88
  entity,
89
89
  pk,
90
90
  status: EntityStatus.Managed,
91
- original: this.createSnapshot(table, entity)
91
+ original: this.createSnapshot(table, entity as Record<string, unknown>)
92
92
  };
93
93
  this.trackedEntities.set(entity, tracked);
94
94
  } else {
@@ -104,7 +104,7 @@ export class UnitOfWork {
104
104
  * @param entity - The entity instance
105
105
  * @param pk - Optional primary key value
106
106
  */
107
- trackNew(table: TableDef, entity: any, pk?: string | number): void {
107
+ trackNew(table: TableDef, entity: unknown, pk?: string | number): void {
108
108
  const tracked: TrackedEntity = {
109
109
  table,
110
110
  entity,
@@ -124,13 +124,13 @@ export class UnitOfWork {
124
124
  * @param pk - The primary key value
125
125
  * @param entity - The entity instance
126
126
  */
127
- trackManaged(table: TableDef, pk: string | number, entity: any): void {
127
+ trackManaged(table: TableDef, pk: string | number, entity: unknown): void {
128
128
  const tracked: TrackedEntity = {
129
129
  table,
130
130
  entity,
131
131
  pk,
132
132
  status: EntityStatus.Managed,
133
- original: this.createSnapshot(table, entity)
133
+ original: this.createSnapshot(table, entity as Record<string, unknown>)
134
134
  };
135
135
  this.trackedEntities.set(entity, tracked);
136
136
  this.registerIdentity(tracked);
@@ -140,7 +140,7 @@ export class UnitOfWork {
140
140
  * Marks an entity as dirty (modified).
141
141
  * @param entity - The entity to mark as dirty
142
142
  */
143
- markDirty(entity: any): void {
143
+ markDirty(entity: unknown): void {
144
144
  const tracked = this.trackedEntities.get(entity);
145
145
  if (!tracked) return;
146
146
  if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
@@ -151,7 +151,7 @@ export class UnitOfWork {
151
151
  * Marks an entity as removed.
152
152
  * @param entity - The entity to mark as removed
153
153
  */
154
- markRemoved(entity: any): void {
154
+ markRemoved(entity: unknown): void {
155
155
  const tracked = this.trackedEntities.get(entity);
156
156
  if (!tracked) return;
157
157
  tracked.status = EntityStatus.Removed;
@@ -194,7 +194,7 @@ export class UnitOfWork {
194
194
  private async flushInsert(tracked: TrackedEntity): Promise<void> {
195
195
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
196
196
 
197
- const payload = this.extractColumns(tracked.table, tracked.entity);
197
+ const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
198
198
  let builder = new InsertQueryBuilder(tracked.table).values(payload);
199
199
  if (this.dialect.supportsReturning()) {
200
200
  builder = builder.returning(...this.getReturningColumns(tracked.table));
@@ -204,7 +204,7 @@ export class UnitOfWork {
204
204
  this.applyReturningResults(tracked, results);
205
205
 
206
206
  tracked.status = EntityStatus.Managed;
207
- tracked.original = this.createSnapshot(tracked.table, tracked.entity);
207
+ tracked.original = this.createSnapshot(tracked.table, tracked.entity as Record<string, unknown>);
208
208
  tracked.pk = this.getPrimaryKeyValue(tracked);
209
209
  this.registerIdentity(tracked);
210
210
 
@@ -241,7 +241,7 @@ export class UnitOfWork {
241
241
  this.applyReturningResults(tracked, results);
242
242
 
243
243
  tracked.status = EntityStatus.Managed;
244
- tracked.original = this.createSnapshot(tracked.table, tracked.entity);
244
+ tracked.original = this.createSnapshot(tracked.table, tracked.entity as Record<string, unknown>);
245
245
  this.registerIdentity(tracked);
246
246
 
247
247
  await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
@@ -279,7 +279,7 @@ export class UnitOfWork {
279
279
  tracked: TrackedEntity
280
280
  ): Promise<void> {
281
281
  if (!hook) return;
282
- await hook(this.hookContext() as any, tracked.entity);
282
+ await hook(this.hookContext(), tracked.entity);
283
283
  }
284
284
 
285
285
  /**
@@ -305,7 +305,7 @@ export class UnitOfWork {
305
305
  * @param entity - The entity instance
306
306
  * @returns Object with column values
307
307
  */
308
- private extractColumns(table: TableDef, entity: any): Record<string, unknown> {
308
+ private extractColumns(table: TableDef, entity: Record<string, unknown>): Record<string, unknown> {
309
309
  const payload: Record<string, unknown> = {};
310
310
  for (const column of Object.keys(table.columns)) {
311
311
  if (entity[column] === undefined) continue;
@@ -381,8 +381,8 @@ export class UnitOfWork {
381
381
  * @param entity - The entity instance
382
382
  * @returns Object with entity state
383
383
  */
384
- private createSnapshot(table: TableDef, entity: any): Record<string, any> {
385
- const snapshot: Record<string, any> = {};
384
+ private createSnapshot(table: TableDef, entity: Record<string, unknown>): Record<string, unknown> {
385
+ const snapshot: Record<string, unknown> = {};
386
386
  for (const column of Object.keys(table.columns)) {
387
387
  snapshot[column] = entity[column];
388
388
  }
@@ -0,0 +1,74 @@
1
+ import { TableDef } from '../schema/table.js';
2
+ import { SelectQueryBuilder } from '../query-builder/select.js';
3
+ import { InsertQueryBuilder } from '../query-builder/insert.js';
4
+ import { UpdateQueryBuilder } from '../query-builder/update.js';
5
+ import { DeleteQueryBuilder } from '../query-builder/delete.js';
6
+ import { QueryTarget, resolveTable } from './target.js';
7
+
8
+ /**
9
+ * Creates a SELECT query builder for the specified table or entity.
10
+ *
11
+ * @template TTable - The table definition type
12
+ * @param target - The table definition or entity constructor to query from
13
+ * @returns A new SelectQueryBuilder instance for building SELECT queries
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const query = selectFrom(UserTable).select('id', 'name');
18
+ * ```
19
+ */
20
+ export const selectFrom = <TTable extends TableDef>(target: QueryTarget<TTable>): SelectQueryBuilder<unknown, TTable> => {
21
+ const table = resolveTable(target);
22
+ return new SelectQueryBuilder(table);
23
+ };
24
+
25
+ /**
26
+ * Creates an INSERT query builder for the specified table or entity.
27
+ *
28
+ * @template TTable - The table definition type
29
+ * @param target - The table definition or entity constructor to insert into
30
+ * @returns A new InsertQueryBuilder instance for building INSERT queries
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const query = insertInto(UserTable).values({ name: 'John', email: 'john@example.com' });
35
+ * ```
36
+ */
37
+ export const insertInto = <TTable extends TableDef>(target: QueryTarget<TTable>): InsertQueryBuilder<unknown> => {
38
+ const table = resolveTable(target);
39
+ return new InsertQueryBuilder(table);
40
+ };
41
+
42
+ /**
43
+ * Creates an UPDATE query builder for the specified table or entity.
44
+ *
45
+ * @template TTable - The table definition type
46
+ * @param target - The table definition or entity constructor to update
47
+ * @returns A new UpdateQueryBuilder instance for building UPDATE queries
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const query = update(UserTable).set({ name: 'Jane' }).where(eq(UserTable.id, 1));
52
+ * ```
53
+ */
54
+ export const update = <TTable extends TableDef>(target: QueryTarget<TTable>): UpdateQueryBuilder<unknown> => {
55
+ const table = resolveTable(target);
56
+ return new UpdateQueryBuilder(table);
57
+ };
58
+
59
+ /**
60
+ * Creates a DELETE query builder for the specified table or entity.
61
+ *
62
+ * @template TTable - The table definition type
63
+ * @param target - The table definition or entity constructor to delete from
64
+ * @returns A new DeleteQueryBuilder instance for building DELETE queries
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const query = deleteFrom(UserTable).where(eq(UserTable.id, 1));
69
+ * ```
70
+ */
71
+ export const deleteFrom = <TTable extends TableDef>(target: QueryTarget<TTable>): DeleteQueryBuilder<unknown> => {
72
+ const table = resolveTable(target);
73
+ return new DeleteQueryBuilder(table);
74
+ };
@@ -0,0 +1,46 @@
1
+ import { TableDef } from '../schema/table.js';
2
+ import { isTableDef } from '../schema/table-guards.js';
3
+ import { EntityConstructor } from '../orm/entity-metadata.js';
4
+ import { getTableDefFromEntity } from '../decorators/bootstrap.js';
5
+
6
+ /**
7
+ * Represents a target for query operations, which can be either a table definition
8
+ * or an entity constructor. This type allows flexible targeting of database tables
9
+ * through either direct table definitions or entity classes decorated with ORM metadata.
10
+ *
11
+ * @template TTable - The table definition type, defaults to TableDef
12
+ */
13
+ export type QueryTarget<TTable extends TableDef = TableDef> = TTable | EntityConstructor;
14
+
15
+ const resolveEntityTarget = <TTable extends TableDef>(ctor: EntityConstructor): TTable => {
16
+ const table = getTableDefFromEntity(ctor);
17
+ if (!table) {
18
+ throw new Error(`Entity '${ctor.name}' is not registered with decorators`);
19
+ }
20
+ return table as TTable;
21
+ };
22
+
23
+ /**
24
+ * Resolves a QueryTarget to its corresponding table definition.
25
+ *
26
+ * If the target is already a TableDef, it returns it directly.
27
+ * If the target is an EntityConstructor, it retrieves the associated table definition
28
+ * from the entity's metadata.
29
+ *
30
+ * @template TTable - The table definition type
31
+ * @param target - The query target to resolve
32
+ * @returns The resolved table definition
33
+ * @throws Error if the entity constructor is not registered with decorators
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const table = resolveTable(UserTable); // Returns UserTable directly
38
+ * const table2 = resolveTable(UserEntity); // Returns table def from UserEntity metadata
39
+ * ```
40
+ */
41
+ export const resolveTable = <TTable extends TableDef>(target: QueryTarget<TTable>): TTable => {
42
+ if (isTableDef(target)) {
43
+ return target as TTable;
44
+ }
45
+ return resolveEntityTarget(target as EntityConstructor);
46
+ };
@@ -14,6 +14,11 @@ export class DeleteQueryState {
14
14
  public readonly table: TableDef;
15
15
  public readonly ast: DeleteQueryNode;
16
16
 
17
+ /**
18
+ * Creates a new DeleteQueryState instance
19
+ * @param table - The table definition for the DELETE query
20
+ * @param ast - Optional initial AST node, defaults to a basic DELETE query
21
+ */
17
22
  constructor(table: TableDef, ast?: DeleteQueryNode) {
18
23
  this.table = table;
19
24
  this.ast = ast ?? {
@@ -27,6 +32,11 @@ export class DeleteQueryState {
27
32
  return new DeleteQueryState(this.table, nextAst);
28
33
  }
29
34
 
35
+ /**
36
+ * Adds a WHERE clause to the DELETE query
37
+ * @param expr - The expression to use as the WHERE condition
38
+ * @returns A new DeleteQueryState with the WHERE clause added
39
+ */
30
40
  withWhere(expr: ExpressionNode): DeleteQueryState {
31
41
  return this.clone({
32
42
  ...this.ast,
@@ -34,6 +44,11 @@ export class DeleteQueryState {
34
44
  });
35
45
  }
36
46
 
47
+ /**
48
+ * Adds a RETURNING clause to the DELETE query
49
+ * @param columns - The columns to return after deletion
50
+ * @returns A new DeleteQueryState with the RETURNING clause added
51
+ */
37
52
  withReturning(columns: ColumnNode[]): DeleteQueryState {
38
53
  return this.clone({
39
54
  ...this.ast,
@@ -41,6 +56,11 @@ export class DeleteQueryState {
41
56
  });
42
57
  }
43
58
 
59
+ /**
60
+ * Adds a USING clause to the DELETE query
61
+ * @param source - The table source to use in the USING clause
62
+ * @returns A new DeleteQueryState with the USING clause added
63
+ */
44
64
  withUsing(source: TableSourceNode): DeleteQueryState {
45
65
  return this.clone({
46
66
  ...this.ast,
@@ -48,6 +68,11 @@ export class DeleteQueryState {
48
68
  });
49
69
  }
50
70
 
71
+ /**
72
+ * Adds a JOIN clause to the DELETE query
73
+ * @param join - The join node to add
74
+ * @returns A new DeleteQueryState with the JOIN clause added
75
+ */
51
76
  withJoin(join: JoinNode): DeleteQueryState {
52
77
  return this.clone({
53
78
  ...this.ast,
@@ -55,6 +80,11 @@ export class DeleteQueryState {
55
80
  });
56
81
  }
57
82
 
83
+ /**
84
+ * Sets an alias for the table in the DELETE query
85
+ * @param alias - The alias to assign to the table
86
+ * @returns A new DeleteQueryState with the table alias set
87
+ */
58
88
  withTableAlias(alias: string): DeleteQueryState {
59
89
  return this.clone({
60
90
  ...this.ast,
@@ -2,12 +2,14 @@ import { TableDef } from '../schema/table.js';
2
2
  import { ColumnDef } from '../schema/column.js';
3
3
  import { ColumnNode, ExpressionNode } from '../core/ast/expression.js';
4
4
  import { JOIN_KINDS, JoinKind } from '../core/sql/sql.js';
5
- import { CompiledQuery, DeleteCompiler, Dialect } from '../core/dialect/abstract.js';
5
+ import { CompiledQuery, Dialect } from '../core/dialect/abstract.js';
6
6
  import { DialectKey, resolveDialectInput } from '../core/dialect/dialect-factory.js';
7
7
  import { TableSourceNode, DeleteQueryNode } from '../core/ast/query.js';
8
8
  import { DeleteQueryState } from './delete-query-state.js';
9
9
  import { createJoinNode } from '../core/ast/join-node.js';
10
10
  import { buildColumnNode } from '../core/ast/builders.js';
11
+ import { OrmSession } from '../orm/orm-session.js';
12
+ import { QueryResult } from '../core/execution/db-executor.js';
11
13
 
12
14
  type DeleteDialectInput = Dialect | DialectKey;
13
15
 
@@ -18,6 +20,11 @@ export class DeleteQueryBuilder<T> {
18
20
  private readonly table: TableDef;
19
21
  private readonly state: DeleteQueryState;
20
22
 
23
+ /**
24
+ * Creates a new DeleteQueryBuilder instance
25
+ * @param table - The table definition for the DELETE query
26
+ * @param state - Optional initial query state, defaults to a new DeleteQueryState
27
+ */
21
28
  constructor(table: TableDef, state?: DeleteQueryState) {
22
29
  this.table = table;
23
30
  this.state = state ?? new DeleteQueryState(table);
@@ -27,18 +34,41 @@ export class DeleteQueryBuilder<T> {
27
34
  return new DeleteQueryBuilder(this.table, state);
28
35
  }
29
36
 
37
+ /**
38
+ * Adds a WHERE clause to the DELETE query
39
+ * @param expr - The expression to use as the WHERE condition
40
+ * @returns A new DeleteQueryBuilder with the WHERE clause added
41
+ */
30
42
  where(expr: ExpressionNode): DeleteQueryBuilder<T> {
31
43
  return this.clone(this.state.withWhere(expr));
32
44
  }
33
45
 
46
+ /**
47
+ * Sets an alias for the table in the DELETE query
48
+ * @param alias - The alias to assign to the table
49
+ * @returns A new DeleteQueryBuilder with the table alias set
50
+ */
34
51
  as(alias: string): DeleteQueryBuilder<T> {
35
52
  return this.clone(this.state.withTableAlias(alias));
36
53
  }
37
54
 
55
+ /**
56
+ * Adds a USING clause to the DELETE query
57
+ * @param source - The table source to use in the USING clause
58
+ * @returns A new DeleteQueryBuilder with the USING clause added
59
+ */
38
60
  using(source: TableDef | TableSourceNode): DeleteQueryBuilder<T> {
39
61
  return this.clone(this.state.withUsing(this.resolveTableSource(source)));
40
62
  }
41
63
 
64
+ /**
65
+ * Adds a JOIN clause to the DELETE query
66
+ * @param table - The table to join with
67
+ * @param condition - The join condition expression
68
+ * @param kind - The type of join (defaults to INNER)
69
+ * @param relationName - Optional name for the relation
70
+ * @returns A new DeleteQueryBuilder with the JOIN clause added
71
+ */
42
72
  join(
43
73
  table: TableDef | TableSourceNode | string,
44
74
  condition: ExpressionNode,
@@ -50,6 +80,11 @@ export class DeleteQueryBuilder<T> {
50
80
  return this.clone(this.state.withJoin(joinNode));
51
81
  }
52
82
 
83
+ /**
84
+ * Adds a RETURNING clause to the DELETE query
85
+ * @param columns - The columns to return after deletion
86
+ * @returns A new DeleteQueryBuilder with the RETURNING clause added
87
+ */
53
88
  returning(...columns: (ColumnDef | ColumnNode)[]): DeleteQueryBuilder<T> {
54
89
  if (!columns.length) return this;
55
90
  const nodes = columns.map(column => buildColumnNode(this.table, column));
@@ -68,28 +103,39 @@ export class DeleteQueryBuilder<T> {
68
103
  return this.resolveTableSource(table);
69
104
  }
70
105
 
71
- // Existing compiler-based compile stays, but we add a new overload.
72
-
73
- // 1) Keep the old behavior (used internally / tests, if any):
74
- compile(compiler: DeleteCompiler): CompiledQuery;
75
- // 2) New ergonomic overload:
76
- compile(dialect: DeleteDialectInput): CompiledQuery;
77
-
78
- compile(arg: DeleteCompiler | DeleteDialectInput): CompiledQuery {
79
- if (typeof (arg as any).compileDelete === 'function') {
80
- // DeleteCompiler path – old behavior
81
- return (arg as DeleteCompiler).compileDelete(this.state.ast);
82
- }
106
+ /**
107
+ * Compiles the DELETE query for the specified dialect
108
+ * @param dialect - The SQL dialect to compile for
109
+ * @returns The compiled query with SQL and parameters
110
+ */
111
+ compile(dialect: DeleteDialectInput): CompiledQuery {
112
+ const resolved = resolveDialectInput(dialect);
113
+ return resolved.compileDelete(this.state.ast);
114
+ }
83
115
 
84
- // Dialect | string path – new behavior
85
- const dialect = resolveDialectInput(arg as DeleteDialectInput);
86
- return dialect.compileDelete(this.state.ast);
116
+ /**
117
+ * Returns the SQL string for the DELETE query
118
+ * @param dialect - The SQL dialect to generate SQL for
119
+ * @returns The SQL string representation of the query
120
+ */
121
+ toSql(dialect: DeleteDialectInput): string {
122
+ return this.compile(dialect).sql;
87
123
  }
88
124
 
89
- toSql(arg: DeleteCompiler | DeleteDialectInput): string {
90
- return this.compile(arg as any).sql;
125
+ /**
126
+ * Executes the DELETE query using the provided session
127
+ * @param session - The ORM session to execute the query with
128
+ * @returns A promise that resolves to the query results
129
+ */
130
+ async execute(session: OrmSession): Promise<QueryResult[]> {
131
+ const compiled = this.compile(session.dialect);
132
+ return session.executor.executeSql(compiled.sql, compiled.params);
91
133
  }
92
134
 
135
+ /**
136
+ * Returns the Abstract Syntax Tree (AST) representation of the query
137
+ * @returns The AST node for the DELETE query
138
+ */
93
139
  getAST(): DeleteQueryNode {
94
140
  return this.state.ast;
95
141
  }