@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,57 @@
|
|
|
1
|
+
import { SqlBuilder } from '../SqlBuilder';
|
|
2
|
+
import { FilterQuery, FindOneOption, FindOptions, ValueOrInstance } from '../driver/driver.interface';
|
|
3
|
+
export declare abstract class BaseEntity {
|
|
4
|
+
private _oldValues;
|
|
5
|
+
private _changedValues;
|
|
6
|
+
private $_isPersisted;
|
|
7
|
+
constructor();
|
|
8
|
+
/**
|
|
9
|
+
* Gets current entity's Repository.
|
|
10
|
+
*/
|
|
11
|
+
static createQueryBuilder<T>(this: {
|
|
12
|
+
new (): T;
|
|
13
|
+
} & typeof BaseEntity): SqlBuilder<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Gets current entity's Repository.
|
|
16
|
+
*/
|
|
17
|
+
private createQueryBuilder;
|
|
18
|
+
static find<T, Hint extends string = never>(this: {
|
|
19
|
+
new (): T;
|
|
20
|
+
} & typeof BaseEntity, where: FilterQuery<T>, options?: FindOptions<T, Hint>): Promise<T[]>;
|
|
21
|
+
static findOne<T, Hint extends string = never>(this: {
|
|
22
|
+
new (): T;
|
|
23
|
+
} & typeof BaseEntity, where: FilterQuery<T>, options?: FindOneOption<T, Hint>): Promise<T | undefined>;
|
|
24
|
+
/**
|
|
25
|
+
* Find a record in the database based on the provided query where and return it, or throw an error if not found.
|
|
26
|
+
*
|
|
27
|
+
* @param {FilterQuery<T>} where - The query where used to search for the record.
|
|
28
|
+
* @param options
|
|
29
|
+
* @return {Promise<T>} - A promise that resolves with the found record.
|
|
30
|
+
*/
|
|
31
|
+
static findOneOrFail<T, Hint extends string = never>(this: {
|
|
32
|
+
new (): T;
|
|
33
|
+
} & typeof BaseEntity, where: FilterQuery<T>, options?: FindOneOption<T, Hint>): Promise<T>;
|
|
34
|
+
static findAll<T extends object, Hint extends string = never>(this: {
|
|
35
|
+
new (): T;
|
|
36
|
+
} & typeof BaseEntity, options: FindOptions<T, Hint>): Promise<T[]>;
|
|
37
|
+
static create<T extends BaseEntity>(this: {
|
|
38
|
+
new (): T;
|
|
39
|
+
} & typeof BaseEntity, where: Partial<{
|
|
40
|
+
[K in keyof T]: ValueOrInstance<T[K]>;
|
|
41
|
+
}>): Promise<T>;
|
|
42
|
+
save(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Determines whether the current object has been persisted after the last modification.
|
|
45
|
+
*
|
|
46
|
+
* @return {boolean} Returns true if the object has been persisted, otherwise false.
|
|
47
|
+
*/
|
|
48
|
+
isPersisted(): boolean;
|
|
49
|
+
toJSON(): Record<string, any>;
|
|
50
|
+
private serializeWithEntity;
|
|
51
|
+
private serializeWithMetadata;
|
|
52
|
+
private shouldSkipProperty;
|
|
53
|
+
private shouldSkipPropertyBasic;
|
|
54
|
+
private isInternalProperty;
|
|
55
|
+
private getHiddenPropertiesFromMetadata;
|
|
56
|
+
private addComputedProperties;
|
|
57
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseEntity = void 0;
|
|
4
|
+
const SqlBuilder_1 = require("../SqlBuilder");
|
|
5
|
+
const entities_1 = require("./entities");
|
|
6
|
+
const core_1 = require("@carno.js/core");
|
|
7
|
+
const constants_1 = require("../constants");
|
|
8
|
+
class BaseEntity {
|
|
9
|
+
constructor() {
|
|
10
|
+
this._oldValues = {};
|
|
11
|
+
this._changedValues = {};
|
|
12
|
+
this.$_isPersisted = false;
|
|
13
|
+
return new Proxy(this, {
|
|
14
|
+
set(target, p, newValue) {
|
|
15
|
+
if (p.startsWith('$') || p.startsWith('_')) {
|
|
16
|
+
target[p] = newValue;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
// se oldvalue não existir, é porque é a primeira vez que o atributo está sendo setado
|
|
20
|
+
if (!(p in target._oldValues)) {
|
|
21
|
+
target._oldValues[p] = newValue;
|
|
22
|
+
}
|
|
23
|
+
// se o valor for diferente do valor antigo, é porque o valor foi alterado
|
|
24
|
+
if (target._oldValues[p] !== newValue) {
|
|
25
|
+
target._changedValues[p] = newValue;
|
|
26
|
+
this.$_isPersisted = false;
|
|
27
|
+
}
|
|
28
|
+
target[p] = newValue;
|
|
29
|
+
return true;
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Gets current entity's Repository.
|
|
35
|
+
*/
|
|
36
|
+
static createQueryBuilder() {
|
|
37
|
+
return new SqlBuilder_1.SqlBuilder(this);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Gets current entity's Repository.
|
|
41
|
+
*/
|
|
42
|
+
createQueryBuilder() {
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
return new SqlBuilder_1.SqlBuilder(this.constructor);
|
|
45
|
+
}
|
|
46
|
+
static async find(where, options) {
|
|
47
|
+
return this.createQueryBuilder()
|
|
48
|
+
.select(options?.fields)
|
|
49
|
+
.setStrategy(options?.loadStrategy)
|
|
50
|
+
.load(options?.load)
|
|
51
|
+
.where(where)
|
|
52
|
+
.limit(options?.limit)
|
|
53
|
+
.offset(options?.offset)
|
|
54
|
+
.orderBy(options?.orderBy)
|
|
55
|
+
.cache(options?.cache)
|
|
56
|
+
.executeAndReturnAll();
|
|
57
|
+
}
|
|
58
|
+
static async findOne(where, options) {
|
|
59
|
+
return this.createQueryBuilder()
|
|
60
|
+
.select(options?.fields)
|
|
61
|
+
.setStrategy(options?.loadStrategy)
|
|
62
|
+
.load(options?.load)
|
|
63
|
+
.where(where)
|
|
64
|
+
.cache(options?.cache)
|
|
65
|
+
.executeAndReturnFirst();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Find a record in the database based on the provided query where and return it, or throw an error if not found.
|
|
69
|
+
*
|
|
70
|
+
* @param {FilterQuery<T>} where - The query where used to search for the record.
|
|
71
|
+
* @param options
|
|
72
|
+
* @return {Promise<T>} - A promise that resolves with the found record.
|
|
73
|
+
*/
|
|
74
|
+
static async findOneOrFail(where, options) {
|
|
75
|
+
return this.createQueryBuilder()
|
|
76
|
+
// @ts-ignore
|
|
77
|
+
.select(options?.fields)
|
|
78
|
+
.setStrategy(options?.loadStrategy)
|
|
79
|
+
.load(options?.load)
|
|
80
|
+
.where(where)
|
|
81
|
+
.orderBy(options?.orderBy)
|
|
82
|
+
.cache(options?.cache)
|
|
83
|
+
.executeAndReturnFirstOrFail();
|
|
84
|
+
}
|
|
85
|
+
static async findAll(options) {
|
|
86
|
+
const builder = this.createQueryBuilder()
|
|
87
|
+
.select(options.fields)
|
|
88
|
+
.setStrategy(options?.loadStrategy)
|
|
89
|
+
.load(options?.load)
|
|
90
|
+
.offset(options?.offset)
|
|
91
|
+
.limit(options.limit)
|
|
92
|
+
.orderBy(options?.orderBy)
|
|
93
|
+
.cache(options?.cache);
|
|
94
|
+
return builder.executeAndReturnAll();
|
|
95
|
+
}
|
|
96
|
+
static async create(where) {
|
|
97
|
+
return this.createQueryBuilder()
|
|
98
|
+
.insert(where)
|
|
99
|
+
.executeAndReturnFirstOrFail();
|
|
100
|
+
}
|
|
101
|
+
async save() {
|
|
102
|
+
const qb = this.createQueryBuilder();
|
|
103
|
+
if (this.$_isPersisted) {
|
|
104
|
+
qb.update(this._changedValues);
|
|
105
|
+
qb.setInstance(this);
|
|
106
|
+
// @ts-ignore
|
|
107
|
+
qb.where({ id: this._oldValues.id });
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
qb.insert(this._oldValues);
|
|
111
|
+
}
|
|
112
|
+
await qb.execute();
|
|
113
|
+
qb.callHook('afterCreate', this);
|
|
114
|
+
qb.callHook('afterUpdate', this);
|
|
115
|
+
this._oldValues = {
|
|
116
|
+
...this._oldValues,
|
|
117
|
+
...this._changedValues,
|
|
118
|
+
};
|
|
119
|
+
this._changedValues = {};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Determines whether the current object has been persisted after the last modification.
|
|
123
|
+
*
|
|
124
|
+
* @return {boolean} Returns true if the object has been persisted, otherwise false.
|
|
125
|
+
*/
|
|
126
|
+
isPersisted() {
|
|
127
|
+
return this.$_isPersisted;
|
|
128
|
+
}
|
|
129
|
+
toJSON() {
|
|
130
|
+
const storage = entities_1.EntityStorage.getInstance();
|
|
131
|
+
const entity = storage.get(this.constructor);
|
|
132
|
+
const data = entity
|
|
133
|
+
? this.serializeWithEntity(entity)
|
|
134
|
+
: this.serializeWithMetadata();
|
|
135
|
+
this.addComputedProperties(data);
|
|
136
|
+
return data;
|
|
137
|
+
}
|
|
138
|
+
serializeWithEntity(entity) {
|
|
139
|
+
const data = {};
|
|
140
|
+
const allProperties = new Set(Object.keys(entity.properties));
|
|
141
|
+
const allRelations = new Set((entity.relations || []).map((relation) => relation.propertyKey));
|
|
142
|
+
const hidePropertiesSet = new Set(entity.hideProperties);
|
|
143
|
+
for (const key in this) {
|
|
144
|
+
if (this.shouldSkipProperty(key, allProperties, allRelations, hidePropertiesSet)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
data[key] = this[key];
|
|
148
|
+
}
|
|
149
|
+
return data;
|
|
150
|
+
}
|
|
151
|
+
serializeWithMetadata() {
|
|
152
|
+
const data = {};
|
|
153
|
+
const hideProperties = this.getHiddenPropertiesFromMetadata();
|
|
154
|
+
const hidePropertiesSet = new Set(hideProperties);
|
|
155
|
+
for (const key in this) {
|
|
156
|
+
if (this.shouldSkipPropertyBasic(key, hidePropertiesSet)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
data[key] = this[key];
|
|
160
|
+
}
|
|
161
|
+
return data;
|
|
162
|
+
}
|
|
163
|
+
shouldSkipProperty(key, allProperties, allRelations, hideProperties) {
|
|
164
|
+
if (this.isInternalProperty(key)) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
if (!allProperties.has(key) && !allRelations.has(key)) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return hideProperties.has(key);
|
|
171
|
+
}
|
|
172
|
+
shouldSkipPropertyBasic(key, hideProperties) {
|
|
173
|
+
if (this.isInternalProperty(key)) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return hideProperties.has(key);
|
|
177
|
+
}
|
|
178
|
+
isInternalProperty(key) {
|
|
179
|
+
return key.startsWith('$') || key.startsWith('_');
|
|
180
|
+
}
|
|
181
|
+
getHiddenPropertiesFromMetadata() {
|
|
182
|
+
const properties = core_1.Metadata.get(constants_1.PROPERTIES_METADATA, this.constructor) || {};
|
|
183
|
+
const hideProperties = [];
|
|
184
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
185
|
+
if (prop.options?.hidden) {
|
|
186
|
+
hideProperties.push(key);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return hideProperties;
|
|
190
|
+
}
|
|
191
|
+
addComputedProperties(data) {
|
|
192
|
+
const computedProperties = core_1.Metadata.get(constants_1.COMPUTED_PROPERTIES, this.constructor) || [];
|
|
193
|
+
for (const key of computedProperties) {
|
|
194
|
+
data[key] = this[key];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
exports.BaseEntity = BaseEntity;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class ArrayCollection<T extends object, O extends object> {
|
|
2
|
+
getItems(): T[];
|
|
3
|
+
}
|
|
4
|
+
export declare class Collection<T extends object, O extends object = object> extends ArrayCollection<T, O> {
|
|
5
|
+
constructor(owner: O, items?: T[], initialized?: boolean);
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Collection = exports.ArrayCollection = void 0;
|
|
4
|
+
class ArrayCollection {
|
|
5
|
+
getItems() {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.ArrayCollection = ArrayCollection;
|
|
10
|
+
class Collection extends ArrayCollection {
|
|
11
|
+
constructor(owner, items, initialized = true) {
|
|
12
|
+
super();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.Collection = Collection;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { PropertyOptions } from "../decorators/property.decorator";
|
|
2
|
+
import { Relationship, SnapshotIndexInfo, SnapshotTable, SnapshotUniqueInfo } from "../driver/driver.interface";
|
|
3
|
+
export type Property = {
|
|
4
|
+
options: PropertyOptions;
|
|
5
|
+
type: Function;
|
|
6
|
+
};
|
|
7
|
+
export type Options = {
|
|
8
|
+
properties: {
|
|
9
|
+
[key: string]: Property;
|
|
10
|
+
};
|
|
11
|
+
hideProperties: string[];
|
|
12
|
+
indexes?: SnapshotIndexInfo[];
|
|
13
|
+
uniques?: SnapshotUniqueInfo[];
|
|
14
|
+
relations: Relationship<any>[];
|
|
15
|
+
tableName: string;
|
|
16
|
+
hooks?: {
|
|
17
|
+
type: string;
|
|
18
|
+
propertyName: string;
|
|
19
|
+
}[];
|
|
20
|
+
schema?: string;
|
|
21
|
+
};
|
|
22
|
+
export declare class EntityStorage {
|
|
23
|
+
static instance: EntityStorage;
|
|
24
|
+
private entities;
|
|
25
|
+
constructor();
|
|
26
|
+
add(entity: {
|
|
27
|
+
target: Function;
|
|
28
|
+
options: any;
|
|
29
|
+
}, properties: {
|
|
30
|
+
[key: string]: Property;
|
|
31
|
+
}, relations: Relationship<any>[], hooks: {
|
|
32
|
+
type: string;
|
|
33
|
+
propertyName: string;
|
|
34
|
+
}[]): void;
|
|
35
|
+
get(entity: Function): Options;
|
|
36
|
+
entries(): MapIterator<[Function, Options]>;
|
|
37
|
+
static getInstance(): EntityStorage;
|
|
38
|
+
snapshot(values: Options): Promise<SnapshotTable>;
|
|
39
|
+
private snapshotColumns;
|
|
40
|
+
private snapshotIndexes;
|
|
41
|
+
private getFkType;
|
|
42
|
+
private getFkIncrement;
|
|
43
|
+
/**
|
|
44
|
+
* If fkKey is null, return the primary key of the entity
|
|
45
|
+
* @private
|
|
46
|
+
* @param relationShip
|
|
47
|
+
*/
|
|
48
|
+
private getFkKey;
|
|
49
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var EntityStorage_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.EntityStorage = void 0;
|
|
14
|
+
const core_1 = require("@carno.js/core");
|
|
15
|
+
const orm_session_context_1 = require("../orm-session-context");
|
|
16
|
+
const utils_1 = require("../utils");
|
|
17
|
+
const index_condition_builder_1 = require("../query/index-condition-builder");
|
|
18
|
+
function buildIndexColumnMap(properties, relations) {
|
|
19
|
+
const map = mapPropertyColumns(properties);
|
|
20
|
+
addRelationColumns(map, relations);
|
|
21
|
+
return map;
|
|
22
|
+
}
|
|
23
|
+
function mapPropertyColumns(properties) {
|
|
24
|
+
const map = {};
|
|
25
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
26
|
+
map[key] = value.options.columnName;
|
|
27
|
+
});
|
|
28
|
+
return map;
|
|
29
|
+
}
|
|
30
|
+
function addRelationColumns(map, relations) {
|
|
31
|
+
relations.forEach((relation) => {
|
|
32
|
+
map[String(relation.propertyKey)] = relation.columnName;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function mapIndexDefinitions(indexes, entityName, columnMap) {
|
|
36
|
+
return indexes.map((index) => toSnapshotIndex(index, entityName, columnMap));
|
|
37
|
+
}
|
|
38
|
+
function toSnapshotIndex(index, entityName, columnMap) {
|
|
39
|
+
const columns = resolveIndexColumns(index, columnMap);
|
|
40
|
+
const indexName = resolveIndexName(index.name, entityName, columns);
|
|
41
|
+
return {
|
|
42
|
+
table: entityName,
|
|
43
|
+
indexName,
|
|
44
|
+
columnName: columns.join(","),
|
|
45
|
+
where: resolveIndexWhere(index.where, columnMap),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function resolveIndexColumns(index, columnMap) {
|
|
49
|
+
return index.properties.map((propName) => resolveIndexColumn(propName, columnMap));
|
|
50
|
+
}
|
|
51
|
+
function resolveIndexColumn(propName, columnMap) {
|
|
52
|
+
const mapped = columnMap[propName];
|
|
53
|
+
if (mapped) {
|
|
54
|
+
return mapped;
|
|
55
|
+
}
|
|
56
|
+
return (0, utils_1.toSnakeCase)(propName);
|
|
57
|
+
}
|
|
58
|
+
function resolveIndexName(name, entityName, columns) {
|
|
59
|
+
if (name.includes('_pkey') || name.includes('[TABLE]')) {
|
|
60
|
+
return name.replace("[TABLE]", entityName);
|
|
61
|
+
}
|
|
62
|
+
return `${columns.join("_")}_index`;
|
|
63
|
+
}
|
|
64
|
+
function resolveIndexWhere(where, columnMap) {
|
|
65
|
+
if (!where) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
if (typeof where === "string") {
|
|
69
|
+
return where;
|
|
70
|
+
}
|
|
71
|
+
if (typeof where === "function") {
|
|
72
|
+
return where(columnMap);
|
|
73
|
+
}
|
|
74
|
+
return buildIndexWhere(where, columnMap);
|
|
75
|
+
}
|
|
76
|
+
function buildIndexWhere(where, columnMap) {
|
|
77
|
+
const builder = new index_condition_builder_1.IndexConditionBuilder(columnMap);
|
|
78
|
+
return builder.build(where);
|
|
79
|
+
}
|
|
80
|
+
function mapUniqueDefinitions(uniques, entityName, columnMap) {
|
|
81
|
+
return uniques.map((unique) => toSnapshotUnique(unique, entityName, columnMap));
|
|
82
|
+
}
|
|
83
|
+
function toSnapshotUnique(unique, entityName, columnMap) {
|
|
84
|
+
const columns = resolveUniqueColumns(unique, columnMap);
|
|
85
|
+
const uniqueName = resolveUniqueName(unique.name, entityName, columns);
|
|
86
|
+
return {
|
|
87
|
+
table: entityName,
|
|
88
|
+
uniqueName,
|
|
89
|
+
columnName: columns.join(","),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function resolveUniqueColumns(unique, columnMap) {
|
|
93
|
+
return unique.properties.map((propName) => resolveUniqueColumn(propName, columnMap));
|
|
94
|
+
}
|
|
95
|
+
function resolveUniqueColumn(propName, columnMap) {
|
|
96
|
+
const mapped = columnMap[propName];
|
|
97
|
+
if (mapped) {
|
|
98
|
+
return mapped;
|
|
99
|
+
}
|
|
100
|
+
return (0, utils_1.toSnakeCase)(propName);
|
|
101
|
+
}
|
|
102
|
+
function resolveUniqueName(name, entityName, columns) {
|
|
103
|
+
return `${columns.join("_")}_unique`;
|
|
104
|
+
}
|
|
105
|
+
let EntityStorage = EntityStorage_1 = class EntityStorage {
|
|
106
|
+
constructor() {
|
|
107
|
+
this.entities = new Map();
|
|
108
|
+
EntityStorage_1.instance = this;
|
|
109
|
+
}
|
|
110
|
+
add(entity, properties, relations, hooks) {
|
|
111
|
+
const entityName = entity.options?.tableName || (0, utils_1.toSnakeCase)(entity.target.name);
|
|
112
|
+
const indexes = core_1.Metadata.get("indexes", entity.target) || [];
|
|
113
|
+
const uniques = core_1.Metadata.get("uniques", entity.target) || [];
|
|
114
|
+
const columnMap = buildIndexColumnMap(properties, relations);
|
|
115
|
+
this.entities.set(entity.target, {
|
|
116
|
+
properties: properties,
|
|
117
|
+
hideProperties: Object.entries(properties)
|
|
118
|
+
.filter(([_key, value]) => value.options.hidden)
|
|
119
|
+
.map(([key]) => key),
|
|
120
|
+
relations,
|
|
121
|
+
indexes: mapIndexDefinitions(indexes, entityName, columnMap),
|
|
122
|
+
uniques: mapUniqueDefinitions(uniques, entityName, columnMap),
|
|
123
|
+
hooks,
|
|
124
|
+
tableName: entityName,
|
|
125
|
+
...entity.options,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
get(entity) {
|
|
129
|
+
return this.entities.get(entity);
|
|
130
|
+
}
|
|
131
|
+
entries() {
|
|
132
|
+
return this.entities.entries();
|
|
133
|
+
}
|
|
134
|
+
static getInstance() {
|
|
135
|
+
const scoped = orm_session_context_1.ormSessionContext.getStorage();
|
|
136
|
+
if (scoped) {
|
|
137
|
+
return scoped;
|
|
138
|
+
}
|
|
139
|
+
return EntityStorage_1.instance;
|
|
140
|
+
}
|
|
141
|
+
async snapshot(values) {
|
|
142
|
+
return {
|
|
143
|
+
tableName: values.tableName,
|
|
144
|
+
schema: values.schema || "public",
|
|
145
|
+
indexes: values.indexes || [],
|
|
146
|
+
uniques: values.uniques || [],
|
|
147
|
+
columns: this.snapshotColumns(values),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
snapshotColumns(values) {
|
|
151
|
+
let properties = Object.entries(values.properties).map(([_key, value]) => {
|
|
152
|
+
return {
|
|
153
|
+
name: value.options.columnName,
|
|
154
|
+
type: value.options.dbType ?? value.type.name,
|
|
155
|
+
nullable: value.options?.nullable,
|
|
156
|
+
default: value.options?.default,
|
|
157
|
+
autoIncrement: value.options?.autoIncrement,
|
|
158
|
+
primary: value.options?.isPrimary,
|
|
159
|
+
unique: value.options?.unique,
|
|
160
|
+
length: value.options?.length,
|
|
161
|
+
isEnum: value.options?.isEnum,
|
|
162
|
+
precision: value.options?.precision,
|
|
163
|
+
scale: value.options?.scale,
|
|
164
|
+
enumItems: value.options?.enumItems,
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
// @ts-ignore
|
|
168
|
+
let relations = values.relations &&
|
|
169
|
+
values.relations
|
|
170
|
+
.filter((relation) => relation.relation === 'many-to-one')
|
|
171
|
+
.map((relation) => {
|
|
172
|
+
const type = this.getFkType(relation);
|
|
173
|
+
return {
|
|
174
|
+
name: relation.columnName,
|
|
175
|
+
type,
|
|
176
|
+
nullable: relation.nullable,
|
|
177
|
+
unique: relation.unique,
|
|
178
|
+
length: relation.length || (0, utils_1.getDefaultLength)(type),
|
|
179
|
+
default: relation.default,
|
|
180
|
+
autoIncrement: relation.autoIncrement,
|
|
181
|
+
primary: relation.isPrimary,
|
|
182
|
+
precision: relation.precision,
|
|
183
|
+
scale: relation.scale,
|
|
184
|
+
foreignKeys: [
|
|
185
|
+
{
|
|
186
|
+
referencedColumnName: this.getFkKey(relation),
|
|
187
|
+
referencedTableName: this.get(relation.entity()).tableName,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
if (!relations) {
|
|
193
|
+
relations = [];
|
|
194
|
+
}
|
|
195
|
+
if (!properties) {
|
|
196
|
+
properties = [];
|
|
197
|
+
}
|
|
198
|
+
return [...properties, ...relations];
|
|
199
|
+
}
|
|
200
|
+
snapshotIndexes(values) {
|
|
201
|
+
return Object.entries(values.properties).map(([key, _value]) => {
|
|
202
|
+
return {
|
|
203
|
+
indexName: key,
|
|
204
|
+
columnName: key,
|
|
205
|
+
table: values.tableName,
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
getFkType(relation) {
|
|
210
|
+
const entity = this.get(relation.entity());
|
|
211
|
+
if (!entity) {
|
|
212
|
+
return "unknown";
|
|
213
|
+
}
|
|
214
|
+
const foreignKey = this.getFkKey(relation);
|
|
215
|
+
const property = entity.properties[foreignKey];
|
|
216
|
+
if (!property) {
|
|
217
|
+
return "unknown";
|
|
218
|
+
}
|
|
219
|
+
if (property.options?.dbType) {
|
|
220
|
+
return property.options.dbType;
|
|
221
|
+
}
|
|
222
|
+
return property.type?.name ?? "unknown";
|
|
223
|
+
}
|
|
224
|
+
getFkIncrement(relation) {
|
|
225
|
+
const entity = this.get(relation.entity());
|
|
226
|
+
if (!entity) {
|
|
227
|
+
return "unknown";
|
|
228
|
+
}
|
|
229
|
+
return entity.properties[this.getFkKey(relation)].options.autoIncrement;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* If fkKey is null, return the primary key of the entity
|
|
233
|
+
* @private
|
|
234
|
+
* @param relationShip
|
|
235
|
+
*/
|
|
236
|
+
getFkKey(relationShip) {
|
|
237
|
+
// se for nullable, deverá retornar o primary key da entidade target
|
|
238
|
+
if (typeof relationShip.fkKey === "undefined") {
|
|
239
|
+
const entity = this.entities.get(relationShip.entity());
|
|
240
|
+
const property = Object.entries(entity.properties).find(([_key, value]) => value.options.isPrimary === true);
|
|
241
|
+
if (!property) {
|
|
242
|
+
throw new Error(`Entity ${entity.tableName} does not have a primary key`);
|
|
243
|
+
}
|
|
244
|
+
return property[0];
|
|
245
|
+
}
|
|
246
|
+
// se o fkKey é uma função, ele retornará a propriedade da entidade que é a chave estrangeira
|
|
247
|
+
// precisamos pegar o nome dessa propriedade
|
|
248
|
+
if (typeof relationShip.fkKey === "string") {
|
|
249
|
+
return relationShip.fkKey;
|
|
250
|
+
}
|
|
251
|
+
const match = /\.(?<propriedade>[\w]+)/.exec(relationShip.fkKey.toString());
|
|
252
|
+
return match ? match.groups.propriedade : "";
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
exports.EntityStorage = EntityStorage;
|
|
256
|
+
exports.EntityStorage = EntityStorage = EntityStorage_1 = __decorate([
|
|
257
|
+
(0, core_1.Service)(),
|
|
258
|
+
__metadata("design:paramtypes", [])
|
|
259
|
+
], EntityStorage);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transparent wrapper type to prevent circular dependencies in TypeScript.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* // Instead of direct import causing circular dependency:
|
|
7
|
+
* import { User } from './User';
|
|
8
|
+
* class Post {
|
|
9
|
+
* @ManyToOne()
|
|
10
|
+
* author: User; // Circular dependency!
|
|
11
|
+
* }
|
|
12
|
+
*
|
|
13
|
+
* // Use Ref to break the cycle:
|
|
14
|
+
* import type { User } from './User';
|
|
15
|
+
* class Post {
|
|
16
|
+
* @ManyToOne()
|
|
17
|
+
* author: Ref<User>; // No circular dependency!
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export type Ref<T> = T;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a reference to an entity.
|
|
24
|
+
* This is an identity function - it returns the input unchanged.
|
|
25
|
+
* Useful for explicit ref creation when needed.
|
|
26
|
+
*
|
|
27
|
+
* @param entity - The entity to wrap in a reference
|
|
28
|
+
* @returns The same entity (identity function)
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const user = new User();
|
|
33
|
+
* const userRef = ref(user); // userRef === user (same reference)
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function ref<T>(entity: T): Ref<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Unwraps a reference to get the underlying entity.
|
|
39
|
+
* This is an identity function - it returns the input unchanged.
|
|
40
|
+
* Provided for API consistency and explicitness.
|
|
41
|
+
*
|
|
42
|
+
* @param reference - The reference to unwrap
|
|
43
|
+
* @returns The underlying entity (same as input)
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const post = await Post.findOne({ id: 1 });
|
|
48
|
+
* const author = unwrap(post.author); // author === post.author
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function unwrap<T>(reference: Ref<T>): T;
|
|
52
|
+
/**
|
|
53
|
+
* Type guard to check if a value is not null or undefined.
|
|
54
|
+
* Useful when working with optional references.
|
|
55
|
+
*
|
|
56
|
+
* @param value - The value to check
|
|
57
|
+
* @returns True if value is not null/undefined
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const post = await Post.findOne({ id: 1 });
|
|
62
|
+
* if (isLoaded(post.author)) {
|
|
63
|
+
* console.log(post.author.name); // TypeScript knows author is defined
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function isLoaded<T>(value: Ref<T> | null | undefined): value is Ref<T>;
|
|
68
|
+
/**
|
|
69
|
+
* @deprecated Use `Ref<T>` type instead. This class is kept for backward compatibility.
|
|
70
|
+
*/
|
|
71
|
+
export declare class Reference<T> {
|
|
72
|
+
private entity;
|
|
73
|
+
constructor(entity: T);
|
|
74
|
+
get(): T;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Creates a lightweight entity reference by class and id without hitting the DB.
|
|
78
|
+
* Useful to assign many-to-one relations when only the id is known.
|
|
79
|
+
*
|
|
80
|
+
* Example:
|
|
81
|
+
* const library = await UserLibrary.create({
|
|
82
|
+
* user: refById(User, userId),
|
|
83
|
+
* course: refById(Course, courseId),
|
|
84
|
+
* });
|
|
85
|
+
*/
|
|
86
|
+
export declare function refById<C extends new () => any, T = InstanceType<C>, PK = any>(Cls: C, id: PK): T;
|