@wordpress/core-data 6.30.0 → 6.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/actions.js +5 -5
  3. package/build/actions.js.map +1 -1
  4. package/build/entities.js +84 -44
  5. package/build/entities.js.map +1 -1
  6. package/build/hooks/use-query-select.js +15 -16
  7. package/build/hooks/use-query-select.js.map +1 -1
  8. package/build/index.js +16 -10
  9. package/build/index.js.map +1 -1
  10. package/build/queried-data/selectors.js +9 -0
  11. package/build/queried-data/selectors.js.map +1 -1
  12. package/build/resolvers.js +7 -6
  13. package/build/resolvers.js.map +1 -1
  14. package/build/selectors.js +5 -0
  15. package/build/selectors.js.map +1 -1
  16. package/build-module/actions.js +5 -5
  17. package/build-module/actions.js.map +1 -1
  18. package/build-module/entities.js +84 -44
  19. package/build-module/entities.js.map +1 -1
  20. package/build-module/hooks/use-query-select.js +15 -16
  21. package/build-module/hooks/use-query-select.js.map +1 -1
  22. package/build-module/index.js +17 -11
  23. package/build-module/index.js.map +1 -1
  24. package/build-module/queried-data/selectors.js +8 -0
  25. package/build-module/queried-data/selectors.js.map +1 -1
  26. package/build-module/resolvers.js +7 -6
  27. package/build-module/resolvers.js.map +1 -1
  28. package/build-module/selectors.js +6 -1
  29. package/build-module/selectors.js.map +1 -1
  30. package/build-types/entities.d.ts +26 -52
  31. package/build-types/entities.d.ts.map +1 -1
  32. package/build-types/index.d.ts.map +1 -1
  33. package/build-types/queried-data/selectors.d.ts +1 -0
  34. package/build-types/queried-data/selectors.d.ts.map +1 -1
  35. package/build-types/resolvers.d.ts.map +1 -1
  36. package/build-types/selectors.d.ts.map +1 -1
  37. package/package.json +17 -17
  38. package/src/actions.js +5 -5
  39. package/src/entities.js +84 -54
  40. package/src/hooks/test/use-entity-record.js +3 -0
  41. package/src/hooks/test/use-entity-records.js +2 -0
  42. package/src/hooks/test/use-query-select.js +3 -0
  43. package/src/hooks/use-query-select.ts +21 -16
  44. package/src/index.js +27 -13
  45. package/src/queried-data/selectors.js +6 -0
  46. package/src/resolvers.js +8 -5
  47. package/src/selectors.ts +10 -1
  48. package/src/test/entities.js +19 -10
  49. package/tsconfig.json +1 -0
  50. package/tsconfig.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/core-data",
3
- "version": "6.30.0",
3
+ "version": "6.31.0",
4
4
  "description": "Access to and manipulation of core WordPress entities.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -31,21 +31,21 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "@babel/runtime": "^7.16.0",
34
- "@wordpress/api-fetch": "^6.50.0",
35
- "@wordpress/block-editor": "^12.21.0",
36
- "@wordpress/blocks": "^12.30.0",
37
- "@wordpress/compose": "^6.30.0",
38
- "@wordpress/data": "^9.23.0",
39
- "@wordpress/deprecated": "^3.53.0",
40
- "@wordpress/element": "^5.30.0",
41
- "@wordpress/html-entities": "^3.53.0",
42
- "@wordpress/i18n": "^4.53.0",
43
- "@wordpress/is-shallow-equal": "^4.53.0",
44
- "@wordpress/private-apis": "^0.35.0",
45
- "@wordpress/rich-text": "^6.30.0",
46
- "@wordpress/sync": "^0.15.0",
47
- "@wordpress/undo-manager": "^0.13.0",
48
- "@wordpress/url": "^3.54.0",
34
+ "@wordpress/api-fetch": "^6.51.0",
35
+ "@wordpress/block-editor": "^12.22.0",
36
+ "@wordpress/blocks": "^12.31.0",
37
+ "@wordpress/compose": "^6.31.0",
38
+ "@wordpress/data": "^9.24.0",
39
+ "@wordpress/deprecated": "^3.54.0",
40
+ "@wordpress/element": "^5.31.0",
41
+ "@wordpress/html-entities": "^3.54.0",
42
+ "@wordpress/i18n": "^4.54.0",
43
+ "@wordpress/is-shallow-equal": "^4.54.0",
44
+ "@wordpress/private-apis": "^0.36.0",
45
+ "@wordpress/rich-text": "^6.31.0",
46
+ "@wordpress/sync": "^0.16.0",
47
+ "@wordpress/undo-manager": "^0.14.0",
48
+ "@wordpress/url": "^3.55.0",
49
49
  "change-case": "^4.1.2",
