@y/y 14.0.0-19 → 14.0.0-21

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 (135) hide show
  1. package/README.md +7 -5
  2. package/dist/src/index.d.ts +2 -1
  3. package/dist/src/internals.d.ts +2 -9
  4. package/dist/src/structs/ContentType.d.ts +6 -12
  5. package/dist/src/structs/ContentType.d.ts.map +1 -1
  6. package/dist/src/structs/Item.d.ts +5 -6
  7. package/dist/src/structs/Item.d.ts.map +1 -1
  8. package/dist/src/utils/AttributionManager.d.ts +16 -14
  9. package/dist/src/utils/AttributionManager.d.ts.map +1 -1
  10. package/dist/src/utils/Doc.d.ts +7 -70
  11. package/dist/src/utils/Doc.d.ts.map +1 -1
  12. package/dist/src/utils/ID.d.ts +2 -2
  13. package/dist/src/utils/ID.d.ts.map +1 -1
  14. package/dist/src/utils/IdMap.d.ts +22 -19
  15. package/dist/src/utils/IdMap.d.ts.map +1 -1
  16. package/dist/src/utils/IdSet.d.ts +6 -6
  17. package/dist/src/utils/IdSet.d.ts.map +1 -1
  18. package/dist/src/utils/RelativePosition.d.ts +8 -8
  19. package/dist/src/utils/RelativePosition.d.ts.map +1 -1
  20. package/dist/src/utils/Snapshot.d.ts +3 -3
  21. package/dist/src/utils/Snapshot.d.ts.map +1 -1
  22. package/dist/src/utils/Transaction.d.ts +9 -5
  23. package/dist/src/utils/Transaction.d.ts.map +1 -1
  24. package/dist/src/utils/UndoManager.d.ts +14 -12
  25. package/dist/src/utils/UndoManager.d.ts.map +1 -1
  26. package/dist/src/utils/UpdateDecoder.d.ts +8 -4
  27. package/dist/src/utils/UpdateDecoder.d.ts.map +1 -1
  28. package/dist/src/utils/UpdateEncoder.d.ts +2 -0
  29. package/dist/src/utils/UpdateEncoder.d.ts.map +1 -1
  30. package/dist/src/utils/YEvent.d.ts +21 -42
  31. package/dist/src/utils/YEvent.d.ts.map +1 -1
  32. package/dist/src/utils/encoding.d.ts +3 -3
  33. package/dist/src/utils/encoding.d.ts.map +1 -1
  34. package/dist/src/utils/isParentOf.d.ts +1 -1
  35. package/dist/src/utils/isParentOf.d.ts.map +1 -1
  36. package/dist/src/utils/logging.d.ts +2 -2
  37. package/dist/src/utils/logging.d.ts.map +1 -1
  38. package/dist/src/utils/meta.d.ts +71 -0
  39. package/dist/src/utils/meta.d.ts.map +1 -0
  40. package/dist/src/utils/ts.d.ts +4 -0
  41. package/dist/src/utils/ts.d.ts.map +1 -0
  42. package/dist/src/utils/updates.d.ts +11 -11
  43. package/dist/src/utils/updates.d.ts.map +1 -1
  44. package/dist/src/ytype.d.ts +498 -0
  45. package/dist/src/ytype.d.ts.map +1 -0
  46. package/dist/tests/IdMap.tests.d.ts.map +1 -1
  47. package/dist/tests/attribution.tests.d.ts +1 -0
  48. package/dist/tests/attribution.tests.d.ts.map +1 -1
  49. package/dist/tests/compatibility.tests.d.ts.map +1 -1
  50. package/dist/tests/doc.tests.d.ts.map +1 -1
  51. package/dist/tests/relativePositions.tests.d.ts +9 -9
  52. package/dist/tests/relativePositions.tests.d.ts.map +1 -1
  53. package/dist/tests/snapshot.tests.d.ts.map +1 -1
  54. package/dist/tests/testHelper.d.ts +28 -27
  55. package/dist/tests/testHelper.d.ts.map +1 -1
  56. package/dist/tests/undo-redo.tests.d.ts.map +1 -1
  57. package/dist/tests/updates.tests.d.ts +2 -1
  58. package/dist/tests/updates.tests.d.ts.map +1 -1
  59. package/dist/tests/y-array.tests.d.ts +0 -2
  60. package/dist/tests/y-array.tests.d.ts.map +1 -1
  61. package/dist/tests/y-map.tests.d.ts +0 -3
  62. package/dist/tests/y-map.tests.d.ts.map +1 -1
  63. package/dist/tests/y-text.tests.d.ts +1 -1
  64. package/dist/tests/y-text.tests.d.ts.map +1 -1
  65. package/dist/tests/y-xml.tests.d.ts +0 -1
  66. package/dist/tests/y-xml.tests.d.ts.map +1 -1
  67. package/package.json +16 -16
  68. package/src/index.js +156 -0
  69. package/src/internals.js +35 -0
  70. package/src/structs/AbstractStruct.js +59 -0
  71. package/src/structs/ContentAny.js +115 -0
  72. package/src/structs/ContentBinary.js +93 -0
  73. package/src/structs/ContentDeleted.js +101 -0
  74. package/src/structs/ContentDoc.js +141 -0
  75. package/src/structs/ContentEmbed.js +98 -0
  76. package/src/structs/ContentFormat.js +105 -0
  77. package/src/structs/ContentJSON.js +119 -0
  78. package/src/structs/ContentString.js +113 -0
  79. package/src/structs/ContentType.js +152 -0
  80. package/src/structs/GC.js +80 -0
  81. package/src/structs/Item.js +841 -0
  82. package/src/structs/Skip.js +75 -0
  83. package/src/utils/AttributionManager.js +653 -0
  84. package/src/utils/Doc.js +266 -0
  85. package/src/utils/EventHandler.js +87 -0
  86. package/src/utils/ID.js +89 -0
  87. package/src/utils/IdMap.js +673 -0
  88. package/src/utils/IdSet.js +825 -0
  89. package/src/utils/RelativePosition.js +352 -0
  90. package/src/utils/Snapshot.js +220 -0
  91. package/src/utils/StructSet.js +137 -0
  92. package/src/utils/StructStore.js +289 -0
  93. package/src/utils/Transaction.js +671 -0
  94. package/src/utils/UndoManager.js +406 -0
  95. package/src/utils/UpdateDecoder.js +285 -0
  96. package/src/utils/UpdateEncoder.js +327 -0
  97. package/src/utils/YEvent.js +189 -0
  98. package/src/utils/delta-helpers.js +54 -0
  99. package/src/utils/encoding.js +623 -0
  100. package/src/utils/isParentOf.js +21 -0
  101. package/src/utils/logging.js +21 -0
  102. package/src/utils/meta.js +190 -0
  103. package/src/utils/ts.js +3 -0
  104. package/src/utils/updates.js +802 -0
  105. package/src/ytype.js +1962 -0
  106. package/dist/Skip-CE05BUF8.js +0 -11875
  107. package/dist/Skip-CE05BUF8.js.map +0 -1
  108. package/dist/index-C21sDQ5u.js +0 -163
  109. package/dist/index-C21sDQ5u.js.map +0 -1
  110. package/dist/internals.js +0 -25
  111. package/dist/internals.js.map +0 -1
  112. package/dist/src/types/AbstractType.d.ts +0 -239
  113. package/dist/src/types/AbstractType.d.ts.map +0 -1
  114. package/dist/src/types/YArray.d.ts +0 -128
  115. package/dist/src/types/YArray.d.ts.map +0 -1
  116. package/dist/src/types/YMap.d.ts +0 -112
  117. package/dist/src/types/YMap.d.ts.map +0 -1
  118. package/dist/src/types/YText.d.ts +0 -216
  119. package/dist/src/types/YText.d.ts.map +0 -1
  120. package/dist/src/types/YXmlElement.d.ts +0 -106
  121. package/dist/src/types/YXmlElement.d.ts.map +0 -1
  122. package/dist/src/types/YXmlFragment.d.ts +0 -143
  123. package/dist/src/types/YXmlFragment.d.ts.map +0 -1
  124. package/dist/src/types/YXmlHook.d.ts +0 -32
  125. package/dist/src/types/YXmlHook.d.ts.map +0 -1
  126. package/dist/src/types/YXmlText.d.ts +0 -34
  127. package/dist/src/types/YXmlText.d.ts.map +0 -1
  128. package/dist/src/utils/AbstractConnector.d.ts +0 -20
  129. package/dist/src/utils/AbstractConnector.d.ts.map +0 -1
  130. package/dist/src/utils/types.d.ts +0 -7
  131. package/dist/src/utils/types.d.ts.map +0 -1
  132. package/dist/testHelper.js +0 -617
  133. package/dist/testHelper.js.map +0 -1
  134. package/dist/yjs.js +0 -26
  135. package/dist/yjs.js.map +0 -1
