metal-orm 1.1.9 → 1.1.10

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 (73) hide show
  1. package/README.md +769 -764
  2. package/dist/index.cjs +2147 -239
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +559 -39
  5. package/dist/index.d.ts +559 -39
  6. package/dist/index.js +2119 -239
  7. package/dist/index.js.map +1 -1
  8. package/package.json +17 -12
  9. package/src/bulk/bulk-context.ts +83 -0
  10. package/src/bulk/bulk-delete-executor.ts +89 -0
  11. package/src/bulk/bulk-executor.base.ts +73 -0
  12. package/src/bulk/bulk-insert-executor.ts +74 -0
  13. package/src/bulk/bulk-types.ts +70 -0
  14. package/src/bulk/bulk-update-executor.ts +192 -0
  15. package/src/bulk/bulk-upsert-executor.ts +95 -0
  16. package/src/bulk/bulk-utils.ts +91 -0
  17. package/src/bulk/index.ts +18 -0
  18. package/src/codegen/typescript.ts +30 -21
  19. package/src/core/ast/expression-builders.ts +107 -10
  20. package/src/core/ast/expression-nodes.ts +52 -22
  21. package/src/core/ast/expression-visitor.ts +23 -13
  22. package/src/core/dialect/abstract.ts +30 -17
  23. package/src/core/dialect/mysql/index.ts +20 -5
  24. package/src/core/execution/db-executor.ts +96 -64
  25. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -0
  26. package/src/core/execution/executors/mssql-executor.ts +66 -34
  27. package/src/core/execution/executors/mysql-executor.ts +98 -66
  28. package/src/core/execution/executors/postgres-executor.ts +33 -11
  29. package/src/core/execution/executors/sqlite-executor.ts +86 -30
  30. package/src/decorators/bootstrap.ts +482 -398
  31. package/src/decorators/column-decorator.ts +87 -96
  32. package/src/decorators/decorator-metadata.ts +100 -24
  33. package/src/decorators/entity.ts +27 -24
  34. package/src/decorators/relations.ts +231 -149
  35. package/src/decorators/transformers/transformer-decorators.ts +26 -29
  36. package/src/decorators/validators/country-validators-decorators.ts +9 -15
  37. package/src/dto/apply-filter.ts +568 -551
  38. package/src/index.ts +16 -9
  39. package/src/orm/entity-hydration.ts +116 -72
  40. package/src/orm/entity-metadata.ts +347 -301
  41. package/src/orm/entity-relations.ts +264 -207
  42. package/src/orm/entity.ts +199 -199
  43. package/src/orm/execute.ts +13 -13
  44. package/src/orm/lazy-batch/morph-many.ts +70 -0
  45. package/src/orm/lazy-batch/morph-one.ts +69 -0
  46. package/src/orm/lazy-batch/morph-to.ts +59 -0
  47. package/src/orm/lazy-batch.ts +4 -1
  48. package/src/orm/orm-session.ts +170 -104
  49. package/src/orm/pooled-executor-factory.ts +99 -58
  50. package/src/orm/query-logger.ts +49 -40
  51. package/src/orm/relation-change-processor.ts +198 -96
  52. package/src/orm/relations/belongs-to.ts +143 -143
  53. package/src/orm/relations/has-many.ts +204 -204
  54. package/src/orm/relations/has-one.ts +174 -174
  55. package/src/orm/relations/many-to-many.ts +288 -288
  56. package/src/orm/relations/morph-many.ts +156 -0
  57. package/src/orm/relations/morph-one.ts +151 -0
  58. package/src/orm/relations/morph-to.ts +162 -0
  59. package/src/orm/save-graph.ts +116 -1
  60. package/src/query-builder/expression-table-mapper.ts +5 -0
  61. package/src/query-builder/hydration-manager.ts +345 -345
  62. package/src/query-builder/hydration-planner.ts +178 -148
  63. package/src/query-builder/relation-conditions.ts +171 -151
  64. package/src/query-builder/relation-cte-builder.ts +5 -1
  65. package/src/query-builder/relation-filter-utils.ts +9 -6
  66. package/src/query-builder/relation-include-strategies.ts +44 -2
  67. package/src/query-builder/relation-join-strategies.ts +8 -1
  68. package/src/query-builder/relation-service.ts +250 -241
  69. package/src/query-builder/select/select-operations.ts +110 -105
  70. package/src/query-builder/update-include.ts +4 -0
  71. package/src/schema/relation.ts +296 -188
  72. package/src/schema/types.ts +138 -123
  73. package/src/tree/tree-decorator.ts +127 -137
