diff --git a/packages/core-data/src/entities.ts b/packages/core-data/src/entities.ts index 622f286062f7d0..ad16bd23b7ae74 100644 --- a/packages/core-data/src/entities.ts +++ b/packages/core-data/src/entities.ts @@ -13,159 +13,310 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import { addEntities } from './actions'; -import * as EntityTypes from './types'; -import { entitiesConfig, Kind, Name } from './types'; +import * as Records from './types'; +import type { Kind, Name, Context } from './types'; +import type { EntityFromConfig } from './types/helpers'; export const DEFAULT_ENTITY_KEY = 'id'; const POST_RAW_ATTRIBUTES = [ 'title', 'excerpt', 'content' ]; -export const defaultEntities = entitiesConfig( [ +export const attachment = { + name: 'media', + kind: 'root', + baseURL: '/wp/v2/media', + baseURLParams: { context: 'edit' }, + plural: 'mediaItems', + label: __( 'Media' ), +} as const; + +type AttachmentEntity< C extends Context > = EntityFromConfig< + typeof attachment, + Records.Attachment< C > +>; + +export const site = { + label: __( 'Site' ), + name: 'site', + kind: 'root', + baseURL: '/wp/v2/settings', + getTitle: ( record ) => { + return get( record, [ 'title' ], __( 'Site Title' ) ); + }, +} as const; + +type SiteEntity< C extends Context > = EntityFromConfig< + typeof site, + Records.Settings< C > +>; + +export const postType = { + label: __( 'Post Type' ), + name: 'postType', + kind: 'root', + key: 'slug', + baseURL: '/wp/v2/types', + baseURLParams: { context: 'edit' }, + rawAttributes: POST_RAW_ATTRIBUTES, +} as const; + +type TypeEntity< C extends Context > = EntityFromConfig< + typeof postType, + Records.Type< C > +>; + +export const taxonomy = { + name: 'taxonomy', + kind: 'root', + key: 'slug', + baseURL: '/wp/v2/taxonomies', + baseURLParams: { context: 'edit' }, + plural: 'taxonomies', + label: __( 'Taxonomy' ), +} as const; + +type TaxonomyEntity< C extends Context > = EntityFromConfig< + typeof taxonomy, + Records.Taxonomy< C > +>; + +export const sidebar = { + name: 'sidebar', + kind: 'root', + baseURL: '/wp/v2/sidebars', + plural: 'sidebars', + transientEdits: { blocks: true }, + label: __( 'Widget areas' ), +} as const; + +type SidebarEntity< C extends Context > = EntityFromConfig< + typeof sidebar, + Records.Sidebar< C > +>; + +export const widget = { + name: 'widget', + kind: 'root', + baseURL: '/wp/v2/widgets', + baseURLParams: { context: 'edit' }, + plural: 'widgets', + transientEdits: { blocks: true }, + label: __( 'Widgets' ), +} as const; + +type WidgetEntity< C extends Context > = EntityFromConfig< + typeof widget, + Records.Widget< C > +>; + +export const widgetType = { + name: 'widgetType', + kind: 'root', + baseURL: '/wp/v2/widget-types', + baseURLParams: { context: 'edit' }, + plural: 'widgetTypes', + label: __( 'Widget types' ), +} as const; + +type WidgetTypeEntity< C extends Context > = EntityFromConfig< + typeof widgetType, + Records.WidgetType< C > +>; + +export const user = { + label: __( 'User' ), + name: 'user', + kind: 'root', + baseURL: '/wp/v2/users', + baseURLParams: { context: 'edit' }, + plural: 'users', +} as const; + +type UserEntity< C extends Context > = EntityFromConfig< + typeof user, + Records.User< C > +>; + +export const comment = { + name: 'comment', + kind: 'root', + baseURL: '/wp/v2/comments', + baseURLParams: { context: 'edit' }, + plural: 'comments', + label: __( 'Comment' ), +} as const; + +type CommentEntity< C extends Context > = EntityFromConfig< + typeof comment, + Records.Comment< C > +>; + +export const menu = { + name: 'menu', + kind: 'root', + baseURL: '/wp/v2/menus', + baseURLParams: { context: 'edit' }, + plural: 'menus', + label: __( 'Menu' ), +} as const; + +type NavMenuEntity< C extends Context > = EntityFromConfig< + typeof menu, + Records.NavMenu< C > +>; + +export const menuItem = { + name: 'menuItem', + kind: 'root', + baseURL: '/wp/v2/menu-items', + baseURLParams: { context: 'edit' }, + plural: 'menuItems', + label: __( 'Menu Item' ), + rawAttributes: [ 'title', 'content' ], +} as const; + +type NavMenuItemEntity< C extends Context > = EntityFromConfig< + typeof menuItem, + Records.NavMenu< C > +>; + +export const menuLocation = { + name: 'menuLocation', + kind: 'root', + baseURL: '/wp/v2/menu-locations', + baseURLParams: { context: 'edit' }, + plural: 'menuLocations', + label: __( 'Menu Location' ), + key: 'name', +} as const; + +type MenuLocationEntity< C extends Context > = EntityFromConfig< + typeof menuLocation, + Records.MenuLocation< C > +>; + +export const navigationArea = { + name: 'navigationArea', + kind: 'root', + baseURL: '/wp/v2/block-navigation-areas', + baseURLParams: { context: 'edit' }, + plural: 'navigationAreas', + label: __( 'Navigation Area' ), + key: 'name', + getTitle: ( record: Records.NavigationArea< 'edit' > | null ) => + record?.description, +} as const; + +type NavigationAreaEntity< C extends Context > = EntityFromConfig< + typeof navigationArea, + Records.NavigationArea< C > +>; + +export const globalStyle = { + label: __( 'Global Styles' ), + name: 'globalStyles', + kind: 'root', + baseURL: '/wp/v2/global-styles', + baseURLParams: { context: 'edit' }, + plural: 'globalStylesVariations', // should be different than name + getTitle: ( record: any ) => record?.title?.rendered || record?.title, +} as const; + +export const theme = { + label: __( 'Themes' ), + name: 'theme', + kind: 'root', + baseURL: '/wp/v2/themes', + baseURLParams: { context: 'edit' }, + key: 'stylesheet', +} as const; + +type ThemeEntity< C extends Context > = EntityFromConfig< + typeof theme, + Records.Theme< C > +>; + +export const plugin = { + label: __( 'Plugins' ), + name: 'plugin', + kind: 'root', + baseURL: '/wp/v2/plugins', + baseURLParams: { context: 'edit' }, + key: 'plugin', +} as const; + +type PluginEntity< C extends Context > = EntityFromConfig< + typeof plugin, + Records.Plugin< C > +>; + +export const defaultEntities = [ { label: __( 'Base' ), kind: 'root', name: '__unstableBase', baseURL: '/', }, - { - label: __( 'Site' ), - name: 'site', - kind: 'root', - baseURL: '/wp/v2/settings', - getTitle: ( record ) => { - return get( record, [ 'title' ], __( 'Site Title' ) ); - }, - }, - { - label: __( 'Post Type' ), - name: 'postType', - kind: 'root', - key: 'slug', - baseURL: '/wp/v2/types', - baseURLParams: { context: 'edit' }, - rawAttributes: POST_RAW_ATTRIBUTES, - }, - { - name: 'media', - kind: 'root', - baseURL: '/wp/v2/media', - baseURLParams: { context: 'edit' }, - plural: 'mediaItems', - label: __( 'Media' ), - }, - { - name: 'taxonomy', - kind: 'root', - key: 'slug', - baseURL: '/wp/v2/taxonomies', - baseURLParams: { context: 'edit' }, - plural: 'taxonomies', - label: __( 'Taxonomy' ), - }, - { - name: 'sidebar', - kind: 'root', - baseURL: '/wp/v2/sidebars', - plural: 'sidebars', - transientEdits: { blocks: true }, - label: __( 'Widget areas' ), - }, - { - name: 'widget', - kind: 'root', - baseURL: '/wp/v2/widgets', - baseURLParams: { context: 'edit' }, - plural: 'widgets', - transientEdits: { blocks: true }, - label: __( 'Widgets' ), - }, - { - name: 'widgetType', - kind: 'root', - baseURL: '/wp/v2/widget-types', - baseURLParams: { context: 'edit' }, - plural: 'widgetTypes', - label: __( 'Widget types' ), - }, - { - label: __( 'User' ), - name: 'user', - kind: 'root', - baseURL: '/wp/v2/users', - baseURLParams: { context: 'edit' }, - plural: 'users', - }, - { - name: 'comment', - kind: 'root', - baseURL: '/wp/v2/comments', - baseURLParams: { context: 'edit' }, - plural: 'comments', - label: __( 'Comment' ), - }, - { - name: 'menu', - kind: 'root', - baseURL: '/wp/v2/menus', - baseURLParams: { context: 'edit' }, - plural: 'menus', - label: __( 'Menu' ), - }, - { - name: 'menuItem', - kind: 'root', - baseURL: '/wp/v2/menu-items', - baseURLParams: { context: 'edit' }, - plural: 'menuItems', - label: __( 'Menu Item' ), - rawAttributes: [ 'title', 'content' ], - }, - { - name: 'menuLocation', - kind: 'root', - baseURL: '/wp/v2/menu-locations', - baseURLParams: { context: 'edit' }, - plural: 'menuLocations', - label: __( 'Menu Location' ), - key: 'name', - }, - { - name: 'navigationArea', - kind: 'root', - baseURL: '/wp/v2/block-navigation-areas', - baseURLParams: { context: 'edit' }, - plural: 'navigationAreas', - label: __( 'Navigation Area' ), - key: 'name', - getTitle: ( record: EntityTypes.NavigationArea< 'edit' > | null ) => - record?.description, - }, - { - label: __( 'Global Styles' ), - name: 'globalStyles', - kind: 'root', - baseURL: '/wp/v2/global-styles', - baseURLParams: { context: 'edit' }, - plural: 'globalStylesVariations', // should be different than name - getTitle: ( record: any ) => record?.title?.rendered || record?.title, - }, - { - label: __( 'Themes' ), - name: 'theme', - kind: 'root', - baseURL: '/wp/v2/themes', - baseURLParams: { context: 'edit' }, - key: 'stylesheet', - }, - { - label: __( 'Plugins' ), - name: 'plugin', - kind: 'root', - baseURL: '/wp/v2/plugins', - baseURLParams: { context: 'edit' }, - key: 'plugin', - }, -] ); + site, + postType, + attachment, + taxonomy, + sidebar, + widget, + widgetType, + user, + comment, + menu, + menuItem, + menuLocation, + globalStyle, + theme, + plugin, +]; + +type PostTypeConfig = { + kind: 'postType'; + key: 'id'; + defaultContext: 'edit'; +}; + +type Post< C extends Context > = PostTypeConfig & { + name: 'post'; + recordType: Records.Post< C >; +}; +type Page< C extends Context > = PostTypeConfig & { + name: 'page'; + recordType: Records.Page< C >; +}; +type WpTemplate< C extends Context > = PostTypeConfig & { + name: 'wp_template'; + recordType: Records.WpTemplate< C >; +}; +type WpTemplatePart< C extends Context > = PostTypeConfig & { + name: 'wp_template_part'; + recordType: Records.WpTemplatePart< C >; +}; + +export type CoreEntity< C extends Context > = + | SiteEntity< C > + | TypeEntity< C > + | AttachmentEntity< C > + | TaxonomyEntity< C > + | SidebarEntity< C > + | WidgetEntity< C > + | WidgetTypeEntity< C > + | UserEntity< C > + | CommentEntity< C > + | NavMenuEntity< C > + | NavMenuItemEntity< C > + | NavigationAreaEntity< C > + | MenuLocationEntity< C > + | ThemeEntity< C > + | PluginEntity< C > + | Post< C > + | Page< C > + | WpTemplate< C > + | WpTemplatePart< C >; export const kinds = [ { name: 'postType', loadEntities: loadPostTypeEntities }, @@ -210,18 +361,18 @@ export const prePersistPostType = ( persistedRecord: any, edits: any ) => { async function loadPostTypeEntities() { const postTypes = ( await apiFetch( { path: '/wp/v2/types?context=view', - } ) ) as Record< string, EntityTypes.Type< 'view' > >; - return map( postTypes, ( postType, name ) => { + } ) ) as Record< string, Records.Type< 'view' > >; + return map( postTypes, ( _postType, name ) => { const isTemplate = [ 'wp_template', 'wp_template_part' ].includes( name ); - const namespace = postType?.rest_namespace ?? 'wp/v2'; + const namespace = _postType?.rest_namespace ?? 'wp/v2'; return { kind: 'postType', - baseURL: `/${ namespace }/${ postType.rest_base }`, + baseURL: `/${ namespace }/${ _postType.rest_base }`, baseURLParams: { context: 'edit' }, name, - label: postType.name, + label: _postType.name, transientEdits: { blocks: true, selection: true, @@ -233,7 +384,7 @@ async function loadPostTypeEntities() { record?.title || ( isTemplate ? startCase( record.slug ) : String( record.id ) ), __unstablePrePersist: isTemplate ? undefined : prePersistPostType, - __unstable_rest_base: postType.rest_base, + __unstable_rest_base: _postType.rest_base, }; } ); } @@ -246,15 +397,15 @@ async function loadPostTypeEntities() { async function loadTaxonomyEntities() { const taxonomies = ( await apiFetch( { path: '/wp/v2/taxonomies?context=view', - } ) ) as Array< EntityTypes.Taxonomy< 'view' > >; - return map( taxonomies, ( taxonomy, name ) => { - const namespace = taxonomy?.rest_namespace ?? 'wp/v2'; + } ) ) as Array< Records.Taxonomy< 'view' > >; + return map( taxonomies, ( _taxonomy, name ) => { + const namespace = _taxonomy?.rest_namespace ?? 'wp/v2'; return { kind: 'taxonomy', - baseURL: `/${ namespace }/${ taxonomy.rest_base }`, + baseURL: `/${ namespace }/${ _taxonomy.rest_base }`, baseURLParams: { context: 'edit' }, name, - label: taxonomy.name, + label: _taxonomy.name, }; } ); } diff --git a/packages/core-data/src/types/attachment.ts b/packages/core-data/src/types/attachment.ts index 4ad94226f0a877..e03465a8aec812 100644 --- a/packages/core-data/src/types/attachment.ts +++ b/packages/core-data/src/types/attachment.ts @@ -12,10 +12,10 @@ import { PingStatus, } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Attachment< C extends Context > { /** * The date the post was published, in the site's timezone. @@ -142,5 +142,5 @@ declare module './base-entity-types' { } export type Attachment< C extends Context > = OmitNevers< - _BaseEntityTypes.Attachment< C > + _BaseEntityRecords.Attachment< C > >; diff --git a/packages/core-data/src/types/base-entity-types.ts b/packages/core-data/src/types/base-entity-records.ts similarity index 74% rename from packages/core-data/src/types/base-entity-types.ts rename to packages/core-data/src/types/base-entity-records.ts index 790eaa63cfc9d3..813df023a11145 100644 --- a/packages/core-data/src/types/base-entity-types.ts +++ b/packages/core-data/src/types/base-entity-records.ts @@ -1,10 +1,10 @@ /** - * This module exists solely to make the BaseEntityTypes namespace extensible + * This module exists solely to make the BaseEntityRecords namespace extensible * with declaration merging: * * ```ts - * declare module './base-entity-types' { - * export namespace BaseEntityTypes { + * declare module './base-entity-records' { + * export namespace BaseEntityRecords { * export interface Comment< C extends Context > { * id: number; * // ... @@ -19,7 +19,7 @@ * ```ts * import type { Context } from '@wordpress/core-data'; * declare module '@wordpress/core-data' { - * export namespace BaseEntityTypes { + * export namespace BaseEntityRecords { * export interface Comment< C extends Context > { * numberOfViews: number; * } @@ -33,4 +33,4 @@ * // c.id is still present * ``` */ -export namespace BaseEntityTypes {} +export namespace BaseEntityRecords {} diff --git a/packages/core-data/src/types/comment.ts b/packages/core-data/src/types/comment.ts index 56cd559780770a..a4abc316dbcc52 100644 --- a/packages/core-data/src/types/comment.ts +++ b/packages/core-data/src/types/comment.ts @@ -8,12 +8,12 @@ import { OmitNevers, RenderedText, } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; export type CommentStatus = 'hold' | 'approve' | 'spam' | 'trash' | '1' | '0'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Comment< C extends Context > { /** * Unique identifier for the comment. @@ -92,5 +92,5 @@ declare module './base-entity-types' { } export type Comment< C extends Context > = OmitNevers< - _BaseEntityTypes.Comment< C > + _BaseEntityRecords.Comment< C > >; diff --git a/packages/core-data/src/types/configure.ts b/packages/core-data/src/types/configure.ts deleted file mode 100644 index 653f051c07e7eb..00000000000000 --- a/packages/core-data/src/types/configure.ts +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Internal dependencies - */ -import type { Context, CoreEntityRecord } from './'; -import * as RecordTypes from './'; - -import { defaultEntities } from '../entities'; - -// Model the configuration entry -interface EntityConfig { - label: string; - kind: string; - name: string; - baseURL: string; - plural?: string; - key?: string; - baseURLParams?: EntityQuery< Context >; - getTitle?: ( - record: CoreEntityRecord< 'edit' > | null - ) => string | undefined; - rawAttributes?: readonly string[]; - transientEdits?: { - blocks: boolean; - }; -} - -export type EntityQuery< C extends Context > = { - [ key: string ]: string; - context?: C; -}; - -// -// This implements asConst + type checking with EntityConfig interface. This is needed to -// use Extract< typeof defaultEntities, { kind: K; name: N } >. -// Inspired by the following SO answer: -// https://stackoverflow.com/questions/57069802/as-const-is-ignored-when-there-is-a-type-definition -type WithLiterals< T, L, LTuple > = T extends - | string - | number - | boolean - | null - | undefined - ? T & L - : { - [ P in keyof T ]: WithLiterals< T[ P ], L, LTuple > & - ( T[ P ] extends Array< any > ? LTuple : unknown ); - }; - -export function entitiesConfig< - LTuple extends [ unknown ] | unknown[], - L extends string | boolean | number, - T extends WithLiterals< EntityConfig[], L, LTuple > ->( o: T ): T extends Array< infer C > ? C : never { - return o as any; -} -// - -// Build the CoreEntity type using the data from entity config when possible: -type DeclaredEntity< - K extends Kind, - N extends Name, - RecordType, - E extends EntityConfig = Extract< - typeof defaultEntities, - { kind: K; name: N } - >, - PrimaryKeyField = E[ 'key' ] extends undefined ? 'id' : E[ 'key' ] -> = { - kind: K; - name: N; - recordType: RecordType; - keyType: PrimaryKeyField extends keyof RecordType - ? RecordType[ PrimaryKeyField ] - : never; - defaultContext: E[ 'baseURLParams' ][ 'context' ] extends Context - ? E[ 'baseURLParams' ][ 'context' ] - : 'view'; -}; - -type APIEntity< - K extends Kind, - N extends Name, - RecordType, - KeyName = 'id', - C = 'edit' -> = { - kind: K; - name: N; - recordType: RecordType; - keyType: KeyName extends keyof RecordType ? RecordType[ KeyName ] : number; - defaultContext: C; -}; - -type CoreEntity< C extends Context = any > = - | DeclaredEntity< 'root', 'comment', RecordTypes.Comment< C > > - | DeclaredEntity< 'root', 'site', RecordTypes.Settings< C > > - | DeclaredEntity< 'root', 'widget', RecordTypes.Widget< C > > - | DeclaredEntity< 'root', 'postType', RecordTypes.Type< C > > - | DeclaredEntity< 'root', 'media', RecordTypes.Attachment< C > > - | DeclaredEntity< 'root', 'taxonomy', RecordTypes.Taxonomy< C > > - | DeclaredEntity< 'root', 'sidebar', RecordTypes.Sidebar< C > > - | DeclaredEntity< 'root', 'widgetType', RecordTypes.WidgetType< C > > - | DeclaredEntity< 'root', 'user', RecordTypes.User< C > > - | DeclaredEntity< 'root', 'menu', RecordTypes.NavMenu< C > > - | DeclaredEntity< 'root', 'menuItem', RecordTypes.NavMenuItem< C > > - | DeclaredEntity< 'root', 'menuLocation', RecordTypes.MenuLocation< C > > - | DeclaredEntity< - 'root', - 'navigationArea', - RecordTypes.NavigationArea< C > - > - | DeclaredEntity< 'root', 'theme', RecordTypes.Theme< C > > - | DeclaredEntity< 'root', 'plugin', RecordTypes.Plugin< C > > - | APIEntity< 'postType', 'post', RecordTypes.Post< C > > - | APIEntity< 'postType', 'page', RecordTypes.Page< C > > - | APIEntity< 'postType', 'wp_template', RecordTypes.WpTemplate< C > > - | APIEntity< - 'postType', - 'wp_template_part', - RecordTypes.WpTemplatePart< C > - >; - -// Enable adding new entity types via interface merging -export interface PerPackageEntity< C extends Context > { - core: CoreEntity< C >; -} - -export type Entity< - C extends Context = any -> = PerPackageEntity< C >[ keyof PerPackageEntity< C > ]; - -// Utility types for building function signatures - -export type Kind = Entity[ 'kind' ]; -export type Name = Entity[ 'name' ]; -export type EntityRecord< - C extends Context = any -> = Entity< C >[ 'recordType' ]; - -export type RecordOf< - K extends Kind, - N extends Name, - C extends Context = any -> = Extract< Entity< C >, { kind: K; name: N } >[ 'recordType' ]; - -export type KindOf< R extends EntityRecord > = Extract< - Entity, - { recordType: R } ->[ 'kind' ]; - -export type NameOf< R extends EntityRecord > = Extract< - Entity, - { recordType: R } ->[ 'name' ]; - -export type KeyOf< R extends EntityRecord > = Extract< - Entity, - { recordType: R } ->[ 'keyType' ]; - -export type DefaultContextOf< R extends EntityRecord > = Extract< - Entity, - { recordType: R } ->[ 'defaultContext' ]; diff --git a/packages/core-data/src/types/helpers.ts b/packages/core-data/src/types/helpers.ts index 9243ef6f096d3d..6e8d33f415d7d1 100644 --- a/packages/core-data/src/types/helpers.ts +++ b/packages/core-data/src/types/helpers.ts @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { CoreEntityRecord } from './index'; +import { EntityRecord } from './index'; export interface AvatarUrls { /** @@ -148,6 +148,35 @@ export interface RenderedText< C extends Context > { * // updatablePost.title is a string * ``` */ -export type Updatable< T extends CoreEntityRecord< 'edit' > > = { +export type Updatable< T extends EntityRecord< 'edit' > > = { [ K in keyof T ]: T[ K ] extends RenderedText< any > ? string : T[ K ]; }; + +/** + * TODO Docstring + */ +export type EntityFromConfig< + E extends { + kind: string; + name: string; + key?: string; + baseURLParams?: EntityQuery< any >; + }, + R +> = { + kind: E[ 'kind' ]; + name: E[ 'name' ]; + recordType: R; + key: E[ 'key' ] extends string ? E[ 'key' ] : 'id'; + defaultContext: E[ 'baseURLParams' ] extends EntityQuery< infer C > + ? C + : 'view'; +}; + +/** + * TODO Docstring + */ +export type EntityQuery< C extends Context > = { + [ key: string ]: string; + context?: C; +}; diff --git a/packages/core-data/src/types/index.ts b/packages/core-data/src/types/index.ts index e01d87d6a31842..d7b7fb4a04587e 100644 --- a/packages/core-data/src/types/index.ts +++ b/packages/core-data/src/types/index.ts @@ -20,14 +20,16 @@ import type { Widget } from './widget'; import type { WidgetType } from './widget-type'; import type { WpTemplate } from './wp-template'; import type { WpTemplatePart } from './wp-template-part'; -import type { Context, Updatable } from './helpers'; +import type { Context, Updatable, EntityQuery } from './helpers'; +import { CoreEntity } from '../entities'; -export type { BaseEntityTypes } from './base-entity-types'; +export type { BaseEntityRecords } from './base-entity-records'; export type { Context, Updatable, Attachment, + EntityQuery, Comment, MenuLocation, NavMenu, @@ -48,25 +50,146 @@ export type { WpTemplatePart, }; -export type CoreEntityRecord< C extends Context > = - | Attachment< C > - | Comment< C > - | MenuLocation< C > - | NavMenu< C > - | NavMenuItem< C > - | NavigationArea< C > - | Page< C > - | Plugin< C > - | Post< C > - | Settings< C > - | Sidebar< C > - | Taxonomy< C > - | Theme< C > - | Type< C > - | User< C > - | Widget< C > - | WidgetType< C > - | WpTemplate< C > - | WpTemplatePart< C >; +/** + * The type that the entries of PerPackageEntities must adhere to. This is for reference only, + * there is no type checking in place. + * + * An entity is like a data type. Core-data knows how to handle data requests + * similar to `getEntityRecord( "root", "comment", 15 )` thanks to the entity configuration + * that ties the entity kind (a namespace) and name ("root" and "comment") to information + * such as the REST API endpoint URL and the primary key field. + * + * Core-data TypeScript types also associate the same kinds and names to their related + * Record data type so that calling `getEntityRecord( "root", "comment", 15 )` returns + * a list of a Comment objects. + */ +export interface EntityInterface< C extends Context > { + /** + * The namespace of the current Entity. + */ + kind: string; + /** + * The name of the current Entity, unique within the `kind` namespace. + */ + name: string; + /** + * The type of the records of the current Entity. It can be optionally parametrized + * by Context. + */ + recordType: Object; + /** + * The name of the primary key field of the current Entity. + */ + key: string; + /** + * The default value of the `context` query parameter that the related API + * endpoint assumes when no context is given. + */ + defaultContext: Context; +} + +/** + * An interface that may be extended to add types for custom Entity types. Each entry + * must be a union of types adhering to the EntityInterface type. + * + * Example: + * + * ```ts + * import type { Context } from '@wordpress/core-data'; + * + * interface Order { + * id: number; + * clientId: number; + * // ... + * } + * + * type OrderEntity = { + * kind: 'myPlugin'; + * name: 'order'; + * recordType: Order; + * } + * + * // ... + * + * declare module '@wordpress/core-data' { + * export interface PerPackageEntities< C extends Context > { + * myPlugin: OrderEntity | ClientEntity + * } + * } + * + * import type { Comment } from '@wordpress/core-data'; + * const c = getEntityRecord( 'myPlugin', 'order', 15 ); + * // c is of the type Order + * ``` + */ +export class PerPackageEntities< C extends Context > { + core: CoreEntity< C >; +} + +/** + * A union of all registered entities. + */ +type Entity< + C extends Context = any +> = PerPackageEntities< C >[ keyof PerPackageEntities< C > ]; + +/** + * A union of all known record types. + */ +export type EntityRecord< + C extends Context = any +> = Entity< C >[ 'recordType' ]; + +/** + * An entity corresponding to a specified record type. + */ +type EntityOf< R extends EntityRecord > = Extract< Entity, { recordType: R } >; + +/** + * Name of the requested entity. + */ +export type NameOf< R extends EntityRecord > = EntityOf< R >[ 'name' ]; -export * from './configure'; +/** + * Kind of the requested entity. + */ +export type KindOf< R extends EntityRecord > = EntityOf< R >[ 'kind' ]; + +/** + * Primary key type of the requested entity, sourced from PerPackageEntities. + * + * For core entities, the key type is computed using the entity configuration in entities.js. + */ +export type KeyOf< + R extends EntityRecord +> = EntityOf< R >[ 'key' ] extends keyof R + ? R[ EntityOf< R >[ 'key' ] ] + : never; + +/** + * Default context of the requested entity, sourced from PerPackageEntities. + * + * For core entities, the default context is extracted from the entity configuration + * in entities.js. + */ +export type DefaultContextOf< + R extends EntityRecord +> = EntityOf< R >[ 'defaultContext' ]; + +/** + * An entity record type associated with specified kind and name, sourced from PerPackageEntities. + */ +export type RecordOf< + K extends Kind, + N extends Name, + C extends Context = any +> = Extract< Entity< C >, { kind: K; name: N } >[ 'recordType' ]; + +/** + * A union of all known entity kinds. + */ +export type Kind = Entity[ 'kind' ]; +/** + * A union of all known entity names. + */ +export type Name = Entity[ 'name' ]; diff --git a/packages/core-data/src/types/menu-location.ts b/packages/core-data/src/types/menu-location.ts index 71fb5abab87148..1bc7ab1a0b2a41 100644 --- a/packages/core-data/src/types/menu-location.ts +++ b/packages/core-data/src/types/menu-location.ts @@ -3,10 +3,10 @@ */ import { Context, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface MenuLocation< C extends Context > { /** * The name of the menu location. @@ -25,5 +25,5 @@ declare module './base-entity-types' { } export type MenuLocation< C extends Context > = OmitNevers< - _BaseEntityTypes.MenuLocation< C > + _BaseEntityRecords.MenuLocation< C > >; diff --git a/packages/core-data/src/types/nav-menu-item.ts b/packages/core-data/src/types/nav-menu-item.ts index dab6b9ffad2f25..c5c80110a9271d 100644 --- a/packages/core-data/src/types/nav-menu-item.ts +++ b/packages/core-data/src/types/nav-menu-item.ts @@ -3,7 +3,7 @@ */ import { RenderedText, Context, ContextualField, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; export type NavMenuItemType = | 'taxonomy' @@ -18,8 +18,8 @@ export type NavMenuItemStatus = | 'private'; export type Target = '_blank' | ''; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface NavMenuItem< C extends Context > { /** * The title for the object. @@ -102,5 +102,5 @@ declare module './base-entity-types' { } export type NavMenuItem< C extends Context > = OmitNevers< - _BaseEntityTypes.NavMenuItem< C > + _BaseEntityRecords.NavMenuItem< C > >; diff --git a/packages/core-data/src/types/nav-menu.ts b/packages/core-data/src/types/nav-menu.ts index aa3417ddba04b2..3ae4e5d5e6059d 100644 --- a/packages/core-data/src/types/nav-menu.ts +++ b/packages/core-data/src/types/nav-menu.ts @@ -3,10 +3,10 @@ */ import { Context, ContextualField, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface NavMenu< C extends Context > { /** * Unique identifier for the term. @@ -49,5 +49,5 @@ declare module './base-entity-types' { } export type NavMenu< C extends Context > = OmitNevers< - _BaseEntityTypes.NavMenu< C > + _BaseEntityRecords.NavMenu< C > >; diff --git a/packages/core-data/src/types/navigation-area.ts b/packages/core-data/src/types/navigation-area.ts index ed5ae88769207e..439ed2f98e94c5 100644 --- a/packages/core-data/src/types/navigation-area.ts +++ b/packages/core-data/src/types/navigation-area.ts @@ -3,10 +3,10 @@ */ import { Context, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface NavigationArea< C extends Context > { /** * The name of the navigation area. @@ -25,5 +25,5 @@ declare module './base-entity-types' { } export type NavigationArea< C extends Context > = OmitNevers< - _BaseEntityTypes.NavigationArea< C > + _BaseEntityRecords.NavigationArea< C > >; diff --git a/packages/core-data/src/types/page.ts b/packages/core-data/src/types/page.ts index eb95206b8424e3..37698b37a3356b 100644 --- a/packages/core-data/src/types/page.ts +++ b/packages/core-data/src/types/page.ts @@ -11,10 +11,10 @@ import { OmitNevers, } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Page< C extends Context > { /** * The date the post was published, in the site's timezone. @@ -140,5 +140,5 @@ declare module './base-entity-types' { } export type Page< C extends Context > = OmitNevers< - _BaseEntityTypes.Page< C > + _BaseEntityRecords.Page< C > >; diff --git a/packages/core-data/src/types/plugin.ts b/packages/core-data/src/types/plugin.ts index 968f71f7a5bb71..1c51ba1c103c9a 100644 --- a/packages/core-data/src/types/plugin.ts +++ b/packages/core-data/src/types/plugin.ts @@ -3,10 +3,10 @@ */ import { Context, ContextualField, RenderedText, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Plugin< C extends Context > { /** * The plugin file. @@ -70,5 +70,5 @@ declare module './base-entity-types' { export type PluginStatus = 'active' | 'inactive'; export type Plugin< C extends Context > = OmitNevers< - _BaseEntityTypes.Plugin< C > + _BaseEntityRecords.Plugin< C > >; diff --git a/packages/core-data/src/types/post.ts b/packages/core-data/src/types/post.ts index 2c7ed4287f1834..aabd4fb65c7367 100644 --- a/packages/core-data/src/types/post.ts +++ b/packages/core-data/src/types/post.ts @@ -12,10 +12,10 @@ import { OmitNevers, } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Post< C extends Context > { /** * The date the post was published, in the site's timezone. @@ -149,5 +149,5 @@ declare module './base-entity-types' { } export type Post< C extends Context > = OmitNevers< - _BaseEntityTypes.Post< C > + _BaseEntityRecords.Post< C > >; diff --git a/packages/core-data/src/types/settings.ts b/packages/core-data/src/types/settings.ts index 978ce32e9b4161..cbb10cac75b086 100644 --- a/packages/core-data/src/types/settings.ts +++ b/packages/core-data/src/types/settings.ts @@ -3,10 +3,10 @@ */ import { CommentingStatus, Context, OmitNevers, PingStatus } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Settings< C extends Context > { /** * What to show on the front page @@ -89,5 +89,5 @@ declare module './base-entity-types' { } export type Settings< C extends Context > = OmitNevers< - _BaseEntityTypes.Settings< C > + _BaseEntityRecords.Settings< C > >; diff --git a/packages/core-data/src/types/sidebar.ts b/packages/core-data/src/types/sidebar.ts index 7ff4b209ea3ce4..a47e68b948a6dc 100644 --- a/packages/core-data/src/types/sidebar.ts +++ b/packages/core-data/src/types/sidebar.ts @@ -4,10 +4,10 @@ import { Widget } from './widget'; import { Context, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Sidebar< C extends Context > { /** * ID of sidebar. @@ -56,5 +56,5 @@ declare module './base-entity-types' { type SidebarStatus = 'active' | 'inactive'; export type Sidebar< C extends Context > = OmitNevers< - _BaseEntityTypes.Sidebar< C > + _BaseEntityRecords.Sidebar< C > >; diff --git a/packages/core-data/src/types/taxonomy.ts b/packages/core-data/src/types/taxonomy.ts index 120831dbd7acfb..43377f649e7062 100644 --- a/packages/core-data/src/types/taxonomy.ts +++ b/packages/core-data/src/types/taxonomy.ts @@ -3,10 +3,10 @@ */ import { Context, ContextualField, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Taxonomy< C extends Context > { /** * All capabilities used by the taxonomy. @@ -88,5 +88,5 @@ declare module './base-entity-types' { } export type Taxonomy< C extends Context > = OmitNevers< - _BaseEntityTypes.Taxonomy< C > + _BaseEntityRecords.Taxonomy< C > >; diff --git a/packages/core-data/src/types/theme.ts b/packages/core-data/src/types/theme.ts index d3e58fb994861e..3a8416ac39668a 100644 --- a/packages/core-data/src/types/theme.ts +++ b/packages/core-data/src/types/theme.ts @@ -3,10 +3,10 @@ */ import { Context, PostFormat, RenderedText, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Theme< C extends Context > { /** * The theme's stylesheet. This uniquely identifies the theme. @@ -218,5 +218,5 @@ declare module './base-entity-types' { } export type Theme< C extends Context > = OmitNevers< - _BaseEntityTypes.Theme< C > + _BaseEntityRecords.Theme< C > >; diff --git a/packages/core-data/src/types/type.ts b/packages/core-data/src/types/type.ts index 2841559eb11392..4d068098f24c75 100644 --- a/packages/core-data/src/types/type.ts +++ b/packages/core-data/src/types/type.ts @@ -3,10 +3,10 @@ */ import { Context, ContextualField, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Type< C extends Context > { /** * All capabilities used by the post type. @@ -76,5 +76,5 @@ declare module './base-entity-types' { } export type Type< C extends Context > = OmitNevers< - _BaseEntityTypes.Type< C > + _BaseEntityRecords.Type< C > >; diff --git a/packages/core-data/src/types/user.ts b/packages/core-data/src/types/user.ts index 172d369b44bbb4..8b46e2157a9a4a 100644 --- a/packages/core-data/src/types/user.ts +++ b/packages/core-data/src/types/user.ts @@ -3,10 +3,10 @@ */ import { AvatarUrls, Context, ContextualField, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface User< C extends Context > { /** * Unique identifier for the user. @@ -105,5 +105,5 @@ declare module './base-entity-types' { } export type User< C extends Context > = OmitNevers< - _BaseEntityTypes.User< C > + _BaseEntityRecords.User< C > >; diff --git a/packages/core-data/src/types/widget-type.ts b/packages/core-data/src/types/widget-type.ts index 82f65ea493141d..9d1ba90e9c8265 100644 --- a/packages/core-data/src/types/widget-type.ts +++ b/packages/core-data/src/types/widget-type.ts @@ -3,10 +3,10 @@ */ import { Context, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface WidgetType< C extends Context > { /** * Unique slug identifying the widget type. @@ -33,5 +33,5 @@ declare module './base-entity-types' { } export type WidgetType< C extends Context > = OmitNevers< - _BaseEntityTypes.WidgetType< C > + _BaseEntityRecords.WidgetType< C > >; diff --git a/packages/core-data/src/types/widget.ts b/packages/core-data/src/types/widget.ts index f1b5a489e92976..28c3d64983b3ab 100644 --- a/packages/core-data/src/types/widget.ts +++ b/packages/core-data/src/types/widget.ts @@ -3,10 +3,10 @@ */ import { Context, ContextualField, OmitNevers } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface Widget< C extends Context > { /** * Unique identifier for the widget. @@ -60,5 +60,5 @@ declare module './base-entity-types' { } export type Widget< C extends Context > = OmitNevers< - _BaseEntityTypes.Widget< C > + _BaseEntityRecords.Widget< C > >; diff --git a/packages/core-data/src/types/wp-template-part.ts b/packages/core-data/src/types/wp-template-part.ts index 36fc0509003383..ef208f36128745 100644 --- a/packages/core-data/src/types/wp-template-part.ts +++ b/packages/core-data/src/types/wp-template-part.ts @@ -9,10 +9,10 @@ import { ContextualField, } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface WpTemplatePart< C extends Context > { /** * ID of template. @@ -90,5 +90,5 @@ declare module './base-entity-types' { } export type WpTemplatePart< C extends Context > = OmitNevers< - _BaseEntityTypes.WpTemplatePart< C > + _BaseEntityRecords.WpTemplatePart< C > >; diff --git a/packages/core-data/src/types/wp-template.ts b/packages/core-data/src/types/wp-template.ts index 33fa31787792a8..6dc687f4cd7a79 100644 --- a/packages/core-data/src/types/wp-template.ts +++ b/packages/core-data/src/types/wp-template.ts @@ -9,10 +9,10 @@ import { ContextualField, } from './helpers'; -import { BaseEntityTypes as _BaseEntityTypes } from './base-entity-types'; +import { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records'; -declare module './base-entity-types' { - export namespace BaseEntityTypes { +declare module './base-entity-records' { + export namespace BaseEntityRecords { export interface WpTemplate< C extends Context > { /** * ID of template. @@ -90,5 +90,5 @@ declare module './base-entity-types' { } export type WpTemplate< C extends Context > = OmitNevers< - _BaseEntityTypes.WpTemplate< C > + _BaseEntityRecords.WpTemplate< C > >;