metal-orm 1.0.64 → 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/src/orm/entity.ts CHANGED
@@ -1,124 +1,124 @@
1
- import { TableDef } from '../schema/table.js';
2
- import { EntityInstance } from '../schema/types.js';
3
- import { EntityContext } from './entity-context.js';
4
- import { ENTITY_META, EntityMeta, RelationKey } from './entity-meta.js';
5
- import { findPrimaryKey } from '../query-builder/hydration-planner.js';
6
- import { RelationIncludeOptions } from '../query-builder/relation-types.js';
7
- import { populateHydrationCache } from './entity-hydration.js';
8
- import { getRelationWrapper, RelationEntityFactory } from './entity-relations.js';
9
-
10
- export { relationLoaderCache } from './entity-relation-cache.js';
11
-
12
- /**
13
- * Creates an entity proxy with lazy loading capabilities.
14
- * @template TTable - The table type
15
- * @template TLazy - The lazy relation keys
16
- * @param ctx - The entity context
17
- * @param table - The table definition
18
- * @param row - The database row
19
- * @param lazyRelations - Optional lazy relations
20
- * @returns The entity instance
21
- */
22
- export const createEntityProxy = <
23
- TTable extends TableDef,
24
- TLazy extends RelationKey<TTable> = RelationKey<TTable>
25
- >(
26
- ctx: EntityContext,
27
- table: TTable,
28
- row: Record<string, unknown>,
29
- lazyRelations: TLazy[] = [] as TLazy[],
30
- lazyRelationOptions: Map<string, RelationIncludeOptions> = new Map()
31
- ): EntityInstance<TTable> => {
32
- const target: Record<string, unknown> = { ...row };
33
- const meta: EntityMeta<TTable> = {
34
- ctx,
35
- table,
36
- lazyRelations: [...lazyRelations],
37
- lazyRelationOptions: new Map(lazyRelationOptions),
38
- relationCache: new Map(),
39
- relationHydration: new Map(),
40
- relationWrappers: new Map()
41
- };
42
-
43
- const createRelationEntity: RelationEntityFactory = (relationTable, relationRow) =>
44
- createEntityFromRow(meta.ctx, relationTable, relationRow);
45
-
46
- Object.defineProperty(target, ENTITY_META, {
47
- value: meta,
48
- enumerable: false,
49
- writable: false
50
- });
51
-
52
- const handler: ProxyHandler<object> = {
53
- get(targetObj, prop, receiver) {
54
- if (prop === ENTITY_META) {
55
- return meta;
56
- }
57
-
58
- if (prop === '$load') {
59
- return async (relationName: RelationKey<TTable>) => {
60
- const wrapper = getRelationWrapper(meta, relationName, receiver, createRelationEntity);
61
- if (wrapper && typeof wrapper.load === 'function') {
62
- return wrapper.load();
63
- }
64
- return undefined;
65
- };
66
- }
67
-
68
- if (typeof prop === 'string' && table.relations[prop]) {
69
- return getRelationWrapper(meta, prop as RelationKey<TTable>, receiver, createRelationEntity);
70
- }
71
-
72
- return Reflect.get(targetObj, prop, receiver);
73
- },
74
-
75
- set(targetObj, prop, value, receiver) {
76
- const result = Reflect.set(targetObj, prop, value, receiver);
77
- if (typeof prop === 'string' && table.columns[prop]) {
78
- ctx.markDirty(receiver);
79
- }
80
- return result;
81
- }
82
- };
83
-
84
- const proxy = new Proxy(target, handler) as EntityInstance<TTable>;
85
- populateHydrationCache(proxy, row, meta);
86
- return proxy;
87
- };
88
-
89
- /**
90
- * Creates an entity instance from a database row.
91
- * @template TTable - The table type
92
- * @template TResult - The result type
93
- * @param ctx - The entity context
94
- * @param table - The table definition
95
- * @param row - The database row
96
- * @param lazyRelations - Optional lazy relations
97
- * @returns The entity instance
98
- */
99
- export const createEntityFromRow = <
100
- TTable extends TableDef,
101
- TResult extends EntityInstance<TTable> = EntityInstance<TTable>
102
- >(
103
- ctx: EntityContext,
104
- table: TTable,
105
- row: Record<string, unknown>,
106
- lazyRelations: RelationKey<TTable>[] = [],
107
- lazyRelationOptions: Map<string, RelationIncludeOptions> = new Map()
108
- ): TResult => {
109
- const pkName = findPrimaryKey(table);
110
- const pkValue = row[pkName];
111
- if (pkValue !== undefined && pkValue !== null) {
112
- const tracked = ctx.getEntity(table, pkValue);
113
- if (tracked) return tracked as TResult;
114
- }
115
-
116
- const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
117
- if (pkValue !== undefined && pkValue !== null) {
118
- ctx.trackManaged(table, pkValue, entity);
119
- } else {
120
- ctx.trackNew(table, entity);
121
- }
122
-
123
- return entity as TResult;
124
- };
1
+ import { TableDef } from '../schema/table.js';
2
+ import { EntityInstance } from '../schema/types.js';
3
+ import type { EntityContext, PrimaryKey } from './entity-context.js';
4
+ import { ENTITY_META, EntityMeta, RelationKey } from './entity-meta.js';
5
+ import { findPrimaryKey } from '../query-builder/hydration-planner.js';
6
+ import { RelationIncludeOptions } from '../query-builder/relation-types.js';
7
+ import { populateHydrationCache } from './entity-hydration.js';
8
+ import { getRelationWrapper, RelationEntityFactory } from './entity-relations.js';
9
+
10
+ export { relationLoaderCache } from './entity-relation-cache.js';
11
+
12
+ /**
13
+ * Creates an entity proxy with lazy loading capabilities.
14
+ * @template TTable - The table type
15
+ * @template TLazy - The lazy relation keys
16
+ * @param ctx - The entity context
17
+ * @param table - The table definition
18
+ * @param row - The database row
19
+ * @param lazyRelations - Optional lazy relations
20
+ * @returns The entity instance
21
+ */
22
+ export const createEntityProxy = <
23
+ TTable extends TableDef,
24
+ TLazy extends RelationKey<TTable> = RelationKey<TTable>
25
+ >(
26
+ ctx: EntityContext,
27
+ table: TTable,
28
+ row: Record<string, unknown>,
29
+ lazyRelations: TLazy[] = [] as TLazy[],
30
+ lazyRelationOptions: Map<string, RelationIncludeOptions> = new Map()
31
+ ): EntityInstance<TTable> => {
32
+ const target: Record<string, unknown> = { ...row };
33
+ const meta: EntityMeta<TTable> = {
34
+ ctx,
35
+ table,
36
+ lazyRelations: [...lazyRelations],
37
+ lazyRelationOptions: new Map(lazyRelationOptions),
38
+ relationCache: new Map(),
39
+ relationHydration: new Map(),
40
+ relationWrappers: new Map()
41
+ };
42
+
43
+ const createRelationEntity: RelationEntityFactory = (relationTable, relationRow) =>
44
+ createEntityFromRow(meta.ctx, relationTable, relationRow);
45
+
46
+ Object.defineProperty(target, ENTITY_META, {
47
+ value: meta,
48
+ enumerable: false,
49
+ writable: false
50
+ });
51
+
52
+ const handler: ProxyHandler<object> = {
53
+ get(targetObj, prop, receiver) {
54
+ if (prop === ENTITY_META) {
55
+ return meta;
56
+ }
57
+
58
+ if (prop === '$load') {
59
+ return async (relationName: RelationKey<TTable>) => {
60
+ const wrapper = getRelationWrapper(meta, relationName, receiver, createRelationEntity);
61
+ if (wrapper && typeof wrapper.load === 'function') {
62
+ return wrapper.load();
63
+ }
64
+ return undefined;
65
+ };
66
+ }
67
+
68
+ if (typeof prop === 'string' && table.relations[prop]) {
69
+ return getRelationWrapper(meta, prop as RelationKey<TTable>, receiver, createRelationEntity);
70
+ }
71
+
72
+ return Reflect.get(targetObj, prop, receiver);
73
+ },
74
+
75
+ set(targetObj, prop, value, receiver) {
76
+ const result = Reflect.set(targetObj, prop, value, receiver);
77
+ if (typeof prop === 'string' && table.columns[prop]) {
78
+ ctx.markDirty(receiver);
79
+ }
80
+ return result;
81
+ }
82
+ };
83
+
84
+ const proxy = new Proxy(target, handler) as EntityInstance<TTable>;
85
+ populateHydrationCache(proxy, row, meta);
86
+ return proxy;
87
+ };
88
+
89
+ /**
90
+ * Creates an entity instance from a database row.
91
+ * @template TTable - The table type
92
+ * @template TResult - The result type
93
+ * @param ctx - The entity context
94
+ * @param table - The table definition
95
+ * @param row - The database row
96
+ * @param lazyRelations - Optional lazy relations
97
+ * @returns The entity instance
98
+ */
99
+ export const createEntityFromRow = <
100
+ TTable extends TableDef,
101
+ TResult extends EntityInstance<TTable> = EntityInstance<TTable>
102
+ >(
103
+ ctx: EntityContext,
104
+ table: TTable,
105
+ row: Record<string, unknown>,
106
+ lazyRelations: RelationKey<TTable>[] = [],
107
+ lazyRelationOptions: Map<string, RelationIncludeOptions> = new Map()
108
+ ): TResult => {
109
+ const pkName = findPrimaryKey(table);
110
+ const pkValue = row[pkName];
111
+ if (pkValue !== undefined && pkValue !== null) {
112
+ const tracked = ctx.getEntity(table, pkValue as PrimaryKey);
113
+ if (tracked) return tracked as TResult;
114
+ }
115
+
116
+ const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
117
+ if (pkValue !== undefined && pkValue !== null) {
118
+ ctx.trackManaged(table, pkValue as PrimaryKey, entity);
119
+ } else {
120
+ ctx.trackNew(table, entity);
121
+ }
122
+
123
+ return entity as TResult;
124
+ };
@@ -1,25 +1,25 @@
1
- import { TableDef } from '../schema/table.js';
2
- import { EntityInstance } from '../schema/types.js';
3
- import { RelationKinds } from '../schema/relation.js';
4
- import { hydrateRows } from './hydration.js';
5
- import { OrmSession } from './orm-session.ts';
6
- import { SelectQueryBuilder } from '../query-builder/select.js';
7
- import {
8
- createEntityProxy,
9
- createEntityFromRow,
10
- relationLoaderCache
11
- } from './entity.js';
12
- import { EntityContext } from './entity-context.js';
13
- import { ExecutionContext } from './execution-context.js';
14
- import { HydrationContext } from './hydration-context.js';
15
- import { RelationIncludeOptions } from '../query-builder/relation-types.js';
16
- import { getEntityMeta, RelationKey } from './entity-meta.js';
17
- import {
18
- loadHasManyRelation,
19
- loadHasOneRelation,
20
- loadBelongsToRelation,
21
- loadBelongsToManyRelation
22
- } from './lazy-batch.js';
1
+ import { TableDef } from '../schema/table.js';
2
+ import { EntityInstance } from '../schema/types.js';
3
+ import { RelationKinds } from '../schema/relation.js';
4
+ import { hydrateRows } from './hydration.js';
5
+ import { OrmSession } from './orm-session.ts';
6
+ import { SelectQueryBuilder } from '../query-builder/select.js';
7
+ import {
8
+ createEntityProxy,
9
+ createEntityFromRow,
10
+ relationLoaderCache
11
+ } from './entity.js';
12
+ import { EntityContext } from './entity-context.js';
13
+ import { ExecutionContext } from './execution-context.js';
14
+ import { HydrationContext } from './hydration-context.js';
15
+ import { RelationIncludeOptions } from '../query-builder/relation-types.js';
16
+ import { getEntityMeta, RelationKey } from './entity-meta.js';
17
+ import {
18
+ loadHasManyRelation,
19
+ loadHasOneRelation,
20
+ loadBelongsToRelation,
21
+ loadBelongsToManyRelation
22
+ } from './lazy-batch.js';
23
23
 
