@y/y 14.0.0-19 → 14.0.0-20

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 (129) 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 +19 -16
  15. package/dist/src/utils/IdMap.d.ts.map +1 -1
  16. package/dist/src/utils/IdSet.d.ts +2 -2
  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/Transaction.d.ts +9 -5
  21. package/dist/src/utils/Transaction.d.ts.map +1 -1
  22. package/dist/src/utils/UndoManager.d.ts +14 -12
  23. package/dist/src/utils/UndoManager.d.ts.map +1 -1
  24. package/dist/src/utils/UpdateEncoder.d.ts +2 -0
  25. package/dist/src/utils/UpdateEncoder.d.ts.map +1 -1
  26. package/dist/src/utils/YEvent.d.ts +21 -42
  27. package/dist/src/utils/YEvent.d.ts.map +1 -1
  28. package/dist/src/utils/isParentOf.d.ts +1 -1
  29. package/dist/src/utils/isParentOf.d.ts.map +1 -1
  30. package/dist/src/utils/logging.d.ts +2 -2
  31. package/dist/src/utils/logging.d.ts.map +1 -1
  32. package/dist/src/utils/meta.d.ts +43 -0
  33. package/dist/src/utils/meta.d.ts.map +1 -0
  34. package/dist/src/utils/ts.d.ts +4 -0
  35. package/dist/src/utils/ts.d.ts.map +1 -0
  36. package/dist/src/utils/updates.d.ts +3 -9
  37. package/dist/src/utils/updates.d.ts.map +1 -1
  38. package/dist/src/ytype.d.ts +498 -0
  39. package/dist/src/ytype.d.ts.map +1 -0
  40. package/dist/tests/IdMap.tests.d.ts.map +1 -1
  41. package/dist/tests/attribution.tests.d.ts +1 -0
  42. package/dist/tests/attribution.tests.d.ts.map +1 -1
  43. package/dist/tests/compatibility.tests.d.ts.map +1 -1
  44. package/dist/tests/doc.tests.d.ts.map +1 -1
  45. package/dist/tests/relativePositions.tests.d.ts +9 -9
  46. package/dist/tests/relativePositions.tests.d.ts.map +1 -1
  47. package/dist/tests/snapshot.tests.d.ts.map +1 -1
  48. package/dist/tests/testHelper.d.ts +28 -27
  49. package/dist/tests/testHelper.d.ts.map +1 -1
  50. package/dist/tests/undo-redo.tests.d.ts.map +1 -1
  51. package/dist/tests/updates.tests.d.ts +1 -1
  52. package/dist/tests/updates.tests.d.ts.map +1 -1
  53. package/dist/tests/y-array.tests.d.ts +0 -2
  54. package/dist/tests/y-array.tests.d.ts.map +1 -1
  55. package/dist/tests/y-map.tests.d.ts +0 -3
  56. package/dist/tests/y-map.tests.d.ts.map +1 -1
  57. package/dist/tests/y-text.tests.d.ts +1 -1
  58. package/dist/tests/y-text.tests.d.ts.map +1 -1
  59. package/dist/tests/y-xml.tests.d.ts +0 -1
  60. package/dist/tests/y-xml.tests.d.ts.map +1 -1
  61. package/package.json +16 -16
  62. package/src/index.js +152 -0
  63. package/src/internals.js +35 -0
  64. package/src/structs/AbstractStruct.js +59 -0
  65. package/src/structs/ContentAny.js +115 -0
  66. package/src/structs/ContentBinary.js +93 -0
  67. package/src/structs/ContentDeleted.js +101 -0
  68. package/src/structs/ContentDoc.js +141 -0
  69. package/src/structs/ContentEmbed.js +98 -0
  70. package/src/structs/ContentFormat.js +105 -0
  71. package/src/structs/ContentJSON.js +119 -0
  72. package/src/structs/ContentString.js +113 -0
  73. package/src/structs/ContentType.js +152 -0
  74. package/src/structs/GC.js +80 -0
  75. package/src/structs/Item.js +841 -0
  76. package/src/structs/Skip.js +75 -0
  77. package/src/utils/AttributionManager.js +653 -0
  78. package/src/utils/Doc.js +266 -0
  79. package/src/utils/EventHandler.js +87 -0
  80. package/src/utils/ID.js +89 -0
  81. package/src/utils/IdMap.js +673 -0
  82. package/src/utils/IdSet.js +825 -0
  83. package/src/utils/RelativePosition.js +352 -0
  84. package/src/utils/Snapshot.js +220 -0
  85. package/src/utils/StructSet.js +137 -0
  86. package/src/utils/StructStore.js +289 -0
  87. package/src/utils/Transaction.js +671 -0
  88. package/src/utils/UndoManager.js +406 -0
  89. package/src/utils/UpdateDecoder.js +281 -0
  90. package/src/utils/UpdateEncoder.js +327 -0
  91. package/src/utils/YEvent.js +189 -0
  92. package/src/utils/delta-helpers.js +54 -0
  93. package/src/utils/encoding.js +623 -0
  94. package/src/utils/isParentOf.js +21 -0
  95. package/src/utils/logging.js +21 -0
  96. package/src/utils/meta.js +97 -0
  97. package/src/utils/ts.js +3 -0
  98. package/src/utils/updates.js +711 -0
  99. package/src/ytype.js +1962 -0
  100. package/dist/Skip-CE05BUF8.js +0 -11875
  101. package/dist/Skip-CE05BUF8.js.map +0 -1
  102. package/dist/index-C21sDQ5u.js +0 -163
  103. package/dist/index-C21sDQ5u.js.map +0 -1
  104. package/dist/internals.js +0 -25
  105. package/dist/internals.js.map +0 -1
  106. package/dist/src/types/AbstractType.d.ts +0 -239
  107. package/dist/src/types/AbstractType.d.ts.map +0 -1
  108. package/dist/src/types/YArray.d.ts +0 -128
  109. package/dist/src/types/YArray.d.ts.map +0 -1
  110. package/dist/src/types/YMap.d.ts +0 -112
  111. package/dist/src/types/YMap.d.ts.map +0 -1
  112. package/dist/src/types/YText.d.ts +0 -216
  113. package/dist/src/types/YText.d.ts.map +0 -1
  114. package/dist/src/types/YXmlElement.d.ts +0 -106
  115. package/dist/src/types/YXmlElement.d.ts.map +0 -1
  116. package/dist/src/types/YXmlFragment.d.ts +0 -143
  117. package/dist/src/types/YXmlFragment.d.ts.map +0 -1
  118. package/dist/src/types/YXmlHook.d.ts +0 -32
  119. package/dist/src/types/YXmlHook.d.ts.map +0 -1
  120. package/dist/src/types/YXmlText.d.ts +0 -34
  121. package/dist/src/types/YXmlText.d.ts.map +0 -1
  122. package/dist/src/utils/AbstractConnector.d.ts +0 -20
  123. package/dist/src/utils/AbstractConnector.d.ts.map +0 -1
  124. package/dist/src/utils/types.d.ts +0 -7
  125. package/dist/src/utils/types.d.ts.map +0 -1
  126. package/dist/testHelper.js +0 -617
  127. package/dist/testHelper.js.map +0 -1
  128. package/dist/yjs.js +0 -26
  129. package/dist/yjs.js.map +0 -1
