@y/y 14.0.0-16
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/LICENSE +23 -0
- package/README.md +1406 -0
- package/dist/Skip-j0kX7pdq.js +12173 -0
- package/dist/Skip-j0kX7pdq.js.map +1 -0
- package/dist/Skip-wRT7BKFP.js +11877 -0
- package/dist/Skip-wRT7BKFP.js.map +1 -0
- package/dist/index-DyTeTfmj.js +163 -0
- package/dist/index-DyTeTfmj.js.map +1 -0
- package/dist/index-R7GxO-36.js +165 -0
- package/dist/index-R7GxO-36.js.map +1 -0
- package/dist/internals.cjs +286 -0
- package/dist/internals.cjs.map +1 -0
- package/dist/internals.mjs +25 -0
- package/dist/internals.mjs.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/internals.d.ts +43 -0
- package/dist/src/internals.d.ts.map +1 -0
- package/dist/src/structs/AbstractStruct.d.ts +42 -0
- package/dist/src/structs/AbstractStruct.d.ts.map +1 -0
- package/dist/src/structs/ContentAny.d.ts +67 -0
- package/dist/src/structs/ContentAny.d.ts.map +1 -0
- package/dist/src/structs/ContentBinary.d.ts +64 -0
- package/dist/src/structs/ContentBinary.d.ts.map +1 -0
- package/dist/src/structs/ContentDeleted.d.ts +64 -0
- package/dist/src/structs/ContentDeleted.d.ts.map +1 -0
- package/dist/src/structs/ContentDoc.d.ts +72 -0
- package/dist/src/structs/ContentDoc.d.ts.map +1 -0
- package/dist/src/structs/ContentEmbed.d.ts +67 -0
- package/dist/src/structs/ContentEmbed.d.ts.map +1 -0
- package/dist/src/structs/ContentFormat.d.ts +69 -0
- package/dist/src/structs/ContentFormat.d.ts.map +1 -0
- package/dist/src/structs/ContentJSON.d.ts +70 -0
- package/dist/src/structs/ContentJSON.d.ts.map +1 -0
- package/dist/src/structs/ContentString.d.ts +70 -0
- package/dist/src/structs/ContentString.d.ts.map +1 -0
- package/dist/src/structs/ContentType.d.ts +83 -0
- package/dist/src/structs/ContentType.d.ts.map +1 -0
- package/dist/src/structs/GC.d.ts +31 -0
- package/dist/src/structs/GC.d.ts.map +1 -0
- package/dist/src/structs/Item.d.ts +212 -0
- package/dist/src/structs/Item.d.ts.map +1 -0
- package/dist/src/structs/Skip.d.ts +33 -0
- package/dist/src/structs/Skip.d.ts.map +1 -0
- package/dist/src/types/AbstractType.d.ts +239 -0
- package/dist/src/types/AbstractType.d.ts.map +1 -0
- package/dist/src/types/YArray.d.ts +128 -0
- package/dist/src/types/YArray.d.ts.map +1 -0
- package/dist/src/types/YMap.d.ts +112 -0
- package/dist/src/types/YMap.d.ts.map +1 -0
- package/dist/src/types/YText.d.ts +216 -0
- package/dist/src/types/YText.d.ts.map +1 -0
- package/dist/src/types/YXmlElement.d.ts +106 -0
- package/dist/src/types/YXmlElement.d.ts.map +1 -0
- package/dist/src/types/YXmlFragment.d.ts +143 -0
- package/dist/src/types/YXmlFragment.d.ts.map +1 -0
- package/dist/src/types/YXmlHook.d.ts +32 -0
- package/dist/src/types/YXmlHook.d.ts.map +1 -0
- package/dist/src/types/YXmlText.d.ts +34 -0
- package/dist/src/types/YXmlText.d.ts.map +1 -0
- package/dist/src/utils/AbstractConnector.d.ts +20 -0
- package/dist/src/utils/AbstractConnector.d.ts.map +1 -0
- package/dist/src/utils/AttributionManager.d.ts +224 -0
- package/dist/src/utils/AttributionManager.d.ts.map +1 -0
- package/dist/src/utils/Doc.d.ts +267 -0
- package/dist/src/utils/Doc.d.ts.map +1 -0
- package/dist/src/utils/EventHandler.d.ts +19 -0
- package/dist/src/utils/EventHandler.d.ts.map +1 -0
- package/dist/src/utils/ID.d.ts +26 -0
- package/dist/src/utils/ID.d.ts.map +1 -0
- package/dist/src/utils/IdMap.d.ts +161 -0
- package/dist/src/utils/IdMap.d.ts.map +1 -0
- package/dist/src/utils/IdSet.d.ts +163 -0
- package/dist/src/utils/IdSet.d.ts.map +1 -0
- package/dist/src/utils/RelativePosition.d.ts +91 -0
- package/dist/src/utils/RelativePosition.d.ts.map +1 -0
- package/dist/src/utils/Snapshot.d.ts +40 -0
- package/dist/src/utils/Snapshot.d.ts.map +1 -0
- package/dist/src/utils/StructSet.d.ts +27 -0
- package/dist/src/utils/StructSet.d.ts.map +1 -0
- package/dist/src/utils/StructStore.d.ts +41 -0
- package/dist/src/utils/StructStore.d.ts.map +1 -0
- package/dist/src/utils/Transaction.d.ts +136 -0
- package/dist/src/utils/Transaction.d.ts.map +1 -0
- package/dist/src/utils/UndoManager.d.ts +188 -0
- package/dist/src/utils/UndoManager.d.ts.map +1 -0
- package/dist/src/utils/UpdateDecoder.d.ts +167 -0
- package/dist/src/utils/UpdateDecoder.d.ts.map +1 -0
- package/dist/src/utils/UpdateEncoder.d.ts +164 -0
- package/dist/src/utils/UpdateEncoder.d.ts.map +1 -0
- package/dist/src/utils/YEvent.d.ts +120 -0
- package/dist/src/utils/YEvent.d.ts.map +1 -0
- package/dist/src/utils/delta-helpers.d.ts +6 -0
- package/dist/src/utils/delta-helpers.d.ts.map +1 -0
- package/dist/src/utils/encoding.d.ts +30 -0
- package/dist/src/utils/encoding.d.ts.map +1 -0
- package/dist/src/utils/isParentOf.d.ts +3 -0
- package/dist/src/utils/isParentOf.d.ts.map +1 -0
- package/dist/src/utils/logging.d.ts +3 -0
- package/dist/src/utils/logging.d.ts.map +1 -0
- package/dist/src/utils/types.d.ts +7 -0
- package/dist/src/utils/types.d.ts.map +1 -0
- package/dist/src/utils/updates.d.ts +89 -0
- package/dist/src/utils/updates.d.ts.map +1 -0
- package/dist/testHelper.cjs +780 -0
- package/dist/testHelper.cjs.map +1 -0
- package/dist/testHelper.mjs +617 -0
- package/dist/testHelper.mjs.map +1 -0
- package/dist/tests/IdMap.tests.d.ts +9 -0
- package/dist/tests/IdMap.tests.d.ts.map +1 -0
- package/dist/tests/IdSet.tests.d.ts +9 -0
- package/dist/tests/IdSet.tests.d.ts.map +1 -0
- package/dist/tests/attribution.tests.d.ts +8 -0
- package/dist/tests/attribution.tests.d.ts.map +1 -0
- package/dist/tests/compatibility.tests.d.ts +5 -0
- package/dist/tests/compatibility.tests.d.ts.map +1 -0
- package/dist/tests/delta.tests.d.ts +7 -0
- package/dist/tests/delta.tests.d.ts.map +1 -0
- package/dist/tests/doc.tests.d.ts +13 -0
- package/dist/tests/doc.tests.d.ts.map +1 -0
- package/dist/tests/encoding.tests.d.ts +5 -0
- package/dist/tests/encoding.tests.d.ts.map +1 -0
- package/dist/tests/index.d.ts +2 -0
- package/dist/tests/index.d.ts.map +1 -0
- package/dist/tests/relativePositions.tests.d.ts +11 -0
- package/dist/tests/relativePositions.tests.d.ts.map +1 -0
- package/dist/tests/snapshot.tests.d.ts +13 -0
- package/dist/tests/snapshot.tests.d.ts.map +1 -0
- package/dist/tests/testHelper.d.ts +167 -0
- package/dist/tests/testHelper.d.ts.map +1 -0
- package/dist/tests/undo-redo.tests.d.ts +27 -0
- package/dist/tests/undo-redo.tests.d.ts.map +1 -0
- package/dist/tests/updates.tests.d.ts +24 -0
- package/dist/tests/updates.tests.d.ts.map +1 -0
- package/dist/tests/y-array.tests.d.ts +45 -0
- package/dist/tests/y-array.tests.d.ts.map +1 -0
- package/dist/tests/y-map.tests.d.ts +45 -0
- package/dist/tests/y-map.tests.d.ts.map +1 -0
- package/dist/tests/y-text.tests.d.ts +49 -0
- package/dist/tests/y-text.tests.d.ts.map +1 -0
- package/dist/tests/y-xml.tests.d.ts +15 -0
- package/dist/tests/y-xml.tests.d.ts.map +1 -0
- package/dist/yjs.cjs +151 -0
- package/dist/yjs.cjs.map +1 -0
- package/dist/yjs.mjs +26 -0
- package/dist/yjs.mjs.map +1 -0
- package/package.json +101 -0
- package/src/index.js +153 -0
- package/src/internals.js +44 -0
- package/src/structs/AbstractStruct.js +59 -0
- package/src/structs/ContentAny.js +115 -0
- package/src/structs/ContentBinary.js +93 -0
- package/src/structs/ContentDeleted.js +101 -0
- package/src/structs/ContentDoc.js +141 -0
- package/src/structs/ContentEmbed.js +98 -0
- package/src/structs/ContentFormat.js +105 -0
- package/src/structs/ContentJSON.js +119 -0
- package/src/structs/ContentString.js +113 -0
- package/src/structs/ContentType.js +176 -0
- package/src/structs/GC.js +80 -0
- package/src/structs/Item.js +845 -0
- package/src/structs/Skip.js +75 -0
- package/src/types/AbstractType.js +1434 -0
- package/src/types/YArray.js +270 -0
- package/src/types/YMap.js +244 -0
- package/src/types/YText.js +934 -0
- package/src/types/YXmlElement.js +227 -0
- package/src/types/YXmlFragment.js +266 -0
- package/src/types/YXmlHook.js +68 -0
- package/src/types/YXmlText.js +66 -0
- package/src/utils/AbstractConnector.js +25 -0
- package/src/utils/AttributionManager.js +619 -0
- package/src/utils/Doc.js +372 -0
- package/src/utils/EventHandler.js +87 -0
- package/src/utils/ID.js +89 -0
- package/src/utils/IdMap.js +629 -0
- package/src/utils/IdSet.js +823 -0
- package/src/utils/RelativePosition.js +352 -0
- package/src/utils/Snapshot.js +220 -0
- package/src/utils/StructSet.js +137 -0
- package/src/utils/StructStore.js +289 -0
- package/src/utils/Transaction.js +489 -0
- package/src/utils/UndoManager.js +391 -0
- package/src/utils/UpdateDecoder.js +281 -0
- package/src/utils/UpdateEncoder.js +320 -0
- package/src/utils/YEvent.js +216 -0
- package/src/utils/delta-helpers.js +54 -0
- package/src/utils/encoding.js +623 -0
- package/src/utils/isParentOf.js +21 -0
- package/src/utils/logging.js +21 -0
- package/src/utils/types.js +28 -0
- package/src/utils/updates.js +715 -0
- package/tests/testHelper.js +600 -0
|
@@ -0,0 +1,715 @@
|
|
|
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
|
+
YXmlElement,
|
|
37
|
+
YXmlHook,
|
|
38
|
+
createIdSet
|
|
39
|
+
} from '../internals.js'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
|
43
|
+
*/
|
|
44
|
+
function * lazyStructReaderGenerator (decoder) {
|
|
45
|
+
const numOfStateUpdates = decoding.readVarUint(decoder.restDecoder)
|
|
46
|
+
for (let i = 0; i < numOfStateUpdates; i++) {
|
|
47
|
+
const numberOfStructs = decoding.readVarUint(decoder.restDecoder)
|
|
48
|
+
const client = decoder.readClient()
|
|
49
|
+
let clock = decoding.readVarUint(decoder.restDecoder)
|
|
50
|
+
for (let i = 0; i < numberOfStructs; i++) {
|
|
51
|
+
const info = decoder.readInfo()
|
|
52
|
+
// @todo use switch instead of ifs
|
|
53
|
+
if (info === 10) {
|
|
54
|
+
const len = decoding.readVarUint(decoder.restDecoder)
|
|
55
|
+
yield new Skip(createID(client, clock), len)
|
|
56
|
+
clock += len
|
|
57
|
+
} else if ((binary.BITS5 & info) !== 0) {
|
|
58
|
+
const cantCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
|
|
59
|
+
// If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
|
|
60
|
+
// and we read the next string as parentYKey.
|
|
61
|
+
// It indicates how we store/retrieve parent from `y.share`
|
|
62
|
+
// @type {string|null}
|
|
63
|
+
const struct = new Item(
|
|
64
|
+
createID(client, clock),
|
|
65
|
+
null, // left
|
|
66
|
+
(info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null, // origin
|
|
67
|
+
null, // right
|
|
68
|
+
(info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin
|
|
69
|
+
// @ts-ignore Force writing a string here.
|
|
70
|
+
cantCopyParentInfo ? (decoder.readParentInfo() ? decoder.readString() : decoder.readLeftID()) : null, // parent
|
|
71
|
+
cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub
|
|
72
|
+
readItemContent(decoder, info) // item content
|
|
73
|
+
)
|
|
74
|
+
yield struct
|
|
75
|
+
clock += struct.length
|
|
76
|
+
} else {
|
|
77
|
+
const len = decoder.readLen()
|
|
78
|
+
yield new GC(createID(client, clock), len)
|
|
79
|
+
clock += len
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class LazyStructReader {
|
|
86
|
+
/**
|
|
87
|
+
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
|
88
|
+
* @param {boolean} filterSkips
|
|
89
|
+
*/
|
|
90
|
+
constructor (decoder, filterSkips) {
|
|
91
|
+
this.gen = lazyStructReaderGenerator(decoder)
|
|
92
|
+
/**
|
|
93
|
+
* @type {null | Item | Skip | GC}
|
|
94
|
+
*/
|
|
95
|
+
this.curr = null
|
|
96
|
+
this.done = false
|
|
97
|
+
this.filterSkips = filterSkips
|
|
98
|
+
this.next()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @return {Item | GC | Skip |null}
|
|
103
|
+
*/
|
|
104
|
+
next () {
|
|
105
|
+
// ignore "Skip" structs
|
|
106
|
+
do {
|
|
107
|
+
this.curr = this.gen.next().value || null
|
|
108
|
+
} while (this.filterSkips && this.curr !== null && this.curr.constructor === Skip)
|
|
109
|
+
return this.curr
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {Uint8Array} update
|
|
115
|
+
*
|
|
116
|
+
*/
|
|
117
|
+
export const logUpdate = update => logUpdateV2(update, UpdateDecoderV1)
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {Uint8Array} update
|
|
121
|
+
* @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder]
|
|
122
|
+
*/
|
|
123
|
+
export const logUpdateV2 = (update, YDecoder = UpdateDecoderV2) => {
|
|
124
|
+
const structs = []
|
|
125
|
+
const updateDecoder = new YDecoder(decoding.createDecoder(update))
|
|
126
|
+
const lazyDecoder = new LazyStructReader(updateDecoder, false)
|
|
127
|
+
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
128
|
+
structs.push(curr)
|
|
129
|
+
}
|
|
130
|
+
logging.print('Structs: ', structs)
|
|
131
|
+
const ds = readIdSet(updateDecoder)
|
|
132
|
+
logging.print('DeleteSet: ', ds)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @param {Uint8Array} update
|
|
137
|
+
*
|
|
138
|
+
*/
|
|
139
|
+
export const decodeUpdate = (update) => decodeUpdateV2(update, UpdateDecoderV1)
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {Uint8Array} update
|
|
143
|
+
* @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder]
|
|
144
|
+
*
|
|
145
|
+
*/
|
|
146
|
+
export const decodeUpdateV2 = (update, YDecoder = UpdateDecoderV2) => {
|
|
147
|
+
const structs = []
|
|
148
|
+
const updateDecoder = new YDecoder(decoding.createDecoder(update))
|
|
149
|
+
const lazyDecoder = new LazyStructReader(updateDecoder, false)
|
|
150
|
+
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
151
|
+
structs.push(curr)
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
structs,
|
|
155
|
+
ds: readIdSet(updateDecoder)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export class LazyStructWriter {
|
|
160
|
+
/**
|
|
161
|
+
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
162
|
+
*/
|
|
163
|
+
constructor (encoder) {
|
|
164
|
+
this.currClient = 0
|
|
165
|
+
this.startClock = 0
|
|
166
|
+
this.written = 0
|
|
167
|
+
this.encoder = encoder
|
|
168
|
+
/**
|
|
169
|
+
* We want to write operations lazily, but also we need to know beforehand how many operations we want to write for each client.
|
|
170
|
+
*
|
|
171
|
+
* This kind of meta-information (#clients, #structs-per-client-written) is written to the restEncoder.
|
|
172
|
+
*
|
|
173
|
+
* We fragment the restEncoder and store a slice of it per-client until we know how many clients there are.
|
|
174
|
+
* When we flush (toUint8Array) we write the restEncoder using the fragments and the meta-information.
|
|
175
|
+
*
|
|
176
|
+
* @type {Array<{ written: number, restEncoder: Uint8Array }>}
|
|
177
|
+
*/
|
|
178
|
+
this.clientStructs = []
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @param {Array<Uint8Array<ArrayBuffer>>} updates
|
|
184
|
+
* @return {Uint8Array<ArrayBuffer>}
|
|
185
|
+
*/
|
|
186
|
+
export const mergeUpdates = updates => mergeUpdatesV2(updates, UpdateDecoderV1, UpdateEncoderV1)
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {Uint8Array} update
|
|
190
|
+
* @param {typeof IdSetEncoderV1 | typeof IdSetEncoderV2} YEncoder
|
|
191
|
+
* @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} YDecoder
|
|
192
|
+
* @return {Uint8Array}
|
|
193
|
+
*/
|
|
194
|
+
export const encodeStateVectorFromUpdateV2 = (update, YEncoder = IdSetEncoderV2, YDecoder = UpdateDecoderV2) => {
|
|
195
|
+
const encoder = new YEncoder()
|
|
196
|
+
const updateDecoder = new LazyStructReader(new YDecoder(decoding.createDecoder(update)), false)
|
|
197
|
+
let curr = updateDecoder.curr
|
|
198
|
+
if (curr !== null) {
|
|
199
|
+
let size = 0
|
|
200
|
+
let currClient = curr.id.client
|
|
201
|
+
let stopCounting = curr.id.clock !== 0 // must start at 0
|
|
202
|
+
let currClock = stopCounting ? 0 : curr.id.clock + curr.length
|
|
203
|
+
for (; curr !== null; curr = updateDecoder.next()) {
|
|
204
|
+
if (currClient !== curr.id.client) {
|
|
205
|
+
if (currClock !== 0) {
|
|
206
|
+
size++
|
|
207
|
+
// We found a new client
|
|
208
|
+
// write what we have to the encoder
|
|
209
|
+
encoding.writeVarUint(encoder.restEncoder, currClient)
|
|
210
|
+
encoding.writeVarUint(encoder.restEncoder, currClock)
|
|
211
|
+
}
|
|
212
|
+
currClient = curr.id.client
|
|
213
|
+
currClock = 0
|
|
214
|
+
stopCounting = curr.id.clock !== 0
|
|
215
|
+
}
|
|
216
|
+
// we ignore skips
|
|
217
|
+
if (curr.constructor === Skip) {
|
|
218
|
+
stopCounting = true
|
|
219
|
+
}
|
|
220
|
+
if (!stopCounting) {
|
|
221
|
+
currClock = curr.id.clock + curr.length
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// write what we have
|
|
225
|
+
if (currClock !== 0) {
|
|
226
|
+
size++
|
|
227
|
+
encoding.writeVarUint(encoder.restEncoder, currClient)
|
|
228
|
+
encoding.writeVarUint(encoder.restEncoder, currClock)
|
|
229
|
+
}
|
|
230
|
+
// prepend the size of the state vector
|
|
231
|
+
const enc = encoding.createEncoder()
|
|
232
|
+
encoding.writeVarUint(enc, size)
|
|
233
|
+
encoding.writeBinaryEncoder(enc, encoder.restEncoder)
|
|
234
|
+
encoder.restEncoder = enc
|
|
235
|
+
return encoder.toUint8Array()
|
|
236
|
+
} else {
|
|
237
|
+
encoding.writeVarUint(encoder.restEncoder, 0)
|
|
238
|
+
return encoder.toUint8Array()
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {Uint8Array} update
|
|
244
|
+
* @return {Uint8Array}
|
|
245
|
+
*/
|
|
246
|
+
export const encodeStateVectorFromUpdate = update => encodeStateVectorFromUpdateV2(update, IdSetEncoderV1, UpdateDecoderV1)
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @param {Uint8Array} update
|
|
250
|
+
* @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder]
|
|
251
|
+
*/
|
|
252
|
+
export const readUpdateIdRangesV2 = (update, YDecoder = UpdateDecoderV2) => {
|
|
253
|
+
const updateDecoder = new YDecoder(decoding.createDecoder(update))
|
|
254
|
+
const lazyDecoder = new LazyStructReader(updateDecoder, true)
|
|
255
|
+
const inserts = createIdSet()
|
|
256
|
+
let lastClientId = -1
|
|
257
|
+
let lastClock = 0
|
|
258
|
+
let lastLen = 0
|
|
259
|
+
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
260
|
+
const currId = curr.id
|
|
261
|
+
if (lastClientId === currId.client && lastClock + lastLen === currId.clock) {
|
|
262
|
+
// default case: extend prev entry
|
|
263
|
+
lastLen += curr.length
|
|
264
|
+
} else {
|
|
265
|
+
if (lastClientId >= 0) {
|
|
266
|
+
inserts.add(lastClientId, lastClock, lastLen)
|
|
267
|
+
}
|
|
268
|
+
lastClientId = currId.client
|
|
269
|
+
lastClock = currId.clock
|
|
270
|
+
lastLen = curr.length
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (lastClientId >= 0) {
|
|
274
|
+
inserts.add(lastClientId, lastClock, lastLen)
|
|
275
|
+
}
|
|
276
|
+
const deletes = readIdSet(updateDecoder)
|
|
277
|
+
return { inserts, deletes }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @param {Uint8Array} update
|
|
282
|
+
*/
|
|
283
|
+
export const readUpdateIdRanges = update => readUpdateIdRangesV2(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.yxml=true] Whether to obfuscate nodeName / hookName
|
|
590
|
+
*/
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* @param {ObfuscatorOptions} obfuscator
|
|
594
|
+
*/
|
|
595
|
+
const createObfuscator = ({ formatting = true, subdocs = true, yxml = 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 (yxml) {
|
|
619
|
+
const type = /** @type {ContentType} */ (content).type
|
|
620
|
+
if (type instanceof YXmlElement) {
|
|
621
|
+
type.nodeName = map.setIfUndefined(nodeNameCache, type.nodeName, () => 'node-' + i)
|
|
622
|
+
}
|
|
623
|
+
// @ts-ignore
|
|
624
|
+
if (type instanceof YXmlHook) {
|
|
625
|
+
type.hookName = map.setIfUndefined(nodeNameCache, type.hookName, () => 'hook-' + i)
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
break
|
|
629
|
+
}
|
|
630
|
+
case ContentAny: {
|
|
631
|
+
const c = /** @type {ContentAny} */ (content)
|
|
632
|
+
c.arr = c.arr.map(() => i)
|
|
633
|
+
break
|
|
634
|
+
}
|
|
635
|
+
case ContentBinary: {
|
|
636
|
+
const c = /** @type {ContentBinary} */ (content)
|
|
637
|
+
c.content = new Uint8Array([i])
|
|
638
|
+
break
|
|
639
|
+
}
|
|
640
|
+
case ContentDoc: {
|
|
641
|
+
const c = /** @type {ContentDoc} */ (content)
|
|
642
|
+
if (subdocs) {
|
|
643
|
+
c.opts = {}
|
|
644
|
+
c.doc.guid = i + ''
|
|
645
|
+
}
|
|
646
|
+
break
|
|
647
|
+
}
|
|
648
|
+
case ContentEmbed: {
|
|
649
|
+
const c = /** @type {ContentEmbed} */ (content)
|
|
650
|
+
c.embed = {}
|
|
651
|
+
break
|
|
652
|
+
}
|
|
653
|
+
case ContentFormat: {
|
|
654
|
+
const c = /** @type {ContentFormat} */ (content)
|
|
655
|
+
if (formatting) {
|
|
656
|
+
c.key = map.setIfUndefined(formattingKeyCache, c.key, () => i + '')
|
|
657
|
+
c.value = map.setIfUndefined(formattingValueCache, c.value, () => ({ i }))
|
|
658
|
+
}
|
|
659
|
+
break
|
|
660
|
+
}
|
|
661
|
+
case ContentJSON: {
|
|
662
|
+
const c = /** @type {ContentJSON} */ (content)
|
|
663
|
+
c.arr = c.arr.map(() => i)
|
|
664
|
+
break
|
|
665
|
+
}
|
|
666
|
+
case ContentString: {
|
|
667
|
+
const c = /** @type {ContentString} */ (content)
|
|
668
|
+
c.str = string.repeat((i % 10) + '', c.str.length)
|
|
669
|
+
break
|
|
670
|
+
}
|
|
671
|
+
default:
|
|
672
|
+
// unknown content type
|
|
673
|
+
error.unexpectedCase()
|
|
674
|
+
}
|
|
675
|
+
if (item.parentSub) {
|
|
676
|
+
item.parentSub = map.setIfUndefined(mapKeyCache, item.parentSub, () => i + '')
|
|
677
|
+
}
|
|
678
|
+
i++
|
|
679
|
+
return block
|
|
680
|
+
}
|
|
681
|
+
default:
|
|
682
|
+
// unknown block-type
|
|
683
|
+
error.unexpectedCase()
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* This function obfuscates the content of a Yjs update. This is useful to share
|
|
690
|
+
* buggy Yjs documents while significantly limiting the possibility that a
|
|
691
|
+
* developer can on the user. Note that it might still be possible to deduce
|
|
692
|
+
* some information by analyzing the "structure" of the document or by analyzing
|
|
693
|
+
* the typing behavior using the CRDT-related metadata that is still kept fully
|
|
694
|
+
* intact.
|
|
695
|
+
*
|
|
696
|
+
* @param {Uint8Array} update
|
|
697
|
+
* @param {ObfuscatorOptions} [opts]
|
|
698
|
+
*/
|
|
699
|
+
export const obfuscateUpdate = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV1, UpdateEncoderV1)
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* @param {Uint8Array} update
|
|
703
|
+
* @param {ObfuscatorOptions} [opts]
|
|
704
|
+
*/
|
|
705
|
+
export const obfuscateUpdateV2 = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV2, UpdateEncoderV2)
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* @param {Uint8Array} update
|
|
709
|
+
*/
|
|
710
|
+
export const convertUpdateFormatV1ToV2 = update => convertUpdateFormat(update, f.id, UpdateDecoderV1, UpdateEncoderV2)
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* @param {Uint8Array} update
|
|
714
|
+
*/
|
|
715
|
+
export const convertUpdateFormatV2ToV1 = update => convertUpdateFormat(update, f.id, UpdateDecoderV2, UpdateEncoderV1)
|