Skip to content

Commit 4dde1d6

Browse files
authored
enhance: Hoist ImmutableJS vs Pojo split to import-time (#3468)
1 parent e710187 commit 4dde1d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+991
-2381
lines changed

.changeset/big-impalas-tap.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
'@data-client/normalizr': patch
3+
---
4+
5+
Add /imm exports path for handling ImmutableJS state
6+
7+
#### MemoCache
8+
9+
```ts
10+
import { MemoCache } from '@data-client/normalizr';
11+
import { MemoPolicy } from '@data-client/normalizr/imm';
12+
13+
const memo = new MemoCache(MemoPolicy);
14+
15+
// entities is an ImmutableJS Map
16+
const value = MemoCache.denormalize(Todo, '1', entities);
17+
```
18+
19+
#### denormalize
20+
21+
non-memoized denormalize
22+
23+
```ts
24+
import { denormalize } from '@data-client/normalizr/imm';
25+
26+
// entities is an ImmutableJS Map
27+
const value = denormalize(Todo, '1', entities);
28+
```

.changeset/legal-files-fly.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@data-client/normalizr': minor
3+
---
4+
5+
BREAKING: denormalize no longer detects ImmutableJS state
6+
7+
Use `/imm` exports to handle ImmutableJS state
8+
9+
#### Before
10+
11+
```ts
12+
import { MemoCache, denormalize } from '@data-client/normalizr';
13+
14+
const memo = new MemoCache();
15+
```
16+
17+
#### After
18+
19+
```ts
20+
import { MemoCache } from '@data-client/normalizr';
21+
import { MemoPolicy, denormalize } from '@data-client/normalizr/imm';
22+
23+
const memo = new MemoCache(MemoPolicy);
24+
```

.changeset/proud-taxes-bake.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@data-client/normalizr': minor
3+
'@data-client/endpoint': minor
4+
---
5+
6+
delegate.getEntity(this.key) -> delegate.getEntities(this.key)
7+
8+
This applies to both schema.queryKey and schema.normalize method delegates.

.changeset/ten-lions-retire.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@data-client/normalizr': patch
3+
'@data-client/core': patch
4+
'@data-client/react': patch
5+
---
6+
7+
Improve performance of get/denormalize for small responses
8+
9+
- 10-20% performance improvement due to removing immutablejs check for every call

packages/core/src/controller/Controller.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,6 @@ export default class Controller<
544544
};
545545
}
546546

