@y/y 14.0.0-20 → 14.0.0-22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +1 -1
- package/dist/src/utils/IdMap.d.ts +4 -4
- package/dist/src/utils/IdMap.d.ts.map +1 -1
- package/dist/src/utils/IdSet.d.ts +4 -4
- package/dist/src/utils/IdSet.d.ts.map +1 -1
- package/dist/src/utils/Snapshot.d.ts +3 -3
- package/dist/src/utils/Snapshot.d.ts.map +1 -1
- package/dist/src/utils/UpdateDecoder.d.ts +8 -4
- package/dist/src/utils/UpdateDecoder.d.ts.map +1 -1
- package/dist/src/utils/encoding.d.ts +3 -3
- package/dist/src/utils/encoding.d.ts.map +1 -1
- package/dist/src/utils/meta.d.ts +31 -3
- package/dist/src/utils/meta.d.ts.map +1 -1
- package/dist/src/utils/updates.d.ts +10 -4
- package/dist/src/utils/updates.d.ts.map +1 -1
- package/dist/tests/updates.tests.d.ts +1 -0
- package/dist/tests/updates.tests.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/index.js +7 -3
- package/src/utils/IdMap.js +5 -5
- package/src/utils/IdSet.js +3 -3
- package/src/utils/Snapshot.js +4 -4
- package/src/utils/UpdateDecoder.js +8 -4
- package/src/utils/encoding.js +4 -4
- package/src/utils/meta.js +96 -3
- package/src/utils/updates.js +104 -13
- package/tests/testHelper.js +594 -0
package/src/utils/meta.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import * as idmap from './IdMap.js'
|
|
6
6
|
import * as idset from './IdSet.js'
|
|
7
7
|
import { IdSetEncoderV2 } from './UpdateEncoder.js'
|
|
8
|
+
import { IdSetDecoderV2 } from './UpdateDecoder.js'
|
|
9
|
+
import * as decoding from 'lib0/decoding'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* @typedef {{ inserts: import('./IdSet.js').IdSet, deletes: import('./IdSet.js').IdSet }} ContentIds
|
|
@@ -18,7 +20,7 @@ import { IdSetEncoderV2 } from './UpdateEncoder.js'
|
|
|
18
20
|
* @param {import('./IdSet.js').IdSet} inserts
|
|
19
21
|
* @param {import('./IdSet.js').IdSet} deletes
|
|
20
22
|
*/
|
|
21
|
-
export const createContentIds = (inserts, deletes) => ({ inserts, deletes })
|
|
23
|
+
export const createContentIds = (inserts = idset.createIdSet(), deletes = idset.createIdSet()) => ({ inserts, deletes })
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* @param {ContentMap} contentMap
|
|
@@ -50,6 +52,31 @@ export const createContentIdsFromDocDiff = (ydocPrev, ydocNext) =>
|
|
|
50
52
|
export const excludeContentIds = (content, excludeContent) =>
|
|
51
53
|
createContentIds(idset.diffIdSet(content.inserts, excludeContent.inserts), idset.diffIdSet(content.deletes, excludeContent.deletes))
|
|
52
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @param {ContentMap} content
|
|
57
|
+
* @param {ContentIds | ContentMap} excludeContent
|
|
58
|
+
*/
|
|
59
|
+
export const excludeContentMaps = (content, excludeContent) => createContentMap(
|
|
60
|
+
idmap.diffIdMap(content.inserts, excludeContent.inserts),
|
|
61
|
+
idmap.diffIdMap(content.deletes, excludeContent.deletes)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {Array<ContentMap>} contents
|
|
66
|
+
*/
|
|
67
|
+
export const mergeContentMaps = contents => createContentMap(
|
|
68
|
+
idmap.mergeIdMaps(contents.map(c => c.inserts)),
|
|
69
|
+
idmap.mergeIdMaps(contents.map(c => c.deletes))
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {Array<ContentIds>} contents
|
|
74
|
+
*/
|
|
75
|
+
export const mergeContentIds = contents => createContentIds(
|
|
76
|
+
idset.mergeIdSets(contents.map(c => c.inserts)),
|
|
77
|
+
idset.mergeIdSets(contents.map(c => c.deletes))
|
|
78
|
+
)
|
|
79
|
+
|
|
53
80
|
/**
|
|
54
81
|
* @param {import('./IdMap.js').IdMap<any>} inserts
|
|
55
82
|
* @param {import('./IdMap.js').IdMap<any>} deletes
|
|
@@ -78,7 +105,27 @@ export const writeContentIds = (encoder, contentIds) => {
|
|
|
78
105
|
/**
|
|
79
106
|
* @param {ContentIds} contentIds
|
|
80
107
|
*/
|
|
81
|
-
export const encodeContentIds = contentIds =>
|
|
108
|
+
export const encodeContentIds = contentIds => {
|
|
109
|
+
const encoder = new IdSetEncoderV2()
|
|
110
|
+
writeContentIds(encoder, contentIds)
|
|
111
|
+
return encoder.toUint8Array()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @todo this encoding needs to be heavily optimized for production
|
|
116
|
+
*
|
|
117
|
+
* @param {import('./UpdateDecoder.js').IdSetDecoder} decoder
|
|
118
|
+
* @return {ContentIds}
|
|
119
|
+
*/
|
|
120
|
+
export const readContentIds = decoder => createContentIds(
|
|
121
|
+
idset.readIdSet(decoder),
|
|
122
|
+
idset.readIdSet(decoder)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {Uint8Array<any>} buf
|
|
127
|
+
*/
|
|
128
|
+
export const decodeContentIds = buf => readContentIds(new IdSetDecoderV2(decoding.createDecoder(buf)))
|
|
82
129
|
|
|
83
130
|
/**
|
|
84
131
|
* @todo this encoding needs to be heavily optimized for production
|
|
@@ -92,6 +139,52 @@ export const writeContentMap = (encoder, contentMap) => {
|
|
|
92
139
|
}
|
|
93
140
|
|
|
94
141
|
/**
|
|
142
|
+
* @todo this encoding needs to be heavily optimized for production
|
|
143
|
+
*
|
|
144
|
+
* @param {import('./UpdateDecoder.js').IdSetDecoder} decoder
|
|
145
|
+
* @return {ContentMap} contentMap
|
|
146
|
+
*/
|
|
147
|
+
export const readContentMap = (decoder) => createContentMap(
|
|
148
|
+
idmap.readIdMap(decoder),
|
|
149
|
+
idmap.readIdMap(decoder)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @param {ContentMap} contentMap
|
|
154
|
+
*/
|
|
155
|
+
export const encodeContentMap = contentMap => {
|
|
156
|
+
const encoder = new IdSetEncoderV2()
|
|
157
|
+
writeContentMap(encoder, contentMap)
|
|
158
|
+
return encoder.toUint8Array()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {ContentMap} mapA
|
|
163
|
+
* @param {ContentMap|ContentIds} mapB
|
|
164
|
+
*/
|
|
165
|
+
export const intersectContentMap = (mapA, mapB) => createContentMap(
|
|
166
|
+
idmap.intersectMaps(mapA.inserts, mapB.inserts),
|
|
167
|
+
idmap.intersectMaps(mapA.deletes, mapB.deletes)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {ContentIds} setA
|
|
172
|
+
* @param {ContentIds|ContentMap} setB
|
|
173
|
+
*/
|
|
174
|
+
export const intersectContentIds = (setA, setB) => createContentIds(
|
|
175
|
+
idset.intersectSets(setA.inserts, setB.inserts),
|
|
176
|
+
idset.intersectSets(setA.deletes, setB.deletes)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param {Uint8Array<any>} buf
|
|
181
|
+
*/
|
|
182
|
+
export const decodeContentMap = buf => readContentMap(new IdSetDecoderV2(decoding.createDecoder(buf)))
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @todo filter by array of content instead
|
|
95
186
|
* @param {ContentMap} contentMap
|
|
187
|
+
* @param {(c:Array<idmap.ContentAttribute<any>>)=>boolean} insertPredicate
|
|
188
|
+
* @param {(c:Array<idmap.ContentAttribute<any>>)=>boolean} deletePredicate
|
|
96
189
|
*/
|
|
97
|
-
export const
|
|
190
|
+
export const filterContentMap = (contentMap, insertPredicate, deletePredicate) => createContentMap(idmap.filterIdMap(contentMap.inserts, insertPredicate), idmap.filterIdMap(contentMap.deletes, deletePredicate))
|
package/src/utils/updates.js
CHANGED
|
@@ -33,9 +33,14 @@ import {
|
|
|
33
33
|
UpdateEncoderV1,
|
|
34
34
|
UpdateEncoderV2,
|
|
35
35
|
writeIdSet,
|
|
36
|
-
createIdSet
|
|
36
|
+
createIdSet,
|
|
37
|
+
Doc,
|
|
38
|
+
applyUpdate,
|
|
39
|
+
applyUpdateV2
|
|
37
40
|
} from '../internals.js'
|
|
38
41
|
|
|
42
|
+
import * as idset from './IdSet.js'
|
|
43
|
+
|
|
39
44
|
/**
|
|
40
45
|
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
|
41
46
|
*/
|
|
@@ -248,7 +253,7 @@ export const encodeStateVectorFromUpdate = update => encodeStateVectorFromUpdate
|
|
|
248
253
|
* @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder]
|
|
249
254
|
* @return {import('./meta.js').ContentIds}
|
|
250
255
|
*/
|
|
251
|
-
export const
|
|
256
|
+
export const createContentIdsFromUpdateV2 = (update, YDecoder = UpdateDecoderV2) => {
|
|
252
257
|
const updateDecoder = new YDecoder(decoding.createDecoder(update))
|
|
253
258
|
const lazyDecoder = new LazyStructReader(updateDecoder, true)
|
|
254
259
|
const inserts = createIdSet()
|
|
@@ -280,7 +285,7 @@ export const readUpdateToContentIdsV2 = (update, YDecoder = UpdateDecoderV2) =>
|
|
|
280
285
|
* @param {Uint8Array} update
|
|
281
286
|
* @return {import('./meta.js').ContentIds}
|
|
282
287
|
*/
|
|
283
|
-
export const
|
|
288
|
+
export const createContentIdsFromUpdate = update => createContentIdsFromUpdateV2(update, UpdateDecoderV1)
|
|
284
289
|
|
|
285
290
|
/**
|
|
286
291
|
* This method is intended to slice any kind of struct and retrieve the right part.
|
|
@@ -390,7 +395,7 @@ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = U
|
|
|
390
395
|
}
|
|
391
396
|
|
|
392
397
|
if (firstClient !== currWrite.struct.id.client) {
|
|
393
|
-
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
|
|
398
|
+
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset, 0)
|
|
394
399
|
currWrite = { struct: curr, offset: 0 }
|
|
395
400
|
currDecoder.next()
|
|
396
401
|
} else {
|
|
@@ -400,7 +405,7 @@ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = U
|
|
|
400
405
|
// extend existing skip
|
|
401
406
|
currWrite.struct.length = curr.id.clock + curr.length - currWrite.struct.id.clock
|
|
402
407
|
} else {
|
|
403
|
-
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
|
|
408
|
+
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset, 0)
|
|
404
409
|
const diff = curr.id.clock - currWrite.struct.id.clock - currWrite.struct.length
|
|
405
410
|
/**
|
|
406
411
|
* @type {Skip}
|
|
@@ -419,7 +424,7 @@ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = U
|
|
|
419
424
|
}
|
|
420
425
|
}
|
|
421
426
|
if (!currWrite.struct.mergeWith(/** @type {any} */ (curr))) {
|
|
422
|
-
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
|
|
427
|
+
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset, 0)
|
|
423
428
|
currWrite = { struct: curr, offset: 0 }
|
|
424
429
|
currDecoder.next()
|
|
425
430
|
}
|
|
@@ -434,12 +439,12 @@ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = U
|
|
|
434
439
|
next !== null && next.id.client === firstClient && next.id.clock === currWrite.struct.id.clock + currWrite.struct.length && next.constructor !== Skip;
|
|
435
440
|
next = currDecoder.next()
|
|
436
441
|
) {
|
|
437
|
-
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
|
|
442
|
+
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset, 0)
|
|
438
443
|
currWrite = { struct: next, offset: 0 }
|
|
439
444
|
}
|
|
440
445
|
}
|
|
441
446
|
if (currWrite !== null) {
|
|
442
|
-
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset)
|
|
447
|
+
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset, 0)
|
|
443
448
|
currWrite = null
|
|
444
449
|
}
|
|
445
450
|
finishLazyStructWriting(lazyStructEncoder)
|
|
@@ -451,6 +456,7 @@ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = U
|
|
|
451
456
|
}
|
|
452
457
|
|
|
453
458
|
/**
|
|
459
|
+
* @deprecated
|
|
454
460
|
* @param {Uint8Array} update
|
|
455
461
|
* @param {Uint8Array} sv
|
|
456
462
|
* @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
|
|
@@ -472,10 +478,10 @@ export const diffUpdateV2 = (update, sv, YDecoder = UpdateDecoderV2, YEncoder =
|
|
|
472
478
|
continue
|
|
473
479
|
}
|
|
474
480
|
if (curr.id.clock + curr.length > svClock) {
|
|
475
|
-
writeStructToLazyStructWriter(lazyStructWriter, curr, math.max(svClock - curr.id.clock, 0))
|
|
481
|
+
writeStructToLazyStructWriter(lazyStructWriter, curr, math.max(svClock - curr.id.clock, 0), 0)
|
|
476
482
|
reader.next()
|
|
477
483
|
while (reader.curr && reader.curr.id.client === currClient) {
|
|
478
|
-
writeStructToLazyStructWriter(lazyStructWriter, reader.curr, 0)
|
|
484
|
+
writeStructToLazyStructWriter(lazyStructWriter, reader.curr, 0, 0)
|
|
479
485
|
reader.next()
|
|
480
486
|
}
|
|
481
487
|
} else {
|
|
@@ -493,6 +499,9 @@ export const diffUpdateV2 = (update, sv, YDecoder = UpdateDecoderV2, YEncoder =
|
|
|
493
499
|
}
|
|
494
500
|
|
|
495
501
|
/**
|
|
502
|
+
* @deprecated
|
|
503
|
+
* @todo remove this in favor of intersectupdate
|
|
504
|
+
*
|
|
496
505
|
* @param {Uint8Array} update
|
|
497
506
|
* @param {Uint8Array} sv
|
|
498
507
|
*/
|
|
@@ -513,8 +522,9 @@ const flushLazyStructWriter = lazyWriter => {
|
|
|
513
522
|
* @param {LazyStructWriter} lazyWriter
|
|
514
523
|
* @param {Item | GC} struct
|
|
515
524
|
* @param {number} offset
|
|
525
|
+
* @param {number} offsetEnd
|
|
516
526
|
*/
|
|
517
|
-
const writeStructToLazyStructWriter = (lazyWriter, struct, offset) => {
|
|
527
|
+
const writeStructToLazyStructWriter = (lazyWriter, struct, offset, offsetEnd) => {
|
|
518
528
|
// flush curr if we start another client
|
|
519
529
|
if (lazyWriter.written > 0 && lazyWriter.currClient !== struct.id.client) {
|
|
520
530
|
flushLazyStructWriter(lazyWriter)
|
|
@@ -526,7 +536,7 @@ const writeStructToLazyStructWriter = (lazyWriter, struct, offset) => {
|
|
|
526
536
|
// write startClock
|
|
527
537
|
encoding.writeVarUint(lazyWriter.encoder.restEncoder, struct.id.clock + offset)
|
|
528
538
|
}
|
|
529
|
-
struct.write(lazyWriter.encoder, offset,
|
|
539
|
+
struct.write(lazyWriter.encoder, offset, offsetEnd)
|
|
530
540
|
lazyWriter.written++
|
|
531
541
|
}
|
|
532
542
|
/**
|
|
@@ -574,7 +584,7 @@ export const convertUpdateFormat = (update, blockTransformer, YDecoder, YEncoder
|
|
|
574
584
|
const updateEncoder = new YEncoder()
|
|
575
585
|
const lazyWriter = new LazyStructWriter(updateEncoder)
|
|
576
586
|
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
577
|
-
writeStructToLazyStructWriter(lazyWriter, blockTransformer(curr), 0)
|
|
587
|
+
writeStructToLazyStructWriter(lazyWriter, blockTransformer(curr), 0, 0)
|
|
578
588
|
}
|
|
579
589
|
finishLazyStructWriting(lazyWriter)
|
|
580
590
|
const ds = readIdSet(updateDecoder)
|
|
@@ -709,3 +719,84 @@ export const convertUpdateFormatV1ToV2 = update => convertUpdateFormat(update, f
|
|
|
709
719
|
* @param {Uint8Array} update
|
|
710
720
|
*/
|
|
711
721
|
export const convertUpdateFormatV2ToV1 = update => convertUpdateFormat(update, f.id, UpdateDecoderV2, UpdateEncoderV1)
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Filter an update to only include content specified by a ContentIds pattern.
|
|
725
|
+
*
|
|
726
|
+
* This function extracts a subset of an update, keeping only the structs whose IDs
|
|
727
|
+
* are present in `contentIds.inserts` and only the delete set entries that are
|
|
728
|
+
* present in `contentIds.deletes`.
|
|
729
|
+
*
|
|
730
|
+
* Note: If a struct partially overlaps with the contentIds pattern, only the
|
|
731
|
+
* overlapping portion is included in the result.
|
|
732
|
+
*
|
|
733
|
+
* @param {Uint8Array} update
|
|
734
|
+
* @param {import('./meta.js').ContentIds} contentIds - Pattern specifying which content to include
|
|
735
|
+
* @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
|
|
736
|
+
* @param {typeof UpdateEncoderV1 | typeof UpdateEncoderV2} [YEncoder]
|
|
737
|
+
* @return {Uint8Array}
|
|
738
|
+
*/
|
|
739
|
+
export const intersectUpdateWithContentIdsV2 = (update, contentIds, YDecoder = UpdateDecoderV2, YEncoder = UpdateEncoderV2) => {
|
|
740
|
+
const { inserts, deletes } = contentIds
|
|
741
|
+
const encoder = new YEncoder()
|
|
742
|
+
const lazyStructWriter = new LazyStructWriter(encoder)
|
|
743
|
+
const decoder = new YDecoder(decoding.createDecoder(update))
|
|
744
|
+
const reader = new LazyStructReader(decoder, true)
|
|
745
|
+
|
|
746
|
+
while (reader.curr) {
|
|
747
|
+
const currClientId = reader.curr.id.client
|
|
748
|
+
let nextClock = reader.curr.id.clock
|
|
749
|
+
let firstWrite = false
|
|
750
|
+
while (reader.curr != null && reader.curr.id.client === currClientId) {
|
|
751
|
+
const curr = reader.curr
|
|
752
|
+
for (const slice of inserts.slice(currClientId, nextClock, curr.length)) {
|
|
753
|
+
if (slice.exists) {
|
|
754
|
+
const skipLen = slice.clock - nextClock
|
|
755
|
+
if (skipLen > 0 && firstWrite) {
|
|
756
|
+
// write missing skip
|
|
757
|
+
writeStructToLazyStructWriter(lazyStructWriter, new Skip(createID(currClientId, nextClock), skipLen), 0, 0)
|
|
758
|
+
}
|
|
759
|
+
// write sliced content
|
|
760
|
+
writeStructToLazyStructWriter(lazyStructWriter, curr, slice.clock - curr.id.clock, (curr.id.clock + curr.length) - (slice.clock + slice.len))
|
|
761
|
+
nextClock = slice.clock + slice.len
|
|
762
|
+
firstWrite = true
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
reader.next()
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
finishLazyStructWriting(lazyStructWriter)
|
|
769
|
+
// Filter the delete set to only include entries in contentIds.deletes
|
|
770
|
+
const ds = readIdSet(decoder)
|
|
771
|
+
const filteredDs = idset.intersectSets(ds, deletes)
|
|
772
|
+
writeIdSet(encoder, filteredDs)
|
|
773
|
+
return encoder.toUint8Array()
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Filter an update (V1 format) to only include content specified by a ContentIds pattern.
|
|
778
|
+
*
|
|
779
|
+
* @param {Uint8Array} update
|
|
780
|
+
* @param {import('./meta.js').ContentIds} contentIds - Pattern specifying which content to include
|
|
781
|
+
* @return {Uint8Array}
|
|
782
|
+
*/
|
|
783
|
+
export const intersectUpdateWithContentIds = (update, contentIds) =>
|
|
784
|
+
intersectUpdateWithContentIdsV2(update, contentIds, UpdateDecoderV1, UpdateEncoderV1)
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* @param {Uint8Array} update
|
|
788
|
+
*/
|
|
789
|
+
export const createDocFromUpdate = update => {
|
|
790
|
+
const ydoc = new Doc()
|
|
791
|
+
applyUpdate(ydoc, update)
|
|
792
|
+
return ydoc
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* @param {Uint8Array} update
|
|
797
|
+
*/
|
|
798
|
+
export const createDocFromUpdateV2 = update => {
|
|
799
|
+
const ydoc = new Doc()
|
|
800
|
+
applyUpdateV2(ydoc, update)
|
|
801
|
+
return ydoc
|
|
802
|
+
}
|