Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/core/src/controller/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,6 @@ export default class Controller<
};
}

// second argument is false if any entities are missing

const { data, paths } = this.memo.denormalize(
schema,
input,
Expand Down
23 changes: 14 additions & 9 deletions packages/endpoint/src/schemas/__tests__/All.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
MemoCache,
denormalize,
INVALID,
BaseDelegate,
} from '@data-client/normalizr';
import { DelegateImmutable } from '@data-client/normalizr/immutable';
import { IDEntity } from '__tests__/new';

import { schema } from '../..';
Expand Down Expand Up @@ -101,15 +103,16 @@ describe.each([[]])(`${schema.All.name} normalization (%s)`, () => {
});

describe.each([
['direct', <T>(data: T) => data, <T>(data: T) => data],
['direct', <T>(data: T) => data, <T>(data: T) => data, BaseDelegate],
[
'immutable',
fromJSState,
(v: any) => (typeof v?.toJS === 'function' ? v.toJS() : v),
DelegateImmutable,
],
])(
`${schema.Array.name} denormalization (%s)`,
(_, createInput, createOutput) => {
(_, createInput, createOutput, Delegate) => {
test('denormalizes a single entity', () => {
class Cat extends IDEntity {}
const state: State<unknown> = createInput({
Expand All @@ -123,7 +126,9 @@ describe.each([
indexes: {},
}) as any;
const sch = new schema.All(Cat);
expect(new Controller().get(sch, state)).toMatchSnapshot();
expect(
new Controller({ memo: new MemoCache(Delegate) }).get(sch, state),
).toMatchSnapshot();
});

test('denormalizes nested in object', () => {
Expand All @@ -140,7 +145,7 @@ describe.each([
});
// use memocache because we don't support 'object' schemas in controller yet
expect(
new MemoCache().query(catSchema, [], state).data,
new MemoCache(Delegate).query(catSchema, [], state).data,
).toMatchSnapshot();
});

Expand All @@ -156,7 +161,7 @@ describe.each([
},
indexes: {},
});
const value = new MemoCache().query(catSchema, [], state).data;
const value = new MemoCache(Delegate).query(catSchema, [], state).data;
expect(value).not.toEqual(expect.any(Symbol));
if (typeof value === 'symbol' || value === undefined) return;
expect(createOutput(value.results)).toMatchSnapshot();
Expand All @@ -177,7 +182,7 @@ describe.each([
},
indexes: {},
});
const value = new MemoCache().query(catSchema, [], state).data;
const value = new MemoCache(Delegate).query(catSchema, [], state).data;
expect(value).not.toEqual(expect.any(Symbol));
if (typeof value === 'symbol' || value === undefined) return;
expect(createOutput(value.results).length).toBe(2);
Expand Down Expand Up @@ -244,7 +249,7 @@ describe.each([
},
indexes: {},
});
const value = new MemoCache().query(catSchema, [], state).data;
const value = new MemoCache(Delegate).query(catSchema, [], state).data;
expect(createOutput(value)).toEqual(expect.any(Symbol));
});

Expand Down Expand Up @@ -276,7 +281,7 @@ describe.each([
},
indexes: {},
});
const value = new MemoCache().query(listSchema, [], state).data;
const value = new MemoCache(Delegate).query(listSchema, [], state).data;
expect(createOutput(value)).toEqual(expect.any(Symbol));
});

Expand Down Expand Up @@ -339,7 +344,7 @@ describe.each([
},
indexes: {},
});
const value = new MemoCache().query(listSchema, [], state).data;
const value = new MemoCache(Delegate).query(listSchema, [], state).data;
expect(value).not.toEqual(expect.any(Symbol));
if (typeof value === 'symbol') return;
expect(value).toMatchSnapshot();
Expand Down
29 changes: 18 additions & 11 deletions packages/endpoint/src/schemas/__tests__/Query.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-env jest
import { MemoCache } from '@data-client/normalizr';
import { MemoCache, BaseDelegate } from '@data-client/normalizr';
import { DelegateImmutable } from '@data-client/normalizr/immutable';
import { useQuery, useSuspense, __INTERNAL__ } from '@data-client/react';
import { RestEndpoint } from '@data-client/rest';
import { IDEntity } from '__tests__/new';
Expand All @@ -26,13 +27,14 @@ class User extends IDEntity {
}

