@koalarx/nest 3.1.27 → 3.1.29
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/core/database/repository.base.d.ts +2 -0
- package/core/database/repository.base.js +49 -26
- package/core/utils/hydrate-entity-from-cache.d.ts +22 -0
- package/core/utils/hydrate-entity-from-cache.js +76 -0
- package/core/utils/is-plain-object.d.ts +1 -0
- package/core/utils/is-plain-object.js +8 -0
- package/core/utils/proxy.d.ts +1 -0
- package/core/utils/proxy.js +27 -0
- package/package.json +1 -1
- package/tsconfig.lib.tsbuildinfo +1 -1
|
@@ -14,6 +14,7 @@ export declare abstract class RepositoryBase<TEntity extends EntityBase<TEntity>
|
|
|
14
14
|
private readonly _modelName;
|
|
15
15
|
private readonly _includeFindMany?;
|
|
16
16
|
constructor({ context, modelName }: RepositoryInitProps<TEntity, TContext>);
|
|
17
|
+
private get entityInstance();
|
|
17
18
|
private getIdPropName;
|
|
18
19
|
private getWhereByIdSchema;
|
|
19
20
|
private getConnectPrismaSchemaForRelation;
|
|
@@ -30,6 +31,7 @@ export declare abstract class RepositoryBase<TEntity extends EntityBase<TEntity>
|
|
|
30
31
|
private orphanRemoval;
|
|
31
32
|
private getIdOnEntity;
|
|
32
33
|
private loadRelationForEntity;
|
|
34
|
+
private loadEntityFromCache;
|
|
33
35
|
private enrichEntityWithRelations;
|
|
34
36
|
private persistRelations;
|
|
35
37
|
protected context(transactionalClient?: TContext): TContext[TModelKey];
|
|
@@ -6,7 +6,9 @@ const pagination_dto_1 = require("../dtos/pagination.dto");
|
|
|
6
6
|
const koala_global_vars_1 = require("../koala-global-vars");
|
|
7
7
|
const auto_mapping_list_1 = require("../mapping/auto-mapping-list");
|
|
8
8
|
const generate_prisma_include_schema_1 = require("../utils/generate-prisma-include-schema");
|
|
9
|
+
const hydrate_entity_from_cache_1 = require("../utils/hydrate-entity-from-cache");
|
|
9
10
|
const list_1 = require("../utils/list");
|
|
11
|
+
const proxy_1 = require("../utils/proxy");
|
|
10
12
|
const entity_base_1 = require("./entity.base");
|
|
11
13
|
class RepositoryBase {
|
|
12
14
|
_context;
|
|
@@ -21,6 +23,9 @@ class RepositoryBase {
|
|
|
21
23
|
entity: this._modelName,
|
|
22
24
|
});
|
|
23
25
|
}
|
|
26
|
+
get entityInstance() {
|
|
27
|
+
return this._modelName.prototype.constructor;
|
|
28
|
+
}
|
|
24
29
|
getIdPropName(entity) {
|
|
25
30
|
const idConfig = Reflect.getMetadata('entity:id', entity ? entity.constructor.prototype : this._modelName.prototype);
|
|
26
31
|
if (idConfig) {
|
|
@@ -76,7 +81,7 @@ class RepositoryBase {
|
|
|
76
81
|
const list = new entity()[prop.name];
|
|
77
82
|
const entityInstance = list.entityType;
|
|
78
83
|
selectSchema[prop.name] = {
|
|
79
|
-
select: this.
|
|
84
|
+
select: this.getWhereByIdSchema(new entityInstance(), true),
|
|
80
85
|
};
|
|
81
86
|
}
|
|
82
87
|
else {
|
|
@@ -307,11 +312,30 @@ class RepositoryBase {
|
|
|
307
312
|
select: this.getSelectRootPrismaSchema(entity),
|
|
308
313
|
where,
|
|
309
314
|
})
|
|
310
|
-
.then((data) => this.enrichEntityWithRelations(entity, data, cache));
|
|
315
|
+
.then((data) => this.enrichEntityWithRelations(entity, (0, proxy_1.createProxy)(data), cache));
|
|
316
|
+
}
|
|
317
|
+
loadEntityFromCache(entity, data, cache) {
|
|
318
|
+
return (0, hydrate_entity_from_cache_1.hydrateEntityFromCache)({
|
|
319
|
+
entity: entity,
|
|
320
|
+
data,
|
|
321
|
+
cache,
|
|
322
|
+
dependencies: {
|
|
323
|
+
getAllProps: (sourceEntity) => auto_mapping_list_1.AutoMappingList.getAllProps(sourceEntity),
|
|
324
|
+
getPropDefinitions: (sourceEntity, propName) => auto_mapping_list_1.AutoMappingList.getPropDefinitions(sourceEntity, propName),
|
|
325
|
+
getSourceByName: (sourceName) => auto_mapping_list_1.AutoMappingList.getSourceByName(sourceName),
|
|
326
|
+
getListEntityType: (sourceEntity, propName) => {
|
|
327
|
+
const list = new sourceEntity()[propName];
|
|
328
|
+
return list.entityType;
|
|
329
|
+
},
|
|
330
|
+
getIdOnEntity: (currentEntity, value) => this.getIdOnEntity(currentEntity, value),
|
|
331
|
+
createEntity: (sourceEntity) => new sourceEntity(),
|
|
332
|
+
},
|
|
333
|
+
});
|
|
311
334
|
}
|
|
312
335
|
async enrichEntityWithRelations(entity, data, cache = new Map()) {
|
|
313
|
-
if (!data)
|
|
314
|
-
return
|
|
336
|
+
if (!data) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
315
339
|
const allProps = auto_mapping_list_1.AutoMappingList.getAllProps(entity);
|
|
316
340
|
for (const prop of allProps) {
|
|
317
341
|
const propName = prop.name;
|
|
@@ -319,31 +343,24 @@ class RepositoryBase {
|
|
|
319
343
|
if (propDef?.type === list_1.List.name) {
|
|
320
344
|
const list = new entity()[prop.name];
|
|
321
345
|
const entityInstance = list.entityType;
|
|
322
|
-
const items = [];
|
|
323
346
|
for (const item of data[propName] || []) {
|
|
324
|
-
const cacheKey = `${
|
|
347
|
+
const cacheKey = `${entityInstance.name}-${this.getIdOnEntity(new entityInstance(), item)}`;
|
|
325
348
|
if (cache.has(cacheKey)) {
|
|
326
|
-
items.push(cache.get(cacheKey));
|
|
327
349
|
continue;
|
|
328
350
|
}
|
|
329
351
|
cache.set(cacheKey, item);
|
|
330
|
-
|
|
331
|
-
cache.set(cacheKey, enrichedItem);
|
|
332
|
-
items.push(enrichedItem);
|
|
352
|
+
await this.loadRelationForEntity(this.getWhereByIdSchema(new entityInstance(), item), entityInstance, cache).then((response) => cache.set(cacheKey, response));
|
|
333
353
|
}
|
|
334
|
-
data[propName] = items;
|
|
335
354
|
continue;
|
|
336
355
|
}
|
|
337
356
|
const relationEntity = auto_mapping_list_1.AutoMappingList.getSourceByName(propDef?.type ?? '');
|
|
338
357
|
if (relationEntity && data[propName]) {
|
|
339
|
-
const cacheKey = `${
|
|
358
|
+
const cacheKey = `${relationEntity.name}-${this.getIdOnEntity(new relationEntity(), data[propName])}`;
|
|
340
359
|
if (cache.has(cacheKey)) {
|
|
341
|
-
|
|
342
|
-
return;
|
|
360
|
+
continue;
|
|
343
361
|
}
|
|
344
362
|
cache.set(cacheKey, data[propName]);
|
|
345
|
-
|
|
346
|
-
cache.set(cacheKey, data[propName]);
|
|
363
|
+
await this.loadRelationForEntity(this.getWhereByIdSchema(new relationEntity(), data[propName]), relationEntity, cache).then((response) => cache.set(cacheKey, response));
|
|
347
364
|
}
|
|
348
365
|
}
|
|
349
366
|
return data;
|
|
@@ -409,35 +426,41 @@ class RepositoryBase {
|
|
|
409
426
|
}
|
|
410
427
|
async findById(id) {
|
|
411
428
|
const data = await this.context().findFirst({
|
|
412
|
-
select: this.getSelectWithRelationsId(this.
|
|
413
|
-
where: this.getWhereByIdSchema(this.
|
|
429
|
+
select: this.getSelectWithRelationsId(this.entityInstance),
|
|
430
|
+
where: this.getWhereByIdSchema(this.entityInstance, {
|
|
414
431
|
id,
|
|
415
432
|
}),
|
|
416
433
|
});
|
|
417
434
|
if (!data)
|
|
418
435
|
return null;
|
|
419
|
-
const
|
|
420
|
-
return this.
|
|
436
|
+
const cache = new Map();
|
|
437
|
+
return this.enrichEntityWithRelations(this.entityInstance, (0, proxy_1.createProxy)(data), cache)
|
|
438
|
+
.then((response) => this.loadEntityFromCache(this.entityInstance, response, cache))
|
|
439
|
+
.then((response) => this.createEntity(response));
|
|
421
440
|
}
|
|
422
441
|
async findFirst(where) {
|
|
423
442
|
const data = await this.context().findFirst({
|
|
424
|
-
select: this.getSelectWithRelationsId(this.
|
|
443
|
+
select: this.getSelectWithRelationsId(this.entityInstance),
|
|
425
444
|
where,
|
|
426
445
|
});
|
|
427
446
|
if (!data)
|
|
428
447
|
return null;
|
|
429
|
-
const
|
|
430
|
-
return this.
|
|
448
|
+
const cache = new Map();
|
|
449
|
+
return this.enrichEntityWithRelations(this.entityInstance, (0, proxy_1.createProxy)(data), cache)
|
|
450
|
+
.then((response) => this.loadEntityFromCache(this.entityInstance, response, cache))
|
|
451
|
+
.then((response) => this.createEntity(response));
|
|
431
452
|
}
|
|
432
453
|
async findUnique(where) {
|
|
433
454
|
const data = await this.context().findUnique({
|
|
434
|
-
select: this.getSelectWithRelationsId(this.
|
|
455
|
+
select: this.getSelectWithRelationsId(this.entityInstance),
|
|
435
456
|
where,
|
|
436
457
|
});
|
|
437
458
|
if (!data)
|
|
438
459
|
return null;
|
|
439
|
-
const
|
|
440
|
-
return this.
|
|
460
|
+
const cache = new Map();
|
|
461
|
+
return this.enrichEntityWithRelations(this.entityInstance, (0, proxy_1.createProxy)(data), cache)
|
|
462
|
+
.then((response) => this.loadEntityFromCache(this.entityInstance, response, cache))
|
|
463
|
+
.then((response) => this.createEntity(response));
|
|
441
464
|
}
|
|
442
465
|
async findMany(where, pagination) {
|
|
443
466
|
return this.context()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Type } from '@nestjs/common';
|
|
2
|
+
export type GraphVisitState = 'loading' | 'loaded';
|
|
3
|
+
export interface HydrateEntityFromCacheDependencies {
|
|
4
|
+
getAllProps(entity: Type<any>): Array<{
|
|
5
|
+
name: string;
|
|
6
|
+
}>;
|
|
7
|
+
getPropDefinitions(entity: Type<any>, propName: string): {
|
|
8
|
+
type?: string;
|
|
9
|
+
} | undefined;
|
|
10
|
+
getSourceByName(sourceName: string): Type<any> | undefined;
|
|
11
|
+
getListEntityType(entity: Type<any>, propName: string): Type<any> | undefined;
|
|
12
|
+
getIdOnEntity(entity: any, data: any): string;
|
|
13
|
+
createEntity(entity: Type<any>): any;
|
|
14
|
+
}
|
|
15
|
+
export interface HydrateEntityFromCacheParams {
|
|
16
|
+
entity: Type<any>;
|
|
17
|
+
data: any;
|
|
18
|
+
cache: Map<string, any>;
|
|
19
|
+
dependencies: HydrateEntityFromCacheDependencies;
|
|
20
|
+
visitState?: Map<string, GraphVisitState>;
|
|
21
|
+
}
|
|
22
|
+
export declare function hydrateEntityFromCache({ entity, data, cache, dependencies, visitState, }: HydrateEntityFromCacheParams): any;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hydrateEntityFromCache = hydrateEntityFromCache;
|
|
4
|
+
function mergeScalarProps(target, source) {
|
|
5
|
+
Object.keys(source || {}).forEach((key) => {
|
|
6
|
+
const value = source[key];
|
|
7
|
+
if (value === undefined) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (value !== null && typeof value === 'object') {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
target[key] = value;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function getCacheKey(entity, data, dependencies) {
|
|
20
|
+
const entityId = dependencies.getIdOnEntity(dependencies.createEntity(entity), data);
|
|
21
|
+
return `${entity.name}-${entityId}`;
|
|
22
|
+
}
|
|
23
|
+
function hydrateEntityFromCache({ entity, data, cache, dependencies, visitState = new Map(), }) {
|
|
24
|
+
if (!data) {
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
const cacheKey = getCacheKey(entity, data, dependencies);
|
|
28
|
+
const sourceData = cache.get(cacheKey) ?? data;
|
|
29
|
+
let canonicalEntity = cache.get(cacheKey);
|
|
30
|
+
if (!canonicalEntity) {
|
|
31
|
+
canonicalEntity = sourceData;
|
|
32
|
+
cache.set(cacheKey, canonicalEntity);
|
|
33
|
+
}
|
|
34
|
+
mergeScalarProps(canonicalEntity, sourceData);
|
|
35
|
+
if (visitState.get(cacheKey) === 'loading') {
|
|
36
|
+
return canonicalEntity;
|
|
37
|
+
}
|
|
38
|
+
visitState.set(cacheKey, 'loading');
|
|
39
|
+
const allProps = dependencies.getAllProps(entity);
|
|
40
|
+
for (const prop of allProps) {
|
|
41
|
+
const propName = prop.name;
|
|
42
|
+
const propDefinition = dependencies.getPropDefinitions(entity, propName);
|
|
43
|
+
const sourcePropValue = sourceData[propName] ?? canonicalEntity[propName];
|
|
44
|
+
if (Array.isArray(sourcePropValue)) {
|
|
45
|
+
const listEntity = dependencies.getListEntityType(entity, propName);
|
|
46
|
+
if (!listEntity) {
|
|
47
|
+
canonicalEntity[propName] = sourcePropValue;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
canonicalEntity[propName] = sourcePropValue.map((item) => hydrateEntityFromCache({
|
|
51
|
+
entity: listEntity,
|
|
52
|
+
data: item,
|
|
53
|
+
cache,
|
|
54
|
+
dependencies,
|
|
55
|
+
visitState,
|
|
56
|
+
}));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const relationEntity = dependencies.getSourceByName(propDefinition?.type ?? '');
|
|
60
|
+
if (relationEntity && sourcePropValue) {
|
|
61
|
+
canonicalEntity[propName] = hydrateEntityFromCache({
|
|
62
|
+
entity: relationEntity,
|
|
63
|
+
data: sourcePropValue,
|
|
64
|
+
cache,
|
|
65
|
+
dependencies,
|
|
66
|
+
visitState,
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (sourcePropValue !== undefined) {
|
|
71
|
+
canonicalEntity[propName] = sourcePropValue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
visitState.set(cacheKey, 'loaded');
|
|
75
|
+
return canonicalEntity;
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isPlainObject(val: any): boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPlainObject = isPlainObject;
|
|
4
|
+
function isPlainObject(val) {
|
|
5
|
+
if (typeof val !== 'object' || val === null)
|
|
6
|
+
return false;
|
|
7
|
+
return Object.prototype.toString.call(val) === '[object Object]';
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createProxy(target: any): any;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createProxy = createProxy;
|
|
4
|
+
const is_plain_object_1 = require("./is-plain-object");
|
|
5
|
+
const handle = {
|
|
6
|
+
get(target, prop) {
|
|
7
|
+
if (prop === '__isProxy')
|
|
8
|
+
return true;
|
|
9
|
+
return target[prop];
|
|
10
|
+
},
|
|
11
|
+
set(target, prop, value) {
|
|
12
|
+
target[prop] = value;
|
|
13
|
+
return true;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
function createProxy(target) {
|
|
17
|
+
const proxy = new Proxy(target, handle);
|
|
18
|
+
Object.keys(proxy).forEach((key) => {
|
|
19
|
+
if ((0, is_plain_object_1.isPlainObject)(proxy[key])) {
|
|
20
|
+
proxy[key] = new Proxy(proxy[key], handle);
|
|
21
|
+
}
|
|
22
|
+
else if (Array.isArray(proxy[key])) {
|
|
23
|
+
proxy[key] = proxy[key].map((item) => (0, is_plain_object_1.isPlainObject)(item) ? new Proxy(item, handle) : item);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return proxy;
|
|
27
|
+
}
|