@wordpress/core-data 7.38.0 → 7.39.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 (109) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/actions.cjs +17 -6
  3. package/build/actions.cjs.map +2 -2
  4. package/build/awareness/base-awareness.cjs +62 -0
  5. package/build/awareness/base-awareness.cjs.map +7 -0
  6. package/build/awareness/config.cjs +34 -0
  7. package/build/awareness/config.cjs.map +7 -0
  8. package/build/awareness/post-editor-awareness.cjs +130 -0
  9. package/build/awareness/post-editor-awareness.cjs.map +7 -0
  10. package/build/awareness/types.cjs +19 -0
  11. package/build/awareness/types.cjs.map +7 -0
  12. package/build/awareness/utils.cjs +116 -0
  13. package/build/awareness/utils.cjs.map +7 -0
  14. package/build/entities.cjs +27 -2
  15. package/build/entities.cjs.map +2 -2
  16. package/build/resolvers.cjs +43 -2
  17. package/build/resolvers.cjs.map +2 -2
  18. package/build/types.cjs.map +1 -1
  19. package/build/utils/block-selection-history.cjs +101 -0
  20. package/build/utils/block-selection-history.cjs.map +7 -0
  21. package/build/utils/crdt-selection.cjs +139 -0
  22. package/build/utils/crdt-selection.cjs.map +7 -0
  23. package/build/utils/crdt-user-selections.cjs +171 -0
  24. package/build/utils/crdt-user-selections.cjs.map +7 -0
  25. package/build/utils/crdt-utils.cjs +29 -0
  26. package/build/utils/crdt-utils.cjs.map +3 -3
  27. package/build/utils/crdt.cjs +16 -4
  28. package/build/utils/crdt.cjs.map +2 -2
  29. package/build-module/actions.mjs +17 -6
  30. package/build-module/actions.mjs.map +2 -2
  31. package/build-module/awareness/base-awareness.mjs +35 -0
  32. package/build-module/awareness/base-awareness.mjs.map +7 -0
  33. package/build-module/awareness/config.mjs +8 -0
  34. package/build-module/awareness/config.mjs.map +7 -0
  35. package/build-module/awareness/post-editor-awareness.mjs +111 -0
  36. package/build-module/awareness/post-editor-awareness.mjs.map +7 -0
  37. package/build-module/awareness/types.mjs +1 -0
  38. package/build-module/awareness/types.mjs.map +7 -0
  39. package/build-module/awareness/utils.mjs +90 -0
  40. package/build-module/awareness/utils.mjs.map +7 -0
  41. package/build-module/entities.mjs +28 -2
  42. package/build-module/entities.mjs.map +2 -2
  43. package/build-module/resolvers.mjs +43 -2
  44. package/build-module/resolvers.mjs.map +2 -2
  45. package/build-module/utils/block-selection-history.mjs +75 -0
  46. package/build-module/utils/block-selection-history.mjs.map +7 -0
  47. package/build-module/utils/crdt-selection.mjs +115 -0
  48. package/build-module/utils/crdt-selection.mjs.map +7 -0
  49. package/build-module/utils/crdt-user-selections.mjs +144 -0
  50. package/build-module/utils/crdt-user-selections.mjs.map +7 -0
  51. package/build-module/utils/crdt-utils.mjs +28 -0
  52. package/build-module/utils/crdt-utils.mjs.map +2 -2
  53. package/build-module/utils/crdt.mjs +18 -3
  54. package/build-module/utils/crdt.mjs.map +2 -2
  55. package/build-types/actions.d.ts.map +1 -1
  56. package/build-types/awareness/base-awareness.d.ts +19 -0
  57. package/build-types/awareness/base-awareness.d.ts.map +1 -0
  58. package/build-types/awareness/config.d.ts +9 -0
  59. package/build-types/awareness/config.d.ts.map +1 -0
  60. package/build-types/awareness/post-editor-awareness.d.ts +38 -0
  61. package/build-types/awareness/post-editor-awareness.d.ts.map +1 -0
  62. package/build-types/awareness/types.d.ts +32 -0
  63. package/build-types/awareness/types.d.ts.map +1 -0
  64. package/build-types/awareness/utils.d.ts +22 -0
  65. package/build-types/awareness/utils.d.ts.map +1 -0
  66. package/build-types/entities.d.ts.map +1 -1
  67. package/build-types/entity-types/test/attachment.test.d.ts +10 -0
  68. package/build-types/entity-types/test/attachment.test.d.ts.map +1 -0
  69. package/build-types/index.d.ts.map +1 -1
  70. package/build-types/resolvers.d.ts.map +1 -1
  71. package/build-types/types.d.ts +12 -2
  72. package/build-types/types.d.ts.map +1 -1
  73. package/build-types/utils/block-selection-history.d.ts +47 -0
  74. package/build-types/utils/block-selection-history.d.ts.map +1 -0
  75. package/build-types/utils/crdt-selection.d.ts +16 -0
  76. package/build-types/utils/crdt-selection.d.ts.map +1 -0
  77. package/build-types/utils/crdt-user-selections.d.ts +66 -0
  78. package/build-types/utils/crdt-user-selections.d.ts.map +1 -0
  79. package/build-types/utils/crdt-utils.d.ts +12 -0
  80. package/build-types/utils/crdt-utils.d.ts.map +1 -1
  81. package/build-types/utils/crdt.d.ts +8 -14
  82. package/build-types/utils/crdt.d.ts.map +1 -1
  83. package/build-types/utils/test/block-selection-history.test.d.ts +2 -0
  84. package/build-types/utils/test/block-selection-history.test.d.ts.map +1 -0
  85. package/build-types/utils/test/crdt-blocks.d.ts +2 -0
  86. package/build-types/utils/test/crdt-blocks.d.ts.map +1 -0
  87. package/build-types/utils/test/crdt.d.ts +2 -0
  88. package/build-types/utils/test/crdt.d.ts.map +1 -0
  89. package/package.json +21 -18
  90. package/src/actions.js +40 -7
  91. package/src/awareness/base-awareness.ts +50 -0
  92. package/src/awareness/config.ts +9 -0
  93. package/src/awareness/post-editor-awareness.ts +167 -0
  94. package/src/awareness/types.ts +38 -0
  95. package/src/awareness/utils.ts +159 -0
  96. package/src/entities.js +32 -2
  97. package/src/entity-types/test/attachment.test.ts +4 -4
  98. package/src/resolvers.js +53 -1
  99. package/src/test/actions.js +402 -0
  100. package/src/test/entity-provider.js +2 -0
  101. package/src/test/resolvers.js +4 -0
  102. package/src/types.ts +12 -3
  103. package/src/utils/block-selection-history.ts +176 -0
  104. package/src/utils/crdt-selection.ts +205 -0
  105. package/src/utils/crdt-user-selections.ts +336 -0
  106. package/src/utils/crdt-utils.ts +54 -0
  107. package/src/utils/crdt.ts +36 -3
  108. package/src/utils/test/block-selection-history.test.ts +764 -0
  109. package/src/utils/test/crdt-blocks.ts +11 -4
