Skip to content

Commit 2367dff

Browse files
committed
for denorm
1 parent a477670 commit 2367dff

10 files changed

+261
-13
lines changed

packages/normalizr/src/__tests__/WeakDependencyMap.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Temporal } from '@js-temporal/polyfill';
22

33
import { getEntities } from '../denormalize/getEntities';
44
import { EntityPath } from '../interface';
5+
import { PlainDelegate } from '../memo/Delegate';
56
import WeakDependencyMap from '../memo/WeakDependencyMap';
67

78
describe('WeakDependencyMap', () => {
@@ -19,7 +20,7 @@ describe('WeakDependencyMap', () => {
1920
'3': c,
2021
},
2122
};
22-
const getEntity = getEntities(state);
23+
const getEntity = PlainDelegate.forDenorm(state);
2324
const depA = { path: { key: 'A', pk: '1' }, entity: a };
2425
const depB = { path: { key: 'B', pk: '2' }, entity: b };
2526
const depC = { path: { key: 'C', pk: '3' }, entity: c };

packages/normalizr/src/__tests__/immutable.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { Entity, schema } from '@data-client/endpoint';
33
import { fromJS, Record, Map } from 'immutable';
44

5-
import { normalize } from '..';
5+
import { normalize } from '../';
66
import { denormalize } from '../denormalize/denormalize';
77

88
export function fromJSEntities(entities: {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import LocalCache from './localCache.js';
2+
import getUnvisit from './unvisit.js';
3+
import type { Schema } from '../interface.js';
4+
import type { DenormalizeNullable } from '../types.js';
5+
import type { INVALID } from './symbol.js';
6+
import { DelegateImmutable } from '../memo/Delegate.immutable.js';
7+
8+
export function denormalize<S extends Schema>(
9+
schema: S | undefined,
10+
input: any,
11+
entities: any,
12+
args: readonly any[] = [],
13+
): DenormalizeNullable<S> | typeof INVALID {
14+
// undefined means don't do anything
15+
if (schema === undefined || input === undefined) {
16+
return input as any;
17+
}
18+
19+
return getUnvisit(
20+
DelegateImmutable.forDenorm(entities),
21+
new LocalCache(),
22+
args,
23+
)(schema, input).data;
24+
}

packages/normalizr/src/denormalize/denormalize.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { getEntities } from './getEntities.js';
21
import LocalCache from './localCache.js';
32
import getUnvisit from './unvisit.js';
43
import type { Schema } from '../interface.js';
54
import type { DenormalizeNullable } from '../types.js';
65
import type { INVALID } from './symbol.js';
6+
import { PlainDelegate } from '../memo/Delegate.js';
77

88
export function denormalize<S extends Schema>(
99
schema: S | undefined,
@@ -17,7 +17,7 @@ export function denormalize<S extends Schema>(
1717
}
1818

1919
return getUnvisit(
20-
getEntities(entities),
20+
PlainDelegate.forDenorm(entities),
2121
new LocalCache(),
2222
args,
2323
)(schema, input).data;

packages/normalizr/src/denormalize/getEntities.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import { DelegateImmutable } from '../immutable.js';
12
import type { EntityPath } from '../interface.js';
3+
import { PlainDelegate } from '../memo/Delegate.js';
24
import type { GetDependency } from '../memo/WeakDependencyMap.js';
35
import { isImmutable } from '../schemas/ImmutableUtils.js';
46

57
export function getEntities<K extends object>(
68
state: State<K>,
7-
): GetDependency<EntityPath, K> {
9+
): GetDependency<EntityPath, K | symbol> {
810
const entityIsImmutable = isImmutable(state);
911

1012
if (entityIsImmutable) {
11-
return ({ key, pk }: EntityPath) => state.getIn([key, pk]);
13+
return DelegateImmutable.forDenorm(state as any) as any;
1214
} else {
13-
return ({ key, pk }: EntityPath) => state[key]?.[pk];
15+
return PlainDelegate.forDenorm(state as any) as any;
1416
}
1517
}
1618

packages/normalizr/src/immutable.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { DelegateImmutable } from './memo/Delegate.immutable.js';
2+
export { denormalize } from './denormalize/denormalize.immutable.js';

packages/normalizr/src/memo/Delegate.immutable.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { EntitiesPath, EntityPath, IndexPath } from '../interface.js';
1+
import type { EntityPath } from '../interface.js';
22
import { BaseDelegate } from './BaseDelegate.js';
33

44
type ImmutableJSEntityTable = {
@@ -37,4 +37,9 @@ export class DelegateImmutable extends BaseDelegate {
3737
) {
3838
return entity?.get?.(value);
3939
}
40+
41+
static forDenorm(entities: { getIn(path: [string, string]): unknown }) {
42+
return ({ key, pk }: EntityPath): symbol | object | undefined =>
43+
entities.getIn([key, pk]) as any;
44+
}
4045
}

packages/normalizr/src/memo/Delegate.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { EntityTable, NormalizedIndex } from '../interface.js';
1+
import type { EntityPath, EntityTable, NormalizedIndex } from '../interface.js';
22
import { BaseDelegate } from './BaseDelegate.js';
33

44
export class PlainDelegate extends BaseDelegate {
@@ -29,4 +29,9 @@ export class PlainDelegate extends BaseDelegate {
2929
getIndexEnd(entity: object | undefined, value: string) {
3030
return entity?.[value];
3131
}
32+
33+
static forDenorm(entities: EntityTable) {
34+
return ({ key, pk }: EntityPath): symbol | object | undefined =>
35+
entities[key]?.[pk] as any;
36+
}
3237
}

packages/normalizr/src/memo/MemoCache.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import buildQueryKey from '../buildQueryKey.js';
44
import { getDependency, type BaseDelegate } from './BaseDelegate.js';
55
import { PlainDelegate } from './Delegate.js';
66
import { GetEntityCache, getEntityCaches } from './entitiesCache.js';
7-
import { getEntities } from '../denormalize/getEntities.js';
7+
import type { GetEntity } from '../denormalize/getEntities.js';
88
import getUnvisit from '../denormalize/unvisit.js';
99
import type {
1010
EntityPath,
@@ -29,9 +29,15 @@ export default class MemoCache {
2929
/** Caches the queryKey based on schema, args, and any used entities or indexes */
3030
protected queryKeys: Map<string, WeakDependencyMap<QueryPath>> = new Map();
3131

32-
declare protected Delegate: DelegateClass;
32+
declare protected Delegate: DelegateClass & {
33+
forDenorm(entities: any): GetEntity;
34+
};
3335

34-
constructor(D: DelegateClass = PlainDelegate) {
36+
constructor(
37+
D: DelegateClass & {
38+
forDenorm(entities: any): GetEntity;
39+
} = PlainDelegate,
40+
) {
3541
this.Delegate = D;
3642
this._getCache = getEntityCaches(new Map());
3743
}
@@ -57,7 +63,7 @@ export default class MemoCache {
5763
if (input === undefined) {
5864
return { data: undefined as any, paths: [] };
5965
}
60-
const getEntity = getEntities(entities);
66+
const getEntity = this.Delegate.forDenorm(entities);
6167

6268
return getUnvisit(
6369
getEntity,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// import {
2+
// EntityTable,
3+
// NormalizedIndex,
4+
// INormalizeDelegate,
5+
// Mergeable,
6+
// } from '../interface.js';
7+
// import { getCheckLoop } from './getCheckLoop.js';
8+
// import { INVALID } from '../denormalize/symbol.js';
9+
// import { DelegateImmutable } from '../memo/Delegate.immutable.js';
10+
11+
// type ImmutableJSEntityTable = {
12+
// getIn(k: [key: string, pk: string]): { toJS(): any } | undefined;
13+
// setIn(k: [key: string, pk: string], value: any);
14+
// };
15+
// type ImmutableJSMeta = {
16+
// getIn(k: [key: string, pk: string]):
17+
// | {
18+
// date: number;
19+
// expiresAt: number;
20+
// fetchedAt: number;
21+
// }
22+
// | undefined;
23+
// setIn(
24+
// k: [key: string, pk: string],
25+
// value: {
26+
// date: number;
27+
// expiresAt: number;
28+
// fetchedAt: number;
29+
// },
30+
// );
31+
// };
32+
33+
// export class NormalizeDelegate
34+
// extends DelegateImmutable
35+
// implements INormalizeDelegate
36+
// {
37+
// declare readonly entitiesMeta: ImmutableJSMeta;
38+
39+
// declare readonly meta: { fetchedAt: number; date: number; expiresAt: number };
40+
// declare checkLoop: (entityKey: string, pk: string, input: object) => boolean;
41+
42+
// protected newEntities = new Map<string, Map<string, any>>();
43+
// protected newIndexes = new Map<string, Map<string, any>>();
44+
45+
// constructor(
46+
// {
47+
// entities,
48+
// indexes,
49+
// entitiesMeta,
50+
// }: {
51+
// entities: ImmutableJSEntityTable;
52+
// indexes: ImmutableJSEntityTable;
53+
// entitiesMeta: ImmutableJSMeta;
54+
// },
55+
// actionMeta: { fetchedAt: number; date: number; expiresAt: number },
56+
// ) {
57+
// super(entities, indexes);
58+
// this.entitiesMeta = entitiesMeta;
59+
// this.meta = actionMeta;
60+
// this.checkLoop = getCheckLoop();
61+
// }
62+
63+
// // getNewEntity(key: string, pk: string) {
64+
// // return this.getNewEntities(key).get(pk);
65+
// // }
66+
67+
// protected getNewEntities(key: string): Map<string, any> {
68+
// // first time we come across this type of entity
69+
// if (!this.newEntities.has(key)) {
70+
// this.newEntities.set(key, new Map());
71+
// }
72+
73+
// return this.newEntities.get(key) as Map<string, any>;
74+
// }
75+
76+
// protected getNewIndexes(key: string): Map<string, any> {
77+
// if (!this.newIndexes.has(key)) {
78+
// this.newIndexes.set(key, new Map());
79+
// }
80+
// return this.newIndexes.get(key) as Map<string, any>;
81+
// }
82+
83+
// // /** Updates an entity using merge lifecycles when it has previously been set */
84+
// // mergeEntity(
85+
// // schema: Mergeable & { indexes?: any },
86+
// // pk: string,
87+
// // incomingEntity: any,
88+
// // ) {
89+
// // const key = schema.key;
90+
91+
// // // default when this is completely new entity
92+
// // let nextEntity = incomingEntity;
93+
// // let nextMeta = this.meta;
94+
95+
// // // if we already processed this entity during this normalization (in another nested place)
96+
// // let entity = this.getNewEntity(key, pk);
97+
// // if (entity) {
98+
// // nextEntity = schema.merge(entity, incomingEntity);
99+
// // } else {
100+
// // // if we find it in the store
101+
// // entity = this.getEntity(key, pk);
102+
// // if (entity) {
103+
// // const meta = this.getMeta(key, pk);
104+
// // nextEntity = schema.mergeWithStore(
105+
// // meta,
106+
// // nextMeta,
107+
// // entity,
108+
// // incomingEntity,
109+
// // );
110+
// // nextMeta = schema.mergeMetaWithStore(
111+
// // meta,
112+
// // nextMeta,
113+
// // entity,
114+
// // incomingEntity,
115+
// // );
116+
// // }
117+
// // }
118+
119+
// // // once we have computed the merged values, set them
120+
// // this.setEntity(schema, pk, nextEntity, nextMeta);
121+
// // }
122+
123+
// /** Sets an entity overwriting any previously set values */
124+
// setEntity(
125+
// schema: { key: string; indexes?: any },
126+
// pk: string,
127+
// entity: any,
128+
// meta: { fetchedAt: number; date: number; expiresAt: number } = this.meta,
129+
// ) {
130+
// const key = schema.key;
131+
// const newEntities = this.getNewEntities(key);
132+
// const updateMeta = !newEntities.has(pk);
133+
// newEntities.set(pk, entity);
134+
135+
// // update index
136+
// if (schema.indexes) {
137+
// handleIndexes(
138+
// pk,
139+
// schema.indexes,
140+
// this.getNewIndexes(key),
141+
// this.indexes[key],
142+
// entity,
143+
// this.entities[key] as any,
144+
// );
145+
// }
146+
147+
// // set this after index updates so we know what indexes to remove from
148+
// (this.entities[key] as any)[pk] = entity;
149+
150+
// if (updateMeta) this.entitiesMeta[key][pk] = meta;
151+
// }
152+
153+
// protected _setEntity(key: string, pk: string, entity: any) {
154+
// this.entities.setIn([key, pk], entity);
155+
// }
156+
157+
// protected _setMeta(
158+
// key: string,
159+
// pk: string,
160+
// meta: { fetchedAt: number; date: number; expiresAt: number },
161+
// ) {
162+
// this.entitiesMeta.setIn([key, pk], meta);
163+
// }
164+
165+
// getMeta(key: string, pk: string) {
166+
// return this.entitiesMeta.getIn([key, pk]);
167+
// }
168+
// }
169+
170+
// function handleIndexes(
171+
// id: string,
172+
// schemaIndexes: string[],
173+
// indexes: Map<string, any>,
174+
// storeIndexes: Record<string, any>,
175+
// entity: any,
176+
// storeEntities: Record<string, any>,
177+
// ) {
178+
// for (const index of schemaIndexes) {
179+
// if (!indexes.has(index)) {
180+
// indexes.set(index, (storeIndexes[index] = {}));
181+
// }
182+
// const indexMap = indexes.get(index);
183+
// if (storeEntities[id]) {
184+
// delete indexMap[storeEntities[id][index]];
185+
// }
186+
// // entity already in cache but the index changed
187+
// if (
188+
// storeEntities &&
189+
// storeEntities[id] &&
190+
// storeEntities[id][index] !== entity[index]
191+
// ) {
192+
// indexMap[storeEntities[id][index]] = INVALID;
193+
// }
194+
// if (index in entity) {
195+
// indexMap[entity[index]] = id;
196+
// } /* istanbul ignore next */ else if (
197+
// process.env.NODE_ENV !== 'production'
198+
// ) {
199+
// console.warn(`Index not found in entity. Indexes must be top-level members of your entity.
200+
// Index: ${index}
201+
// Entity: ${JSON.stringify(entity, undefined, 2)}`);
202+
// }
203+
// }
204+
// }

0 commit comments

Comments
 (0)