metal-orm 1.0.15 → 1.0.16

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 (43) hide show
  1. package/README.md +34 -27
  2. package/dist/decorators/index.cjs +339 -153
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -5
  5. package/dist/decorators/index.d.ts +1 -5
  6. package/dist/decorators/index.js +339 -153
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +838 -484
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +17 -14
  11. package/dist/index.d.ts +17 -14
  12. package/dist/index.js +833 -483
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-Bkv8g8u_.d.cts → select-BKZrMRCQ.d.cts} +363 -28
  15. package/dist/{select-Bkv8g8u_.d.ts → select-BKZrMRCQ.d.ts} +363 -28
  16. package/package.json +1 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +48 -53
  19. package/src/core/ddl/schema-generator.ts +3 -2
  20. package/src/core/ddl/schema-introspect.ts +1 -1
  21. package/src/core/dialect/abstract.ts +28 -0
  22. package/src/decorators/column.ts +13 -4
  23. package/src/index.ts +8 -1
  24. package/src/orm/entity-context.ts +30 -0
  25. package/src/orm/entity-meta.ts +2 -2
  26. package/src/orm/entity-metadata.ts +1 -6
  27. package/src/orm/entity.ts +88 -88
  28. package/src/orm/execute.ts +42 -25
  29. package/src/orm/execution-context.ts +12 -0
  30. package/src/orm/hydration-context.ts +14 -0
  31. package/src/orm/identity-map.ts +4 -0
  32. package/src/orm/interceptor-pipeline.ts +29 -0
  33. package/src/orm/lazy-batch.ts +6 -6
  34. package/src/orm/orm-session.ts +234 -0
  35. package/src/orm/orm.ts +58 -0
  36. package/src/orm/relation-change-processor.ts +5 -1
  37. package/src/orm/relations/belongs-to.ts +45 -44
  38. package/src/orm/relations/has-many.ts +44 -43
  39. package/src/orm/relations/has-one.ts +140 -139
  40. package/src/orm/relations/many-to-many.ts +46 -45
  41. package/src/orm/unit-of-work.ts +6 -1
  42. package/src/query-builder/select.ts +509 -3
  43. package/src/orm/orm-context.ts +0 -159
@@ -1,32 +1,33 @@
1
1
  import { HasManyCollection } from '../../schema/types.js';
2
- import { OrmContext, RelationKey } from '../orm-context.js';
2
+ import { EntityContext } from '../entity-context.js';
3
+ import { RelationKey } from '../runtime-types.js';
3
4
  import { HasManyRelation } from '../../schema/relation.js';
4
5
  import { TableDef } from '../../schema/table.js';
5
6
  import { EntityMeta, getHydrationRows } from '../entity-meta.js';
6
7
 
