@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.
@@ -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.getSelectRootPrismaSchema(entityInstance),
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 data;
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 = `${entity.name}-${propName}-${this.getIdOnEntity(new entityInstance(), item)}`;
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
- const enrichedItem = await this.loadRelationForEntity(this.getWhereByIdSchema(new entityInstance(), item), entityInstance, cache);
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 = `${entity.name}-${propName}-${this.getIdOnEntity(new relationEntity(), data[propName])}`;
358
+ const cacheKey = `${relationEntity.name}-${this.getIdOnEntity(new relationEntity(), data[propName])}`;
340
359
  if (cache.has(cacheKey)) {
341
- data[propName] = cache.get(cacheKey);
342
- return;
360
+ continue;
343
361
  }
344
362
  cache.set(cacheKey, data[propName]);
345
- data[propName] = await this.loadRelationForEntity(this.getWhereByIdSchema(relationEntity, data[propName]), relationEntity, cache);
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._modelName.prototype.constructor),
413
- where: this.getWhereByIdSchema(this._modelName.prototype.constructor, {
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 enrichedEntity = await this.enrichEntityWithRelations(this._modelName.prototype.constructor, { ...data });
420
- return this.createEntity(enrichedEntity);
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._modelName.prototype.constructor),
443
+ select: this.getSelectWithRelationsId(this.entityInstance),
425
444
  where,
426
445
  });
427
446
  if (!data)
428
447
  return null;
429
- const enrichedEntity = await this.enrichEntityWithRelations(this._modelName.prototype.constructor, { ...data });
430
- return this.createEntity(enrichedEntity);
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._modelName.prototype.constructor),
455
+ select: this.getSelectWithRelationsId(this.entityInstance),
435
456
  where,
436
457
  });
437
458
  if (!data)
438
459
  return null;
439
- const enrichedEntity = await this.enrichEntityWithRelations(this._modelName.prototype.constructor, { ...data });
440
- return this.createEntity(enrichedEntity);
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koalarx/nest",
3
- "version": "3.1.27",
3
+ "version": "3.1.29",
4
4
  "description": "",
5
5
  "author": "Igor D. Rangel",
6
6
  "license": "MIT",