Skip to content

Commit 23b2f80

Browse files
committed
enhance: Hoist getEntityCaches computation to MemoCache init
1 parent 44ed77a commit 23b2f80

File tree

11 files changed

+105
-87
lines changed

11 files changed

+105
-87
lines changed

packages/normalizr/src/denormalize/localCache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default class LocalCache implements Cache {
1515
if (!this.localCache.has(key)) {
1616
this.localCache.set(key, new Map<string, any>());
1717
}
18-
const localCacheKey = this.localCache.get(key) as Map<string, any>;
18+
const localCacheKey = this.localCache.get(key)!;
1919

2020
if (!localCacheKey.get(pk)) {
2121
computeValue(localCacheKey);

packages/normalizr/src/memo/MemoCache.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import WeakDependencyMap from './WeakDependencyMap.js';
33
import buildQueryKey from '../buildQueryKey.js';
44
import { getDependency, type BaseDelegate } from './BaseDelegate.js';
55
import { PlainDelegate } from './Delegate.js';
6+
import { GetEntityCache, getEntityCaches } from './entitiesCache.js';
67
import { getEntities } from '../denormalize/getEntities.js';
78
import getUnvisit from '../denormalize/unvisit.js';
89
import type {
@@ -12,7 +13,7 @@ import type {
1213
Schema,
1314
} from '../interface.js';
1415
import type { DenormalizeNullable, NormalizeNullable } from '../types.js';
15-
import { EndpointsCache, EntityCache } from './types.js';
16+
import { EndpointsCache } from './types.js';
1617
import type { INVALID } from '../denormalize/symbol.js';
1718

1819
type DelegateClass = new (v: { entities: any; indexes: any }) => BaseDelegate;
@@ -22,15 +23,17 @@ type DelegateClass = new (v: { entities: any; indexes: any }) => BaseDelegate;
2223
/** Singleton to store the memoization cache for denormalization methods */
2324
export default class MemoCache {
2425
/** Cache for every entity based on its dependencies and its own input */
25-
protected entities: EntityCache = new Map();
26+
declare protected _getCache: GetEntityCache;
2627
/** Caches the final denormalized form based on input, entities */
2728
protected endpoints: EndpointsCache = new WeakDependencyMap<EntityPath>();
2829
/** Caches the queryKey based on schema, args, and any used entities or indexes */
2930
protected queryKeys: Map<string, WeakDependencyMap<QueryPath>> = new Map();
31+
3032
declare protected Delegate: DelegateClass;
3133

3234
constructor(D: DelegateClass = PlainDelegate) {
3335
this.Delegate = D;
36+
this._getCache = getEntityCaches(new Map());
3437
}
3538

3639
/** Compute denormalized form maintaining referential equality for same inputs */
@@ -58,7 +61,7 @@ export default class MemoCache {
5861

5962
return getUnvisit(
6063
getEntity,
61-
new GlobalCache(getEntity, this.entities, this.endpoints),
64+
new GlobalCache(getEntity, this._getCache, this.endpoints),
6265
args,
6366
)(schema, input);
6467
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { EntityInterface, EntityPath } from '../interface.js';
2+
import { EntityCache } from './types.js';
3+
import WeakDependencyMap from './WeakDependencyMap.js';
4+
5+
export type GetEntityCache = (
6+
pk: string,
7+
schema: EntityInterface,
8+
) => WeakDependencyMap<EntityPath, object, any>;
9+
10+
export const getEntityCaches = (entityCache: EntityCache): GetEntityCache => {
11+
return (pk: string, schema: EntityInterface) => {
12+
const key = schema.key;
13+
// collections should use the entities they collect over
14+
// TODO: this should be based on a public interface
15+
const entityInstance: EntityInterface = (schema.cacheWith as any) ?? schema;
16+
17+
if (!entityCache.has(key)) {
18+
entityCache.set(key, new Map());
19+
}
20+
const entityCacheKey = entityCache.get(key)!;
21+
22+
if (!entityCacheKey.has(pk)) entityCacheKey.set(pk, new WeakMap());
23+
const entityCachePk = entityCacheKey.get(pk)!;
24+
25+
let wem = entityCachePk.get(entityInstance);
26+
if (!wem) {
27+
wem = new WeakDependencyMap<EntityPath, object, any>();
28+
entityCachePk.set(entityInstance, wem);
29+
}
30+
31+
return wem;
32+
};
33+
};

packages/normalizr/src/memo/globalCache.ts

Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
1-
import { EndpointsCache, EntityCache } from './types.js';
1+
import { EndpointsCache } from './types.js';
22
import WeakDependencyMap, { type Dep } from './WeakDependencyMap.js';
33
import type Cache from '../denormalize/cache.js';
44
import type { GetEntity } from '../denormalize/getEntities.js';
55
import type { INVALID } from '../denormalize/symbol.js';
66
import type { EntityInterface, EntityPath } from '../interface.js';
7+
import type { GetEntityCache } from './entitiesCache.js';
78

89
export default class GlobalCache implements Cache {
910
private dependencies: Dep<EntityPath>[] = [];
1011
private cycleCache: Map<string, Map<string, number>> = new Map();
1112
private cycleIndex = -1;
1213
private localCache: Map<string, Map<string, any>> = new Map();
1314

14-
declare private getCache: (
15-
pk: string,
16-
schema: EntityInterface,
17-
) => WeakDependencyMap<EntityPath, object, any>;
15+
declare private _getCache: GetEntityCache;
1816

1917
declare private _getEntity: GetEntity;
20-
declare private resultCache: EndpointsCache;
18+
declare private _resultCache: EndpointsCache;
2119

2220
constructor(
2321
getEntity: GetEntity,
24-
entityCache: EntityCache,
22+
getCache: GetEntityCache,
2523
resultCache: EndpointsCache,
2624
) {
2725
this._getEntity = getEntity;
28-
this.getCache = getEntityCaches(entityCache);
29-
this.resultCache = resultCache;
26+
this._getCache = getCache;
27+
this._resultCache = resultCache;
3028
}
3129

3230
getEntity(
@@ -43,7 +41,7 @@ export default class GlobalCache implements Cache {
4341
EntityPath,
4442
object,
4543
EntityCacheValue
46-
> = this.getCache(pk, schema);
44+
> = this._getCache(pk, schema);
4745
const [cacheValue, cachePath] = globalCache.get(entity, this._getEntity);
4846
// TODO: what if this just returned the deps - then we don't need to store them
4947

@@ -118,15 +116,15 @@ export default class GlobalCache implements Cache {
118116
return { data: computeValue(), paths: this.paths() };
119117
}
120118

121-
let [data, paths] = this.resultCache.get(input, this._getEntity);
119+
let [data, paths] = this._resultCache.get(input, this._getEntity);
122120

123121
if (paths === undefined) {
124122
data = computeValue();
125123
// we want to do this before we add our 'input' entry
126124
paths = this.paths();
127125
// for the first entry, `path` is ignored so empty members is fine
128126
this.dependencies.unshift({ path: { key: '', pk: '' }, entity: input });
129-
this.resultCache.set(this.dependencies, data);
127+
this._resultCache.set(this.dependencies, data);
130128
} else {
131129
paths.shift();
132130
}
@@ -142,41 +140,3 @@ interface EntityCacheValue {
142140
dependencies: Dep<EntityPath>[];
143141
value: object | typeof INVALID | undefined;
144142
}
145-
146-
const getEntityCaches = (entityCache: EntityCache) => {
147-
return (pk: string, schema: EntityInterface) => {
148-
const key = schema.key;
149-
// collections should use the entities they collect over
150-
// TODO: this should be based on a public interface
151-
const entityInstance: EntityInterface = (schema.cacheWith as any) ?? schema;
152-
153-
if (!entityCache.has(key)) {
154-
entityCache.set(key, new Map());
155-
}
156-
const entityCacheKey = entityCache.get(key)!;
157-
if (!entityCacheKey.get(pk))
158-
entityCacheKey.set(
159-
pk,
160-
new WeakMap<
161-
EntityInterface,
162-
WeakDependencyMap<EntityPath, object, any>
163-
>(),
164-
);
165-
166-
const entityCachePk = entityCacheKey.get(pk) as WeakMap<
167-
EntityInterface,
168-
WeakDependencyMap<EntityPath, object, any>
169-
>;
170-
let wem = entityCachePk.get(entityInstance) as WeakDependencyMap<
171-
EntityPath,
172-
object,
173-
any
174-
>;
175-
if (!wem) {
176-
wem = new WeakDependencyMap<EntityPath, object, any>();
177-
entityCachePk.set(entityInstance, wem);
178-
}
179-
180-
return wem;
181-
};
182-
};

packages/normalizr/src/normalize/getCheckLoop.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@ export function getCheckLoop() {
55
if (!visitedEntities.has(entityKey)) {
66
visitedEntities.set(entityKey, new Map<string, object[]>());
77
}
8-
// we have to tell typescript this can't be undefined (due to line above)
9-
const entitiesOneType: Map<string, object[]> = visitedEntities.get(
10-
entityKey,
11-
) as Map<string, object[]>;
8+
const entitiesOneType = visitedEntities.get(entityKey)!;
129

1310
if (!entitiesOneType.has(pk)) {
1411
entitiesOneType.set(pk, []);
1512
}
16-
const visitedEntityList = entitiesOneType.get(pk) as object[];
13+
const visitedEntityList = entitiesOneType.get(pk)!;
14+
1715
if (visitedEntityList.some((entity: any) => entity === input)) {
1816
return true;
1917
}

website/src/components/Playground/editor-types/@data-client/core.d.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,23 @@ interface NormalizedIndex {
4949
};
5050
};
5151
}
52-
type EntityPath = [key: string, pk: string];
52+
/** Used in denormalize. Lookup to find an entity in the store table */
53+
interface EntityPath {
54+
key: string;
55+
pk: string;
56+
}
5357
type IndexPath = [key: string, index: string, value: string];
5458
type EntitiesPath = [key: string];
55-
type QueryPath = IndexPath | EntityPath | EntitiesPath;
59+
type QueryPath = IndexPath | [key: string, pk: string] | EntitiesPath;
5660
/** Get all normalized entities of one type from store */
5761
interface GetEntities {
58-
(...path: EntitiesPath): {
62+
(key: string): {
5963
readonly [pk: string]: any;
6064
} | undefined;
6165
}
6266
/** Get normalized Entity from store */
6367
interface GetEntity {
64-
(...path: EntityPath): any;
68+
(key: string, pk: string): any;
6569
}
6670
/** Get PK using an Entity Index */
6771
interface GetIndex {
@@ -226,24 +230,24 @@ declare abstract class BaseDelegate {
226230
indexes: any;
227231
});
228232
abstract getEntities(...path: EntitiesPath): object | undefined;
229-
abstract getEntity(...path: EntityPath): object | undefined;
233+
abstract getEntity(key: string, pk: string): object | undefined;
230234
abstract getIndex(...path: IndexPath): object | undefined;
231235
abstract getIndexEnd(entity: any, value: string): string | undefined;
232236
tracked(schema: any): [delegate: IQueryDelegate, dependencies: Dep<QueryPath>[]];
233237
}
234238

235-
interface EntityCache extends Map<string, Map<string, WeakMap<EntityInterface, WeakDependencyMap<EntityPath, object, any>>>> {
236-
}
237239
type EndpointsCache = WeakDependencyMap<EntityPath, object, any>;
238240

241+
type GetEntityCache = (pk: string, schema: EntityInterface) => WeakDependencyMap<EntityPath, object, any>;
242+
239243
type DelegateClass = new (v: {
240244
entities: any;
241245
indexes: any;
242246
}) => BaseDelegate;
243247
/** Singleton to store the memoization cache for denormalization methods */
244248
declare class MemoCache {
245249
/** Cache for every entity based on its dependencies and its own input */
246-
protected entities: EntityCache;
250+
protected _getCache: GetEntityCache;
247251
/** Caches the final denormalized form based on input, entities */
248252
protected endpoints: EndpointsCache;
249253
/** Caches the queryKey based on schema, args, and any used entities or indexes */

website/src/components/Playground/editor-types/@data-client/endpoint.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,11 @@ interface Visit {
253253
(schema: any, value: any, parent: any, key: any, args: readonly any[]): any;
254254
creating?: boolean;
255255
}
256-
type EntityPath = [key: string, pk: string];
256+
/** Used in denormalize. Lookup to find an entity in the store table */
257+
interface EntityPath {
258+
key: string;
259+
pk: string;
260+
}
257261
type IndexPath = [key: string, index: string, value: string];
258262
type EntitiesPath = [key: string];
259263
/** Returns true if a circular reference is found */
@@ -262,13 +266,13 @@ interface CheckLoop {
262266
}
263267
/** Get all normalized entities of one type from store */
264268
interface GetEntities {
265-
(...path: EntitiesPath): {
269+
(key: string): {
266270
readonly [pk: string]: any;
267271
} | undefined;
268272
}
269273
/** Get normalized Entity from store */
270274
interface GetEntity {
271-
(...path: EntityPath): any;
275+
(key: string, pk: string): any;
272276
}
273277
/** Get PK using an Entity Index */
274278
interface GetIndex {

website/src/components/Playground/editor-types/@data-client/graphql.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,11 @@ interface Visit {
253253
(schema: any, value: any, parent: any, key: any, args: readonly any[]): any;
254254
creating?: boolean;
255255
}
256-
type EntityPath = [key: string, pk: string];
256+
/** Used in denormalize. Lookup to find an entity in the store table */
257+
interface EntityPath {
258+
key: string;
259+
pk: string;
260+
}
257261
type IndexPath = [key: string, index: string, value: string];
258262
type EntitiesPath = [key: string];
259263
/** Returns true if a circular reference is found */
@@ -262,13 +266,13 @@ interface CheckLoop {
262266
}
263267
/** Get all normalized entities of one type from store */
264268
interface GetEntities {
265-
(...path: EntitiesPath): {
269+
(key: string): {
266270
readonly [pk: string]: any;
267271
} | undefined;
268272
}
269273
/** Get normalized Entity from store */
270274
interface GetEntity {
271-
(...path: EntityPath): any;
275+
(key: string, pk: string): any;
272276
}
273277
/** Get PK using an Entity Index */
274278
interface GetIndex {

website/src/components/Playground/editor-types/@data-client/normalizr.d.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,27 @@ interface EntityTable {
5858
interface Visit {
5959
(schema: any, value: any, parent: any, key: any, args: readonly any[]): any;
6060
}
61-
type EntityPath = [key: string, pk: string];
61+
/** Used in denormalize. Lookup to find an entity in the store table */
62+
interface EntityPath {
63+
key: string;
64+
pk: string;
65+
}
6266
type IndexPath = [key: string, index: string, value: string];
6367
type EntitiesPath = [key: string];
64-
type QueryPath = IndexPath | EntityPath | EntitiesPath;
68+
type QueryPath = IndexPath | [key: string, pk: string] | EntitiesPath;
6569
/** Returns true if a circular reference is found */
6670
interface CheckLoop {
6771
(entityKey: string, pk: string, input: object): boolean;
6872
}
6973
/** Get all normalized entities of one type from store */
7074
interface GetEntities {
71-
(...path: EntitiesPath): {
75+
(key: string): {
7276
readonly [pk: string]: any;
7377
} | undefined;
7478
}
7579
/** Get normalized Entity from store */
7680
interface GetEntity {
77-
(...path: EntityPath): any;
81+
(key: string, pk: string): any;
7882
}
7983
/** Get PK using an Entity Index */
8084
interface GetIndex {
@@ -266,24 +270,24 @@ declare abstract class BaseDelegate {
266270
indexes: any;
267271
});
268272
abstract getEntities(...path: EntitiesPath): object | undefined;
269-
abstract getEntity(...path: EntityPath): object | undefined;
273+
abstract getEntity(key: string, pk: string): object | undefined;
270274
abstract getIndex(...path: IndexPath): object | undefined;
271275
abstract getIndexEnd(entity: any, value: string): string | undefined;
272276
tracked(schema: any): [delegate: IQueryDelegate, dependencies: Dep<QueryPath>[]];
273277
}
274278

275-
interface EntityCache extends Map<string, Map<string, WeakMap<EntityInterface, WeakDependencyMap<EntityPath, object, any>>>> {
276-
}
277279
type EndpointsCache = WeakDependencyMap<EntityPath, object, any>;
278280

281+
type GetEntityCache = (pk: string, schema: EntityInterface) => WeakDependencyMap<EntityPath, object, any>;
282+
279283
type DelegateClass = new (v: {
280284
entities: any;
281285
indexes: any;
282286
}) => BaseDelegate;
283287
/** Singleton to store the memoization cache for denormalization methods */
284288
declare class MemoCache {
285289
/** Cache for every entity based on its dependencies and its own input */
286-
protected entities: EntityCache;
290+
protected _getCache: GetEntityCache;
287291
/** Caches the final denormalized form based on input, entities */
288292
protected endpoints: EndpointsCache;
289293
/** Caches the queryKey based on schema, args, and any used entities or indexes */

0 commit comments

Comments
 (0)