7
- type Rows = Record<string, any>[];
8
-
9
- const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
10
-
11
- const hideInternal = (obj: any, keys: string[]): void => {
12
- for (const key of keys) {
13
- Object.defineProperty(obj, key, {
14
- value: obj[key],
15
- writable: false,
16
- configurable: false,
17
- enumerable: false
18
- });
19
- }
20
- };
21
-
22
- export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChild> {
23
- private loaded = false;
24
- private items: TChild[] = [];
25
- private readonly added = new Set<TChild>();
26
- private readonly removed = new Set<TChild>();
8
+ type Rows = Record<string, any>[];
9
+
10
+ const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
11
+
12
+ const hideInternal = (obj: any, keys: string[]): void => {
13
+ for (const key of keys) {
14
+ Object.defineProperty(obj, key, {
15
+ value: obj[key],
16
+ writable: false,
17
+ configurable: false,
18
+ enumerable: false
19
+ });
20
+ }
21
+ };
22
+
23
+ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChild> {
24
+ private loaded = false;
25
+ private items: TChild[] = [];
26
+ private readonly added = new Set<TChild>();
27
+ private readonly removed = new Set<TChild>();
27
28
 
28
29
  constructor(
29
- private readonly ctx: OrmContext,
30
+ private readonly ctx: EntityContext,
30
31
  private readonly meta: EntityMeta<any>,
31
32
  private readonly root: any,
32
33
  private readonly relationName: string,
@@ -34,14 +35,14 @@ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChil
34
35
  private readonly rootTable: TableDef,
35
36
  private readonly loader: () => Promise<Map<string, Rows>>,
36
37
  private readonly createEntity: (row: Record<string, any>) => TChild,
37
- private readonly localKey: string
38
- ) {
39
- hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
40
- this.hydrateFromCache();
41
- }
42
-
43
- async load(): Promise<TChild[]> {
44
- if (this.loaded) return this.items;
38
+ private readonly localKey: string
39
+ ) {
40
+ hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
41
+ this.hydrateFromCache();
42
+ }
43
+
44
+ async load(): Promise<TChild[]> {
45
+ if (this.loaded) return this.items;
45
46
  const map = await this.loader();
46
47
  const key = toKey(this.root[this.localKey]);
47
48
  const rows = map.get(key) ?? [];
@@ -112,16 +113,16 @@ export class DefaultHasManyCollection<TChild> implements HasManyCollection<TChil
112
113
  return `${this.rootTable.name}.${this.relationName}`;
113
114
  }
114
115
 
115
- private hydrateFromCache(): void {
116
- const keyValue = this.root[this.localKey];
117
- if (keyValue === undefined || keyValue === null) return;
118
- const rows = getHydrationRows(this.meta, this.relationName, keyValue);
119
- if (!rows?.length) return;
120
- this.items = rows.map(row => this.createEntity(row));
121
- this.loaded = true;
122
- }
123
-
124
- toJSON(): TChild[] {
125
- return this.items;
126
- }
127
- }
116
+ private hydrateFromCache(): void {
117
+ const keyValue = this.root[this.localKey];
118
+ if (keyValue === undefined || keyValue === null) return;
119
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
120
+ if (!rows?.length) return;
121
+ this.items = rows.map(row => this.createEntity(row));
122
+ this.loaded = true;
123
+ }
124
+
125
+ toJSON(): TChild[] {
126
+ return this.items;
127
+ }
128
+ }
@@ -1,139 +1,140 @@
1
- import { HasOneReference } from '../../schema/types.js';
2
- import { OrmContext, RelationKey } from '../orm-context.js';
3
- import { HasOneRelation } from '../../schema/relation.js';
4
- import { TableDef } from '../../schema/table.js';
5
- import { EntityMeta, getHydrationRecord, hasEntityMeta } from '../entity-meta.js';
6
-
7
- type Row = Record<string, any>;
8
-
9
- const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
10
-
11
- const hideInternal = (obj: any, keys: string[]): void => {
12
- for (const key of keys) {
13
- Object.defineProperty(obj, key, {
14
- value: obj[key],
15
- writable: false,
16
- configurable: false,
17
- enumerable: false
18
- });
19
- }
20
- };
21
-
22
- export class DefaultHasOneReference<TChild> implements HasOneReference<TChild> {
23
- private loaded = false;
24
- private current: TChild | null = null;
25
-
26
- constructor(
27
- private readonly ctx: OrmContext,
28
- private readonly meta: EntityMeta<any>,
29
- private readonly root: any,
30
- private readonly relationName: string,
31
- private readonly relation: HasOneRelation,
32
- private readonly rootTable: TableDef,
33
- private readonly loader: () => Promise<Map<string, Row>>,
34
- private readonly createEntity: (row: Row) => TChild,
35
- private readonly localKey: string
36
- ) {
37
- hideInternal(this, [
38
- 'ctx',
39
- 'meta',
40
- 'root',
41
- 'relationName',
42
- 'relation',
43
- 'rootTable',
44
- 'loader',
45
- 'createEntity',
46
- 'localKey'
47
- ]);
48
- this.populateFromHydrationCache();
49
- }
50
-
51
- async load(): Promise<TChild | null> {
52
- if (this.loaded) return this.current;
53
- const map = await this.loader();
54
- const keyValue = this.root[this.localKey];
55
- if (keyValue === undefined || keyValue === null) {
56
- this.loaded = true;
57
- return this.current;
58
- }
59
- const row = map.get(toKey(keyValue));
60
- this.current = row ? this.createEntity(row) : null;
61
- this.loaded = true;
62
- return this.current;
63
- }
64
-
65
- get(): TChild | null {
66
- return this.current;
67
- }
68
-
69
- set(data: Partial<TChild> | TChild | null): TChild | null {
70
- if (data === null) {
71
- return this.detachCurrent();
72
- }
73
-
74
- const entity = hasEntityMeta(data) ? (data as TChild) : this.createEntity(data as Row);
75
- if (this.current && this.current !== entity) {
76
- this.ctx.registerRelationChange(
77
- this.root,
78
- this.relationKey,
79
- this.rootTable,
80
- this.relationName,
81
- this.relation,
82
- { kind: 'remove', entity: this.current }
83
- );
84
- }
85
-
86
- this.assignForeignKey(entity);
87
- this.current = entity;
88
- this.loaded = true;
89
-
90
- this.ctx.registerRelationChange(
91
- this.root,
92
- this.relationKey,
93
- this.rootTable,
94
- this.relationName,
95
- this.relation,
96
- { kind: 'attach', entity }
97
- );
98
-
99
- return entity;
100
- }
101
-
102
- toJSON(): TChild | null {
103
- return this.current;
104
- }
105
-
106
- private detachCurrent(): TChild | null {
107
- const previous = this.current;
108
- if (!previous) return null;
109
- this.current = null;
110
- this.loaded = true;
111
- this.ctx.registerRelationChange(
112
- this.root,
113
- this.relationKey,
114
- this.rootTable,
115
- this.relationName,
116
- this.relation,
117
- { kind: 'remove', entity: previous }
118
- );
119
- return null;
120
- }
121
-
122
- private assignForeignKey(entity: TChild): void {
123
- const keyValue = this.root[this.localKey];
124
- (entity as Row)[this.relation.foreignKey] = keyValue;
125
- }
126
-
127
- private get relationKey(): RelationKey {
128
- return `${this.rootTable.name}.${this.relationName}`;
129
- }
130
-
131
- private populateFromHydrationCache(): void {
132
- const keyValue = this.root[this.localKey];
133
- if (keyValue === undefined || keyValue === null) return;
134
- const row = getHydrationRecord(this.meta, this.relationName, keyValue);
135
- if (!row) return;
136
- this.current = this.createEntity(row);
137
- this.loaded = true;
138
- }
139
- }
1
+ import { HasOneReference } from '../../schema/types.js';
2
+ import { EntityContext } from '../entity-context.js';
3
+ import { RelationKey } from '../runtime-types.js';
4
+ import { HasOneRelation } from '../../schema/relation.js';
5
+ import { TableDef } from '../../schema/table.js';
6
+ import { EntityMeta, getHydrationRecord, hasEntityMeta } from '../entity-meta.js';
7
+
8
+ type Row = Record<string, any>;
9
+
10
+ const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
11
+
12
+ const hideInternal = (obj: any, keys: string[]): void => {
13
+ for (const key of keys) {
14
+ Object.defineProperty(obj, key, {
15
+ value: obj[key],
16
+ writable: false,
17
+ configurable: false,
18
+ enumerable: false
19
+ });
20
+ }
21
+ };
22
+
23
+ export class DefaultHasOneReference<TChild> implements HasOneReference<TChild> {
24
+ private loaded = false;
25
+ private current: TChild | null = null;
26
+
27
+ constructor(
28
+ private readonly ctx: EntityContext,
29
+ private readonly meta: EntityMeta<any>,
30
+ private readonly root: any,
31
+ private readonly relationName: string,
32
+ private readonly relation: HasOneRelation,
33
+ private readonly rootTable: TableDef,
34
+ private readonly loader: () => Promise<Map<string, Row>>,
35
+ private readonly createEntity: (row: Row) => TChild,
36
+ private readonly localKey: string
37
+ ) {
38
+ hideInternal(this, [
39
+ 'ctx',
40
+ 'meta',
41
+ 'root',
42
+ 'relationName',
43
+ 'relation',
44
+ 'rootTable',
45
+ 'loader',
46
+ 'createEntity',
47
+ 'localKey'
48
+ ]);
49
+ this.populateFromHydrationCache();
50
+ }
51
+
52
+ async load(): Promise<TChild | null> {
53
+ if (this.loaded) return this.current;
54
+ const map = await this.loader();
55
+ const keyValue = this.root[this.localKey];
56
+ if (keyValue === undefined || keyValue === null) {
57
+ this.loaded = true;
58
+ return this.current;
59
+ }
60
+ const row = map.get(toKey(keyValue));
61
+ this.current = row ? this.createEntity(row) : null;
62
+ this.loaded = true;
63
+ return this.current;
64
+ }
65
+
66
+ get(): TChild | null {
67
+ return this.current;
68
+ }
69
+
70
+ set(data: Partial<TChild> | TChild | null): TChild | null {
71
+ if (data === null) {
72
+ return this.detachCurrent();
73
+ }
74
+
75
+ const entity = hasEntityMeta(data) ? (data as TChild) : this.createEntity(data as Row);
76
+ if (this.current && this.current !== entity) {
77
+ this.ctx.registerRelationChange(
78
+ this.root,
79
+ this.relationKey,
80
+ this.rootTable,
81
+ this.relationName,
82
+ this.relation,
83
+ { kind: 'remove', entity: this.current }
84
+ );
85
+ }
86
+
87
+ this.assignForeignKey(entity);
88
+ this.current = entity;
89
+ this.loaded = true;
90
+
91
+ this.ctx.registerRelationChange(
92
+ this.root,
93
+ this.relationKey,
94
+ this.rootTable,
95
+ this.relationName,
96
+ this.relation,
97
+ { kind: 'attach', entity }
98
+ );
99
+
100
+ return entity;
101
+ }
102
+
103
+ toJSON(): TChild | null {
104
+ return this.current;
105
+ }
106
+
107
+ private detachCurrent(): TChild | null {
108
+ const previous = this.current;
109
+ if (!previous) return null;
110
+ this.current = null;
111
+ this.loaded = true;
112
+ this.ctx.registerRelationChange(
113
+ this.root,
114
+ this.relationKey,
115
+ this.rootTable,
116
+ this.relationName,
117
+ this.relation,
118
+ { kind: 'remove', entity: previous }
119
+ );
120
+ return null;
121
+ }
122
+
123
+ private assignForeignKey(entity: TChild): void {
124
+ const keyValue = this.root[this.localKey];
125
+ (entity as Row)[this.relation.foreignKey] = keyValue;
126
+ }
127
+
128
+ private get relationKey(): RelationKey {
129
+ return `${this.rootTable.name}.${this.relationName}`;
130
+ }
131
+
132
+ private populateFromHydrationCache(): void {
133
+ const keyValue = this.root[this.localKey];
134
+ if (keyValue === undefined || keyValue === null) return;
135
+ const row = getHydrationRecord(this.meta, this.relationName, keyValue);
136
+ if (!row) return;
137
+ this.current = this.createEntity(row);
138
+ this.loaded = true;
139
+ }
140
+ }
@@ -1,46 +1,47 @@
1
1
  import { ManyToManyCollection } from '../../schema/types.js';
