@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.
- package/CHANGELOG.md +7 -1
- package/build/entities.cjs +28 -20
- package/build/entities.cjs.map +2 -2
- package/build/hooks/index.cjs +3 -3
- package/build/hooks/index.cjs.map +1 -1
- package/build/hooks/use-entity-record.cjs +4 -4
- package/build/hooks/use-entity-record.cjs.map +2 -2
- package/build/hooks/use-entity-records.cjs +3 -3
- package/build/hooks/use-entity-records.cjs.map +2 -2
- package/build/hooks/use-query-select.cjs +2 -2
- package/build/hooks/use-query-select.cjs.map +2 -2
- package/build/hooks/use-resource-permissions.cjs +4 -4
- package/build/hooks/use-resource-permissions.cjs.map +2 -2
- package/build/queried-data/selectors.cjs +21 -17
- package/build/queried-data/selectors.cjs.map +2 -2
- package/build/resolvers.cjs +6 -6
- package/build/resolvers.cjs.map +2 -2
- package/build/selectors.cjs +4 -2
- package/build/selectors.cjs.map +2 -2
- package/build/utils/crdt-blocks.cjs +122 -30
- package/build/utils/crdt-blocks.cjs.map +2 -2
- package/build/utils/crdt.cjs +8 -0
- package/build/utils/crdt.cjs.map +2 -2
- package/build-module/entities.mjs +29 -20
- package/build-module/entities.mjs.map +2 -2
- package/build-module/hooks/index.mjs +6 -6
- package/build-module/hooks/index.mjs.map +2 -2
- package/build-module/hooks/use-entity-record.mjs +3 -3
- package/build-module/hooks/use-entity-record.mjs.map +2 -2
- package/build-module/hooks/use-entity-records.mjs +2 -2
- package/build-module/hooks/use-entity-records.mjs.map +2 -2
- package/build-module/hooks/use-query-select.mjs +1 -1
- package/build-module/hooks/use-query-select.mjs.map +1 -1
- package/build-module/hooks/use-resource-permissions.mjs +3 -3
- package/build-module/hooks/use-resource-permissions.mjs.map +2 -2
- package/build-module/queried-data/selectors.mjs +21 -17
- package/build-module/queried-data/selectors.mjs.map +2 -2
- package/build-module/resolvers.mjs +6 -6
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/selectors.mjs +4 -2
- package/build-module/selectors.mjs.map +2 -2
- package/build-module/utils/crdt-blocks.mjs +122 -30
- package/build-module/utils/crdt-blocks.mjs.map +2 -2
- package/build-module/utils/crdt.mjs +7 -0
- package/build-module/utils/crdt.mjs.map +2 -2
- package/build-types/entities.d.ts +51 -32
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/hooks/index.d.ts +3 -3
- package/build-types/hooks/index.d.ts.map +1 -1
- package/build-types/hooks/use-entity-record.d.ts +1 -1
- package/build-types/hooks/use-entity-record.d.ts.map +1 -1
- package/build-types/hooks/use-entity-records.d.ts +1 -1
- package/build-types/hooks/use-entity-records.d.ts.map +1 -1
- package/build-types/hooks/use-resource-permissions.d.ts +1 -1
- package/build-types/hooks/use-resource-permissions.d.ts.map +1 -1
- package/build-types/index.d.ts.map +1 -1
- package/build-types/queried-data/selectors.d.ts +5 -3
- package/build-types/queried-data/selectors.d.ts.map +1 -1
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/utils/crdt-blocks.d.ts.map +1 -1
- package/build-types/utils/crdt.d.ts +7 -0
- package/build-types/utils/crdt.d.ts.map +1 -1
- package/package.json +18 -18
- package/src/entities.js +16 -8
- package/src/hooks/index.ts +3 -3
- package/src/hooks/test/use-entity-records.js +1 -1
- package/src/hooks/use-entity-record.ts +1 -1
- package/src/hooks/use-entity-records.ts +1 -1
- package/src/hooks/use-query-select.ts +1 -1
- package/src/hooks/use-resource-permissions.ts +1 -1
- package/src/queried-data/selectors.js +32 -26
- package/src/queried-data/test/selectors.js +30 -21
- package/src/resolvers.js +6 -6
- package/src/selectors.ts +8 -2
- package/src/utils/crdt-blocks.ts +268 -61
- package/src/utils/crdt.ts +15 -0
- package/src/utils/test/crdt-blocks.ts +785 -6
- package/src/utils/test/crdt.ts +39 -1
- package/build/hooks/memoize.cjs +0 -38
- package/build/hooks/memoize.cjs.map +0 -7
- package/build-module/hooks/memoize.mjs +0 -7
- package/build-module/hooks/memoize.mjs.map +0 -7
- package/build-types/hooks/memoize.d.ts +0 -3
- package/build-types/hooks/memoize.d.ts.map +0 -1
- 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 (
|
|
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 );
|
package/src/utils/crdt-blocks.ts
CHANGED
|
@@ -23,14 +23,14 @@ interface BlockAttributes {
|
|
|
23
23
|
[ key: string ]: unknown;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
interface
|
|
26
|
+
interface BlockAttributeSchema {
|
|
27
27
|
role?: string;
|
|
28
28
|
type?: string;
|
|
29
|
-
query?: Record< string,
|
|
29
|
+
query?: Record< string, BlockAttributeSchema >;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
interface BlockType {
|
|
33
|
-
attributes?: Record< string,
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
-
|
|
262
|
-
return new Y.Text( attributeValue?.toString() ?? '' );
|
|
297
|
+
return yArray;
|
|
263
298
|
}
|
|
264
299
|
|
|
265
|
-
|
|
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
|
-
*
|
|
585
|
+
* Merge an incoming plain array into an existing Y.Array in-place.
|
|
503
586
|
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
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
|
|
522
|
-
const currentAttribute = currentAttributes.get( attributeName );
|
|
744
|
+
const schema = getBlockAttributeSchema( blockName, attributeName );
|
|
523
745
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
currentAttributes
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
|
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
|
|
768
|
+
function getBlockAttributeSchema(
|
|
552
769
|
blockName: string,
|
|
553
770
|
attributeName: string
|
|
554
|
-
):
|
|
555
|
-
if ( !
|
|
771
|
+
): BlockAttributeSchema | undefined {
|
|
772
|
+
if ( ! cachedBlockAttributeSchemas ) {
|
|
556
773
|
// Parse the attributes for all blocks once.
|
|
557
|
-
|
|
774
|
+
cachedBlockAttributeSchemas = new Map();
|
|
558
775
|
|
|
559
776
|
for ( const blockType of getBlockTypes() as BlockType[] ) {
|
|
560
|
-
|
|
777
|
+
cachedBlockAttributeSchemas.set(
|
|
561
778
|
blockType.name,
|
|
562
|
-
new Map< string,
|
|
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
|
|
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
|
|
591
|
-
blockName,
|
|
592
|
-
attributeName
|
|
593
|
-
)?.type;
|
|
807
|
+
const schema = getBlockAttributeSchema( blockName, attributeName );
|
|
594
808
|
|
|
595
|
-
if (
|
|
809
|
+
if ( schema?.type === 'rich-text' ) {
|
|
596
810
|
return attributeValue instanceof Y.Text;
|
|
597
811
|
}
|
|
598
812
|
|
|
599
|
-
if (
|
|
813
|
+
if ( schema?.type === 'string' ) {
|
|
600
814
|
return typeof attributeValue === 'string';
|
|
601
815
|
}
|
|
602
816
|
|
|
603
|
-
|
|
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
|
-
'
|
|
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`).
|