metal-orm 1.0.58 → 1.0.59

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.
Files changed (40) hide show
  1. package/README.md +34 -31
  2. package/dist/index.cjs +1463 -1003
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +148 -129
  5. package/dist/index.d.ts +148 -129
  6. package/dist/index.js +1459 -1003
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/core/ddl/schema-generator.ts +44 -1
  10. package/src/decorators/bootstrap.ts +183 -146
  11. package/src/decorators/column-decorator.ts +8 -49
  12. package/src/decorators/decorator-metadata.ts +10 -46
  13. package/src/decorators/entity.ts +30 -40
  14. package/src/decorators/relations.ts +30 -56
  15. package/src/orm/entity-hydration.ts +72 -0
  16. package/src/orm/entity-meta.ts +13 -11
  17. package/src/orm/entity-metadata.ts +240 -238
  18. package/src/orm/entity-relation-cache.ts +39 -0
  19. package/src/orm/entity-relations.ts +207 -0
  20. package/src/orm/entity.ts +124 -410
  21. package/src/orm/execute.ts +4 -4
  22. package/src/orm/lazy-batch/belongs-to-many.ts +134 -0
  23. package/src/orm/lazy-batch/belongs-to.ts +108 -0
  24. package/src/orm/lazy-batch/has-many.ts +69 -0
  25. package/src/orm/lazy-batch/has-one.ts +68 -0
  26. package/src/orm/lazy-batch/shared.ts +125 -0
  27. package/src/orm/lazy-batch.ts +4 -492
  28. package/src/orm/relations/many-to-many.ts +2 -1
  29. package/src/query-builder/relation-cte-builder.ts +63 -0
  30. package/src/query-builder/relation-filter-utils.ts +159 -0
  31. package/src/query-builder/relation-include-strategies.ts +177 -0
  32. package/src/query-builder/relation-join-planner.ts +80 -0
  33. package/src/query-builder/relation-service.ts +119 -479
  34. package/src/query-builder/relation-types.ts +41 -10
  35. package/src/query-builder/select/projection-facet.ts +23 -23
  36. package/src/query-builder/select/select-operations.ts +145 -0
  37. package/src/query-builder/select.ts +351 -422
  38. package/src/schema/relation.ts +22 -18
  39. package/src/schema/table.ts +22 -9
  40. package/src/schema/types.ts +14 -12