@@ -1,207 +1,264 @@
1
- import { TableDef } from '../schema/table.js';
2
- import { EntityInstance, HasManyCollection, HasOneReference, BelongsToReference, ManyToManyCollection } from '../schema/types.js';
3
- import { EntityMeta, RelationKey } from './entity-meta.js';
4
- import { DefaultHasManyCollection } from './relations/has-many.js';
5
- import { DefaultHasOneReference } from './relations/has-one.js';
6
- import { DefaultBelongsToReference } from './relations/belongs-to.js';
7
- import { DefaultManyToManyCollection } from './relations/many-to-many.js';
8
- import { HasManyRelation, HasOneRelation, BelongsToRelation, BelongsToManyRelation, RelationKinds } from '../schema/relation.js';
9
- import { loadHasManyRelation, loadHasOneRelation, loadBelongsToRelation, loadBelongsToManyRelation } from './lazy-batch.js';
10
- import { findPrimaryKey } from '../query-builder/hydration-planner.js';
11
- import { relationLoaderCache } from './entity-relation-cache.js';
12
-
13
- export type RelationEntityFactory = (
14
- table: TableDef,
15
- row: Record<string, unknown>
16
- ) => EntityInstance<TableDef>;
17
-
18
- const proxifyRelationWrapper = <T extends object>(wrapper: T): T => {
19
- return new Proxy(wrapper, {
20
- get(target, prop, receiver) {
21
- if (typeof prop === 'symbol') {
22
- return Reflect.get(target, prop, receiver);
23
- }
24
-
25
- if (prop in target) {
26
- return Reflect.get(target, prop, receiver);
27
- }
28
-
29
- const getItems = (target as { getItems?: () => unknown }).getItems;
30
- if (typeof getItems === 'function') {
31
- const items = getItems.call(target);
32
- if (items && prop in (items as object)) {
33
- const propName = prop as string;
34
- const value = (items as Record<string, unknown>)[propName];
35
- return typeof value === 'function' ? value.bind(items) : value;
36
- }
37
- }
38
-
39
- const getRef = (target as { get?: () => unknown }).get;
40
- if (typeof getRef === 'function') {
41
- const current = getRef.call(target);
42
- if (current && prop in (current as object)) {
43
- const propName = prop as string;
44
- const value = (current as Record<string, unknown>)[propName];
45
- return typeof value === 'function' ? value.bind(current) : value;
46
- }
47
- }
48
-
49
- return undefined;
50
- },
51
-
52
- set(target, prop, value, receiver) {
53
- if (typeof prop === 'symbol') {
54
- return Reflect.set(target, prop, value, receiver);
55
- }
56
-
57
- if (prop in target) {
58
- return Reflect.set(target, prop, value, receiver);
59
- }
60
-
61
- const getRef = (target as { get?: () => unknown }).get;
62
- if (typeof getRef === 'function') {
63
- const current = getRef.call(target);
64
- if (current && typeof current === 'object') {
65
- return Reflect.set(current as object, prop, value);
66
- }
67
- }
68
-
69
- const getItems = (target as { getItems?: () => unknown }).getItems;
70
- if (typeof getItems === 'function') {
71
- const items = getItems.call(target);
72
- return Reflect.set(items as object, prop, value);
73
- }
74
-
75
- return Reflect.set(target, prop, value, receiver);
76
- }
77
- });
78
- };
79
-
80
- /**
81
- * Gets a relation wrapper for an entity.
82
- * @param meta - The entity metadata
83
- * @param relationName - The relation name
84
- * @param owner - The owner entity
85
- * @param createEntity - The entity factory for relation rows
86
- * @returns The relation wrapper or undefined
87
- */
88
- export const getRelationWrapper = <TTable extends TableDef>(
89
- meta: EntityMeta<TTable>,
90
- relationName: RelationKey<TTable> | string,
91
- owner: unknown,
92
- createEntity: RelationEntityFactory
93
- ): HasManyCollection<unknown> | HasOneReference<object> | BelongsToReference<object> | ManyToManyCollection<unknown> | undefined => {
94
- const relationKey = relationName as string;
95
-
96
- if (meta.relationWrappers.has(relationKey)) {
97
- return meta.relationWrappers.get(relationKey) as HasManyCollection<unknown>;
98
- }
99
-
100
- const relation = meta.table.relations[relationKey];
101
- if (!relation) return undefined;
102
-
103
- const wrapper = instantiateWrapper(meta, relationKey, relation, owner, createEntity);
104
- if (!wrapper) return undefined;
105
-
106
- const proxied = proxifyRelationWrapper(wrapper as object);
107
- meta.relationWrappers.set(relationKey, proxied);
108
- return proxied as HasManyCollection<unknown>;
109
- };
110
-
111
- /**
112
- * Instantiates the appropriate relation wrapper based on relation type.
113
- * @param meta - The entity metadata
114
- * @param relationName - The relation name
115
- * @param relation - The relation definition
116
- * @param owner - The owner entity
117
- * @param createEntity - The entity factory for relation rows
118
- * @returns The relation wrapper or undefined
119
- */
120
- const instantiateWrapper = <TTable extends TableDef>(
121
- meta: EntityMeta<TTable>,
122
- relationName: string,
123
- relation: HasManyRelation | HasOneRelation | BelongsToRelation | BelongsToManyRelation,
124
- owner: unknown,
125
- createEntity: RelationEntityFactory
126
- ): HasManyCollection<unknown> | HasOneReference<object> | BelongsToReference<object> | ManyToManyCollection<unknown> | undefined => {
127
- const metaBase = meta as unknown as EntityMeta<TableDef>;
128
- const loadCached = <T extends Map<string, unknown>>(factory: () => Promise<T>) =>
129
- relationLoaderCache(metaBase, relationName, factory);
130
- const resolveOptions = () => meta.lazyRelationOptions.get(relationName);
131
- switch (relation.type) {
132
- case RelationKinds.HasOne: {
133
- const hasOne = relation as HasOneRelation;
134
- const localKey = hasOne.localKey || findPrimaryKey(meta.table);
135
- const loader = () => loadCached(() =>
136
- loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne, resolveOptions())
137
- );
138
- return new DefaultHasOneReference(
139
- meta.ctx,
140
- metaBase,
141
- owner,
142
- relationName,
143
- hasOne,
144
- meta.table,
145
- loader,
146
- (row: Record<string, unknown>) => createEntity(hasOne.target, row),
147
- localKey
148
- );
149
- }
150
- case RelationKinds.HasMany: {
151
- const hasMany = relation as HasManyRelation;
152
- const localKey = hasMany.localKey || findPrimaryKey(meta.table);
153
- const loader = () => loadCached(() =>
154
- loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany, resolveOptions())
155
- );
156
- return new DefaultHasManyCollection(
157
- meta.ctx,
158
- metaBase,
159
- owner,
160
- relationName,
161
- hasMany,
162
- meta.table,
163
- loader,
164
- (row: Record<string, unknown>) => createEntity(relation.target, row),
165
- localKey
166
- );
167
- }
168
- case RelationKinds.BelongsTo: {
169
- const belongsTo = relation as BelongsToRelation;
170
- const targetKey = belongsTo.localKey || findPrimaryKey(belongsTo.target);
171
- const loader = () => loadCached(() =>
172
- loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo, resolveOptions())
173
- );
174
- return new DefaultBelongsToReference(
175
- meta.ctx,
176
- metaBase,
177
- owner,
178
- relationName,
179
- belongsTo,
180
- meta.table,
181
- loader,
182
- (row: Record<string, unknown>) => createEntity(relation.target, row),
183
- targetKey
184
- );
185
- }
186
- case RelationKinds.BelongsToMany: {
187
- const many = relation as BelongsToManyRelation;
188
- const localKey = many.localKey || findPrimaryKey(meta.table);
189
- const loader = () => loadCached(() =>
190
- loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, resolveOptions())
191
- );
192
- return new DefaultManyToManyCollection(
193
- meta.ctx,
194
- metaBase,
195
- owner,
196
- relationName,
197
- many,
198
- meta.table,
199
- loader,
200
- (row: Record<string, unknown>) => createEntity(relation.target, row),
201
- localKey
202
- );
203
- }
204
- default:
205
- return undefined;
206
- }
207
- };
1
+ import { TableDef } from '../schema/table.js';
2
+ import { EntityInstance, HasManyCollection, HasOneReference, BelongsToReference, ManyToManyCollection } from '../schema/types.js';
3
+ import { EntityMeta, RelationKey } from './entity-meta.js';
4
+ import { DefaultHasManyCollection } from './relations/has-many.js';
5
+ import { DefaultHasOneReference } from './relations/has-one.js';
6
+ import { DefaultBelongsToReference } from './relations/belongs-to.js';
7
+ import { DefaultManyToManyCollection } from './relations/many-to-many.js';
8
+ import { DefaultMorphOneReference } from './relations/morph-one.js';
9
+ import { DefaultMorphManyCollection } from './relations/morph-many.js';
10
+ import { DefaultMorphToReference } from './relations/morph-to.js';
11
+ import { HasManyRelation, HasOneRelation, BelongsToRelation, BelongsToManyRelation, MorphOneRelation, MorphManyRelation, MorphToRelation, RelationKinds } from '../schema/relation.js';
12
+ import { loadHasManyRelation, loadHasOneRelation, loadBelongsToRelation, loadBelongsToManyRelation, loadMorphOneRelation, loadMorphManyRelation, loadMorphToRelation } from './lazy-batch.js';
13
+ import { findPrimaryKey } from '../query-builder/hydration-planner.js';
14
+ import { relationLoaderCache } from './entity-relation-cache.js';
15
+
16
+ export type RelationEntityFactory = (
17
+ table: TableDef,
18
+ row: Record<string, unknown>
19
+ ) => EntityInstance<TableDef>;
20
+
21
+ const proxifyRelationWrapper = <T extends object>(wrapper: T): T => {
22
+ return new Proxy(wrapper, {
23
+ get(target, prop, receiver) {
24
+ if (typeof prop === 'symbol') {
25
+ return Reflect.get(target, prop, receiver);
26
+ }
27
+
28
+ if (prop in target) {
29
+ return Reflect.get(target, prop, receiver);
30
+ }
31
+
32
+ const getItems = (target as { getItems?: () => unknown }).getItems;
33
+ if (typeof getItems === 'function') {
34
+ const items = getItems.call(target);
35
+ if (items && prop in (items as object)) {
36
+ const propName = prop as string;
37
+ const value = (items as Record<string, unknown>)[propName];
38
+ return typeof value === 'function' ? value.bind(items) : value;
39
+ }
40
+ }
41
+
42
+ const getRef = (target as { get?: () => unknown }).get;
43
+ if (typeof getRef === 'function') {
44
+ const current = getRef.call(target);
45
+ if (current && prop in (current as object)) {
46
+ const propName = prop as string;
47
+ const value = (current as Record<string, unknown>)[propName];
48
+ return typeof value === 'function' ? value.bind(current) : value;
49
+ }
50
+ }
51
+
52
+ return undefined;
53
+ },
54
+
55
+ set(target, prop, value, receiver) {
56
+ if (typeof prop === 'symbol') {
57
+ return Reflect.set(target, prop, value, receiver);
58
+ }
59
+
60
+ if (prop in target) {
61
+ return Reflect.set(target, prop, value, receiver);
62
+ }
63
+
64
+ const getRef = (target as { get?: () => unknown }).get;
65
+ if (typeof getRef === 'function') {
66
+ const current = getRef.call(target);
67
+ if (current && typeof current === 'object') {
68
+ return Reflect.set(current as object, prop, value);
69
+ }
70
+ }
71
+
72
+ const getItems = (target as { getItems?: () => unknown }).getItems;
73
+ if (typeof getItems === 'function') {
74
+ const items = getItems.call(target);
75
+ return Reflect.set(items as object, prop, value);
76
+ }
77
+
78
+ return Reflect.set(target, prop, value, receiver);
79
+ }
80
+ });
81
+ };
82
+
83
+ /**
84
+ * Gets a relation wrapper for an entity.
85
+ * @param meta - The entity metadata
86
+ * @param relationName - The relation name
87
+ * @param owner - The owner entity
88
+ * @param createEntity - The entity factory for relation rows
89
+ * @returns The relation wrapper or undefined
90
+ */
91
+ export const getRelationWrapper = <TTable extends TableDef>(
92
+ meta: EntityMeta<TTable>,
93
+ relationName: RelationKey<TTable> | string,
94
+ owner: unknown,
95
+ createEntity: RelationEntityFactory
96
+ ): HasManyCollection<unknown> | HasOneReference<object> | BelongsToReference<object> | ManyToManyCollection<unknown> | undefined => {
97
+ const relationKey = relationName as string;
98
+
99
+ if (meta.relationWrappers.has(relationKey)) {
100
+ return meta.relationWrappers.get(relationKey) as HasManyCollection<unknown>;
101
+ }
102
+
103
+ const relation = meta.table.relations[relationKey];
104
+ if (!relation) return undefined;
105
+
106
+ const wrapper = instantiateWrapper(meta, relationKey, relation, owner, createEntity);
107
+ if (!wrapper) return undefined;
108
+
109
+ const proxied = proxifyRelationWrapper(wrapper as object);
110
+ meta.relationWrappers.set(relationKey, proxied);
111
+ return proxied as HasManyCollection<unknown>;
112
+ };
113
+
114
+ /**
115
+ * Instantiates the appropriate relation wrapper based on relation type.
116
+ * @param meta - The entity metadata
117
+ * @param relationName - The relation name
118
+ * @param relation - The relation definition
119
+ * @param owner - The owner entity
120
+ * @param createEntity - The entity factory for relation rows
121
+ * @returns The relation wrapper or undefined
122
+ */
123
+ const instantiateWrapper = <TTable extends TableDef>(
124
+ meta: EntityMeta<TTable>,
125
+ relationName: string,
126
+ relation: HasManyRelation | HasOneRelation | BelongsToRelation | BelongsToManyRelation | MorphOneRelation | MorphManyRelation | MorphToRelation,
127
+ owner: unknown,
128
+ createEntity: RelationEntityFactory
129
+ ): HasManyCollection<unknown> | HasOneReference<object> | BelongsToReference<object> | ManyToManyCollection<unknown> | undefined => {
130
+ const metaBase = meta as unknown as EntityMeta<TableDef>;
131
+ const loadCached = <T extends Map<string, unknown>>(factory: () => Promise<T>) =>
132
+ relationLoaderCache(metaBase, relationName, factory);
133
+ const resolveOptions = () => meta.lazyRelationOptions.get(relationName);
134
+ switch (relation.type) {
135
+ case RelationKinds.HasOne: {
136
+ const hasOne = relation as HasOneRelation;
137
+ const localKey = hasOne.localKey || findPrimaryKey(meta.table);
138
+ const loader = () => loadCached(() =>
139
+ loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne, resolveOptions())
140
+ );
141
+ return new DefaultHasOneReference(
142
+ meta.ctx,
143
+ metaBase,
144
+ owner,
145
+ relationName,
146
+ hasOne,
147
+ meta.table,
148
+ loader,
149
+ (row: Record<string, unknown>) => createEntity(hasOne.target, row),
150
+ localKey
151
+ );
152
+ }
153
+ case RelationKinds.HasMany: {
154
+ const hasMany = relation as HasManyRelation;
155
+ const localKey = hasMany.localKey || findPrimaryKey(meta.table);
156
+ const loader = () => loadCached(() =>
157
+ loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany, resolveOptions())
158
+ );
159
+ return new DefaultHasManyCollection(
160
+ meta.ctx,
161
+ metaBase,
162
+ owner,
163
+ relationName,
164
+ hasMany,
165
+ meta.table,
166
+ loader,
167
+ (row: Record<string, unknown>) => createEntity(relation.target, row),
168
+ localKey
169
+ );
170
+ }
171
+ case RelationKinds.BelongsTo: {
172
+ const belongsTo = relation as BelongsToRelation;
173
+ const targetKey = belongsTo.localKey || findPrimaryKey(belongsTo.target);
174
+ const loader = () => loadCached(() =>
175
+ loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo, resolveOptions())
176
+ );
177
+ return new DefaultBelongsToReference(
178
+ meta.ctx,
179
+ metaBase,
180
+ owner,
181
+ relationName,
182
+ belongsTo,
183
+ meta.table,
184
+ loader,
185
+ (row: Record<string, unknown>) => createEntity(relation.target, row),
186
+ targetKey
187
+ );
188
+ }
189
+ case RelationKinds.BelongsToMany: {
190
+ const many = relation as BelongsToManyRelation;
191
+ const localKey = many.localKey || findPrimaryKey(meta.table);
192
+ const loader = () => loadCached(() =>
193
+ loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, resolveOptions())
194
+ );
195
+ return new DefaultManyToManyCollection(
196
+ meta.ctx,
197
+ metaBase,
198
+ owner,
199
+ relationName,
200
+ many,
201
+ meta.table,
202
+ loader,
203
+ (row: Record<string, unknown>) => createEntity(relation.target, row),
204
+ localKey
205
+ );
206
+ }
207
+ case RelationKinds.MorphOne: {
208
+ const morphOne = relation as MorphOneRelation;
209
+ const localKey = morphOne.localKey || findPrimaryKey(meta.table);
210
+ const loader = () => loadCached(() =>
211
+ loadMorphOneRelation(meta.ctx, meta.table, relationName, morphOne, resolveOptions())
212
+ );
213
+ return new DefaultMorphOneReference(
214
+ meta.ctx,
215
+ metaBase,
216
+ owner,
217
+ relationName,
218
+ morphOne,
219
+ meta.table,
220
+ loader,
221
+ (row: Record<string, unknown>) => createEntity(morphOne.target, row),
222
+ localKey
223
+ );
224
+ }
225
+ case RelationKinds.MorphMany: {
226
+ const morphMany = relation as MorphManyRelation;
227
+ const localKey = morphMany.localKey || findPrimaryKey(meta.table);
228
+ const loader = () => loadCached(() =>
229
+ loadMorphManyRelation(meta.ctx, meta.table, relationName, morphMany, resolveOptions())
230
+ );
231
+ return new DefaultMorphManyCollection(
232
+ meta.ctx,
233
+ metaBase,
234
+ owner,
235
+ relationName,
236
+ morphMany,
237
+ meta.table,
238
+ loader,
239
+ (row: Record<string, unknown>) => createEntity(morphMany.target, row),
240
+ localKey
241
+ );
242
+ }
243
+ case RelationKinds.MorphTo: {
244
+ const morphTo = relation as MorphToRelation;
245
+ const loader = () => loadCached(() =>
246
+ loadMorphToRelation(meta.ctx, meta.table, relationName, morphTo)
247
+ );
248
+ const resolveTargetTable = (typeValue: string) => morphTo.targets[typeValue];
249
+ return new DefaultMorphToReference(
250
+ meta.ctx,
251
+ metaBase,
252
+ owner,
253
+ relationName,
254
+ morphTo,
255
+ meta.table,
256
+ loader,
257
+ (table: TableDef, row: Record<string, unknown>) => createEntity(table, row),
258
+ resolveTargetTable
259
+ );
260
+ }
261
+ default:
262
+ return undefined;
263
+ }
264
+ };