2
- import { OrmContext, RelationKey } from '../orm-context.js';
2
+ import { EntityContext } from '../entity-context.js';
3
+ import { RelationKey } from '../runtime-types.js';
3
4
  import { BelongsToManyRelation } from '../../schema/relation.js';
4
5
  import { TableDef } from '../../schema/table.js';
5
6
  import { findPrimaryKey } from '../../query-builder/hydration-planner.js';
6
7
  import { EntityMeta, getHydrationRows } from '../entity-meta.js';
7
8
 
8
- type Rows = Record<string, any>[];
9
-
10
- const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
11
-
12
- const hideInternal = (obj: any, keys: string[]): void => {
13
- for (const key of keys) {
14
- Object.defineProperty(obj, key, {
15
- value: obj[key],
16
- writable: false,
17
- configurable: false,
18
- enumerable: false
19
- });
20
- }
21
- };
22
-
23
- export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollection<TTarget> {
24
- private loaded = false;
25
- private items: TTarget[] = [];
26
-
27
- constructor(
28
- private readonly ctx: OrmContext,
9
+ type Rows = Record<string, any>[];
10
+
11
+ const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
12
+
13
+ const hideInternal = (obj: any, keys: string[]): void => {
14
+ for (const key of keys) {
15
+ Object.defineProperty(obj, key, {
16
+ value: obj[key],
17
+ writable: false,
18
+ configurable: false,
19
+ enumerable: false
20
+ });
21
+ }
22
+ };
23
+
24
+ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollection<TTarget> {
25
+ private loaded = false;
26
+ private items: TTarget[] = [];
27
+
28
+ constructor(
29
+ private readonly ctx: EntityContext,
29
30
  private readonly meta: EntityMeta<any>,
30
31
  private readonly root: any,
31
32
  private readonly relationName: string,
32
33
  private readonly relation: BelongsToManyRelation,
33
34
  private readonly rootTable: TableDef,
34
35
  private readonly loader: () => Promise<Map<string, Rows>>,
35
- private readonly createEntity: (row: Record<string, any>) => TTarget,
36
- private readonly localKey: string
37
- ) {
38
- hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
39
- this.hydrateFromCache();
40
- }
41
-
42
- async load(): Promise<TTarget[]> {
43
- if (this.loaded) return this.items;
36
+ private readonly createEntity: (row: Record<string, any>) => TTarget,
37
+ private readonly localKey: string
38
+ ) {
39
+ hideInternal(this, ['ctx', 'meta', 'root', 'relationName', 'relation', 'rootTable', 'loader', 'createEntity', 'localKey']);
40
+ this.hydrateFromCache();
41
+ }
42
+
43
+ async load(): Promise<TTarget[]> {
44
+ if (this.loaded) return this.items;
44
45
  const map = await this.loader();
45
46
  const key = toKey(this.root[this.localKey]);
46
47
  const rows = map.get(key) ?? [];
@@ -144,22 +145,22 @@ export class DefaultManyToManyCollection<TTarget> implements ManyToManyCollectio
144
145
  return this.relation.targetKey || findPrimaryKey(this.relation.target);
145
146
  }
146
147
 
147
- private hydrateFromCache(): void {
148
- const keyValue = this.root[this.localKey];
149
- if (keyValue === undefined || keyValue === null) return;
150
- const rows = getHydrationRows(this.meta, this.relationName, keyValue);
151
- if (!rows?.length) return;
148
+ private hydrateFromCache(): void {
149
+ const keyValue = this.root[this.localKey];
150
+ if (keyValue === undefined || keyValue === null) return;
151
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
152
+ if (!rows?.length) return;
152
153
  this.items = rows.map(row => {
153
154
  const entity = this.createEntity(row);
154
155
  if ((row as any)._pivot) {
155
156
  (entity as any)._pivot = (row as any)._pivot;
156
157
  }
157
- return entity;
158
- });
159
- this.loaded = true;
160
- }
161
-
162
- toJSON(): TTarget[] {
163
- return this.items;
164
- }
165
- }
158
+ return entity;
159
+ });
160
+ this.loaded = true;
161
+ }
162
+
163
+ toJSON(): TTarget[] {
164
+ return this.items;
165
+ }
166
+ }
@@ -18,7 +18,7 @@ export class UnitOfWork {
18
18
  private readonly executor: DbExecutor,
19
19
  private readonly identityMap: IdentityMap,
20
20
  private readonly hookContext: () => unknown
21
- ) {}
21
+ ) { }
22
22
 
23
23
  get identityBuckets(): Map<string, Map<string, TrackedEntity>> {
24
24
  return this.identityMap.bucketsMap;
@@ -117,6 +117,11 @@ export class UnitOfWork {
117
117
  }
118
118
  }
119
119
 
120
+ reset(): void {
121
+ this.trackedEntities.clear();
122
+ this.identityMap.clear();
123
+ }
124
+
120
125
  private async flushInsert(tracked: TrackedEntity): Promise<void> {
121
126
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
122
127