metal-orm 1.0.41 → 1.0.43
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 +74 -20
- package/dist/index.cjs +180 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -96
- package/dist/index.d.ts +142 -96
- package/dist/index.js +177 -74
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/scripts/run-eslint.mjs +34 -0
- package/src/codegen/typescript.ts +32 -15
- package/src/core/ast/builders.ts +7 -2
- package/src/core/ast/expression-builders.ts +0 -2
- package/src/core/ast/expression-nodes.ts +14 -5
- package/src/core/ast/expression-visitor.ts +11 -8
- package/src/core/ast/expression.ts +2 -2
- package/src/core/ast/join-node.ts +1 -1
- package/src/core/ast/query.ts +6 -6
- package/src/core/ast/window-functions.ts +10 -2
- package/src/core/ddl/dialects/base-schema-dialect.ts +30 -3
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +4 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +2 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +13 -1
- package/src/core/ddl/dialects/render-reference.test.ts +69 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
- package/src/core/ddl/introspect/mssql.ts +42 -8
- package/src/core/ddl/introspect/mysql.ts +30 -6
- package/src/core/ddl/introspect/postgres.ts +88 -34
- package/src/core/ddl/introspect/run-select.ts +6 -4
- package/src/core/ddl/introspect/sqlite.ts +56 -11
- package/src/core/ddl/introspect/types.ts +0 -1
- package/src/core/ddl/introspect/utils.ts +3 -3
- package/src/core/ddl/schema-dialect.ts +1 -0
- package/src/core/ddl/schema-generator.ts +4 -12
- package/src/core/ddl/sql-writing.ts +4 -4
- package/src/core/dialect/abstract.ts +18 -6
- package/src/core/dialect/base/function-table-formatter.ts +3 -2
- package/src/core/dialect/base/join-compiler.ts +5 -3
- package/src/core/dialect/base/returning-strategy.ts +1 -0
- package/src/core/dialect/base/sql-dialect.ts +3 -3
- package/src/core/dialect/mssql/functions.ts +24 -25
- package/src/core/dialect/mssql/index.ts +1 -4
- package/src/core/dialect/mysql/functions.ts +0 -1
- package/src/core/dialect/postgres/functions.ts +33 -34
- package/src/core/dialect/postgres/index.ts +1 -0
- package/src/core/dialect/sqlite/functions.ts +18 -19
- package/src/core/dialect/sqlite/index.ts +2 -0
- package/src/core/execution/db-executor.ts +1 -1
- package/src/core/execution/executors/mysql-executor.ts +2 -2
- package/src/core/execution/executors/postgres-executor.ts +1 -1
- package/src/core/execution/pooling/pool.ts +2 -0
- package/src/core/functions/datetime.ts +1 -1
- package/src/core/functions/numeric.ts +1 -1
- package/src/core/functions/text.ts +1 -1
- package/src/decorators/bootstrap.ts +27 -8
- package/src/decorators/column.ts +3 -11
- package/src/decorators/decorator-metadata.ts +3 -9
- package/src/decorators/entity.ts +21 -5
- package/src/decorators/relations.ts +2 -11
- package/src/orm/entity-context.ts +8 -8
- package/src/orm/entity-meta.ts +8 -8
- package/src/orm/entity-metadata.ts +11 -9
- package/src/orm/entity.ts +28 -29
- package/src/orm/execute.ts +4 -4
- package/src/orm/hydration.ts +42 -39
- package/src/orm/identity-map.ts +1 -1
- package/src/orm/lazy-batch.ts +9 -9
- package/src/orm/orm-session.ts +24 -23
- package/src/orm/orm.ts +2 -5
- package/src/orm/relation-change-processor.ts +12 -11
- package/src/orm/relations/belongs-to.ts +11 -11
- package/src/orm/relations/has-many.ts +10 -10
- package/src/orm/relations/has-one.ts +8 -7
- package/src/orm/relations/many-to-many.ts +13 -13
- package/src/orm/runtime-types.ts +4 -4
- package/src/orm/save-graph.ts +31 -25
- package/src/orm/unit-of-work.ts +17 -17
- package/src/query-builder/delete.ts +4 -3
- package/src/query-builder/hydration-manager.ts +6 -5
- package/src/query-builder/insert.ts +12 -8
- package/src/query-builder/query-ast-service.ts +2 -2
- package/src/query-builder/raw-column-parser.ts +2 -1
- package/src/query-builder/select-helpers.ts +2 -2
- package/src/query-builder/select.ts +31 -31
- package/src/query-builder/update.ts +4 -3
- package/src/schema/column.ts +26 -26
- package/src/schema/table.ts +239 -115
- package/src/schema/types.ts +22 -22
package/src/orm/save-graph.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
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,
|
|
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,
|
|
27
|
-
const columns: Record<string,
|
|
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:
|
|
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
|
|
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
|
|
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
|
};
|
package/src/orm/unit-of-work.ts
CHANGED
|
@@ -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<
|
|
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):
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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()
|
|
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:
|
|
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:
|
|
385
|
-
const snapshot: Record<string,
|
|
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
|
}
|
|
@@ -76,9 +76,10 @@ export class DeleteQueryBuilder<T> {
|
|
|
76
76
|
compile(dialect: DeleteDialectInput): CompiledQuery;
|
|
77
77
|
|
|
78
78
|
compile(arg: DeleteCompiler | DeleteDialectInput): CompiledQuery {
|
|
79
|
-
|
|
79
|
+
const candidate = arg as { compileDelete?: (ast: DeleteQueryNode) => CompiledQuery };
|
|
80
|
+
if (typeof candidate.compileDelete === 'function') {
|
|
80
81
|
// DeleteCompiler path – old behavior
|
|
81
|
-
return
|
|
82
|
+
return candidate.compileDelete(this.state.ast);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// Dialect | string path – new behavior
|
|
@@ -87,7 +88,7 @@ export class DeleteQueryBuilder<T> {
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
toSql(arg: DeleteCompiler | DeleteDialectInput): string {
|
|
90
|
-
return this.compile(arg as
|
|
91
|
+
return this.compile(arg as DeleteCompiler).sql;
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
getAST(): DeleteQueryNode {
|
|
@@ -20,7 +20,7 @@ export class HydrationManager {
|
|
|
20
20
|
constructor(
|
|
21
21
|
private readonly table: TableDef,
|
|
22
22
|
private readonly planner: HydrationPlanner
|
|
23
|
-
) {}
|
|
23
|
+
) { }
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Creates a new HydrationManager with updated planner
|
|
@@ -219,7 +219,8 @@ export class HydrationManager {
|
|
|
219
219
|
private getProjectionNames(columns: ProjectionNode[]): string[] | undefined {
|
|
220
220
|
const names: string[] = [];
|
|
221
221
|
for (const col of columns) {
|
|
222
|
-
const
|
|
222
|
+
const node = col as { alias?: string; name?: string };
|
|
223
|
+
const alias = node.alias ?? node.name;
|
|
223
224
|
if (!alias) return undefined;
|
|
224
225
|
names.push(alias);
|
|
225
226
|
}
|
|
@@ -267,7 +268,7 @@ export class HydrationManager {
|
|
|
267
268
|
baseAlias: string,
|
|
268
269
|
availableColumns: Set<string>
|
|
269
270
|
): OrderByNode['term'] | null {
|
|
270
|
-
if (
|
|
271
|
+
if (term.type === 'Column') {
|
|
271
272
|
const col = term as ColumnNode;
|
|
272
273
|
if (col.table !== plan.rootTable) return null;
|
|
273
274
|
const alias = projectionAliases.get(`${col.table}.${col.name}`) ?? col.name;
|
|
@@ -275,8 +276,8 @@ export class HydrationManager {
|
|
|
275
276
|
return { type: 'Column', table: baseAlias, name: alias };
|
|
276
277
|
}
|
|
277
278
|
|
|
278
|
-
if (
|
|
279
|
-
const aliasName =
|
|
279
|
+
if (term.type === 'AliasRef') {
|
|
280
|
+
const aliasName = term.name;
|
|
280
281
|
if (!availableColumns.has(aliasName)) return null;
|
|
281
282
|
return { type: 'Column', table: baseAlias, name: aliasName };
|
|
282
283
|
}
|
|
@@ -37,8 +37,8 @@ export class InsertQueryBuilder<T> {
|
|
|
37
37
|
return this.clone(this.state.withColumns(this.resolveColumnNodes(columns)));
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
fromSelect(
|
|
41
|
-
query: SelectQueryNode | SelectQueryBuilder<
|
|
40
|
+
fromSelect<TSource extends TableDef>(
|
|
41
|
+
query: SelectQueryNode | SelectQueryBuilder<unknown, TSource>,
|
|
42
42
|
columns: (ColumnDef | ColumnNode)[] = []
|
|
43
43
|
): InsertQueryBuilder<T> {
|
|
44
44
|
const ast = this.resolveSelectQuery(query);
|
|
@@ -57,9 +57,12 @@ export class InsertQueryBuilder<T> {
|
|
|
57
57
|
return columns.map(column => buildColumnNode(this.table, column));
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
private resolveSelectQuery
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
private resolveSelectQuery<TSource extends TableDef>(
|
|
61
|
+
query: SelectQueryNode | SelectQueryBuilder<unknown, TSource>
|
|
62
|
+
): SelectQueryNode {
|
|
63
|
+
const candidate = query as { getAST?: () => SelectQueryNode };
|
|
64
|
+
return typeof candidate.getAST === 'function' && candidate.getAST
|
|
65
|
+
? candidate.getAST()
|
|
63
66
|
: (query as SelectQueryNode);
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -71,9 +74,10 @@ export class InsertQueryBuilder<T> {
|
|
|
71
74
|
compile(dialect: InsertDialectInput): CompiledQuery;
|
|
72
75
|
|
|
73
76
|
compile(arg: InsertCompiler | InsertDialectInput): CompiledQuery {
|
|
74
|
-
|
|
77
|
+
const candidate = arg as { compileInsert?: (ast: InsertQueryNode) => CompiledQuery };
|
|
78
|
+
if (typeof candidate.compileInsert === 'function') {
|
|
75
79
|
// InsertCompiler path – old behavior
|
|
76
|
-
return
|
|
80
|
+
return candidate.compileInsert(this.state.ast);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
// Dialect | string path – new behavior
|
|
@@ -82,7 +86,7 @@ export class InsertQueryBuilder<T> {
|
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
toSql(arg: InsertCompiler | InsertDialectInput): string {
|
|
85
|
-
return this.compile(arg as
|
|
89
|
+
return this.compile(arg as InsertCompiler).sql;
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
getAST(): InsertQueryNode {
|
|
@@ -49,7 +49,7 @@ export class QueryAstService {
|
|
|
49
49
|
* @param table - Table definition
|
|
50
50
|
* @param state - Current query state
|
|
51
51
|
*/
|
|
52
|
-
constructor(private readonly table: TableDef, private readonly state: SelectQueryState) {}
|
|
52
|
+
constructor(private readonly table: TableDef, private readonly state: SelectQueryState) { }
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Selects columns for the query
|
|
@@ -254,7 +254,7 @@ export class QueryAstService {
|
|
|
254
254
|
private normalizeOrderingTerm(term: ColumnDef | OrderingTerm): OrderingTerm {
|
|
255
255
|
const from = this.state.ast.from;
|
|
256
256
|
const tableRef = from.type === 'Table' && from.alias ? { ...this.table, alias: from.alias } : this.table;
|
|
257
|
-
const termType = (term as
|
|
257
|
+
const termType = (term as { type?: string }).type;
|
|
258
258
|
if (termType === 'Column') {
|
|
259
259
|
return term as ColumnNode;
|
|
260
260
|
}
|
|
@@ -11,7 +11,8 @@ export const parseRawColumn = (
|
|
|
11
11
|
ctes?: CommonTableExpressionNode[]
|
|
12
12
|
): ColumnNode => {
|
|
13
13
|
if (col.includes('(')) {
|
|
14
|
-
const [
|
|
14
|
+
const [_fn, rest] = col.split('(');
|
|
15
|
+
void _fn;
|
|
15
16
|
const colName = rest.replace(')', '');
|
|
16
17
|
const [table, name] = colName.includes('.') ? colName.split('.') : [tableName, colName];
|
|
17
18
|
return { type: 'Column', table, name, alias: col };
|
|
@@ -22,12 +22,12 @@ export function sel<
|
|
|
22
22
|
return selection;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
type Ctor<T> = { new
|
|
25
|
+
type Ctor<T> = { new(...args: unknown[]): T };
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Build a typed selection map from an entity constructor.
|
|
29
29
|
*/
|
|
30
|
-
export function esel<TEntity, K extends keyof TEntity & string>(
|
|
30
|
+
export function esel<TEntity extends object, K extends keyof TEntity & string>(
|
|
31
31
|
entity: Ctor<TEntity>,
|
|
32
32
|
...props: K[]
|
|
33
33
|
): Record<K, ColumnDef> {
|
|
@@ -95,8 +95,8 @@ type WhereHasOptions = {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
type RelationCallback = <TChildTable extends TableDef>(
|
|
98
|
-
qb: SelectQueryBuilder<
|
|
99
|
-
) => SelectQueryBuilder<
|
|
98
|
+
qb: SelectQueryBuilder<unknown, TChildTable>
|
|
99
|
+
) => SelectQueryBuilder<unknown, TChildTable>;
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
/**
|
|
@@ -109,7 +109,7 @@ type RelationCallback = <TChildTable extends TableDef>(
|
|
|
109
109
|
|
|
110
110
|
*/
|
|
111
111
|
|
|
112
|
-
export class SelectQueryBuilder<T =
|
|
112
|
+
export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef> {
|
|
113
113
|
|
|
114
114
|
private readonly env: SelectQueryBuilderEnvironment;
|
|
115
115
|
|
|
@@ -205,12 +205,11 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
205
205
|
|
|
206
206
|
|
|
207
207
|
|
|
208
|
-
private resolveQueryNode(query: SelectQueryBuilder<
|
|
209
|
-
|
|
210
|
-
return typeof (query as any).getAST === 'function'
|
|
211
|
-
|
|
212
|
-
? (query as SelectQueryBuilder<any, TableDef<any>>).getAST()
|
|
208
|
+
private resolveQueryNode<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryNode {
|
|
213
209
|
|
|
210
|
+
const candidate = query as { getAST?: () => SelectQueryNode };
|
|
211
|
+
return typeof candidate.getAST === 'function' && candidate.getAST
|
|
212
|
+
? candidate.getAST()
|
|
214
213
|
: (query as SelectQueryNode);
|
|
215
214
|
|
|
216
215
|
}
|
|
@@ -272,11 +271,11 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
272
271
|
|
|
273
272
|
|
|
274
273
|
|
|
275
|
-
private applySetOperation(
|
|
274
|
+
private applySetOperation<TSub extends TableDef>(
|
|
276
275
|
|
|
277
276
|
operator: SetOperationKind,
|
|
278
277
|
|
|
279
|
-
query: SelectQueryBuilder<
|
|
278
|
+
query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode
|
|
280
279
|
|
|
281
280
|
): SelectQueryBuilderContext {
|
|
282
281
|
|
|
@@ -357,7 +356,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
357
356
|
|
|
358
357
|
*/
|
|
359
358
|
|
|
360
|
-
with(name: string, query: SelectQueryBuilder<
|
|
359
|
+
with<TSub extends TableDef>(name: string, query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
|
|
361
360
|
|
|
362
361
|
const subAst = this.resolveQueryNode(query);
|
|
363
362
|
|
|
@@ -383,7 +382,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
383
382
|
|
|
384
383
|
*/
|
|
385
384
|
|
|
386
|
-
withRecursive(name: string, query: SelectQueryBuilder<
|
|
385
|
+
withRecursive<TSub extends TableDef>(name: string, query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
|
|
387
386
|
|
|
388
387
|
const subAst = this.resolveQueryNode(query);
|
|
389
388
|
|
|
@@ -401,8 +400,8 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
401
400
|
* @param columnAliases - Optional column alias list
|
|
402
401
|
* @returns New query builder instance with updated FROM
|
|
403
402
|
*/
|
|
404
|
-
fromSubquery(
|
|
405
|
-
subquery: SelectQueryBuilder<
|
|
403
|
+
fromSubquery<TSub extends TableDef>(
|
|
404
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
406
405
|
alias: string,
|
|
407
406
|
columnAliases?: string[]
|
|
408
407
|
): SelectQueryBuilder<T, TTable> {
|
|
@@ -426,7 +425,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
426
425
|
|
|
427
426
|
*/
|
|
428
427
|
|
|
429
|
-
selectSubquery(alias: string, sub: SelectQueryBuilder<
|
|
428
|
+
selectSubquery<TSub extends TableDef>(alias: string, sub: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
430
429
|
|
|
431
430
|
const query = this.resolveQueryNode(sub);
|
|
432
431
|
|
|
@@ -444,8 +443,8 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
444
443
|
* @param columnAliases - Optional column alias list for the derived table
|
|
445
444
|
* @returns New query builder instance with the derived-table join
|
|
446
445
|
*/
|
|
447
|
-
joinSubquery(
|
|
448
|
-
subquery: SelectQueryBuilder<
|
|
446
|
+
joinSubquery<TSub extends TableDef>(
|
|
447
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
449
448
|
alias: string,
|
|
450
449
|
condition: BinaryExpressionNode,
|
|
451
450
|
joinKind: JoinKind = JOIN_KINDS.INNER,
|
|
@@ -655,10 +654,11 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
655
654
|
* Selects columns for the root table and relations from a single config object.
|
|
656
655
|
*/
|
|
657
656
|
selectColumnsDeep(config: DeepSelectConfig<TTable>): SelectQueryBuilder<T, TTable> {
|
|
658
|
-
|
|
657
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
658
|
+
let currBuilder: SelectQueryBuilder<T, TTable> = this;
|
|
659
659
|
|
|
660
660
|
if (config.root?.length) {
|
|
661
|
-
|
|
661
|
+
currBuilder = currBuilder.selectColumns(...config.root);
|
|
662
662
|
}
|
|
663
663
|
|
|
664
664
|
for (const key of Object.keys(config) as (keyof typeof config)[]) {
|
|
@@ -666,10 +666,10 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
666
666
|
const relName = key as keyof TTable['relations'] & string;
|
|
667
667
|
const cols = config[relName as keyof DeepSelectConfig<TTable>] as string[] | undefined;
|
|
668
668
|
if (!cols || !cols.length) continue;
|
|
669
|
-
|
|
669
|
+
currBuilder = currBuilder.selectRelationColumns(relName, ...(cols as string[]));
|
|
670
670
|
}
|
|
671
671
|
|
|
672
|
-
return
|
|
672
|
+
return currBuilder;
|
|
673
673
|
}
|
|
674
674
|
|
|
675
675
|
|
|
@@ -848,7 +848,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
848
848
|
|
|
849
849
|
*/
|
|
850
850
|
|
|
851
|
-
union(query: SelectQueryBuilder<
|
|
851
|
+
union<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
852
852
|
|
|
853
853
|
return this.clone(this.applySetOperation('UNION', query));
|
|
854
854
|
|
|
@@ -866,7 +866,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
866
866
|
|
|
867
867
|
*/
|
|
868
868
|
|
|
869
|
-
unionAll(query: SelectQueryBuilder<
|
|
869
|
+
unionAll<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
870
870
|
|
|
871
871
|
return this.clone(this.applySetOperation('UNION ALL', query));
|
|
872
872
|
|
|
@@ -884,7 +884,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
884
884
|
|
|
885
885
|
*/
|
|
886
886
|
|
|
887
|
-
intersect(query: SelectQueryBuilder<
|
|
887
|
+
intersect<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
888
888
|
|
|
889
889
|
return this.clone(this.applySetOperation('INTERSECT', query));
|
|
890
890
|
|
|
@@ -902,7 +902,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
902
902
|
|
|
903
903
|
*/
|
|
904
904
|
|
|
905
|
-
except(query: SelectQueryBuilder<
|
|
905
|
+
except<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
906
906
|
|
|
907
907
|
return this.clone(this.applySetOperation('EXCEPT', query));
|
|
908
908
|
|
|
@@ -920,8 +920,8 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
920
920
|
|
|
921
921
|
*/
|
|
922
922
|
|
|
923
|
-
whereExists(
|
|
924
|
-
subquery: SelectQueryBuilder<
|
|
923
|
+
whereExists<TSub extends TableDef>(
|
|
924
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
925
925
|
correlate?: ExpressionNode
|
|
926
926
|
): SelectQueryBuilder<T, TTable> {
|
|
927
927
|
const subAst = this.resolveQueryNode(subquery);
|
|
@@ -941,8 +941,8 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
941
941
|
|
|
942
942
|
*/
|
|
943
943
|
|
|
944
|
-
whereNotExists(
|
|
945
|
-
subquery: SelectQueryBuilder<
|
|
944
|
+
whereNotExists<TSub extends TableDef>(
|
|
945
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
946
946
|
correlate?: ExpressionNode
|
|
947
947
|
): SelectQueryBuilder<T, TTable> {
|
|
948
948
|
const subAst = this.resolveQueryNode(subquery);
|
|
@@ -987,7 +987,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
987
987
|
const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
|
|
988
988
|
const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
|
|
989
989
|
|
|
990
|
-
let subQb = this.createChildBuilder<
|
|
990
|
+
let subQb = this.createChildBuilder<unknown, typeof relation.target>(relation.target);
|
|
991
991
|
|
|
992
992
|
if (callback) {
|
|
993
993
|
|
|
@@ -1042,7 +1042,7 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
|
|
|
1042
1042
|
const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
|
|
1043
1043
|
const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
|
|
1044
1044
|
|
|
1045
|
-
let subQb = this.createChildBuilder<
|
|
1045
|
+
let subQb = this.createChildBuilder<unknown, typeof relation.target>(relation.target);
|
|
1046
1046
|
|
|
1047
1047
|
if (callback) {
|
|
1048
1048
|
|