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/orm-session.ts
CHANGED
|
@@ -141,8 +141,8 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
141
141
|
* @param pk - The primary key value
|
|
142
142
|
* @returns The entity or undefined if not found
|
|
143
143
|
*/
|
|
144
|
-
getEntity(table: TableDef, pk:
|
|
145
|
-
return this.unitOfWork.getEntity(table, pk);
|
|
144
|
+
getEntity(table: TableDef, pk: unknown): unknown | undefined {
|
|
145
|
+
return this.unitOfWork.getEntity(table, pk as string | number);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/**
|
|
@@ -151,8 +151,8 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
151
151
|
* @param pk - The primary key value
|
|
152
152
|
* @param entity - The entity instance
|
|
153
153
|
*/
|
|
154
|
-
setEntity(table: TableDef, pk:
|
|
155
|
-
this.unitOfWork.setEntity(table, pk, entity);
|
|
154
|
+
setEntity(table: TableDef, pk: unknown, entity: unknown): void {
|
|
155
|
+
this.unitOfWork.setEntity(table, pk as string | number, entity);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
/**
|
|
@@ -161,8 +161,8 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
161
161
|
* @param entity - The entity instance
|
|
162
162
|
* @param pk - Optional primary key value
|
|
163
163
|
*/
|
|
164
|
-
trackNew(table: TableDef, entity:
|
|
165
|
-
this.unitOfWork.trackNew(table, entity, pk);
|
|
164
|
+
trackNew(table: TableDef, entity: unknown, pk?: unknown): void {
|
|
165
|
+
this.unitOfWork.trackNew(table, entity, pk as string | number);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
@@ -171,15 +171,15 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
171
171
|
* @param pk - The primary key value
|
|
172
172
|
* @param entity - The entity instance
|
|
173
173
|
*/
|
|
174
|
-
trackManaged(table: TableDef, pk:
|
|
175
|
-
this.unitOfWork.trackManaged(table, pk, entity);
|
|
174
|
+
trackManaged(table: TableDef, pk: unknown, entity: unknown): void {
|
|
175
|
+
this.unitOfWork.trackManaged(table, pk as string | number, entity);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
/**
|
|
179
179
|
* Marks an entity as dirty (modified).
|
|
180
180
|
* @param entity - The entity to mark as dirty
|
|
181
181
|
*/
|
|
182
|
-
markDirty(entity:
|
|
182
|
+
markDirty(entity: unknown): void {
|
|
183
183
|
this.unitOfWork.markDirty(entity);
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -187,7 +187,7 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
187
187
|
* Marks an entity as removed.
|
|
188
188
|
* @param entity - The entity to mark as removed
|
|
189
189
|
*/
|
|
190
|
-
markRemoved(entity:
|
|
190
|
+
markRemoved(entity: unknown): void {
|
|
191
191
|
this.unitOfWork.markRemoved(entity);
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -201,12 +201,12 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
201
201
|
* @param change - The relation change
|
|
202
202
|
*/
|
|
203
203
|
registerRelationChange = (
|
|
204
|
-
root:
|
|
204
|
+
root: unknown,
|
|
205
205
|
relationKey: RelationKey,
|
|
206
206
|
rootTable: TableDef,
|
|
207
207
|
relationName: string,
|
|
208
208
|
relation: RelationDef,
|
|
209
|
-
change: RelationChange<
|
|
209
|
+
change: RelationChange<unknown>
|
|
210
210
|
): void => {
|
|
211
211
|
this.relationChanges.registerChange(
|
|
212
212
|
buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
|
|
@@ -250,9 +250,9 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
250
250
|
* @returns The entity instance or null if not found
|
|
251
251
|
* @throws If entity metadata is not bootstrapped or table has no primary key
|
|
252
252
|
*/
|
|
253
|
-
async find<TCtor extends EntityConstructor<
|
|
253
|
+
async find<TCtor extends EntityConstructor<object>>(
|
|
254
254
|
entityClass: TCtor,
|
|
255
|
-
id:
|
|
255
|
+
id: unknown
|
|
256
256
|
): Promise<InstanceType<TCtor> | null> {
|
|
257
257
|
const table = getTableDefFromEntity(entityClass);
|
|
258
258
|
if (!table) {
|
|
@@ -269,7 +269,7 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
269
269
|
}, {});
|
|
270
270
|
const qb = selectFromEntity(entityClass)
|
|
271
271
|
.select(columnSelections)
|
|
272
|
-
.where(eq(column, id))
|
|
272
|
+
.where(eq(column, id as string | number))
|
|
273
273
|
.limit(1);
|
|
274
274
|
const rows = await executeHydrated(this, qb);
|
|
275
275
|
return (rows[0] ?? null) as InstanceType<TCtor> | null;
|
|
@@ -281,7 +281,7 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
281
281
|
* @param qb - The query builder
|
|
282
282
|
* @returns The first entity instance or null if not found
|
|
283
283
|
*/
|
|
284
|
-
async findOne<TTable extends TableDef>(qb: SelectQueryBuilder<
|
|
284
|
+
async findOne<TTable extends TableDef>(qb: SelectQueryBuilder<unknown, TTable>): Promise<EntityInstance<TTable> | null> {
|
|
285
285
|
const limited = qb.limit(1);
|
|
286
286
|
const rows = await executeHydrated(this, limited);
|
|
287
287
|
return rows[0] ?? null;
|
|
@@ -293,7 +293,7 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
293
293
|
* @param qb - The query builder
|
|
294
294
|
* @returns Array of entity instances
|
|
295
295
|
*/
|
|
296
|
-
async findMany<TTable extends TableDef>(qb: SelectQueryBuilder<
|
|
296
|
+
async findMany<TTable extends TableDef>(qb: SelectQueryBuilder<unknown, TTable>): Promise<EntityInstance<TTable>[]> {
|
|
297
297
|
return executeHydrated(this, qb);
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -304,9 +304,9 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
304
304
|
* @param options - Graph save options
|
|
305
305
|
* @returns The root entity instance
|
|
306
306
|
*/
|
|
307
|
-
async saveGraph<TCtor extends EntityConstructor<
|
|
307
|
+
async saveGraph<TCtor extends EntityConstructor<object>>(
|
|
308
308
|
entityClass: TCtor,
|
|
309
|
-
payload: Record<string,
|
|
309
|
+
payload: Record<string, unknown>,
|
|
310
310
|
options?: SaveGraphOptions & { transactional?: boolean }
|
|
311
311
|
): Promise<InstanceType<TCtor>> {
|
|
312
312
|
const { transactional = true, ...graphOptions } = options ?? {};
|
|
@@ -326,12 +326,13 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
326
326
|
if (this.unitOfWork.findTracked(entity)) {
|
|
327
327
|
return;
|
|
328
328
|
}
|
|
329
|
-
|
|
329
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
330
|
+
const table = getTableDefFromEntity((entity as { constructor: EntityConstructor }).constructor);
|
|
330
331
|
if (!table) {
|
|
331
332
|
throw new Error('Entity metadata has not been bootstrapped');
|
|
332
333
|
}
|
|
333
334
|
const primaryKey = findPrimaryKey(table);
|
|
334
|
-
const pkValue = (entity as Record<string,
|
|
335
|
+
const pkValue = (entity as Record<string, unknown>)[primaryKey];
|
|
335
336
|
if (pkValue !== undefined && pkValue !== null) {
|
|
336
337
|
this.trackManaged(table, pkValue, entity);
|
|
337
338
|
} else {
|
|
@@ -449,12 +450,12 @@ export class OrmSession<E extends DomainEvent = OrmDomainEvent> implements Entit
|
|
|
449
450
|
}
|
|
450
451
|
|
|
451
452
|
const buildRelationChangeEntry = (
|
|
452
|
-
root:
|
|
453
|
+
root: unknown,
|
|
453
454
|
relationKey: RelationKey,
|
|
454
455
|
rootTable: TableDef,
|
|
455
456
|
relationName: string,
|
|
456
457
|
relation: RelationDef,
|
|
457
|
-
change: RelationChange<
|
|
458
|
+
change: RelationChange<unknown>
|
|
458
459
|
): RelationChangeEntry => ({
|
|
459
460
|
root,
|
|
460
461
|
relationKey,
|
package/src/orm/orm.ts
CHANGED
|
@@ -8,9 +8,8 @@ import { OrmSession } from './orm-session.js';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Options for creating an ORM instance.
|
|
11
|
-
* @template E - The domain event type
|
|
12
11
|
*/
|
|
13
|
-
export interface OrmOptions
|
|
12
|
+
export interface OrmOptions {
|
|
14
13
|
/** The database dialect */
|
|
15
14
|
dialect: Dialect;
|
|
16
15
|
/** The database executor factory */
|
|
@@ -61,7 +60,7 @@ export class Orm<E extends DomainEvent = OrmDomainEvent> {
|
|
|
61
60
|
* Creates a new ORM instance.
|
|
62
61
|
* @param opts - ORM options
|
|
63
62
|
*/
|
|
64
|
-
constructor(opts: OrmOptions
|
|
63
|
+
constructor(opts: OrmOptions) {
|
|
65
64
|
this.dialect = opts.dialect;
|
|
66
65
|
this.interceptors = opts.interceptors ?? new InterceptorPipeline();
|
|
67
66
|
this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
|
|
@@ -92,8 +91,6 @@ export class Orm<E extends DomainEvent = OrmDomainEvent> {
|
|
|
92
91
|
try {
|
|
93
92
|
// A real transaction scope: begin before running user code, commit/rollback after.
|
|
94
93
|
return await session.transaction(() => fn(session));
|
|
95
|
-
} catch (err) {
|
|
96
|
-
throw err;
|
|
97
94
|
} finally {
|
|
98
95
|
await session.dispose();
|
|
99
96
|
}
|
|
@@ -86,13 +86,13 @@ export class RelationChangeProcessor {
|
|
|
86
86
|
if (rootValue === undefined || rootValue === null) return;
|
|
87
87
|
|
|
88
88
|
if (entry.change.kind === 'add' || entry.change.kind === 'attach') {
|
|
89
|
-
this.assignHasManyForeignKey(tracked.entity, relation, rootValue);
|
|
89
|
+
this.assignHasManyForeignKey(tracked.entity as Record<string, unknown>, relation, rootValue);
|
|
90
90
|
this.unitOfWork.markDirty(tracked.entity);
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
if (entry.change.kind === 'remove') {
|
|
95
|
-
this.detachHasManyChild(tracked.entity, relation);
|
|
95
|
+
this.detachHasManyChild(tracked.entity as Record<string, unknown>, relation);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -113,13 +113,13 @@ export class RelationChangeProcessor {
|
|
|
113
113
|
if (rootValue === undefined || rootValue === null) return;
|
|
114
114
|
|
|
115
115
|
if (entry.change.kind === 'attach' || entry.change.kind === 'add') {
|
|
116
|
-
this.assignHasOneForeignKey(tracked.entity, relation, rootValue);
|
|
116
|
+
this.assignHasOneForeignKey(tracked.entity as Record<string, unknown>, relation, rootValue);
|
|
117
117
|
this.unitOfWork.markDirty(tracked.entity);
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
if (entry.change.kind === 'remove') {
|
|
122
|
-
this.detachHasOneChild(tracked.entity, relation);
|
|
122
|
+
this.detachHasOneChild(tracked.entity as Record<string, unknown>, relation);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -128,6 +128,7 @@ export class RelationChangeProcessor {
|
|
|
128
128
|
* @param _entry - The relation change entry (reserved for future use)
|
|
129
129
|
*/
|
|
130
130
|
private async handleBelongsToChange(_entry: RelationChangeEntry): Promise<void> {
|
|
131
|
+
void _entry;
|
|
131
132
|
// Reserved for future cascade/persist behaviors for belongs-to relations.
|
|
132
133
|
}
|
|
133
134
|
|
|
@@ -141,7 +142,7 @@ export class RelationChangeProcessor {
|
|
|
141
142
|
const rootId = entry.root[rootKey];
|
|
142
143
|
if (rootId === undefined || rootId === null) return;
|
|
143
144
|
|
|
144
|
-
const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
|
|
145
|
+
const targetId = this.resolvePrimaryKeyValue(entry.change.entity as Record<string, unknown>, relation.target);
|
|
145
146
|
if (targetId === null) return;
|
|
146
147
|
|
|
147
148
|
if (entry.change.kind === 'attach' || entry.change.kind === 'add') {
|
|
@@ -164,7 +165,7 @@ export class RelationChangeProcessor {
|
|
|
164
165
|
* @param relation - The has-many relation
|
|
165
166
|
* @param rootValue - The root entity's primary key value
|
|
166
167
|
*/
|
|
167
|
-
private assignHasManyForeignKey(child:
|
|
168
|
+
private assignHasManyForeignKey(child: Record<string, unknown>, relation: HasManyRelation, rootValue: unknown): void {
|
|
168
169
|
const current = child[relation.foreignKey];
|
|
169
170
|
if (current === rootValue) return;
|
|
170
171
|
child[relation.foreignKey] = rootValue;
|
|
@@ -175,7 +176,7 @@ export class RelationChangeProcessor {
|
|
|
175
176
|
* @param child - The child entity
|
|
176
177
|
* @param relation - The has-many relation
|
|
177
178
|
*/
|
|
178
|
-
private detachHasManyChild(child:
|
|
179
|
+
private detachHasManyChild(child: Record<string, unknown>, relation: HasManyRelation): void {
|
|
179
180
|
if (relation.cascade === 'all' || relation.cascade === 'remove') {
|
|
180
181
|
this.unitOfWork.markRemoved(child);
|
|
181
182
|
return;
|
|
@@ -190,7 +191,7 @@ export class RelationChangeProcessor {
|
|
|
190
191
|
* @param relation - The has-one relation
|
|
191
192
|
* @param rootValue - The root entity's primary key value
|
|
192
193
|
*/
|
|
193
|
-
private assignHasOneForeignKey(child:
|
|
194
|
+
private assignHasOneForeignKey(child: Record<string, unknown>, relation: HasOneRelation, rootValue: unknown): void {
|
|
194
195
|
const current = child[relation.foreignKey];
|
|
195
196
|
if (current === rootValue) return;
|
|
196
197
|
child[relation.foreignKey] = rootValue;
|
|
@@ -201,7 +202,7 @@ export class RelationChangeProcessor {
|
|
|
201
202
|
* @param child - The child entity
|
|
202
203
|
* @param relation - The has-one relation
|
|
203
204
|
*/
|
|
204
|
-
private detachHasOneChild(child:
|
|
205
|
+
private detachHasOneChild(child: Record<string, unknown>, relation: HasOneRelation): void {
|
|
205
206
|
if (relation.cascade === 'all' || relation.cascade === 'remove') {
|
|
206
207
|
this.unitOfWork.markRemoved(child);
|
|
207
208
|
return;
|
|
@@ -250,11 +251,11 @@ export class RelationChangeProcessor {
|
|
|
250
251
|
* @param table - The table definition
|
|
251
252
|
* @returns The primary key value or null
|
|
252
253
|
*/
|
|
253
|
-
private resolvePrimaryKeyValue(entity:
|
|
254
|
+
private resolvePrimaryKeyValue(entity: Record<string, unknown>, table: TableDef): string | number | null {
|
|
254
255
|
if (!entity) return null;
|
|
255
256
|
const key = findPrimaryKey(table);
|
|
256
257
|
const value = entity[key];
|
|
257
258
|
if (value === undefined || value === null) return null;
|
|
258
|
-
return value;
|
|
259
|
+
return (value as string | number | null | undefined) ?? null;
|
|
259
260
|
}
|
|
260
261
|
}
|
|
@@ -5,11 +5,11 @@ import { BelongsToRelation } from '../../schema/relation.js';
|
|
|
5
5
|
import { TableDef } from '../../schema/table.js';
|
|
6
6
|
import { EntityMeta, getHydrationRecord, hasEntityMeta } from '../entity-meta.js';
|
|
7
7
|
|
|
8
|
-
type Rows = Record<string,
|
|
8
|
+
type Rows = Record<string, unknown>;
|
|
9
9
|
|
|
10
10
|
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
11
|
|
|
12
|
-
const hideInternal = (obj:
|
|
12
|
+
const hideInternal = (obj: object, keys: string[]): void => {
|
|
13
13
|
for (const key of keys) {
|
|
14
14
|
Object.defineProperty(obj, key, {
|
|
15
15
|
value: obj[key],
|
|
@@ -26,13 +26,13 @@ export class DefaultBelongsToReference<TParent> implements BelongsToReference<TP
|
|
|
26
26
|
|
|
27
27
|
constructor(
|
|
28
28
|
private readonly ctx: EntityContext,
|
|
29
|
-
private readonly meta: EntityMeta<
|
|
30
|
-
private readonly root:
|
|
29
|
+
private readonly meta: EntityMeta<TableDef>,
|
|
30
|
+
private readonly root: unknown,
|
|
31
31
|
private readonly relationName: string,
|
|
32
32
|
private readonly relation: BelongsToRelation,
|
|
33
33
|
private readonly rootTable: TableDef,
|
|
34
34
|
private readonly loader: () => Promise<Map<string, Rows>>,
|
|
35
|
-
private readonly createEntity: (row: Record<string,
|
|
35
|
+
private readonly createEntity: (row: Record<string, unknown>) => TParent,
|
|
36
36
|
private readonly targetKey: string
|
|
37
37
|
) {
|
|
38
38
|
hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'targetKey']);
|
|
@@ -42,7 +42,7 @@ export class DefaultBelongsToReference<TParent> implements BelongsToReference<TP
|
|
|
42
42
|
async load(): Promise<TParent | null> {
|
|
43
43
|
if (this.loaded) return this.current;
|
|
44
44
|
const map = await this.loader();
|
|
45
|
-
const fkValue = this.root[this.relation.foreignKey];
|
|
45
|
+
const fkValue = (this.root as Record<string, unknown>)[this.relation.foreignKey];
|
|
46
46
|
if (fkValue === null || fkValue === undefined) {
|
|
47
47
|
this.current = null;
|
|
48
48
|
} else {
|
|
@@ -60,7 +60,7 @@ export class DefaultBelongsToReference<TParent> implements BelongsToReference<TP
|
|
|
60
60
|
set(data: Partial<TParent> | TParent | null): TParent | null {
|
|
61
61
|
if (data === null) {
|
|
62
62
|
const previous = this.current;
|
|
63
|
-
this.root[this.relation.foreignKey] = null;
|
|
63
|
+
(this.root as Record<string, unknown>)[this.relation.foreignKey] = null;
|
|
64
64
|
this.current = null;
|
|
65
65
|
this.ctx.registerRelationChange(
|
|
66
66
|
this.root,
|
|
@@ -73,10 +73,10 @@ export class DefaultBelongsToReference<TParent> implements BelongsToReference<TP
|
|
|
73
73
|
return null;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
const entity = hasEntityMeta(data) ? (data as TParent) : this.createEntity(data as Record<string,
|
|
77
|
-
const pkValue = (entity as
|
|
76
|
+
const entity = hasEntityMeta(data) ? (data as TParent) : this.createEntity(data as Record<string, unknown>);
|
|
77
|
+
const pkValue = (entity as Record<string, unknown>)[this.targetKey];
|
|
78
78
|
if (pkValue !== undefined) {
|
|
79
|
-
this.root[this.relation.foreignKey] = pkValue;
|
|
79
|
+
(this.root as Record<string, unknown>)[this.relation.foreignKey] = pkValue;
|
|
80
80
|
}
|
|
81
81
|
this.current = entity;
|
|
82
82
|
this.ctx.registerRelationChange(
|
|
@@ -95,7 +95,7 @@ export class DefaultBelongsToReference<TParent> implements BelongsToReference<TP
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
private populateFromHydrationCache(): void {
|
|
98
|
-
const fkValue = this.root[this.relation.foreignKey];
|
|
98
|
+
const fkValue = (this.root as Record<string, unknown>)[this.relation.foreignKey];
|
|
99
99
|
if (fkValue === undefined || fkValue === null) return;
|
|
100
100
|
const row = getHydrationRecord(this.meta, this.relationName, fkValue);
|
|
101
101
|
if (!row) return;
|
|
@@ -5,11 +5,11 @@ import { HasManyRelation } from '../../schema/relation.js';
|
|
|
5
5
|
import { TableDef } from '../../schema/table.js';
|
|
6
6
|
import { EntityMeta, getHydrationRows } from '../entity-meta.js';
|
|
7
7
|
|
|
8
|
-
type Rows = Record<string,
|
|
8
|
+
type Rows = Record<string, unknown>[];
|
|
9
9
|
|
|
10
10
|
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
11
|
|
|
12
|
-
const hideInternal = (obj:
|
|
12
|
+
const hideInternal = (obj: object, keys: string[]): void => {
|
|
13
13
|
for (const key of keys) {
|
|
14
14
|
Object.defineProperty(obj, key, {
|
|
15
15
|
value: obj[key],
|
|
@@ -28,13 +28,13 @@ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChil
|
|
|
28
28
|
|
|
29
29
|
constructor(
|
|
30
30
|
private readonly ctx: EntityContext,
|
|
31
|
-
private readonly meta: EntityMeta<
|
|
32
|
-
private readonly root:
|
|
31
|
+
private readonly meta: EntityMeta<TableDef>,
|
|
32
|
+
private readonly root: unknown,
|
|
33
33
|
private readonly relationName: string,
|
|
34
34
|
private readonly relation: HasManyRelation,
|
|
35
35
|
private readonly rootTable: TableDef,
|
|
36
36
|
private readonly loader: () => Promise<Map<string, Rows>>,
|
|
37
|
-
private readonly createEntity: (row: Record<string,
|
|
37
|
+
private readonly createEntity: (row: Record<string, unknown>) => TChild,
|
|
38
38
|
private readonly localKey: string
|
|
39
39
|
) {
|
|
40
40
|
hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
|
|
@@ -44,7 +44,7 @@ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChil
|
|
|
44
44
|
async load(): Promise<TChild[]> {
|
|
45
45
|
if (this.loaded) return this.items;
|
|
46
46
|
const map = await this.loader();
|
|
47
|
-
const key = toKey(this.root[this.localKey]);
|
|
47
|
+
const key = toKey((this.root as Record<string, unknown>)[this.localKey]);
|
|
48
48
|
const rows = map.get(key) ?? [];
|
|
49
49
|
this.items = rows.map(row => this.createEntity(row));
|
|
50
50
|
this.loaded = true;
|
|
@@ -56,8 +56,8 @@ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChil
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
add(data: Partial<TChild>): TChild {
|
|
59
|
-
const keyValue = this.root[this.localKey];
|
|
60
|
-
const childRow: Record<string,
|
|
59
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
60
|
+
const childRow: Record<string, unknown> = {
|
|
61
61
|
...data,
|
|
62
62
|
[this.relation.foreignKey]: keyValue
|
|
63
63
|
};
|
|
@@ -77,7 +77,7 @@ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChil
|
|
|
77
77
|
|
|
78
78
|
attach(entity: TChild): void {
|
|
79
79
|
const keyValue = this.root[this.localKey];
|
|
80
|
-
(entity as Record<string,
|
|
80
|
+
(entity as Record<string, unknown>)[this.relation.foreignKey] = keyValue;
|
|
81
81
|
this.ctx.markDirty(entity);
|
|
82
82
|
this.items.push(entity);
|
|
83
83
|
this.ctx.registerRelationChange(
|
|
@@ -114,7 +114,7 @@ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChil
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
private hydrateFromCache(): void {
|
|
117
|
-
const keyValue = this.root[this.localKey];
|
|
117
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
118
118
|
if (keyValue === undefined || keyValue === null) return;
|
|
119
119
|
const rows = getHydrationRows(this.meta, this.relationName, keyValue);
|
|
120
120
|
if (!rows?.length) return;
|
|
@@ -5,11 +5,11 @@ import { HasOneRelation } from '../../schema/relation.js';
|
|
|
5
5
|
import { TableDef } from '../../schema/table.js';
|
|
6
6
|
import { EntityMeta, getHydrationRecord, hasEntityMeta } from '../entity-meta.js';
|
|
7
7
|
|
|
8
|
-
type Row = Record<string,
|
|
8
|
+
type Row = Record<string, unknown>;
|
|
9
9
|
|
|
10
10
|
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
11
|
|
|
12
|
-
const hideInternal = (obj:
|
|
12
|
+
const hideInternal = (obj: object, keys: string[]): void => {
|
|
13
13
|
for (const key of keys) {
|
|
14
14
|
Object.defineProperty(obj, key, {
|
|
15
15
|
value: obj[key],
|
|
@@ -26,8 +26,8 @@ export class DefaultHasOneReference<TChild> implements HasOneReference<TChild> {
|
|
|
26
26
|
|
|
27
27
|
constructor(
|
|
28
28
|
private readonly ctx: EntityContext,
|
|
29
|
-
private readonly meta: EntityMeta<
|
|
30
|
-
private readonly root:
|
|
29
|
+
private readonly meta: EntityMeta<TableDef>,
|
|
30
|
+
private readonly root: unknown,
|
|
31
31
|
private readonly relationName: string,
|
|
32
32
|
private readonly relation: HasOneRelation,
|
|
33
33
|
private readonly rootTable: TableDef,
|
|
@@ -52,7 +52,7 @@ export class DefaultHasOneReference<TChild> implements HasOneReference<TChild> {
|
|
|
52
52
|
async load(): Promise<TChild | null> {
|
|
53
53
|
if (this.loaded) return this.current;
|
|
54
54
|
const map = await this.loader();
|
|
55
|
-
const keyValue = this.root[this.localKey];
|
|
55
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
56
56
|
if (keyValue === undefined || keyValue === null) {
|
|
57
57
|
this.loaded = true;
|
|
58
58
|
return this.current;
|
|
@@ -121,7 +121,8 @@ export class DefaultHasOneReference<TChild> implements HasOneReference<TChild> {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
private assignForeignKey(entity: TChild): void {
|
|
124
|
-
const keyValue = this.root[this.localKey];
|
|
124
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
126
|
(entity as Row)[this.relation.foreignKey] = keyValue;
|
|
126
127
|
}
|
|
127
128
|
|
|
@@ -130,7 +131,7 @@ export class DefaultHasOneReference<TChild> implements HasOneReference<TChild> {
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
private populateFromHydrationCache(): void {
|
|
133
|
-
const keyValue = this.root[this.localKey];
|
|
134
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
134
135
|
if (keyValue === undefined || keyValue === null) return;
|
|
135
136
|
const row = getHydrationRecord(this.meta, this.relationName, keyValue);
|
|
136
137
|
if (!row) return;
|
|
@@ -6,11 +6,11 @@ import { TableDef } from '../../schema/table.js';
|
|
|
6
6
|
import { findPrimaryKey } from '../../query-builder/hydration-planner.js';
|
|
7
7
|
import { EntityMeta, getHydrationRows } from '../entity-meta.js';
|
|
8
8
|
|
|
9
|
-
type Rows = Record<string,
|
|
9
|
+
type Rows = Record<string, unknown>[];
|
|
10
10
|
|
|
11
11
|
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
12
12
|
|
|
13
|
-
const hideInternal = (obj:
|
|
13
|
+
const hideInternal = (obj: object, keys: string[]): void => {
|
|
14
14
|
for (const key of keys) {
|
|
15
15
|
Object.defineProperty(obj, key, {
|
|
16
16
|
value: obj[key],
|
|
@@ -27,13 +27,13 @@ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollectio
|
|
|
27
27
|
|
|
28
28
|
constructor(
|
|
29
29
|
private readonly ctx: EntityContext,
|
|
30
|
-
private readonly meta: EntityMeta<
|
|
31
|
-
private readonly root:
|
|
30
|
+
private readonly meta: EntityMeta<TableDef>,
|
|
31
|
+
private readonly root: unknown,
|
|
32
32
|
private readonly relationName: string,
|
|
33
33
|
private readonly relation: BelongsToManyRelation,
|
|
34
34
|
private readonly rootTable: TableDef,
|
|
35
35
|
private readonly loader: () => Promise<Map<string, Rows>>,
|
|
36
|
-
private readonly createEntity: (row: Record<string,
|
|
36
|
+
private readonly createEntity: (row: Record<string, unknown>) => TTarget,
|
|
37
37
|
private readonly localKey: string
|
|
38
38
|
) {
|
|
39
39
|
hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
|
|
@@ -47,8 +47,8 @@ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollectio
|
|
|
47
47
|
const rows = map.get(key) ?? [];
|
|
48
48
|
this.items = rows.map(row => {
|
|
49
49
|
const entity = this.createEntity(row);
|
|
50
|
-
if ((row as
|
|
51
|
-
(entity as
|
|
50
|
+
if ((row as { _pivot?: unknown })._pivot) {
|
|
51
|
+
(entity as { _pivot?: unknown })._pivot = (row as { _pivot?: unknown })._pivot;
|
|
52
52
|
}
|
|
53
53
|
return entity;
|
|
54
54
|
});
|
|
@@ -103,7 +103,6 @@ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollectio
|
|
|
103
103
|
|
|
104
104
|
async syncByIds(ids: (number | string)[]): Promise<void> {
|
|
105
105
|
await this.load();
|
|
106
|
-
const targetKey = this.relation.targetKey || findPrimaryKey(this.relation.target);
|
|
107
106
|
const normalized = new Set(ids.map(id => toKey(id)));
|
|
108
107
|
const currentIds = new Set(this.items.map(item => toKey(this.extractId(item))));
|
|
109
108
|
|
|
@@ -123,7 +122,7 @@ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollectio
|
|
|
123
122
|
|
|
124
123
|
private ensureEntity(target: TTarget | number | string): TTarget {
|
|
125
124
|
if (typeof target === 'number' || typeof target === 'string') {
|
|
126
|
-
const stub: Record<string,
|
|
125
|
+
const stub: Record<string, unknown> = {
|
|
127
126
|
[this.targetKey]: target
|
|
128
127
|
};
|
|
129
128
|
return this.createEntity(stub);
|
|
@@ -136,7 +135,7 @@ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollectio
|
|
|
136
135
|
if (typeof entity === 'number' || typeof entity === 'string') {
|
|
137
136
|
return entity;
|
|
138
137
|
}
|
|
139
|
-
return (entity as
|
|
138
|
+
return (entity as Record<string, unknown>)[this.targetKey] as string | number | null ?? null;
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
private get relationKey(): RelationKey {
|
|
@@ -148,14 +147,15 @@ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollectio
|
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
private hydrateFromCache(): void {
|
|
151
|
-
const keyValue = this.root[this.localKey];
|
|
150
|
+
const keyValue = (this.root as Record<string, unknown>)[this.localKey];
|
|
152
151
|
if (keyValue === undefined || keyValue === null) return;
|
|
153
152
|
const rows = getHydrationRows(this.meta, this.relationName, keyValue);
|
|
154
153
|
if (!rows?.length) return;
|
|
155
154
|
this.items = rows.map(row => {
|
|
156
155
|
const entity = this.createEntity(row);
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
157
|
+
if ((row as { _pivot?: unknown })._pivot) {
|
|
158
|
+
(entity as { _pivot?: unknown })._pivot = (row as { _pivot?: unknown })._pivot;
|
|
159
159
|
}
|
|
160
160
|
return entity;
|
|
161
161
|
});
|
package/src/orm/runtime-types.ts
CHANGED
|
@@ -24,13 +24,13 @@ export interface TrackedEntity {
|
|
|
24
24
|
/** The table definition this entity belongs to */
|
|
25
25
|
table: TableDef;
|
|
26
26
|
/** The actual entity instance */
|
|
27
|
-
entity:
|
|
27
|
+
entity: unknown;
|
|
28
28
|
/** Primary key value of the entity */
|
|
29
29
|
pk: string | number | null;
|
|
30
30
|
/** Current status of the entity */
|
|
31
31
|
status: EntityStatus;
|
|
32
32
|
/** Original values of the entity when it was loaded */
|
|
33
|
-
original: Record<string,
|
|
33
|
+
original: Record<string, unknown> | null;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -53,7 +53,7 @@ export type RelationChange<T> =
|
|
|
53
53
|
*/
|
|
54
54
|
export interface RelationChangeEntry {
|
|
55
55
|
/** Root entity that owns the relation */
|
|
56
|
-
root:
|
|
56
|
+
root: unknown;
|
|
57
57
|
/** Key of the relation being changed */
|
|
58
58
|
relationKey: RelationKey;
|
|
59
59
|
/** Table definition of the root entity */
|
|
@@ -63,7 +63,7 @@ export interface RelationChangeEntry {
|
|
|
63
63
|
/** Relation definition */
|
|
64
64
|
relation: RelationDef;
|
|
65
65
|
/** The change being applied */
|
|
66
|
-
change: RelationChange<
|
|
66
|
+
change: RelationChange<unknown>;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|