@wordpress/core-data 7.43.0 → 7.43.2-next.v.202604091042.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 (85) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/build/entities.cjs +28 -20
  3. package/build/entities.cjs.map +2 -2
  4. package/build/hooks/index.cjs +3 -3
  5. package/build/hooks/index.cjs.map +1 -1
  6. package/build/hooks/use-entity-record.cjs +4 -4
  7. package/build/hooks/use-entity-record.cjs.map +2 -2
  8. package/build/hooks/use-entity-records.cjs +3 -3
  9. package/build/hooks/use-entity-records.cjs.map +2 -2
  10. package/build/hooks/use-query-select.cjs +2 -2
  11. package/build/hooks/use-query-select.cjs.map +2 -2
  12. package/build/hooks/use-resource-permissions.cjs +4 -4
  13. package/build/hooks/use-resource-permissions.cjs.map +2 -2
  14. package/build/queried-data/selectors.cjs +21 -17
  15. package/build/queried-data/selectors.cjs.map +2 -2
  16. package/build/resolvers.cjs +6 -6
  17. package/build/resolvers.cjs.map +2 -2
  18. package/build/selectors.cjs +4 -2
  19. package/build/selectors.cjs.map +2 -2
  20. package/build/utils/crdt-blocks.cjs +122 -30
  21. package/build/utils/crdt-blocks.cjs.map +2 -2
  22. package/build/utils/crdt.cjs +8 -0
  23. package/build/utils/crdt.cjs.map +2 -2
  24. package/build-module/entities.mjs +29 -20
  25. package/build-module/entities.mjs.map +2 -2
  26. package/build-module/hooks/index.mjs +6 -6
  27. package/build-module/hooks/index.mjs.map +2 -2
  28. package/build-module/hooks/use-entity-record.mjs +3 -3
  29. package/build-module/hooks/use-entity-record.mjs.map +2 -2
  30. package/build-module/hooks/use-entity-records.mjs +2 -2
  31. package/build-module/hooks/use-entity-records.mjs.map +2 -2
  32. package/build-module/hooks/use-query-select.mjs +1 -1
  33. package/build-module/hooks/use-query-select.mjs.map +1 -1
  34. package/build-module/hooks/use-resource-permissions.mjs +3 -3
  35. package/build-module/hooks/use-resource-permissions.mjs.map +2 -2
  36. package/build-module/queried-data/selectors.mjs +21 -17
  37. package/build-module/queried-data/selectors.mjs.map +2 -2
  38. package/build-module/resolvers.mjs +6 -6
  39. package/build-module/resolvers.mjs.map +2 -2
  40. package/build-module/selectors.mjs +4 -2
  41. package/build-module/selectors.mjs.map +2 -2
  42. package/build-module/utils/crdt-blocks.mjs +122 -30
  43. package/build-module/utils/crdt-blocks.mjs.map +2 -2
  44. package/build-module/utils/crdt.mjs +7 -0
  45. package/build-module/utils/crdt.mjs.map +2 -2
  46. package/build-types/entities.d.ts +51 -32
  47. package/build-types/entities.d.ts.map +1 -1
  48. package/build-types/hooks/index.d.ts +3 -3
  49. package/build-types/hooks/index.d.ts.map +1 -1
  50. package/build-types/hooks/use-entity-record.d.ts +1 -1
  51. package/build-types/hooks/use-entity-record.d.ts.map +1 -1
  52. package/build-types/hooks/use-entity-records.d.ts +1 -1
  53. package/build-types/hooks/use-entity-records.d.ts.map +1 -1
  54. package/build-types/hooks/use-resource-permissions.d.ts +1 -1
  55. package/build-types/hooks/use-resource-permissions.d.ts.map +1 -1
  56. package/build-types/index.d.ts.map +1 -1
  57. package/build-types/queried-data/selectors.d.ts +5 -3
  58. package/build-types/queried-data/selectors.d.ts.map +1 -1
  59. package/build-types/selectors.d.ts.map +1 -1
  60. package/build-types/utils/crdt-blocks.d.ts.map +1 -1
  61. package/build-types/utils/crdt.d.ts +7 -0
  62. package/build-types/utils/crdt.d.ts.map +1 -1
  63. package/package.json +18 -18
  64. package/src/entities.js +16 -8
  65. package/src/hooks/index.ts +3 -3
  66. package/src/hooks/test/use-entity-records.js +1 -1
  67. package/src/hooks/use-entity-record.ts +1 -1
  68. package/src/hooks/use-entity-records.ts +1 -1
  69. package/src/hooks/use-query-select.ts +1 -1
  70. package/src/hooks/use-resource-permissions.ts +1 -1
  71. package/src/queried-data/selectors.js +32 -26
  72. package/src/queried-data/test/selectors.js +30 -21
  73. package/src/resolvers.js +6 -6
  74. package/src/selectors.ts +8 -2
  75. package/src/utils/crdt-blocks.ts +268 -61
  76. package/src/utils/crdt.ts +15 -0
  77. package/src/utils/test/crdt-blocks.ts +785 -6
  78. package/src/utils/test/crdt.ts +39 -1
  79. package/build/hooks/memoize.cjs +0 -38
  80. package/build/hooks/memoize.cjs.map +0 -7
  81. package/build-module/hooks/memoize.mjs +0 -7
  82. package/build-module/hooks/memoize.mjs.map +0 -7
  83. package/build-types/hooks/memoize.d.ts +0 -3
  84. package/build-types/hooks/memoize.d.ts.map +0 -1
  85. package/src/hooks/memoize.js +0 -7