@@ -1,265 +1,267 @@
1
- import { ColumnDef } from '../schema/column-types.js';
2
- import { defineTable, TableDef, TableHooks } from '../schema/table.js';
3
- import { CascadeMode, RelationKinds } from '../schema/relation.js';
4
-
5
- /**
6
- * Constructor type for entities.
7
- * Supports any constructor signature for maximum flexibility with decorator-based entities.
8
- * @template T - The entity type
9
- */
10
- export type EntityConstructor<T = object> = new (...args: never[]) => T;
11
-
12
- /**
13
- * Target that can be an entity constructor or table definition.
14
- */
15
- export type EntityOrTableTarget = EntityConstructor | TableDef;
16
-
17
- /**
18
- * Resolver for entity or table target, can be direct or function.
19
- */
20
- export type EntityOrTableTargetResolver = EntityOrTableTarget | (() => EntityOrTableTarget);
21
-
22
- /**
23
- * Simplified column definition structure used during metadata registration.
24
- * @template T - Concrete column definition type being extended
25
- */
26
- export type ColumnDefLike<T extends ColumnDef = ColumnDef> = Omit<T, 'name' | 'table'>;
27
-
28
- /**
29
- * Transforms simplified column metadata into full ColumnDef objects during table building.
30
- * @template TColumns - Mapping of column names to simplified definitions
31
- */
32
- type MaterializeColumns<TColumns extends Record<string, ColumnDefLike>> = {
33
- [K in keyof TColumns]: ColumnDef<TColumns[K]['type'], TColumns[K]['tsType']> & Omit<
34
- TColumns[K],
35
- 'name' | 'table' | 'type' | 'tsType'
36
- > & { name: string; table: string };
37
- };
38
-
39
- /**
40
- * Common properties shared by all relation metadata types.
41
- */
42
- interface BaseRelationMetadata {
43
- /** The property key for the relation */
44
- propertyKey: string;
45
- /** The target entity or table */
46
- target: EntityOrTableTargetResolver;
47
- /** Optional cascade mode */
48
- cascade?: CascadeMode;
49
- }
50
-
51
- /**
52
- * Metadata for has many relations.
53
- */
1
+ import { ColumnDef } from '../schema/column-types.js';
2
+ import { defineTable, TableDef, TableHooks } from '../schema/table.js';
3
+ import { CascadeMode, RelationKinds } from '../schema/relation.js';
4
+
5
+ /**
6
+ * Constructor type for entities.
7
+ * Supports any constructor signature for maximum flexibility with decorator-based entities.
8
+ * @template T - The entity type
9
+ */
10
+ export type EntityConstructor<T = object> = new (...args: never[]) => T;
11
+
12
+ /**
13
+ * Target that can be an entity constructor or table definition.
14
+ */
15
+ export type EntityOrTableTarget = EntityConstructor | TableDef;
16
+
17
+ /**
18
+ * Resolver for entity or table target, can be direct or function.
19
+ * @typeParam T - Specific target type that should be resolved
20
+ */
21
+ export type EntityOrTableTargetResolver<T extends EntityOrTableTarget = EntityOrTableTarget> =
22
+ T | (() => T);
23
+
24
+ /**
25
+ * Simplified column definition structure used during metadata registration.
26
+ * @template T - Concrete column definition type being extended
27
+ */
28
+ export type ColumnDefLike<T extends ColumnDef = ColumnDef> = Omit<T, 'name' | 'table'>;
29
+
30
+ /**
31
+ * Transforms simplified column metadata into full ColumnDef objects during table building.
32
+ * @template TColumns - Mapping of column names to simplified definitions
33
+ */
34
+ type MaterializeColumns<TColumns extends Record<string, ColumnDefLike>> = {
35
+ [K in keyof TColumns]: ColumnDef<TColumns[K]['type'], TColumns[K]['tsType']> & Omit<
36
+ TColumns[K],
37
+ 'name' | 'table' | 'type' | 'tsType'
38
+ > & { name: string; table: string };
39
+ };
40
+
41
+ /**
42
+ * Common properties shared by all relation metadata types.
43
+ */
44
+ interface BaseRelationMetadata {
45
+ /** The property key for the relation */
46
+ propertyKey: string;
47
+ /** The target entity or table */
48
+ target: EntityOrTableTargetResolver;
49
+ /** Optional cascade mode */
50
+ cascade?: CascadeMode;
51
+ }
52
+
53
+ /**
54
+ * Metadata for has many relations.
55
+ */
54
56
  export interface HasManyRelationMetadata extends BaseRelationMetadata {
55
57
  /** The relation kind */
56
58
  kind: typeof RelationKinds.HasMany;
57
59
  /** The foreign key */
58
- foreignKey: string;
60
+ foreignKey?: string;
59
61
  /** Optional local key */
60
62
  localKey?: string;
61
63
  }
62
-
63
- /**
64
- * Metadata for has one relations.
65
- */
64
+
65
+ /**
66
+ * Metadata for has one relations.
67
+ */
66
68
  export interface HasOneRelationMetadata extends BaseRelationMetadata {
67
69
  /** The relation kind */
68
70
  kind: typeof RelationKinds.HasOne;
69
71
  /** The foreign key */
70
- foreignKey: string;
71
- /** Optional local key */
72
- localKey?: string;
73
- }
74
-
75
- /**
76
- * Metadata for belongs to relations.
77
- */
78
- export interface BelongsToRelationMetadata extends BaseRelationMetadata {
79
- /** The relation kind */
80
- kind: typeof RelationKinds.BelongsTo;
81
- /** The foreign key */
82
- foreignKey: string;
72
+ foreignKey?: string;
83
73
  /** Optional local key */
84
74
  localKey?: string;
85
75
  }
