@wordpress/core-data 7.40.2-next.v.202602271551.0 → 7.40.2

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 (182) hide show
  1. package/README.md +0 -27
  2. package/build/actions.cjs +9 -26
  3. package/build/actions.cjs.map +2 -2
  4. package/build/awareness/post-editor-awareness.cjs +45 -6
  5. package/build/awareness/post-editor-awareness.cjs.map +2 -2
  6. package/build/awareness/types.cjs.map +1 -1
  7. package/build/entities.cjs +36 -16
  8. package/build/entities.cjs.map +2 -2
  9. package/build/entity-provider.cjs +15 -6
  10. package/build/entity-provider.cjs.map +2 -2
  11. package/build/hooks/use-entity-prop.cjs +33 -2
  12. package/build/hooks/use-entity-prop.cjs.map +2 -2
  13. package/build/hooks/use-post-editor-awareness-state.cjs +120 -1
  14. package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
  15. package/build/index.cjs +0 -3
  16. package/build/index.cjs.map +2 -2
  17. package/build/private-actions.cjs +27 -3
  18. package/build/private-actions.cjs.map +2 -2
  19. package/build/private-apis.cjs +14 -5
  20. package/build/private-apis.cjs.map +3 -3
  21. package/build/private-selectors.cjs +22 -2
  22. package/build/private-selectors.cjs.map +2 -2
  23. package/build/queried-data/actions.cjs +1 -1
  24. package/build/queried-data/actions.cjs.map +2 -2
  25. package/build/queried-data/reducer.cjs +19 -13
  26. package/build/queried-data/reducer.cjs.map +2 -2
  27. package/build/reducer.cjs +19 -2
  28. package/build/reducer.cjs.map +2 -2
  29. package/build/resolvers.cjs +24 -17
  30. package/build/resolvers.cjs.map +2 -2
  31. package/build/selectors.cjs +0 -15
  32. package/build/selectors.cjs.map +2 -2
  33. package/build/sync.cjs +12 -3
  34. package/build/sync.cjs.map +2 -2
  35. package/build/types.cjs.map +1 -1
  36. package/build/utils/block-selection-history.cjs +1 -1
  37. package/build/utils/block-selection-history.cjs.map +2 -2
  38. package/build/utils/crdt-blocks.cjs +85 -28
  39. package/build/utils/crdt-blocks.cjs.map +2 -2
  40. package/build/utils/crdt-selection.cjs +4 -1
  41. package/build/utils/crdt-selection.cjs.map +2 -2
  42. package/build/utils/crdt-text.cjs +52 -0
  43. package/build/utils/crdt-text.cjs.map +7 -0
  44. package/build/utils/crdt-user-selections.cjs +16 -6
  45. package/build/utils/crdt-user-selections.cjs.map +3 -3
  46. package/build/utils/crdt-utils.cjs +54 -2
  47. package/build/utils/crdt-utils.cjs.map +2 -2
  48. package/build/utils/crdt.cjs +26 -28
  49. package/build/utils/crdt.cjs.map +2 -2
  50. package/build-module/actions.mjs +14 -26
  51. package/build-module/actions.mjs.map +2 -2
  52. package/build-module/awareness/post-editor-awareness.mjs +47 -7
  53. package/build-module/awareness/post-editor-awareness.mjs.map +2 -2
  54. package/build-module/entities.mjs +37 -16
  55. package/build-module/entities.mjs.map +2 -2
  56. package/build-module/entity-provider.mjs +15 -6
  57. package/build-module/entity-provider.mjs.map +2 -2
  58. package/build-module/hooks/use-entity-prop.mjs +34 -3
  59. package/build-module/hooks/use-entity-prop.mjs.map +2 -2
  60. package/build-module/hooks/use-post-editor-awareness-state.mjs +117 -1
  61. package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
  62. package/build-module/index.mjs +0 -2
  63. package/build-module/index.mjs.map +2 -2
  64. package/build-module/private-actions.mjs +24 -2
  65. package/build-module/private-actions.mjs.map +2 -2
  66. package/build-module/private-apis.mjs +21 -6
  67. package/build-module/private-apis.mjs.map +3 -3
  68. package/build-module/private-selectors.mjs +19 -1
  69. package/build-module/private-selectors.mjs.map +2 -2
  70. package/build-module/queried-data/actions.mjs +1 -1
  71. package/build-module/queried-data/actions.mjs.map +2 -2
  72. package/build-module/queried-data/reducer.mjs +19 -13
  73. package/build-module/queried-data/reducer.mjs.map +2 -2
  74. package/build-module/reducer.mjs +18 -2
  75. package/build-module/reducer.mjs.map +2 -2
  76. package/build-module/resolvers.mjs +24 -17
  77. package/build-module/resolvers.mjs.map +2 -2
  78. package/build-module/selectors.mjs +0 -14
  79. package/build-module/selectors.mjs.map +2 -2
  80. package/build-module/sync.mjs +8 -2
  81. package/build-module/sync.mjs.map +2 -2
  82. package/build-module/utils/block-selection-history.mjs +5 -2
  83. package/build-module/utils/block-selection-history.mjs.map +2 -2
  84. package/build-module/utils/crdt-blocks.mjs +84 -28
  85. package/build-module/utils/crdt-blocks.mjs.map +2 -2
  86. package/build-module/utils/crdt-selection.mjs +8 -2
  87. package/build-module/utils/crdt-selection.mjs.map +2 -2
  88. package/build-module/utils/crdt-text.mjs +26 -0
  89. package/build-module/utils/crdt-text.mjs.map +7 -0
  90. package/build-module/utils/crdt-user-selections.mjs +16 -7
  91. package/build-module/utils/crdt-user-selections.mjs.map +3 -3
  92. package/build-module/utils/crdt-utils.mjs +51 -1
  93. package/build-module/utils/crdt-utils.mjs.map +2 -2
  94. package/build-module/utils/crdt.mjs +26 -28
  95. package/build-module/utils/crdt.mjs.map +2 -2
  96. package/build-types/actions.d.ts +0 -11
  97. package/build-types/actions.d.ts.map +1 -1
  98. package/build-types/awareness/post-editor-awareness.d.ts +2 -2
  99. package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
  100. package/build-types/awareness/types.d.ts +6 -1
  101. package/build-types/awareness/types.d.ts.map +1 -1
  102. package/build-types/entities.d.ts +20 -1
  103. package/build-types/entities.d.ts.map +1 -1
  104. package/build-types/entity-provider.d.ts +11 -6
  105. package/build-types/entity-provider.d.ts.map +1 -1
  106. package/build-types/hooks/use-entity-prop.d.ts.map +1 -1
  107. package/build-types/hooks/use-post-editor-awareness-state.d.ts +40 -7
  108. package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
  109. package/build-types/index.d.ts +0 -3
  110. package/build-types/index.d.ts.map +1 -1
  111. package/build-types/parsed-blocks-cache.d.ts +10 -0
  112. package/build-types/parsed-blocks-cache.d.ts.map +1 -0
  113. package/build-types/private-actions.d.ts +12 -0
  114. package/build-types/private-actions.d.ts.map +1 -1
  115. package/build-types/private-apis.d.ts +21 -1
  116. package/build-types/private-apis.d.ts.map +1 -1
  117. package/build-types/private-selectors.d.ts +17 -0
  118. package/build-types/private-selectors.d.ts.map +1 -1
  119. package/build-types/queried-data/reducer.d.ts.map +1 -1
  120. package/build-types/reducer.d.ts +15 -0
  121. package/build-types/reducer.d.ts.map +1 -1
  122. package/build-types/resolvers.d.ts.map +1 -1
  123. package/build-types/selectors.d.ts +1 -9
  124. package/build-types/selectors.d.ts.map +1 -1
  125. package/build-types/sync.d.ts +2 -2
  126. package/build-types/sync.d.ts.map +1 -1
  127. package/build-types/types.d.ts +10 -2
  128. package/build-types/types.d.ts.map +1 -1
  129. package/build-types/utils/block-selection-history.d.ts.map +1 -1
  130. package/build-types/utils/crdt-blocks.d.ts +11 -0
  131. package/build-types/utils/crdt-blocks.d.ts.map +1 -1
  132. package/build-types/utils/crdt-selection.d.ts.map +1 -1
  133. package/build-types/utils/crdt-text.d.ts +16 -0
  134. package/build-types/utils/crdt-text.d.ts.map +1 -0
  135. package/build-types/utils/crdt-user-selections.d.ts +17 -4
  136. package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
  137. package/build-types/utils/crdt-utils.d.ts +20 -0
  138. package/build-types/utils/crdt-utils.d.ts.map +1 -1
  139. package/build-types/utils/crdt.d.ts +13 -7
  140. package/build-types/utils/crdt.d.ts.map +1 -1
  141. package/build-types/utils/normalize-query-for-resolution.d.ts +12 -0
  142. package/build-types/utils/normalize-query-for-resolution.d.ts.map +1 -0
  143. package/build-types/utils/save-crdt-doc.d.ts +8 -0
  144. package/build-types/utils/save-crdt-doc.d.ts.map +1 -0
  145. package/build-types/utils/test/crdt-utils.d.ts +2 -0
  146. package/build-types/utils/test/crdt-utils.d.ts.map +1 -0
  147. package/package.json +18 -18
  148. package/src/actions.js +26 -41
  149. package/src/awareness/post-editor-awareness.ts +106 -7
  150. package/src/awareness/test/post-editor-awareness.ts +50 -10
  151. package/src/awareness/types.ts +7 -1
  152. package/src/entities.js +43 -17
  153. package/src/entity-provider.js +24 -11
  154. package/src/hooks/test/use-post-editor-awareness-state.ts +446 -3
  155. package/src/hooks/use-entity-prop.js +43 -3
  156. package/src/hooks/use-post-editor-awareness-state.ts +229 -7
  157. package/src/index.js +0 -6
  158. package/src/private-actions.js +43 -1
  159. package/src/{private-apis.js → private-apis.ts} +21 -3
  160. package/src/private-selectors.ts +43 -0
  161. package/src/queried-data/actions.js +1 -1
  162. package/src/queried-data/reducer.js +26 -14
  163. package/src/reducer.js +36 -1
  164. package/src/resolvers.js +33 -23
  165. package/src/selectors.ts +1 -32
  166. package/src/sync.ts +6 -0
  167. package/src/test/entities.js +204 -16
  168. package/src/test/private-actions.js +1 -1
  169. package/src/test/resolvers.js +144 -57
  170. package/src/test/store.js +116 -0
  171. package/src/types.ts +20 -2
  172. package/src/utils/block-selection-history.ts +5 -2
  173. package/src/utils/crdt-blocks.ts +159 -56
  174. package/src/utils/crdt-selection.ts +8 -2
  175. package/src/utils/crdt-text.ts +43 -0
  176. package/src/utils/crdt-user-selections.ts +29 -8
  177. package/src/utils/crdt-utils.ts +99 -0
  178. package/src/utils/crdt.ts +50 -40
  179. package/src/utils/test/crdt-blocks.ts +588 -24
  180. package/src/utils/test/crdt-user-selections.ts +5 -0
  181. package/src/utils/test/crdt-utils.ts +387 -0
  182. package/src/utils/test/crdt.ts +291 -54