547-
// second argument is false if any entities are missing
548-
549547
const { data, paths } = this.memo.denormalize(
550548
schema,
551549
input,
@@ -652,7 +650,7 @@ function entityExpiresAt(
652650
},
653651
) {
654652
let expiresAt = Infinity;
655-
for (const { pk, key } of paths) {
653+
for (const { key, pk } of paths) {
656654
const entityExpiry = entitiesMeta[key]?.[pk]?.expiresAt;
657655
// expiresAt will always resolve to false with any comparison
658656
if (entityExpiry < expiresAt) expiresAt = entityExpiry;

packages/core/src/state/GCPolicy.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ export class GCPolicy implements GCInterface {
4646
if (key)
4747
this.endpointCount.set(key, (this.endpointCount.get(key) ?? 0) + 1);
4848
paths.forEach(path => {
49-
if (!this.entityCount.has(path.key)) {
50-
this.entityCount.set(path.key, new Map<string, number>());
49+
const { key, pk } = path;
50+
if (!this.entityCount.has(key)) {
51+
this.entityCount.set(key, new Map<string, number>());
5152
}
52-
const instanceCount = this.entityCount.get(path.key)!;
53-
instanceCount.set(path.pk, (instanceCount.get(path.pk) ?? 0) + 1);
53+
const instanceCount = this.entityCount.get(key)!;
54+
instanceCount.set(pk, (instanceCount.get(pk) ?? 0) + 1);
5455
});
5556

5657
// decrement
@@ -68,18 +69,19 @@ export class GCPolicy implements GCInterface {
6869
}
6970
}
7071
paths.forEach(path => {
71-
if (!this.entityCount.has(path.key)) {
72+
const { key, pk } = path;
73+
if (!this.entityCount.has(key)) {
7274
return;
7375
}
74-
const instanceCount = this.entityCount.get(path.key)!;
75-
const entityCount = instanceCount.get(path.pk)!;
76+
const instanceCount = this.entityCount.get(key)!;
77+
const entityCount = instanceCount.get(pk)!;
7678
if (entityCount !== undefined) {
7779
if (entityCount <= 1) {
78-
instanceCount.delete(path.pk);
80+
instanceCount.delete(pk);
7981
// queue for cleanup
8082
this.entitiesQ.push(path);
8183
} else {
82-
instanceCount.set(path.pk, entityCount - 1);
84+
instanceCount.set(pk, entityCount - 1);
8385
}
8486
}
8587
});

packages/endpoint/src/index.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,7 @@ export { default as Entity } from './schemas/Entity.js';
1717
export { default as EntityMixin } from './schemas/EntityMixin.js';
1818
export type { IEntityClass, IEntityInstance } from './schemas/EntityTypes.js';
1919
export { default as validateRequired } from './validateRequired.js';
20-
export type {
21-
EndpointInterface,
22-
ReadEndpoint,
23-
MutateEndpoint,
24-
Schema,
25-
IQueryDelegate,
26-
INormalizeDelegate,
27-
SnapshotInterface,
28-
ExpiryStatusInterface,
29-
SchemaSimple,
30-
SchemaClass,
31-
PolymorphicInterface,
32-
Queryable,
33-
Mergeable,
34-
} from './interface.js';
20+
export * from './interface.js';
3521
export type { EntityFields } from './schemas/EntityFields.js';
3622
export type {
3723
AbstractInstanceType,

packages/endpoint/src/interface.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,24 +113,37 @@ export interface Visit {
113113
creating?: boolean;
114114
}
115115

116+
/** Used in denormalize. Lookup to find an entity in the store table */
117+
export interface EntityPath {
118+
key: string;
119+
pk: string;
120+
}
121+
122+
export type IndexPath = [key: string, index: string, value: string];
123+
export type EntitiesPath = [key: string];
124+
116125
/** Returns true if a circular reference is found */
117126
export interface CheckLoop {
118127
(entityKey: string, pk: string, input: object): boolean;
119128
}
120129

121-
/** Get Array of entities with map function applied */
130+
/** Get all normalized entities of one type from store */
131+
export interface GetEntities {
132+
(key: string): { readonly [pk: string]: any } | undefined;
133+
}
134+
/** Get normalized Entity from store */
122135
export interface GetEntity {
123-
(entityKey: string | symbol): { readonly [pk: string]: any } | undefined;
124-
(entityKey: string | symbol, pk: string | number): any;
136+
(key: string, pk: string): any;
125137
}
126138
/** Get PK using an Entity Index */
127139
export interface GetIndex {
128140
/** getIndex('User', 'username', 'ntucker') */
129-
(entityKey: string, field: string, value: string): string | undefined;
141+
(...path: IndexPath): string | undefined;
130142
}
131143

132144
/** Accessors to the currently processing state while building query */
133145
export interface IQueryDelegate {
146+
getEntities: GetEntities;
134147
getEntity: GetEntity;
135148
getIndex: GetIndex;
136149
/** Return to consider results invalid */
@@ -141,6 +154,8 @@ export interface IQueryDelegate {
141154
export interface INormalizeDelegate {
142155
/** Action meta-data for this normalize call */
143156
readonly meta: { fetchedAt: number; date: number; expiresAt: number };
157+
/** Get all normalized entities of one type from store */
158+
getEntities: GetEntities;
144159
/** Gets any previously normalized entity from store */
145160
getEntity: GetEntity;
146161
/** Updates an entity using merge lifecycles when it has previously been set */

packages/endpoint/src/schemas/All.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default class AllSchema<
2626

2727
queryKey(args: any, unvisit: any, delegate: IQueryDelegate): any {
2828
if (this.isSingleSchema) {
29-
const entitiesEntry = delegate.getEntity(this.schema.key);
29+
const entitiesEntry = delegate.getEntities(this.schema.key);
3030
// we must wait until there are entries for any 'All' query to be Valid
3131
if (entitiesEntry === undefined) return delegate.INVALID;
3232
return Object.values(entitiesEntry).map(
@@ -36,7 +36,7 @@ export default class AllSchema<
3636
let found = false;
3737
const list = Object.values(this.schema as Record<string, any>).flatMap(
3838
(schema: EntityInterface) => {
39-
const entitiesEntry = delegate.getEntity(schema.key);
39+
const entitiesEntry = delegate.getEntities(schema.key);
4040
if (entitiesEntry === undefined) return [];
4141
found = true;
4242
return Object.entries(entitiesEntry).map(([key, entity]) => ({

packages/endpoint/src/schemas/Collection.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,9 @@ export default class CollectionSchema<
219219

220220
queryKey(args: Args, unvisit: unknown, delegate: IQueryDelegate): any {
221221
if (this.argsKey) {
222-
const id = this.pk(undefined, undefined, '', args);
222+
const pk = this.pk(undefined, undefined, '', args);
223223
// ensure this actually has entity or we shouldn't try to use it in our query
224-
if (delegate.getEntity(this.key, id)) return id;
224+
if (delegate.getEntity(this.key, pk)) return pk;
225225
}
226226
}
227227

@@ -326,7 +326,7 @@ function normalizeCreate(
326326
// parent is args when not nested
327327
const filterCollections = (this.createCollectionFilter as any)(...args);
328328
// add to any collections that match this
329-
const entities = delegate.getEntity(this.key);
329+
const entities = delegate.getEntities(this.key);
330330
if (entities)
331331
Object.keys(entities).forEach(collectionPk => {
332332
if (!filterCollections(JSON.parse(collectionPk))) return;

0 commit comments

Comments
 (0)