metal-orm 1.1.7 → 1.1.8
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/dist/index.cjs +262 -202
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +261 -202
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-entities/schema.mjs +196 -195
- package/src/decorators/decorator-metadata.ts +52 -46
- package/src/decorators/entity.ts +73 -68
- package/src/orm/entity-metadata.ts +301 -301
- package/src/orm/entity.ts +199 -199
- package/src/orm/save-graph.ts +446 -446
- package/src/orm/unit-of-work.ts +6 -6
- package/src/tree/tree-decorator.ts +137 -54
package/package.json
CHANGED
|
@@ -1,203 +1,204 @@
|
|
|
1
|
-
import { detectTreeTable, mapTreeTables } from './tree-detection.mjs';
|
|
2
|
-
|
|
3
|
-
const normalizeName = name => (typeof name === 'string' && name.includes('.') ? name.split('.').pop() : name);
|
|
4
|
-
|
|
5
|
-
export const mapRelations = (tables, naming) => {
|
|
6
|
-
const relationMap = new Map();
|
|
7
|
-
const relationKeys = new Map();
|
|
8
|
-
const fkIndex = new Map();
|
|
9
|
-
const uniqueSingleColumns = new Map();
|
|
10
|
-
|
|
11
|
-
for (const table of tables) {
|
|
12
|
-
relationMap.set(table.name, []);
|
|
13
|
-
relationKeys.set(table.name, new Set());
|
|
14
|
-
for (const col of table.columns) {
|
|
15
|
-
if (col.references) {
|
|
16
|
-
const list = fkIndex.get(table.name) || [];
|
|
17
|
-
list.push(col);
|
|
18
|
-
fkIndex.set(table.name, list);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const uniqueCols = new Set();
|
|
23
|
-
if (Array.isArray(table.primaryKey) && table.primaryKey.length === 1) {
|
|
24
|
-
uniqueCols.add(table.primaryKey[0]);
|
|
25
|
-
}
|
|
26
|
-
for (const col of table.columns) {
|
|
27
|
-
if (col.unique) uniqueCols.add(col.name);
|
|
28
|
-
}
|
|
29
|
-
for (const idx of table.indexes || []) {
|
|
30
|
-
if (!idx?.unique) continue;
|
|
31
|
-
if (!Array.isArray(idx.columns) || idx.columns.length !== 1 || !idx.columns[0]?.column) continue;
|
|
32
|
-
const columnName = idx.columns[0].column;
|
|
33
|
-
if (idx.where) {
|
|
34
|
-
const predicate = String(idx.where);
|
|
35
|
-
const isNotNullOnly = new RegExp(`\\b${columnName}\\b\\s+is\\s+not\\s+null\\b`, 'i').test(predicate);
|
|
36
|
-
if (!isNotNullOnly) continue;
|
|
37
|
-
}
|
|
38
|
-
uniqueCols.add(columnName);
|
|
39
|
-
}
|
|
40
|
-
uniqueSingleColumns.set(table.name, uniqueCols);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const findTable = name => {
|
|
44
|
-
const norm = normalizeName(name);
|
|
45
|
-
return tables.find(t => t.name === name || t.name === norm);
|
|
46
|
-
};
|
|
47
|
-
|
|
1
|
+
import { detectTreeTable, mapTreeTables } from './tree-detection.mjs';
|
|
2
|
+
|
|
3
|
+
const normalizeName = name => (typeof name === 'string' && name.includes('.') ? name.split('.').pop() : name);
|
|
4
|
+
|
|
5
|
+
export const mapRelations = (tables, naming) => {
|
|
6
|
+
const relationMap = new Map();
|
|
7
|
+
const relationKeys = new Map();
|
|
8
|
+
const fkIndex = new Map();
|
|
9
|
+
const uniqueSingleColumns = new Map();
|
|
10
|
+
|
|
11
|
+
for (const table of tables) {
|
|
12
|
+
relationMap.set(table.name, []);
|
|
13
|
+
relationKeys.set(table.name, new Set());
|
|
14
|
+
for (const col of table.columns) {
|
|
15
|
+
if (col.references) {
|
|
16
|
+
const list = fkIndex.get(table.name) || [];
|
|
17
|
+
list.push(col);
|
|
18
|
+
fkIndex.set(table.name, list);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const uniqueCols = new Set();
|
|
23
|
+
if (Array.isArray(table.primaryKey) && table.primaryKey.length === 1) {
|
|
24
|
+
uniqueCols.add(table.primaryKey[0]);
|
|
25
|
+
}
|
|
26
|
+
for (const col of table.columns) {
|
|
27
|
+
if (col.unique) uniqueCols.add(col.name);
|
|
28
|
+
}
|
|
29
|
+
for (const idx of table.indexes || []) {
|
|
30
|
+
if (!idx?.unique) continue;
|
|
31
|
+
if (!Array.isArray(idx.columns) || idx.columns.length !== 1 || !idx.columns[0]?.column) continue;
|
|
32
|
+
const columnName = idx.columns[0].column;
|
|
33
|
+
if (idx.where) {
|
|
34
|
+
const predicate = String(idx.where);
|
|
35
|
+
const isNotNullOnly = new RegExp(`\\b${columnName}\\b\\s+is\\s+not\\s+null\\b`, 'i').test(predicate);
|
|
36
|
+
if (!isNotNullOnly) continue;
|
|
37
|
+
}
|
|
38
|
+
uniqueCols.add(columnName);
|
|
39
|
+
}
|
|
40
|
+
uniqueSingleColumns.set(table.name, uniqueCols);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const findTable = name => {
|
|
44
|
+
const norm = normalizeName(name);
|
|
45
|
+
return tables.find(t => t.name === name || t.name === norm);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
48
|
const pivotTables = new Set();
|
|
49
49
|
for (const table of tables) {
|
|
50
50
|
const fkCols = fkIndex.get(table.name) || [];
|
|
51
|
+
const hasSelfReference = fkCols.some(c => normalizeName(c.references.table) === table.name);
|
|
51
52
|
const distinctTargets = Array.from(new Set(fkCols.map(c => normalizeName(c.references.table))));
|
|
52
|
-
if (fkCols.length === 2 && distinctTargets.length === 2) {
|
|
53
|
+
if (!hasSelfReference && fkCols.length === 2 && distinctTargets.length === 2) {
|
|
53
54
|
const [a, b] = fkCols;
|
|
54
55
|
pivotTables.add(table.name);
|
|
55
56
|
const targetA = findTable(a.references.table);
|
|
56
57
|
const targetB = findTable(b.references.table);
|
|
57
|
-
if (targetA && targetB) {
|
|
58
|
-
const aKey = relationKeys.get(targetA.name);
|
|
59
|
-
const bKey = relationKeys.get(targetB.name);
|
|
60
|
-
const aProp = naming.belongsToManyProperty(targetB.name);
|
|
61
|
-
const bProp = naming.belongsToManyProperty(targetA.name);
|
|
62
|
-
if (!aKey.has(aProp)) {
|
|
63
|
-
aKey.add(aProp);
|
|
64
|
-
relationMap.get(targetA.name)?.push({
|
|
65
|
-
kind: 'belongsToMany',
|
|
66
|
-
property: aProp,
|
|
67
|
-
target: targetB.name,
|
|
68
|
-
pivotTable: table.name,
|
|
69
|
-
pivotForeignKeyToRoot: a.name,
|
|
70
|
-
pivotForeignKeyToTarget: b.name
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
if (!bKey.has(bProp)) {
|
|
74
|
-
bKey.add(bProp);
|
|
75
|
-
relationMap.get(targetB.name)?.push({
|
|
76
|
-
kind: 'belongsToMany',
|
|
77
|
-
property: bProp,
|
|
78
|
-
target: targetA.name,
|
|
79
|
-
pivotTable: table.name,
|
|
80
|
-
pivotForeignKeyToRoot: b.name,
|
|
81
|
-
pivotForeignKeyToTarget: a.name
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
for (const table of tables) {
|
|
89
|
-
const fkCols = fkIndex.get(table.name) || [];
|
|
90
|
-
for (const fk of fkCols) {
|
|
91
|
-
const targetTable = fk.references.table;
|
|
92
|
-
const targetKey = normalizeName(targetTable);
|
|
93
|
-
const belongsKey = relationKeys.get(table.name);
|
|
94
|
-
const hasManyKey = targetKey ? relationKeys.get(targetKey) : undefined;
|
|
95
|
-
|
|
96
|
-
if (!belongsKey || !hasManyKey) continue;
|
|
97
|
-
|
|
98
|
-
const belongsProp = naming.belongsToProperty(fk.name, targetTable);
|
|
99
|
-
if (!belongsKey.has(belongsProp)) {
|
|
100
|
-
belongsKey.add(belongsProp);
|
|
101
|
-
relationMap.get(table.name)?.push({
|
|
102
|
-
kind: 'belongsTo',
|
|
103
|
-
property: belongsProp,
|
|
104
|
-
target: targetTable,
|
|
105
|
-
foreignKey: fk.name
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Skip generating HasMany/HasOne relations TO pivot tables
|
|
110
|
-
// (pivot data is accessible via _pivot on BelongsToMany)
|
|
111
|
-
if (pivotTables.has(table.name)) continue;
|
|
112
|
-
|
|
113
|
-
const uniqueCols = uniqueSingleColumns.get(table.name);
|
|
114
|
-
const isHasOne = Boolean(uniqueCols?.has(fk.name));
|
|
115
|
-
const relationKind = isHasOne ? 'hasOne' : 'hasMany';
|
|
116
|
-
const inverseProp = isHasOne ? naming.hasOneProperty(table.name) : naming.hasManyProperty(table.name);
|
|
117
|
-
if (!hasManyKey.has(inverseProp)) {
|
|
118
|
-
hasManyKey.add(inverseProp);
|
|
119
|
-
relationMap.get(targetKey)?.push({
|
|
120
|
-
kind: relationKind,
|
|
121
|
-
property: inverseProp,
|
|
122
|
-
target: table.name,
|
|
123
|
-
foreignKey: fk.name
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return relationMap;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export const buildSchemaMetadata = (schema, naming) => {
|
|
133
|
-
const tables = schema.tables.map(t => {
|
|
134
|
-
const indexes = Array.isArray(t.indexes) ? t.indexes.map(idx => ({ ...idx })) : [];
|
|
135
|
-
const uniqueSingleColumns = new Set(
|
|
136
|
-
indexes
|
|
137
|
-
.filter(idx => idx?.unique && !idx?.where && Array.isArray(idx.columns) && idx.columns.length === 1)
|
|
138
|
-
.map(idx => idx.columns[0]?.column)
|
|
139
|
-
.filter(Boolean)
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
name: t.name,
|
|
144
|
-
schema: t.schema,
|
|
145
|
-
columns: (t.columns || []).map(col => {
|
|
146
|
-
const unique = col.unique !== undefined ? col.unique : uniqueSingleColumns.has(col.name) ? true : undefined;
|
|
147
|
-
return { ...col, unique };
|
|
148
|
-
}),
|
|
149
|
-
primaryKey: t.primaryKey || [],
|
|
150
|
-
indexes,
|
|
151
|
-
isView: false
|
|
152
|
-
};
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const views = (schema.views || []).map(v => ({
|
|
156
|
-
name: v.name,
|
|
157
|
-
schema: v.schema,
|
|
158
|
-
columns: (v.columns || []).map(col => ({ ...col })),
|
|
159
|
-
primaryKey: [],
|
|
160
|
-
indexes: [],
|
|
161
|
-
isView: true,
|
|
162
|
-
definition: v.definition,
|
|
163
|
-
comment: v.comment
|
|
164
|
-
}));
|
|
165
|
-
|
|
166
|
-
const allEntities = [...tables, ...views];
|
|
167
|
-
|
|
168
|
-
const classNames = new Map();
|
|
169
|
-
allEntities.forEach(t => {
|
|
170
|
-
const className = naming.classNameFromTable(t.name);
|
|
171
|
-
classNames.set(t.name, className);
|
|
172
|
-
if (t.schema) {
|
|
173
|
-
const qualified = `${t.schema}.${t.name}`;
|
|
174
|
-
if (!classNames.has(qualified)) {
|
|
175
|
-
classNames.set(qualified, className);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const resolveClassName = target => {
|
|
181
|
-
if (!target) return undefined;
|
|
182
|
-
if (classNames.has(target)) return classNames.get(target);
|
|
183
|
-
const fallback = target.split('.').pop();
|
|
184
|
-
if (fallback && classNames.has(fallback)) {
|
|
185
|
-
return classNames.get(fallback);
|
|
186
|
-
}
|
|
187
|
-
return undefined;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const relations = mapRelations(tables, naming);
|
|
191
|
-
|
|
192
|
-
// Detect tree tables
|
|
193
|
-
const treeConfigs = mapTreeTables(tables, naming);
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
tables,
|
|
197
|
-
views,
|
|
198
|
-
classNames,
|
|
199
|
-
relations,
|
|
200
|
-
resolveClassName,
|
|
201
|
-
treeConfigs
|
|
202
|
-
};
|
|
203
|
-
};
|
|
58
|
+
if (targetA && targetB) {
|
|
59
|
+
const aKey = relationKeys.get(targetA.name);
|
|
60
|
+
const bKey = relationKeys.get(targetB.name);
|
|
61
|
+
const aProp = naming.belongsToManyProperty(targetB.name);
|
|
62
|
+
const bProp = naming.belongsToManyProperty(targetA.name);
|
|
63
|
+
if (!aKey.has(aProp)) {
|
|
64
|
+
aKey.add(aProp);
|
|
65
|
+
relationMap.get(targetA.name)?.push({
|
|
66
|
+
kind: 'belongsToMany',
|
|
67
|
+
property: aProp,
|
|
68
|
+
target: targetB.name,
|
|
69
|
+
pivotTable: table.name,
|
|
70
|
+
pivotForeignKeyToRoot: a.name,
|
|
71
|
+
pivotForeignKeyToTarget: b.name
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (!bKey.has(bProp)) {
|
|
75
|
+
bKey.add(bProp);
|
|
76
|
+
relationMap.get(targetB.name)?.push({
|
|
77
|
+
kind: 'belongsToMany',
|
|
78
|
+
property: bProp,
|
|
79
|
+
target: targetA.name,
|
|
80
|
+
pivotTable: table.name,
|
|
81
|
+
pivotForeignKeyToRoot: b.name,
|
|
82
|
+
pivotForeignKeyToTarget: a.name
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const table of tables) {
|
|
90
|
+
const fkCols = fkIndex.get(table.name) || [];
|
|
91
|
+
for (const fk of fkCols) {
|
|
92
|
+
const targetTable = fk.references.table;
|
|
93
|
+
const targetKey = normalizeName(targetTable);
|
|
94
|
+
const belongsKey = relationKeys.get(table.name);
|
|
95
|
+
const hasManyKey = targetKey ? relationKeys.get(targetKey) : undefined;
|
|
96
|
+
|
|
97
|
+
if (!belongsKey || !hasManyKey) continue;
|
|
98
|
+
|
|
99
|
+
const belongsProp = naming.belongsToProperty(fk.name, targetTable);
|
|
100
|
+
if (!belongsKey.has(belongsProp)) {
|
|
101
|
+
belongsKey.add(belongsProp);
|
|
102
|
+
relationMap.get(table.name)?.push({
|
|
103
|
+
kind: 'belongsTo',
|
|
104
|
+
property: belongsProp,
|
|
105
|
+
target: targetTable,
|
|
106
|
+
foreignKey: fk.name
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Skip generating HasMany/HasOne relations TO pivot tables
|
|
111
|
+
// (pivot data is accessible via _pivot on BelongsToMany)
|
|
112
|
+
if (pivotTables.has(table.name)) continue;
|
|
113
|
+
|
|
114
|
+
const uniqueCols = uniqueSingleColumns.get(table.name);
|
|
115
|
+
const isHasOne = Boolean(uniqueCols?.has(fk.name));
|
|
116
|
+
const relationKind = isHasOne ? 'hasOne' : 'hasMany';
|
|
117
|
+
const inverseProp = isHasOne ? naming.hasOneProperty(table.name) : naming.hasManyProperty(table.name);
|
|
118
|
+
if (!hasManyKey.has(inverseProp)) {
|
|
119
|
+
hasManyKey.add(inverseProp);
|
|
120
|
+
relationMap.get(targetKey)?.push({
|
|
121
|
+
kind: relationKind,
|
|
122
|
+
property: inverseProp,
|
|
123
|
+
target: table.name,
|
|
124
|
+
foreignKey: fk.name
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return relationMap;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const buildSchemaMetadata = (schema, naming) => {
|
|
134
|
+
const tables = schema.tables.map(t => {
|
|
135
|
+
const indexes = Array.isArray(t.indexes) ? t.indexes.map(idx => ({ ...idx })) : [];
|
|
136
|
+
const uniqueSingleColumns = new Set(
|
|
137
|
+
indexes
|
|
138
|
+
.filter(idx => idx?.unique && !idx?.where && Array.isArray(idx.columns) && idx.columns.length === 1)
|
|
139
|
+
.map(idx => idx.columns[0]?.column)
|
|
140
|
+
.filter(Boolean)
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
name: t.name,
|
|
145
|
+
schema: t.schema,
|
|
146
|
+
columns: (t.columns || []).map(col => {
|
|
147
|
+
const unique = col.unique !== undefined ? col.unique : uniqueSingleColumns.has(col.name) ? true : undefined;
|
|
148
|
+
return { ...col, unique };
|
|
149
|
+
}),
|
|
150
|
+
primaryKey: t.primaryKey || [],
|
|
151
|
+
indexes,
|
|
152
|
+
isView: false
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const views = (schema.views || []).map(v => ({
|
|
157
|
+
name: v.name,
|
|
158
|
+
schema: v.schema,
|
|
159
|
+
columns: (v.columns || []).map(col => ({ ...col })),
|
|
160
|
+
primaryKey: [],
|
|
161
|
+
indexes: [],
|
|
162
|
+
isView: true,
|
|
163
|
+
definition: v.definition,
|
|
164
|
+
comment: v.comment
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
const allEntities = [...tables, ...views];
|
|
168
|
+
|
|
169
|
+
const classNames = new Map();
|
|
170
|
+
allEntities.forEach(t => {
|
|
171
|
+
const className = naming.classNameFromTable(t.name);
|
|
172
|
+
classNames.set(t.name, className);
|
|
173
|
+
if (t.schema) {
|
|
174
|
+
const qualified = `${t.schema}.${t.name}`;
|
|
175
|
+
if (!classNames.has(qualified)) {
|
|
176
|
+
classNames.set(qualified, className);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const resolveClassName = target => {
|
|
182
|
+
if (!target) return undefined;
|
|
183
|
+
if (classNames.has(target)) return classNames.get(target);
|
|
184
|
+
const fallback = target.split('.').pop();
|
|
185
|
+
if (fallback && classNames.has(fallback)) {
|
|
186
|
+
return classNames.get(fallback);
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const relations = mapRelations(tables, naming);
|
|
192
|
+
|
|
193
|
+
// Detect tree tables
|
|
194
|
+
const treeConfigs = mapTreeTables(tables, naming);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
tables,
|
|
198
|
+
views,
|
|
199
|
+
classNames,
|
|
200
|
+
relations,
|
|
201
|
+
resolveClassName,
|
|
202
|
+
treeConfigs
|
|
203
|
+
};
|
|
204
|
+
};
|
|
@@ -1,28 +1,34 @@
|
|
|
1
|
-
import { ColumnDefLike, RelationMetadata } from '../orm/entity-metadata.js';
|
|
2
|
-
import type { TransformerMetadata } from './transformers/transformer-metadata.js';
|
|
3
|
-
|
|
1
|
+
import { ColumnDefLike, RelationMetadata } from '../orm/entity-metadata.js';
|
|
2
|
+
import type { TransformerMetadata } from './transformers/transformer-metadata.js';
|
|
3
|
+
|
|
4
4
|
/**
|
|
5
5
|
* Bag for storing decorator metadata during the decoration phase.
|
|
6
6
|
*/
|
|
7
|
+
export interface DecoratorTreeMetadata {
|
|
8
|
+
parentProperty?: string;
|
|
9
|
+
childrenProperty?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
export interface DecoratorMetadataBag {
|
|
8
13
|
columns: Array<{ propertyName: string; column: ColumnDefLike }>;
|
|
9
14
|
relations: Array<{ propertyName: string; relation: RelationMetadata }>;
|
|
10
15
|
transformers: Array<{ propertyName: string; metadata: TransformerMetadata }>;
|
|
16
|
+
tree?: DecoratorTreeMetadata;
|
|
11
17
|
}
|
|
12
|
-
|
|
13
|
-
const METADATA_KEY = 'metal-orm:decorators';
|
|
14
|
-
|
|
15
|
-
type MetadataCarrier = {
|
|
16
|
-
metadata?: Record<PropertyKey, unknown>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Gets or creates a metadata bag for the given decorator context.
|
|
21
|
-
* @param context - The decorator context with metadata support.
|
|
22
|
-
* @returns The metadata bag.
|
|
23
|
-
*/
|
|
24
|
-
export const getOrCreateMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag => {
|
|
25
|
-
const metadata = context.metadata || (context.metadata = {} as Record<PropertyKey, unknown>);
|
|
18
|
+
|
|
19
|
+
const METADATA_KEY = 'metal-orm:decorators';
|
|
20
|
+
|
|
21
|
+
type MetadataCarrier = {
|
|
22
|
+
metadata?: Record<PropertyKey, unknown>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets or creates a metadata bag for the given decorator context.
|
|
27
|
+
* @param context - The decorator context with metadata support.
|
|
28
|
+
* @returns The metadata bag.
|
|
29
|
+
*/
|
|
30
|
+
export const getOrCreateMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag => {
|
|
31
|
+
const metadata = context.metadata || (context.metadata = {} as Record<PropertyKey, unknown>);
|
|
26
32
|
let bag = metadata[METADATA_KEY] as DecoratorMetadataBag | undefined;
|
|
27
33
|
if (!bag) {
|
|
28
34
|
bag = { columns: [], relations: [], transformers: [] };
|
|
@@ -30,32 +36,32 @@ export const getOrCreateMetadataBag = (context: MetadataCarrier): DecoratorMetad
|
|
|
30
36
|
}
|
|
31
37
|
return bag;
|
|
32
38
|
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Reads the metadata bag from the given decorator context.
|
|
36
|
-
* @param context - The decorator context with metadata support.
|
|
37
|
-
* @returns The metadata bag if present.
|
|
38
|
-
*/
|
|
39
|
-
export const readMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag | undefined => {
|
|
40
|
-
return context.metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Reads the metadata bag from a decorated constructor when using standard decorators.
|
|
45
|
-
* @param ctor - The entity constructor.
|
|
46
|
-
* @returns The metadata bag if present.
|
|
47
|
-
*/
|
|
48
|
-
export const readMetadataBagFromConstructor = (ctor: object): DecoratorMetadataBag | undefined => {
|
|
49
|
-
const metadataSymbol = (Symbol as { metadata?: symbol }).metadata;
|
|
50
|
-
if (!metadataSymbol) return undefined;
|
|
51
|
-
const metadata = Reflect.get(ctor, metadataSymbol) as Record<PropertyKey, unknown> | undefined;
|
|
52
|
-
return metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Public helper to read decorator metadata from a class constructor.
|
|
57
|
-
* @param ctor - The entity constructor.
|
|
58
|
-
* @returns The metadata bag if present.
|
|
59
|
-
*/
|
|
60
|
-
export const getDecoratorMetadata = (ctor: object): DecoratorMetadataBag | undefined =>
|
|
61
|
-
readMetadataBagFromConstructor(ctor);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Reads the metadata bag from the given decorator context.
|
|
42
|
+
* @param context - The decorator context with metadata support.
|
|
43
|
+
* @returns The metadata bag if present.
|
|
44
|
+
*/
|
|
45
|
+
export const readMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag | undefined => {
|
|
46
|
+
return context.metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Reads the metadata bag from a decorated constructor when using standard decorators.
|
|
51
|
+
* @param ctor - The entity constructor.
|
|
52
|
+
* @returns The metadata bag if present.
|
|
53
|
+
*/
|
|
54
|
+
export const readMetadataBagFromConstructor = (ctor: object): DecoratorMetadataBag | undefined => {
|
|
55
|
+
const metadataSymbol = (Symbol as { metadata?: symbol }).metadata;
|
|
56
|
+
if (!metadataSymbol) return undefined;
|
|
57
|
+
const metadata = Reflect.get(ctor, metadataSymbol) as Record<PropertyKey, unknown> | undefined;
|
|
58
|
+
return metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Public helper to read decorator metadata from a class constructor.
|
|
63
|
+
* @param ctor - The entity constructor.
|
|
64
|
+
* @returns The metadata bag if present.
|
|
65
|
+
*/
|
|
66
|
+
export const getDecoratorMetadata = (ctor: object): DecoratorMetadataBag | undefined =>
|
|
67
|
+
readMetadataBagFromConstructor(ctor);
|