@@ -0,0 +1,115 @@
1
+ // packages/core-data/src/utils/crdt-selection.ts
2
+ import { dispatch, select } from "@wordpress/data";
3
+ import { store as blockEditorStore } from "@wordpress/block-editor";
4
+ import { isUnmodifiedBlock } from "@wordpress/blocks";
5
+ import { Y } from "@wordpress/sync";
6
+ import {
7
+ createBlockSelectionHistory,
8
+ YSelectionType
9
+ } from "./block-selection-history.mjs";
10
+ import { findBlockByClientIdInDoc } from "./crdt-utils.mjs";
11
+ var selectionHistoryMap = /* @__PURE__ */ new WeakMap();
12
+ function getBlockSelectionHistory(ydoc) {
13
+ let history = selectionHistoryMap.get(ydoc);
14
+ if (!history) {
15
+ history = createBlockSelectionHistory(ydoc);
16
+ selectionHistoryMap.set(ydoc, history);
17
+ }
18
+ return history;
19
+ }
20
+ function getSelectionHistory(ydoc) {
21
+ return getBlockSelectionHistory(ydoc).getSelectionHistory();
22
+ }
23
+ function updateSelectionHistory(ydoc, wpSelection) {
24
+ return getBlockSelectionHistory(ydoc).updateSelection(wpSelection);
25
+ }
26
+ function convertYSelectionToBlockSelection(ySelection, ydoc) {
27
+ if (ySelection.type === YSelectionType.RelativeSelection) {
28
+ const { relativePosition, attributeKey, clientId } = ySelection;
29
+ const absolutePosition = Y.createAbsolutePositionFromRelativePosition(
30
+ relativePosition,
31
+ ydoc
32
+ );
33
+ if (absolutePosition) {
34
+ return {
35
+ clientId,
36
+ attributeKey,
37
+ offset: absolutePosition.index
38
+ };
39
+ }
40
+ } else if (ySelection.type === YSelectionType.BlockSelection) {
41
+ return {
42
+ clientId: ySelection.clientId,
43
+ attributeKey: void 0,
44
+ offset: void 0
45
+ };
46
+ }
47
+ return null;
48
+ }
49
+ function findSelectionFromHistory(ydoc, selectionHistory) {
50
+ for (const positionToTry of selectionHistory) {
51
+ const { start, end } = positionToTry;
52
+ const startBlock = findBlockByClientIdInDoc(start.clientId, ydoc);
53
+ const endBlock = findBlockByClientIdInDoc(end.clientId, ydoc);
54
+ if (!startBlock || !endBlock) {
55
+ continue;
56
+ }
57
+ const startBlockSelection = convertYSelectionToBlockSelection(
58
+ start,
59
+ ydoc
60
+ );
61
+ const endBlockSelection = convertYSelectionToBlockSelection(
62
+ end,
63
+ ydoc
64
+ );
65
+ if (startBlockSelection === null || endBlockSelection === null) {
66
+ continue;
67
+ }
68
+ return {
69
+ selectionStart: startBlockSelection,
70
+ selectionEnd: endBlockSelection
71
+ };
72
+ }
73
+ return null;
74
+ }
75
+ function restoreSelection(selectionHistory, ydoc) {
76
+ const selectionToRestore = findSelectionFromHistory(
77
+ ydoc,
78
+ selectionHistory
79
+ );
80
+ if (selectionToRestore === null) {
81
+ return;
82
+ }
83
+ const { getBlock } = select(blockEditorStore);
84
+ const { resetSelection } = dispatch(blockEditorStore);
85
+ const { selectionStart, selectionEnd } = selectionToRestore;
86
+ const isSelectionInSameBlock = selectionStart.clientId === selectionEnd.clientId;
87
+ if (isSelectionInSameBlock) {
88
+ const block = getBlock(selectionStart.clientId);
89
+ const isBlockEmpty = block && isUnmodifiedBlock(block);
90
+ const isBeginningOfEmptyBlock = 0 === selectionStart.offset && 0 === selectionEnd.offset && isBlockEmpty;
91
+ if (isBeginningOfEmptyBlock) {
92
+ const selectionStartWithoutOffset = {
93
+ clientId: selectionStart.clientId
94
+ };
95
+ const selectionEndWithoutOffset = {
96
+ clientId: selectionEnd.clientId
97
+ };
98
+ resetSelection(
99
+ selectionStartWithoutOffset,
100
+ selectionEndWithoutOffset,
101
+ 0
102
+ );
103
+ } else {
104
+ resetSelection(selectionStart, selectionEnd, 0);
105
+ }
106
+ } else {
107
+ resetSelection(selectionEnd, selectionEnd, 0);
108
+ }
109
+ }
110
+ export {
111
+ getSelectionHistory,
112
+ restoreSelection,
113
+ updateSelectionHistory
114
+ };
115
+ //# sourceMappingURL=crdt-selection.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/crdt-selection.ts"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { dispatch, select } from '@wordpress/data';\n// @ts-expect-error No exported types.\nimport { store as blockEditorStore } from '@wordpress/block-editor';\n// @ts-expect-error No exported types.\nimport { isUnmodifiedBlock } from '@wordpress/blocks';\nimport { type CRDTDoc, Y } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport {\n\tcreateBlockSelectionHistory,\n\tYSelectionType,\n\ttype BlockSelectionHistory,\n\ttype YFullSelection,\n\ttype YSelection,\n} from './block-selection-history';\nimport { findBlockByClientIdInDoc } from './crdt-utils';\nimport type { WPBlockSelection, WPSelection } from '../types';\n\n// WeakMap to store BlockSelectionHistory instances per Y.Doc\nconst selectionHistoryMap = new WeakMap< CRDTDoc, BlockSelectionHistory >();\n\n/**\n * Get or create a BlockSelectionHistory instance for a given Y.Doc.\n *\n * @param ydoc The Y.Doc to get the selection history for\n * @return The BlockSelectionHistory instance\n */\nfunction getBlockSelectionHistory( ydoc: CRDTDoc ): BlockSelectionHistory {\n\tlet history = selectionHistoryMap.get( ydoc );\n\n\tif ( ! history ) {\n\t\thistory = createBlockSelectionHistory( ydoc );\n\t\tselectionHistoryMap.set( ydoc, history );\n\t}\n\n\treturn history;\n}\n\nexport function getSelectionHistory( ydoc: CRDTDoc ): YFullSelection[] {\n\treturn getBlockSelectionHistory( ydoc ).getSelectionHistory();\n}\n\nexport function updateSelectionHistory(\n\tydoc: CRDTDoc,\n\twpSelection: WPSelection\n): void {\n\treturn getBlockSelectionHistory( ydoc ).updateSelection( wpSelection );\n}\n\n/**\n * Convert a YSelection to a WPBlockSelection.\n * @param ySelection The YSelection (relative) to convert\n * @param ydoc The Y.Doc to convert the selection to a block selection for\n * @return The converted WPBlockSelection, or null if the conversion fails\n */\nfunction convertYSelectionToBlockSelection(\n\tySelection: YSelection,\n\tydoc: Y.Doc\n): WPBlockSelection | null {\n\tif ( ySelection.type === YSelectionType.RelativeSelection ) {\n\t\tconst { relativePosition, attributeKey, clientId } = ySelection;\n\n\t\tconst absolutePosition = Y.createAbsolutePositionFromRelativePosition(\n\t\t\trelativePosition,\n\t\t\tydoc\n\t\t);\n\n\t\tif ( absolutePosition ) {\n\t\t\treturn {\n\t\t\t\tclientId,\n\t\t\t\tattributeKey,\n\t\t\t\toffset: absolutePosition.index,\n\t\t\t};\n\t\t}\n\t} else if ( ySelection.type === YSelectionType.BlockSelection ) {\n\t\treturn {\n\t\t\tclientId: ySelection.clientId,\n\t\t\tattributeKey: undefined,\n\t\t\toffset: undefined,\n\t\t};\n\t}\n\n\treturn null;\n}\n\n/**\n * Given a Y.Doc and a selection history, find the most recent selection\n * that exists in the document. Skip any selections that are not in the document.\n * @param ydoc The Y.Doc to find the selection in\n * @param selectionHistory The selection history to check\n * @return The most recent selection that exists in the document, or null if no selection exists.\n */\nfunction findSelectionFromHistory(\n\tydoc: Y.Doc,\n\tselectionHistory: YFullSelection[]\n): WPSelection | null {\n\t// Try each position until we find one that exists in the document\n\tfor ( const positionToTry of selectionHistory ) {\n\t\tconst { start, end } = positionToTry;\n\t\tconst startBlock = findBlockByClientIdInDoc( start.clientId, ydoc );\n\t\tconst endBlock = findBlockByClientIdInDoc( end.clientId, ydoc );\n\n\t\tif ( ! startBlock || ! endBlock ) {\n\t\t\t// This block no longer exists, skip it.\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst startBlockSelection = convertYSelectionToBlockSelection(\n\t\t\tstart,\n\t\t\tydoc\n\t\t);\n\t\tconst endBlockSelection = convertYSelectionToBlockSelection(\n\t\t\tend,\n\t\t\tydoc\n\t\t);\n\n\t\tif ( startBlockSelection === null || endBlockSelection === null ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn {\n\t\t\tselectionStart: startBlockSelection,\n\t\t\tselectionEnd: endBlockSelection,\n\t\t};\n\t}\n\n\treturn null;\n}\n\n/**\n * Restore the selection to the most recent selection in history that is\n * available in the document.\n * @param selectionHistory The selection history to restore\n * @param ydoc The Y.Doc where blocks are stored\n */\nexport function restoreSelection(\n\tselectionHistory: YFullSelection[],\n\tydoc: Y.Doc\n): void {\n\t// Find the most recent selection in history that is available in\n\t// the document.\n\tconst selectionToRestore = findSelectionFromHistory(\n\t\tydoc,\n\t\tselectionHistory\n\t);\n\n\tif ( selectionToRestore === null ) {\n\t\t// Case 1: No blocks in history are available for restoration.\n\t\t// Do nothing.\n\t\treturn;\n\t}\n\n\tconst { getBlock } = select( blockEditorStore );\n\tconst { resetSelection } = dispatch( blockEditorStore );\n\tconst { selectionStart, selectionEnd } = selectionToRestore;\n\tconst isSelectionInSameBlock =\n\t\tselectionStart.clientId === selectionEnd.clientId;\n\n\tif ( isSelectionInSameBlock ) {\n\t\t// Case 2: After content is restored, the selection is available\n\t\t// within the same block\n\n\t\tconst block = getBlock( selectionStart.clientId );\n\t\tconst isBlockEmpty = block && isUnmodifiedBlock( block );\n\t\tconst isBeginningOfEmptyBlock =\n\t\t\t0 === selectionStart.offset &&\n\t\t\t0 === selectionEnd.offset &&\n\t\t\tisBlockEmpty;\n\n\t\tif ( isBeginningOfEmptyBlock ) {\n\t\t\t// Case 2a: When the content in a block has been removed after an\n\t\t\t// undo, WordPress will set the selection to the block's client ID\n\t\t\t// with an undefined startOffset and endOffset.\n\t\t\t//\n\t\t\t// To match the default behavior and tests, exclude the selection\n\t\t\t// offset when resetting to position 0.\n\t\t\tconst selectionStartWithoutOffset = {\n\t\t\t\tclientId: selectionStart.clientId,\n\t\t\t};\n\t\t\tconst selectionEndWithoutOffset = {\n\t\t\t\tclientId: selectionEnd.clientId,\n\t\t\t};\n\n\t\t\tresetSelection(\n\t\t\t\tselectionStartWithoutOffset,\n\t\t\t\tselectionEndWithoutOffset,\n\t\t\t\t0\n\t\t\t);\n\t\t} else {\n\t\t\t// Case 2b: Otherwise, reset including the saved selection offset.\n\t\t\tresetSelection( selectionStart, selectionEnd, 0 );\n\t\t}\n\t} else {\n\t\t// Case 3: A multi-block selection was made. resetSelection() can only\n\t\t// restore selections within the same block.\n\t\t// When a multi-block selection is made, selectionEnd represents\n\t\t// where the user's cursor ended.\n\t\tresetSelection( selectionEnd, selectionEnd, 0 );\n\t}\n}\n"],
5
+ "mappings": ";AAGA,SAAS,UAAU,cAAc;AAEjC,SAAS,SAAS,wBAAwB;AAE1C,SAAS,yBAAyB;AAClC,SAAuB,SAAS;AAKhC;AAAA,EACC;AAAA,EACA;AAAA,OAIM;AACP,SAAS,gCAAgC;AAIzC,IAAM,sBAAsB,oBAAI,QAA0C;AAQ1E,SAAS,yBAA0B,MAAuC;AACzE,MAAI,UAAU,oBAAoB,IAAK,IAAK;AAE5C,MAAK,CAAE,SAAU;AAChB,cAAU,4BAA6B,IAAK;AAC5C,wBAAoB,IAAK,MAAM,OAAQ;AAAA,EACxC;AAEA,SAAO;AACR;AAEO,SAAS,oBAAqB,MAAkC;AACtE,SAAO,yBAA0B,IAAK,EAAE,oBAAoB;AAC7D;AAEO,SAAS,uBACf,MACA,aACO;AACP,SAAO,yBAA0B,IAAK,EAAE,gBAAiB,WAAY;AACtE;AAQA,SAAS,kCACR,YACA,MAC0B;AAC1B,MAAK,WAAW,SAAS,eAAe,mBAAoB;AAC3D,UAAM,EAAE,kBAAkB,cAAc,SAAS,IAAI;AAErD,UAAM,mBAAmB,EAAE;AAAA,MAC1B;AAAA,MACA;AAAA,IACD;AAEA,QAAK,kBAAmB;AACvB,aAAO;AAAA,QACN;AAAA,QACA;AAAA,QACA,QAAQ,iBAAiB;AAAA,MAC1B;AAAA,IACD;AAAA,EACD,WAAY,WAAW,SAAS,eAAe,gBAAiB;AAC/D,WAAO;AAAA,MACN,UAAU,WAAW;AAAA,MACrB,cAAc;AAAA,MACd,QAAQ;AAAA,IACT;AAAA,EACD;AAEA,SAAO;AACR;AASA,SAAS,yBACR,MACA,kBACqB;AAErB,aAAY,iBAAiB,kBAAmB;AAC/C,UAAM,EAAE,OAAO,IAAI,IAAI;AACvB,UAAM,aAAa,yBAA0B,MAAM,UAAU,IAAK;AAClE,UAAM,WAAW,yBAA0B,IAAI,UAAU,IAAK;AAE9D,QAAK,CAAE,cAAc,CAAE,UAAW;AAEjC;AAAA,IACD;AAEA,UAAM,sBAAsB;AAAA,MAC3B;AAAA,MACA;AAAA,IACD;AACA,UAAM,oBAAoB;AAAA,MACzB;AAAA,MACA;AAAA,IACD;AAEA,QAAK,wBAAwB,QAAQ,sBAAsB,MAAO;AACjE;AAAA,IACD;AAEA,WAAO;AAAA,MACN,gBAAgB;AAAA,MAChB,cAAc;AAAA,IACf;AAAA,EACD;AAEA,SAAO;AACR;AAQO,SAAS,iBACf,kBACA,MACO;AAGP,QAAM,qBAAqB;AAAA,IAC1B;AAAA,IACA;AAAA,EACD;AAEA,MAAK,uBAAuB,MAAO;AAGlC;AAAA,EACD;AAEA,QAAM,EAAE,SAAS,IAAI,OAAQ,gBAAiB;AAC9C,QAAM,EAAE,eAAe,IAAI,SAAU,gBAAiB;AACtD,QAAM,EAAE,gBAAgB,aAAa,IAAI;AACzC,QAAM,yBACL,eAAe,aAAa,aAAa;AAE1C,MAAK,wBAAyB;AAI7B,UAAM,QAAQ,SAAU,eAAe,QAAS;AAChD,UAAM,eAAe,SAAS,kBAAmB,KAAM;AACvD,UAAM,0BACL,MAAM,eAAe,UACrB,MAAM,aAAa,UACnB;AAED,QAAK,yBAA0B;AAO9B,YAAM,8BAA8B;AAAA,QACnC,UAAU,eAAe;AAAA,MAC1B;AACA,YAAM,4BAA4B;AAAA,QACjC,UAAU,aAAa;AAAA,MACxB;AAEA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,IACD,OAAO;AAEN,qBAAgB,gBAAgB,cAAc,CAAE;AAAA,IACjD;AAAA,EACD,OAAO;AAKN,mBAAgB,cAAc,cAAc,CAAE;AAAA,EAC/C;AACD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,144 @@
1
+ // packages/core-data/src/utils/crdt-user-selections.ts
2
+ import { Y, CRDT_RECORD_MAP_KEY } from "@wordpress/sync";
3
+ import { getRootMap } from "./crdt-utils.mjs";
4
+ var SelectionType = /* @__PURE__ */ ((SelectionType2) => {
5
+ SelectionType2["None"] = "none";
6
+ SelectionType2["Cursor"] = "cursor";
7
+ SelectionType2["SelectionInOneBlock"] = "selection-in-one-block";
8
+ SelectionType2["SelectionInMultipleBlocks"] = "selection-in-multiple-blocks";
9
+ SelectionType2["WholeBlock"] = "whole-block";
10
+ return SelectionType2;
11
+ })(SelectionType || {});
12
+ function getSelectionState(selectionStart, selectionEnd, yDoc) {
13
+ const ymap = getRootMap(yDoc, CRDT_RECORD_MAP_KEY);
14
+ const yBlocks = ymap.get("blocks") ?? new Y.Array();
15
+ const isSelectionEmpty = Object.keys(selectionStart).length === 0;
16
+ const noSelection = {
17
+ type: "none" /* None */
18
+ };
19
+ if (isSelectionEmpty) {
20
+ return noSelection;
21
+ }
22
+ const isSelectionInOneBlock = selectionStart.clientId === selectionEnd.clientId;
23
+ const isCursorOnly = isSelectionInOneBlock && selectionStart.offset === selectionEnd.offset;
24
+ const isSelectionAWholeBlock = isSelectionInOneBlock && selectionStart.offset === void 0 && selectionEnd.offset === void 0;
25
+ if (isSelectionAWholeBlock) {
26
+ return {
27
+ type: "whole-block" /* WholeBlock */,
28
+ blockId: selectionStart.clientId
29
+ };
30
+ } else if (isCursorOnly) {
31
+ const cursorPosition = getCursorPosition(selectionStart, yBlocks);
32
+ if (!cursorPosition) {
33
+ return noSelection;
34
+ }
35
+ return {
36
+ type: "cursor" /* Cursor */,
37
+ blockId: selectionStart.clientId,
38
+ cursorPosition
39
+ };
40
+ } else if (isSelectionInOneBlock) {
41
+ const cursorStartPosition2 = getCursorPosition(
42
+ selectionStart,
43
+ yBlocks
44
+ );
45
+ const cursorEndPosition2 = getCursorPosition(selectionEnd, yBlocks);
46
+ if (!cursorStartPosition2 || !cursorEndPosition2) {
47
+ return noSelection;
48
+ }
49
+ return {
50
+ type: "selection-in-one-block" /* SelectionInOneBlock */,
51
+ blockId: selectionStart.clientId,
52
+ cursorStartPosition: cursorStartPosition2,
53
+ cursorEndPosition: cursorEndPosition2
54
+ };
55
+ }
56
+ const cursorStartPosition = getCursorPosition(selectionStart, yBlocks);
57
+ const cursorEndPosition = getCursorPosition(selectionEnd, yBlocks);
58
+ if (!cursorStartPosition || !cursorEndPosition) {
59
+ return noSelection;
60
+ }
61
+ return {
62
+ type: "selection-in-multiple-blocks" /* SelectionInMultipleBlocks */,
63
+ blockStartId: selectionStart.clientId,
64
+ blockEndId: selectionEnd.clientId,
65
+ cursorStartPosition,
66
+ cursorEndPosition
67
+ };
68
+ }
69
+ function getCursorPosition(selection, blocks) {
70
+ const block = findBlockByClientId(selection.clientId, blocks);
71
+ if (!block || !selection.attributeKey || void 0 === selection.offset) {
72
+ return null;
73
+ }
74
+ const attributes = block.get("attributes");
75
+ const currentYText = attributes?.get(selection.attributeKey);
76
+ const relativePosition = Y.createRelativePositionFromTypeIndex(
77
+ currentYText,
78
+ selection.offset
79
+ );
80
+ return {
81
+ relativePosition,
82
+ absoluteOffset: selection.offset
83
+ };
84
+ }
85
+ function findBlockByClientId(blockId, blocks) {
86
+ for (const block of blocks) {
87
+ if (block.get("clientId") === blockId) {
88
+ return block;
89
+ }
90
+ const innerBlocks = block.get("innerBlocks");
91
+ if (innerBlocks && innerBlocks.length > 0) {
92
+ const innerBlock = findBlockByClientId(blockId, innerBlocks);
93
+ if (innerBlock) {
94
+ return innerBlock;
95
+ }
96
+ }
97
+ }
98
+ return null;
99
+ }
100
+ function areSelectionsStatesEqual(selection1, selection2) {
101
+ if (selection1.type !== selection2.type) {
102
+ return false;
103
+ }
104
+ switch (selection1.type) {
105
+ case "none" /* None */:
106
+ return true;
107
+ case "cursor" /* Cursor */:
108
+ return selection1.blockId === selection2.blockId && areCursorPositionsEqual(
109
+ selection1.cursorPosition,
110
+ selection2.cursorPosition
111
+ );
112
+ case "selection-in-one-block" /* SelectionInOneBlock */:
113
+ return selection1.blockId === selection2.blockId && areCursorPositionsEqual(
114
+ selection1.cursorStartPosition,
115
+ selection2.cursorStartPosition
116
+ ) && areCursorPositionsEqual(
117
+ selection1.cursorEndPosition,
118
+ selection2.cursorEndPosition
119
+ );
120
+ case "selection-in-multiple-blocks" /* SelectionInMultipleBlocks */:
121
+ return selection1.blockStartId === selection2.blockStartId && selection1.blockEndId === selection2.blockEndId && areCursorPositionsEqual(
122
+ selection1.cursorStartPosition,
123
+ selection2.cursorStartPosition
124
+ ) && areCursorPositionsEqual(
125
+ selection1.cursorEndPosition,
126
+ selection2.cursorEndPosition
127
+ );
128
+ case "whole-block" /* WholeBlock */:
129
+ return selection1.blockId === selection2.blockId;
130
+ default:
131
+ return false;
132
+ }
133
+ }
134
+ function areCursorPositionsEqual(cursorPosition1, cursorPosition2) {
135
+ const isRelativePositionEqual = JSON.stringify(cursorPosition1.relativePosition) === JSON.stringify(cursorPosition2.relativePosition);
136
+ const isAbsoluteOffsetEqual = cursorPosition1.absoluteOffset === cursorPosition2.absoluteOffset;
137
+ return isRelativePositionEqual && isAbsoluteOffsetEqual;
138
+ }
139
+ export {
140
+ SelectionType,
141
+ areSelectionsStatesEqual,
142
+ getSelectionState
143
+ };
144
+ //# sourceMappingURL=crdt-user-selections.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/crdt-user-selections.ts"],
4
+ "sourcesContent": ["/**\n * External dependencies\n */\nimport { Y, CRDT_RECORD_MAP_KEY } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport type { YPostRecord } from './crdt';\nimport type { YBlock, YBlocks } from './crdt-blocks';\nimport { getRootMap } from './crdt-utils';\nimport type { WPBlockSelection } from '../types';\n\n/**\n * The type of selection.\n */\nexport enum SelectionType {\n\tNone = 'none',\n\tCursor = 'cursor',\n\tSelectionInOneBlock = 'selection-in-one-block',\n\tSelectionInMultipleBlocks = 'selection-in-multiple-blocks',\n\tWholeBlock = 'whole-block',\n}\n\n/**\n * The position of the cursor.\n */\nexport type CursorPosition = {\n\trelativePosition: Y.RelativePosition;\n\n\t// Also store the absolute offset index of the cursor from the perspective\n\t// of the user who is updating the selection.\n\t//\n\t// Do not use this value directly, instead use `createAbsolutePositionFromRelativePosition()`\n\t// on relativePosition for the most up-to-date positioning.\n\t//\n\t// This is used because local Y.Text changes (e.g. adding or deleting a character)\n\t// can result in the same relative position if it is pinned to an unchanged\n\t// character. With both of these values as editor state, a change in perceived\n\t// position will always result in a redraw.\n\tabsoluteOffset: number;\n};\n\nexport type SelectionNone = {\n\t// The user has not made a selection.\n\ttype: SelectionType.None;\n};\n\nexport type SelectionCursor = {\n\t// The user has a cursor position in a block with no text highlighted.\n\ttype: SelectionType.Cursor;\n\tblockId: string;\n\tcursorPosition: CursorPosition;\n};\n\nexport type SelectionInOneBlock = {\n\t// The user has highlighted text in a single block.\n\ttype: SelectionType.SelectionInOneBlock;\n\tblockId: string;\n\tcursorStartPosition: CursorPosition;\n\tcursorEndPosition: CursorPosition;\n};\n\nexport type SelectionInMultipleBlocks = {\n\t// The user has highlighted text over multiple blocks.\n\ttype: SelectionType.SelectionInMultipleBlocks;\n\tblockStartId: string;\n\tblockEndId: string;\n\tcursorStartPosition: CursorPosition;\n\tcursorEndPosition: CursorPosition;\n};\n\nexport type SelectionWholeBlock = {\n\t// The user has a non-text block selected, like an image block.\n\ttype: SelectionType.WholeBlock;\n\tblockId: string;\n};\n\nexport type SelectionState =\n\t| SelectionNone\n\t| SelectionCursor\n\t| SelectionInOneBlock\n\t| SelectionInMultipleBlocks\n\t| SelectionWholeBlock;\n\n/**\n * Converts WordPress block editor selection to a SelectionState.\n *\n * @param selectionStart - The start position of the selection\n * @param selectionEnd - The end position of the selection\n * @param yDoc - The Yjs document\n * @return The SelectionState\n */\nexport function getSelectionState(\n\tselectionStart: WPBlockSelection,\n\tselectionEnd: WPBlockSelection,\n\tyDoc: Y.Doc\n): SelectionState {\n\tconst ymap = getRootMap< YPostRecord >( yDoc, CRDT_RECORD_MAP_KEY );\n\tconst yBlocks = ymap.get( 'blocks' ) ?? new Y.Array< YBlock >();\n\n\tconst isSelectionEmpty = Object.keys( selectionStart ).length === 0;\n\tconst noSelection: SelectionNone = {\n\t\ttype: SelectionType.None,\n\t};\n\n\tif ( isSelectionEmpty ) {\n\t\t// Case 1: No selection\n\t\treturn noSelection;\n\t}\n\n\t// When the page initially loads, selectionStart can contain an empty object `{}`.\n\tconst isSelectionInOneBlock =\n\t\tselectionStart.clientId === selectionEnd.clientId;\n\tconst isCursorOnly =\n\t\tisSelectionInOneBlock && selectionStart.offset === selectionEnd.offset;\n\tconst isSelectionAWholeBlock =\n\t\tisSelectionInOneBlock &&\n\t\tselectionStart.offset === undefined &&\n\t\tselectionEnd.offset === undefined;\n\n\tif ( isSelectionAWholeBlock ) {\n\t\t// Case 2: A whole block is selected.\n\t\treturn {\n\t\t\ttype: SelectionType.WholeBlock,\n\t\t\tblockId: selectionStart.clientId,\n\t\t};\n\t} else if ( isCursorOnly ) {\n\t\t// Case 3: Cursor only, no text selected\n\t\tconst cursorPosition = getCursorPosition( selectionStart, yBlocks );\n\n\t\tif ( ! cursorPosition ) {\n\t\t\t// If we can't find the cursor position in block text, treat it as a non-selection.\n\t\t\treturn noSelection;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: SelectionType.Cursor,\n\t\t\tblockId: selectionStart.clientId,\n\t\t\tcursorPosition,\n\t\t};\n\t} else if ( isSelectionInOneBlock ) {\n\t\t// Case 4: Selection in a single block\n\t\tconst cursorStartPosition = getCursorPosition(\n\t\t\tselectionStart,\n\t\t\tyBlocks\n\t\t);\n\t\tconst cursorEndPosition = getCursorPosition( selectionEnd, yBlocks );\n\n\t\tif ( ! cursorStartPosition || ! cursorEndPosition ) {\n\t\t\t// If we can't find the cursor positions in block text, treat it as a non-selection.\n\t\t\treturn noSelection;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: SelectionType.SelectionInOneBlock,\n\t\t\tblockId: selectionStart.clientId,\n\t\t\tcursorStartPosition,\n\t\t\tcursorEndPosition,\n\t\t};\n\t}\n\n\t// Caes 5: Selection in multiple blocks\n\tconst cursorStartPosition = getCursorPosition( selectionStart, yBlocks );\n\tconst cursorEndPosition = getCursorPosition( selectionEnd, yBlocks );\n\tif ( ! cursorStartPosition || ! cursorEndPosition ) {\n\t\t// If we can't find the cursor positions in block text, treat it as a non-selection.\n\t\treturn noSelection;\n\t}\n\n\treturn {\n\t\ttype: SelectionType.SelectionInMultipleBlocks,\n\t\tblockStartId: selectionStart.clientId,\n\t\tblockEndId: selectionEnd.clientId,\n\t\tcursorStartPosition,\n\t\tcursorEndPosition,\n\t};\n}\n\n/**\n * Get the cursor position from a selection.\n *\n * @param selection - The selection.\n * @param blocks - The blocks to search through.\n * @return The cursor position, or null if not found.\n */\nfunction getCursorPosition(\n\tselection: WPBlockSelection,\n\tblocks: YBlocks\n): CursorPosition | null {\n\tconst block = findBlockByClientId( selection.clientId, blocks );\n\tif (\n\t\t! block ||\n\t\t! selection.attributeKey ||\n\t\tundefined === selection.offset\n\t) {\n\t\treturn null;\n\t}\n\n\tconst attributes = block.get( 'attributes' );\n\tconst currentYText = attributes?.get( selection.attributeKey ) as Y.Text;\n\n\tconst relativePosition = Y.createRelativePositionFromTypeIndex(\n\t\tcurrentYText,\n\t\tselection.offset\n\t);\n\n\treturn {\n\t\trelativePosition,\n\t\tabsoluteOffset: selection.offset,\n\t};\n}\n\n/**\n * Find a block by its client ID.\n *\n * @param blockId - The client ID of the block.\n * @param blocks - The blocks to search through.\n * @return The block if found, null otherwise.\n */\nfunction findBlockByClientId(\n\tblockId: string,\n\tblocks: YBlocks\n): YBlock | null {\n\tfor ( const block of blocks ) {\n\t\tif ( block.get( 'clientId' ) === blockId ) {\n\t\t\treturn block;\n\t\t}\n\n\t\tconst innerBlocks = block.get( 'innerBlocks' );\n\n\t\tif ( innerBlocks && innerBlocks.length > 0 ) {\n\t\t\tconst innerBlock = findBlockByClientId( blockId, innerBlocks );\n\n\t\t\tif ( innerBlock ) {\n\t\t\t\treturn innerBlock;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Check if two selection states are equal.\n *\n * @param selection1 - The first selection state.\n * @param selection2 - The second selection state.\n * @return True if the selection states are equal, false otherwise.\n */\nexport function areSelectionsStatesEqual(\n\tselection1: SelectionState,\n\tselection2: SelectionState\n): boolean {\n\tif ( selection1.type !== selection2.type ) {\n\t\treturn false;\n\t}\n\n\tswitch ( selection1.type ) {\n\t\tcase SelectionType.None:\n\t\t\treturn true;\n\n\t\tcase SelectionType.Cursor:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t\t( selection2 as SelectionCursor ).blockId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorPosition,\n\t\t\t\t\t( selection2 as SelectionCursor ).cursorPosition\n\t\t\t\t)\n\t\t\t);\n\n\t\tcase SelectionType.SelectionInOneBlock:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).blockId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorStartPosition,\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).cursorStartPosition\n\t\t\t\t) &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorEndPosition,\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).cursorEndPosition\n\t\t\t\t)\n\t\t\t);\n\n\t\tcase SelectionType.SelectionInMultipleBlocks:\n\t\t\treturn (\n\t\t\t\tselection1.blockStartId ===\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks ).blockStartId &&\n\t\t\t\tselection1.blockEndId ===\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks ).blockEndId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorStartPosition,\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks )\n\t\t\t\t\t\t.cursorStartPosition\n\t\t\t\t) &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorEndPosition,\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks )\n\t\t\t\t\t\t.cursorEndPosition\n\t\t\t\t)\n\t\t\t);\n\t\tcase SelectionType.WholeBlock:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t( selection2 as SelectionWholeBlock ).blockId\n\t\t\t);\n\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Check if two cursor positions are equal.\n *\n * @param cursorPosition1 - The first cursor position.\n * @param cursorPosition2 - The second cursor position.\n * @return True if the cursor positions are equal, false otherwise.\n */\nfunction areCursorPositionsEqual(\n\tcursorPosition1: CursorPosition,\n\tcursorPosition2: CursorPosition\n): boolean {\n\tconst isRelativePositionEqual =\n\t\tJSON.stringify( cursorPosition1.relativePosition ) ===\n\t\tJSON.stringify( cursorPosition2.relativePosition );\n\n\t// Ensure a change in calculated absolute offset results in a treating the cursor as modified.\n\t// This is necessary because Y.Text relative positions can remain the same after text changes.\n\tconst isAbsoluteOffsetEqual =\n\t\tcursorPosition1.absoluteOffset === cursorPosition2.absoluteOffset;\n\n\treturn isRelativePositionEqual && isAbsoluteOffsetEqual;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["SelectionType", "cursorStartPosition", "cursorEndPosition"]
7
+ }
@@ -1,5 +1,6 @@
1
1
  // packages/core-data/src/utils/crdt-utils.ts