package/src/selectors.ts CHANGED
@@ -655,7 +655,10 @@ export const getEntityRecords = ( <
655
655
  if ( ! queriedState ) {
656
656
  return null;
657
657
  }
658
- return getQueriedItems( queriedState, query );
658
+ return getQueriedItems( queriedState, query, {
659
+ supportsPagination: !! getEntityConfig( state, kind, name )
660
+ ?.supportsPagination,
661
+ } );
659
662
  } ) as GetEntityRecords;
660
663
 
661
664
  /**
@@ -713,7 +716,10 @@ export const getEntityRecordsTotalPages = (
713
716
  if ( ! queriedState ) {
714
717
  return null;
715
718
  }
716
- if ( query?.per_page === -1 ) {
719
+ if (
720
+ ! getEntityConfig( state, kind, name )?.supportsPagination ||
721
+ query?.per_page === -1
722
+ ) {
717
723
  return 1;
718
724
  }
719
725
  const totalItems = getQueriedTotalItems( queriedState, query );
@@ -23,14 +23,14 @@ interface BlockAttributes {
23
23
  [ key: string ]: unknown;
24
24
  }
25
25
 
26
- interface BlockAttributeType {
26
+ interface BlockAttributeSchema {
27
27
  role?: string;
28
28
  type?: string;
29
- query?: Record< string, BlockAttributeType >;
29
+ query?: Record< string, BlockAttributeSchema >;
30
30
  }
31
31
 
32
32
  interface BlockType {
33
- attributes?: Record< string, BlockAttributeType >;
33
+ attributes?: Record< string, BlockAttributeSchema >;
34
34
  name: string;
35
35
  }
36
36
 
@@ -134,7 +134,7 @@ function makeBlocksSerializable( blocks: Block[] ): Block[] {
134
134
  * @return The value with rich-text strings replaced by RichTextData.
135
135
  */
