@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,623 @@
1
+ /**
2
+ * @module encoding
3
+ */
4
+ /*
5
+ * We use the first five bits in the info flag for determining the type of the struct.
6
+ *
7
+ * 0: GC
8
+ * 1: Item with Deleted content
9
+ * 2: Item with JSON content
10
+ * 3: Item with Binary content
11
+ * 4: Item with String content
12
+ * 5: Item with Embed content (for richtext content)
13
+ * 6: Item with Format content (a formatting marker for richtext content)
14
+ * 7: Item with Type
15
+ */
16
+
17
+ import {
18
+ findIndexSS,
19
+ getState,
20
+ getStateVector,
21
+ readAndApplyDeleteSet,
22
+ writeIdSet,
23
+ transact,
24
+ UpdateDecoderV1,
25
+ UpdateDecoderV2,
26
+ UpdateEncoderV1,
27
+ UpdateEncoderV2,
28
+ IdSetEncoderV2,
29
+ IdSetDecoderV1,
30
+ IdSetEncoderV1,
31
+ mergeUpdates,
32
+ mergeUpdatesV2,
33
+ Skip,
34
+ diffUpdateV2,
35
+ convertUpdateFormatV2ToV1,
36
+ readStructSet,
37
+ removeRangesFromStructSet,
38
+ createIdSet,
39
+ StructSet, IdSet, IdSetDecoderV2, Doc, Transaction, GC, Item, StructStore, // eslint-disable-line
40
+ createID,
41
+ IdRange
42
+ } from '../internals.js'
43
+
44
+ import * as encoding from 'lib0/encoding'
45
+ import * as decoding from 'lib0/decoding'
46
+ import * as map from 'lib0/map'
47
+ import * as math from 'lib0/math'
48
+ import * as array from 'lib0/array'
49
+
50
+ /**
51
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
52
+ * @param {Array<GC|Item>} structs All structs by `client`
53
+ * @param {number} client
54
+ * @param {Array<IdRange>} idranges
55
+ *
56
+ * @function
57
+ */
58
+ const writeStructs = (encoder, structs, client, idranges) => {
59
+ let structsToWrite = 0 // this accounts for the skips
60
+ /**
61
+ * @type {Array<{ start: number, end: number, startClock: number, endClock: number }>}
62
+ */
63
+ const indexRanges = []
64
+ const firstPossibleClock = structs[0].id.clock
65
+ const lastStruct = array.last(structs)
66
+ const lastPossibleClock = lastStruct.id.clock + lastStruct.length
67
+ idranges.forEach(idrange => {
68
+ const startClock = math.max(idrange.clock, firstPossibleClock)
69
+ const endClock = math.min(idrange.clock + idrange.len, lastPossibleClock)
70
+ if (startClock >= endClock) return // structs for this range do not exist
71
+ // inclusive start
72
+ const start = findIndexSS(structs, startClock)
73
+ // exclusive end
74
+ const end = findIndexSS(structs, endClock - 1) + 1
75
+ structsToWrite += end - start
76
+ indexRanges.push({
77
+ start,
78
+ end,
79
+ startClock,
80
+ endClock
81
+ })
82
+ })
83
+ structsToWrite += idranges.length - 1
84
+ // start writing with this clock. this is updated to the next clock that we expect to write
85
+ let clock = indexRanges[0].startClock
86
+ // write # encoded structs
87
+ encoding.writeVarUint(encoder.restEncoder, structsToWrite)
88
+ encoder.writeClient(client)
89
+ // write clock
90
+ encoding.writeVarUint(encoder.restEncoder, clock)
91
+ indexRanges.forEach(indexRange => {
92
+ const skipLen = indexRange.startClock - clock
93
+ if (skipLen > 0) {
94
+ new Skip(createID(client, clock), skipLen).write(encoder, 0)
95
+ clock += skipLen
96
+ }
97
+ for (let i = indexRange.start; i < indexRange.end; i++) {
98
+ const struct = structs[i]
99
+ const structEnd = struct.id.clock + struct.length
100
+ const offsetEnd = math.max(structEnd - indexRange.endClock, 0)
101
+ struct.write(encoder, clock - struct.id.clock, offsetEnd)
102
+ clock = structEnd - offsetEnd
103
+ }
104
+ })
105
+ }
106
+
107
+ /**
108
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
109
+ * @param {StructStore} store
110
+ * @param {Map<number,number>} _sm
111
+ *
112
+ * @private
113
+ * @function
114
+ */
115
+ export const writeClientsStructs = (encoder, store, _sm) => {
116
+ // we filter all valid _sm entries into sm
117
+ const sm = new Map()
118
+ _sm.forEach((clock, client) => {
119
+ // only write if new structs are available
120
+ if (getState(store, client) > clock) {
121
+ sm.set(client, clock)
122
+ }
123
+ })
124
+ getStateVector(store).forEach((_clock, client) => {
125
+ if (!_sm.has(client)) {
126
+ sm.set(client, 0)
127
+ }
128
+ })
129
+ // write # states that were updated
130
+ encoding.writeVarUint(encoder.restEncoder, sm.size)
131
+ // Write items with higher client ids first
132
+ // This heavily improves the conflict algorithm.
133
+ array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
134
+ const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
135
+ const lastStruct = structs[structs.length - 1]
136
+ writeStructs(encoder, structs, client, [new IdRange(clock, lastStruct.id.clock + lastStruct.length - clock)])
137
+ })
138
+ }
139
+
140
+ /**
141
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
142
+ * @param {StructStore} store
143
+ * @param {IdSet} idset
144
+ *
145
+ * @todo at the moment this writes the full deleteset range
146
+ *
147
+ * @private
148
+ * @function
149
+ */
150
+ export const writeStructsFromIdSet = (encoder, store, idset) => {
151
+ // write # states that were updated
152
+ encoding.writeVarUint(encoder.restEncoder, idset.clients.size)
153
+ // Write items with higher client ids first
154
+ // This heavily improves the conflict algorithm.
155
+ array.from(idset.clients.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, ids]) => {
156
+ const idRanges = ids.getIds()
157
+ const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
158
+ writeStructs(encoder, structs, client, idRanges)
159
+ })
160
+ }
161
+
162
+ /**
163
+ * Resume computing structs generated by struct readers.
164
+ *
165
+ * While there is something to do, we integrate structs in this order
166
+ * 1. top element on stack, if stack is not empty
167
+ * 2. next element from current struct reader (if empty, use next struct reader)
168
+ *
169
+ * If struct causally depends on another struct (ref.missing), we put next reader of
170
+ * `ref.id.client` on top of stack.
171
+ *
172
+ * At some point we find a struct that has no causal dependencies,
173
+ * then we start emptying the stack.
174
+ *
175
+ * It is not possible to have circles: i.e. struct1 (from client1) depends on struct2 (from client2)
176
+ * depends on struct3 (from client1). Therefore the max stack size is equal to `structReaders.length`.
177
+ *
178
+ * This method is implemented in a way so that we can resume computation if this update
179
+ * causally depends on another update.
180
+ *
181
+ * @param {Transaction} transaction
182
+ * @param {StructStore} store
183
+ * @param {StructSet} clientsStructRefs
184
+ * @return { null | { update: Uint8Array<ArrayBuffer>, missing: Map<number,number> } }
185
+ *
186
+ * @private
187
+ * @function
188
+ */
189
+ const integrateStructs = (transaction, store, clientsStructRefs) => {
190
+ /**
191
+ * @type {Array<Item | GC>}
192
+ */
193
+ const stack = []
194
+ // sort them so that we take the higher id first, in case of conflicts the lower id will probably not conflict with the id from the higher user.
195
+ let clientsStructRefsIds = array.from(clientsStructRefs.clients.keys()).sort((a, b) => a - b)
196
+ if (clientsStructRefsIds.length === 0) {
197
+ return null
198
+ }
199
+ const getNextStructTarget = () => {
200
+ if (clientsStructRefsIds.length === 0) {
201
+ return null
202
+ }
203
+ let nextStructsTarget = /** @type {{i:number,refs:Array<GC|Item>}} */ (clientsStructRefs.clients.get(clientsStructRefsIds[clientsStructRefsIds.length - 1]))
204
+ while (nextStructsTarget.refs.length === nextStructsTarget.i) {
205
+ clientsStructRefsIds.pop()
206
+ if (clientsStructRefsIds.length > 0) {
207
+ nextStructsTarget = /** @type {{i:number,refs:Array<GC|Item>}} */ (clientsStructRefs.clients.get(clientsStructRefsIds[clientsStructRefsIds.length - 1]))
208
+ } else {
209
+ return null
210
+ }
211
+ }
212
+ return nextStructsTarget
213
+ }
214
+ let curStructsTarget = getNextStructTarget()
215
+ if (curStructsTarget === null) {
216
+ return null
217
+ }
218
+
219
+ /**
220
+ * @type {StructStore}
221
+ */
222
+ const restStructs = new StructStore()
223
+ const missingSV = new Map()
224
+ /**
225
+ * @param {number} client
226
+ * @param {number} clock
227
+ */
228
+ const updateMissingSv = (client, clock) => {
229
+ const mclock = missingSV.get(client)
230
+ if (mclock == null || mclock > clock) {
231
+ missingSV.set(client, clock)
232
+ }
233
+ }
234
+ /**
235
+ * @type {GC|Item}
236
+ */
237
+ let stackHead = /** @type {any} */ (curStructsTarget).refs[/** @type {any} */ (curStructsTarget).i++]
238
+ // caching the state because it is used very often
239
+ const state = new Map()
240
+
241
+ // // caching the state because it is used very often
242
+ // const currentInsertSet = createIdSet()
243
+ // clientsStructRefsIds.forEach(clientId => {
244
+ // currentInsertSet.clients.set(clientid, new IdRanges(_createInsertSliceFromStructs(store.clients.get(clientId) ?? [], false)))
245
+ // })
246
+
247
+ const addStackToRestSS = () => {
248
+ for (const item of stack) {
249
+ const client = item.id.client
250
+ const inapplicableItems = clientsStructRefs.clients.get(client)
251
+ if (inapplicableItems) {
252
+ // decrement because we weren't able to apply previous operation
253
+ inapplicableItems.i--
254
+ restStructs.clients.set(client, inapplicableItems.refs.slice(inapplicableItems.i))
255
+ clientsStructRefs.clients.delete(client)
256
+ inapplicableItems.i = 0
257
+ inapplicableItems.refs = []
258
+ } else {
259
+ // item was the last item on clientsStructRefs and the field was already cleared. Add item to restStructs and continue
260
+ restStructs.clients.set(client, [item])
261
+ }
262
+ // remove client from clientsStructRefsIds to prevent users from applying the same update again
263
+ clientsStructRefsIds = clientsStructRefsIds.filter(c => c !== client)
264
+ }
265
+ stack.length = 0
266
+ }
267
+
268
+ // iterate over all struct readers until we are done
269
+ while (true) {
270
+ if (stackHead.constructor !== Skip) {
271
+ const localClock = map.setIfUndefined(state, stackHead.id.client, () => getState(store, stackHead.id.client))
272
+ const offset = localClock - stackHead.id.clock
273
+ const missing = stackHead.getMissing(transaction, store)
274
+ if (missing !== null) {
275
+ stack.push(stackHead)
276
+ // get the struct reader that has the missing struct
277
+ /**
278
+ * @type {{ refs: Array<GC|Item>, i: number }}
279
+ */
280
+ const structRefs = clientsStructRefs.clients.get(/** @type {number} */ (missing)) || { refs: [], i: 0 }
281
+ if (structRefs.refs.length === structRefs.i || missing === stackHead.id.client || stack.some(s => s.id.client === missing)) { // @todo this could be optimized!
282
+ // This update message causally depends on another update message that doesn't exist yet
283
+ updateMissingSv(/** @type {number} */ (missing), getState(store, missing))
284
+ addStackToRestSS()
285
+ } else {
286
+ stackHead = structRefs.refs[structRefs.i++]
287
+ continue
288
+ }
289
+ } else {
290
+ // all fine, apply the stackhead
291
+ // but first add a skip to structs if necessary
292
+ if (offset < 0) {
293
+ const skip = new Skip(createID(stackHead.id.client, localClock), -offset)
294
+ skip.integrate(transaction, 0)
295
+ }
296
+ stackHead.integrate(transaction, 0)
297
+ state.set(stackHead.id.client, math.max(stackHead.id.clock + stackHead.length, localClock))
298
+ }
299
+ }
300
+ // iterate to next stackHead
301
+ if (stack.length > 0) {
302
+ stackHead = /** @type {GC|Item} */ (stack.pop())
303
+ } else if (curStructsTarget !== null && curStructsTarget.i < curStructsTarget.refs.length) {
304
+ stackHead = /** @type {GC|Item} */ (curStructsTarget.refs[curStructsTarget.i++])
305
+ } else {
306
+ curStructsTarget = getNextStructTarget()
307
+ if (curStructsTarget === null) {
308
+ // we are done!
309
+ break
310
+ } else {
311
+ stackHead = /** @type {GC|Item} */ (curStructsTarget.refs[curStructsTarget.i++])
312
+ }
313
+ }
314
+ }
315
+ if (restStructs.clients.size > 0) {
316
+ const encoder = new UpdateEncoderV2()
317
+ writeClientsStructs(encoder, restStructs, new Map())
318
+ // write empty deleteset
319
+ // writeDeleteSet(encoder, new DeleteSet())
320
+ encoding.writeVarUint(encoder.restEncoder, 0) // => no need for an extra function call, just write 0 deletes
321
+ return { missing: missingSV, update: encoder.toUint8Array() }
322
+ }
323
+ return null
324
+ }
325
+
326
+ /**
327
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
328
+ * @param {Transaction} transaction
329
+ *
330
+ * @private
331
+ * @function
332
+ */
333
+ export const writeStructsFromTransaction = (encoder, transaction) => writeStructsFromIdSet(encoder, transaction.doc.store, transaction.insertSet)
334
+
335
+ /**
336
+ * Read and apply a document update.
337
+ *
338
+ * This function has the same effect as `applyUpdate` but accepts a decoder.
339
+ *
340
+ * @param {decoding.Decoder} decoder
341
+ * @param {Doc} ydoc
342
+ * @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
343
+ * @param {UpdateDecoderV1 | UpdateDecoderV2} [structDecoder]
344
+ *
345
+ * @function
346
+ */
347
+ export const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = new UpdateDecoderV2(decoder)) =>
348
+ transact(ydoc, transaction => {
349
+ // force that transaction.local is set to non-local
350
+ transaction.local = false
351
+ let retry = false
352
+ const doc = transaction.doc
353
+ const store = doc.store
354
+ // let start = performance.now()
355
+ const ss = readStructSet(structDecoder, doc)
356
+ const knownState = createIdSet()
357
+ ss.clients.forEach((_, client) => {
358
+ const storeStructs = store.clients.get(client)
359
+ if (storeStructs) {
360
+ const last = storeStructs[storeStructs.length - 1]
361
+ knownState.add(client, 0, last.id.clock + last.length)
362
+ // remove known items from ss
363
+ store.skips.clients.get(client)?.getIds().forEach(idrange => {
364
+ knownState.delete(client, idrange.clock, idrange.len)
365
+ })
366
+ }
367
+ })
368
+ // remove known items from ss
369
+ removeRangesFromStructSet(ss, knownState)
370
+ // console.log('time to read structs: ', performance.now() - start) // @todo remove
371
+ // start = performance.now()
372
+ // console.log('time to merge: ', performance.now() - start) // @todo remove
373
+ // start = performance.now()
374
+ const restStructs = integrateStructs(transaction, store, ss)
375
+ const pending = store.pendingStructs
376
+ if (pending) {
377
+ // check if we can apply something
378
+ for (const [client, clock] of pending.missing) {
379
+ if (ss.clients.has(client) || clock < getState(store, client)) {
380
+ retry = true
381
+ break
382
+ }
383
+ }
384
+ if (restStructs) {
385
+ // merge restStructs into store.pending
386
+ for (const [client, clock] of restStructs.missing) {
387
+ const mclock = pending.missing.get(client)
388
+ if (mclock == null || mclock > clock) {
389
+ pending.missing.set(client, clock)
390
+ }
391
+ }
392
+ pending.update = mergeUpdatesV2([pending.update, restStructs.update])
393
+ }
394
+ } else {
395
+ store.pendingStructs = restStructs
396
+ }
397
+ // console.log('time to integrate: ', performance.now() - start) // @todo remove
398
+ // start = performance.now()
399
+ const dsRest = readAndApplyDeleteSet(structDecoder, transaction, store)
400
+ if (store.pendingDs) {
401
+ // @todo we could make a lower-bound state-vector check as we do above
402
+ const pendingDSUpdate = new UpdateDecoderV2(decoding.createDecoder(store.pendingDs))
403
+ decoding.readVarUint(pendingDSUpdate.restDecoder) // read 0 structs, because we only encode deletes in pendingdsupdate
404
+ const dsRest2 = readAndApplyDeleteSet(pendingDSUpdate, transaction, store)
405
+ if (dsRest && dsRest2) {
406
+ // case 1: ds1 != null && ds2 != null
407
+ store.pendingDs = mergeUpdatesV2([dsRest, dsRest2])
408
+ } else {
409
+ // case 2: ds1 != null
410
+ // case 3: ds2 != null
411
+ // case 4: ds1 == null && ds2 == null
412
+ store.pendingDs = dsRest || dsRest2
413
+ }
414
+ } else {
415
+ // Either dsRest == null && pendingDs == null OR dsRest != null
416
+ store.pendingDs = dsRest
417
+ }
418
+ // console.log('time to cleanup: ', performance.now() - start) // @todo remove
419
+ // start = performance.now()
420
+
421
+ // console.log('time to resume delete readers: ', performance.now() - start) // @todo remove
422
+ // start = performance.now()
423
+ if (retry) {
424
+ const update = /** @type {{update: Uint8Array}} */ (store.pendingStructs).update
425
+ store.pendingStructs = null
426
+ applyUpdateV2(transaction.doc, update)
427
+ }
428
+ }, transactionOrigin, false)
429
+
430
+ /**
431
+ * Read and apply a document update.
432
+ *
433
+ * This function has the same effect as `applyUpdate` but accepts a decoder.
434
+ *
435
+ * @param {decoding.Decoder} decoder
436
+ * @param {Doc} ydoc
437
+ * @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
438
+ *
439
+ * @function
440
+ */
441
+ export const readUpdate = (decoder, ydoc, transactionOrigin) => readUpdateV2(decoder, ydoc, transactionOrigin, new UpdateDecoderV1(decoder))
442
+
443
+ /**
444
+ * Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
445
+ *
446
+ * This function has the same effect as `readUpdate` but accepts an Uint8Array instead of a Decoder.
447
+ *
448
+ * @param {Doc} ydoc
449
+ * @param {Uint8Array} update
450
+ * @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
451
+ * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
452
+ *
453
+ * @function
454
+ */
455
+ export const applyUpdateV2 = (ydoc, update, transactionOrigin, YDecoder = UpdateDecoderV2) => {
456
+ const decoder = decoding.createDecoder(update)
457
+ readUpdateV2(decoder, ydoc, transactionOrigin, new YDecoder(decoder))
458
+ }
459
+
460
+ /**
461
+ * Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
462
+ *
463
+ * This function has the same effect as `readUpdate` but accepts an Uint8Array instead of a Decoder.
464
+ *
465
+ * @param {Doc} ydoc
466
+ * @param {Uint8Array} update
467
+ * @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
468
+ *
469
+ * @function
470
+ */
471
+ export const applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(ydoc, update, transactionOrigin, UpdateDecoderV1)
472
+
473
+ /**
474
+ * Write all the document as a single update message. If you specify the state of the remote client (`targetStateVector`) it will
475
+ * only write the operations that are missing.
476
+ *
477
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
478
+ * @param {Doc} doc
479
+ * @param {Map<number,number>} [targetStateVector] The state of the target that receives the update. Leave empty to write all known structs
480
+ *
481
+ * @function
482
+ */
483
+ export const writeStateAsUpdate = (encoder, doc, targetStateVector = new Map()) => {
484
+ writeClientsStructs(encoder, doc.store, targetStateVector)
485
+ writeIdSet(encoder, doc.store.ds)
486
+ }
487
+
488
+ /**
489
+ * Write all the document as a single update message that can be applied on the remote document. If you specify the state of the remote client (`targetState`) it will
490
+ * only write the operations that are missing.
491
+ *
492
+ * Use `writeStateAsUpdate` instead if you are working with lib0/encoding.js#Encoder
493
+ *
494
+ * @param {Doc} doc
495
+ * @param {Uint8Array} [encodedTargetStateVector] The state of the target that receives the update. Leave empty to write all known structs
496
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} [encoder]
497
+ * @return {Uint8Array<ArrayBuffer>}
498
+ *
499
+ * @function
500
+ */
501
+ export const encodeStateAsUpdateV2 = (doc, encodedTargetStateVector = new Uint8Array([0]), encoder = new UpdateEncoderV2()) => {
502
+ const targetStateVector = decodeStateVector(encodedTargetStateVector)
503
+ writeStateAsUpdate(encoder, doc, targetStateVector)
504
+ const updates = [encoder.toUint8Array()]
505
+ // also add the pending updates (if there are any)
506
+ if (doc.store.pendingDs) {
507
+ updates.push(doc.store.pendingDs)
508
+ }
509
+ if (doc.store.pendingStructs) {
510
+ updates.push(diffUpdateV2(doc.store.pendingStructs.update, encodedTargetStateVector))
511
+ }
512
+ if (updates.length > 1) {
513
+ if (encoder.constructor === UpdateEncoderV1) {
514
+ return mergeUpdates(updates.map((update, i) => i === 0 ? update : convertUpdateFormatV2ToV1(update)))
515
+ } else if (encoder.constructor === UpdateEncoderV2) {
516
+ return mergeUpdatesV2(updates)
517
+ }
518
+ }
519
+ return updates[0]
520
+ }
521
+
522
+ /**
523
+ * Write all the document as a single update message that can be applied on the remote document. If you specify the state of the remote client (`targetState`) it will
524
+ * only write the operations that are missing.
525
+ *
526
+ * Use `writeStateAsUpdate` instead if you are working with lib0/encoding.js#Encoder
527
+ *
528
+ * @param {Doc} doc
529
+ * @param {Uint8Array} [encodedTargetStateVector] The state of the target that receives the update. Leave empty to write all known structs
530
+ * @return {Uint8Array<ArrayBuffer>}
531
+ *
532
+ * @function
533
+ */
534
+ export const encodeStateAsUpdate = (doc, encodedTargetStateVector) => encodeStateAsUpdateV2(doc, encodedTargetStateVector, new UpdateEncoderV1())
535
+
536
+ /**
537
+ * Read state vector from Decoder and return as Map
538
+ *
539
+ * @param {IdSetDecoderV1 | IdSetDecoderV2} decoder
540
+ * @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
541
+ *
542
+ * @function
543
+ */
544
+ export const readStateVector = decoder => {
545
+ const ss = new Map()
546
+ const ssLength = decoding.readVarUint(decoder.restDecoder)
547
+ for (let i = 0; i < ssLength; i++) {
548
+ const client = decoding.readVarUint(decoder.restDecoder)
549
+ const clock = decoding.readVarUint(decoder.restDecoder)
550
+ ss.set(client, clock)
551
+ }
552
+ return ss
553
+ }
554
+
555
+ /**
556
+ * Read decodedState and return State as Map.
557
+ *
558
+ * @param {Uint8Array} decodedState
559
+ * @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
560
+ *
561
+ * @function
562
+ */
563
+ // export const decodeStateVectorV2 = decodedState => readStateVector(new DSDecoderV2(decoding.createDecoder(decodedState)))
564
+
565
+ /**
566
+ * Read decodedState and return State as Map.
567
+ *
568
+ * @param {Uint8Array} decodedState
569
+ * @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
570
+ *
571
+ * @function
572
+ */
573
+ export const decodeStateVector = decodedState => readStateVector(new IdSetDecoderV1(decoding.createDecoder(decodedState)))
574
+
575
+ /**
576
+ * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder
577
+ * @param {Map<number,number>} sv
578
+ * @function
579
+ */
580
+ export const writeStateVector = (encoder, sv) => {
581
+ encoding.writeVarUint(encoder.restEncoder, sv.size)
582
+ array.from(sv.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
583
+ encoding.writeVarUint(encoder.restEncoder, client) // @todo use a special client decoder that is based on mapping
584
+ encoding.writeVarUint(encoder.restEncoder, clock)
585
+ })
586
+ return encoder
587
+ }
588
+
589
+ /**
590
+ * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder
591
+ * @param {Doc} doc
592
+ *
593
+ * @function
594
+ */
595
+ export const writeDocumentStateVector = (encoder, doc) => writeStateVector(encoder, getStateVector(doc.store))
596
+
597
+ /**
598
+ * Encode State as Uint8Array.
599
+ *
600
+ * @param {Doc|Map<number,number>} doc
601
+ * @param {IdSetEncoderV1 | IdSetEncoderV2} [encoder]
602
+ * @return {Uint8Array}
603
+ *
604
+ * @function
605
+ */
606
+ export const encodeStateVectorV2 = (doc, encoder = new IdSetEncoderV2()) => {
607
+ if (doc instanceof Map) {
608
+ writeStateVector(encoder, doc)
609
+ } else {
610
+ writeDocumentStateVector(encoder, doc)
611
+ }
612
+ return encoder.toUint8Array()
613
+ }
614
+
615
+ /**
616
+ * Encode State as Uint8Array.
617
+ *
618
+ * @param {Doc|Map<number,number>} doc
619
+ * @return {Uint8Array}
620
+ *
621
+ * @function
622
+ */
623
+ export const encodeStateVector = doc => encodeStateVectorV2(doc, new IdSetEncoderV1())
@@ -0,0 +1,21 @@
1
+ import { Item } from '../internals.js' // eslint-disable-line
2
+
3
+ /**
4
+ * Check if `parent` is a parent of `child`.
5
+ *
6
+ * @param {import('../ytype.js').YType} parent
7
+ * @param {Item|null} child
8
+ * @return {Boolean} Whether `parent` is a parent of `child`.
9
+ *
10
+ * @private
11
+ * @function
12
+ */
13
+ export const isParentOf = (parent, child) => {
14
+ while (child !== null) {
15
+ if (child.parent === parent) {
16
+ return true
17
+ }
18
+ child = /** @type {import('../ytype.js').YType} */ (child.parent)._item
19
+ }
20
+ return false
21
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ YType // eslint-disable-line
3
+ } from '../internals.js'
4
+
5
+ /**
6
+ * Convenient helper to log type information.
7
+ *
8
+ * Do not use in productive systems as the output can be immense!
9
+ *
10
+ * @param {YType<any>} type
11
+ */
12
+ export const logType = type => {
13
+ const res = []
14
+ let n = type._start
15
+ while (n) {
16
+ res.push(n)
17
+ n = n.right
18
+ }
19
+ console.log('Children: ', res)
20
+ console.log('Children content: ', res.filter(m => !m.deleted).map(m => m.content))
21
+ }