2
2
  import { Y } from "@wordpress/sync";
3
+ import { CRDT_RECORD_MAP_KEY } from "../sync.mjs";
3
4
  function getRootMap(doc, key) {
4
5
  return doc.getMap(key);
5
6
  }
@@ -9,8 +10,35 @@ function createYMap(partial = {}) {
9
10
  function isYMap(value) {
10
11
  return value instanceof Y.Map;
11
12
  }
13
+ function findBlockByClientIdInDoc(blockId, ydoc) {
14
+ const ymap = getRootMap(ydoc, CRDT_RECORD_MAP_KEY);
15
+ const blocks = ymap.get("blocks");
16
+ if (!(blocks instanceof Y.Array)) {
17
+ return null;
18
+ }
19
+ return findBlockByClientIdInBlocks(blockId, blocks);
20
+ }
21
+ function findBlockByClientIdInBlocks(blockId, blocks) {
22
+ for (const block of blocks) {
23
+ if (block.get("clientId") === blockId) {
24
+ return block;
25
+ }
26
+ const innerBlocks = block.get("innerBlocks");
27
+ if (innerBlocks && innerBlocks.length > 0) {
28
+ const innerBlock = findBlockByClientIdInBlocks(
29
+ blockId,
30
+ innerBlocks
31
+ );
32
+ if (innerBlock) {
33
+ return innerBlock;
34
+ }
35
+ }
36
+ }
37
+ return null;
38
+ }
12
39
  export {
13
40
  createYMap,
41
+ findBlockByClientIdInDoc,
14
42
  getRootMap,
15
43
  isYMap
16
44
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/crdt-utils.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Y } from '@wordpress/sync';\n\n/**\n * A YMapRecord represents the shape of the data stored in a Y.Map.\n */\nexport type YMapRecord = Record< string, unknown >;\n\n/**\n * A wrapper around Y.Map to provide type safety. The generic type accepted by\n * Y.Map represents the union of possible values of the map, which are varied in\n * many cases. This type is accurate, but its non-specificity requires aggressive\n * type narrowing or type casting / destruction with `as`.\n *\n * This type provides type enhancements so that the correct value type can be\n * inferred based on the provided key. It is just a type wrap / overlay, and\n * does not change the runtime behavior of Y.Map.\n *\n * This interface cannot extend Y.Map directly due to the limitations of\n * TypeScript's structural typing. One negative consequence of this is that\n * `instanceof` checks against Y.Map continue to work at runtime but will blur\n * the type at compile time. To navigate this, use the `isYMap` function below.\n */\nexport interface YMapWrap< T extends YMapRecord > extends Y.AbstractType< T > {\n\tdelete: < K extends keyof T >( key: K ) => void;\n\tforEach: (\n\t\tcallback: (\n\t\t\tvalue: T[ keyof T ],\n\t\t\tkey: keyof T,\n\t\t\tmap: YMapWrap< T >\n\t\t) => void\n\t) => void;\n\thas: < K extends keyof T >( key: K ) => boolean;\n\tget: < K extends keyof T >( key: K ) => T[ K ] | undefined;\n\tset: < K extends keyof T >( key: K, value: T[ K ] ) => void;\n\ttoJSON: () => T;\n\t// add types for other Y.Map methods as needed\n}\n\n/**\n * Get or create a root-level Map for the given Y.Doc. Use this instead of\n * doc.getMap() for additional type safety.\n *\n * @param doc Y.Doc\n * @param key Map key\n */\nexport function getRootMap< T extends YMapRecord >(\n\tdoc: Y.Doc,\n\tkey: string\n): YMapWrap< T > {\n\treturn doc.getMap< T >( key ) as unknown as YMapWrap< T >;\n}\n\n/**\n * Create a new Y.Map (provided with YMapWrap type), optionally initialized with\n * data. Use this instead of `new Y.Map()` for additional type safety.\n *\n * @param partial Partial data to initialize the map with.\n */\nexport function createYMap< T extends YMapRecord >(\n\tpartial: Partial< T > = {}\n): YMapWrap< T > {\n\treturn new Y.Map( Object.entries( partial ) ) as unknown as YMapWrap< T >;\n}\n\n/**\n * Type guard to check if a value is a Y.Map without losing type information.\n *\n * @param value Value to check.\n */\nexport function isYMap< T extends YMapRecord >(\n\tvalue: YMapWrap< T > | undefined\n): value is YMapWrap< T > {\n\treturn value instanceof Y.Map;\n}\n"],
5
- "mappings": ";AAGA,SAAS,SAAS;AA6CX,SAAS,WACf,KACA,KACgB;AAChB,SAAO,IAAI,OAAa,GAAI;AAC7B;AAQO,SAAS,WACf,UAAwB,CAAC,GACT;AAChB,SAAO,IAAI,EAAE,IAAK,OAAO,QAAS,OAAQ,CAAE;AAC7C;AAOO,SAAS,OACf,OACyB;AACzB,SAAO,iBAAiB,EAAE;AAC3B;",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { Y } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport type { YBlock, YBlocks } from './crdt-blocks';\nimport type { YPostRecord } from './crdt';\nimport { CRDT_RECORD_MAP_KEY } from '../sync';\n\n/**\n * A YMapRecord represents the shape of the data stored in a Y.Map.\n */\nexport type YMapRecord = Record< string, unknown >;\n\n/**\n * A wrapper around Y.Map to provide type safety. The generic type accepted by\n * Y.Map represents the union of possible values of the map, which are varied in\n * many cases. This type is accurate, but its non-specificity requires aggressive\n * type narrowing or type casting / destruction with `as`.\n *\n * This type provides type enhancements so that the correct value type can be\n * inferred based on the provided key. It is just a type wrap / overlay, and\n * does not change the runtime behavior of Y.Map.\n *\n * This interface cannot extend Y.Map directly due to the limitations of\n * TypeScript's structural typing. One negative consequence of this is that\n * `instanceof` checks against Y.Map continue to work at runtime but will blur\n * the type at compile time. To navigate this, use the `isYMap` function below.\n */\nexport interface YMapWrap< T extends YMapRecord > extends Y.AbstractType< T > {\n\tdelete: < K extends keyof T >( key: K ) => void;\n\tforEach: (\n\t\tcallback: (\n\t\t\tvalue: T[ keyof T ],\n\t\t\tkey: keyof T,\n\t\t\tmap: YMapWrap< T >\n\t\t) => void\n\t) => void;\n\thas: < K extends keyof T >( key: K ) => boolean;\n\tget: < K extends keyof T >( key: K ) => T[ K ] | undefined;\n\tset: < K extends keyof T >( key: K, value: T[ K ] ) => void;\n\ttoJSON: () => T;\n\t// add types for other Y.Map methods as needed\n}\n\n/**\n * Get or create a root-level Map for the given Y.Doc. Use this instead of\n * doc.getMap() for additional type safety.\n *\n * @param doc Y.Doc\n * @param key Map key\n */\nexport function getRootMap< T extends YMapRecord >(\n\tdoc: Y.Doc,\n\tkey: string\n): YMapWrap< T > {\n\treturn doc.getMap< T >( key ) as unknown as YMapWrap< T >;\n}\n\n/**\n * Create a new Y.Map (provided with YMapWrap type), optionally initialized with\n * data. Use this instead of `new Y.Map()` for additional type safety.\n *\n * @param partial Partial data to initialize the map with.\n */\nexport function createYMap< T extends YMapRecord >(\n\tpartial: Partial< T > = {}\n): YMapWrap< T > {\n\treturn new Y.Map( Object.entries( partial ) ) as unknown as YMapWrap< T >;\n}\n\n/**\n * Type guard to check if a value is a Y.Map without losing type information.\n *\n * @param value Value to check.\n */\nexport function isYMap< T extends YMapRecord >(\n\tvalue: YMapWrap< T > | undefined\n): value is YMapWrap< T > {\n\treturn value instanceof Y.Map;\n}\n\n/**\n * Given a block ID and a Y.Doc, find the block in the document.\n *\n * @param blockId The block ID to find\n * @param ydoc The Y.Doc to find the block in\n * @return The block, or null if the block is not found\n */\nexport function findBlockByClientIdInDoc(\n\tblockId: string,\n\tydoc: Y.Doc\n): YBlock | null {\n\tconst ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );\n\tconst blocks = ymap.get( 'blocks' );\n\n\tif ( ! ( blocks instanceof Y.Array ) ) {\n\t\treturn null;\n\t}\n\n\treturn findBlockByClientIdInBlocks( blockId, blocks );\n}\n\nfunction findBlockByClientIdInBlocks(\n\tblockId: string,\n\tblocks: YBlocks\n): YBlock | null {\n\tfor ( const block of blocks ) {\n\t\tif ( block.get( 'clientId' ) === blockId ) {\n\t\t\treturn block;\n\t\t}\n\n\t\tconst innerBlocks = block.get( 'innerBlocks' );\n\n\t\tif ( innerBlocks && innerBlocks.length > 0 ) {\n\t\t\tconst innerBlock = findBlockByClientIdInBlocks(\n\t\t\t\tblockId,\n\t\t\t\tinnerBlocks\n\t\t\t);\n\n\t\t\tif ( innerBlock ) {\n\t\t\t\treturn innerBlock;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n"],
5
+ "mappings": ";AAGA,SAAS,SAAS;AAOlB,SAAS,2BAA2B;AA6C7B,SAAS,WACf,KACA,KACgB;AAChB,SAAO,IAAI,OAAa,GAAI;AAC7B;AAQO,SAAS,WACf,UAAwB,CAAC,GACT;AAChB,SAAO,IAAI,EAAE,IAAK,OAAO,QAAS,OAAQ,CAAE;AAC7C;AAOO,SAAS,OACf,OACyB;AACzB,SAAO,iBAAiB,EAAE;AAC3B;AASO,SAAS,yBACf,SACA,MACgB;AAChB,QAAM,OAAO,WAA2B,MAAM,mBAAoB;AAClE,QAAM,SAAS,KAAK,IAAK,QAAS;AAElC,MAAK,EAAI,kBAAkB,EAAE,QAAU;AACtC,WAAO;AAAA,EACR;AAEA,SAAO,4BAA6B,SAAS,MAAO;AACrD;AAEA,SAAS,4BACR,SACA,QACgB;AAChB,aAAY,SAAS,QAAS;AAC7B,QAAK,MAAM,IAAK,UAAW,MAAM,SAAU;AAC1C,aAAO;AAAA,IACR;AAEA,UAAM,cAAc,MAAM,IAAK,aAAc;AAE7C,QAAK,eAAe,YAAY,SAAS,GAAI;AAC5C,YAAM,aAAa;AAAA,QAClB;AAAA,QACA;AAAA,MACD;AAEA,UAAK,YAAa;AACjB,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,10 @@
1
1
  // packages/core-data/src/utils/crdt.ts
2
2
  import fastDeepEqual from "fast-deep-equal/es6/index.js";
3
3
  import { __unstableSerializeAndClean } from "@wordpress/blocks";
4
- import { Y } from "@wordpress/sync";
4
+ import {
5
+ Y
6
+ } from "@wordpress/sync";
7
+ import { BaseAwareness } from "../awareness/base-awareness.mjs";
5
8
  import {
6
9
  mergeCrdtBlocks
7
10
  } from "./crdt-blocks.mjs";
@@ -10,6 +13,7 @@ import {
10
13
  CRDT_RECORD_MAP_KEY,
11
14
  WORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE
12
15
  } from "../sync.mjs";
16
+ import { updateSelectionHistory } from "./crdt-selection.mjs";
13
17
  import {
14
18
  createYMap,
15
19
  getRootMap,
@@ -18,6 +22,7 @@ import {
18
22
  var allowedPostProperties = /* @__PURE__ */ new Set([
19
23
  "author",
20
24
  "blocks",
25
+ "categories",
21
26
  "comment_status",
22
27
  "date",
23
28
  "excerpt",
@@ -126,6 +131,12 @@ function applyPostChangesToCRDTDoc(ydoc, changes, _postType) {
126
131
  }
127
132
  }
128
133
  });
134
+ if (changes.selection) {
135
+ const selection = changes.selection;
136
+ setTimeout(() => {
137
+ updateSelectionHistory(ydoc, selection);
138
+ }, 0);
139
+ }
129
140
  }
130
141
  function defaultGetChangesFromCRDTDoc(crdtDoc) {
131
142
  return getRootMap(crdtDoc, CRDT_RECORD_MAP_KEY).toJSON();
@@ -198,6 +209,11 @@ function getPostChangesFromCRDTDoc(ydoc, editedRecord, _postType) {
198
209
  }
199
210
  return changes;
200
211
  }
212
+ var defaultSyncConfig = {
213
+ applyChangesToCRDTDoc: defaultApplyChangesToCRDTDoc,
214
+ createAwareness: (ydoc) => new BaseAwareness(ydoc),
215
+ getChangesFromCRDTDoc: defaultGetChangesFromCRDTDoc
216
+ };
201
217
  function getRawValue(value) {
202
218
  if ("string" === typeof value) {
203
219
  return value;
@@ -221,8 +237,7 @@ function updateMapValue(map, key, currentValue, newValue) {
221
237
  }
222
238
  export {
223
239
  applyPostChangesToCRDTDoc,
224
- defaultApplyChangesToCRDTDoc,
225
- defaultGetChangesFromCRDTDoc,
240
+ defaultSyncConfig,
226
241
  getPostChangesFromCRDTDoc
227
242
  };
228
243
  //# sourceMappingURL=crdt.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/crdt.ts"],
4
- "sourcesContent": ["/**\n * External dependencies\n */\nimport fastDeepEqual from 'fast-deep-equal/es6/index.js';\n\n/**\n * WordPress dependencies\n */\n// @ts-expect-error No exported types.\nimport { __unstableSerializeAndClean } from '@wordpress/blocks';\nimport { type CRDTDoc, type ObjectData, Y } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport {\n\tmergeCrdtBlocks,\n\ttype Block,\n\ttype YBlock,\n\ttype YBlocks,\n} from './crdt-blocks';\nimport { type Post } from '../entity-types/post';\nimport { type Type } from '../entity-types';\nimport {\n\tCRDT_DOC_META_PERSISTENCE_KEY,\n\tCRDT_RECORD_MAP_KEY,\n\tWORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE,\n} from '../sync';\nimport type { WPSelection } from '../types';\nimport {\n\tcreateYMap,\n\tgetRootMap,\n\tisYMap,\n\ttype YMapRecord,\n\ttype YMapWrap,\n} from './crdt-utils';\n\n// Changes that can be applied to a post entity record.\nexport type PostChanges = Partial< Post > & {\n\tblocks?: Block[];\n\texcerpt?: Post[ 'excerpt' ] | string;\n\tselection?: WPSelection;\n\ttitle?: Post[ 'title' ] | string;\n};\n\n// A post record as represented in the CRDT document (Y.Map).\nexport interface YPostRecord extends YMapRecord {\n\tauthor: number;\n\tblocks: YBlocks;\n\tcomment_status: string;\n\tdate: string | null;\n\texcerpt: string;\n\tfeatured_media: number;\n\tformat: string;\n\tmeta: YMapWrap< YMapRecord >;\n\tping_status: string;\n\tslug: string;\n\tstatus: string;\n\tsticky: boolean;\n\ttags: number[];\n\ttemplate: string;\n\ttitle: string;\n}\n\n// Properties that are allowed to be synced for a post.\nconst allowedPostProperties = new Set< string >( [\n\t'author',\n\t'blocks',\n\t'comment_status',\n\t'date',\n\t'excerpt',\n\t'featured_media',\n\t'format',\n\t'meta',\n\t'ping_status',\n\t'slug',\n\t'status',\n\t'sticky',\n\t'tags',\n\t'template',\n\t'title',\n] );\n\n// Post meta keys that should *not* be synced.\nconst disallowedPostMetaKeys = new Set< string >( [\n\tWORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE,\n] );\n\n/**\n * Given a set of local changes to a generic entity record, apply those changes\n * to the local Y.Doc.\n *\n * @param {CRDTDoc} ydoc\n * @param {Partial< ObjectData >} changes\n * @return {void}\n */\nexport function defaultApplyChangesToCRDTDoc(\n\tydoc: CRDTDoc,\n\tchanges: ObjectData\n): void {\n\tconst ymap = getRootMap( ydoc, CRDT_RECORD_MAP_KEY );\n\n\tObject.entries( changes ).forEach( ( [ key, newValue ] ) => {\n\t\t// Cannot serialize function values, so cannot sync them.\n\t\tif ( 'function' === typeof newValue ) {\n\t\t\treturn;\n\t\t}\n\n\t\tswitch ( key ) {\n\t\t\t// Add support for additional data types here.\n\n\t\t\tdefault: {\n\t\t\t\tconst currentValue = ymap.get( key );\n\t\t\t\tupdateMapValue( ymap, key, currentValue, newValue );\n\t\t\t}\n\t\t}\n\t} );\n}\n\n/**\n * Given a set of local changes to a post record, apply those changes to the\n * local Y.Doc.\n *\n * @param {CRDTDoc} ydoc\n * @param {PostChanges} changes\n * @param {Type} _postType\n * @return {void}\n */\nexport function applyPostChangesToCRDTDoc(\n\tydoc: CRDTDoc,\n\tchanges: PostChanges,\n\t_postType: Type // eslint-disable-line @typescript-eslint/no-unused-vars\n): void {\n\tconst ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );\n\n\tObject.keys( changes ).forEach( ( key ) => {\n\t\tif ( ! allowedPostProperties.has( key ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst newValue = changes[ key ];\n\n\t\t// Cannot serialize function values, so cannot sync them.\n\t\tif ( 'function' === typeof newValue ) {\n\t\t\treturn;\n\t\t}\n\n\t\tswitch ( key ) {\n\t\t\tcase 'blocks': {\n\t\t\t\tlet currentBlocks = ymap.get( key );\n\n\t\t\t\t// Initialize.\n\t\t\t\tif ( ! ( currentBlocks instanceof Y.Array ) ) {\n\t\t\t\t\tcurrentBlocks = new Y.Array< YBlock >();\n\t\t\t\t\tymap.set( key, currentBlocks );\n\t\t\t\t}\n\n\t\t\t\t// Block[] from local changes.\n\t\t\t\tconst newBlocks = ( newValue as PostChanges[ 'blocks' ] ) ?? [];\n\n\t\t\t\t// Block changes from typing are bundled with a 'selection' update.\n\t\t\t\t// Pass the resulting cursor position to the mergeCrdtBlocks function.\n\t\t\t\tconst cursorPosition =\n\t\t\t\t\tchanges.selection?.selectionStart?.offset ?? null;\n\n\t\t\t\t// Merge blocks does not need `setValue` because it is operating on a\n\t\t\t\t// Yjs type that is already in the Y.Doc.\n\t\t\t\tmergeCrdtBlocks( currentBlocks, newBlocks, cursorPosition );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'excerpt': {\n\t\t\t\tconst currentValue = ymap.get( 'excerpt' );\n\t\t\t\tconst rawNewValue = getRawValue( newValue );\n\n\t\t\t\tupdateMapValue( ymap, key, currentValue, rawNewValue );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// \"Meta\" is overloaded term; here, it refers to post meta.\n\t\t\tcase 'meta': {\n\t\t\t\tlet metaMap = ymap.get( 'meta' );\n\n\t\t\t\t// Initialize.\n\t\t\t\tif ( ! isYMap( metaMap ) ) {\n\t\t\t\t\tmetaMap = createYMap< YMapRecord >();\n\t\t\t\t\tymap.set( 'meta', metaMap );\n\t\t\t\t}\n\n\t\t\t\t// Iterate over each meta property in the new value and merge it if it\n\t\t\t\t// should be synced.\n\t\t\t\tObject.entries( newValue ?? {} ).forEach(\n\t\t\t\t\t( [ metaKey, metaValue ] ) => {\n\t\t\t\t\t\tif ( disallowedPostMetaKeys.has( metaKey ) ) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tupdateMapValue(\n\t\t\t\t\t\t\tmetaMap,\n\t\t\t\t\t\t\tmetaKey,\n\t\t\t\t\t\t\tmetaMap.get( metaKey ), // current value in CRDT\n\t\t\t\t\t\t\tmetaValue // new value from changes\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'slug': {\n\t\t\t\t// Do not sync an empty slug. This indicates that the post is using\n\t\t\t\t// the default auto-generated slug.\n\t\t\t\tif ( ! newValue ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst currentValue = ymap.get( key );\n\t\t\t\tupdateMapValue( ymap, key, currentValue, newValue );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'title': {\n\t\t\t\tconst currentValue = ymap.get( key );\n\n\t\t\t\t// Copy logic from prePersistPostType to ensure that the \"Auto\n\t\t\t\t// Draft\" template title is not synced.\n\t\t\t\tlet rawNewValue = getRawValue( newValue );\n\t\t\t\tif ( ! currentValue && 'Auto Draft' === rawNewValue ) {\n\t\t\t\t\trawNewValue = '';\n\t\t\t\t}\n\n\t\t\t\tupdateMapValue( ymap, key, currentValue, rawNewValue );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Add support for additional properties here.\n\n\t\t\tdefault: {\n\t\t\t\tconst currentValue = ymap.get( key );\n\t\t\t\tupdateMapValue( ymap, key, currentValue, newValue );\n\t\t\t}\n\t\t}\n\t} );\n}\n\nexport function defaultGetChangesFromCRDTDoc( crdtDoc: CRDTDoc ): ObjectData {\n\treturn getRootMap( crdtDoc, CRDT_RECORD_MAP_KEY ).toJSON();\n}\n\n/**\n * Given a local Y.Doc that *may* contain changes from remote peers, compare\n * against the local record and determine if there are changes (edits) we want\n * to dispatch.\n *\n * @param {CRDTDoc} ydoc\n * @param {Post} editedRecord\n * @param {Type} _postType\n * @return {Partial<PostChanges>} The changes that should be applied to the local record.\n */\nexport function getPostChangesFromCRDTDoc(\n\tydoc: CRDTDoc,\n\teditedRecord: Post,\n\t_postType: Type // eslint-disable-line @typescript-eslint/no-unused-vars\n): PostChanges {\n\tconst ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );\n\n\tlet allowedMetaChanges: Post[ 'meta' ] = {};\n\n\tconst changes = Object.fromEntries(\n\t\tObject.entries( ymap.toJSON() ).filter( ( [ key, newValue ] ) => {\n\t\t\tif ( ! allowedPostProperties.has( key ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst currentValue = editedRecord[ key ];\n\n\t\t\tswitch ( key ) {\n\t\t\t\tcase 'blocks': {\n\t\t\t\t\t// When we are passed a persisted CRDT document, make a special\n\t\t\t\t\t// comparison of the content and blocks.\n\t\t\t\t\t//\n\t\t\t\t\t// When other fields (besides `blocks`) are mutated outside the block\n\t\t\t\t\t// editor, the change is caught by an equality check (see other cases\n\t\t\t\t\t// in this `switch` statement). As a transient property, `blocks`\n\t\t\t\t\t// cannot be directly mutated outside the block editor -- only\n\t\t\t\t\t// `content` can.\n\t\t\t\t\t//\n\t\t\t\t\t// Therefore, for this special comparison, we serialize the `blocks`\n\t\t\t\t\t// from the persisted CRDT document and compare that to the content\n\t\t\t\t\t// from the persisted record. If they differ, we know that the content\n\t\t\t\t\t// in the database has changed, and therefore the blocks have changed.\n\t\t\t\t\t//\n\t\t\t\t\t// We cannot directly compare the `blocks` from the CRDT document to\n\t\t\t\t\t// the `blocks` derived from the `content` in the persisted record,\n\t\t\t\t\t// because the latter will have different client IDs.\n\t\t\t\t\tif (\n\t\t\t\t\t\tydoc.meta?.get( CRDT_DOC_META_PERSISTENCE_KEY ) &&\n\t\t\t\t\t\teditedRecord.content\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst blocks = ymap.get( 'blocks' ) as YBlocks;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t__unstableSerializeAndClean(\n\t\t\t\t\t\t\t\tblocks.toJSON()\n\t\t\t\t\t\t\t).trim() !== editedRecord.content.raw.trim()\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// The consumers of blocks have memoization that renders optimization\n\t\t\t\t\t// here unnecessary.\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tcase 'date': {\n\t\t\t\t\t// Do not overwrite a \"floating\" date. Borrowing logic from the\n\t\t\t\t\t// isEditedPostDateFloating selector.\n\t\t\t\t\tconst currentDateIsFloating =\n\t\t\t\t\t\t[ 'draft', 'auto-draft', 'pending' ].includes(\n\t\t\t\t\t\t\tymap.get( 'status' ) as string\n\t\t\t\t\t\t) &&\n\t\t\t\t\t\t( null === currentValue ||\n\t\t\t\t\t\t\teditedRecord.modified === currentValue );\n\n\t\t\t\t\tif ( currentDateIsFloating ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn haveValuesChanged( currentValue, newValue );\n\t\t\t\t}\n\n\t\t\t\tcase 'meta': {\n\t\t\t\t\tallowedMetaChanges = Object.fromEntries(\n\t\t\t\t\t\tObject.entries( newValue ?? {} ).filter(\n\t\t\t\t\t\t\t( [ metaKey ] ) =>\n\t\t\t\t\t\t\t\t! disallowedPostMetaKeys.has( metaKey )\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\n\t\t\t\t\t// Merge the allowed meta changes with the current meta values since\n\t\t\t\t\t// not all meta properties are synced.\n\t\t\t\t\tconst mergedValue = {\n\t\t\t\t\t\t...( currentValue as PostChanges[ 'meta' ] ),\n\t\t\t\t\t\t...allowedMetaChanges,\n\t\t\t\t\t};\n\n\t\t\t\t\treturn haveValuesChanged( currentValue, mergedValue );\n\t\t\t\t}\n\n\t\t\t\tcase 'status': {\n\t\t\t\t\t// Do not sync an invalid status.\n\t\t\t\t\tif ( 'auto-draft' === newValue ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn haveValuesChanged( currentValue, newValue );\n\t\t\t\t}\n\n\t\t\t\tcase 'excerpt':\n\t\t\t\tcase 'title': {\n\t\t\t\t\treturn haveValuesChanged(\n\t\t\t\t\t\tgetRawValue( currentValue ),\n\t\t\t\t\t\tnewValue\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Add support for additional data types here.\n\n\t\t\t\tdefault: {\n\t\t\t\t\treturn haveValuesChanged( currentValue, newValue );\n\t\t\t\t}\n\t\t\t}\n\t\t} )\n\t);\n\n\t// Meta changes must be merged with the edited record since not all meta\n\t// properties are synced.\n\tif ( 'object' === typeof changes.meta ) {\n\t\tchanges.meta = {\n\t\t\t...editedRecord.meta,\n\t\t\t...allowedMetaChanges,\n\t\t};\n\t}\n\n\treturn changes;\n}\n\n/**\n * Extract the raw string value from a property that may be a string or an object\n * with a `raw` property (`RenderedText`).\n *\n * @param {unknown} value The value to extract from.\n * @return {string|undefined} The raw string value, or undefined if it could not be determined.\n */\nfunction getRawValue( value?: unknown ): string | undefined {\n\t// Value may be a string property or a nested object with a `raw` property.\n\tif ( 'string' === typeof value ) {\n\t\treturn value;\n\t}\n\n\tif (\n\t\tvalue &&\n\t\t'object' === typeof value &&\n\t\t'raw' in value &&\n\t\t'string' === typeof value.raw\n\t) {\n\t\treturn value.raw;\n\t}\n\n\treturn undefined;\n}\n\nfunction haveValuesChanged< ValueType >(\n\tcurrentValue: ValueType | undefined,\n\tnewValue: ValueType | undefined\n): boolean {\n\treturn ! fastDeepEqual( currentValue, newValue );\n}\n\nfunction updateMapValue< T extends YMapRecord, K extends keyof T >(\n\tmap: YMapWrap< T >,\n\tkey: K,\n\tcurrentValue: T[ K ] | undefined,\n\tnewValue: T[ K ] | undefined\n): void {\n\tif ( undefined === newValue ) {\n\t\tmap.delete( key );\n\t\treturn;\n\t}\n\n\tif ( haveValuesChanged< T[ K ] >( currentValue, newValue ) ) {\n\t\tmap.set( key, newValue );\n\t}\n}\n"],
5
- "mappings": ";AAGA,OAAO,mBAAmB;AAM1B,SAAS,mCAAmC;AAC5C,SAAwC,SAAS;AAKjD;AAAA,EACC;AAAA,OAIM;AAGP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAEP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OAGM;AA8BP,IAAM,wBAAwB,oBAAI,IAAe;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAE;AAGF,IAAM,yBAAyB,oBAAI,IAAe;AAAA,EACjD;AACD,CAAE;AAUK,SAAS,6BACf,MACA,SACO;AACP,QAAM,OAAO,WAAY,MAAM,mBAAoB;AAEnD,SAAO,QAAS,OAAQ,EAAE,QAAS,CAAE,CAAE,KAAK,QAAS,MAAO;AAE3D,QAAK,eAAe,OAAO,UAAW;AACrC;AAAA,IACD;AAEA,YAAS,KAAM;AAAA;AAAA,MAGd,SAAS;AACR,cAAM,eAAe,KAAK,IAAK,GAAI;AACnC,uBAAgB,MAAM,KAAK,cAAc,QAAS;AAAA,MACnD;AAAA,IACD;AAAA,EACD,CAAE;AACH;AAWO,SAAS,0BACf,MACA,SACA,WACO;AACP,QAAM,OAAO,WAA2B,MAAM,mBAAoB;AAElE,SAAO,KAAM,OAAQ,EAAE,QAAS,CAAE,QAAS;AAC1C,QAAK,CAAE,sBAAsB,IAAK,GAAI,GAAI;AACzC;AAAA,IACD;AAEA,UAAM,WAAW,QAAS,GAAI;AAG9B,QAAK,eAAe,OAAO,UAAW;AACrC;AAAA,IACD;AAEA,YAAS,KAAM;AAAA,MACd,KAAK,UAAU;AACd,YAAI,gBAAgB,KAAK,IAAK,GAAI;AAGlC,YAAK,EAAI,yBAAyB,EAAE,QAAU;AAC7C,0BAAgB,IAAI,EAAE,MAAgB;AACtC,eAAK,IAAK,KAAK,aAAc;AAAA,QAC9B;AAGA,cAAM,YAAc,YAAyC,CAAC;AAI9D,cAAM,iBACL,QAAQ,WAAW,gBAAgB,UAAU;AAI9C,wBAAiB,eAAe,WAAW,cAAe;AAC1D;AAAA,MACD;AAAA,MAEA,KAAK,WAAW;AACf,cAAM,eAAe,KAAK,IAAK,SAAU;AACzC,cAAM,cAAc,YAAa,QAAS;AAE1C,uBAAgB,MAAM,KAAK,cAAc,WAAY;AACrD;AAAA,MACD;AAAA;AAAA,MAGA,KAAK,QAAQ;AACZ,YAAI,UAAU,KAAK,IAAK,MAAO;AAG/B,YAAK,CAAE,OAAQ,OAAQ,GAAI;AAC1B,oBAAU,WAAyB;AACnC,eAAK,IAAK,QAAQ,OAAQ;AAAA,QAC3B;AAIA,eAAO,QAAS,YAAY,CAAC,CAAE,EAAE;AAAA,UAChC,CAAE,CAAE,SAAS,SAAU,MAAO;AAC7B,gBAAK,uBAAuB,IAAK,OAAQ,GAAI;AAC5C;AAAA,YACD;AAEA;AAAA,cACC;AAAA,cACA;AAAA,cACA,QAAQ,IAAK,OAAQ;AAAA;AAAA,cACrB;AAAA;AAAA,YACD;AAAA,UACD;AAAA,QACD;AACA;AAAA,MACD;AAAA,MAEA,KAAK,QAAQ;AAGZ,YAAK,CAAE,UAAW;AACjB;AAAA,QACD;AAEA,cAAM,eAAe,KAAK,IAAK,GAAI;AACnC,uBAAgB,MAAM,KAAK,cAAc,QAAS;AAClD;AAAA,MACD;AAAA,MAEA,KAAK,SAAS;AACb,cAAM,eAAe,KAAK,IAAK,GAAI;AAInC,YAAI,cAAc,YAAa,QAAS;AACxC,YAAK,CAAE,gBAAgB,iBAAiB,aAAc;AACrD,wBAAc;AAAA,QACf;AAEA,uBAAgB,MAAM,KAAK,cAAc,WAAY;AACrD;AAAA,MACD;AAAA;AAAA,MAIA,SAAS;AACR,cAAM,eAAe,KAAK,IAAK,GAAI;AACnC,uBAAgB,MAAM,KAAK,cAAc,QAAS;AAAA,MACnD;AAAA,IACD;AAAA,EACD,CAAE;AACH;AAEO,SAAS,6BAA8B,SAA+B;AAC5E,SAAO,WAAY,SAAS,mBAAoB,EAAE,OAAO;AAC1D;AAYO,SAAS,0BACf,MACA,cACA,WACc;AACd,QAAM,OAAO,WAA2B,MAAM,mBAAoB;AAElE,MAAI,qBAAqC,CAAC;AAE1C,QAAM,UAAU,OAAO;AAAA,IACtB,OAAO,QAAS,KAAK,OAAO,CAAE,EAAE,OAAQ,CAAE,CAAE,KAAK,QAAS,MAAO;AAChE,UAAK,CAAE,sBAAsB,IAAK,GAAI,GAAI;AACzC,eAAO;AAAA,MACR;AAEA,YAAM,eAAe,aAAc,GAAI;AAEvC,cAAS,KAAM;AAAA,QACd,KAAK,UAAU;AAkBd,cACC,KAAK,MAAM,IAAK,6BAA8B,KAC9C,aAAa,SACZ;AACD,kBAAM,SAAS,KAAK,IAAK,QAAS;AAClC,mBACC;AAAA,cACC,OAAO,OAAO;AAAA,YACf,EAAE,KAAK,MAAM,aAAa,QAAQ,IAAI,KAAK;AAAA,UAE7C;AAIA,iBAAO;AAAA,QACR;AAAA,QAEA,KAAK,QAAQ;AAGZ,gBAAM,wBACL,CAAE,SAAS,cAAc,SAAU,EAAE;AAAA,YACpC,KAAK,IAAK,QAAS;AAAA,UACpB,MACE,SAAS,gBACV,aAAa,aAAa;AAE5B,cAAK,uBAAwB;AAC5B,mBAAO;AAAA,UACR;AAEA,iBAAO,kBAAmB,cAAc,QAAS;AAAA,QAClD;AAAA,QAEA,KAAK,QAAQ;AACZ,+BAAqB,OAAO;AAAA,YAC3B,OAAO,QAAS,YAAY,CAAC,CAAE,EAAE;AAAA,cAChC,CAAE,CAAE,OAAQ,MACX,CAAE,uBAAuB,IAAK,OAAQ;AAAA,YACxC;AAAA,UACD;AAIA,gBAAM,cAAc;AAAA,YACnB,GAAK;AAAA,YACL,GAAG;AAAA,UACJ;AAEA,iBAAO,kBAAmB,cAAc,WAAY;AAAA,QACrD;AAAA,QAEA,KAAK,UAAU;AAEd,cAAK,iBAAiB,UAAW;AAChC,mBAAO;AAAA,UACR;AAEA,iBAAO,kBAAmB,cAAc,QAAS;AAAA,QAClD;AAAA,QAEA,KAAK;AAAA,QACL,KAAK,SAAS;AACb,iBAAO;AAAA,YACN,YAAa,YAAa;AAAA,YAC1B;AAAA,UACD;AAAA,QACD;AAAA;AAAA,QAIA,SAAS;AACR,iBAAO,kBAAmB,cAAc,QAAS;AAAA,QAClD;AAAA,MACD;AAAA,IACD,CAAE;AAAA,EACH;AAIA,MAAK,aAAa,OAAO,QAAQ,MAAO;AACvC,YAAQ,OAAO;AAAA,MACd,GAAG,aAAa;AAAA,MAChB,GAAG;AAAA,IACJ;AAAA,EACD;AAEA,SAAO;AACR;AASA,SAAS,YAAa,OAAsC;AAE3D,MAAK,aAAa,OAAO,OAAQ;AAChC,WAAO;AAAA,EACR;AAEA,MACC,SACA,aAAa,OAAO,SACpB,SAAS,SACT,aAAa,OAAO,MAAM,KACzB;AACD,WAAO,MAAM;AAAA,EACd;AAEA,SAAO;AACR;AAEA,SAAS,kBACR,cACA,UACU;AACV,SAAO,CAAE,cAAe,cAAc,QAAS;AAChD;AAEA,SAAS,eACR,KACA,KACA,cACA,UACO;AACP,MAAK,WAAc,UAAW;AAC7B,QAAI,OAAQ,GAAI;AAChB;AAAA,EACD;AAEA,MAAK,kBAA6B,cAAc,QAAS,GAAI;AAC5D,QAAI,IAAK,KAAK,QAAS;AAAA,EACxB;AACD;",
4
+ "sourcesContent": ["/**\n * External dependencies\n */\nimport fastDeepEqual from 'fast-deep-equal/es6/index.js';\n\n/**\n * WordPress dependencies\n */\n// @ts-expect-error No exported types.\nimport { __unstableSerializeAndClean } from '@wordpress/blocks';\nimport {\n\ttype CRDTDoc,\n\ttype ObjectData,\n\ttype SyncConfig,\n\tY,\n} from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport { BaseAwareness } from '../awareness/base-awareness';\nimport { type BaseState } from '../awareness/types';\nimport {\n\tmergeCrdtBlocks,\n\ttype Block,\n\ttype YBlock,\n\ttype YBlocks,\n} from './crdt-blocks';\nimport { type Post } from '../entity-types/post';\nimport { type Type } from '../entity-types';\nimport {\n\tCRDT_DOC_META_PERSISTENCE_KEY,\n\tCRDT_RECORD_MAP_KEY,\n\tWORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE,\n} from '../sync';\nimport type { WPSelection } from '../types';\nimport { updateSelectionHistory } from './crdt-selection';\nimport {\n\tcreateYMap,\n\tgetRootMap,\n\tisYMap,\n\ttype YMapRecord,\n\ttype YMapWrap,\n} from './crdt-utils';\n\n// Changes that can be applied to a post entity record.\nexport type PostChanges = Partial< Post > & {\n\tblocks?: Block[];\n\texcerpt?: Post[ 'excerpt' ] | string;\n\tselection?: WPSelection;\n\ttitle?: Post[ 'title' ] | string;\n};\n\n// A post record as represented in the CRDT document (Y.Map).\nexport interface YPostRecord extends YMapRecord {\n\tauthor: number;\n\tblocks: YBlocks;\n\tcategories: number[];\n\tcomment_status: string;\n\tdate: string | null;\n\texcerpt: string;\n\tfeatured_media: number;\n\tformat: string;\n\tmeta: YMapWrap< YMapRecord >;\n\tping_status: string;\n\tslug: string;\n\tstatus: string;\n\tsticky: boolean;\n\ttags: number[];\n\ttemplate: string;\n\ttitle: string;\n}\n\n// Properties that are allowed to be synced for a post.\nconst allowedPostProperties = new Set< string >( [\n\t'author',\n\t'blocks',\n\t'categories',\n\t'comment_status',\n\t'date',\n\t'excerpt',\n\t'featured_media',\n\t'format',\n\t'meta',\n\t'ping_status',\n\t'slug',\n\t'status',\n\t'sticky',\n\t'tags',\n\t'template',\n\t'title',\n] );\n\n// Post meta keys that should *not* be synced.\nconst disallowedPostMetaKeys = new Set< string >( [\n\tWORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE,\n] );\n\n/**\n * Given a set of local changes to a generic entity record, apply those changes\n * to the local Y.Doc.\n *\n * @param {CRDTDoc} ydoc\n * @param {Partial< ObjectData >} changes\n * @return {void}\n */\nfunction defaultApplyChangesToCRDTDoc(\n\tydoc: CRDTDoc,\n\tchanges: ObjectData\n): void {\n\tconst ymap = getRootMap( ydoc, CRDT_RECORD_MAP_KEY );\n\n\tObject.entries( changes ).forEach( ( [ key, newValue ] ) => {\n\t\t// Cannot serialize function values, so cannot sync them.\n\t\tif ( 'function' === typeof newValue ) {\n\t\t\treturn;\n\t\t}\n\n\t\tswitch ( key ) {\n\t\t\t// Add support for additional data types here.\n\n\t\t\tdefault: {\n\t\t\t\tconst currentValue = ymap.get( key );\n\t\t\t\tupdateMapValue( ymap, key, currentValue, newValue );\n\t\t\t}\n\t\t}\n\t} );\n}\n\n/**\n * Given a set of local changes to a post record, apply those changes to the\n * local Y.Doc.\n *\n * @param {CRDTDoc} ydoc\n * @param {PostChanges} changes\n * @param {Type} _postType\n * @return {void}\n */\nexport function applyPostChangesToCRDTDoc(\n\tydoc: CRDTDoc,\n\tchanges: PostChanges,\n\t_postType: Type // eslint-disable-line @typescript-eslint/no-unused-vars\n): void {\n\tconst ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );\n\n\tObject.keys( changes ).forEach( ( key ) => {\n\t\tif ( ! allowedPostProperties.has( key ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst newValue = changes[ key ];\n\n\t\t// Cannot serialize function values, so cannot sync them.\n\t\tif ( 'function' === typeof newValue ) {\n\t\t\treturn;\n\t\t}\n\n\t\tswitch ( key ) {\n\t\t\tcase 'blocks': {\n\t\t\t\tlet currentBlocks = ymap.get( key );\n\n\t\t\t\t// Initialize.\n\t\t\t\tif ( ! ( currentBlocks instanceof Y.Array ) ) {\n\t\t\t\t\tcurrentBlocks = new Y.Array< YBlock >();\n\t\t\t\t\tymap.set( key, currentBlocks );\n\t\t\t\t}\n\n\t\t\t\t// Block[] from local changes.\n\t\t\t\tconst newBlocks = ( newValue as PostChanges[ 'blocks' ] ) ?? [];\n\n\t\t\t\t// Block changes from typing are bundled with a 'selection' update.\n\t\t\t\t// Pass the resulting cursor position to the mergeCrdtBlocks function.\n\t\t\t\tconst cursorPosition =\n\t\t\t\t\tchanges.selection?.selectionStart?.offset ?? null;\n\n\t\t\t\t// Merge blocks does not need `setValue` because it is operating on a\n\t\t\t\t// Yjs type that is already in the Y.Doc.\n\t\t\t\tmergeCrdtBlocks( currentBlocks, newBlocks, cursorPosition );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'excerpt': {\n\t\t\t\tconst currentValue = ymap.get( 'excerpt' );\n\t\t\t\tconst rawNewValue = getRawValue( newValue );\n\n\t\t\t\tupdateMapValue( ymap, key, currentValue, rawNewValue );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// \"Meta\" is overloaded term; here, it refers to post meta.\n\t\t\tcase 'meta': {\n\t\t\t\tlet metaMap = ymap.get( 'meta' );\n\n\t\t\t\t// Initialize.\n\t\t\t\tif ( ! isYMap( metaMap ) ) {\n\t\t\t\t\tmetaMap = createYMap< YMapRecord >();\n\t\t\t\t\tymap.set( 'meta', metaMap );\n\t\t\t\t}\n\n\t\t\t\t// Iterate over each meta property in the new value and merge it if it\n\t\t\t\t// should be synced.\n\t\t\t\tObject.entries( newValue ?? {} ).forEach(\n\t\t\t\t\t( [ metaKey, metaValue ] ) => {\n\t\t\t\t\t\tif ( disallowedPostMetaKeys.has( metaKey ) ) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tupdateMapValue(\n\t\t\t\t\t\t\tmetaMap,\n\t\t\t\t\t\t\tmetaKey,\n\t\t\t\t\t\t\tmetaMap.get( metaKey ), // current value in CRDT\n\t\t\t\t\t\t\tmetaValue // new value from changes\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'slug': {\n\t\t\t\t// Do not sync an empty slug. This indicates that the post is using\n\t\t\t\t// the default auto-generated slug.\n\t\t\t\tif ( ! newValue ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst currentValue = ymap.get( key );\n\t\t\t\tupdateMapValue( ymap, key, currentValue, newValue );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'title': {\n\t\t\t\tconst currentValue = ymap.get( key );\n\n\t\t\t\t// Copy logic from prePersistPostType to ensure that the \"Auto\n\t\t\t\t// Draft\" template title is not synced.\n\t\t\t\tlet rawNewValue = getRawValue( newValue );\n\t\t\t\tif ( ! currentValue && 'Auto Draft' === rawNewValue ) {\n\t\t\t\t\trawNewValue = '';\n\t\t\t\t}\n\n\t\t\t\tupdateMapValue( ymap, key, currentValue, rawNewValue );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Add support for additional properties here.\n\n\t\t\tdefault: {\n\t\t\t\tconst currentValue = ymap.get( key );\n\t\t\t\tupdateMapValue( ymap, key, currentValue, newValue );\n\t\t\t}\n\t\t}\n\t} );\n\n\t// Process changes that we don't want to persist to the CRDT document.\n\tif ( changes.selection ) {\n\t\tconst selection = changes.selection;\n\t\t// Persist selection changes at the end of the current event loop.\n\t\t// This allows undo meta to be saved with the current selection before\n\t\t// it is overwritten by the new selection from Gutenberg.\n\t\t// Without this, selection history will already contain the latest\n\t\t// selection (after this change) when the undo stack is saved.\n\t\tsetTimeout( () => {\n\t\t\tupdateSelectionHistory( ydoc, selection );\n\t\t}, 0 );\n\t}\n}\n\nfunction defaultGetChangesFromCRDTDoc( crdtDoc: CRDTDoc ): ObjectData {\n\treturn getRootMap( crdtDoc, CRDT_RECORD_MAP_KEY ).toJSON();\n}\n\n/**\n * Given a local Y.Doc that *may* contain changes from remote peers, compare\n * against the local record and determine if there are changes (edits) we want\n * to dispatch.\n *\n * @param {CRDTDoc} ydoc\n * @param {Post} editedRecord\n * @param {Type} _postType\n * @return {Partial<PostChanges>} The changes that should be applied to the local record.\n */\nexport function getPostChangesFromCRDTDoc(\n\tydoc: CRDTDoc,\n\teditedRecord: Post,\n\t_postType: Type // eslint-disable-line @typescript-eslint/no-unused-vars\n): PostChanges {\n\tconst ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );\n\n\tlet allowedMetaChanges: Post[ 'meta' ] = {};\n\n\tconst changes = Object.fromEntries(\n\t\tObject.entries( ymap.toJSON() ).filter( ( [ key, newValue ] ) => {\n\t\t\tif ( ! allowedPostProperties.has( key ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst currentValue = editedRecord[ key ];\n\n\t\t\tswitch ( key ) {\n\t\t\t\tcase 'blocks': {\n\t\t\t\t\t// When we are passed a persisted CRDT document, make a special\n\t\t\t\t\t// comparison of the content and blocks.\n\t\t\t\t\t//\n\t\t\t\t\t// When other fields (besides `blocks`) are mutated outside the block\n\t\t\t\t\t// editor, the change is caught by an equality check (see other cases\n\t\t\t\t\t// in this `switch` statement). As a transient property, `blocks`\n\t\t\t\t\t// cannot be directly mutated outside the block editor -- only\n\t\t\t\t\t// `content` can.\n\t\t\t\t\t//\n\t\t\t\t\t// Therefore, for this special comparison, we serialize the `blocks`\n\t\t\t\t\t// from the persisted CRDT document and compare that to the content\n\t\t\t\t\t// from the persisted record. If they differ, we know that the content\n\t\t\t\t\t// in the database has changed, and therefore the blocks have changed.\n\t\t\t\t\t//\n\t\t\t\t\t// We cannot directly compare the `blocks` from the CRDT document to\n\t\t\t\t\t// the `blocks` derived from the `content` in the persisted record,\n\t\t\t\t\t// because the latter will have different client IDs.\n\t\t\t\t\tif (\n\t\t\t\t\t\tydoc.meta?.get( CRDT_DOC_META_PERSISTENCE_KEY ) &&\n\t\t\t\t\t\teditedRecord.content\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst blocks = ymap.get( 'blocks' ) as YBlocks;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t__unstableSerializeAndClean(\n\t\t\t\t\t\t\t\tblocks.toJSON()\n\t\t\t\t\t\t\t).trim() !== editedRecord.content.raw.trim()\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// The consumers of blocks have memoization that renders optimization\n\t\t\t\t\t// here unnecessary.\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tcase 'date': {\n\t\t\t\t\t// Do not overwrite a \"floating\" date. Borrowing logic from the\n\t\t\t\t\t// isEditedPostDateFloating selector.\n\t\t\t\t\tconst currentDateIsFloating =\n\t\t\t\t\t\t[ 'draft', 'auto-draft', 'pending' ].includes(\n\t\t\t\t\t\t\tymap.get( 'status' ) as string\n\t\t\t\t\t\t) &&\n\t\t\t\t\t\t( null === currentValue ||\n\t\t\t\t\t\t\teditedRecord.modified === currentValue );\n\n\t\t\t\t\tif ( currentDateIsFloating ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn haveValuesChanged( currentValue, newValue );\n\t\t\t\t}\n\n\t\t\t\tcase 'meta': {\n\t\t\t\t\tallowedMetaChanges = Object.fromEntries(\n\t\t\t\t\t\tObject.entries( newValue ?? {} ).filter(\n\t\t\t\t\t\t\t( [ metaKey ] ) =>\n\t\t\t\t\t\t\t\t! disallowedPostMetaKeys.has( metaKey )\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\n\t\t\t\t\t// Merge the allowed meta changes with the current meta values since\n\t\t\t\t\t// not all meta properties are synced.\n\t\t\t\t\tconst mergedValue = {\n\t\t\t\t\t\t...( currentValue as PostChanges[ 'meta' ] ),\n\t\t\t\t\t\t...allowedMetaChanges,\n\t\t\t\t\t};\n\n\t\t\t\t\treturn haveValuesChanged( currentValue, mergedValue );\n\t\t\t\t}\n\n\t\t\t\tcase 'status': {\n\t\t\t\t\t// Do not sync an invalid status.\n\t\t\t\t\tif ( 'auto-draft' === newValue ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn haveValuesChanged( currentValue, newValue );\n\t\t\t\t}\n\n\t\t\t\tcase 'excerpt':\n\t\t\t\tcase 'title': {\n\t\t\t\t\treturn haveValuesChanged(\n\t\t\t\t\t\tgetRawValue( currentValue ),\n\t\t\t\t\t\tnewValue\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Add support for additional data types here.\n\n\t\t\t\tdefault: {\n\t\t\t\t\treturn haveValuesChanged( currentValue, newValue );\n\t\t\t\t}\n\t\t\t}\n\t\t} )\n\t);\n\n\t// Meta changes must be merged with the edited record since not all meta\n\t// properties are synced.\n\tif ( 'object' === typeof changes.meta ) {\n\t\tchanges.meta = {\n\t\t\t...editedRecord.meta,\n\t\t\t...allowedMetaChanges,\n\t\t};\n\t}\n\n\treturn changes;\n}\n\n/**\n * This default sync config can be used for entities that are flat maps of\n * primitive values and do not require custom logic to merge changes.\n */\nexport const defaultSyncConfig: SyncConfig< BaseState > = {\n\tapplyChangesToCRDTDoc: defaultApplyChangesToCRDTDoc,\n\tcreateAwareness: ( ydoc: CRDTDoc ) => new BaseAwareness( ydoc ),\n\tgetChangesFromCRDTDoc: defaultGetChangesFromCRDTDoc,\n};\n\n/**\n * Extract the raw string value from a property that may be a string or an object\n * with a `raw` property (`RenderedText`).\n *\n * @param {unknown} value The value to extract from.\n * @return {string|undefined} The raw string value, or undefined if it could not be determined.\n */\nfunction getRawValue( value?: unknown ): string | undefined {\n\t// Value may be a string property or a nested object with a `raw` property.\n\tif ( 'string' === typeof value ) {\n\t\treturn value;\n\t}\n\n\tif (\n\t\tvalue &&\n\t\t'object' === typeof value &&\n\t\t'raw' in value &&\n\t\t'string' === typeof value.raw\n\t) {\n\t\treturn value.raw;\n\t}\n\n\treturn undefined;\n}\n\nfunction haveValuesChanged< ValueType >(\n\tcurrentValue: ValueType | undefined,\n\tnewValue: ValueType | undefined\n): boolean {\n\treturn ! fastDeepEqual( currentValue, newValue );\n}\n\nfunction updateMapValue< T extends YMapRecord, K extends keyof T >(\n\tmap: YMapWrap< T >,\n\tkey: K,\n\tcurrentValue: T[ K ] | undefined,\n\tnewValue: T[ K ] | undefined\n): void {\n\tif ( undefined === newValue ) {\n\t\tmap.delete( key );\n\t\treturn;\n\t}\n\n\tif ( haveValuesChanged< T[ K ] >( currentValue, newValue ) ) {\n\t\tmap.set( key, newValue );\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.js"],"names":[],"mappings":"AAwBA;;;;;;;;;;GAUG;AACH,0CALW,MAAM,SACN,WAAY,OAUtB;AAED;;;;;;;;;GASG;AACH,0DAKC;AAED;;;;;;GAMG;AACH,kDAKC;AAED;;;;;;;;;;;GAWG;AACH,2CATW,MAAM,QACN,MAAM,WACN,WAAY,UACZ,UAAO,oBACN,OAAO,OAAA,UACR,UAAO,SACP,UAAO,OAmCjB;AAED;;;;;;;;;GASG;AACH,4DAKC;AAED;;;;;;;;;GASG;AACH,kFAJW,MAAM,OAWhB;AAED;;;;;;;;;;GAUG;AACH,uEALW,MAAM,0BAchB;AAED;;;;;;;;;;GAUG;AACH,4EALW,MAAM,0BAchB;AAED;;;;;;GAMG;AACH,4CAQC;AAED;;;;;;;;;;;;GAYG;AACH,4DALW,MAAM,yBAkBhB;AAED;;;;;;;;;;;GAWG;AACH,yCALW,MAAM,WACN,GAAC,OAUX;AA0nBD;;;;;;;;GAQG;AACH,+DAJW,OAAO,OAWjB;AAED;;;;;;;;;;;GAWG;AACH,2CALW,MAAM,aACN,OAAO,OAUjB;AAED;;;;;;;;;;;;;;GAcG;AACH;;QAKC;AAED;;;;;;;;;;;GAWG;AACH,yCALW,MAAM,aACN,WAAY,OAUtB;AAED;;;;;;GAMG;AACH,wDAHW,OAAO,OAQjB;AAED;;;;;;;GAOG;AACH,iEAJW,MAAM,OAUhB;AArtBM,yCAZI,MAAM,QACN,MAAM,YACN,MAAM,GAAC,MAAM,SACb,UAAO,sCAGf;IAAgC,eAAe;IAGf,YAAY;CAE9C,IASQ;;;CAA2B,kCA0ElC;AAeK,uCATI,MAAM,QACN,MAAM,YACN,MAAM,GAAC,MAAM,wBAGrB;IAAgC,UAAU;CAE1C,OAwED;AAMK,yBAEJ;;;CAAoB,UASrB;AAMK,yBAEJ;;;CAAoB,UASrB;AAOK,iDAIL;AAgBK,uCAXI,MAAM,QACN,MAAM,gEAGd;IAA2B,UAAU;IACV,eAAe;IAGf,YAAY;CAEzC,IAYQ;;;;CAAmC,kBAsO1C;AAwBK,sDAHK,CAAC,SAAS,KAAQ,iBAAY,CAuCxC;AAUK,6CALI,MAAM,QACN,MAAM,2BAEN,eAAO,IAIT;;;;CAAmC,kBAqB1C;AAWK,6DANI,MAAM,QACN,MAAM,YACN,MAAM,GAAC,MAAM,sCAMf;;;;CAAmC,kBAwC1C;AA2HK,uCARI,MAAM,QACN,MAAM,aACN,MAAM,GAAC,MAAM,WACb,WAAY,SACZ,UAAO,oBACN,OAAO,OAAA,qBACR,UAAO,IAIT;;;CAA2B,mBAsBlC"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.js"],"names":[],"mappings":"AAwBA;;;;;;;;;;GAUG;AACH,0CALW,MAAM,SACN,WAAY,OAUtB;AAED;;;;;;;;;GASG;AACH,0DAKC;AAED;;;;;;GAMG;AACH,kDAKC;AAED;;;;;;;;;;;GAWG;AACH,2CATW,MAAM,QACN,MAAM,WACN,WAAY,UACZ,UAAO,oBACN,OAAO,OAAA,UACR,UAAO,SACP,UAAO,OAmCjB;AAED;;;;;;;;;GASG;AACH,4DAKC;AAED;;;;;;;;;GASG;AACH,kFAJW,MAAM,OAWhB;AAED;;;;;;;;;;GAUG;AACH,uEALW,MAAM,0BAchB;AAED;;;;;;;;;;GAUG;AACH,4EALW,MAAM,0BAchB;AAED;;;;;;GAMG;AACH,4CAQC;AAED;;;;;;;;;;;;GAYG;AACH,4DALW,MAAM,yBAkBhB;AAED;;;;;;;;;;;GAWG;AACH,yCALW,MAAM,WACN,GAAC,OAUX;AA2pBD;;;;;;;;GAQG;AACH,+DAJW,OAAO,OAWjB;AAED;;;;;;;;;;;GAWG;AACH,2CALW,MAAM,aACN,OAAO,OAUjB;AAED;;;;;;;;;;;;;;GAcG;AACH;;QAKC;AAED;;;;;;;;;;;GAWG;AACH,yCALW,MAAM,aACN,WAAY,OAUtB;AAED;;;;;;GAMG;AACH,wDAHW,OAAO,OAQjB;AAED;;;;;;;GAOG;AACH,iEAJW,MAAM,OAUhB;AAtvBM,yCAZI,MAAM,QACN,MAAM,YACN,MAAM,GAAC,MAAM,SACb,UAAO,sCAGf;IAAgC,eAAe;IAGf,YAAY;CAE9C,IASQ;;;CAA2B,kCAmFlC;AAeK,uCATI,MAAM,QACN,MAAM,YACN,MAAM,GAAC,MAAM,wBAGrB;IAAgC,UAAU;CAE1C,OAgGD;AAMK,yBAEJ;;;CAAoB,UASrB;AAMK,yBAEJ;;;CAAoB,UASrB;AAOK,iDAIL;AAgBK,uCAXI,MAAM,QACN,MAAM,gEAGd;IAA2B,UAAU;IACV,eAAe;IAGf,YAAY;CAEzC,IAYQ;;;;CAAmC,kBAsO1C;AAwBK,sDAHK,CAAC,SAAS,KAAQ,iBAAY,CAuCxC;AAUK,6CALI,MAAM,QACN,MAAM,2BAEN,eAAO,IAIT;;;;CAAmC,kBAqB1C;AAWK,6DANI,MAAM,QACN,MAAM,YACN,MAAM,GAAC,MAAM,sCAMf;;;;CAAmC,kBAwC1C;AA2HK,uCARI,MAAM,QACN,MAAM,aACN,MAAM,GAAC,MAAM,WACb,WAAY,SACZ,UAAO,oBACN,OAAO,OAAA,qBACR,UAAO,IAIT;;;CAA2B,mBAsBlC"}
@@ -0,0 +1,19 @@
1
+ import { AwarenessState } from '@wordpress/sync';
2
+ import { areUserInfosEqual } from './utils';
3
+ import type { BaseState } from './types';
4
+ export declare abstract class BaseAwarenessState<State extends BaseState> extends AwarenessState<State> {
5
+ setUp(): void;
6
+ /**
7
+ * Set the current user info in the local state.
8
+ */
9
+ private setCurrentUserInfo;
10
+ }
11
+ export declare const baseEqualityFieldChecks: {
12
+ userInfo: typeof areUserInfosEqual;
13
+ };
14
+ export declare class BaseAwareness extends BaseAwarenessState<BaseState> {
15
+ protected equalityFieldChecks: {
16
+ userInfo: typeof areUserInfosEqual;
17
+ };
18
+ }
19
+ //# sourceMappingURL=base-awareness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-awareness.d.ts","sourceRoot":"","sources":["../../src/awareness/base-awareness.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMjD,OAAO,EAAoB,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,8BAAsB,kBAAkB,CACvC,KAAK,SAAS,SAAS,CACtB,SAAQ,cAAc,CAAE,KAAK,CAAE;IACzB,KAAK,IAAI,IAAI;IAMpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAe1B;AAED,eAAO,MAAM,uBAAuB;;CAEnC,CAAC;AAEF,qBAAa,aAAc,SAAQ,kBAAkB,CAAE,SAAS,CAAE;IACjE,SAAS,CAAC,mBAAmB;;MAA2B;CACxD"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Delay in milliseconds before throttling the cursor position updates.
3
+ */
4
+ export declare const AWARENESS_CURSOR_UPDATE_THROTTLE_IN_MS = 100;
5
+ /**
6
+ * Delay in milliseconds before updating the cursor position.
7
+ */
8
+ export declare const LOCAL_CURSOR_UPDATE_DEBOUNCE_IN_MS = 5;
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/awareness/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,kCAAkC,IAAI,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { Y } from '@wordpress/sync';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ import { BaseAwarenessState } from './base-awareness';
6
+ import type { EditorState, PostEditorState } from './types';
7
+ export declare class PostEditorAwareness extends BaseAwarenessState<PostEditorState> {
8
+ private kind;
9
+ private name;
10
+ private postId;
11
+ protected equalityFieldChecks: {
12
+ editorState: (state1?: EditorState, state2?: EditorState) => boolean;
13
+ userInfo: typeof import("./utils").areUserInfosEqual;
14
+ };
15
+ constructor(doc: Y.Doc, kind: string, name: string, postId: number);
16
+ setUp(): void;
17
+ /**
18
+ * Subscribe to user selection changes and update the selection state.
19
+ */
20
+ private subscribeToUserSelectionChanges;
21
+ /**
22
+ * Update the entity record with the current user's selection.
23
+ *
24
+ * @param selectionStart - The start position of the selection.
25
+ * @param selectionEnd - The end position of the selection.
26
+ * @param initialPosition - The initial position of the selection.
27
+ */
28
+ private updateSelectionInEntityRecord;
29
+ /**
30
+ * Check if two editor states are equal.
31
+ *
32
+ * @param state1 - The first editor state.
33
+ * @param state2 - The second editor state.
34
+ * @return True if the editor states are equal, false otherwise.
35
+ */
36
+ private areEditorStatesEqual;
37
+ }
38
+ //# sourceMappingURL=post-editor-awareness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-editor-awareness.d.ts","sourceRoot":"","sources":["../../src/awareness/post-editor-awareness.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAIzC;;GAEG;AACH,OAAO,EAAE,kBAAkB,EAA2B,MAAM,kBAAkB,CAAC;AAY/E,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE5D,qBAAa,mBAAoB,SAAQ,kBAAkB,CAAE,eAAe,CAAE;IAQ5E,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IATf,SAAS,CAAC,mBAAmB;+BAmInB,WAAW,WACX,WAAW,KAClB,OAAO;;MAlIR;gBAGD,GAAG,EAAE,CAAC,CAAC,GAAG,EACF,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM;IAKhB,KAAK,IAAI,IAAI;IAMpB;;OAEG;IACH,OAAO,CAAC,+BAA+B;IA8DvC;;;;;;OAMG;YACW,6BAA6B;IA+B3C;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;CAU5B"}