136
136
  function deserializeAttributeValue(
137
- schema: BlockAttributeType | undefined,
137
+ schema: BlockAttributeSchema | undefined,
138
138
  value: unknown
139
139
  ): unknown {
140
140
  if ( schema?.type === 'rich-text' && typeof value === 'string' ) {
@@ -184,7 +184,7 @@ export function deserializeBlockAttributes( blocks: Block[] ): Block[] {
184
184
  const newAttributes = { ...attributes };
185
185
 
186
186
  for ( const [ key, value ] of Object.entries( attributes ) ) {
187
- const schema = getBlockAttributeType( name, key );
187
+ const schema = getBlockAttributeSchema( name, key );
188
188
 
189
189
  if ( schema ) {
190
190
  newAttributes[ key ] = deserializeAttributeValue(
@@ -255,14 +255,89 @@ function createNewYAttributeValue(
255
255
  blockName: string,
256
256
  attributeName: string,
257
257
  attributeValue: unknown
258
- ): Y.Text | unknown {
259
- const isRichText = isRichTextAttribute( blockName, attributeName );
258
+ ): Y.Text | Y.Array< unknown > | Y.Map< unknown > | unknown {
259
+ const schema = getBlockAttributeSchema( blockName, attributeName );
260
+ return createYValueFromSchema( schema, attributeValue );
261
+ }
262
+
263
+ /**
264
+ * Recursively create the appropriate Y.js type for a value based on its
265
+ * block-attribute schema.
266
+ *
267
+ * - `rich-text` -> Y.Text
268
+ * - `array` with query -> Y.Array of Y.Maps
269
+ * - `object` with query -> Y.Map
270
+ * - anything else -> plain value (unchanged)
271
+ *
272
+ * @param schema The attribute type definition.
273
+ * @param value The plain JS value to convert.
274
+ * @return A Y.js type or the original value.
275
+ */
276
+ function createYValueFromSchema(
277
+ schema: BlockAttributeSchema | undefined,
278
+ value: unknown
279
+ ): Y.Text | Y.Array< unknown > | Y.Map< unknown > | unknown {
280
+ if ( ! schema ) {
281
+ return value;
282
+ }
283
+
284
+ if ( schema.type === 'rich-text' ) {
285
+ return new Y.Text( value?.toString() ?? '' );
286
+ }
287
+
288
+ if ( schema.type === 'array' && schema.query && Array.isArray( value ) ) {
289
+ const query = schema.query;
290
+ const yArray = new Y.Array< Y.Map< unknown > >();
291
+
292
+ yArray.insert(
293
+ 0,
294
+ value.map( ( item ) => createYMapFromQuery( query, item ) )
295
+ );
260
296
 
261
- if ( isRichText ) {
262
- return new Y.Text( attributeValue?.toString() ?? '' );
297
+ return yArray;
263
298
  }
264
299
 
265
- return attributeValue;
300
+ if ( schema.type === 'object' && schema.query && isRecord( value ) ) {
301
+ return createYMapFromQuery( schema.query, value );
302
+ }
303
+
304
+ return value;
305
+ }
306
+
307
+ /**
308
+ * Type guard that narrows `unknown` to `Record< string, unknown >`.
309
+ *
310
+ * @param value Value to check.
311
+ * @return True if `value` is a non-null, non-array object.
312
+ */
313
+ function isRecord( value: unknown ): value is Record< string, unknown > {
314
+ return !! value && typeof value === 'object' && ! Array.isArray( value );
315
+ }
316
+
317
+ /**
318
+ * Create a Y.Map from a plain object, using a query schema to decide which
319
+ * properties should become nested Y.js types (Y.Text, Y.Array, Y.Map).
320
+ *
321
+ * @param query The query schema defining the properties.
322
+ * @param obj The plain object to convert.
323
+ * @return A Y.Map with typed values.
324
+ */
325
+ function createYMapFromQuery(
326
+ query: Record< string, BlockAttributeSchema >,
327
+ obj: unknown
328
+ ): Y.Map< unknown > {
329
+ if ( ! isRecord( obj ) ) {
330
+ return new Y.Map();
331
+ }
332
+
333
+ const entries: [ string, unknown ][] = Object.entries( obj ).map(
334
+ ( [ key, val ] ): [ string, unknown ] => {
335
+ const subSchema = query[ key ];
336
+ return [ key, createYValueFromSchema( subSchema, val ) ];
337
+ }
338
+ );
339
+
340
+ return new Y.Map( entries );
266
341
  }
267
342
 
268
343
  function createNewYBlock( block: Block ): YBlock {
@@ -408,8 +483,16 @@ export function mergeCrdtBlocks(
408
483
  currentAttribute
409
484
  );
410
485
 
486
+ // Y types (Y.Text, Y.Array, Y.Map) cannot be
487
+ // compared with fastDeepEqual against plain values.
488
+ // Delegate to mergeYValue which handles no-op
489
+ // detection at the edges.
490
+ const isYType =
491
+ currentAttribute instanceof Y.AbstractType;
492
+
411
493
  const isAttributeChanged =
412
494
  ! isExpectedType ||
495
+ isYType ||
413
496
  ! fastDeepEqual(
414
497
  currentAttribute,
415
498
  attributeValue
@@ -499,11 +582,151 @@ export function mergeCrdtBlocks(
499
582
  }
500
583
 
501
584
  /**
502
- * Update a single attribute on a Yjs block attributes map (currentAttributes).
585
+ * Merge an incoming plain array into an existing Y.Array in-place.
503
586
  *
504
- * For rich-text attributes that already exist as Y.Text instances, the update
505
- * is applied as a delta merge so that concurrent edits are preserved. All
506
- * other attributes are replaced wholesale via `createNewYAttributeValue`.
587
+ * When the array length is unchanged (stable structure), each element is
588
+ * merged individually via `mergeYMapValues`, preserving concurrent edits to
589
+ * different elements. When the length changes (structural edit such as row
590
+ * insertion/deletion), the Y.Array is rebuilt from scratch.
591
+ *
592
+ * @param yArray The existing Y.Array to update.
593
+ * @param newValue The new plain array to merge into the Y.Array.
594
+ * @param schema The attribute schema (must have `query`).
595
+ * @param cursorPosition The local cursor position for rich-text delta merges.
596
+ */
597
+ function mergeYArray(
598
+ yArray: Y.Array< unknown >,
599
+ newValue: unknown[],
600
+ schema: BlockAttributeSchema,
601
+ cursorPosition: number | null
602
+ ): void {
603
+ if ( ! schema.query ) {
604
+ return;
605
+ }
606
+
607
+ const query = schema.query;
608
+
609
+ if ( yArray.length === newValue.length ) {
610
+ // Same length: update each element in-place.
611
+ for ( let i = 0; i < newValue.length; i++ ) {
612
+ const currentElement = yArray.get( i );
613
+ const newElement = newValue[ i ];
614
+
615
+ if ( currentElement instanceof Y.Map && isRecord( newElement ) ) {
616
+ mergeYMapValues(
617
+ currentElement,
618
+ newElement,
619
+ query,
620
+ cursorPosition
621
+ );
622
+ } else {
623
+ // Element is the wrong type (e.g. partial migration) or the
624
+ // incoming value is not an object. Rebuild the entire array.
625
+ yArray.delete( 0, yArray.length );
626
+ yArray.insert(
627
+ 0,
628
+ newValue.map( ( item ) =>
629
+ createYMapFromQuery( query, item )
630
+ )
631
+ );
632
+ return;
633
+ }
634
+ }
635
+ } else {
636
+ // Structure changed: rebuild the Y.Array.
637
+ yArray.delete( 0, yArray.length );
638
+ yArray.insert(
639
+ 0,
640
+ newValue.map( ( item ) => createYMapFromQuery( query, item ) )
641
+ );
642
+ }
643
+ }
644
+
645
+ /**
646
+ * Merge a single value into a Y.Map entry, using the attribute schema to
647
+ * decide how to merge.
648
+ *
649
+ * If the current value is already a matching Y.js type (Y.Text, Y.Array,
650
+ * Y.Map), the update is merged in-place so concurrent edits are preserved.
651
+ * Otherwise the value is replaced wholesale.
652
+ *
653
+ * @param schema The attribute type definition for this value.
654
+ * @param newVal The new value to merge into the Y.Map entry.
655
+ * @param yMap The Y.Map that owns this entry.
656
+ * @param key The key of this entry in the Y.Map.
657
+ * @param cursorPosition The local cursor position for rich-text delta merges.
658
+ */
659
+ function mergeYValue(
660
+ schema: BlockAttributeSchema | undefined,
661
+ newVal: unknown,
662
+ yMap: Y.Map< unknown >,
663
+ key: string,
664
+ cursorPosition: number | null
665
+ ): void {
666
+ const currentVal = yMap.get( key );
667
+ if (
668
+ schema?.type === 'rich-text' &&
669
+ typeof newVal === 'string' &&
670
+ currentVal instanceof Y.Text
671
+ ) {
672
+ mergeRichTextUpdate( currentVal, newVal, cursorPosition );
673
+ } else if (
674
+ schema?.type === 'array' &&
675
+ schema.query &&
676
+ Array.isArray( newVal ) &&
677
+ currentVal instanceof Y.Array
678
+ ) {
679
+ mergeYArray( currentVal, newVal, schema, cursorPosition );
680
+ } else if (
681
+ schema?.type === 'object' &&
682
+ schema.query &&
683
+ isRecord( newVal ) &&
684
+ currentVal instanceof Y.Map
685
+ ) {
686
+ mergeYMapValues( currentVal, newVal, schema.query, cursorPosition );
687
+ } else {
688
+ const newYValue = createYValueFromSchema( schema, newVal );
689
+
690
+ // If createYValueFromSchema wrapped the value into a Y type, the
691
+ // current value is the wrong type and needs upgrading. Otherwise,
692
+ // only replace if the raw value actually changed.
693
+ if ( newYValue !== newVal || ! fastDeepEqual( currentVal, newVal ) ) {
694
+ yMap.set( key, newYValue );
695
+ }
696
+ }
697
+ }
698
+
699
+ /**
700
+ * Merge an incoming plain object into an existing Y.Map in-place, using
701
+ * the query schema to decide how each property should be merged.
702
+ *
703
+ * Properties present in the Y.Map but absent from `newObj` are deleted.
704
+ *
705
+ * @param yMap The existing Y.Map to update.
706
+ * @param newObj The new plain object to merge into the Y.Map.
707
+ * @param query The query schema defining property types.
708
+ * @param cursorPosition The local cursor position for rich-text delta merges.
709
+ */
710
+ function mergeYMapValues(
711
+ yMap: Y.Map< unknown >,
712
+ newObj: Record< string, unknown >,
713
+ query: Record< string, BlockAttributeSchema >,
714
+ cursorPosition: number | null
715
+ ): void {
716
+ for ( const [ key, newVal ] of Object.entries( newObj ) ) {
717
+ mergeYValue( query[ key ], newVal, yMap, key, cursorPosition );
718
+ }
719
+
720
+ // Delete properties absent from the incoming object.
721
+ for ( const key of yMap.keys() ) {
722
+ if ( ! Object.hasOwn( newObj, key ) ) {
723
+ yMap.delete( key );
724
+ }
725
+ }
726
+ }
727
+
728
+ /**
729
+ * Update a single attribute on a Yjs block attributes map (currentAttributes).
507
730
  *
508
731
  * @param blockName The block type name, e.g. 'core/paragraph'.
509
732
  * @param attributeName The name of the attribute to update, e.g. 'content'.
@@ -518,28 +741,22 @@ function updateYBlockAttribute(
518
741
  currentAttributes: YBlockAttributes,
519
742
  cursorPosition: number | null
520
743
  ): void {
521
- const isRichText = isRichTextAttribute( blockName, attributeName );
522
- const currentAttribute = currentAttributes.get( attributeName );
744
+ const schema = getBlockAttributeSchema( blockName, attributeName );
523
745
 
524
- if (
525
- isRichText &&
526
- 'string' === typeof attributeValue &&
527
- currentAttributes.has( attributeName ) &&
528
- currentAttribute instanceof Y.Text
529
- ) {
530
- // Rich text values are stored as persistent Y.Text instances.
531
- // Update the value with a delta in place.
532
- mergeRichTextUpdate( currentAttribute, attributeValue, cursorPosition );
533
- } else {
534
- currentAttributes.set(
535
- attributeName,
536
- createNewYAttributeValue( blockName, attributeName, attributeValue )
537
- );
538
- }
746
+ mergeYValue(
747
+ schema,
748
+ attributeValue,
749
+ currentAttributes,
750
+ attributeName,
751
+ cursorPosition
752
+ );
539
753
  }
540
754
 
541
755
  // Cached block attribute types, populated once from getBlockTypes().
542
- let cachedBlockAttributeTypes: Map< string, Map< string, BlockAttributeType > >;
756
+ let cachedBlockAttributeSchemas: Map<
757
+ string,
758
+ Map< string, BlockAttributeSchema >
759
+ >;
543
760
 
544
761
  /**
545
762
  * Get the attribute type definition for a block attribute.
@@ -548,18 +765,18 @@ let cachedBlockAttributeTypes: Map< string, Map< string, BlockAttributeType > >;
548
765
  * @param attributeName The name of the attribute, e.g. 'content'.
549
766
  * @return The type definition of the attribute.
550
767
  */
551
- function getBlockAttributeType(
768
+ function getBlockAttributeSchema(
552
769
  blockName: string,
553
770
  attributeName: string
554
- ): BlockAttributeType | undefined {
555
- if ( ! cachedBlockAttributeTypes ) {
771
+ ): BlockAttributeSchema | undefined {
772
+ if ( ! cachedBlockAttributeSchemas ) {
556
773
  // Parse the attributes for all blocks once.
557
- cachedBlockAttributeTypes = new Map();
774
+ cachedBlockAttributeSchemas = new Map();
558
775
 
559
776
  for ( const blockType of getBlockTypes() as BlockType[] ) {
560
- cachedBlockAttributeTypes.set(
777
+ cachedBlockAttributeSchemas.set(
561
778
  blockType.name,
562
- new Map< string, BlockAttributeType >(
779
+ new Map< string, BlockAttributeSchema >(
563
780
  Object.entries( blockType.attributes ?? {} ).map(
564
781
  ( [ name, definition ] ) => {
565
782
  const { role, type, query } = definition;
@@ -571,7 +788,7 @@ function getBlockAttributeType(
571
788
  }
572
789
  }
573
790
 
574
- return cachedBlockAttributeTypes.get( blockName )?.get( attributeName );
791
+ return cachedBlockAttributeSchemas.get( blockName )?.get( attributeName );
575
792
  }
576
793
 
577
794
  /**
@@ -587,20 +804,24 @@ function isExpectedAttributeType(
587
804
  attributeName: string,
588
805
  attributeValue: unknown
589
806
  ): boolean {
590
- const expectedAttributeType = getBlockAttributeType(
591
- blockName,
592
- attributeName
593
- )?.type;
807
+ const schema = getBlockAttributeSchema( blockName, attributeName );
594
808
 
595
- if ( expectedAttributeType === 'rich-text' ) {
809
+ if ( schema?.type === 'rich-text' ) {
596
810
  return attributeValue instanceof Y.Text;
597
811
  }
598
812
 
599
- if ( expectedAttributeType === 'string' ) {
813
+ if ( schema?.type === 'string' ) {
600
814
  return typeof attributeValue === 'string';
601
815
  }
602
816
 
603
- // No other types comparisons use special logic.
817
+ if ( schema?.type === 'array' && schema.query ) {
818
+ return attributeValue instanceof Y.Array;
819
+ }
820
+
821
+ if ( schema?.type === 'object' && schema.query ) {
822
+ return attributeValue instanceof Y.Map;
823
+ }
824
+
604
825
  return true;
605
826
  }
606
827
 
@@ -613,22 +834,8 @@ function isExpectedAttributeType(
613
834
  * @return True if the attribute is local, false otherwise.
614
835
  */
615
836
  function isLocalAttribute( blockName: string, attributeName: string ): boolean {
616
- return 'local' === getBlockAttributeType( blockName, attributeName )?.role;
617
- }
618
-
619
- /**
620
- * Given a block name and attribute key, return true if the attribute is rich-text typed.
621
- *
622
- * @param blockName The name of the block, e.g. 'core/paragraph'.
623
- * @param attributeName The name of the attribute to check, e.g. 'content'.
624
- * @return True if the attribute is rich-text typed, false otherwise.
625
- */
626
- function isRichTextAttribute(
627
- blockName: string,
628
- attributeName: string
629
- ): boolean {
630
837
  return (
631
- 'rich-text' === getBlockAttributeType( blockName, attributeName )?.type
838
+ 'local' === getBlockAttributeSchema( blockName, attributeName )?.role
632
839
  );
633
840
  }
634
841
 
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';
@@ -428,6 +430,19 @@ export const defaultSyncConfig: SyncConfig = {
428
430
  getChangesFromCRDTDoc: defaultGetChangesFromCRDTDoc,
429
431
  };
430
432
 
433
+ /**
434
+ * This default collection sync config can be used to sync entity collections
435
+ * (e.g., block comments) where we are not interested in merging changes at the
436
+ * individual record level, but instead want to replace the entire collection
437
+ * when changes are detected.
438
+ */
439
+ export const defaultCollectionSyncConfig: SyncConfig = {
440
+ applyChangesToCRDTDoc: () => {},
441
+ getChangesFromCRDTDoc: () => ( {} ),
442
+ shouldSync: ( _: ObjectType, objectId: ObjectID | null ) =>
443
+ null === objectId,
444
+ };
445
+
431
446
  /**
432
447
  * Extract the raw string value from a property that may be a string or an object
433
448
  * with a `raw` property (`RenderedText`).