24
24
  type Row = Record<string, unknown>;
25
25
 
@@ -38,152 +38,152 @@ const flattenResults = (results: { columns: string[]; values: unknown[][] }[]):
38
38
  return rows;
39
39
  };
40
40
 
41
- const executeWithContexts = async <TTable extends TableDef>(
42
- execCtx: ExecutionContext,
43
- entityCtx: EntityContext,
44
- qb: SelectQueryBuilder<unknown, TTable>
45
- ): Promise<EntityInstance<TTable>[]> => {
46
- const ast = qb.getAST();
47
- const compiled = execCtx.dialect.compileSelect(ast);
48
- const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
49
- const rows = flattenResults(executed);
50
- const lazyRelations = qb.getLazyRelations() as RelationKey<TTable>[];
51
- const lazyRelationOptions = qb.getLazyRelationOptions();
52
-
53
- if (ast.setOps && ast.setOps.length > 0) {
54
- const proxies = rows.map(row => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
55
- await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
56
- return proxies;
57
- }
58
-
59
- const hydrated = hydrateRows(rows, qb.getHydrationPlan());
60
- const entities = hydrated.map(row => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
61
- await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
62
- return entities;
63
- };
64
-
65
- const executePlainWithContexts = async <TTable extends TableDef>(
66
- execCtx: ExecutionContext,
67
- qb: SelectQueryBuilder<unknown, TTable>
68
- ): Promise<Record<string, unknown>[]> => {
69
- const ast = qb.getAST();
70
- const compiled = execCtx.dialect.compileSelect(ast);
71
- const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
72
- const rows = flattenResults(executed);
73
-
74
- if (ast.setOps && ast.setOps.length > 0) {
75
- return rows;
76
- }
77
-
78
- return hydrateRows(rows, qb.getHydrationPlan());
79
- };
80
-
81
- /**
82
- * Executes a hydrated query using the ORM session.
83
- * @template TTable - The table type
84
- * @param session - The ORM session
85
- * @param qb - The select query builder
41
+ const executeWithContexts = async <TTable extends TableDef>(
42
+ execCtx: ExecutionContext,
43
+ entityCtx: EntityContext,
44
+ qb: SelectQueryBuilder<unknown, TTable>
45
+ ): Promise<EntityInstance<TTable>[]> => {
46
+ const ast = qb.getAST();
47
+ const compiled = execCtx.dialect.compileSelect(ast);
48
+ const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
49
+ const rows = flattenResults(executed);
50
+ const lazyRelations = qb.getLazyRelations() as RelationKey<TTable>[];
51
+ const lazyRelationOptions = qb.getLazyRelationOptions();
52
+
53
+ if (ast.setOps && ast.setOps.length > 0) {
54
+ const proxies = rows.map(row => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
55
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
56
+ return proxies;
57
+ }
58
+
59
+ const hydrated = hydrateRows(rows, qb.getHydrationPlan());
60
+ const entities = hydrated.map(row => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
61
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
62
+ return entities;
63
+ };
64
+
65
+ const executePlainWithContexts = async <TTable extends TableDef>(
66
+ execCtx: ExecutionContext,
67
+ qb: SelectQueryBuilder<unknown, TTable>
68
+ ): Promise<Record<string, unknown>[]> => {
69
+ const ast = qb.getAST();
70
+ const compiled = execCtx.dialect.compileSelect(ast);
71
+ const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
72
+ const rows = flattenResults(executed);
73
+
74
+ if (ast.setOps && ast.setOps.length > 0) {
75
+ return rows;
76
+ }
77
+
78
+ return hydrateRows(rows, qb.getHydrationPlan());
79
+ };
80
+
81
+ /**
82
+ * Executes a hydrated query using the ORM session.
83
+ * @template TTable - The table type
84
+ * @param session - The ORM session
85
+ * @param qb - The select query builder
86
86
  * @returns Promise resolving to array of entity instances
87
87
  */
88
- export async function executeHydrated<TTable extends TableDef>(
89
- session: OrmSession,
90
- qb: SelectQueryBuilder<unknown, TTable>
91
- ): Promise<EntityInstance<TTable>[]> {
92
- return executeWithContexts(session.getExecutionContext(), session, qb);
93
- }
94
-
95
- /**
96
- * Executes a hydrated query and returns plain row objects (no entity proxies).
97
- * @template TTable - The table type
98
- * @param session - The ORM session
99
- * @param qb - The select query builder
100
- * @returns Promise resolving to array of plain row objects
101
- */
102
- export async function executeHydratedPlain<TTable extends TableDef>(
103
- session: OrmSession,
104
- qb: SelectQueryBuilder<unknown, TTable>
105
- ): Promise<Record<string, unknown>[]> {
106
- return executePlainWithContexts(session.getExecutionContext(), qb);
107
- }
108
-
109
- /**
110
- * Executes a hydrated query using execution and hydration contexts.
111
- * @template TTable - The table type
112
- * @param _execCtx - The execution context (unused)
113
- * @param hydCtx - The hydration context
88
+ export async function executeHydrated<TTable extends TableDef>(
89
+ session: OrmSession,
90
+ qb: SelectQueryBuilder<unknown, TTable>
91
+ ): Promise<EntityInstance<TTable>[]> {
92
+ return executeWithContexts(session.getExecutionContext(), session, qb);
93
+ }
94
+
95
+ /**
96
+ * Executes a hydrated query and returns plain row objects (no entity proxies).
97
+ * @template TTable - The table type
98
+ * @param session - The ORM session
99
+ * @param qb - The select query builder
100
+ * @returns Promise resolving to array of plain row objects
101
+ */
102
+ export async function executeHydratedPlain<TTable extends TableDef>(
103
+ session: OrmSession,
104
+ qb: SelectQueryBuilder<unknown, TTable>
105
+ ): Promise<Record<string, unknown>[]> {
106
+ return executePlainWithContexts(session.getExecutionContext(), qb);
107
+ }
108
+
109
+ /**
110
+ * Executes a hydrated query using execution and hydration contexts.
111
+ * @template TTable - The table type
112
+ * @param _execCtx - The execution context (unused)
113
+ * @param hydCtx - The hydration context
114
114
  * @param qb - The select query builder
115
115
  * @returns Promise resolving to array of entity instances
116
116
  */
117
- export async function executeHydratedWithContexts<TTable extends TableDef>(
118
- execCtx: ExecutionContext,
119
- hydCtx: HydrationContext,
120
- qb: SelectQueryBuilder<unknown, TTable>
121
- ): Promise<EntityInstance<TTable>[]> {
122
- const entityCtx = hydCtx.entityContext;
123
- if (!entityCtx) {
124
- throw new Error('Hydration context is missing an EntityContext');
125
- }
126
- return executeWithContexts(execCtx, entityCtx, qb);
127
- }
128
-
129
- /**
130
- * Executes a hydrated query using execution context and returns plain row objects.
131
- * @template TTable - The table type
132
- * @param execCtx - The execution context
133
- * @param qb - The select query builder
134
- * @returns Promise resolving to array of plain row objects
135
- */
136
- export async function executeHydratedPlainWithContexts<TTable extends TableDef>(
137
- execCtx: ExecutionContext,
138
- qb: SelectQueryBuilder<unknown, TTable>
139
- ): Promise<Record<string, unknown>[]> {
140
- return executePlainWithContexts(execCtx, qb);
141
- }
142
-
143
- const loadLazyRelationsForTable = async <TTable extends TableDef>(
144
- ctx: EntityContext,
145
- table: TTable,
146
- lazyRelations: RelationKey<TTable>[],
147
- lazyRelationOptions: Map<string, RelationIncludeOptions>
148
- ): Promise<void> => {
149
- if (!lazyRelations.length) return;
150
-
151
- const tracked = ctx.getEntitiesForTable(table);
152
- if (!tracked.length) return;
153
-
154
- const meta = getEntityMeta(tracked[0].entity);
155
- if (!meta) return;
156
-
157
- for (const relationName of lazyRelations) {
158
- const relation = table.relations[relationName as string];
159
- if (!relation) continue;
160
- const key = relationName as string;
161
- const options = lazyRelationOptions.get(key);
162
- if (!options) {
163
- continue;
164
- }
165
-
166
- switch (relation.type) {
167
- case RelationKinds.HasOne:
168
- await relationLoaderCache(meta, key, () =>
169
- loadHasOneRelation(ctx, table, key, relation, options)
170
- );
171
- break;
172
- case RelationKinds.HasMany:
173
- await relationLoaderCache(meta, key, () =>
174
- loadHasManyRelation(ctx, table, key, relation, options)
175
- );
176
- break;
177
- case RelationKinds.BelongsTo:
178
- await relationLoaderCache(meta, key, () =>
179
- loadBelongsToRelation(ctx, table, key, relation, options)
180
- );
181
- break;
182
- case RelationKinds.BelongsToMany:
183
- await relationLoaderCache(meta, key, () =>
184
- loadBelongsToManyRelation(ctx, table, key, relation, options)
185
- );
186
- break;
187
- }
188
- }
189
- };
117
+ export async function executeHydratedWithContexts<TTable extends TableDef>(
118
+ execCtx: ExecutionContext,
119
+ hydCtx: HydrationContext,
120
+ qb: SelectQueryBuilder<unknown, TTable>
121
+ ): Promise<EntityInstance<TTable>[]> {
122
+ const entityCtx = hydCtx.entityContext;
123
+ if (!entityCtx) {
124
+ throw new Error('Hydration context is missing an EntityContext');
125
+ }
126
+ return executeWithContexts(execCtx, entityCtx, qb);
127
+ }
128
+
129
+ /**
130
+ * Executes a hydrated query using execution context and returns plain row objects.
131
+ * @template TTable - The table type
132
+ * @param execCtx - The execution context
133
+ * @param qb - The select query builder
134
+ * @returns Promise resolving to array of plain row objects
135
+ */
136
+ export async function executeHydratedPlainWithContexts<TTable extends TableDef>(
137
+ execCtx: ExecutionContext,
138
+ qb: SelectQueryBuilder<unknown, TTable>
139
+ ): Promise<Record<string, unknown>[]> {
140
+ return executePlainWithContexts(execCtx, qb);
141
+ }
142
+
143
+ const loadLazyRelationsForTable = async <TTable extends TableDef>(
144
+ ctx: EntityContext,
145
+ table: TTable,
146
+ lazyRelations: RelationKey<TTable>[],
147
+ lazyRelationOptions: Map<string, RelationIncludeOptions>
148
+ ): Promise<void> => {
149
+ if (!lazyRelations.length) return;
150
+
151
+ const tracked = ctx.getEntitiesForTable(table);
152
+ if (!tracked.length) return;
153
+
154
+ const meta = getEntityMeta(tracked[0].entity);
155
+ if (!meta) return;
156
+
157
+ for (const relationName of lazyRelations) {
158
+ const relation = table.relations[relationName as string];
159
+ if (!relation) continue;
160
+ const key = relationName as string;
161
+ const options = lazyRelationOptions.get(key);
162
+ if (!options) {
163
+ continue;
164
+ }
165
+
166
+ switch (relation.type) {
167
+ case RelationKinds.HasOne:
168
+ await relationLoaderCache(meta, key, () =>
169
+ loadHasOneRelation(ctx, table, key, relation, options)
170
+ );
171
+ break;
172
+ case RelationKinds.HasMany:
173
+ await relationLoaderCache(meta, key, () =>
174
+ loadHasManyRelation(ctx, table, key, relation, options)
175
+ );
176
+ break;
177
+ case RelationKinds.BelongsTo:
178
+ await relationLoaderCache(meta, key, () =>
179
+ loadBelongsToRelation(ctx, table, key, relation, options)
180
+ );
181
+ break;
182
+ case RelationKinds.BelongsToMany:
183
+ await relationLoaderCache(meta, key, () =>
184
+ loadBelongsToManyRelation(ctx, table, key, relation, options)
185
+ );
186
+ break;
187
+ }
188
+ }
189
+ };
@@ -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): unknown | 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
  }
@@ -71,7 +71,7 @@ export const toKey = (value: unknown): string => (value === null || value === un
71
71
  export const collectKeysFromRoots = (roots: EntityTracker[], key: string): Set<unknown> => {
72
72
  const collected = new Set<unknown>();
73
73
  for (const tracked of roots) {
74
- const value = tracked.entity[key];
74
+ const value = (tracked.entity as Record<string, unknown>)[key];
75
75
  if (value !== null && value !== undefined) {
76
76
  collected.add(value);
77
77
  }