@@ -0,0 +1,841 @@
1
+ import {
2
+ GC,
3
+ getState,
4
+ AbstractStruct,
5
+ replaceStruct,
6
+ addStruct,
7
+ addToIdSet,
8
+ findRootTypeKey,
9
+ compareIDs,
10
+ getItem,
11
+ getItemCleanEnd,
12
+ getItemCleanStart,
13
+ readContentDeleted,
14
+ readContentBinary,
15
+ readContentJSON,
16
+ readContentAny,
17
+ readContentString,
18
+ readContentEmbed,
19
+ readContentDoc,
20
+ createID,
21
+ readContentFormat,
22
+ readContentType,
23
+ addChangedTypeToTransaction,
24
+ addStructToIdSet,
25
+ IdSet, StackItem, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, YType, Transaction, // eslint-disable-line
26
+ } from '../internals.js'
27
+
28
+ import * as error from 'lib0/error'
29
+ import * as binary from 'lib0/binary'
30
+ import * as array from 'lib0/array'
31
+
32
+ /**
33
+ * @todo This should return several items
34
+ *
35
+ * @param {StructStore} store
36
+ * @param {ID} id
37
+ * @return {{item:Item, diff:number}}
38
+ */
39
+ export const followRedone = (store, id) => {
40
+ /**
41
+ * @type {ID|null}
42
+ */
43
+ let nextID = id
44
+ let diff = 0
45
+ let item
46
+ do {
47
+ if (diff > 0) {
48
+ nextID = createID(nextID.client, nextID.clock + diff)
49
+ }
50
+ item = getItem(store, nextID)
51
+ diff = nextID.clock - item.id.clock
52
+ nextID = item.redone
53
+ } while (nextID !== null && item instanceof Item)
54
+ return {
55
+ item, diff
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Make sure that neither item nor any of its parents is ever deleted.
61
+ *
62
+ * This property does not persist when storing it into a database or when
63
+ * sending it to other peers
64
+ *
65
+ * @param {Item|null} item
66
+ * @param {boolean} keep
67
+ */
68
+ export const keepItem = (item, keep) => {
69
+ while (item !== null && item.keep !== keep) {
70
+ item.keep = keep
71
+ item = /** @type {YType} */ (item.parent)._item
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Split leftItem into two items
77
+ * @param {Transaction?} transaction
78
+ * @param {Item} leftItem
79
+ * @param {number} diff
80
+ * @return {Item}
81
+ *
82
+ * @function
83
+ * @private
84
+ */
85
+ export const splitItem = (transaction, leftItem, diff) => {
86
+ // create rightItem
87
+ const { client, clock } = leftItem.id
88
+ const rightItem = new Item(
89
+ createID(client, clock + diff),
90
+ leftItem,
91
+ createID(client, clock + diff - 1),
92
+ leftItem.right,
93
+ leftItem.rightOrigin,
94
+ leftItem.parent,
95
+ leftItem.parentSub,
96
+ leftItem.content.splice(diff)
97
+ )
98
+ if (leftItem.deleted) {
99
+ rightItem.markDeleted()
100
+ }
101
+ if (leftItem.keep) {
102
+ rightItem.keep = true
103
+ }
104
+ if (leftItem.redone !== null) {
105
+ rightItem.redone = createID(leftItem.redone.client, leftItem.redone.clock + diff)
106
+ }
107
+ if (transaction != null) {
108
+ // update left (do not set leftItem.rightOrigin as it will lead to problems when syncing)
109
+ leftItem.right = rightItem
110
+ // update right
111
+ if (rightItem.right !== null) {
112
+ rightItem.right.left = rightItem
113
+ }
114
+ // right is more specific.
115
+ transaction._mergeStructs.push(rightItem)
116
+ // update parent._map
117
+ if (rightItem.parentSub !== null && rightItem.right === null) {
118
+ /** @type {YType} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem)
119
+ }
120
+ } else {
121
+ rightItem.left = null
122
+ rightItem.right = null
123
+ }
124
+ leftItem.length = diff
125
+ return rightItem
126
+ }
127
+
128
+ /**
129
+ * More generalized version of splitItem. Split leftStruct into two structs
130
+ * @param {Transaction?} transaction
131
+ * @param {AbstractStruct} leftStruct
132
+ * @param {number} diff
133
+ * @return {GC|Item}
134
+ *
135
+ * @function
136
+ * @private
137
+ */
138
+ export const splitStruct = (transaction, leftStruct, diff) => {
139
+ if (leftStruct instanceof Item) {
140
+ return splitItem(transaction, leftStruct, diff)
141
+ } else {
142
+ const rightItem = leftStruct.splice(diff)
143
+ transaction?._mergeStructs.push(rightItem)
144
+ return rightItem
145
+ }
146
+ }
147
+
148
+ /**
149
+ * @param {Array<StackItem>} stack
150
+ * @param {ID} id
151
+ */
152
+ const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => s.deletes.hasId(id))
153
+
154
+ /**
155
+ * Redoes the effect of this operation.
156
+ *
157
+ * @param {Transaction} transaction The Yjs instance.
158
+ * @param {Item} item
159
+ * @param {Set<Item>} redoitems
160
+ * @param {IdSet} itemsToDelete
161
+ * @param {boolean} ignoreRemoteMapChanges
162
+ * @param {import('../utils/UndoManager.js').UndoManager} um
163
+ *
164
+ * @return {Item|null}
165
+ *
166
+ * @private
167
+ */
168
+ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) => {
169
+ const doc = transaction.doc
170
+ const store = doc.store
171
+ const ownClientID = doc.clientID
172
+ const redone = item.redone
173
+ if (redone !== null) {
174
+ return getItemCleanStart(transaction, redone)
175
+ }
176
+ let parentItem = /** @type {YType} */ (item.parent)._item
177
+ /**
178
+ * @type {Item|null}
179
+ */
180
+ let left = null
181
+ /**
182
+ * @type {Item|null}
183
+ */
184
+ let right
185
+ // make sure that parent is redone
186
+ if (parentItem !== null && parentItem.deleted === true) {
187
+ // try to undo parent if it will be undone anyway
188
+ if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) === null)) {
189
+ return null
190
+ }
191
+ while (parentItem.redone !== null) {
192
+ parentItem = getItemCleanStart(transaction, parentItem.redone)
193
+ }
194
+ }
195
+ /**
196
+ * @type {YType}
197
+ */
198
+ const parentType = /** @type {YType} */ (parentItem === null ? item.parent : /** @type {ContentType} */ (parentItem.content).type)
199
+
200
+ if (item.parentSub === null) {
201
+ // Is an array item. Insert at the old position
202
+ left = item.left
203
+ right = item
204
+ // find next cloned_redo items
205
+ while (left !== null) {
206
+ /**
207
+ * @type {Item|null}
208
+ */
209
+ let leftTrace = left
210
+ // trace redone until parent matches
211
+ while (leftTrace !== null && /** @type {YType} */ (leftTrace.parent)._item !== parentItem) {
212
+ leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone)
213
+ }
214
+ if (leftTrace !== null && /** @type {YType} */ (leftTrace.parent)._item === parentItem) {
215
+ left = leftTrace
216
+ break
217
+ }
218
+ left = left.left
219
+ }
220
+ while (right !== null) {
221
+ /**
222
+ * @type {Item|null}
223
+ */
224
+ let rightTrace = right
225
+ // trace redone until parent matches
226
+ while (rightTrace !== null && /** @type {YType} */ (rightTrace.parent)._item !== parentItem) {
227
+ rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone)
228
+ }
229
+ if (rightTrace !== null && /** @type {YType} */ (rightTrace.parent)._item === parentItem) {
230
+ right = rightTrace
231
+ break
232
+ }
233
+ right = right.right
234
+ }
235
+ } else {
236
+ right = null
237
+ if (item.right && !ignoreRemoteMapChanges) {
238
+ left = item
239
+ // Iterate right while right is in itemsToDelete
240
+ // If it is intended to delete right while item is redone, we can expect that item should replace right.
241
+ while (left !== null && left.right !== null && (left.right.redone || itemsToDelete.hasId(left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) {
242
+ left = left.right
243
+ // follow redone
244
+ while (left.redone) left = getItemCleanStart(transaction, left.redone)
245
+ }
246
+ if (left && left.right !== null) {
247
+ // It is not possible to redo this item because it conflicts with a
248
+ // change from another client
249
+ return null
250
+ }
251
+ } else {
252
+ left = parentType._map.get(item.parentSub) || null
253
+ }
254
+ }
255
+ const nextClock = getState(store, ownClientID)
256
+ const nextId = createID(ownClientID, nextClock)
257
+ const redoneItem = new Item(
258
+ nextId,
259
+ left, left && left.lastId,
260
+ right, right && right.id,
261
+ parentType,
262
+ item.parentSub,
263
+ item.content.copy()
264
+ )
265
+ item.redone = nextId
266
+ keepItem(redoneItem, true)
267
+ redoneItem.integrate(transaction, 0)
268
+ return redoneItem
269
+ }
270
+
271
+ /**
272
+ * Abstract class that represents any content.
273
+ */
274
+ export class Item extends AbstractStruct {
275
+ /**
276
+ * @param {ID} id
277
+ * @param {Item | null} left
278
+ * @param {ID | null} origin
279
+ * @param {Item | null} right
280
+ * @param {ID | null} rightOrigin
281
+ * @param {YType|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it.
282
+ * @param {string | null} parentSub
283
+ * @param {AbstractContent} content
284
+ */
285
+ constructor (id, left, origin, right, rightOrigin, parent, parentSub, content) {
286
+ super(id, content.getLength())
287
+ /**
288
+ * The item that was originally to the left of this item.
289
+ * @type {ID | null}
290
+ */
291
+ this.origin = origin
292
+ /**
293
+ * The item that is currently to the left of this item.
294
+ * @type {Item | null}
295
+ */
296
+ this.left = left
297
+ /**
298
+ * The item that is currently to the right of this item.
299
+ * @type {Item | null}
300
+ */
301
+ this.right = right
302
+ /**
303
+ * The item that was originally to the right of this item.
304
+ * @type {ID | null}
305
+ */
306
+ this.rightOrigin = rightOrigin
307
+ /**
308
+ * @type {YType|ID|null}
309
+ */
310
+ this.parent = parent
311
+ /**
312
+ * If the parent refers to this item with some kind of key (e.g. YMap, the
313
+ * key is specified here. The key is then used to refer to the list in which
314
+ * to insert this item. If `parentSub = null` type._start is the list in
315
+ * which to insert to. Otherwise it is `parent._map`.
316
+ * @type {String | null}
317
+ */
318
+ this.parentSub = parentSub
319
+ /**
320
+ * If this type's effect is redone this type refers to the type that undid
321
+ * this operation.
322
+ * @type {ID | null}
323
+ */
324
+ this.redone = null
325
+ /**
326
+ * @type {AbstractContent}
327
+ */
328
+ this.content = content
329
+ /**
330
+ * bit1: keep
331
+ * bit2: countable
332
+ * bit3: deleted
333
+ * bit4: mark - mark node as fast-search-marker
334
+ * @type {number} byte
335
+ */
336
+ this.info = this.content.isCountable() ? binary.BIT2 : 0
337
+ }
338
+
339
+ /**
340
+ * This is used to mark the item as an indexed fast-search marker
341
+ *
342
+ * @type {boolean}
343
+ */
344
+ set marker (isMarked) {
345
+ if (((this.info & binary.BIT4) > 0) !== isMarked) {
346
+ this.info ^= binary.BIT4
347
+ }
348
+ }
349
+
350
+ get marker () {
351
+ return (this.info & binary.BIT4) > 0
352
+ }
353
+
354
+ /**
355
+ * If true, do not garbage collect this Item.
356
+ */
357
+ get keep () {
358
+ return (this.info & binary.BIT1) > 0
359
+ }
360
+
361
+ set keep (doKeep) {
362
+ if (this.keep !== doKeep) {
363
+ this.info ^= binary.BIT1
364
+ }
365
+ }
366
+
367
+ get countable () {
368
+ return (this.info & binary.BIT2) > 0
369
+ }
370
+
371
+ /**
372
+ * Whether this item was deleted or not.
373
+ * @type {Boolean}
374
+ */
375
+ get deleted () {
376
+ return (this.info & binary.BIT3) > 0
377
+ }
378
+
379
+ set deleted (doDelete) {
380
+ if (this.deleted !== doDelete) {
381
+ this.info ^= binary.BIT3
382
+ }
383
+ }
384
+
385
+ markDeleted () {
386
+ this.info |= binary.BIT3
387
+ }
388
+
389
+ /**
390
+ * Return the creator clientID of the missing op or define missing items and return null.
391
+ *
392
+ * @param {Transaction} transaction
393
+ * @param {StructStore} store
394
+ * @return {null | number}
395
+ */
396
+ getMissing (transaction, store) {
397
+ if (this.origin && (this.origin.clock >= getState(store, this.origin.client) || store.skips.hasId(this.origin))) {
398
+ return this.origin.client
399
+ }
400
+ if (this.rightOrigin && (this.rightOrigin.clock >= getState(store, this.rightOrigin.client) || store.skips.hasId(this.rightOrigin))) {
401
+ return this.rightOrigin.client
402
+ }
403
+ if (this.parent && this.parent.constructor === ID && (this.parent.clock >= getState(store, this.parent.client) || store.skips.hasId(this.parent))) {
404
+ return this.parent.client
405
+ }
406
+ // We have all missing ids, now find the items
407
+ if (this.origin) {
408
+ this.left = getItemCleanEnd(transaction, store, this.origin)
409
+ this.origin = this.left.lastId
410
+ }
411
+ if (this.rightOrigin) {
412
+ this.right = getItemCleanStart(transaction, this.rightOrigin)
413
+ this.rightOrigin = this.right.id
414
+ }
415
+ if ((this.left && this.left.constructor === GC) || (this.right && this.right.constructor === GC)) {
416
+ this.parent = null
417
+ } else if (!this.parent) {
418
+ // only set parent if this shouldn't be garbage collected
419
+ if (this.left && this.left.constructor === Item) {
420
+ this.parent = this.left.parent
421
+ this.parentSub = this.left.parentSub
422
+ } else if (this.right && this.right.constructor === Item) {
423
+ this.parent = this.right.parent
424
+ this.parentSub = this.right.parentSub
425
+ }
426
+ } else if (this.parent.constructor === ID) {
427
+ const parentItem = getItem(store, this.parent)
428
+ if (parentItem.constructor === GC) {
429
+ this.parent = null
430
+ } else {
431
+ this.parent = /** @type {ContentType} */ (parentItem.content).type
432
+ }
433
+ }
434
+ return null
435
+ }
436
+
437
+ /**
438
+ * @param {Transaction} transaction
439
+ * @param {number} offset
440
+ */
441
+ integrate (transaction, offset) {
442
+ if (offset > 0) {
443
+ this.id.clock += offset
444
+ this.left = getItemCleanEnd(transaction, transaction.doc.store, createID(this.id.client, this.id.clock - 1))
445
+ this.origin = this.left.lastId
446
+ this.content = this.content.splice(offset)
447
+ this.length -= offset
448
+ }
449
+
450
+ if (this.parent) {
451
+ if ((!this.left && (!this.right || this.right.left !== null)) || (this.left && this.left.right !== this.right)) {
452
+ /**
453
+ * @type {Item|null}
454
+ */
455
+ let left = this.left
456
+
457
+ /**
458
+ * @type {Item|null}
459
+ */
460
+ let o
461
+ // set o to the first conflicting item
462
+ if (left !== null) {
463
+ o = left.right
464
+ } else if (this.parentSub !== null) {
465
+ o = /** @type {YType} */ (this.parent)._map.get(this.parentSub) || null
466
+ while (o !== null && o.left !== null) {
467
+ o = o.left
468
+ }
469
+ } else {
470
+ o = /** @type {YType} */ (this.parent)._start
471
+ }
472
+ // TODO: use something like DeleteSet here (a tree implementation would be best)
473
+ // @todo use global set definitions
474
+ /**
475
+ * @type {Set<Item>}
476
+ */
477
+ const conflictingItems = new Set()
478
+ /**
479
+ * @type {Set<Item>}
480
+ */
481
+ const itemsBeforeOrigin = new Set()
482
+ // Let c in conflictingItems, b in itemsBeforeOrigin
483
+ // ***{origin}bbbb{this}{c,b}{c,b}{o}***
484
+ // Note that conflictingItems is a subset of itemsBeforeOrigin
485
+ while (o !== null && o !== this.right) {
486
+ itemsBeforeOrigin.add(o)
487
+ conflictingItems.add(o)
488
+ if (compareIDs(this.origin, o.origin)) {
489
+ // case 1
490
+ if (o.id.client < this.id.client) {
491
+ left = o
492
+ conflictingItems.clear()
493
+ } else if (compareIDs(this.rightOrigin, o.rightOrigin)) {
494
+ // this and o are conflicting and point to the same integration points. The id decides which item comes first.
495
+ // Since this is to the left of o, we can break here
496
+ break
497
+ } // else, o might be integrated before an item that this conflicts with. If so, we will find it in the next iterations
498
+ } else if (o.origin !== null && itemsBeforeOrigin.has(getItem(transaction.doc.store, o.origin))) { // use getItem instead of getItemCleanEnd because we don't want / need to split items.
499
+ // case 2
500
+ if (!conflictingItems.has(getItem(transaction.doc.store, o.origin))) {
501
+ left = o
502
+ conflictingItems.clear()
503
+ }
504
+ } else {
505
+ break
506
+ }
507
+ o = o.right
508
+ }
509
+ this.left = left
510
+ }
511
+ // reconnect left/right + update parent map/start if necessary
512
+ if (this.left !== null) {
513
+ const right = this.left.right
514
+ this.right = right
515
+ this.left.right = this
516
+ } else {
517
+ let r
518
+ if (this.parentSub !== null) {
519
+ r = /** @type {YType} */ (this.parent)._map.get(this.parentSub) || null
520
+ while (r !== null && r.left !== null) {
521
+ r = r.left
522
+ }
523
+ } else {
524
+ r = /** @type {YType} */ (this.parent)._start
525
+ ;/** @type {YType} */ (this.parent)._start = this
526
+ }
527
+ this.right = r
528
+ }
529
+ if (this.right !== null) {
530
+ this.right.left = this
531
+ } else if (this.parentSub !== null) {
532
+ // set as current parent value if right === null and this is parentSub
533
+ /** @type {YType} */ (this.parent)._map.set(this.parentSub, this)
534
+ if (this.left !== null) {
535
+ // this is the current attribute value of parent. delete right
536
+ this.left.delete(transaction)
537
+ }
538
+ }
539
+ // adjust length of parent
540
+ if (this.parentSub === null && this.countable && !this.deleted) {
541
+ /** @type {YType} */ (this.parent)._length += this.length
542
+ }
543
+ addStructToIdSet(transaction.insertSet, this)
544
+ addStruct(transaction.doc.store, this)
545
+ this.content.integrate(transaction, this)
546
+ // add parent to transaction.changed
547
+ addChangedTypeToTransaction(transaction, /** @type {YType} */ (this.parent), this.parentSub)
548
+ if ((/** @type {YType} */ (this.parent)._item !== null && /** @type {YType} */ (this.parent)._item.deleted) || (this.parentSub !== null && this.right !== null)) {
549
+ // delete if parent is deleted or if this is not the current attribute value of parent
550
+ this.delete(transaction)
551
+ }
552
+ } else {
553
+ // parent is not defined. Integrate GC struct instead
554
+ new GC(this.id, this.length).integrate(transaction, 0)
555
+ }
556
+ }
557
+
558
+ /**
559
+ * Returns the next non-deleted item
560
+ */
561
+ get next () {
562
+ let n = this.right
563
+ while (n !== null && n.deleted) {
564
+ n = n.right
565
+ }
566
+ return n
567
+ }
568
+
569
+ /**
570
+ * Returns the previous non-deleted item
571
+ */
572
+ get prev () {
573
+ let n = this.left
574
+ while (n !== null && n.deleted) {
575
+ n = n.left
576
+ }
577
+ return n
578
+ }
579
+
580
+ /**
581
+ * Computes the last content address of this Item.
582
+ */
583
+ get lastId () {
584
+ // allocating ids is pretty costly because of the amount of ids created, so we try to reuse whenever possible
585
+ return this.length === 1 ? this.id : createID(this.id.client, this.id.clock + this.length - 1)
586
+ }
587
+
588
+ /**
589
+ * Try to merge two items
590
+ *
591
+ * @param {Item} right
592
+ * @return {boolean}
593
+ */
594
+ mergeWith (right) {
595
+ if (
596
+ this.constructor === right.constructor &&
597
+ compareIDs(right.origin, this.lastId) &&
598
+ this.right === right &&
599
+ compareIDs(this.rightOrigin, right.rightOrigin) &&
600
+ this.id.client === right.id.client &&
601
+ this.id.clock + this.length === right.id.clock &&
602
+ this.deleted === right.deleted &&
603
+ this.redone === null &&
604
+ right.redone === null &&
605
+ this.content.constructor === right.content.constructor &&
606
+ this.content.mergeWith(right.content)
607
+ ) {
608
+ const searchMarker = /** @type {YType} */ (this.parent)._searchMarker
609
+ if (searchMarker) {
610
+ searchMarker.forEach(marker => {
611
+ if (marker.p === right) {
612
+ // right is going to be "forgotten" so we need to update the marker
613
+ marker.p = this
614
+ // adjust marker index
615
+ if (!this.deleted && this.countable) {
616
+ marker.index -= this.length
617
+ }
618
+ }
619
+ })
620
+ }
621
+ if (right.keep) {
622
+ this.keep = true
623
+ }
624
+ this.right = right.right
625
+ if (this.right !== null) {
626
+ this.right.left = this
627
+ }
628
+ this.length += right.length
629
+ return true
630
+ }
631
+ return false
632
+ }
633
+
634
+ /**
635
+ * Mark this Item as deleted.
636
+ *
637
+ * @param {Transaction} transaction
638
+ */
639
+ delete (transaction) {
640
+ if (!this.deleted) {
641
+ const parent = /** @type {YType} */ (this.parent)
642
+ // adjust the length of parent
643
+ if (this.countable && this.parentSub === null) {
644
+ parent._length -= this.length
645
+ }
646
+ this.markDeleted()
647
+ addToIdSet(transaction.deleteSet, this.id.client, this.id.clock, this.length)
648
+ addChangedTypeToTransaction(transaction, parent, this.parentSub)
649
+ this.content.delete(transaction)
650
+ }
651
+ }
652
+
653
+ /**
654
+ * @param {Transaction} tr
655
+ * @param {boolean} parentGCd
656
+ */
657
+ gc (tr, parentGCd) {
658
+ if (!this.deleted) {
659
+ throw error.unexpectedCase()
660
+ }
661
+ this.content.gc(tr)
662
+ if (parentGCd) {
663
+ replaceStruct(tr, this, new GC(this.id, this.length))
664
+ } else {
665
+ this.content = new ContentDeleted(this.length)
666
+ }
667
+ }
668
+
669
+ /**
670
+ * Transform the properties of this type to binary and write it to an
671
+ * BinaryEncoder.
672
+ *
673
+ * This is called when this Item is sent to a remote peer.
674
+ *
675
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
676
+ * @param {number} offset
677
+ * @param {number} offsetEnd
678
+ */
679
+ write (encoder, offset, offsetEnd) {
680
+ const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin
681
+ const rightOrigin = this.rightOrigin
682
+ const parentSub = this.parentSub
683
+ const info = (this.content.getRef() & binary.BITS5) |
684
+ (origin === null ? 0 : binary.BIT8) | // origin is defined
685
+ (rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined
686
+ (parentSub === null ? 0 : binary.BIT6) // parentSub is non-null
687
+ encoder.writeInfo(info)
688
+ if (origin !== null) {
689
+ encoder.writeLeftID(origin)
690
+ }
691
+ if (rightOrigin !== null) {
692
+ encoder.writeRightID(rightOrigin)
693
+ }
694
+ if (origin === null && rightOrigin === null) {
695
+ const parent = /** @type {YType<any>} */ (this.parent)
696
+ if (parent._item !== undefined) {
697
+ const parentItem = parent._item
698
+ if (parentItem === null) {
699
+ // parent type on y._map
700
+ // find the correct key
701
+ const ykey = findRootTypeKey(parent)
702
+ encoder.writeParentInfo(true) // write parentYKey
703
+ encoder.writeString(ykey)
704
+ } else {
705
+ encoder.writeParentInfo(false) // write parent id
706
+ encoder.writeLeftID(parentItem.id)
707
+ }
708
+ } else if (parent.constructor === String) { // this edge case was added by differential updates
709
+ encoder.writeParentInfo(true) // write parentYKey
710
+ encoder.writeString(parent)
711
+ } else if (parent.constructor === ID) {
712
+ encoder.writeParentInfo(false) // write parent id
713
+ encoder.writeLeftID(parent)
714
+ } else {
715
+ error.unexpectedCase()
716
+ }
717
+ if (parentSub !== null) {
718
+ encoder.writeString(parentSub)
719
+ }
720
+ }
721
+ this.content.write(encoder, offset, offsetEnd)
722
+ }
723
+ }
724
+
725
+ /**
726
+ * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
727
+ * @param {number} info
728
+ */
729
+ export const readItemContent = (decoder, info) => contentRefs[info & binary.BITS5](decoder)
730
+
731
+ /**
732
+ * A lookup map for reading Item content.
733
+ *
734
+ * @type {Array<function(UpdateDecoderV1 | UpdateDecoderV2):AbstractContent>}
735
+ */
736
+ export const contentRefs = [
737
+ () => { error.unexpectedCase() }, // GC is not ItemContent
738
+ readContentDeleted, // 1
739
+ readContentJSON, // 2
740
+ readContentBinary, // 3
741
+ readContentString, // 4
742
+ readContentEmbed, // 5
743
+ readContentFormat, // 6
744
+ readContentType, // 7
745
+ readContentAny, // 8
746
+ readContentDoc, // 9
747
+ () => { error.unexpectedCase() } // 10 - Skip is not ItemContent
748
+ ]
749
+
750
+ /**
751
+ * Do not implement this class!
752
+ */
753
+ export class AbstractContent {
754
+ /**
755
+ * @return {number}
756
+ */
757
+ getLength () {
758
+ throw error.methodUnimplemented()
759
+ }
760
+
761
+ /**
762
+ * @return {Array<any>}
763
+ */
764
+ getContent () {
765
+ throw error.methodUnimplemented()
766
+ }
767
+
768
+ /**
769
+ * Should return false if this Item is some kind of meta information
770
+ * (e.g. format information).
771
+ *
772
+ * * Whether this Item should be addressable via `yarray.get(i)`
773
+ * * Whether this Item should be counted when computing yarray.length
774
+ *
775
+ * @return {boolean}
776
+ */
777
+ isCountable () {
778
+ throw error.methodUnimplemented()
779
+ }
780
+
781
+ /**
782
+ * @return {AbstractContent}
783
+ */
784
+ copy () {
785
+ throw error.methodUnimplemented()
786
+ }
787
+
788
+ /**
789
+ * @param {number} _offset
790
+ * @return {AbstractContent}
791
+ */
792
+ splice (_offset) {
793
+ throw error.methodUnimplemented()
794
+ }
795
+
796
+ /**
797
+ * @param {AbstractContent} _right
798
+ * @return {boolean}
799
+ */
800
+ mergeWith (_right) {
801
+ throw error.methodUnimplemented()
802
+ }
803
+
804
+ /**
805
+ * @param {Transaction} _transaction
806
+ * @param {Item} _item
807
+ */
808
+ integrate (_transaction, _item) {
809
+ throw error.methodUnimplemented()
810
+ }
811
+
812
+ /**
813
+ * @param {Transaction} _transaction
814
+ */
815
+ delete (_transaction) {
816
+ throw error.methodUnimplemented()
817
+ }
818
+
819
+ /**
820
+ * @param {Transaction} _transaction
821
+ */
822
+ gc (_transaction) {
823
+ throw error.methodUnimplemented()
824
+ }
825
+
826
+ /**
827
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} _encoder
828
+ * @param {number} _offset
829
+ * @param {number} _offsetEnd
830
+ */
831
+ write (_encoder, _offset, _offsetEnd) {
832
+ throw error.methodUnimplemented()
833
+ }
834
+
835
+ /**
836
+ * @return {number}
837
+ */
838
+ getRef () {
839
+ throw error.methodUnimplemented()
840
+ }
841
+ }