86
-
87
- /**
88
- * Metadata for belongs to many relations.
89
- */
76
+
77
+ /**
78
+ * Metadata for belongs to relations.
79
+ */
80
+ export interface BelongsToRelationMetadata extends BaseRelationMetadata {
81
+ /** The relation kind */
82
+ kind: typeof RelationKinds.BelongsTo;
83
+ /** The foreign key */
84
+ foreignKey: string;
85
+ /** Optional local key */
86
+ localKey?: string;
87
+ }
88
+
89
+ /**
90
+ * Metadata for belongs to many relations.
91
+ */
90
92
  export interface BelongsToManyRelationMetadata extends BaseRelationMetadata {
91
93
  /** The relation kind */
92
94
  kind: typeof RelationKinds.BelongsToMany;
93
95
  /** The pivot table */
94
96
  pivotTable: EntityOrTableTargetResolver;
95
97
  /** The pivot foreign key to root */
96
- pivotForeignKeyToRoot: string;
98
+ pivotForeignKeyToRoot?: string;
97
99
  /** The pivot foreign key to target */
98
- pivotForeignKeyToTarget: string;
100
+ pivotForeignKeyToTarget?: string;
99
101
  /** Optional local key */
100
102
  localKey?: string;
101
103
  /** Optional target key */
102
104
  targetKey?: string;
103
105
  /** Optional pivot primary key */
104
- pivotPrimaryKey?: string;
105
- /** Optional default pivot columns */
106
- defaultPivotColumns?: string[];
107
- }
108
-
109
- /**
110
- * Union type for all relation metadata.
111
- */
112
- export type RelationMetadata =
113
- | HasManyRelationMetadata
114
- | HasOneRelationMetadata
115
- | BelongsToRelationMetadata
116
- | BelongsToManyRelationMetadata;
117
-
118
- /**
119
- * Metadata for entities.
120
- * @template TColumns - The columns type
121
- */
122
- export interface EntityMetadata<TColumns extends Record<string, ColumnDefLike> = Record<string, ColumnDefLike>> {
123
- /** The entity constructor */
124
- target: EntityConstructor;
125
- /** The table name */
126
- tableName: string;
127
- /** The columns */
128
- columns: TColumns;
129
- /** The relations */
130
- relations: Record<string, RelationMetadata>;
131
- /** Optional hooks */
132
- hooks?: TableHooks;
133
- /** Optional table definition */
134
- table?: TableDef<MaterializeColumns<TColumns>>;
135
- }
136
-
137
- const metadataMap = new Map<EntityConstructor, EntityMetadata>();
138
-
139
- /**
140
- * Registers entity metadata.
141
- * @param meta - The entity metadata to register
142
- */
143
- export const registerEntityMetadata = (meta: EntityMetadata): void => {
144
- metadataMap.set(meta.target, meta);
145
- };
146
-
147
- /**
148
- * Ensures entity metadata exists for the target, creating it if necessary.
149
- * @param target - The entity constructor
150
- * @returns The entity metadata
151
- */
152
- export const ensureEntityMetadata = (target: EntityConstructor): EntityMetadata => {
153
- let meta = metadataMap.get(target);
154
- if (!meta) {
155
- meta = {
156
- target,
157
- tableName: target.name || 'unknown',
158
- columns: {},
159
- relations: {}
160
- };
161
- metadataMap.set(target, meta);
162
- }
163
- return meta;
164
- };
165
-
166
- /**
167
- * Gets entity metadata for the target.
168
- * @param target - The entity constructor
169
- * @returns The entity metadata or undefined if not found
170
- */
171
- export const getEntityMetadata = (target: EntityConstructor): EntityMetadata | undefined => {
172
- return metadataMap.get(target);
173
- };
174
-
175
- /**
176
- * Gets all entity metadata.
177
- * @returns Array of all entity metadata
178
- */
179
- export const getAllEntityMetadata = (): EntityMetadata[] => {
180
- return Array.from(metadataMap.values());
181
- };
182
-
183
- /**
184
- * Clears all entity metadata.
185
- */
186
- export const clearEntityMetadata = (): void => {
187
- metadataMap.clear();
188
- };
189
-
190
- /**
191
- * Adds column metadata to an entity.
192
- * @param target - The entity constructor
193
- * @param propertyKey - The property key
194
- * @param column - The column definition
195
- */
196
- export const addColumnMetadata = (
197
- target: EntityConstructor,
198
- propertyKey: string,
199
- column: ColumnDefLike
200
- ): void => {
201
- const meta = ensureEntityMetadata(target);
202
- (meta.columns as Record<string, ColumnDefLike>)[propertyKey] = { ...column };
203
- };
204
-
205
- /**
206
- * Adds relation metadata to an entity.
207
- * @param target - The entity constructor
208
- * @param propertyKey - The property key
209
- * @param relation - The relation metadata
210
- */
211
- export const addRelationMetadata = (
212
- target: EntityConstructor,
213
- propertyKey: string,
214
- relation: RelationMetadata
215
- ): void => {
216
- const meta = ensureEntityMetadata(target);
217
- meta.relations[propertyKey] = relation;
218
- };
219
-
220
- /**
221
- * Sets the table name and hooks for an entity.
222
- * @param target - The entity constructor
223
- * @param tableName - The table name
224
- * @param hooks - Optional table hooks
225
- */
226
- export const setEntityTableName = (
227
- target: EntityConstructor,
228
- tableName: string,
229
- hooks?: TableHooks
230
- ): void => {
231
- const meta = ensureEntityMetadata(target);
232
- if (tableName && tableName.length > 0) {
233
- meta.tableName = tableName;
234
- }
235
- if (hooks) {
236
- meta.hooks = hooks;
237
- }
238
- };
239
-
240
- /**
241
- * Builds a table definition from entity metadata.
242
- * @template TColumns - The columns type
243
- * @param meta - The entity metadata
244
- * @returns The table definition
245
- */
246
- export const buildTableDef = <TColumns extends Record<string, ColumnDefLike>>(meta: EntityMetadata<TColumns>): TableDef<MaterializeColumns<TColumns>> => {
247
- if (meta.table) {
248
- return meta.table;
249
- }
250
-
251
- // Build columns using a simpler approach that avoids type assertion
252
- const columns: Record<string, ColumnDef> = {};
253
- for (const [key, def] of Object.entries(meta.columns)) {
254
- columns[key] = {
255
- ...def,
256
- name: key,
257
- table: meta.tableName
258
- } as ColumnDef;
259
- }
260
-
261
- const table = defineTable(meta.tableName, columns as MaterializeColumns<TColumns>, {}, meta.hooks);
262
- meta.table = table;
263
- return table;
264
- };
106
+ pivotPrimaryKey?: string;
107
+ /** Optional default pivot columns */
108
+ defaultPivotColumns?: string[];
109
+ }
110
+
111
+ /**
112
+ * Union type for all relation metadata.
113
+ */
114
+ export type RelationMetadata =
115
+ | HasManyRelationMetadata
116
+ | HasOneRelationMetadata
117
+ | BelongsToRelationMetadata
118
+ | BelongsToManyRelationMetadata;
119
+
120
+ /**
121
+ * Metadata for entities.
122
+ * @template TColumns - The columns type
123
+ */
124
+ export interface EntityMetadata<TColumns extends Record<string, ColumnDefLike> = Record<string, ColumnDefLike>> {
125
+ /** The entity constructor */
126
+ target: EntityConstructor;
127
+ /** The table name */
128
+ tableName: string;
129
+ /** The columns */
130
+ columns: TColumns;
131
+ /** The relations */
132
+ relations: Record<string, RelationMetadata>;
133
+ /** Optional hooks */
134
+ hooks?: TableHooks;
135
+ /** Optional table definition */
136
+ table?: TableDef<MaterializeColumns<TColumns>>;
137
+ }
138
+
139
+ const metadataMap = new Map<EntityConstructor, EntityMetadata>();
140
+
141
+ /**
142
+ * Registers entity metadata.
143
+ * @param meta - The entity metadata to register
144
+ */
145
+ export const registerEntityMetadata = (meta: EntityMetadata): void => {
146
+ metadataMap.set(meta.target, meta);
147
+ };
148
+
149
+ /**
150
+ * Ensures entity metadata exists for the target, creating it if necessary.
151
+ * @param target - The entity constructor
152
+ * @returns The entity metadata
153
+ */
154
+ export const ensureEntityMetadata = (target: EntityConstructor): EntityMetadata => {
155
+ let meta = metadataMap.get(target);
156
+ if (!meta) {
157
+ meta = {
158
+ target,
159
+ tableName: target.name || 'unknown',
160
+ columns: {},
161
+ relations: {}
162
+ };
163
+ metadataMap.set(target, meta);
164
+ }
165
+ return meta;
166
+ };
167
+
168
+ /**
169
+ * Gets entity metadata for the target.
170
+ * @param target - The entity constructor
171
+ * @returns The entity metadata or undefined if not found
172
+ */
173
+ export const getEntityMetadata = (target: EntityConstructor): EntityMetadata | undefined => {
174
+ return metadataMap.get(target);
175
+ };
176
+
177
+ /**
178
+ * Gets all entity metadata.
179
+ * @returns Array of all entity metadata
180
+ */
181
+ export const getAllEntityMetadata = (): EntityMetadata[] => {
182
+ return Array.from(metadataMap.values());
183
+ };
184
+
185
+ /**
186
+ * Clears all entity metadata.
187
+ */
188
+ export const clearEntityMetadata = (): void => {
189
+ metadataMap.clear();
190
+ };
191
+
192
+ /**
193
+ * Adds column metadata to an entity.
194
+ * @param target - The entity constructor
195
+ * @param propertyKey - The property key
196
+ * @param column - The column definition
197
+ */
198
+ export const addColumnMetadata = (
199
+ target: EntityConstructor,
200
+ propertyKey: string,
201
+ column: ColumnDefLike
202
+ ): void => {
203
+ const meta = ensureEntityMetadata(target);
204
+ (meta.columns as Record<string, ColumnDefLike>)[propertyKey] = { ...column };
205
+ };
206
+
207
+ /**
208
+ * Adds relation metadata to an entity.
209
+ * @param target - The entity constructor
210
+ * @param propertyKey - The property key
211
+ * @param relation - The relation metadata
212
+ */
213
+ export const addRelationMetadata = (
214
+ target: EntityConstructor,
215
+ propertyKey: string,
216
+ relation: RelationMetadata
217
+ ): void => {
218
+ const meta = ensureEntityMetadata(target);
219
+ meta.relations[propertyKey] = relation;
220
+ };
221
+
222
+ /**
223
+ * Sets the table name and hooks for an entity.
224
+ * @param target - The entity constructor
225
+ * @param tableName - The table name
226
+ * @param hooks - Optional table hooks
227
+ */
228
+ export const setEntityTableName = (
229
+ target: EntityConstructor,
230
+ tableName: string,
231
+ hooks?: TableHooks
232
+ ): void => {
233
+ const meta = ensureEntityMetadata(target);
234
+ if (tableName && tableName.length > 0) {
235
+ meta.tableName = tableName;
236
+ }
237
+ if (hooks) {
238
+ meta.hooks = hooks;
239
+ }
240
+ };
241
+
242
+ /**
243
+ * Builds a table definition from entity metadata.
244
+ * @template TColumns - The columns type
245
+ * @param meta - The entity metadata
246
+ * @returns The table definition
247
+ */
248
+ export const buildTableDef = <TColumns extends Record<string, ColumnDefLike>>(meta: EntityMetadata<TColumns>): TableDef<MaterializeColumns<TColumns>> => {
249
+ if (meta.table) {
250
+ return meta.table;
251
+ }
252
+
253
+ // Build columns using a simpler approach that avoids type assertion
254
+ const columns: Record<string, ColumnDef> = {};
255
+ for (const [key, def] of Object.entries(meta.columns)) {
256
+ columns[key] = {
257
+ ...def,
258
+ name: key,
259
+ table: meta.tableName
260
+ } as ColumnDef;
261
+ }
262
+
263
+ const table = defineTable(meta.tableName, columns as MaterializeColumns<TColumns>, {}, meta.hooks);
264
+ meta.table = table;
265
+ return table;
266
+ };
265
267
 