@@ -0,0 +1,711 @@
1
+ import * as binary from 'lib0/binary'
2
+ import * as decoding from 'lib0/decoding'
3
+ import * as encoding from 'lib0/encoding'
4
+ import * as error from 'lib0/error'
5
+ import * as f from 'lib0/function'
6
+ import * as logging from 'lib0/logging'
7
+ import * as map from 'lib0/map'
8
+ import * as math from 'lib0/math'
9
+ import * as string from 'lib0/string'
10
+
11
+ import {
12
+ ContentAny,
13
+ ContentBinary,
14
+ ContentDeleted,
15
+ ContentDoc,
16
+ ContentEmbed,
17
+ ContentFormat,
18
+ ContentJSON,
19
+ ContentString,
20
+ ContentType,
21
+ createID,
22
+ decodeStateVector,
23
+ IdSetEncoderV1,
24
+ IdSetEncoderV2,
25
+ GC,
26
+ Item,
27
+ mergeIdSets,
28
+ readIdSet,
29
+ readItemContent,
30
+ Skip,
31
+ UpdateDecoderV1,
32
+ UpdateDecoderV2,
33
+ UpdateEncoderV1,
34
+ UpdateEncoderV2,
35
+ writeIdSet,
36
+ createIdSet
37
+ } from '../internals.js'
38
+
39
+ /**
40
+ * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
41
+ */
42
+ function * lazyStructReaderGenerator (decoder) {
43
+ const numOfStateUpdates = decoding.readVarUint(decoder.restDecoder)
44
+ for (let i = 0; i < numOfStateUpdates; i++) {
45
+ const numberOfStructs = decoding.readVarUint(decoder.restDecoder)
46
+ const client = decoder.readClient()
47
+ let clock = decoding.readVarUint(decoder.restDecoder)
48
+ for (let i = 0; i < numberOfStructs; i++) {
49
+ const info = decoder.readInfo()
50
+ // @todo use switch instead of ifs
51
+ if (info === 10) {
52
+ const len = decoding.readVarUint(decoder.restDecoder)
53
+ yield new Skip(createID(client, clock), len)
54
+ clock += len
55
+ } else if ((binary.BITS5 & info) !== 0) {
56
+ const cantCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
57
+ // If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
58
+ // and we read the next string as parentYKey.
59
+ // It indicates how we store/retrieve parent from `y.share`
60
+ // @type {string|null}
61
+ const struct = new Item(
62
+ createID(client, clock),
63
+ null, // left
64
+ (info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null, // origin
65
+ null, // right
66
+ (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin
67
+ // @ts-ignore Force writing a string here.
68
+ cantCopyParentInfo ? (decoder.readParentInfo() ? decoder.readString() : decoder.readLeftID()) : null, // parent
69
+ cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub
70
+ readItemContent(decoder, info) // item content
71
+ )
72
+ yield struct
73
+ clock += struct.length
74
+ } else {
75
+ const len = decoder.readLen()
76
+ yield new GC(createID(client, clock), len)
77
+ clock += len
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ export class LazyStructReader {
84
+ /**
85
+ * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
86
+ * @param {boolean} filterSkips
87
+ */
88
+ constructor (decoder, filterSkips) {
89
+ this.gen = lazyStructReaderGenerator(decoder)
90
+ /**
91
+ * @type {null | Item | Skip | GC}
92
+ */
93
+ this.curr = null
94
+ this.done = false
95
+ this.filterSkips = filterSkips
96
+ this.next()
97
+ }
98
+
99
+ /**
100
+ * @return {Item | GC | Skip |null}
101
+ */
102
+ next () {
103
+ // ignore "Skip" structs
104
+ do {
105
+ this.curr = this.gen.next().value || null
106
+ } while (this.filterSkips && this.curr !== null && this.curr.constructor === Skip)
107
+ return this.curr
108
+ }
109
+ }
110
+
111
+ /**
112
+ * @param {Uint8Array} update
113
+ *
114
+ */
115
+ export const logUpdate = update => logUpdateV2(update, UpdateDecoderV1)
116
+
117
+ /**
118
+ * @param {Uint8Array} update
119
+ * @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder]
120
+ */
121
+ export const logUpdateV2 = (update, YDecoder = UpdateDecoderV2) => {
122
+ const structs = []
123
+ const updateDecoder = new YDecoder(decoding.createDecoder(update))
124
+ const lazyDecoder = new LazyStructReader(updateDecoder, false)
125
+ for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
126
+ structs.push(curr)
127
+ }
128
+ logging.print('Structs: ', structs)
129
+ const ds = readIdSet(updateDecoder)
130
+ logging.print('DeleteSet: ', ds)
131
+ }
132
+
133
+ /**
134
+ * @param {Uint8Array} update
135
+ *
136
+ */
137
+ export const decodeUpdate = (update) => decodeUpdateV2(update, UpdateDecoderV1)
138
+
139
+ /**
140
+ * @param {Uint8Array} update
141
+ * @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder]
142
+ *
143
+ */
144
+ export const decodeUpdateV2 = (update, YDecoder = UpdateDecoderV2) => {
145
+ const structs = []
146
+ const updateDecoder = new YDecoder(decoding.createDecoder(update))
147
+ const lazyDecoder = new LazyStructReader(updateDecoder, false)
148
+ for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
149
+ structs.push(curr)
150
+ }
151
+ return {
152
+ structs,
153
+ ds: readIdSet(updateDecoder)
154
+ }
155
+ }
156
+
157
+ export class LazyStructWriter {
158
+ /**
159
+ * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
160
+ */
161
+ constructor (encoder) {
162
+ this.currClient = 0
163
+ this.startClock = 0
164
+ this.written = 0
165
+ this.encoder = encoder
166
+ /**
167
+ * We want to write operations lazily, but also we need to know beforehand how many operations we want to write for each client.
168
+ *
169
+ * This kind of meta-information (#clients, #structs-per-client-written) is written to the restEncoder.
170
+ *
171
+ * We fragment the restEncoder and store a slice of it per-client until we know how many clients there are.
172
+ * When we flush (toUint8Array) we write the restEncoder using the fragments and the meta-information.
173
+ *
174
+ * @type {Array<{ written: number, restEncoder: Uint8Array }>}
175
+ */
176
+ this.clientStructs = []
177
+ }
178
+ }
179
+
180
+ /**
181
+ * @param {Array<Uint8Array<ArrayBuffer>>} updates
182
+ * @return {Uint8Array<ArrayBuffer>}
183
+ */
184
+ export const mergeUpdates = updates => mergeUpdatesV2(updates, UpdateDecoderV1, UpdateEncoderV1)
185
+
186
+ /**
187
+ * @param {Uint8Array} update
188
+ * @param {typeof IdSetEncoderV1 | typeof IdSetEncoderV2} YEncoder
189
+ * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} YDecoder
190
+ * @return {Uint8Array}
191
+ */
192
+ export const encodeStateVectorFromUpdateV2 = (update, YEncoder = IdSetEncoderV2, YDecoder = UpdateDecoderV2) => {
193
+ const encoder = new YEncoder()
194
+ const updateDecoder = new LazyStructReader(new YDecoder(decoding.createDecoder(update)), false)
195
+ let curr = updateDecoder.curr
196
+ if (curr !== null) {
197
+ let size = 0
198
+ let currClient = curr.id.client
199
+ let stopCounting = curr.id.clock !== 0 // must start at 0
200
+ let currClock = stopCounting ? 0 : curr.id.clock + curr.length
201
+ for (; curr !== null; curr = updateDecoder.next()) {
202
+ if (currClient !== curr.id.client) {
203
+ if (currClock !== 0) {
204
+ size++
205
+ // We found a new client
206
+ // write what we have to the encoder
207
+ encoding.writeVarUint(encoder.restEncoder, currClient)
208
+ encoding.writeVarUint(encoder.restEncoder, currClock)
209
+ }
210
+ currClient = curr.id.client
211
+ currClock = 0
212
+ stopCounting = curr.id.clock !== 0
213
+ }
214
+ // we ignore skips
215
+ if (curr.constructor === Skip) {
216
+ stopCounting = true
217
+ }
218
+ if (!stopCounting) {
219
+ currClock = curr.id.clock + curr.length
220
+ }
221
+ }
222
+ // write what we have
223
+ if (currClock !== 0) {
224
+ size++
225
+ encoding.writeVarUint(encoder.restEncoder, currClient)
226
+ encoding.writeVarUint(encoder.restEncoder, currClock)
227
+ }
228
+ // prepend the size of the state vector
229
+ const enc = encoding.createEncoder()
230
+ encoding.writeVarUint(enc, size)
231
+ encoding.writeBinaryEncoder(enc, encoder.restEncoder)
232
+ encoder.restEncoder = enc
233
+ return encoder.toUint8Array()
234
+ } else {
235
+ encoding.writeVarUint(encoder.restEncoder, 0)
236
+ return encoder.toUint8Array()
237
+ }
238
+ }
239
+
240
+ /**
241
+ * @param {Uint8Array} update
242
+ * @return {Uint8Array}
243
+ */
244
+ export const encodeStateVectorFromUpdate = update => encodeStateVectorFromUpdateV2(update, IdSetEncoderV1, UpdateDecoderV1)
245
+
246
+ /**
247
+ * @param {Uint8Array} update
248
+ * @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder]
249
+ * @return {import('./meta.js').ContentIds}
250
+ */
251
+ export const readUpdateToContentIdsV2 = (update, YDecoder = UpdateDecoderV2) => {
252
+ const updateDecoder = new YDecoder(decoding.createDecoder(update))
253
+ const lazyDecoder = new LazyStructReader(updateDecoder, true)
254
+ const inserts = createIdSet()
255
+ let lastClientId = -1
256
+ let lastClock = 0
257
+ let lastLen = 0
258
+ for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
259
+ const currId = curr.id
260
+ if (lastClientId === currId.client && lastClock + lastLen === currId.clock) {
261
+ // default case: extend prev entry
262
+ lastLen += curr.length
263
+ } else {
264
+ if (lastClientId >= 0) {
265
+ inserts.add(lastClientId, lastClock, lastLen)
266
+ }
267
+ lastClientId = currId.client
268
+ lastClock = currId.clock
269
+ lastLen = curr.length
270
+ }
271
+ }
272
+ if (lastClientId >= 0) {
273
+ inserts.add(lastClientId, lastClock, lastLen)
274
+ }
275
+ const deletes = readIdSet(updateDecoder)
276
+ return { inserts, deletes }
277
+ }
278
+
279
+ /**
280
+ * @param {Uint8Array} update
281
+ * @return {import('./meta.js').ContentIds}
282
+ */
283
+ export const readUpdateToContentIds = update => readUpdateToContentIdsV2(update, UpdateDecoderV1)
284
+
285
+ /**
286
+ * This method is intended to slice any kind of struct and retrieve the right part.
287
+ * It does not handle side-effects, so it should only be used by the lazy-encoder.
288
+ *
289
+ * @param {Item | GC | Skip} left
290
+ * @param {number} diff
291
+ * @return {Item | GC}
292
+ */
293
+ const sliceStruct = (left, diff) => {
294
+ if (left.constructor === GC) {
295
+ const { client, clock } = left.id
296
+ return new GC(createID(client, clock + diff), left.length - diff)
297
+ } else if (left.constructor === Skip) {
298
+ const { client, clock } = left.id
299
+ return new Skip(createID(client, clock + diff), left.length - diff)
300
+ } else {
301
+ const leftItem = /** @type {Item} */ (left)
302
+ const { client, clock } = leftItem.id
303
+ return new Item(
304
+ createID(client, clock + diff),
305
+ null,
306
+ createID(client, clock + diff - 1),
307
+ null,
308
+ leftItem.rightOrigin,
309
+ leftItem.parent,
310
+ leftItem.parentSub,
311
+ leftItem.content.splice(diff)
312
+ )
313
+ }
314
+ }
315
+
316
+ /**
317
+ *
318
+ * This function works similarly to `readUpdateV2`.
319
+ *
320
+ * @param {Array<Uint8Array<ArrayBuffer>>} updates
321
+ * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
322
+ * @param {typeof UpdateEncoderV1 | typeof UpdateEncoderV2} [YEncoder]
323
+ * @return {Uint8Array<ArrayBuffer>}
324
+ */
325
+ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = UpdateEncoderV2) => {
326
+ if (updates.length === 1) {
327
+ return updates[0]
328
+ }
329
+ const updateDecoders = updates.map(update => new YDecoder(decoding.createDecoder(update)))
330
+ let lazyStructDecoders = updateDecoders.map(decoder => new LazyStructReader(decoder, true))
331
+
332
+ /**
333
+ * @todo we don't need offset because we always slice before
334
+ * @type {null | { struct: Item | GC | Skip, offset: number }}
335
+ */
336
+ let currWrite = null
337
+
338
+ const updateEncoder = new YEncoder()
339
+ // write structs lazily
340
+ const lazyStructEncoder = new LazyStructWriter(updateEncoder)
341
+
342
+ // Note: We need to ensure that all lazyStructDecoders are fully consumed
343
+ // Note: Should merge document updates whenever possible - even from different updates
344
+ // Note: Should handle that some operations cannot be applied yet ()
345
+
346
+ while (true) {
347
+ // Write higher clients first ⇒ sort by clientID & clock and remove decoders without content
348
+ lazyStructDecoders = lazyStructDecoders.filter(dec => dec.curr !== null)
349
+ lazyStructDecoders.sort(
350
+ /** @type {function(any,any):number} */ (dec1, dec2) => {
351
+ if (dec1.curr.id.client === dec2.curr.id.client) {
352
+ const clockDiff = dec1.curr.id.clock - dec2.curr.id.clock
353
+ if (clockDiff === 0) {
354
+ // @todo remove references to skip since the structDecoders must filter Skips.
355
+ return dec1.curr.constructor === dec2.curr.constructor
356
+ ? 0
357
+ : dec1.curr.constructor === Skip ? 1 : -1 // we are filtering skips anyway.
358
+ } else {
359
+ return clockDiff
360
+ }
361
+ } else {
362
+ return dec2.curr.id.client - dec1.curr.id.client
363
+ }
364
+ }
365
+ )
366
+ if (lazyStructDecoders.length === 0) {
367
+ break
368
+ }
369
+ const currDecoder = lazyStructDecoders[0]
370
+ // write from currDecoder until the next operation is from another client or if filler-struct
371
+ // then we need to reorder the decoders and find the next operation to write
372
+ const firstClient = /** @type {Item | GC} */ (currDecoder.curr).id.client
373
+
374
+ if (currWrite !== null) {
375
+ let curr = /** @type {Item | GC | null} */ (currDecoder.curr)
376
+ let iterated = false
377
+
378
+ // iterate until we find something that we haven't written already
379
+ // remember: first the high client-ids are written
380
+ while (curr !== null && curr.id.clock + curr.length <= currWrite.struct.id.clock + currWrite.struct.length && curr.id.client >= currWrite.struct.id.client) {
381
+ curr = currDecoder.next()
382
+ iterated = true
383
+ }
384
+ if (
385
+ curr === null || // current decoder is empty
386
+ curr.id.client !== firstClient || // check whether there is another decoder that has has updates from `firstClient`
387
+ (iterated && curr.id.clock > currWrite.struct.id.clock + currWrite.struct.length) // the above while loop was used and we are potentially missing updates
388
+ ) {
389
+ continue
390
+ }
391
+
392
+ if (firstClient !== currWrite.struct.id.client) {
393
+ writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
394
+ currWrite = { struct: curr, offset: 0 }
395
+ currDecoder.next()
396
+ } else {
397
+ if (currWrite.struct.id.clock + currWrite.struct.length < curr.id.clock) {
398
+ // @todo write currStruct & set currStruct = Skip(clock = currStruct.id.clock + currStruct.length, length = curr.id.clock - self.clock)
399
+ if (currWrite.struct.constructor === Skip) {
400
+ // extend existing skip
401
+ currWrite.struct.length = curr.id.clock + curr.length - currWrite.struct.id.clock
402
+ } else {
403
+ writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
404
+ const diff = curr.id.clock - currWrite.struct.id.clock - currWrite.struct.length
405
+ /**
406
+ * @type {Skip}
407
+ */
408
+ const struct = new Skip(createID(firstClient, currWrite.struct.id.clock + currWrite.struct.length), diff)
409
+ currWrite = { struct, offset: 0 }
410
+ }
411
+ } else { // if (currWrite.struct.id.clock + currWrite.struct.length >= curr.id.clock) {
412
+ const diff = currWrite.struct.id.clock + currWrite.struct.length - curr.id.clock
413
+ if (diff > 0) {
414
+ if (currWrite.struct.constructor === Skip) {
415
+ // prefer to slice Skip because the other struct might contain more information
416
+ currWrite.struct.length -= diff
417
+ } else {
418
+ curr = sliceStruct(curr, diff)
419
+ }
420
+ }
421
+ if (!currWrite.struct.mergeWith(/** @type {any} */ (curr))) {
422
+ writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
423
+ currWrite = { struct: curr, offset: 0 }
424
+ currDecoder.next()
425
+ }
426
+ }
427
+ }
428
+ } else {
429
+ currWrite = { struct: /** @type {Item | GC} */ (currDecoder.curr), offset: 0 }
430
+ currDecoder.next()
431
+ }
432
+ for (
433
+ let next = currDecoder.curr;
434
+ next !== null && next.id.client === firstClient && next.id.clock === currWrite.struct.id.clock + currWrite.struct.length && next.constructor !== Skip;
435
+ next = currDecoder.next()
436
+ ) {
437
+ writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
438
+ currWrite = { struct: next, offset: 0 }
439
+ }
440
+ }
441
+ if (currWrite !== null) {
442
+ writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
443
+ currWrite = null
444
+ }
445
+ finishLazyStructWriting(lazyStructEncoder)
446
+
447
+ const dss = updateDecoders.map(decoder => readIdSet(decoder))
448
+ const ds = mergeIdSets(dss)
449
+ writeIdSet(updateEncoder, ds)
450
+ return updateEncoder.toUint8Array()
451
+ }
452
+
453
+ /**
454
+ * @param {Uint8Array} update
455
+ * @param {Uint8Array} sv
456
+ * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
457
+ * @param {typeof UpdateEncoderV1 | typeof UpdateEncoderV2} [YEncoder]
458
+ */
459
+ export const diffUpdateV2 = (update, sv, YDecoder = UpdateDecoderV2, YEncoder = UpdateEncoderV2) => {
460
+ const state = decodeStateVector(sv)
461
+ const encoder = new YEncoder()
462
+ const lazyStructWriter = new LazyStructWriter(encoder)
463
+ const decoder = new YDecoder(decoding.createDecoder(update))
464
+ const reader = new LazyStructReader(decoder, false)
465
+ while (reader.curr) {
466
+ const curr = reader.curr
467
+ const currClient = curr.id.client
468
+ const svClock = state.get(currClient) || 0
469
+ if (reader.curr.constructor === Skip) {
470
+ // the first written struct shouldn't be a skip
471
+ reader.next()
472
+ continue
473
+ }
474
+ if (curr.id.clock + curr.length > svClock) {
475
+ writeStructToLazyStructWriter(lazyStructWriter, curr, math.max(svClock - curr.id.clock, 0))
476
+ reader.next()
477
+ while (reader.curr && reader.curr.id.client === currClient) {
478
+ writeStructToLazyStructWriter(lazyStructWriter, reader.curr, 0)
479
+ reader.next()
480
+ }
481
+ } else {
482
+ // read until something new comes up
483
+ while (reader.curr && reader.curr.id.client === currClient && reader.curr.id.clock + reader.curr.length <= svClock) {
484
+ reader.next()
485
+ }
486
+ }
487
+ }
488
+ finishLazyStructWriting(lazyStructWriter)
489
+ // write ds
490
+ const ds = readIdSet(decoder)
491
+ writeIdSet(encoder, ds)
492
+ return encoder.toUint8Array()
493
+ }
494
+
495
+ /**
496
+ * @param {Uint8Array} update
497
+ * @param {Uint8Array} sv
498
+ */
499
+ export const diffUpdate = (update, sv) => diffUpdateV2(update, sv, UpdateDecoderV1, UpdateEncoderV1)
500
+
501
+ /**
502
+ * @param {LazyStructWriter} lazyWriter
503
+ */
504
+ const flushLazyStructWriter = lazyWriter => {
505
+ if (lazyWriter.written > 0) {
506
+ lazyWriter.clientStructs.push({ written: lazyWriter.written, restEncoder: encoding.toUint8Array(lazyWriter.encoder.restEncoder) })
507
+ lazyWriter.encoder.restEncoder = encoding.createEncoder()
508
+ lazyWriter.written = 0
509
+ }
510
+ }
511
+
512
+ /**
513
+ * @param {LazyStructWriter} lazyWriter
514
+ * @param {Item | GC} struct
515
+ * @param {number} offset
516
+ */
517
+ const writeStructToLazyStructWriter = (lazyWriter, struct, offset) => {
518
+ // flush curr if we start another client
519
+ if (lazyWriter.written > 0 && lazyWriter.currClient !== struct.id.client) {
520
+ flushLazyStructWriter(lazyWriter)
521
+ }
522
+ if (lazyWriter.written === 0) {
523
+ lazyWriter.currClient = struct.id.client
524
+ // write next client
525
+ lazyWriter.encoder.writeClient(struct.id.client)
526
+ // write startClock
527
+ encoding.writeVarUint(lazyWriter.encoder.restEncoder, struct.id.clock + offset)
528
+ }
529
+ struct.write(lazyWriter.encoder, offset, 0)
530
+ lazyWriter.written++
531
+ }
532
+ /**
533
+ * Call this function when we collected all parts and want to
534
+ * put all the parts together. After calling this method,
535
+ * you can continue using the UpdateEncoder.
536
+ *
537
+ * @param {LazyStructWriter} lazyWriter
538
+ */
539
+ const finishLazyStructWriting = (lazyWriter) => {
540
+ flushLazyStructWriter(lazyWriter)
541
+
542
+ // this is a fresh encoder because we called flushCurr
543
+ const restEncoder = lazyWriter.encoder.restEncoder
544
+
545
+ /**
546
+ * Now we put all the fragments together.
547
+ * This works similarly to `writeClientsStructs`
548
+ */
549
+
550
+ // write # states that were updated - i.e. the clients
551
+ encoding.writeVarUint(restEncoder, lazyWriter.clientStructs.length)
552
+
553
+ for (let i = 0; i < lazyWriter.clientStructs.length; i++) {
554
+ const partStructs = lazyWriter.clientStructs[i]
555
+ /**
556
+ * Works similarly to `writeStructs`
557
+ */
558
+ // write # encoded structs
559
+ encoding.writeVarUint(restEncoder, partStructs.written)
560
+ // write the rest of the fragment
561
+ encoding.writeUint8Array(restEncoder, partStructs.restEncoder)
562
+ }
563
+ }
564
+
565
+ /**
566
+ * @param {Uint8Array} update
567
+ * @param {function(Item|GC|Skip):Item|GC|Skip} blockTransformer
568
+ * @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} YDecoder
569
+ * @param {typeof UpdateEncoderV2 | typeof UpdateEncoderV1 } YEncoder
570
+ */
571
+ export const convertUpdateFormat = (update, blockTransformer, YDecoder, YEncoder) => {
572
+ const updateDecoder = new YDecoder(decoding.createDecoder(update))
573
+ const lazyDecoder = new LazyStructReader(updateDecoder, false)
574
+ const updateEncoder = new YEncoder()
575
+ const lazyWriter = new LazyStructWriter(updateEncoder)
576
+ for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
577
+ writeStructToLazyStructWriter(lazyWriter, blockTransformer(curr), 0)
578
+ }
579
+ finishLazyStructWriting(lazyWriter)
580
+ const ds = readIdSet(updateDecoder)
581
+ writeIdSet(updateEncoder, ds)
582
+ return updateEncoder.toUint8Array()
583
+ }
584
+
585
+ /**
586
+ * @typedef {Object} ObfuscatorOptions
587
+ * @property {boolean} [ObfuscatorOptions.formatting=true]
588
+ * @property {boolean} [ObfuscatorOptions.subdocs=true]
589
+ * @property {boolean} [ObfuscatorOptions.name=true] Whether to obfuscate nodeName / hookName
590
+ */
591
+
592
+ /**
593
+ * @param {ObfuscatorOptions} obfuscator
594
+ */
595
+ const createObfuscator = ({ formatting = true, subdocs = true, name = true } = {}) => {
596
+ let i = 0
597
+ const mapKeyCache = map.create()
598
+ const nodeNameCache = map.create()
599
+ const formattingKeyCache = map.create()
600
+ const formattingValueCache = map.create()
601
+ formattingValueCache.set(null, null) // end of a formatting range should always be the end of a formatting range
602
+ /**
603
+ * @param {Item|GC|Skip} block
604
+ * @return {Item|GC|Skip}
605
+ */
606
+ return block => {
607
+ switch (block.constructor) {
608
+ case GC:
609
+ case Skip:
610
+ return block
611
+ case Item: {
612
+ const item = /** @type {Item} */ (block)
613
+ const content = item.content
614
+ switch (content.constructor) {
615
+ case ContentDeleted:
616
+ break
617
+ case ContentType: {
618
+ if (name) {
619
+ const type = /** @type {ContentType} */ (content).type
620
+ if (type.name != null) {
621
+ type.name = map.setIfUndefined(nodeNameCache, type.name, () => 'typename-' + i)
622
+ }
623
+ }
624
+ break
625
+ }
626
+ case ContentAny: {
627
+ const c = /** @type {ContentAny} */ (content)
628
+ c.arr = c.arr.map(() => i)
629
+ break
630
+ }
631
+ case ContentBinary: {
632
+ const c = /** @type {ContentBinary} */ (content)
633
+ c.content = new Uint8Array([i])
634
+ break
635
+ }
636
+ case ContentDoc: {
637
+ const c = /** @type {ContentDoc} */ (content)
638
+ if (subdocs) {
639
+ c.opts = {}
640
+ c.doc.guid = i + ''
641
+ }
642
+ break
643
+ }
644
+ case ContentEmbed: {
645
+ const c = /** @type {ContentEmbed} */ (content)
646
+ c.embed = {}
647
+ break
648
+ }
649
+ case ContentFormat: {
650
+ const c = /** @type {ContentFormat} */ (content)
651
+ if (formatting) {
652
+ c.key = map.setIfUndefined(formattingKeyCache, c.key, () => i + '')
653
+ c.value = map.setIfUndefined(formattingValueCache, c.value, () => ({ i }))
654
+ }
655
+ break
656
+ }
657
+ case ContentJSON: {
658
+ const c = /** @type {ContentJSON} */ (content)
659
+ c.arr = c.arr.map(() => i)
660
+ break
661
+ }
662
+ case ContentString: {
663
+ const c = /** @type {ContentString} */ (content)
664
+ c.str = string.repeat((i % 10) + '', c.str.length)
665
+ break
666
+ }
667
+ default:
668
+ // unknown content type
669
+ error.unexpectedCase()
670
+ }
671
+ if (item.parentSub) {
672
+ item.parentSub = map.setIfUndefined(mapKeyCache, item.parentSub, () => i + '')
673
+ }
674
+ i++
675
+ return block
676
+ }
677
+ default:
678
+ // unknown block-type
679
+ error.unexpectedCase()
680
+ }
681
+ }
682
+ }
683
+
684
+ /**
685
+ * This function obfuscates the content of a Yjs update. This is useful to share
686
+ * buggy Yjs documents while significantly limiting the possibility that a
687
+ * developer can on the user. Note that it might still be possible to deduce
688
+ * some information by analyzing the "structure" of the document or by analyzing
689
+ * the typing behavior using the CRDT-related metadata that is still kept fully
690
+ * intact.
691
+ *
692
+ * @param {Uint8Array} update
693
+ * @param {ObfuscatorOptions} [opts]
694
+ */
695
+ export const obfuscateUpdate = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV1, UpdateEncoderV1)
696
+
697
+ /**
698
+ * @param {Uint8Array} update
699
+ * @param {ObfuscatorOptions} [opts]
700
+ */
701
+ export const obfuscateUpdateV2 = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV2, UpdateEncoderV2)
702
+
703
+ /**
704
+ * @param {Uint8Array} update
705
+ */
706
+ export const convertUpdateFormatV1ToV2 = update => convertUpdateFormat(update, f.id, UpdateDecoderV1, UpdateEncoderV2)
707
+
708
+ /**
709
+ * @param {Uint8Array} update
710
+ */
711
+ export const convertUpdateFormatV2ToV1 = update => convertUpdateFormat(update, f.id, UpdateDecoderV2, UpdateEncoderV1)