describe.each([
['direct', <T>(data: T) => data, <T>(data: T) => data],
['direct', <T>(data: T) => data, <T>(data: T) => data, BaseDelegate],
[
'immutable',
fromJSState,
(v: any) => (typeof v?.toJS === 'function' ? v.toJS() : v),
DelegateImmutable,
],
])(`input (%s)`, (_, createInput, createOutput) => {
])(`input (%s)`, (_, createInput, createOutput, Delegate) => {
const SCHEMA_CASES = [
['All', new schema.Object({ results: new schema.All(User) })],
[
Expand Down Expand Up @@ -76,7 +78,7 @@ describe.each([
},
});
const users: DenormalizeNullable<typeof sortedUsers> | symbol =
new MemoCache().query(sortedUsers, [], state).data;
new MemoCache(Delegate).query(sortedUsers, [], state).data;
expect(users).not.toEqual(expect.any(Symbol));
if (typeof users === 'symbol') return;
expect(users && users[0].name).toBe('Zeta');
Expand All @@ -101,21 +103,22 @@ describe.each([
},
});
expect(
new MemoCache().query(sortedUsers, [{ asc: true }], state).data,
new MemoCache(Delegate).query(sortedUsers, [{ asc: true }], state)
.data,
).toMatchSnapshot();
});

test('denormalizes should not be found when no entities are present', () => {
const state = {
const state = createInput({
...initialState,
entities: {
DOG: {
1: { id: '1', name: 'Milo' },
2: { id: '2', name: 'Jake' },
},
},
};
const { data } = new MemoCache().query(sortedUsers, [], state);
});
const { data } = new MemoCache(Delegate).query(sortedUsers, [], state);

expect(createOutput(data)).not.toEqual(expect.any(Array));
});
Expand Down Expand Up @@ -152,20 +155,24 @@ describe.each([
});
const totalCount:
| DenormalizeNullable<typeof userCountByAdmin>
| symbol = new MemoCache().query(userCountByAdmin, [], state).data;
| symbol = new MemoCache(Delegate).query(
userCountByAdmin,
[],
state,
).data;