@@ -0,0 +1,39 @@
1
+ import { TableDef } from '../schema/table.js';
2
+ import { EntityMeta, getEntityMeta } from './entity-meta.js';
3
+
4
+ /**
5
+ * Caches relation loader results across entities of the same type.
6
+ * @template T - The cache type
7
+ * @param meta - The entity metadata
8
+ * @param relationName - The relation name
9
+ * @param factory - The factory function to create the cache
10
+ * @returns Promise with the cached relation data
11
+ */
12
+ export const relationLoaderCache = <TTable extends TableDef, T extends Map<string, unknown>>(
13
+ meta: EntityMeta<TTable>,
14
+ relationName: string,
15
+ factory: () => Promise<T>
16
+ ): Promise<T> => {
17
+ if (meta.relationCache.has(relationName)) {
18
+ return meta.relationCache.get(relationName)! as Promise<T>;
19
+ }
20
+
21
+ const promise = factory().then(value => {
22
+ for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
23
+ const otherMeta = getEntityMeta(tracked.entity);
24
+ if (!otherMeta) continue;
25
+ otherMeta.relationHydration.set(relationName, value);
26
+ }
27
+ return value;
28
+ });
29
+
30
+ meta.relationCache.set(relationName, promise);
31
+
32
+ for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
33
+ const otherMeta = getEntityMeta(tracked.entity);
34
+ if (!otherMeta) continue;
35
+ otherMeta.relationCache.set(relationName, promise);
36
+ }
37
+
38
+ return promise;
39
+ };