metal-orm 1.0.57 → 1.0.58

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,69 +1,69 @@
1
- {
2
- "name": "metal-orm",
3
- "version": "1.0.57",
4
- "type": "module",
5
- "types": "./dist/index.d.ts",
6
- "engines": {
7
- "node": ">=20.0.0"
8
- },
9
- "bin": {
10
- "metal-orm-gen": "./scripts/generate-entities.mjs"
11
- },
12
- "exports": {
13
- ".": {
14
- "types": "./dist/index.d.ts",
15
- "import": "./dist/index.js",
16
- "require": "./dist/index.cjs"
17
- }
18
- },
19
- "files": [
20
- "dist",
21
- "src",
22
- "global.d.ts",
23
- "scripts"
24
- ],
25
- "scripts": {
26
- "build": "tsup",
27
- "check": "tsc --noEmit",
28
- "gen:entities": "node scripts/generate-entities.mjs",
29
- "test": "vitest",
30
- "test:ui": "vitest --ui",
31
- "show-sql": "node scripts/show-sql.mjs",
32
- "lint": "node scripts/run-eslint.mjs",
33
- "lint:fix": "node scripts/run-eslint.mjs --fix"
34
- },
35
- "peerDependencies": {
36
- "mysql2": "^3.9.0",
37
- "pg": "^8.0.0",
38
- "sqlite3": "^5.1.6",
39
- "tedious": "^16.0.0"
40
- },
41
- "peerDependenciesMeta": {
42
- "mysql2": {
43
- "optional": true
44
- },
45
- "pg": {
46
- "optional": true
47
- },
48
- "sqlite3": {
49
- "optional": true
50
- },
51
- "tedious": {
52
- "optional": true
53
- }
54
- },
55
- "devDependencies": {
56
- "@vitest/ui": "^4.0.14",
57
- "@typescript-eslint/eslint-plugin": "^8.20.0",
58
- "@typescript-eslint/parser": "^8.20.0",
59
- "eslint": "^8.57.0",
60
- "eslint-plugin-deprecation": "^3.0.0",
61
- "mysql2": "^3.15.3",
62
- "pg": "^8.16.3",
63
- "sqlite3": "^5.1.7",
64
- "tedious": "^19.1.3",
65
- "tsup": "^8.0.0",
66
- "typescript": "^5.5.0",
67
- "vitest": "^4.0.14"
68
- }
69
- }
1
+ {
2
+ "name": "metal-orm",
3
+ "version": "1.0.58",
4
+ "type": "module",
5
+ "types": "./dist/index.d.ts",
6
+ "engines": {
7
+ "node": ">=20.0.0"
8
+ },
9
+ "bin": {
10
+ "metal-orm-gen": "./scripts/generate-entities.mjs"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "src",
22
+ "global.d.ts",
23
+ "scripts"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "check": "tsc --noEmit",
28
+ "gen:entities": "node scripts/generate-entities.mjs",
29
+ "test": "vitest",
30
+ "test:ui": "vitest --ui",
31
+ "show-sql": "node scripts/show-sql.mjs",
32
+ "lint": "node scripts/run-eslint.mjs",
33
+ "lint:fix": "node scripts/run-eslint.mjs --fix"
34
+ },
35
+ "peerDependencies": {
36
+ "mysql2": "^3.9.0",
37
+ "pg": "^8.0.0",
38
+ "sqlite3": "^5.1.6",
39
+ "tedious": "^16.0.0"
40
+ },
41
+ "peerDependenciesMeta": {
42
+ "mysql2": {
43
+ "optional": true
44
+ },
45
+ "pg": {
46
+ "optional": true
47
+ },
48
+ "sqlite3": {
49
+ "optional": true
50
+ },
51
+ "tedious": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "devDependencies": {
56
+ "@vitest/ui": "^4.0.14",
57
+ "@typescript-eslint/eslint-plugin": "^8.20.0",
58
+ "@typescript-eslint/parser": "^8.20.0",
59
+ "eslint": "^8.57.0",
60
+ "eslint-plugin-deprecation": "^3.0.0",
61
+ "mysql2": "^3.15.3",
62
+ "pg": "^8.16.3",
63
+ "sqlite3": "^5.1.7",
64
+ "tedious": "^19.1.3",
65
+ "tsup": "^8.0.0",
66
+ "typescript": "^5.5.0",
67
+ "vitest": "^4.0.14"
68
+ }
69
+ }
@@ -5,6 +5,10 @@ import {
5
5
  belongsTo,
6
6
  belongsToMany,
7
7
  RelationKinds,
8
+ type HasManyRelation,
9
+ type HasOneRelation,
10
+ type BelongsToRelation,
11
+ type BelongsToManyRelation,
8
12
  type RelationDef
9
13
  } from '../schema/relation.js';
10
14
  import { TableDef } from '../schema/table.js';
@@ -20,7 +24,14 @@ import {
20
24
  } from '../orm/entity-metadata.js';
21
25
 
22
26
  import { tableRef, type TableRef } from '../schema/table.js';
23
- import { SelectableKeys, ColumnDef } from '../schema/types.js';
27
+ import {
28
+ SelectableKeys,
29
+ ColumnDef,
30
+ HasManyCollection,
31
+ HasOneReference,
32
+ BelongsToReference,
33
+ ManyToManyCollection
34
+ } from '../schema/types.js';
24
35
 
25
36
  const unwrapTarget = (target: EntityOrTableTargetResolver): EntityOrTableTarget => {
26
37
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
@@ -144,14 +155,39 @@ export const getTableDefFromEntity = <TTable extends TableDef = TableDef>(ctor:
144
155
  * @param ctor - The entity constructor.
145
156
  * @returns A select query builder for the entity.
146
157
  */
158
+ type NonFunctionKeys<T> = {
159
+ [K in keyof T]-?: T[K] extends (...args: unknown[]) => unknown ? never : K
160
+ }[keyof T];
161
+
162
+ type RelationKeys<TEntity extends object> =
163
+ Exclude<NonFunctionKeys<TEntity>, SelectableKeys<TEntity>> & string;
164
+
165
+ type EntityTable<TEntity extends object> =
166
+ Omit<TableDef<{ [K in SelectableKeys<TEntity>]: ColumnDef }>, 'relations'> & {
167
+ relations: {
168
+ [K in RelationKeys<TEntity>]:
169
+ NonNullable<TEntity[K]> extends HasManyCollection<infer TChild>
170
+ ? HasManyRelation<EntityTable<NonNullable<TChild> & object>>
171
+ : NonNullable<TEntity[K]> extends ManyToManyCollection<infer TTarget>
172
+ ? BelongsToManyRelation<EntityTable<NonNullable<TTarget> & object>>
173
+ : NonNullable<TEntity[K]> extends HasOneReference<infer TChild>
174
+ ? HasOneRelation<EntityTable<NonNullable<TChild> & object>>
175
+ : NonNullable<TEntity[K]> extends BelongsToReference<infer TParent>
176
+ ? BelongsToRelation<EntityTable<NonNullable<TParent> & object>>
177
+ : NonNullable<TEntity[K]> extends object
178
+ ? BelongsToRelation<EntityTable<NonNullable<TEntity[K]> & object>>
179
+ : never;
180
+ };
181
+ };
182
+
147
183
  export const selectFromEntity = <TEntity extends object>(
148
184
  ctor: EntityConstructor<TEntity>
149
- ): SelectQueryBuilder<unknown, TableDef<{ [K in SelectableKeys<TEntity>]: ColumnDef }>> => {
185
+ ): SelectQueryBuilder<unknown, EntityTable<TEntity>> => {
150
186
  const table = getTableDefFromEntity(ctor);
151
187
  if (!table) {
152
188
  throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
153
189
  }
154
- return new SelectQueryBuilder(table as unknown as TableDef<{ [K in SelectableKeys<TEntity>]: ColumnDef }>);
190
+ return new SelectQueryBuilder(table as unknown as EntityTable<TEntity>);
155
191
  };
156
192
 
157
193
  /**
@@ -1,4 +1,5 @@
1
- import { TableDef } from '../schema/table.js';
1
+ import { TableDef } from '../schema/table.js';
2
+ import { RelationIncludeOptions } from '../query-builder/relation-types.js';
2
3
  import { EntityContext } from './entity-context.js';
3
4
  import { RelationMap } from '../schema/types.js';
4
5
 
@@ -18,8 +19,10 @@ export interface EntityMeta<TTable extends TableDef> {
18
19
  ctx: EntityContext;
19
20
  /** Table definition */
20
21
  table: TTable;
21
- /** Relations that should be loaded lazily */
22
- lazyRelations: (keyof RelationMap<TTable>)[];
22
+ /** Relations that should be loaded lazily */
23
+ lazyRelations: (keyof RelationMap<TTable>)[];
24
+ /** Include options for lazy relations */
25
+ lazyRelationOptions: Map<string, RelationIncludeOptions>;
23
26
  /** Cache for relation promises */
24
27
  relationCache: Map<string, Promise<unknown>>;
25
28
  /** Hydration data for relations */
package/src/orm/entity.ts CHANGED
@@ -9,6 +9,7 @@ import { DefaultManyToManyCollection } from './relations/many-to-many.js';
9
9
  import { HasManyRelation, HasOneRelation, BelongsToRelation, BelongsToManyRelation, RelationKinds } from '../schema/relation.js';
10
10
  import { loadHasManyRelation, loadHasOneRelation, loadBelongsToRelation, loadBelongsToManyRelation } from './lazy-batch.js';
11
11
  import { findPrimaryKey } from '../query-builder/hydration-planner.js';
12
+ import { RelationIncludeOptions } from '../query-builder/relation-types.js';
12
13
 
13
14
  /**
14
15
  * Type representing an array of database rows.
@@ -23,7 +24,7 @@ type Rows = Record<string, unknown>[];
23
24
  * @param factory - The factory function to create the cache
24
25
  * @returns Promise with the cached relation data
25
26
  */
26
- const relationLoaderCache = <T extends Map<string, unknown>>(
27
+ export const relationLoaderCache = <T extends Map<string, unknown>>(
27
28
  meta: EntityMeta<TableDef>,
28
29
  relationName: string,
29
30
  factory: () => Promise<T>
@@ -69,13 +70,15 @@ export const createEntityProxy = <
69
70
  ctx: EntityContext,
70
71
  table: TTable,
71
72
  row: Record<string, unknown>,
72
- lazyRelations: TLazy[] = [] as TLazy[]
73
+ lazyRelations: TLazy[] = [] as TLazy[],
74
+ lazyRelationOptions: Map<string, RelationIncludeOptions> = new Map()
73
75
  ): EntityInstance<TTable> => {
74
76
  const target: Record<string, unknown> = { ...row };
75
77
  const meta: EntityMeta<TTable> = {
76
78
  ctx,
77
79
  table,
78
80
  lazyRelations: [...lazyRelations],
81
+ lazyRelationOptions: new Map(lazyRelationOptions),
79
82
  relationCache: new Map(),
80
83
  relationHydration: new Map(),
81
84
  relationWrappers: new Map()
@@ -141,7 +144,8 @@ export const createEntityFromRow = <
141
144
  ctx: EntityContext,
142
145
  table: TTable,
143
146
  row: Record<string, unknown>,
144
- lazyRelations: (keyof RelationMap<TTable>)[] = []
147
+ lazyRelations: (keyof RelationMap<TTable>)[] = [],
148
+ lazyRelationOptions: Map<string, RelationIncludeOptions> = new Map()
145
149
  ): TResult => {
146
150
  const pkName = findPrimaryKey(table);
147
151
  const pkValue = row[pkName];
@@ -150,7 +154,7 @@ export const createEntityFromRow = <
150
154
  if (tracked) return tracked as TResult;
151
155
  }
152
156
 
153
- const entity = createEntityProxy(ctx, table, row, lazyRelations);
157
+ const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
154
158
  if (pkValue !== undefined && pkValue !== null) {
155
159
  ctx.trackManaged(table, pkValue, entity);
156
160
  } else {
@@ -223,6 +227,68 @@ const populateHydrationCache = <TTable extends TableDef>(
223
227
  }
224
228
  };
225
229
 
230
+ const proxifyRelationWrapper = <T extends object>(wrapper: T): T => {
231
+ return new Proxy(wrapper, {
232
+ get(target, prop, receiver) {
233
+ if (typeof prop === 'symbol') {
234
+ return Reflect.get(target, prop, receiver);
235
+ }
236
+
237
+ if (prop in target) {
238
+ return Reflect.get(target, prop, receiver);
239
+ }
240
+
241
+ const getItems = (target as { getItems?: () => unknown }).getItems;
242
+ if (typeof getItems === 'function') {
243
+ const items = getItems.call(target);
244
+ if (items && prop in (items as object)) {
245
+ const propName = prop as string;
246
+ const value = (items as Record<string, unknown>)[propName];
247
+ return typeof value === 'function' ? value.bind(items) : value;
248
+ }
249
+ }
250
+
251
+ const getRef = (target as { get?: () => unknown }).get;
252
+ if (typeof getRef === 'function') {
253
+ const current = getRef.call(target);
254
+ if (current && prop in (current as object)) {
255
+ const propName = prop as string;
256
+ const value = (current as Record<string, unknown>)[propName];
257
+ return typeof value === 'function' ? value.bind(current) : value;
258
+ }
259
+ }
260
+
261
+ return undefined;
262
+ },
263
+
264
+ set(target, prop, value, receiver) {
265
+ if (typeof prop === 'symbol') {
266
+ return Reflect.set(target, prop, value, receiver);
267
+ }
268
+
269
+ if (prop in target) {
270
+ return Reflect.set(target, prop, value, receiver);
271
+ }
272
+
273
+ const getRef = (target as { get?: () => unknown }).get;
274
+ if (typeof getRef === 'function') {
275
+ const current = getRef.call(target);
276
+ if (current && typeof current === 'object') {
277
+ return Reflect.set(current as object, prop, value);
278
+ }
279
+ }
280
+
281
+ const getItems = (target as { getItems?: () => unknown }).getItems;
282
+ if (typeof getItems === 'function') {
283
+ const items = getItems.call(target);
284
+ return Reflect.set(items as object, prop, value);
285
+ }
286
+
287
+ return Reflect.set(target, prop, value, receiver);
288
+ }
289
+ });
290
+ };
291
+
226
292
  /**
227
293
  * Gets a relation wrapper for an entity.
228
294
  * @param meta - The entity metadata
@@ -234,7 +300,7 @@ const getRelationWrapper = (
234
300
  meta: EntityMeta<TableDef>,
235
301
  relationName: string,
236
302
  owner: unknown
237
- ): HasManyCollection<unknown> | HasOneReference<unknown> | BelongsToReference<unknown> | ManyToManyCollection<unknown> | undefined => {
303
+ ): HasManyCollection<unknown> | HasOneReference<object> | BelongsToReference<object> | ManyToManyCollection<unknown> | undefined => {
238
304
  if (meta.relationWrappers.has(relationName)) {
239
305
  return meta.relationWrappers.get(relationName) as HasManyCollection<unknown>;
240
306
  }
@@ -243,11 +309,11 @@ const getRelationWrapper = (
243
309
  if (!relation) return undefined;
244
310
 
245
311
  const wrapper = instantiateWrapper(meta, relationName, relation, owner);
246
- if (wrapper) {
247
- meta.relationWrappers.set(relationName, wrapper);
248
- }
312
+ if (!wrapper) return undefined;
249
313
 
250
- return wrapper;
314
+ const proxied = proxifyRelationWrapper(wrapper as object);
315
+ meta.relationWrappers.set(relationName, proxied);
316
+ return proxied as HasManyCollection<unknown>;
251
317
  };
252
318
 
253
319
  /**
@@ -263,13 +329,14 @@ const instantiateWrapper = (
263
329
  relationName: string,
264
330
  relation: HasManyRelation | HasOneRelation | BelongsToRelation | BelongsToManyRelation,
265
331
  owner: unknown
266
- ): HasManyCollection<unknown> | HasOneReference<unknown> | BelongsToReference<unknown> | ManyToManyCollection<unknown> | undefined => {
332
+ ): HasManyCollection<unknown> | HasOneReference<object> | BelongsToReference<object> | ManyToManyCollection<unknown> | undefined => {
333
+ const lazyOptions = meta.lazyRelationOptions.get(relationName);
267
334
  switch (relation.type) {
268
335
  case RelationKinds.HasOne: {
269
336
  const hasOne = relation as HasOneRelation;
270
337
  const localKey = hasOne.localKey || findPrimaryKey(meta.table);
271
338
  const loader = () => relationLoaderCache(meta, relationName, () =>
272
- loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne)
339
+ loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne, lazyOptions)
273
340
  );
274
341
  return new DefaultHasOneReference(
275
342
  meta.ctx,
@@ -287,7 +354,7 @@ const instantiateWrapper = (
287
354
  const hasMany = relation as HasManyRelation;
288
355
  const localKey = hasMany.localKey || findPrimaryKey(meta.table);
289
356
  const loader = () => relationLoaderCache(meta, relationName, () =>
290
- loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany)
357
+ loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany, lazyOptions)
291
358
  );
292
359
  return new DefaultHasManyCollection(
293
360
  meta.ctx,
@@ -305,7 +372,7 @@ const instantiateWrapper = (
305
372
  const belongsTo = relation as BelongsToRelation;
306
373
  const targetKey = belongsTo.localKey || findPrimaryKey(belongsTo.target);
307
374
  const loader = () => relationLoaderCache(meta, relationName, () =>
308
- loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo)
375
+ loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo, lazyOptions)
309
376
  );
310
377
  return new DefaultBelongsToReference(
311
378
  meta.ctx,
@@ -323,7 +390,7 @@ const instantiateWrapper = (
323
390
  const many = relation as BelongsToManyRelation;
324
391
  const localKey = many.localKey || findPrimaryKey(meta.table);
325
392
  const loader = () => relationLoaderCache(meta, relationName, () =>
326
- loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many)
393
+ loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, lazyOptions)
327
394
  );
328
395
  return new DefaultManyToManyCollection(
329
396
  meta.ctx,
@@ -1,16 +1,29 @@
1
- import { TableDef } from '../schema/table.js';
2
- import { EntityInstance } from '../schema/types.js';
3
- import { hydrateRows } from './hydration.js';
4
- import { OrmSession } from './orm-session.ts';
5
- import { SelectQueryBuilder } from '../query-builder/select.js';
6
- import { createEntityProxy, createEntityFromRow } from './entity.js';
1
+ import { TableDef } from '../schema/table.js';
2
+ import { EntityInstance, RelationMap } 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';
7
12
  import { EntityContext } from './entity-context.js';
8
13
  import { ExecutionContext } from './execution-context.js';
9
14
  import { HydrationContext } from './hydration-context.js';
15
+ import { RelationIncludeOptions } from '../query-builder/relation-types.js';
16
+ import { getEntityMeta } from './entity-meta.js';
17
+ import {
18
+ loadHasManyRelation,
19
+ loadHasOneRelation,
20
+ loadBelongsToRelation,
21
+ loadBelongsToManyRelation
22
+ } from './lazy-batch.js';
10
23
 
11
24
  type Row = Record<string, unknown>;
12
25
 
13
- const flattenResults = (results: { columns: string[]; values: unknown[][] }[]): Row[] => {
26
+ const flattenResults = (results: { columns: string[]; values: unknown[][] }[]): Row[] => {
14
27
  const rows: Row[] = [];
15
28
  for (const result of results) {
16
29
  const { columns, values } = result;
@@ -23,8 +36,8 @@ const flattenResults = (results: { columns: string[]; values: unknown[][] }[]):
23
36
  }
24
37
  }
25
38
  return rows;
26
- };
27
-
39
+ };
40
+
28
41
  const executeWithContexts = async <TTable extends TableDef>(
29
42
  execCtx: ExecutionContext,
30
43
  entityCtx: EntityContext,
@@ -34,14 +47,20 @@ const executeWithContexts = async <TTable extends TableDef>(
34
47
  const compiled = execCtx.dialect.compileSelect(ast);
35
48
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
36
49
  const rows = flattenResults(executed);
50
+ const lazyRelations = qb.getLazyRelations();
51
+ const lazyRelationOptions = qb.getLazyRelationOptions();
37
52
 
38
53
  if (ast.setOps && ast.setOps.length > 0) {
39
- return rows.map(row => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
54
+ const proxies = rows.map(row => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
55
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
56
+ return proxies;
40
57
  }
41
-
42
- const hydrated = hydrateRows(rows, qb.getHydrationPlan());
43
- return hydrated.map(row => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
44
- };
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
+ };
45
64
 
46
65
  /**
47
66
  * Executes a hydrated query using the ORM session.
@@ -50,12 +69,12 @@ const executeWithContexts = async <TTable extends TableDef>(
50
69
  * @param qb - The select query builder
51
70
  * @returns Promise resolving to array of entity instances
52
71
  */
53
- export async function executeHydrated<TTable extends TableDef>(
54
- session: OrmSession,
55
- qb: SelectQueryBuilder<unknown, TTable>
56
- ): Promise<EntityInstance<TTable>[]> {
57
- return executeWithContexts(session.getExecutionContext(), session, qb);
58
- }
72
+ export async function executeHydrated<TTable extends TableDef>(
73
+ session: OrmSession,
74
+ qb: SelectQueryBuilder<unknown, TTable>
75
+ ): Promise<EntityInstance<TTable>[]> {
76
+ return executeWithContexts(session.getExecutionContext(), session, qb);
77
+ }
59
78
 
60
79
  /**
61
80
  * Executes a hydrated query using execution and hydration contexts.
@@ -76,3 +95,51 @@ export async function executeHydratedWithContexts<TTable extends TableDef>(
76
95
  }
77
96
  return executeWithContexts(execCtx, entityCtx, qb);
78
97
  }
98
+
99
+ const loadLazyRelationsForTable = async <TTable extends TableDef>(
100
+ ctx: EntityContext,
101
+ table: TTable,
102
+ lazyRelations: (keyof RelationMap<TTable>)[],
103
+ lazyRelationOptions: Map<string, RelationIncludeOptions>
104
+ ): Promise<void> => {
105
+ if (!lazyRelations.length) return;
106
+
107
+ const tracked = ctx.getEntitiesForTable(table);
108
+ if (!tracked.length) return;
109
+
110
+ const meta = getEntityMeta(tracked[0].entity);
111
+ if (!meta) return;
112
+
113
+ for (const relationName of lazyRelations) {
114
+ const relation = table.relations[relationName as string];
115
+ if (!relation) continue;
116
+ const key = relationName as string;
117
+ const options = lazyRelationOptions.get(key);
118
+ if (!options) {
119
+ continue;
120
+ }
121
+
122
+ switch (relation.type) {
123
+ case RelationKinds.HasOne:
124
+ await relationLoaderCache(meta, key, () =>
125
+ loadHasOneRelation(ctx, table, key, relation, options)
126
+ );
127
+ break;
128
+ case RelationKinds.HasMany:
129
+ await relationLoaderCache(meta, key, () =>
130
+ loadHasManyRelation(ctx, table, key, relation, options)
131
+ );
132
+ break;
133
+ case RelationKinds.BelongsTo:
134
+ await relationLoaderCache(meta, key, () =>
135
+ loadBelongsToRelation(ctx, table, key, relation, options)
136
+ );
137
+ break;
138
+ case RelationKinds.BelongsToMany:
139
+ await relationLoaderCache(meta, key, () =>
140
+ loadBelongsToManyRelation(ctx, table, key, relation, options)
141
+ );
142
+ break;
143
+ }
144
+ }
145
+ };