@carno.js/orm 0.2.3
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/LICENSE +674 -0
- package/dist/SqlBuilder.d.ts +103 -0
- package/dist/SqlBuilder.js +618 -0
- package/dist/cache/cache-key-generator.d.ts +13 -0
- package/dist/cache/cache-key-generator.js +66 -0
- package/dist/cache/query-cache-manager.d.ts +14 -0
- package/dist/cache/query-cache-manager.js +44 -0
- package/dist/common/email.vo.d.ts +4 -0
- package/dist/common/email.vo.js +11 -0
- package/dist/common/uuid.d.ts +4 -0
- package/dist/common/uuid.js +10 -0
- package/dist/common/value-object.d.ts +95 -0
- package/dist/common/value-object.js +99 -0
- package/dist/constants.d.ts +6 -0
- package/dist/constants.js +9 -0
- package/dist/decorators/computed.decorator.d.ts +1 -0
- package/dist/decorators/computed.decorator.js +12 -0
- package/dist/decorators/entity.decorator.d.ts +3 -0
- package/dist/decorators/entity.decorator.js +12 -0
- package/dist/decorators/enum.decorator.d.ts +2 -0
- package/dist/decorators/enum.decorator.js +16 -0
- package/dist/decorators/event-hook.decorator.d.ts +4 -0
- package/dist/decorators/event-hook.decorator.js +31 -0
- package/dist/decorators/index.decorator.d.ts +17 -0
- package/dist/decorators/index.decorator.js +36 -0
- package/dist/decorators/one-many.decorator.d.ts +6 -0
- package/dist/decorators/one-many.decorator.js +42 -0
- package/dist/decorators/primary-key.decorator.d.ts +2 -0
- package/dist/decorators/primary-key.decorator.js +8 -0
- package/dist/decorators/property.decorator.d.ts +24 -0
- package/dist/decorators/property.decorator.js +44 -0
- package/dist/decorators/unique.decorator.d.ts +9 -0
- package/dist/decorators/unique.decorator.js +44 -0
- package/dist/domain/base-entity.d.ts +57 -0
- package/dist/domain/base-entity.js +198 -0
- package/dist/domain/collection.d.ts +6 -0
- package/dist/domain/collection.js +15 -0
- package/dist/domain/entities.d.ts +49 -0
- package/dist/domain/entities.js +259 -0
- package/dist/domain/reference.d.ts +86 -0
- package/dist/domain/reference.js +86 -0
- package/dist/driver/bun-driver.base.d.ts +72 -0
- package/dist/driver/bun-driver.base.js +270 -0
- package/dist/driver/bun-mysql.driver.d.ts +53 -0
- package/dist/driver/bun-mysql.driver.js +256 -0
- package/dist/driver/bun-pg.driver.d.ts +52 -0
- package/dist/driver/bun-pg.driver.js +263 -0
- package/dist/driver/driver.interface.d.ts +333 -0
- package/dist/driver/driver.interface.js +2 -0
- package/dist/entry.d.ts +2 -0
- package/dist/entry.js +13 -0
- package/dist/identity-map/entity-key-generator.d.ts +10 -0
- package/dist/identity-map/entity-key-generator.js +45 -0
- package/dist/identity-map/entity-registry.d.ts +11 -0
- package/dist/identity-map/entity-registry.js +41 -0
- package/dist/identity-map/identity-map-context.d.ts +9 -0
- package/dist/identity-map/identity-map-context.js +22 -0
- package/dist/identity-map/identity-map-integration.d.ts +5 -0
- package/dist/identity-map/identity-map-integration.js +37 -0
- package/dist/identity-map/identity-map.d.ts +11 -0
- package/dist/identity-map/identity-map.js +35 -0
- package/dist/identity-map/index.d.ts +5 -0
- package/dist/identity-map/index.js +14 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +48 -0
- package/dist/middleware/identity-map.middleware.d.ts +4 -0
- package/dist/middleware/identity-map.middleware.js +22 -0
- package/dist/orm-session-context.d.ts +16 -0
- package/dist/orm-session-context.js +22 -0
- package/dist/orm.d.ts +20 -0
- package/dist/orm.js +69 -0
- package/dist/orm.service.d.ts +13 -0
- package/dist/orm.service.js +361 -0
- package/dist/query/index-condition-builder.d.ts +41 -0
- package/dist/query/index-condition-builder.js +235 -0
- package/dist/query/model-transformer.d.ts +27 -0
- package/dist/query/model-transformer.js +201 -0
- package/dist/query/sql-column-manager.d.ts +28 -0
- package/dist/query/sql-column-manager.js +157 -0
- package/dist/query/sql-condition-builder.d.ts +51 -0
- package/dist/query/sql-condition-builder.js +264 -0
- package/dist/query/sql-join-manager.d.ts +39 -0
- package/dist/query/sql-join-manager.js +242 -0
- package/dist/query/sql-subquery-builder.d.ts +20 -0
- package/dist/query/sql-subquery-builder.js +119 -0
- package/dist/repository/Repository.d.ts +121 -0
- package/dist/repository/Repository.js +174 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/with-database.d.ts +20 -0
- package/dist/testing/with-database.js +311 -0
- package/dist/transaction/transaction-context.d.ts +10 -0
- package/dist/transaction/transaction-context.js +19 -0
- package/dist/utils/value-processor.d.ts +14 -0
- package/dist/utils/value-processor.js +94 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +24 -0
- package/package.json +59 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IndexConditionBuilder = void 0;
|
|
4
|
+
const value_object_1 = require("../common/value-object");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
class IndexConditionBuilder {
|
|
7
|
+
constructor(columnMap) {
|
|
8
|
+
this.columnMap = columnMap;
|
|
9
|
+
this.OPERATORS = [
|
|
10
|
+
'$eq',
|
|
11
|
+
'$ne',
|
|
12
|
+
'$in',
|
|
13
|
+
'$nin',
|
|
14
|
+
'$like',
|
|
15
|
+
'$gt',
|
|
16
|
+
'$gte',
|
|
17
|
+
'$lt',
|
|
18
|
+
'$lte',
|
|
19
|
+
'$and',
|
|
20
|
+
'$or',
|
|
21
|
+
'$nor',
|
|
22
|
+
];
|
|
23
|
+
this.lastKeyNotOperator = '';
|
|
24
|
+
}
|
|
25
|
+
build(condition) {
|
|
26
|
+
const sqlParts = this.processConditions(condition);
|
|
27
|
+
if (sqlParts.length === 0) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
return this.wrapWithLogicalOperator(sqlParts, 'AND');
|
|
31
|
+
}
|
|
32
|
+
processConditions(condition) {
|
|
33
|
+
const entries = this.getEntries(condition);
|
|
34
|
+
return this.processEntries(entries);
|
|
35
|
+
}
|
|
36
|
+
getEntries(condition) {
|
|
37
|
+
if (!condition || typeof condition !== 'object') {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
return Object.entries(condition);
|
|
41
|
+
}
|
|
42
|
+
processEntries(entries) {
|
|
43
|
+
const sqlParts = [];
|
|
44
|
+
for (const [key, value] of entries) {
|
|
45
|
+
const extractedValue = this.extractValueFromValueObject(value);
|
|
46
|
+
const conditionSql = this.processEntry(key, extractedValue);
|
|
47
|
+
if (conditionSql) {
|
|
48
|
+
sqlParts.push(conditionSql);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return sqlParts;
|
|
52
|
+
}
|
|
53
|
+
processEntry(key, value) {
|
|
54
|
+
this.trackLastNonOperatorKey(key);
|
|
55
|
+
if (this.isScalarValue(value)) {
|
|
56
|
+
return this.handleScalarValue(key, value);
|
|
57
|
+
}
|
|
58
|
+
if (this.isArrayValue(key, value)) {
|
|
59
|
+
return this.buildInCondition(key, value);
|
|
60
|
+
}
|
|
61
|
+
return this.handleObjectValue(key, value);
|
|
62
|
+
}
|
|
63
|
+
handleScalarValue(key, value) {
|
|
64
|
+
if (key === '$eq') {
|
|
65
|
+
return this.buildSimpleCondition(this.lastKeyNotOperator, value, '=');
|
|
66
|
+
}
|
|
67
|
+
return this.buildSimpleCondition(key, value, '=');
|
|
68
|
+
}
|
|
69
|
+
handleObjectValue(key, value) {
|
|
70
|
+
if (this.isLogicalOperator(key)) {
|
|
71
|
+
return this.buildLogicalOperatorCondition(key, value);
|
|
72
|
+
}
|
|
73
|
+
if (this.isNorOperator(key)) {
|
|
74
|
+
return this.buildNorCondition(value);
|
|
75
|
+
}
|
|
76
|
+
return this.buildOperatorConditions(key, value);
|
|
77
|
+
}
|
|
78
|
+
buildLogicalOperatorCondition(key, value) {
|
|
79
|
+
const conditions = value.map((cond) => this.build(cond));
|
|
80
|
+
const operator = this.extractLogicalOperator(key);
|
|
81
|
+
return this.wrapWithLogicalOperator(conditions, operator);
|
|
82
|
+
}
|
|
83
|
+
buildOperatorConditions(key, value) {
|
|
84
|
+
const parts = [];
|
|
85
|
+
for (const operator of this.OPERATORS) {
|
|
86
|
+
if (operator in value) {
|
|
87
|
+
const condition = this.buildOperatorCondition(key, operator, value[operator]);
|
|
88
|
+
parts.push(condition);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return parts.join(' AND ');
|
|
92
|
+
}
|
|
93
|
+
buildOperatorCondition(key, operator, value) {
|
|
94
|
+
switch (operator) {
|
|
95
|
+
case '$eq':
|
|
96
|
+
return this.buildSimpleCondition(key, value, '=');
|
|
97
|
+
case '$ne':
|
|
98
|
+
return this.buildSimpleCondition(key, value, '!=');
|
|
99
|
+
case '$in':
|
|
100
|
+
return this.buildInCondition(key, value);
|
|
101
|
+
case '$nin':
|
|
102
|
+
return this.buildNotInCondition(key, value);
|
|
103
|
+
case '$like':
|
|
104
|
+
return this.buildLikeCondition(key, value);
|
|
105
|
+
case '$gt':
|
|
106
|
+
return this.buildComparisonCondition(key, value, '>');
|
|
107
|
+
case '$gte':
|
|
108
|
+
return this.buildComparisonCondition(key, value, '>=');
|
|
109
|
+
case '$lt':
|
|
110
|
+
return this.buildComparisonCondition(key, value, '<');
|
|
111
|
+
case '$lte':
|
|
112
|
+
return this.buildComparisonCondition(key, value, '<=');
|
|
113
|
+
case '$and':
|
|
114
|
+
case '$or':
|
|
115
|
+
return this.buildNestedLogicalCondition(operator, value);
|
|
116
|
+
case '$nor':
|
|
117
|
+
return this.buildNorCondition(value);
|
|
118
|
+
default:
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
buildSimpleCondition(key, value, operator) {
|
|
123
|
+
const column = this.resolveColumnName(key);
|
|
124
|
+
if (this.isNullish(value))
|
|
125
|
+
return this.buildNullCondition(column, operator);
|
|
126
|
+
const formattedValue = this.formatValue(value);
|
|
127
|
+
return `${column} ${operator} ${formattedValue}`;
|
|
128
|
+
}
|
|
129
|
+
buildInCondition(key, values) {
|
|
130
|
+
const column = this.resolveColumnName(key);
|
|
131
|
+
const formattedValues = values.map((val) => this.formatValue(val)).join(', ');
|
|
132
|
+
return `${column} IN (${formattedValues})`;
|
|
133
|
+
}
|
|
134
|
+
buildNotInCondition(key, values) {
|
|
135
|
+
const column = this.resolveColumnName(key);
|
|
136
|
+
const formattedValues = values.map((val) => this.formatValue(val)).join(', ');
|
|
137
|
+
return `${column} NOT IN (${formattedValues})`;
|
|
138
|
+
}
|
|
139
|
+
buildLikeCondition(key, value) {
|
|
140
|
+
const column = this.resolveColumnName(key);
|
|
141
|
+
return `${column} LIKE '${value}'`;
|
|
142
|
+
}
|
|
143
|
+
buildComparisonCondition(key, value, operator) {
|
|
144
|
+
const column = this.resolveColumnName(key);
|
|
145
|
+
if (this.isNullish(value))
|
|
146
|
+
return this.buildNullCondition(column, operator);
|
|
147
|
+
const formattedValue = this.formatValue(value);
|
|
148
|
+
return `${column} ${operator} ${formattedValue}`;
|
|
149
|
+
}
|
|
150
|
+
buildNestedLogicalCondition(operator, value) {
|
|
151
|
+
const conditions = value.map((cond) => this.build(cond));
|
|
152
|
+
const logicalOp = this.extractLogicalOperator(operator);
|
|
153
|
+
return this.wrapWithLogicalOperator(conditions, logicalOp);
|
|
154
|
+
}
|
|
155
|
+
buildNorCondition(value) {
|
|
156
|
+
const conditions = value.map((cond) => this.build(cond));
|
|
157
|
+
const wrapped = this.wrapWithLogicalOperator(conditions, 'OR');
|
|
158
|
+
return `NOT ${wrapped}`;
|
|
159
|
+
}
|
|
160
|
+
wrapWithLogicalOperator(conditions, operator) {
|
|
161
|
+
return `(${conditions.join(` ${operator} `)})`;
|
|
162
|
+
}
|
|
163
|
+
extractValueFromValueObject(value) {
|
|
164
|
+
if ((0, utils_1.extendsFrom)(value_object_1.ValueObject, value?.constructor?.prototype)) {
|
|
165
|
+
return value.getValue();
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
formatValue(value) {
|
|
170
|
+
if (value instanceof Date)
|
|
171
|
+
return this.formatDate(value);
|
|
172
|
+
if (this.isNullish(value))
|
|
173
|
+
return 'NULL';
|
|
174
|
+
if (this.isPrimitive(value))
|
|
175
|
+
return this.formatPrimitive(value);
|
|
176
|
+
return this.formatJson(value);
|
|
177
|
+
}
|
|
178
|
+
formatDate(value) {
|
|
179
|
+
return `'${value.toISOString()}'`;
|
|
180
|
+
}
|
|
181
|
+
isNullish(value) {
|
|
182
|
+
return value === null || value === undefined;
|
|
183
|
+
}
|
|
184
|
+
isPrimitive(value) {
|
|
185
|
+
return ['string', 'number', 'boolean', 'bigint'].includes(typeof value);
|
|
186
|
+
}
|
|
187
|
+
formatPrimitive(value) {
|
|
188
|
+
if (typeof value === 'string')
|
|
189
|
+
return `'${this.escapeString(value)}'`;
|
|
190
|
+
return `${value}`;
|
|
191
|
+
}
|
|
192
|
+
formatJson(value) {
|
|
193
|
+
return `'${this.escapeString(JSON.stringify(value))}'`;
|
|
194
|
+
}
|
|
195
|
+
escapeString(value) {
|
|
196
|
+
return value.replace(/'/g, "''");
|
|
197
|
+
}
|
|
198
|
+
isScalarValue(value) {
|
|
199
|
+
const isDate = value instanceof Date;
|
|
200
|
+
return typeof value !== 'object' || value === null || isDate;
|
|
201
|
+
}
|
|
202
|
+
isArrayValue(key, value) {
|
|
203
|
+
return !this.OPERATORS.includes(key) && Array.isArray(value);
|
|
204
|
+
}
|
|
205
|
+
isLogicalOperator(key) {
|
|
206
|
+
return ['$or', '$and'].includes(key);
|
|
207
|
+
}
|
|
208
|
+
isNorOperator(key) {
|
|
209
|
+
return key === '$nor';
|
|
210
|
+
}
|
|
211
|
+
extractLogicalOperator(key) {
|
|
212
|
+
return key.toUpperCase().replace('$', '');
|
|
213
|
+
}
|
|
214
|
+
trackLastNonOperatorKey(key) {
|
|
215
|
+
if (!this.OPERATORS.includes(key)) {
|
|
216
|
+
this.lastKeyNotOperator = key;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
resolveColumnName(property) {
|
|
220
|
+
if (property.startsWith('$')) {
|
|
221
|
+
return property;
|
|
222
|
+
}
|
|
223
|
+
const column = this.columnMap[property];
|
|
224
|
+
if (column) {
|
|
225
|
+
return column;
|
|
226
|
+
}
|
|
227
|
+
return (0, utils_1.toSnakeCase)(property);
|
|
228
|
+
}
|
|
229
|
+
buildNullCondition(column, operator) {
|
|
230
|
+
if (operator === '!=' || operator === '<>')
|
|
231
|
+
return `${column} IS NOT NULL`;
|
|
232
|
+
return `${column} IS NULL`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.IndexConditionBuilder = IndexConditionBuilder;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Statement } from '../driver/driver.interface';
|
|
2
|
+
import { EntityStorage } from '../domain/entities';
|
|
3
|
+
export declare class ModelTransformer {
|
|
4
|
+
private entityStorage;
|
|
5
|
+
constructor(entityStorage: EntityStorage);
|
|
6
|
+
transform<T>(model: any, statement: Statement<any>, data: any): T;
|
|
7
|
+
private registerInstancesInIdentityMap;
|
|
8
|
+
private createInstances;
|
|
9
|
+
private createInstance;
|
|
10
|
+
private addJoinedInstances;
|
|
11
|
+
private extractPrimaryKeyFromData;
|
|
12
|
+
private extractPrimaryKeyValue;
|
|
13
|
+
private getPrimaryKeyFromData;
|
|
14
|
+
private findPrimaryKeyProperty;
|
|
15
|
+
private buildOptionsMap;
|
|
16
|
+
private populateProperties;
|
|
17
|
+
private parseColumnKey;
|
|
18
|
+
private setPropertyValue;
|
|
19
|
+
private findPropertyByColumnName;
|
|
20
|
+
private isValueObjectType;
|
|
21
|
+
private linkJoinedEntities;
|
|
22
|
+
private linkSingleJoin;
|
|
23
|
+
private findRelationProperty;
|
|
24
|
+
private attachJoinedEntity;
|
|
25
|
+
private appendToArray;
|
|
26
|
+
private resetChangedValues;
|
|
27
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ModelTransformer = void 0;
|
|
4
|
+
const value_object_1 = require("../common/value-object");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const identity_map_1 = require("../identity-map");
|
|
7
|
+
class ModelTransformer {
|
|
8
|
+
constructor(entityStorage) {
|
|
9
|
+
this.entityStorage = entityStorage;
|
|
10
|
+
}
|
|
11
|
+
transform(model, statement, data) {
|
|
12
|
+
const { instanceMap, cachedAliases } = this.createInstances(model, statement, data);
|
|
13
|
+
const optionsMap = this.buildOptionsMap(instanceMap);
|
|
14
|
+
this.populateProperties(data, instanceMap, optionsMap, cachedAliases);
|
|
15
|
+
this.linkJoinedEntities(statement, instanceMap, optionsMap);
|
|
16
|
+
this.resetChangedValues(instanceMap, cachedAliases);
|
|
17
|
+
this.registerInstancesInIdentityMap(instanceMap, cachedAliases);
|
|
18
|
+
return instanceMap[statement.alias];
|
|
19
|
+
}
|
|
20
|
+
registerInstancesInIdentityMap(instanceMap, cachedAliases) {
|
|
21
|
+
Object.entries(instanceMap).forEach(([alias, instance]) => {
|
|
22
|
+
// Skip registering entities that were already in cache
|
|
23
|
+
if (cachedAliases.has(alias)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
identity_map_1.IdentityMapIntegration.registerEntity(instance);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
createInstances(model, statement, data) {
|
|
30
|
+
const cachedAliases = new Set();
|
|
31
|
+
const primaryKey = this.extractPrimaryKeyFromData(model, statement.alias, data);
|
|
32
|
+
const { instance, wasCached } = this.createInstance(model, primaryKey);
|
|
33
|
+
if (wasCached) {
|
|
34
|
+
cachedAliases.add(statement.alias);
|
|
35
|
+
}
|
|
36
|
+
const instanceMap = {
|
|
37
|
+
[statement.alias]: instance,
|
|
38
|
+
};
|
|
39
|
+
if (statement.join) {
|
|
40
|
+
this.addJoinedInstances(statement, instanceMap, data, cachedAliases);
|
|
41
|
+
}
|
|
42
|
+
return { instanceMap, cachedAliases };
|
|
43
|
+
}
|
|
44
|
+
createInstance(model, primaryKey) {
|
|
45
|
+
const cached = identity_map_1.IdentityMapIntegration.getEntity(model, primaryKey);
|
|
46
|
+
if (cached) {
|
|
47
|
+
return { instance: cached, wasCached: true };
|
|
48
|
+
}
|
|
49
|
+
const instance = new model();
|
|
50
|
+
instance.$_isPersisted = true;
|
|
51
|
+
// Note: Registration happens later in registerInstancesInIdentityMap after properties are populated
|
|
52
|
+
return { instance, wasCached: false };
|
|
53
|
+
}
|
|
54
|
+
addJoinedInstances(statement, instanceMap, data, cachedAliases) {
|
|
55
|
+
statement.join.forEach(join => {
|
|
56
|
+
const primaryKey = this.extractPrimaryKeyFromData(join.joinEntity, join.joinAlias, data);
|
|
57
|
+
const { instance: joinInstance, wasCached } = this.createInstance(join.joinEntity, primaryKey);
|
|
58
|
+
if (wasCached) {
|
|
59
|
+
cachedAliases.add(join.joinAlias);
|
|
60
|
+
}
|
|
61
|
+
instanceMap[join.joinAlias] = joinInstance;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
extractPrimaryKeyFromData(model, alias, data) {
|
|
65
|
+
const options = this.entityStorage.get(model);
|
|
66
|
+
if (!options) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return this.extractPrimaryKeyValue(options, alias, data);
|
|
70
|
+
}
|
|
71
|
+
extractPrimaryKeyValue(options, alias, data) {
|
|
72
|
+
const pkProperty = this.findPrimaryKeyProperty(options);
|
|
73
|
+
if (!pkProperty) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
return this.getPrimaryKeyFromData(pkProperty, alias, data);
|
|
77
|
+
}
|
|
78
|
+
getPrimaryKeyFromData(pkProperty, alias, data) {
|
|
79
|
+
const pkColumnName = pkProperty.options.columnName;
|
|
80
|
+
const pkKey = `${alias}_${pkColumnName}`;
|
|
81
|
+
return data[pkKey];
|
|
82
|
+
}
|
|
83
|
+
findPrimaryKeyProperty(options) {
|
|
84
|
+
for (const prop in options.properties) {
|
|
85
|
+
if (options.properties[prop].options.isPrimary) {
|
|
86
|
+
return options.properties[prop];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
buildOptionsMap(instanceMap) {
|
|
92
|
+
const optionsMap = new Map();
|
|
93
|
+
for (const [alias, instance] of Object.entries(instanceMap)) {
|
|
94
|
+
const options = this.entityStorage.get(instance.constructor);
|
|
95
|
+
if (options) {
|
|
96
|
+
optionsMap.set(alias, options);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return optionsMap;
|
|
100
|
+
}
|
|
101
|
+
populateProperties(data, instanceMap, optionsMap, cachedAliases) {
|
|
102
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
103
|
+
const { alias, propertyName } = this.parseColumnKey(key);
|
|
104
|
+
const entity = instanceMap[alias];
|
|
105
|
+
if (!entity) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Skip populating properties for cached entities to preserve in-memory changes
|
|
109
|
+
if (cachedAliases.has(alias)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.setPropertyValue(entity, propertyName, value, optionsMap.get(alias));
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
parseColumnKey(key) {
|
|
116
|
+
const index = key.indexOf('_');
|
|
117
|
+
return {
|
|
118
|
+
alias: key.substring(0, index),
|
|
119
|
+
propertyName: key.substring(index + 1),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
setPropertyValue(entity, columnName, value, options) {
|
|
123
|
+
const propertyInfo = this.findPropertyByColumnName(columnName, options);
|
|
124
|
+
if (!propertyInfo) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const { key, property } = propertyInfo;
|
|
128
|
+
if (this.isValueObjectType(property.type)) {
|
|
129
|
+
entity[key] = new property.type(value);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
entity[key] = value;
|
|
133
|
+
}
|
|
134
|
+
findPropertyByColumnName(columnName, options) {
|
|
135
|
+
// First, try to find in regular properties
|
|
136
|
+
const entry = Object.entries(options.properties).find(([_, prop]) => prop.options.columnName === columnName);
|
|
137
|
+
if (entry) {
|
|
138
|
+
return { key: entry[0], property: entry[1] };
|
|
139
|
+
}
|
|
140
|
+
// If not found, try to find in relations (many-to-one)
|
|
141
|
+
const relation = options.relations?.find((rel) => rel.columnName === columnName && rel.relation === 'many-to-one');
|
|
142
|
+
if (relation) {
|
|
143
|
+
return { key: relation.propertyKey, property: relation };
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
isValueObjectType(type) {
|
|
148
|
+
return (0, utils_1.extendsFrom)(value_object_1.ValueObject, type?.prototype);
|
|
149
|
+
}
|
|
150
|
+
linkJoinedEntities(statement, instanceMap, optionsMap) {
|
|
151
|
+
if (!statement.join) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
statement.join.forEach(join => {
|
|
155
|
+
this.linkSingleJoin(join, instanceMap, optionsMap);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
linkSingleJoin(join, instanceMap, optionsMap) {
|
|
159
|
+
const { joinAlias, originAlias, propertyKey } = join;
|
|
160
|
+
const originEntity = instanceMap[originAlias];
|
|
161
|
+
const joinEntity = instanceMap[joinAlias];
|
|
162
|
+
if (!originEntity || !joinEntity) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const property = this.findRelationProperty(originAlias, propertyKey, optionsMap);
|
|
166
|
+
if (property) {
|
|
167
|
+
this.attachJoinedEntity(originEntity, joinEntity, propertyKey, property);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
findRelationProperty(alias, propertyKey, optionsMap) {
|
|
171
|
+
return optionsMap.get(alias)?.relations.find(rel => rel.propertyKey === propertyKey);
|
|
172
|
+
}
|
|
173
|
+
attachJoinedEntity(originEntity, joinEntity, propertyKey, property) {
|
|
174
|
+
if (property.type === Array) {
|
|
175
|
+
originEntity[propertyKey] = this.appendToArray(originEntity[propertyKey], joinEntity);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
originEntity[propertyKey] = joinEntity;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
appendToArray(existingArray, newItem) {
|
|
182
|
+
return existingArray ? [...existingArray, newItem] : [newItem];
|
|
183
|
+
}
|
|
184
|
+
resetChangedValues(instanceMap, cachedAliases) {
|
|
185
|
+
Object.entries(instanceMap).forEach(([alias, instance]) => {
|
|
186
|
+
// Skip resetting changed values for cached entities to preserve in-memory changes
|
|
187
|
+
if (cachedAliases.has(alias)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const currentValues = {};
|
|
191
|
+
for (const key in instance) {
|
|
192
|
+
if (!key.startsWith('_') && !key.startsWith('$')) {
|
|
193
|
+
currentValues[key] = instance[key];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
instance._oldValues = currentValues;
|
|
197
|
+
instance._changedValues = {};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
exports.ModelTransformer = ModelTransformer;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Statement } from '../driver/driver.interface';
|
|
2
|
+
import { EntityStorage, Options } from '../domain/entities';
|
|
3
|
+
export declare class SqlColumnManager {
|
|
4
|
+
private entityStorage;
|
|
5
|
+
private statements;
|
|
6
|
+
private entity;
|
|
7
|
+
constructor(entityStorage: EntityStorage, statements: Statement<any>, entity: Options);
|
|
8
|
+
generateColumns(model: Function, updatedColumns: string[]): string[];
|
|
9
|
+
processUserColumns(columns: string[]): string[];
|
|
10
|
+
getColumnsForEntity(entity: Function, alias: string): string[];
|
|
11
|
+
discoverAlias(column: string, onlyAlias?: boolean): string | undefined;
|
|
12
|
+
private getJoinColumns;
|
|
13
|
+
private getPropertyColumns;
|
|
14
|
+
private getRelationColumns;
|
|
15
|
+
private extractAliases;
|
|
16
|
+
private filterValid;
|
|
17
|
+
private isNestedColumn;
|
|
18
|
+
private buildSimpleColumnAlias;
|
|
19
|
+
private buildNestedColumnAlias;
|
|
20
|
+
private validateJoinsExist;
|
|
21
|
+
private resolveNestedAlias;
|
|
22
|
+
private buildJoinMaps;
|
|
23
|
+
private findNextAlias;
|
|
24
|
+
private formatColumnWithAlias;
|
|
25
|
+
private getColumnNameFromProperty;
|
|
26
|
+
private getColumnNameFromPropertyForEntity;
|
|
27
|
+
private getEntityFromAlias;
|
|
28
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SqlColumnManager = void 0;
|
|
4
|
+
class SqlColumnManager {
|
|
5
|
+
constructor(entityStorage, statements, entity) {
|
|
6
|
+
this.entityStorage = entityStorage;
|
|
7
|
+
this.statements = statements;
|
|
8
|
+
this.entity = entity;
|
|
9
|
+
}
|
|
10
|
+
generateColumns(model, updatedColumns) {
|
|
11
|
+
const baseColumns = this.getColumnsForEntity(model, this.statements.alias);
|
|
12
|
+
const joinColumns = this.getJoinColumns();
|
|
13
|
+
const allColumns = [...baseColumns, ...joinColumns];
|
|
14
|
+
return [...allColumns, ...updatedColumns];
|
|
15
|
+
}
|
|
16
|
+
processUserColumns(columns) {
|
|
17
|
+
const aliasedColumns = this.extractAliases(columns);
|
|
18
|
+
return this.filterValid(aliasedColumns);
|
|
19
|
+
}
|
|
20
|
+
getColumnsForEntity(entity, alias) {
|
|
21
|
+
const entityOptions = this.entityStorage.get(entity);
|
|
22
|
+
if (!entityOptions) {
|
|
23
|
+
throw new Error('Entity not found');
|
|
24
|
+
}
|
|
25
|
+
const propertyColumns = this.getPropertyColumns(entityOptions, alias);
|
|
26
|
+
const relationColumns = this.getRelationColumns(entityOptions, alias);
|
|
27
|
+
return [...propertyColumns, ...relationColumns];
|
|
28
|
+
}
|
|
29
|
+
discoverAlias(column, onlyAlias = false) {
|
|
30
|
+
if (!this.isNestedColumn(column)) {
|
|
31
|
+
return this.buildSimpleColumnAlias(column, this.statements.alias, onlyAlias);
|
|
32
|
+
}
|
|
33
|
+
return this.buildNestedColumnAlias(column, onlyAlias);
|
|
34
|
+
}
|
|
35
|
+
getJoinColumns() {
|
|
36
|
+
if (!this.statements.join) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return this.statements.join.flatMap(join => this.getColumnsForEntity(join.joinEntity, join.joinAlias));
|
|
40
|
+
}
|
|
41
|
+
getPropertyColumns(entityOptions, alias) {
|
|
42
|
+
return Object.keys(entityOptions.properties).map(key => {
|
|
43
|
+
const columnName = entityOptions.properties[key].options.columnName;
|
|
44
|
+
return `${alias}."${columnName}" as "${alias}_${columnName}"`;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
getRelationColumns(entityOptions, alias) {
|
|
48
|
+
if (!entityOptions.relations) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
return entityOptions.relations
|
|
52
|
+
.filter(relation => relation.relation === 'many-to-one')
|
|
53
|
+
.map(relation => `${alias}."${relation.columnName}" as "${alias}_${relation.columnName}"`);
|
|
54
|
+
}
|
|
55
|
+
extractAliases(columns) {
|
|
56
|
+
return columns
|
|
57
|
+
.map(column => this.discoverAlias(column))
|
|
58
|
+
.flat()
|
|
59
|
+
.filter((col) => col !== undefined);
|
|
60
|
+
}
|
|
61
|
+
filterValid(columns) {
|
|
62
|
+
return columns.filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
isNestedColumn(column) {
|
|
65
|
+
return column.includes('.');
|
|
66
|
+
}
|
|
67
|
+
buildSimpleColumnAlias(column, alias, onlyAlias) {
|
|
68
|
+
const columnName = this.getColumnNameFromProperty(column);
|
|
69
|
+
if (onlyAlias) {
|
|
70
|
+
return `${alias}."${columnName}"`;
|
|
71
|
+
}
|
|
72
|
+
return `${alias}."${columnName}" as ${alias}_${columnName}`;
|
|
73
|
+
}
|
|
74
|
+
buildNestedColumnAlias(column, onlyAlias) {
|
|
75
|
+
this.validateJoinsExist();
|
|
76
|
+
const parts = column.split('.');
|
|
77
|
+
const aliasInfo = this.resolveNestedAlias(parts);
|
|
78
|
+
if (!aliasInfo) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return this.formatColumnWithAlias(aliasInfo.alias, parts[parts.length - 1], onlyAlias);
|
|
82
|
+
}
|
|
83
|
+
validateJoinsExist() {
|
|
84
|
+
if (!this.statements.join && !this.statements.selectJoin) {
|
|
85
|
+
throw new Error('Join not found');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
resolveNestedAlias(parts) {
|
|
89
|
+
const joinMaps = this.buildJoinMaps();
|
|
90
|
+
let currentAlias = this.statements.alias;
|
|
91
|
+
for (let i = 0; i < parts.length; i++) {
|
|
92
|
+
const part = parts[i];
|
|
93
|
+
const isLastPart = i === parts.length - 1;
|
|
94
|
+
if (isLastPart) {
|
|
95
|
+
return { alias: currentAlias };
|
|
96
|
+
}
|
|
97
|
+
const nextAlias = this.findNextAlias(part, joinMaps, i === 0);
|
|
98
|
+
if (!nextAlias) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
currentAlias = nextAlias;
|
|
102
|
+
}
|
|
103
|
+
return { alias: currentAlias };
|
|
104
|
+
}
|
|
105
|
+
buildJoinMaps() {
|
|
106
|
+
const relationsMap = new Map(this.entity.relations.map(rel => [rel.propertyKey, rel]));
|
|
107
|
+
const joinMap = new Map();
|
|
108
|
+
this.statements.join?.forEach(join => joinMap.set(join.joinProperty, join));
|
|
109
|
+
const selectJoinMap = new Map();
|
|
110
|
+
this.statements.selectJoin?.forEach(join => selectJoinMap.set(join.joinProperty, join));
|
|
111
|
+
return { joinMap, selectJoinMap, relationsMap };
|
|
112
|
+
}
|
|
113
|
+
findNextAlias(part, maps, isFirstPart) {
|
|
114
|
+
if (maps.joinMap.has(part)) {
|
|
115
|
+
return maps.joinMap.get(part).joinAlias;
|
|
116
|
+
}
|
|
117
|
+
if (maps.selectJoinMap.has(part)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
formatColumnWithAlias(alias, propertyName, onlyAlias) {
|
|
123
|
+
const entity = this.getEntityFromAlias(alias);
|
|
124
|
+
const columnName = this.getColumnNameFromPropertyForEntity(propertyName, entity);
|
|
125
|
+
if (onlyAlias) {
|
|
126
|
+
return `${alias}."${columnName}"`;
|
|
127
|
+
}
|
|
128
|
+
return `${alias}."${columnName}" as ${alias}_${columnName}`;
|
|
129
|
+
}
|
|
130
|
+
getColumnNameFromProperty(propertyName) {
|
|
131
|
+
return this.getColumnNameFromPropertyForEntity(propertyName, this.entity);
|
|
132
|
+
}
|
|
133
|
+
getColumnNameFromPropertyForEntity(propertyName, entity) {
|
|
134
|
+
if (entity.properties[propertyName]) {
|
|
135
|
+
return entity.properties[propertyName].options.columnName;
|
|
136
|
+
}
|
|
137
|
+
const relation = entity.relations?.find(rel => rel.propertyKey === propertyName);
|
|
138
|
+
if (relation) {
|
|
139
|
+
return relation.columnName;
|
|
140
|
+
}
|
|
141
|
+
return propertyName;
|
|
142
|
+
}
|
|
143
|
+
getEntityFromAlias(alias) {
|
|
144
|
+
if (alias === this.statements.alias) {
|
|
145
|
+
return this.entity;
|
|
146
|
+
}
|
|
147
|
+
const join = this.statements.join?.find(j => j.joinAlias === alias);
|
|
148
|
+
if (join?.joinEntity) {
|
|
149
|
+
const entity = this.entityStorage.get(join.joinEntity);
|
|
150
|
+
if (entity) {
|
|
151
|
+
return entity;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return this.entity;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.SqlColumnManager = SqlColumnManager;
|