From dab41ae31a704c7f6cb0a6ee42cbb388e8d1e9c3 Mon Sep 17 00:00:00 2001 From: Nathaniel Tucker Date: Tue, 9 Jan 2024 15:01:34 +0000 Subject: [PATCH] feat: Use Querable schemas in useCache() --- docs/core/shared/_schema_table.mdx | 21 ++++++------ .../src-4.0-types/schemas/EntityFields.d.ts | 4 +++ packages/endpoint/src/index.ts | 2 +- packages/endpoint/src/interface.ts | 24 ++++++++++--- packages/endpoint/src/normal.ts | 34 ++++++------------- packages/endpoint/src/queryEndpoint.ts | 4 +-- packages/endpoint/src/schema.d.ts | 11 +++--- packages/endpoint/src/schemaTypes.ts | 9 ++--- packages/endpoint/src/schemas/Collection.ts | 4 +-- packages/endpoint/src/schemas/EntityFields.ts | 5 +++ packages/endpoint/src/schemas/Invalidate.ts | 2 +- packages/normalizr/src/EntityFields.ts | 5 +++ packages/normalizr/src/index.ts | 1 + packages/normalizr/src/interface.ts | 27 +++++++++++---- packages/normalizr/src/types.ts | 18 +++++++++- 15 files changed, 106 insertions(+), 65 deletions(-) create mode 100644 packages/endpoint/src-4.0-types/schemas/EntityFields.d.ts create mode 100644 packages/endpoint/src/schemas/EntityFields.ts create mode 100644 packages/normalizr/src/EntityFields.ts diff --git a/docs/core/shared/_schema_table.mdx b/docs/core/shared/_schema_table.mdx index a44ac5200b51..31a8d353228f 100644 --- a/docs/core/shared/_schema_table.mdx +++ b/docs/core/shared/_schema_table.mdx @@ -1,10 +1,11 @@ -| Schema | Description | Data Type | Mutable | Has A | -| ---------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------- | -| [Entity](/rest/api/Entity) | single _unique_ object | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | ✅ | | -| [Object](/rest/api/Object) | statically known keys | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | 🛑 | | -| [Array](/rest/api/Array) | lists of any size | [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) | 🛑 | | -| [Values](/rest/api/Values) | maps of any size | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | 🛑 | | -| [All](/rest/api/All) | list of all entities of a kind | [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) | 🛑 | Entity | -| [Collection](/rest/api/Collection) | enables adding new items | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) or [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) | ✅ | Array or Values | -| [Union](/rest/api/Union) | one of many different types (`A \| B`) | Polymorphic [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | ✅ | Entity | -| [Invalidate](/rest/api/Invalidate) | [remove an entity](../concepts/expiry-policy.md#any-endpoint-with-an-entity) | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | ✅ | Entity | +| Schema | Description | Data Type | Mutable | Queryable | Of A | +| ---------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------- | --------- | +| [Entity](/rest/api/Entity) | single _unique_ object | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | ✅ | ✅ | | +| [Object](/rest/api/Object) | statically known keys | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | 🛑 | 🛑 | | +| [Array](/rest/api/Array) | lists of any size | [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) | 🛑 | 🛑 | | +| [Values](/rest/api/Values) | maps of any size | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | 🛑 | 🛑 | | +| [All](/rest/api/All) | list of all entities of a kind | [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) | 🛑 | ✅ | | +| [Collection](/rest/api/Collection) | enables adding new items | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) or [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) | ✅ | ✅ | | +| [Query](/rest/api/Query) | memoized custom transforms | any | 🛑 | ✅ | Queryable | +| [Union](/rest/api/Union) | one of many different types (`A \| B`) | Polymorphic [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | ✅ | ✅ | Entity | +| [Invalidate](/rest/api/Invalidate) | [remove an entity](../concepts/expiry-policy.md#any-endpoint-with-an-entity) | [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) | ✅ | 🛑 | Entity | diff --git a/packages/endpoint/src-4.0-types/schemas/EntityFields.d.ts b/packages/endpoint/src-4.0-types/schemas/EntityFields.d.ts new file mode 100644 index 000000000000..caad6c30e20b --- /dev/null +++ b/packages/endpoint/src-4.0-types/schemas/EntityFields.d.ts @@ -0,0 +1,4 @@ +/** Attempts to infer reasonable input type to construct an Entity */ +export type EntityFields = { + readonly [K in keyof U]?: U[K]; +}; diff --git a/packages/endpoint/src/index.ts b/packages/endpoint/src/index.ts index 83d753829e8e..eb55b3b2c744 100644 --- a/packages/endpoint/src/index.ts +++ b/packages/endpoint/src/index.ts @@ -34,7 +34,7 @@ export type { NormalizeNullable, Denormalize, DenormalizeNullable, - SchemaToArgs, + SchemaArgs, } from './normal.js'; export type { EndpointExtraOptions, diff --git a/packages/endpoint/src/interface.ts b/packages/endpoint/src/interface.ts index 92fb117f4bc7..d0d19d9f90ba 100644 --- a/packages/endpoint/src/interface.ts +++ b/packages/endpoint/src/interface.ts @@ -10,6 +10,16 @@ export type Schema = | SchemaSimple | Serializable; +export type Queryable = { + infer( + args: readonly any[], + indexes: NormalizedIndex, + recurse: (...args: any) => any, + entities: EntityTable, + // Must be non-void + ): {}; +}; + export type Serializable< T extends { toJSON(): string } = { toJSON(): string }, > = (value: any) => T; @@ -23,7 +33,7 @@ export interface SchemaSimple { addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, - args: Args, + args: any[], ): any; denormalize( input: {}, @@ -31,15 +41,18 @@ export interface SchemaSimple { unvisit: (input: any, schema: any) => any, ): T; infer( - args: readonly any[], + args: Args, indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable, ): any; } -export interface SchemaClass - extends SchemaSimple { +export interface SchemaClass< + T = any, + N = T | undefined, + Args extends any[] = any[], +> extends SchemaSimple { // this is not an actual member, but is needed for the recursive NormalizeNullable<> type algo _normalizeNullable(): any; // this is not an actual member, but is needed for the recursive DenormalizeNullable<> type algo @@ -69,7 +82,8 @@ export interface EntityInterface extends SchemaSimple { } /** Represents Array or Values */ -export interface PolymorphicInterface extends SchemaSimple { +export interface PolymorphicInterface + extends SchemaSimple { readonly schema: any; // this is not an actual member, but is needed for the recursive NormalizeNullable<> type algo _normalizeNullable(): any; diff --git a/packages/endpoint/src/normal.ts b/packages/endpoint/src/normal.ts index 8b48620db2f7..9308bf869806 100644 --- a/packages/endpoint/src/normal.ts +++ b/packages/endpoint/src/normal.ts @@ -3,7 +3,9 @@ import type { Serializable, EntityInterface, NormalizedIndex, + Queryable, } from './interface.js'; +import { EntityFields } from './schemas/EntityFields.js'; // TypeScript <4.2 InstanceType<> does not work on abstract classes export type AbstractInstanceType = @@ -117,33 +119,17 @@ export interface EntityMap { readonly [k: string]: EntityInterface; } -export type SchemaToArgs< - S extends { - normalize( - input: any, - parent: any, - key: any, - visit: (...args: any) => any, - addEntity: (...args: any) => any, - visitedEntities: Record, - storeEntities: any, - args: any, - ): any; - }, -> = - S extends ( +export type SchemaArgs = + S extends EntityInterface ? EntityFields + : S extends ( { - normalize( - input: any, - parent: any, - key: any, - visit: (...args: any) => any, - addEntity: (...args: any) => any, - visitedEntities: Record, - storeEntities: any, + infer( args: infer Args, + indexes: any, + recurse: (...args: any) => any, + entities: any, ): any; } ) ? Args - : never; + : []; diff --git a/packages/endpoint/src/queryEndpoint.ts b/packages/endpoint/src/queryEndpoint.ts index cf9eaa4ced31..dc70affae1f4 100644 --- a/packages/endpoint/src/queryEndpoint.ts +++ b/packages/endpoint/src/queryEndpoint.ts @@ -3,7 +3,7 @@ import type { NormalizedIndex, SchemaSimple, } from './interface.js'; -import type { Denormalize, SchemaToArgs } from './normal.js'; +import type { Denormalize, SchemaArgs } from './normal.js'; /** * Programmatic cache reading @@ -11,7 +11,7 @@ import type { Denormalize, SchemaToArgs } from './normal.js'; */ export class Query< S extends SchemaSimple, - P extends SchemaToArgs = SchemaToArgs, + P extends SchemaArgs = SchemaArgs, R = Denormalize, > { declare schema: QuerySchema; diff --git a/packages/endpoint/src/schema.d.ts b/packages/endpoint/src/schema.d.ts index c132f1569548..cef0f8d8a29a 100644 --- a/packages/endpoint/src/schema.d.ts +++ b/packages/endpoint/src/schema.d.ts @@ -18,6 +18,7 @@ import type { NormalizedNullableObject, EntityMap, } from './normal.js'; +import { EntityFields } from './schemas/EntityFields.js'; import { EntityOptions, IEntityClass, @@ -66,7 +67,7 @@ export class Array implements SchemaClass { addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, - args?: any[], + args: any[], ): (S extends EntityMap ? UnionResult : Normalize)[]; _normalizeNullable(): @@ -88,7 +89,7 @@ export class Array implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } /** @@ -136,7 +137,7 @@ export class All< ): (S extends EntityMap ? T : Denormalize)[]; infer( - args: readonly any[], + args: [], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable, @@ -222,7 +223,7 @@ export class Union implements SchemaClass { ): AbstractInstanceType; infer( - args: readonly any[], + args: [EntityFields>], indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, @@ -293,7 +294,7 @@ export class Values implements SchemaClass { indexes: NormalizedIndex, recurse: (...args: any) => any, entities: any, - ): any; + ): undefined; } export type CollectionArrayAdder = diff --git a/packages/endpoint/src/schemaTypes.ts b/packages/endpoint/src/schemaTypes.ts index dc71e26c2677..84919e2a93fa 100644 --- a/packages/endpoint/src/schemaTypes.ts +++ b/packages/endpoint/src/schemaTypes.ts @@ -50,7 +50,7 @@ export interface CollectionInterface< addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, - args: Args, + args: any, ): string; merge(existing: any, incoming: any): any; @@ -99,12 +99,7 @@ export interface CollectionInterface< fetchedAt: number; }; - infer( - args: unknown, - indexes: unknown, - recurse: unknown, - entities: unknown, - ): any; + infer(args: Args, indexes: unknown, recurse: unknown, entities: unknown): any; createIfValid: (value: any) => any | undefined; denormalize( diff --git a/packages/endpoint/src/schemas/Collection.ts b/packages/endpoint/src/schemas/Collection.ts index 33a8b9422389..722f6b37f102 100644 --- a/packages/endpoint/src/schemas/Collection.ts +++ b/packages/endpoint/src/schemas/Collection.ts @@ -155,7 +155,7 @@ export default class CollectionSchema< addEntity: (...args: any) => any, visitedEntities: Record, storeEntities: any, - args: Args, + args: any, ): string { const normalizedValue = this.schema.normalize( input, @@ -218,7 +218,7 @@ export default class CollectionSchema< // >>>>>>>>>>>>>>DENORMALIZE<<<<<<<<<<<<<< - infer(args: any, indexes: unknown, recurse: unknown, entities: any): any { + infer(args: Args, indexes: unknown, recurse: unknown, entities: any): any { if (this.argsKey) { const id = this.pk(undefined, undefined, '', args); if (entities[this.key]?.[id]) return id; diff --git a/packages/endpoint/src/schemas/EntityFields.ts b/packages/endpoint/src/schemas/EntityFields.ts new file mode 100644 index 000000000000..65dc372f052f --- /dev/null +++ b/packages/endpoint/src/schemas/EntityFields.ts @@ -0,0 +1,5 @@ +/** Attempts to infer reasonable input type to construct an Entity */ +export type EntityFields = { + readonly [K in keyof U as U[K] extends (...args: any) => any ? never + : K]?: U[K]; +}; diff --git a/packages/endpoint/src/schemas/Invalidate.ts b/packages/endpoint/src/schemas/Invalidate.ts index 9da7c0ee7a2a..71344e986cab 100644 --- a/packages/endpoint/src/schemas/Invalidate.ts +++ b/packages/endpoint/src/schemas/Invalidate.ts @@ -98,7 +98,7 @@ export default class Invalidate< /** /End Normalize lifecycles **/ - infer(args: any, indexes: any, recurse: any): any { + infer(args: any, indexes: any, recurse: any): undefined { return undefined; } diff --git a/packages/normalizr/src/EntityFields.ts b/packages/normalizr/src/EntityFields.ts new file mode 100644 index 000000000000..65dc372f052f --- /dev/null +++ b/packages/normalizr/src/EntityFields.ts @@ -0,0 +1,5 @@ +/** Attempts to infer reasonable input type to construct an Entity */ +export type EntityFields = { + readonly [K in keyof U as U[K] extends (...args: any) => any ? never + : K]?: U[K]; +}; diff --git a/packages/normalizr/src/index.ts b/packages/normalizr/src/index.ts index 781f3dee32b2..0d7beff98010 100644 --- a/packages/normalizr/src/index.ts +++ b/packages/normalizr/src/index.ts @@ -20,6 +20,7 @@ export type { DenormalizeNullable, Normalize, NormalizeNullable, + SchemaArgs, } from './types.js'; export * from './endpoint/types.js'; export * from './interface.js'; diff --git a/packages/normalizr/src/interface.ts b/packages/normalizr/src/interface.ts index 07b3e848a026..7d69977cf200 100644 --- a/packages/normalizr/src/interface.ts +++ b/packages/normalizr/src/interface.ts @@ -6,11 +6,21 @@ export type Schema = | SchemaSimple | Serializable; +export type Queryable = { + infer( + args: readonly any[], + indexes: NormalizedIndex, + recurse: (...args: any) => any, + entities: EntityTable, + // Must be non-void + ): {}; +}; + export type Serializable< T extends { toJSON(): string } = { toJSON(): string }, > = (value: any) => T; -export interface SchemaSimple { +export interface SchemaSimple { normalize( input: any, parent: any, @@ -18,24 +28,27 @@ export interface SchemaSimple { visit: (...args: any) => any, addEntity: (...args: any) => any, visitedEntities: Record, - storeEntities?: any, - args?: any[], + storeEntities: any, + args: any[], ): any; denormalize( input: {}, - args: any, + args: readonly any[], unvisit: (input: any, schema: any) => any, ): T; infer( - args: readonly any[], + args: Args, indexes: NormalizedIndex, recurse: (...args: any) => any, entities: EntityTable, ): any; } -export interface SchemaClass - extends SchemaSimple { +export interface SchemaClass< + T = any, + N = T | undefined, + Args extends any[] = any[], +> extends SchemaSimple { // this is not an actual member, but is needed for the recursive NormalizeNullable<> type algo _normalizeNullable(): any; // this is not an actual member, but is needed for the recursive DenormalizeNullable<> type algo diff --git a/packages/normalizr/src/types.ts b/packages/normalizr/src/types.ts index 780c146634e7..e9b31e5fc786 100644 --- a/packages/normalizr/src/types.ts +++ b/packages/normalizr/src/types.ts @@ -1,9 +1,10 @@ +import { EntityFields } from './EntityFields.js'; import type { Schema, Serializable, EntityInterface, NormalizedIndex, - SchemaClass, + Queryable, } from './interface.js'; import type WeakEntityMap from './WeakEntityMap.js'; @@ -132,3 +133,18 @@ export type NormalizedSchema = { }; export type EntityMap = Record>; + +export type SchemaArgs = + S extends EntityInterface ? EntityFields + : S extends ( + { + infer( + args: infer Args, + indexes: any, + recurse: (...args: any) => any, + entities: any, + ): any; + } + ) ? + Args + : [];