50
50
  "equivalent-key-map": "^0.2.2",
51
51
  "fast-deep-equal": "^3.1.3",
@@ -60,5 +60,5 @@
60
60
  "publishConfig": {
61
61
  "access": "public"
62
62
  },
63
- "gitHead": "ac3c3e465a083081a86a4da6ee6fb817b41e5130"
63
+ "gitHead": "ffc07735d0abfb3f69e91d48f25b7fe8d1ef92d2"
64
64
  }
package/src/actions.js CHANGED
@@ -286,7 +286,7 @@ export const deleteEntityRecord =
286
286
  { __unstableFetch = apiFetch, throwOnError = false } = {}
287
287
  ) =>
288
288
  async ( { dispatch } ) => {
289
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
289
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
290
290
  const entityConfig = configs.find(
291
291
  ( config ) => config.kind === kind && config.name === name
292
292
  );
@@ -503,7 +503,7 @@ export const saveEntityRecord =
503
503
  } = {}
504
504
  ) =>
505
505
  async ( { select, resolveSelect, dispatch } ) => {
506
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
506
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
507
507
  const entityConfig = configs.find(
508
508
  ( config ) => config.kind === kind && config.name === name
509
509
  );
@@ -780,7 +780,7 @@ export const saveEditedEntityRecord =
780
780
  if ( ! select.hasEditsForEntityRecord( kind, name, recordId ) ) {
781
781
  return;
782
782
  }
783
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
783
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
784
784
  const entityConfig = configs.find(
785
785
  ( config ) => config.kind === kind && config.name === name
786
786
  );
@@ -824,7 +824,7 @@ export const __experimentalSaveSpecifiedEntityEdits =
824
824
  setNestedValue( editsToSave, item, getNestedValue( edits, item ) );
825
825
  }
826
826
 
827
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
827
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
828
828
  const entityConfig = configs.find(
829
829
  ( config ) => config.kind === kind && config.name === name
830
830
  );