@@ -2,6 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { Y } from '@wordpress/sync';
5
+ import { create, insert, toHTMLString } from '@wordpress/rich-text';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -104,6 +105,104 @@ export function findBlockByClientIdInDoc(
104
105
  return findBlockByClientIdInBlocks( blockId, blocks );
105
106
  }
106
107
 
108
+ // Marker for insertion.
109
+ const MARKER_START = 0xe000;
110
+
111
+ /**
112
+ * Pick a marker character that does not appear in `text`. Returns the marker
113
+ * or `null` if all candidates are present (extremely unlikely in practice).
114
+ *
115
+ * @param text The string to check for existing marker characters.
116
+ */
117
+ function pickMarker( text: string ): string | null {
118
+ const tryCount = 0x10;
119
+
120
+ // Scan the unicode private use area for the first code point not present
121
+ // in the text.
122
+ for ( let code = MARKER_START; code < MARKER_START + tryCount; code++ ) {
123
+ const candidate = String.fromCharCode( code );
124
+
125
+ if ( ! text.includes( candidate ) ) {
126
+ return candidate;
127
+ }
128
+ }
129
+
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * Convert an HTML character index (counting tag characters) to a rich-text
135
+ * offset (counting only text characters). Used on read paths where Y.Text
136
+ * resolves to an HTML index but the block editor expects a text offset.
137
+ *
138
+ * @param html The full HTML string from Y.Text.
139
+ * @param htmlIndex The HTML character index.
140
+ * @return The corresponding rich-text offset.
141
+ */
142
+ export function htmlIndexToRichTextOffset(
143
+ html: string,
144
+ htmlIndex: number
145
+ ): number {
146
+ if ( ! html.includes( '<' ) && ! html.includes( '&' ) ) {
147
+ return htmlIndex;
148
+ }
149
+
150
+ const marker = pickMarker( html );
151
+ if ( ! marker ) {
152
+ return htmlIndex;
153
+ }
154
+
155
+ // Insert marker and let create() do the parsing.
156
+ const withMarker =
157
+ html.slice( 0, htmlIndex ) + marker + html.slice( htmlIndex );
158
+ const value = create( { html: withMarker } );
159
+ const markerPos = value.text.indexOf( marker );
160
+
161
+ return markerPos === -1 ? htmlIndex : markerPos;
162
+ }
163
+
164
+ /**
165
+ * Convert a rich-text offset (counting only text characters) to an HTML
166
+ * character index (counting tag characters). Used on write paths where the
167
+ * block editor provides a text offset but Y.Text expects an HTML index.
168
+ *
169
+ * @param html The full HTML string from Y.Text.
170
+ * @param richTextOffset The rich-text text offset.
171
+ * @return The corresponding HTML character index.
172
+ */
173
+ export function richTextOffsetToHtmlIndex(
174
+ html: string,
175
+ richTextOffset: number
176
+ ): number {
177
+ if ( ! html.includes( '<' ) && ! html.includes( '&' ) ) {
178
+ return richTextOffset;
179
+ }
180
+
181
+ const marker = pickMarker( html );
182
+ if ( ! marker ) {
183
+ return richTextOffset;
184
+ }
185
+
186
+ const value = create( { html } );
187
+ const markerValue = create( { text: marker } );
188
+ // The marker must inherit the formatting at the insertion point so that
189
+ // toHTMLString does not split surrounding tags (e.g. <strong>) around it.
190
+ if ( value.formats[ richTextOffset ] ) {
191
+ markerValue.formats[ 0 ] = value.formats[ richTextOffset ];
192
+ }
193
+
194
+ const withMarker = insert(
195
+ value,
196
+ markerValue,
197
+ richTextOffset,
198
+ richTextOffset
199
+ );
200
+
201
+ const htmlWithMarker = toHTMLString( { value: withMarker } );
202
+ const markerIndex = htmlWithMarker.indexOf( marker );
203
+ return markerIndex === -1 ? richTextOffset : markerIndex;
204
+ }
205
+
107
206
  function findBlockByClientIdInBlocks(
108
207
  blockId: string,
109
208
  blocks: YBlocks
package/src/utils/crdt.ts CHANGED
@@ -11,6 +11,8 @@ import { __unstableSerializeAndClean } from '@wordpress/blocks';
11
11
  import {
12
12
  type CRDTDoc,
13
13
  type ObjectData,
14
+ type ObjectID,
15
+ type ObjectType,
14
16
  type SyncConfig,
15
17
  Y,
16
18
  } from '@wordpress/sync';
@@ -20,6 +22,7 @@ import {
20
22
  */
21
23
  import { BaseAwareness } from '../awareness/base-awareness';
22
24
  import {
25
+ deserializeBlockAttributes,
23
26
  mergeCrdtBlocks,
24
27
  mergeRichTextUpdate,
25
28
  type Block,
@@ -27,7 +30,6 @@ import {
27
30
  type YBlocks,
28
31
  } from './crdt-blocks';
29
32
  import { type Post } from '../entity-types/post';
30
- import { type Type } from '../entity-types';
31
33
  import { CRDT_DOC_META_PERSISTENCE_KEY, CRDT_RECORD_MAP_KEY } from '../sync';
32
34
  import type { WPSelection } from '../types';
33
35
  import {
@@ -76,27 +78,6 @@ export interface YPostRecord extends YMapRecord {
76
78
 
77
79
  export const POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE = '_crdt_document';
78
80
 
79
- // Properties that are allowed to be synced for a post.
80
- const allowedPostProperties = new Set< string >( [
81
- 'author',
82
- 'blocks',
83
- 'content',
84
- 'categories',
85
- 'comment_status',
86
- 'date',
87
- 'excerpt',
88
- 'featured_media',
89
- 'format',
90
- 'meta',
91
- 'ping_status',
92
- 'slug',
93
- 'status',
94
- 'sticky',
95
- 'tags',
96
- 'template',
97
- 'title',
98
- ] );
99
-
100
81
  // Post meta keys that should *not* be synced.
101
82
  const disallowedPostMetaKeys = new Set< string >( [
102
83
  POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE,
@@ -139,18 +120,18 @@ function defaultApplyChangesToCRDTDoc(
139
120
  *
140
121
  * @param {CRDTDoc} ydoc
141
122
  * @param {PostChanges} changes
142
- * @param {Type} _postType
123
+ * @param {Set<string>} syncedProperties
143
124
  * @return {void}
144
125
  */
145
126
  export function applyPostChangesToCRDTDoc(
146
127
  ydoc: CRDTDoc,
147
128
  changes: PostChanges,
148
- _postType: Type // eslint-disable-line @typescript-eslint/no-unused-vars
129
+ syncedProperties: Set< string >
149
130
  ): void {
150
131
  const ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );
151
132
 
152
133
  Object.keys( changes ).forEach( ( key ) => {
153
- if ( ! allowedPostProperties.has( key ) ) {
134
+ if ( ! syncedProperties.has( key ) ) {
154
135
  return;
155
136
  }
156
137
 
@@ -289,15 +270,15 @@ function defaultGetChangesFromCRDTDoc( crdtDoc: CRDTDoc ): ObjectData {
289
270
  * against the local record and determine if there are changes (edits) we want
290
271
  * to dispatch.
291
272
  *
292
- * @param {CRDTDoc} ydoc
293
- * @param {Post} editedRecord
294
- * @param {Type} _postType
273
+ * @param {CRDTDoc} ydoc
274
+ * @param {Post} editedRecord
275
+ * @param {Set<string>} syncedProperties
295
276
  * @return {Partial<PostChanges>} The changes that should be applied to the local record.
296
277
  */
297
278
  export function getPostChangesFromCRDTDoc(
298
279
  ydoc: CRDTDoc,
299
280
  editedRecord: Post,
300
- _postType: Type // eslint-disable-line @typescript-eslint/no-unused-vars
281
+ syncedProperties: Set< string >
301
282
  ): PostChanges {
302
283
  const ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );
303
284
 
@@ -305,7 +286,7 @@ export function getPostChangesFromCRDTDoc(
305
286
 
306
287
  const changes = Object.fromEntries(
307
288
  Object.entries( ymap.toJSON() ).filter( ( [ key, newValue ] ) => {
308
- if ( ! allowedPostProperties.has( key ) ) {
289
+ if ( ! syncedProperties.has( key ) ) {
309
290
  return false;
310
291
  }
311
292
 
@@ -342,8 +323,6 @@ export function getPostChangesFromCRDTDoc(
342
323
  );
343
324
  }
344
325
 
345
- // The consumers of blocks have memoization that renders optimization
346
- // here unnecessary.
347
326
  return true;
348
327
  }
349
328
 
@@ -351,11 +330,8 @@ export function getPostChangesFromCRDTDoc(
351
330
  // Do not overwrite a "floating" date. Borrowing logic from the
352
331
  // isEditedPostDateFloating selector.
353
332
  const currentDateIsFloating =
354
- [ 'draft', 'auto-draft', 'pending' ].includes(
355
- ymap.get( 'status' ) as string
356
- ) &&
357
- ( null === currentValue ||
358
- editedRecord.modified === currentValue );
333
+ null === currentValue ||
334
+ editedRecord.modified === currentValue;
359
335
 
360
336
  if ( currentDateIsFloating ) {
361
337
  return false;
@@ -365,17 +341,29 @@ export function getPostChangesFromCRDTDoc(
365
341
  }
366
342
 
367
343
  case 'meta': {
344
+ const currentMeta =
345
+ ( currentValue as PostChanges[ 'meta' ] ) ?? {};
346
+
368
347
  allowedMetaChanges = Object.fromEntries(
369
348
  Object.entries( newValue ?? {} ).filter(
370
- ( [ metaKey ] ) =>
371
- ! disallowedPostMetaKeys.has( metaKey )
349
+ ( [ metaKey ] ) => {
350
+ if ( disallowedPostMetaKeys.has( metaKey ) ) {
351
+ return false;
352
+ }
353
+
354
+ // Ignore meta keys that are no longer registered
355
+ // for this post (absent from the REST response).
356
+ // Without this, orphaned CRDT meta would mark
357
+ // the post permanently dirty.
358
+ return metaKey in currentMeta;
359
+ }
372
360
  )
373
361
  );
374
362
 
375
363
  // Merge the allowed meta changes with the current meta values since
376
364
  // not all meta properties are synced.
377
365
  const mergedValue = {
378
- ...( currentValue as PostChanges[ 'meta' ] ),
366
+ ...currentMeta,
379
367
  ...allowedMetaChanges,
380
368
  };
381
369
 
@@ -409,6 +397,15 @@ export function getPostChangesFromCRDTDoc(
409
397
  } )
410
398
  );
411
399
 
400
+ // Blocks extracted from the CRDT document have rich-text attributes as
401
+ // plain strings (from Y.Text.toJSON()). Convert them back to RichTextData
402
+ // so block edit components receive the same types as locally-created blocks.
403
+ if ( changes.blocks ) {
404
+ changes.blocks = deserializeBlockAttributes(
405
+ changes.blocks as Block[]
406
+ );
407
+ }
408
+
412
409
  // Meta changes must be merged with the edited record since not all meta
413
410
  // properties are synced.
414
411
  if ( 'object' === typeof changes.meta ) {
@@ -445,6 +442,19 @@ export const defaultSyncConfig: SyncConfig = {
445
442
  getChangesFromCRDTDoc: defaultGetChangesFromCRDTDoc,
446
443
  };
447
444
 
445
+ /**
446
+ * This default collection sync config can be used to sync entity collections
447
+ * (e.g., block comments) where we are not interested in merging changes at the
448
+ * individual record level, but instead want to replace the entire collection
449
+ * when changes are detected.
450
+ */
451
+ export const defaultCollectionSyncConfig: SyncConfig = {
452
+ applyChangesToCRDTDoc: () => {},
453
+ getChangesFromCRDTDoc: () => ( {} ),
454
+ shouldSync: ( _: ObjectType, objectId: ObjectID | null ) =>
455
+ null === objectId,
456
+ };
457
+
448
458
  /**
449
459
  * Extract the raw string value from a property that may be a string or an object
450
460
  * with a `raw` property (`RenderedText`).