metal-orm 1.0.65 → 1.0.66

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metal-orm",
3
- "version": "1.0.65",
3
+ "version": "1.0.66",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "engines": {
@@ -1,5 +1,6 @@
1
1
  import type { TableDef } from '../schema/table.js';
2
2
  import type { TrackedEntity } from './runtime-types.js';
3
+ import type { PrimaryKey } from './entity-context.js';
3
4
 
4
5
  /**
5
6
  * Simple identity map for tracking entities within a session.
@@ -18,7 +19,7 @@ export class IdentityMap {
18
19
  * @param pk The primary key value.
19
20
  * @returns The entity instance if found, undefined otherwise.
20
21
  */
21
- getEntity(table: TableDef, pk: string | number): object | undefined {
22
+ getEntity(table: TableDef, pk: PrimaryKey): object | undefined {
22
23
  const bucket = this.buckets.get(table.name);
23
24
  return bucket?.get(this.toIdentityKey(pk))?.entity;
24
25
  }
@@ -54,7 +55,7 @@ export class IdentityMap {
54
55
  this.buckets.clear();
55
56
  }
56
57
 
57
- private toIdentityKey(pk: string | number): string {
58
+ private toIdentityKey(pk: PrimaryKey): string {
58
59
  return String(pk);
59
60
  }
60
61
  }
@@ -20,6 +20,7 @@ import { createEntityFromRow } from './entity.js';
20
20
  import type { EntityConstructor } from './entity-metadata.js';
21
21
  import { getTableDefFromEntity } from '../decorators/bootstrap.js';
22
22
  import type { OrmSession } from './orm-session.js';
23
+ import type { PrimaryKey } from './entity-context.js';
23
24
 
24
25
  /**
25
26
  * Options for controlling the behavior of save graph operations.
@@ -36,9 +37,6 @@ export interface SaveGraphOptions {
36
37
  }
37
38
 
38
39
  /** Represents an entity object with arbitrary properties. */
39
-
40
- /** Represents an entity object with arbitrary properties. */
41
-
42
40
  type AnyEntity = Record<string, unknown>;
43
41
 
44
42
  /**
@@ -102,7 +100,7 @@ const ensureEntity = <TTable extends TableDef>(
102
100
  const pkValue = payload[pk];
103
101
 
104
102
  if (pkValue !== undefined && pkValue !== null) {
105
- const tracked = session.getEntity(table, pkValue as string | number);
103
+ const tracked = session.getEntity(table, pkValue as PrimaryKey);
106
104
  if (tracked) {
107
105
  return tracked as EntityInstance<TTable>;
108
106
  }
@@ -159,7 +157,7 @@ const handleHasMany = async (
159
157
 
160
158
  const current =
161
159
  findInCollectionByPk(existing, targetPk, pkValue) ??
162
- (pkValue !== undefined && pkValue !== null ? session.getEntity(targetTable, pkValue as string | number) : undefined);
160
+ (pkValue !== undefined && pkValue !== null ? session.getEntity(targetTable, pkValue as PrimaryKey) : undefined);
163
161
 
164
162
  const entity = current ?? ensureEntity(session, targetTable, asObj, options);
165
163
  assignColumns(targetTable, entity as AnyEntity, asObj, options);
@@ -202,7 +200,7 @@ const handleHasOne = async (
202
200
  if (typeof payload === 'number' || typeof payload === 'string') {
203
201
  const entity = ref.set({ [pk]: payload });
204
202
  if (entity) {
205
- await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload }, options);
203
+ await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload as PrimaryKey }, options);
206
204
  }
207
205
  return;
208
206
  }
@@ -230,7 +228,7 @@ const handleBelongsTo = async (
230
228
  if (typeof payload === 'number' || typeof payload === 'string') {
231
229
  const entity = ref.set({ [pk]: payload });
232
230
  if (entity) {
233
- await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload }, options);
231
+ await applyGraphToEntity(session, relation.target, entity as AnyEntity, { [pk]: payload as PrimaryKey }, options);
234
232
  }
235
233
  return;
236
234
  }
@@ -268,7 +266,7 @@ const handleBelongsToMany = async (
268
266
  const asObj = item as AnyEntity;
269
267
  const pkValue = asObj[targetPk];
270
268
  const entity = pkValue !== undefined && pkValue !== null
271
- ? session.getEntity(targetTable, pkValue as string | number) ?? ensureEntity(session, targetTable, asObj, options)
269
+ ? session.getEntity(targetTable, pkValue as PrimaryKey) ?? ensureEntity(session, targetTable, asObj, options)
272
270
  : ensureEntity(session, targetTable, asObj, options);
273
271
 
274
272
  assignColumns(targetTable, entity as AnyEntity, asObj, options);
@@ -1,5 +1,5 @@
1
- import { ColumnNode, eq } from '../core/ast/expression.js';
2
- import type { ValueOperandInput } from '../core/ast/expression.js';
1
+ import { ColumnNode, eq } from '../core/ast/expression.js';
2
+ import type { ValueOperandInput } from '../core/ast/expression.js';
3
3
  import type { Dialect, CompiledQuery } from '../core/dialect/abstract.js';
4
4
  import { InsertQueryBuilder } from '../query-builder/insert.js';
5
5
  import { UpdateQueryBuilder } from '../query-builder/update.js';
@@ -10,12 +10,13 @@ import type { DbExecutor, QueryResult } from '../core/execution/db-executor.js';
10
10
  import { IdentityMap } from './identity-map.js';
11
11
  import { EntityStatus } from './runtime-types.js';
12
12
  import type { TrackedEntity } from './runtime-types.js';
13
+ import type { PrimaryKey } from './entity-context.js';
13
14
 
14
15
  /**
15
16
  * Unit of Work pattern implementation for tracking entity changes.
16
17
  */
17
- export class UnitOfWork {
18
- private readonly trackedEntities = new Map<object, TrackedEntity>();
18
+ export class UnitOfWork {
19
+ private readonly trackedEntities = new Map<object, TrackedEntity>();
19
20
 
20
21
  /**
21
22
  * Creates a new UnitOfWork instance.
@@ -52,9 +53,9 @@ export class UnitOfWork {
52
53
  * @param pk - The primary key value
53
54
  * @returns The entity or undefined if not found
54
55
  */
55
- getEntity(table: TableDef, pk: string | number): object | undefined {
56
- return this.identityMap.getEntity(table, pk);
57
- }
56
+ getEntity(table: TableDef, pk: PrimaryKey): object | undefined {
57
+ return this.identityMap.getEntity(table, pk);
58
+ }
58
59
 
59
60
  /**
60
61
  * Gets all tracked entities for a specific table.
@@ -70,9 +71,9 @@ export class UnitOfWork {
70
71
  * @param entity - The entity to find
71
72
  * @returns The tracked entity or undefined if not found
72
73
  */
73
- findTracked(entity: object): TrackedEntity | undefined {
74
- return this.trackedEntities.get(entity);
75
- }
74
+ findTracked(entity: object): TrackedEntity | undefined {
75
+ return this.trackedEntities.get(entity);
76
+ }
76
77
 
77
78
  /**
78
79
  * Sets an entity in the identity map.
@@ -80,9 +81,9 @@ export class UnitOfWork {
80
81
  * @param pk - The primary key value
81
82
  * @param entity - The entity instance
82
83
  */
83
- setEntity(table: TableDef, pk: string | number, entity: object): void {
84
- if (pk === null || pk === undefined) return;
85
- let tracked = this.trackedEntities.get(entity);
84
+ setEntity(table: TableDef, pk: PrimaryKey, entity: object): void {
85
+ if (pk === null || pk === undefined) return;
86
+ let tracked = this.trackedEntities.get(entity);
86
87
  if (!tracked) {
87
88
  tracked = {
88
89
  table,
@@ -105,7 +106,7 @@ export class UnitOfWork {
105
106
  * @param entity - The entity instance
106
107
  * @param pk - Optional primary key value
107
108
  */
108
- trackNew(table: TableDef, entity: object, pk?: string | number): void {
109
+ trackNew(table: TableDef, entity: object, pk?: PrimaryKey): void {
109
110
  const tracked: TrackedEntity = {
110
111
  table,
111
112
  entity,
@@ -125,7 +126,7 @@ export class UnitOfWork {
125
126
  * @param pk - The primary key value
126
127
  * @param entity - The entity instance
127
128
  */
128
- trackManaged(table: TableDef, pk: string | number, entity: object): void {
129
+ trackManaged(table: TableDef, pk: PrimaryKey, entity: object): void {
129
130
  const tracked: TrackedEntity = {
130
131
  table,
131
132
  entity,
@@ -141,22 +142,22 @@ export class UnitOfWork {
141
142
  * Marks an entity as dirty (modified).
142
143
  * @param entity - The entity to mark as dirty
143
144
  */
144
- markDirty(entity: object): void {
145
- const tracked = this.trackedEntities.get(entity);
146
- if (!tracked) return;
147
- if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
148
- tracked.status = EntityStatus.Dirty;
149
- }
145
+ markDirty(entity: object): void {
146
+ const tracked = this.trackedEntities.get(entity);
147
+ if (!tracked) return;
148
+ if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
149
+ tracked.status = EntityStatus.Dirty;
150
+ }
150
151
 
151
152
  /**
152
153
  * Marks an entity as removed.
153
154
  * @param entity - The entity to mark as removed
154
155
  */
155
- markRemoved(entity: object): void {
156
- const tracked = this.trackedEntities.get(entity);
157
- if (!tracked) return;
158
- tracked.status = EntityStatus.Removed;
159
- }
156
+ markRemoved(entity: object): void {
157
+ const tracked = this.trackedEntities.get(entity);
158
+ if (!tracked) return;
159
+ tracked.status = EntityStatus.Removed;
160
+ }
160
161
 
161
162
  /**
162
163
  * Flushes pending changes to the database.
@@ -195,8 +196,8 @@ export class UnitOfWork {
195
196
  private async flushInsert(tracked: TrackedEntity): Promise<void> {
196
197
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
197
198
 
198
- const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
199
- let builder = new InsertQueryBuilder(tracked.table).values(payload as Record<string, ValueOperandInput>);
199
+ const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
200
+ let builder = new InsertQueryBuilder(tracked.table).values(payload as Record<string, ValueOperandInput>);
200
201
  if (this.dialect.supportsReturning()) {
201
202
  builder = builder.returning(...this.getReturningColumns(tracked.table));
202
203
  }
@@ -288,16 +289,16 @@ export class UnitOfWork {
288
289
  * @param tracked - The tracked entity
289
290
  * @returns Object with changed column values
290
291
  */
291
- private computeChanges(tracked: TrackedEntity): Record<string, unknown> {
292
- const snapshot = tracked.original ?? {};
293
- const changes: Record<string, unknown> = {};
294
- for (const column of Object.keys(tracked.table.columns)) {
295
- const current = (tracked.entity as Record<string, unknown>)[column];
296
- if (snapshot[column] !== current) {
297
- changes[column] = current;
298
- }
299
- }
300
- return changes;
292
+ private computeChanges(tracked: TrackedEntity): Record<string, unknown> {
293
+ const snapshot = tracked.original ?? {};
294
+ const changes: Record<string, unknown> = {};
295
+ for (const column of Object.keys(tracked.table.columns)) {
296
+ const current = (tracked.entity as Record<string, unknown>)[column];
297
+ if (snapshot[column] !== current) {
298
+ changes[column] = current;
299
+ }
300
+ }
301
+ return changes;
301
302
  }
302
303
 
303
304
  /**
@@ -343,18 +344,18 @@ export class UnitOfWork {
343
344
  * @param tracked - The tracked entity
344
345
  * @param results - Query results
345
346
  */
346
- private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
347
- if (!this.dialect.supportsReturning()) return;
348
- const first = results[0];
349
- if (!first || first.values.length === 0) return;
350
-
351
- const row = first.values[0];
352
- for (let i = 0; i < first.columns.length; i++) {
353
- const columnName = this.normalizeColumnName(first.columns[i]);
354
- if (!(columnName in tracked.table.columns)) continue;
355
- (tracked.entity as Record<string, unknown>)[columnName] = row[i];
356
- }
357
- }
347
+ private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
348
+ if (!this.dialect.supportsReturning()) return;
349
+ const first = results[0];
350
+ if (!first || first.values.length === 0) return;
351
+
352
+ const row = first.values[0];
353
+ for (let i = 0; i < first.columns.length; i++) {
354
+ const columnName = this.normalizeColumnName(first.columns[i]);
355
+ if (!(columnName in tracked.table.columns)) continue;
356
+ (tracked.entity as Record<string, unknown>)[columnName] = row[i];
357
+ }
358
+ }
358
359
 
359
360
  /**
360
361
  * Normalizes a column name by removing quotes and table prefixes.
@@ -395,11 +396,11 @@ export class UnitOfWork {
395
396
  * @param tracked - The tracked entity
396
397
  * @returns Primary key value or null
397
398
  */
398
- private getPrimaryKeyValue(tracked: TrackedEntity): string | number | null {
399
- const key = findPrimaryKey(tracked.table);
400
- const val = (tracked.entity as Record<string, unknown>)[key];
401
- if (val === undefined || val === null) return null;
402
- if (typeof val !== 'string' && typeof val !== 'number') return null;
403
- return val;
404
- }
405
- }
399
+ private getPrimaryKeyValue(tracked: TrackedEntity): PrimaryKey | null {
400
+ const key = findPrimaryKey(tracked.table);
401
+ const val = (tracked.entity as Record<string, unknown>)[key];
402
+ if (val === undefined || val === null) return null;
403
+ if (typeof val !== 'string' && typeof val !== 'number') return null;
404
+ return val as PrimaryKey;
405
+ }
406
+ }