@@ -948,7 +948,7 @@ export function receiveDefaultTemplateId( query, templateId ) {
948
948
  export const receiveRevisions =
949
949
  ( kind, name, recordKey, records, query, invalidateCache = false, meta ) =>
950
950
  async ( { dispatch } ) => {
951
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
951
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
952
952
  const entityConfig = configs.find(
953
953
  ( config ) => config.kind === kind && config.name === name
954
954
  );
package/src/entities.js CHANGED
@@ -39,6 +39,9 @@ export const rootEntitiesConfig = [
39
39
  'url',
40
40
  ].join( ',' ),
41
41
  },
42
+ // The entity doesn't support selecting multiple records.
43
+ // The property is maintained for backward compatibility.
44
+ plural: '__unstableBases',
42
45
  syncConfig: {
43
46
  fetch: async () => {
44
47
  return apiFetch( { path: '/' } );
@@ -58,33 +61,6 @@ export const rootEntitiesConfig = [
58
61
  syncObjectType: 'root/base',
59
62
  getSyncObjectId: () => 'index',
60
63
  },
61
- {
62
- label: __( 'Site' ),
63
- name: 'site',
64
- kind: 'root',
65
- baseURL: '/wp/v2/settings',
66
- getTitle: ( record ) => {
67
- return record?.title ?? __( 'Site Title' );
68
- },
69
- syncConfig: {
70
- fetch: async () => {
71
- return apiFetch( { path: '/wp/v2/settings' } );
72
- },
73
- applyChangesToDoc: ( doc, changes ) => {
74
- const document = doc.getMap( 'document' );
75
- Object.entries( changes ).forEach( ( [ key, value ] ) => {
76
- if ( document.get( key ) !== value ) {
77
- document.set( key, value );
78
- }
79
- } );
80
- },
81
- fromCRDTDoc: ( doc ) => {
82
- return doc.getMap( 'document' ).toJSON();
83
- },
84
- },
85
- syncObjectType: 'root/site',
86
- getSyncObjectId: () => 'index',
87
- },
88
64
  {
89
65
  label: __( 'Post Type' ),
90
66
  name: 'postType',
@@ -92,6 +68,7 @@ export const rootEntitiesConfig = [
92
68
  key: 'slug',
93
69
  baseURL: '/wp/v2/types',
94
70
  baseURLParams: { context: 'edit' },
71
+ plural: 'postTypes',
95
72
  syncConfig: {
96
73
  fetch: async ( id ) => {
97
74
  return apiFetch( {
@@ -220,6 +197,7 @@ export const rootEntitiesConfig = [
220
197
  kind: 'root',
221
198
  baseURL: '/wp/v2/themes',
222
199
  baseURLParams: { context: 'edit' },
200
+ plural: 'themes',
223
201
  key: 'stylesheet',
224
202
  },
225
203
  {
@@ -228,6 +206,7 @@ export const rootEntitiesConfig = [
228
206
  kind: 'root',
229
207
  baseURL: '/wp/v2/plugins',
230
208
  baseURLParams: { context: 'edit' },
209
+ plural: 'plugins',
231
210
  key: 'plugin',
232
211
  },
233
212
  {
@@ -244,6 +223,12 @@ export const rootEntitiesConfig = [
244
223
  export const additionalEntityConfigLoaders = [
245
224
  { kind: 'postType', loadEntities: loadPostTypeEntities },
246
225
  { kind: 'taxonomy', loadEntities: loadTaxonomyEntities },
226
+ {
227
+ kind: 'root',
228
+ name: 'site',
229
+ plural: 'sites',
230
+ loadEntities: loadSiteEntity,
231
+ },
247
232
  ];
248
233
 
249
234
  /**
@@ -401,39 +386,76 @@ async function loadTaxonomyEntities() {
401
386
  }
402
387
 
403
388
  /**
404
- * Returns the entity's getter method name given its kind and name.
389
+ * Returns the Site entity.
390
+ *
391
+ * @return {Promise} Entity promise
392
+ */
393
+ async function loadSiteEntity() {
394
+ const entity = {
395
+ label: __( 'Site' ),
396
+ name: 'site',
397
+ kind: 'root',
398
+ baseURL: '/wp/v2/settings',
399
+ syncConfig: {
400
+ fetch: async () => {
401
+ return apiFetch( { path: '/wp/v2/settings' } );
402
+ },
403
+ applyChangesToDoc: ( doc, changes ) => {
404
+ const document = doc.getMap( 'document' );
405
+ Object.entries( changes ).forEach( ( [ key, value ] ) => {
406
+ if ( document.get( key ) !== value ) {
407
+ document.set( key, value );
408
+ }
409
+ } );
410
+ },
411
+ fromCRDTDoc: ( doc ) => {
412
+ return doc.getMap( 'document' ).toJSON();
413
+ },
414
+ },
415
+ syncObjectType: 'root/site',
416
+ getSyncObjectId: () => 'index',
417
+ meta: {},
418
+ };
419
+
420
+ const site = await apiFetch( {
421
+ path: entity.baseURL,
422
+ method: 'OPTIONS',
423
+ } );
424
+
425
+ const labels = {};
426
+ Object.entries( site?.schema?.properties ?? {} ).forEach(
427
+ ( [ key, value ] ) => {
428
+ // Ignore properties `title` and `type` keys.
429
+ if ( typeof value === 'object' && value.title ) {
430
+ labels[ key ] = value.title;
431
+ }
432
+ }
433
+ );
434
+
435
+ return [ { ...entity, meta: { labels } } ];
436
+ }
437
+
438
+ /**
439
+ * Returns the entity's getter method name given its kind and name or plural name.
405
440
  *
406
441
  * @example
407
442
  * ```js
408
443
  * const nameSingular = getMethodName( 'root', 'theme', 'get' );
409
444
  * // nameSingular is getRootTheme
410
445
  *
411
- * const namePlural = getMethodName( 'root', 'theme', 'set' );
446
+ * const namePlural = getMethodName( 'root', 'themes', 'set' );
412
447
  * // namePlural is setRootThemes
413
448
  * ```
414
449
  *
415
- * @param {string} kind Entity kind.
416
- * @param {string} name Entity name.
417
- * @param {string} prefix Function prefix.
418
- * @param {boolean} usePlural Whether to use the plural form or not.
450
+ * @param {string} kind Entity kind.
451
+ * @param {string} name Entity name or plural name.
452
+ * @param {string} prefix Function prefix.
419
453
  *
420
454
  * @return {string} Method name
421
455
  */
422
- export const getMethodName = (
423
- kind,
424
- name,
425
- prefix = 'get',
426
- usePlural = false
427
- ) => {
428
- const entityConfig = rootEntitiesConfig.find(
429
- ( config ) => config.kind === kind && config.name === name
430
- );
456
+ export const getMethodName = ( kind, name, prefix = 'get' ) => {
431
457
  const kindPrefix = kind === 'root' ? '' : pascalCase( kind );
432
- const nameSuffix = pascalCase( name ) + ( usePlural ? 's' : '' );
433
- const suffix =
434
- usePlural && 'plural' in entityConfig && entityConfig?.plural
435
- ? pascalCase( entityConfig.plural )
436
- : nameSuffix;
458
+ const suffix = pascalCase( name );
437
459
  return `${ prefix }${ kindPrefix }${ suffix }`;
438
460
  };
439
461
 
@@ -447,17 +469,21 @@ function registerSyncConfigs( configs ) {
447
469
  }
448
470
 
449
471
  /**
450
- * Loads the kind entities into the store.
472
+ * Loads the entities into the store.
451
473
  *
452
- * @param {string} kind Kind
474
+ * Note: The `name` argument is used for `root` entities requiring additional server data.
453
475
  *
476
+ * @param {string} kind Kind
477
+ * @param {string} name Name
454
478
  * @return {(thunkArgs: object) => Promise<Array>} Entities
455
479
  */
456
480
  export const getOrLoadEntitiesConfig =
457
- ( kind ) =>
481
+ ( kind, name ) =>
458
482
  async ( { select, dispatch } ) => {
459
483
  let configs = select.getEntitiesConfig( kind );
460
- if ( configs && configs.length !== 0 ) {
484
+ const hasConfig = !! select.getEntityConfig( kind, name );
485
+
486
+ if ( configs?.length > 0 && hasConfig ) {
461
487
  if ( window.__experimentalEnableSync ) {
462
488
  if ( process.env.IS_GUTENBERG_PLUGIN ) {
463
489
  registerSyncConfigs( configs );
@@ -467,9 +493,13 @@ export const getOrLoadEntitiesConfig =
467
493
  return configs;
468
494
  }
469
495
 
470
- const loader = additionalEntityConfigLoaders.find(
471
- ( l ) => l.kind === kind
472
- );
496
+ const loader = additionalEntityConfigLoaders.find( ( l ) => {
497
+ if ( ! name || ! l.name ) {
498
+ return l.kind === kind;
499
+ }
500
+
501
+ return l.kind === kind && l.name === name;
502
+ } );
473
503
  if ( ! loader ) {
474
504
  return [];
475
505
  }
@@ -51,6 +51,7 @@ describe( 'useEntityRecord', () => {
51
51
  record: undefined,
52
52
  save: expect.any( Function ),
53
53
  hasResolved: false,
54
+ hasStarted: false,
54
55
  isResolving: false,
55
56
  status: 'IDLE',
56
57
  } );
@@ -70,6 +71,7 @@ describe( 'useEntityRecord', () => {
70
71
  record: { hello: 'world', id: 1 },
71
72
  save: expect.any( Function ),
72
73
  hasResolved: true,
74
+ hasStarted: true,
73
75
  isResolving: false,
74
76
  status: 'SUCCESS',
75
77
  } );
@@ -99,6 +101,7 @@ describe( 'useEntityRecord', () => {
99
101
  record: { hello: 'world', id: 1 },
100
102
  save: expect.any( Function ),
101
103
  hasResolved: true,
104
+ hasStarted: true,
102
105
  isResolving: false,
103
106
  status: 'SUCCESS',
104
107
  } )
@@ -49,6 +49,7 @@ describe( 'useEntityRecords', () => {
49
49
  expect( data ).toEqual( {
50
50
  records: null,
51
51
  hasResolved: false,
52
+ hasStarted: false,
52
53
  isResolving: false,
53
54
  status: 'IDLE',
54
55
  totalItems: null,
@@ -65,6 +66,7 @@ describe( 'useEntityRecords', () => {
65
66
  expect( data ).toEqual( {
66
67
  records: TEST_RECORDS,
67
68
  hasResolved: true,
69
+ hasStarted: true,
68
70
  isResolving: false,
69
71
  status: 'SUCCESS',
70
72
  totalItems: null,
@@ -116,6 +116,7 @@ describe( 'useQuerySelect', () => {
116
116
  data: 'bar',
117
117
  isResolving: false,
118
118
  hasResolved: false,
119
+ hasStarted: false,
119
120
  status: 'IDLE',
120
121
  } );
121
122
  } );
@@ -165,6 +166,7 @@ describe( 'useQuerySelect', () => {
165
166
  data: 10,
166
167
  isResolving: false,
167
168
  hasResolved: false,
169
+ hasStarted: false,
168
170
  status: 'IDLE',
169
171
  } );
170
172
 
@@ -180,6 +182,7 @@ describe( 'useQuerySelect', () => {
180
182
  data: 15,
181
183
  isResolving: false,
182
184
  hasResolved: true,
185
+ hasStarted: true,
183
186
  status: 'SUCCESS',
184
187
  } )
185
188
  );
@@ -21,7 +21,7 @@ interface QuerySelectResponse< Data > {
21
21
  /** the requested selector return value */
22
22
  data: Data;
23
23
 
24
- /** is the record still being resolved? Via the `getIsResolving` meta-selector */
24
+ /** is the record still being resolved? Via the `isResolving` meta-selector */
25
25
  isResolving: boolean;
26
26
 
27
27
  /** was the resolution started? Via the `hasStartedResolution` meta-selector */
@@ -108,31 +108,36 @@ const enrichSelectors = memoize( ( ( selectors ) => {
108
108
  get:
109
109
  () =>
110
110
  ( ...args: unknown[] ) => {
111
- const { getIsResolving, hasFinishedResolution } = selectors;
112
- const isResolving = !! getIsResolving( selectorName, args );
113
- const hasResolved =
114
- ! isResolving &&
115
- hasFinishedResolution( selectorName, args );
116
111
  const data = selectors[ selectorName ]( ...args );
112
+ const resolutionStatus = selectors.getResolutionState(
113
+ selectorName,
114
+ args
115
+ )?.status;
117
116
 
118
117
  let status;
119
- if ( isResolving ) {
120
- status = Status.Resolving;
121
- } else if ( hasResolved ) {
122
- if ( data ) {
118
+ switch ( resolutionStatus ) {
119
+ case 'resolving':
120
+ status = Status.Resolving;
121
+ break;
122
+ case 'finished':
123
123
  status = Status.Success;
124
- } else {
124
+ break;
125
+ case 'error':
125
126
  status = Status.Error;
126
- }
127
- } else {
128
- status = Status.Idle;
127
+ break;
128
+ case undefined:
129
+ status = Status.Idle;
130
+ break;
129
131
  }
130
132
 
131
133
  return {
132
134
  data,
133
135
  status,
134
- isResolving,
135
- hasResolved,
136
+ isResolving: status === Status.Resolving,
137
+ hasStarted: status !== Status.Idle,
138
+ hasResolved:
139
+ status === Status.Success ||
140
+ status === Status.Error,
136
141
  };
137
142
  },
138
143
  } );
package/src/index.js CHANGED
@@ -12,7 +12,11 @@ import * as privateSelectors from './private-selectors';
12
12
  import * as actions from './actions';
13
13
  import * as resolvers from './resolvers';
14
14
  import createLocksActions from './locks/actions';
15
- import { rootEntitiesConfig, getMethodName } from './entities';
15
+ import {
16
+ rootEntitiesConfig,
17
+ additionalEntityConfigLoaders,
18
+ getMethodName,
19
+ } from './entities';
16
20
  import { STORE_NAME } from './name';
17
21
  import { unlock } from './private-apis';
18
22
 
@@ -20,29 +24,39 @@ import { unlock } from './private-apis';
20
24
  // (getEntityRecord, getEntityRecords, updateEntityRecord, updateEntityRecords)
21
25
  // Instead of getEntityRecord, the consumer could use more user-friendly named selector: getPostType, getTaxonomy...
22
26
  // The "kind" and the "name" of the entity are combined to generate these shortcuts.
27
+ const entitiesConfig = [
28
+ ...rootEntitiesConfig,
29
+ ...additionalEntityConfigLoaders.filter( ( config ) => !! config.name ),
30
+ ];
23
31
 
24
- const entitySelectors = rootEntitiesConfig.reduce( ( result, entity ) => {
25
- const { kind, name } = entity;
32
+ const entitySelectors = entitiesConfig.reduce( ( result, entity ) => {
33
+ const { kind, name, plural } = entity;
26
34
  result[ getMethodName( kind, name ) ] = ( state, key, query ) =>
27
35
  selectors.getEntityRecord( state, kind, name, key, query );
28
- result[ getMethodName( kind, name, 'get', true ) ] = ( state, query ) =>
29
- selectors.getEntityRecords( state, kind, name, query );
36
+
37
+ if ( plural ) {
38
+ result[ getMethodName( kind, plural, 'get' ) ] = ( state, query ) =>
39
+ selectors.getEntityRecords( state, kind, name, query );
40
+ }
30
41
  return result;
31
42
  }, {} );
32
43
 
33
- const entityResolvers = rootEntitiesConfig.reduce( ( result, entity ) => {
34
- const { kind, name } = entity;
44
+ const entityResolvers = entitiesConfig.reduce( ( result, entity ) => {
45
+ const { kind, name, plural } = entity;
35
46
  result[ getMethodName( kind, name ) ] = ( key, query ) =>
36
47
  resolvers.getEntityRecord( kind, name, key, query );
37
- const pluralMethodName = getMethodName( kind, name, 'get', true );
38
- result[ pluralMethodName ] = ( ...args ) =>
39
- resolvers.getEntityRecords( kind, name, ...args );
40
- result[ pluralMethodName ].shouldInvalidate = ( action ) =>
41
- resolvers.getEntityRecords.shouldInvalidate( action, kind, name );
48
+
49
+ if ( plural ) {
50
+ const pluralMethodName = getMethodName( kind, plural, 'get' );
51
+ result[ pluralMethodName ] = ( ...args ) =>
52
+ resolvers.getEntityRecords( kind, name, ...args );
53
+ result[ pluralMethodName ].shouldInvalidate = ( action ) =>
54
+ resolvers.getEntityRecords.shouldInvalidate( action, kind, name );
55
+ }
42
56
  return result;
43
57
  }, {} );
44
58
 
45
- const entityActions = rootEntitiesConfig.reduce( ( result, entity ) => {
59
+ const entityActions = entitiesConfig.reduce( ( result, entity ) => {
46
60
  const { kind, name } = entity;
47
61
  result[ getMethodName( kind, name, 'save' ) ] = ( record, options ) =>
48
62
  actions.saveEntityRecord( kind, name, record, options );
@@ -126,3 +126,9 @@ export function getQueriedTotalItems( state, query = {} ) {
126
126
 
127
127
  return state.queries?.[ context ]?.[ stableKey ]?.meta?.totalItems ?? null;
128
128
  }
129
+
130
+ export function getQueriedTotalPages( state, query = {} ) {
131
+ const { stableKey, context } = getQueryParts( query );
132
+
133
+ return state.queries?.[ context ]?.[ stableKey ]?.meta?.totalPages ?? null;
134
+ }
package/src/resolvers.js CHANGED
@@ -59,7 +59,7 @@ export const getCurrentUser =
59
59
  export const getEntityRecord =
60
60
  ( kind, name, key = '', query ) =>
61
61
  async ( { select, dispatch } ) => {
62
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
62
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
63
63
  const entityConfig = configs.find(
64
64
  ( config ) => config.name === name && config.kind === kind
65
65
  );
@@ -194,7 +194,7 @@ export const getEditedEntityRecord = forwardResolver( 'getEntityRecord' );
194
194
  export const getEntityRecords =
195
195
  ( kind, name, query = {} ) =>
196
196
  async ( { dispatch } ) => {
197
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
197
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
198
198
  const entityConfig = configs.find(
199
199
  ( config ) => config.name === name && config.kind === kind
200
200
  );
@@ -238,6 +238,9 @@ export const getEntityRecords =
238
238
  totalItems: parseInt(
239
239
  response.headers.get( 'X-WP-Total' )
240
240
  ),
241
+ totalPages: parseInt(
242
+ response.headers.get( 'X-WP-TotalPages' )
243
+ ),
241
244
  };
242
245
  } else {
243
246
  records = Object.values( await apiFetch( { path } ) );
@@ -426,7 +429,7 @@ export const canUser =
426
429
  export const canUserEditEntityRecord =
427
430
  ( kind, name, recordId ) =>
428
431
  async ( { dispatch } ) => {
429
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
432
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
430
433
  const entityConfig = configs.find(
431
434
  ( config ) => config.name === name && config.kind === kind
432
435
  );
@@ -723,7 +726,7 @@ export const getDefaultTemplateId =
723
726
  export const getRevisions =
724
727
  ( kind, name, recordKey, query = {} ) =>
725
728
  async ( { dispatch } ) => {
726
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
729
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
727
730
  const entityConfig = configs.find(
728
731
  ( config ) => config.name === name && config.kind === kind
729
732
  );
@@ -848,7 +851,7 @@ getRevisions.shouldInvalidate = ( action, kind, name, recordKey ) =>
848
851
  export const getRevision =
849
852
  ( kind, name, recordKey, revisionKey, query ) =>
850
853
  async ( { dispatch } ) => {
851
- const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
854
+ const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
852
855
  const entityConfig = configs.find(
853
856
  ( config ) => config.name === name && config.kind === kind
854
857
  );
package/src/selectors.ts CHANGED
@@ -14,7 +14,11 @@ import deprecated from '@wordpress/deprecated';
14
14
  * Internal dependencies
15
15
  */
16
16
  import { STORE_NAME } from './name';
17
- import { getQueriedItems, getQueriedTotalItems } from './queried-data';
17
+ import {
18
+ getQueriedItems,
19
+ getQueriedTotalItems,
20
+ getQueriedTotalPages,
21
+ } from './queried-data';
18
22
  import { DEFAULT_ENTITY_KEY } from './entities';
19
23
  import {
20
24
  getNormalizedCommaSeparable,
@@ -623,6 +627,11 @@ export const getEntityRecordsTotalPages = (
623
627
  if ( query.per_page === -1 ) return 1;
624
628
  const totalItems = getQueriedTotalItems( queriedState, query );
625
629
  if ( ! totalItems ) return totalItems;
630
+ // If `per_page` is not set and the query relies on the defaults of the
631
+ // REST endpoint, get the info from query's meta.
632
+ if ( ! query.per_page ) {
633
+ return getQueriedTotalPages( queriedState, query );
634
+ }
626
635
  return Math.ceil( totalItems / query.per_page );
627
636
  };
628
637
 
@@ -27,14 +27,8 @@ describe( 'getMethodName', () => {
27
27
  expect( methodName ).toEqual( 'setPostType' );
28
28
  } );
29
29
 
30
- it( 'should use the plural form', () => {
31
- const methodName = getMethodName( 'root', 'postType', 'get', true );
32
-
33
- expect( methodName ).toEqual( 'getPostTypes' );
34
- } );
35
-
36
30
  it( 'should use the given plural form', () => {
37
- const methodName = getMethodName( 'root', 'taxonomy', 'get', true );
31
+ const methodName = getMethodName( 'root', 'taxonomies', 'get' );
38
32
 
39
33
  expect( methodName ).toEqual( 'getTaxonomies' );
40
34
  } );
@@ -58,9 +52,16 @@ describe( 'getKindEntities', () => {
58
52
  const dispatch = jest.fn();
59
53
  const select = {
60
54
  getEntitiesConfig: jest.fn( () => entities ),
55
+ getEntityConfig: jest.fn( () => ( {
56
+ kind: 'postType',
57
+ name: 'post',
58
+ } ) ),
61
59
  };
62
60
  const entities = [ { kind: 'postType' } ];
63
- await getOrLoadEntitiesConfig( 'postType' )( { dispatch, select } );
61
+ await getOrLoadEntitiesConfig(
62
+ 'postType',
63
+ 'post'
64
+ )( { dispatch, select } );
64
65
  expect( dispatch ).not.toHaveBeenCalled();
65
66
  } );
66
67
 
@@ -68,8 +69,12 @@ describe( 'getKindEntities', () => {
68
69
  const dispatch = jest.fn();
69
70
  const select = {
70
71
  getEntitiesConfig: jest.fn( () => [] ),
72
+ getEntityConfig: jest.fn( () => undefined ),
71
73
  };
72
- await getOrLoadEntitiesConfig( 'unknownKind' )( { dispatch, select } );
74
+ await getOrLoadEntitiesConfig(
75
+ 'unknownKind',
76
+ undefined
77
+ )( { dispatch, select } );
73
78
  expect( dispatch ).not.toHaveBeenCalled();
74
79
  } );
75
80
 
@@ -88,10 +93,14 @@ describe( 'getKindEntities', () => {
88
93
  const dispatch = jest.fn();
89
94
  const select = {
90
95
  getEntitiesConfig: jest.fn( () => [] ),
96
+ getEntityConfig: jest.fn( () => undefined ),
91
97
  };
92
98
  triggerFetch.mockImplementation( () => fetchedEntities );
93
99
 
94
- await getOrLoadEntitiesConfig( 'postType' )( { dispatch, select } );
100
+ await getOrLoadEntitiesConfig(
101
+ 'postType',
102
+ 'post'
103
+ )( { dispatch, select } );
95
104
  expect( dispatch ).toHaveBeenCalledTimes( 1 );
96
105
  expect( dispatch.mock.calls[ 0 ][ 0 ].type ).toBe( 'ADD_ENTITIES' );
97
106
  expect( dispatch.mock.calls[ 0 ][ 0 ].entities.length ).toBe( 1 );
package/tsconfig.json CHANGED
@@ -1,4 +1,5 @@
1
1
  {
2
+ "$schema": "https://json.schemastore.org/tsconfig.json",
2
3
  "extends": "../../tsconfig.base.json",
3
4
  "compilerOptions": {
4
5
  "rootDir": "src",