Skip to content

Commit f190842

Browse files
committed
feat: Add schema.Collection
fix: changes to
1 parent 2e959e8 commit f190842

38 files changed

+1261
-112
lines changed

.vscode/tasks.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@
4646
"endsPattern": "compiled successfully"
4747
}
4848
}
49+
},
50+
{
51+
"type": "npm",
52+
"script": "build",
53+
"group": "build",
54+
"problemMatcher": [],
55+
"label": "npm: build",
56+
"detail": "yarn build:types && yarn workspaces foreach -ptiv --no-private run build"
4957
}
5058
]
5159
}

docs/rest/guides/summary-list.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ delay: 150,
4545
},
4646
]}>
4747

48-
```typescript title="api/Article.ts" {9}
48+
```typescript title="api/Article.ts" {11,24}
49+
import { validateRequired } from '@rest-hooks/rest';
50+
4951
class ArticleSummary extends Entity {
5052
readonly id: string = '';
5153
readonly title: string = '';
@@ -170,6 +172,7 @@ class Article extends ArticleSummary {
170172

171173
static validate(processedEntity) {
172174
return (
175+
// highlight-next-line
173176
validateRequired(processedEntity, this.defaults) ||
174177
super.validate(processedEntity)
175178
);

examples/benchmark/normalizr.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,15 @@ export default function addNormlizrSuite(suite) {
5757
entities,
5858
denormCache.entities,
5959
denormCache.results['/fake'],
60+
[],
6061
);
6162
denormalizeCached(
6263
queryState.result,
6364
ProjectQuery,
6465
queryState.entities,
6566
denormCache.entities,
6667
denormCache.results['/fakeQuery'],
68+
[],
6769
);
6870
%OptimizeFunctionOnNextCall(denormalizeCached);
6971
%OptimizeFunctionOnNextCall(normalize);
@@ -113,6 +115,7 @@ export default function addNormlizrSuite(suite) {
113115
entities,
114116
denormCache.entities,
115117
denormCache.results['/fake'],
118+
[],
116119
);
117120
})
118121
.add('denormalizeLong All withCache', () => {
@@ -122,6 +125,7 @@ export default function addNormlizrSuite(suite) {
122125
queryState.entities,
123126
denormCache.entities,
124127
denormCache.results['/fakeQuery'],
128+
[],
125129
);
126130
})
127131
.add('denormalizeLong Query-sorted withCache', () => {
@@ -131,6 +135,7 @@ export default function addNormlizrSuite(suite) {
131135
queryState.entities,
132136
denormCache.entities,
133137
denormCache.results['/fakeQuery'],
138+
[],
134139
);
135140
})
136141
.on('complete', function () {

packages/core/src/controller/BaseController.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ export default class Controller<
401401
state.entities,
402402
this.globalCache.entities,
403403
this.globalCache.results[key],
404+
args,
404405
) as { data: DenormalizeNullable<E['schema']>; paths: Path[] };
405406
const invalidDenormalize = typeof data === 'symbol';
406407

packages/core/src/next/Controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default class Controller<
3030

3131
if (endpoint.schema) {
3232
return action.meta.promise.then(input =>
33-
denormalize(input, endpoint.schema, {}),
33+
denormalize(input, endpoint.schema, {}, args),
3434
) as any;
3535
}
3636
return action.meta.promise as any;

packages/core/src/state/reducer/setReducer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export function setReducer(
4040
const { result, entities, indexes, entityMeta } = normalize(
4141
payload,
4242
action.meta.schema,
43+
action.meta.args as any,
4344
state.entities,
4445
state.indexes,
4546
state.entityMeta,

packages/endpoint/src/interface.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@ export interface SchemaSimple<T = any> {
2525
visit: (...args: any) => any,
2626
addEntity: (...args: any) => any,
2727
visitedEntities: Record<string, any>,
28+
storeEntities: any,
2829
): any;
2930
denormalize(
3031
// eslint-disable-next-line @typescript-eslint/ban-types
3132
input: {},
3233
unvisit: UnvisitFunction,
3334
): [denormalized: T, found: boolean, suspend: boolean];
34-
denormalizeOnly?(input: {}, unvisit: (input: any, schema: any) => any): T;
35+
denormalizeOnly?(
36+
input: {},
37+
args: any,
38+
unvisit: (input: any, schema: any) => any,
39+
): T;
3540
infer(
3641
args: readonly any[],
3742
indexes: NormalizedIndex,

packages/endpoint/src/normal.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export type Denormalize<S> = S extends EntityInterface<infer U>
7070
? AbstractInstanceType<S>
7171
: S extends { denormalizeOnly: (...args: any) => any }
7272
? ReturnType<S['denormalizeOnly']>
73-
: S extends SchemaClass
73+
: S extends { denormalize: (...args: any) => any }
7474
? DenormalizeReturnType<S['denormalize']>
7575
: S extends Serializable<infer T>
7676
? T
@@ -84,7 +84,7 @@ export type DenormalizeNullable<S> = S extends EntityInterface<any>
8484
? DenormalizeNullableNestedSchema<S> | undefined
8585
: S extends RecordClass
8686
? DenormalizeNullableNestedSchema<S>
87-
: S extends SchemaClass
87+
: S extends { _denormalizeNullable: (...args: any) => any }
8888
? DenormalizeReturnType<S['_denormalizeNullable']>
8989
: S extends Serializable<infer T>
9090
? T
@@ -98,7 +98,7 @@ export type Normalize<S> = S extends EntityInterface
9898
? string
9999
: S extends RecordClass
100100
? NormalizeObject<S['schema']>
101-
: S extends SchemaClass
101+
: S extends { normalize: (...args: any) => any }
102102
? NormalizeReturnType<S['normalize']>
103103
: S extends Serializable<infer T>
104104
? T
@@ -112,7 +112,7 @@ export type NormalizeNullable<S> = S extends EntityInterface
112112
? string | undefined
113113
: S extends RecordClass
114114
? NormalizedNullableObject<S['schema']>
115-
: S extends SchemaClass
115+
: S extends { _normalizeNullable: (...args: any) => any }
116116
? NormalizeReturnType<S['_normalizeNullable']>
117117
: S extends Serializable<infer T>
118118
? T

packages/endpoint/src/queryEndpoint.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ export class Query<
4646
if (schema.denormalizeOnly)
4747
query.denormalizeOnly = (
4848
{ args, input }: { args: P; input: any },
49+
_: P,
4950
unvisit: any,
5051
) => {
5152
if (input === undefined) return undefined;
52-
const value = (schema as any).denormalizeOnly(input, unvisit);
53+
const value = (schema as any).denormalizeOnly(input, args, unvisit);
5354
return typeof value === 'symbol'
5455
? undefined
5556
: this.process(value, ...args);
@@ -83,5 +84,9 @@ type QuerySchema<Schema, R> = Exclude<
8384
input: {},
8485
unvisit: UnvisitFunction,
8586
): [denormalized: R | undefined, found: boolean, suspend: boolean];
86-
denormalizeOnly(input: {}, unvisit: (input: any, schema: any) => any): R;
87+
denormalizeOnly(
88+
input: {},
89+
args: readonly any[],
90+
unvisit: (input: any, schema: any) => any,
91+
): R;
8792
};

packages/endpoint/src/schema.d.ts

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export class Array<S extends Schema = Schema> implements SchemaClass {
5656
visit: (...args: any) => any,
5757
addEntity: (...args: any) => any,
5858
visitedEntities: Record<string, any>,
59+
storeEntities: any,
5960
): (S extends EntityMap ? UnionResult<S> : Normalize<S>)[];
6061

6162
_normalizeNullable():
@@ -80,6 +81,7 @@ export class Array<S extends Schema = Schema> implements SchemaClass {
8081

8182
denormalizeOnly(
8283
input: {},
84+
args: readonly any[],
8385
unvisit: (input: any, schema: any) => any,
8486
): (S extends EntityMap<infer T> ? T : Denormalize<S>)[];
8587

@@ -116,6 +118,7 @@ export class All<
116118
visit: (...args: any) => any,
117119
addEntity: (...args: any) => any,
118120
visitedEntities: Record<string, any>,
121+
storeEntities: any,
119122
): (S extends EntityMap ? UnionResult<S> : Normalize<S>)[];
120123

121124
_normalizeNullable():
@@ -140,6 +143,7 @@ export class All<
140143

141144
denormalizeOnly(
142145
input: {},
146+
args: readonly any[],
143147
unvisit: (input: any, schema: any) => any,
144148
): (S extends EntityMap<infer T> ? T : Denormalize<S>)[];
145149

@@ -168,6 +172,7 @@ export class Object<O extends Record<string, any> = Record<string, Schema>>
168172
visit: (...args: any) => any,
169173
addEntity: (...args: any) => any,
170174
visitedEntities: Record<string, any>,
175+
storeEntities: any,
171176
): NormalizeObject<O>;
172177

173178
_normalizeNullable(): NormalizedNullableObject<O>;
@@ -182,6 +187,7 @@ export class Object<O extends Record<string, any> = Record<string, Schema>>
182187

183188
denormalizeOnly(
184189
input: {},
190+
args: readonly any[],
185191
unvisit: (input: any, schema: any) => any,
186192
): DenormalizeObject<O>;
187193

@@ -215,6 +221,7 @@ export class Union<Choices extends EntityMap = any> implements SchemaClass {
215221
visit: (...args: any) => any,
216222
addEntity: (...args: any) => any,
217223
visitedEntities: Record<string, any>,
224+
storeEntities: any,
218225
): UnionResult<Choices>;
219226

220227
_normalizeNullable(): UnionResult<Choices> | undefined;
@@ -237,6 +244,7 @@ export class Union<Choices extends EntityMap = any> implements SchemaClass {
237244

238245
denormalizeOnly(
239246
input: {},
247+
args: readonly any[],
240248
unvisit: (input: any, schema: any) => any,
241249
): AbstractInstanceType<Choices[keyof Choices]>;
242250

@@ -277,6 +285,7 @@ export class Values<Choices extends Schema = any> implements SchemaClass {
277285
visit: (...args: any) => any,
278286
addEntity: (...args: any) => any,
279287
visitedEntities: Record<string, any>,
288+
storeEntities: any,
280289
): Record<
281290
string,
282291
Choices extends EntityMap ? UnionResult<Choices> : Normalize<Choices>
@@ -317,6 +326,7 @@ export class Values<Choices extends Schema = any> implements SchemaClass {
317326

318327
denormalizeOnly(
319328
input: {},
329+
args: readonly any[],
320330
unvisit: (input: any, schema: any) => any,
321331
): Record<
322332
string,
@@ -330,6 +340,125 @@ export class Values<Choices extends Schema = any> implements SchemaClass {
330340
): any;
331341
}
332342

343+
/**
344+
* Entities but for Arrays instead of classes
345+
* @see https://resthooks.io/rest/api/Collection
346+
*/
347+
export class CollectionSchema<
348+
S extends Array<any> | Values<any> = any,
349+
Parent extends any[] = any,
350+
> {
351+
readonly schema: S;
352+
key: string;
353+
pk(value: any, parent: any, key: string): string;
354+
normalize(
355+
input: any,
356+
parent: Parent,
357+
key: string,
358+
visit: (...args: any) => any,
359+
addEntity: (...args: any) => any,
360+
visitedEntities: Record<string, any>,
361+
storeEntities: any,
362+
): string;
363+
364+
merge(existing: any, incoming: any): any;
365+
shouldReorder(
366+
existingMeta: {
367+
date: number;
368+
fetchedAt: number;
369+
},
370+
incomingMeta: {
371+
date: number;
372+
fetchedAt: number;
373+
},
374+
existing: any,
375+
incoming: any,
376+
): boolean;
377+
378+
mergeWithStore(
379+
existingMeta: {
380+
date: number;
381+
fetchedAt: number;
382+
},
383+
incomingMeta: {
384+
date: number;
385+
fetchedAt: number;
386+
},
387+
existing: any,
388+
incoming: any,
389+
): any;
390+
391+
mergeMetaWithStore(
392+
existingMeta: {
393+
expiresAt: number;
394+
date: number;
395+
fetchedAt: number;
396+
},
397+
incomingMeta: {
398+
expiresAt: number;
399+
date: number;
400+
fetchedAt: number;
401+
},
402+
existing: any,
403+
incoming: any,
404+
): {
405+
expiresAt: number;
406+
date: number;
407+
fetchedAt: number;
408+
};
409+
410+
infer(
411+
args: unknown,
412+
indexes: unknown,
413+
recurse: unknown,
414+
entities: unknown,
415+
): any;
416+
417+
createIfValid: (value: any) => any | undefined;
418+
denormalizeOnly(
419+
input: any,
420+
args: readonly any[],
421+
unvisit: (input: any, schema: any) => any,
422+
): ReturnType<S['denormalizeOnly']>;
423+
424+
_denormalizeNullable(): ReturnType<S['_denormalizeNullable']>;
425+
_normalizeNullable(): ReturnType<S['_normalizeNullable']>;
426+
}
427+
export interface CollectionConstructor {
428+
new <
429+
S extends SchemaSimple[] | Array<any> | Values<any> = any,
430+
Parent extends any[] = [
431+
urlParams: Record<string, any>,
432+
body?: Record<string, any>,
433+
],
434+
>(
435+
schema: S,
436+
instanceKey: (parent: any, key: string) => Record<string, any>,
437+
createCollectionFilter?: (
438+
...args: Parent
439+
) => (collectionKey: Record<string, any>) => boolean,
440+
): CollectionSchema<S extends any[] ? Array<S[number]> : S> &
441+
(S extends any[]
442+
? {
443+
push: CollectionSchema<Array<S[number]>, Parent>;
444+
unshift: CollectionSchema<Array<S[number]>, Parent>;
445+
}
446+
: S extends Values<any>
447+
? { add: CollectionSchema<S, Parent> }
448+
: S extends Array<any>
449+
? {
450+
push: CollectionSchema<S, Parent>;
451+
unshift: CollectionSchema<S, Parent>;
452+
}
453+
: never);
454+
readonly prototype: CollectionSchema;
455+
}
456+
/**
457+
* Entities but for Arrays instead of classes
458+
* @see https://resthooks.io/rest/api/Collection
459+
*/
460+
export declare let Collection: CollectionConstructor;
461+
333462
export type StrategyFunction<T> = (value: any, parent: any, key: string) => T;
334463
export type SchemaFunction<K = string> = (
335464
value: any,
@@ -363,8 +492,13 @@ export interface SchemaSimpleNew<T = any> {
363492
visit: (...args: any) => any,
364493
addEntity: (...args: any) => any,
365494
visitedEntities: Record<string, any>,
495+
storeEntities: any,
366496
): any;
367-
denormalizeOnly(input: {}, unvisit: (input: any, schema: any) => any): T;
497+
denormalizeOnly(
498+
input: {},
499+
args: readonly any[],
500+
unvisit: (input: any, schema: any) => any,
501+
): T;
368502
infer(
369503
args: readonly any[],
370504
indexes: NormalizedIndex,

0 commit comments

Comments
 (0)