expect(totalCount).toBe(4);
const nonAdminCount:
| DenormalizeNullable<typeof userCountByAdmin>
| symbol = new MemoCache().query(
| symbol = new MemoCache(Delegate).query(
userCountByAdmin,
[{ isAdmin: false }],
state,
).data;
expect(nonAdminCount).toBe(3);
const adminCount:
| DenormalizeNullable<typeof userCountByAdmin>
| symbol = new MemoCache().query(
| symbol = new MemoCache(Delegate).query(
userCountByAdmin,
[{ isAdmin: true }],
state,
Expand Down
4 changes: 4 additions & 0 deletions packages/normalizr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@
"react-native": "./lib/index.js",
"default": "./lib/index.js"
},
"./immutable": {
"types": "./lib/immutable.d.ts",
"default": "./lib/immutable.js"
},
"./package.json": "./package.json"
},
"type": "module",
Expand Down
29 changes: 18 additions & 11 deletions packages/normalizr/src/__tests__/MemoCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {

import { fromJSState } from './immutable.test';
import { IQueryDelegate } from '../interface';
import { BaseDelegate } from '../memo/Delegate';
import { DelegateImmutable } from '../memo/Delegate.immutable';
import MemoCache from '../memo/MemoCache';

class IDEntity extends Entity {
Expand Down Expand Up @@ -1009,13 +1011,9 @@ describe('MemoCache', () => {
});

describe.each([
['direct', <T>(data: T) => data, <T>(data: T) => data],
[
'immutable',
fromJSState,
(v: any) => (typeof v?.toJS === 'function' ? v.toJS() : v),
],
])(`query (%s)`, (_, createInput, createOutput) => {
['direct', <T>(data: T) => data, BaseDelegate],
['immutable', fromJSState, DelegateImmutable],
])(`query (%s)`, (_, createInput, Delegate) => {
class Cat extends IDEntity {
id = '0';
name = '';
Expand All @@ -1038,20 +1036,29 @@ describe('MemoCache', () => {
});

test('works with indexes', () => {
const m = new MemoCache().query(Cat, [{ username: 'm' }], state).data;
const m = new MemoCache(Delegate).query(
Cat,
[{ username: 'm' }],
state,
).data;
expect(m).toBeDefined();
expect(m).toMatchSnapshot();
expect(
new MemoCache().query(Cat, [{ username: 'doesnotexist' }], state).data,
new MemoCache(Delegate).query(
Cat,
[{ username: 'doesnotexist' }],
state,
).data,
).toBeUndefined();
});

test('works with pk', () => {
const m = new MemoCache().query(Cat, [{ id: '1' }], state);
const m = new MemoCache(Delegate).query(Cat, [{ id: '1' }], state);
expect(m).toBeDefined();
expect(m).toMatchSnapshot();
expect(
new MemoCache().query(Cat, [{ id: 'doesnotexist' }], state).data,
new MemoCache(Delegate).query(Cat, [{ id: 'doesnotexist' }], state)
.data,
).toBeUndefined();
});
});
Expand Down
2 changes: 0 additions & 2 deletions packages/normalizr/src/denormalize/denormalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { getEntities } from './getEntities.js';
import LocalCache from './localCache.js';
import getUnvisit from './unvisit.js';
import type { Schema } from '../interface.js';
import { isImmutable } from '../schemas/ImmutableUtils.js';
import type { DenormalizeNullable } from '../types.js';
import type { INVALID } from './symbol.js';

Expand All @@ -21,6 +20,5 @@ export function denormalize<S extends Schema>(
getEntities(entities),
new LocalCache(),
args,
isImmutable(entities),
)(schema, input).data;
}
10 changes: 1 addition & 9 deletions packages/normalizr/src/denormalize/unvisit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const getUnvisitEntity = (
getEntity: GetEntity,
cache: Cache,
args: readonly any[],
isImmutable: boolean,
unvisit: (schema: any, input: any) => any,
) => {
return function unvisitEntity(
Expand Down Expand Up @@ -103,16 +102,9 @@ const getUnvisit = (
getEntity: GetEntity,
cache: Cache,
args: readonly any[],
isImmutable: boolean,
) => {
// we don't inline this as making this function too big inhibits v8's JIT
const unvisitEntity = getUnvisitEntity(
getEntity,
cache,
args,
isImmutable,
unvisit,
);
const unvisitEntity = getUnvisitEntity(getEntity, cache, args, unvisit);
function unvisit(schema: any, input: any): any {
if (!schema) return input;

Expand Down
1 change: 1 addition & 0 deletions packages/normalizr/src/immutable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { DelegateImmutable } from './memo/Delegate.immutable.js';
1 change: 1 addition & 0 deletions packages/normalizr/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import WeakDependencyMap from './memo/WeakDependencyMap.js';
import { normalize } from './normalize/normalize.js';

export { default as MemoCache } from './memo/MemoCache.js';
export { BaseDelegate } from './memo/Delegate.js';
export type {
AbstractInstanceType,
NormalizeReturnType,
Expand Down
14 changes: 14 additions & 0 deletions packages/normalizr/src/interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import type { QueryPath } from './memo/types.js';
import type { Dep } from './memo/WeakDependencyMap.js';

export type Schema =
| null
| string
Expand Down Expand Up @@ -123,6 +126,17 @@ export interface IQueryDelegate {
INVALID: symbol;
}

export interface IBaseDelegate {
entities: any;
indexes: any;

getEntity(entityKey: string | symbol, pk?: string): any;
getIndex(key: string, field: string): any;
tracked(
schema: any,
): IQueryDelegate & { readonly dependencies: Dep<QueryPath>[] };
}

/** Helpers during schema.normalize() */
export interface INormalizeDelegate {
/** Action meta-data for this normalize call */
Expand Down
7 changes: 6 additions & 1 deletion packages/normalizr/src/memo/Delegate.immutable.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IBaseDelegate, TrackingQueryDelegate } from './Delegate.js';
import { IBaseDelegate } from '../interface.js';
import { TrackingQueryDelegate } from './Delegate.js';
import { IndexPath } from './types.js';

type ImmutableJSEntityTable = {
Expand Down Expand Up @@ -31,6 +32,10 @@ export class DelegateImmutable implements IBaseDelegate {
getIndex(key: string, field: string) {
return this.indexes.getIn([key, field]);
}

tracked(schema: any) {
return new TrackingQueryDelegateImmutable(this, schema);
}
}

export class TrackingQueryDelegateImmutable extends TrackingQueryDelegate {
Expand Down
13 changes: 5 additions & 8 deletions packages/normalizr/src/memo/Delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
EntityTable,
NormalizedIndex,
IQueryDelegate,
IBaseDelegate,
} from '../interface.js';
import { QueryPath, IndexPath } from './types.js';
import { INVALID } from '../denormalize/symbol.js';
Expand All @@ -15,14 +16,6 @@ export const getDependency =
delegate.getIndex(args[0], args[1])
: delegate.getEntity(...(args as [any]));

export interface IBaseDelegate {
entities: any;
indexes: any;

getEntity(entityKey: string | symbol, pk?: string): any;
getIndex(key: string, field: string): any;
}

export class BaseDelegate implements IBaseDelegate {
declare entities: EntityTable;
declare indexes: {
Expand Down Expand Up @@ -56,6 +49,10 @@ export class BaseDelegate implements IBaseDelegate {
getIndex(key: string, field: string) {
return this.indexes[key]?.[field];
}

tracked(schema: any) {
return new TrackingQueryDelegate(this, schema);
}
}

export class TrackingQueryDelegate implements IQueryDelegate {
Expand Down
Loading