@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/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 => writeContentIds(new IdSetEncoderV2(), 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 encodeContentMap = contentMap => writeContentMap(new IdSetEncoderV2(), contentMap)
190
+ export const filterContentMap = (contentMap, insertPredicate, deletePredicate) => createContentMap(idmap.filterIdMap(contentMap.inserts, insertPredicate), idmap.filterIdMap(contentMap.deletes, deletePredicate))
@@ -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 readUpdateToContentIdsV2 = (update, YDecoder = UpdateDecoderV2) => {
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 readUpdateToContentIds = update => readUpdateToContentIdsV2(update, UpdateDecoderV1)
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, 0)
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
+ }