@y/y 14.0.0-16 → 14.0.0-18
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/{index-DyTeTfmj.js → index-BV-j5wdP.js} +2 -2
- package/dist/{index-R7GxO-36.js.map → index-BV-j5wdP.js.map} +1 -1
- package/dist/{internals.mjs → internals.js} +1 -1
- package/dist/internals.js.map +1 -0
- package/dist/{testHelper.mjs → testHelper.js} +2 -2
- package/dist/testHelper.js.map +1 -0
- package/dist/{yjs.mjs → yjs.js} +2 -2
- package/dist/yjs.js.map +1 -0
- package/package.json +9 -18
- package/dist/Skip-j0kX7pdq.js +0 -12173
- package/dist/Skip-j0kX7pdq.js.map +0 -1
- package/dist/index-DyTeTfmj.js.map +0 -1
- package/dist/index-R7GxO-36.js +0 -165
- package/dist/internals.cjs +0 -286
- package/dist/internals.cjs.map +0 -1
- package/dist/internals.mjs.map +0 -1
- package/dist/testHelper.cjs +0 -780
- package/dist/testHelper.cjs.map +0 -1
- package/dist/testHelper.mjs.map +0 -1
- package/dist/yjs.cjs +0 -151
- package/dist/yjs.cjs.map +0 -1
- package/dist/yjs.mjs.map +0 -1
- package/src/index.js +0 -153
- package/src/internals.js +0 -44
- package/src/structs/AbstractStruct.js +0 -59
- package/src/structs/ContentAny.js +0 -115
- package/src/structs/ContentBinary.js +0 -93
- package/src/structs/ContentDeleted.js +0 -101
- package/src/structs/ContentDoc.js +0 -141
- package/src/structs/ContentEmbed.js +0 -98
- package/src/structs/ContentFormat.js +0 -105
- package/src/structs/ContentJSON.js +0 -119
- package/src/structs/ContentString.js +0 -113
- package/src/structs/ContentType.js +0 -176
- package/src/structs/GC.js +0 -80
- package/src/structs/Item.js +0 -845
- package/src/structs/Skip.js +0 -75
- package/src/types/AbstractType.js +0 -1434
- package/src/types/YArray.js +0 -270
- package/src/types/YMap.js +0 -244
- package/src/types/YText.js +0 -934
- package/src/types/YXmlElement.js +0 -227
- package/src/types/YXmlFragment.js +0 -266
- package/src/types/YXmlHook.js +0 -68
- package/src/types/YXmlText.js +0 -66
- package/src/utils/AbstractConnector.js +0 -25
- package/src/utils/AttributionManager.js +0 -619
- package/src/utils/Doc.js +0 -372
- package/src/utils/EventHandler.js +0 -87
- package/src/utils/ID.js +0 -89
- package/src/utils/IdMap.js +0 -629
- package/src/utils/IdSet.js +0 -823
- package/src/utils/RelativePosition.js +0 -352
- package/src/utils/Snapshot.js +0 -220
- package/src/utils/StructSet.js +0 -137
- package/src/utils/StructStore.js +0 -289
- package/src/utils/Transaction.js +0 -489
- package/src/utils/UndoManager.js +0 -391
- package/src/utils/UpdateDecoder.js +0 -281
- package/src/utils/UpdateEncoder.js +0 -320
- package/src/utils/YEvent.js +0 -216
- package/src/utils/delta-helpers.js +0 -54
- package/src/utils/encoding.js +0 -623
- package/src/utils/isParentOf.js +0 -21
- package/src/utils/logging.js +0 -21
- package/src/utils/types.js +0 -28
- package/src/utils/updates.js +0 -715
- package/tests/testHelper.js +0 -600
package/src/utils/Transaction.js
DELETED
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getState,
|
|
3
|
-
writeStructsFromTransaction,
|
|
4
|
-
writeIdSet,
|
|
5
|
-
getStateVector,
|
|
6
|
-
findIndexSS,
|
|
7
|
-
callEventHandlerListeners,
|
|
8
|
-
createIdSet,
|
|
9
|
-
Item,
|
|
10
|
-
generateNewClientId,
|
|
11
|
-
createID,
|
|
12
|
-
cleanupYTextAfterTransaction,
|
|
13
|
-
IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
|
|
14
|
-
} from '../internals.js'
|
|
15
|
-
|
|
16
|
-
import * as error from 'lib0/error'
|
|
17
|
-
import * as map from 'lib0/map'
|
|
18
|
-
import * as math from 'lib0/math'
|
|
19
|
-
import * as set from 'lib0/set'
|
|
20
|
-
import * as logging from 'lib0/logging'
|
|
21
|
-
import { callAll } from 'lib0/function'
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* A transaction is created for every change on the Yjs model. It is possible
|
|
25
|
-
* to bundle changes on the Yjs model in a single transaction to
|
|
26
|
-
* minimize the number on messages sent and the number of observer calls.
|
|
27
|
-
* If possible the user of this library should bundle as many changes as
|
|
28
|
-
* possible. Here is an example to illustrate the advantages of bundling:
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* const ydoc = new Y.Doc()
|
|
32
|
-
* const map = ydoc.getMap('map')
|
|
33
|
-
* // Log content when change is triggered
|
|
34
|
-
* map.observe(() => {
|
|
35
|
-
* console.log('change triggered')
|
|
36
|
-
* })
|
|
37
|
-
* // Each change on the map type triggers a log message:
|
|
38
|
-
* map.set('a', 0) // => "change triggered"
|
|
39
|
-
* map.set('b', 0) // => "change triggered"
|
|
40
|
-
* // When put in a transaction, it will trigger the log after the transaction:
|
|
41
|
-
* ydoc.transact(() => {
|
|
42
|
-
* map.set('a', 1)
|
|
43
|
-
* map.set('b', 1)
|
|
44
|
-
* }) // => "change triggered"
|
|
45
|
-
*
|
|
46
|
-
* @public
|
|
47
|
-
*/
|
|
48
|
-
export class Transaction {
|
|
49
|
-
/**
|
|
50
|
-
* @param {Doc} doc
|
|
51
|
-
* @param {any} origin
|
|
52
|
-
* @param {boolean} local
|
|
53
|
-
*/
|
|
54
|
-
constructor (doc, origin, local) {
|
|
55
|
-
/**
|
|
56
|
-
* The Yjs instance.
|
|
57
|
-
* @type {Doc}
|
|
58
|
-
*/
|
|
59
|
-
this.doc = doc
|
|
60
|
-
/**
|
|
61
|
-
* Describes the set of deleted items by ids
|
|
62
|
-
*/
|
|
63
|
-
this.deleteSet = createIdSet()
|
|
64
|
-
/**
|
|
65
|
-
* Describes the set of items that are cleaned up / deleted by ids. It is a subset of
|
|
66
|
-
* this.deleteSet
|
|
67
|
-
*/
|
|
68
|
-
this.cleanUps = createIdSet()
|
|
69
|
-
/**
|
|
70
|
-
* Describes the set of inserted items by ids
|
|
71
|
-
*/
|
|
72
|
-
this.insertSet = createIdSet()
|
|
73
|
-
/**
|
|
74
|
-
* Holds the state before the transaction started.
|
|
75
|
-
* @type {Map<Number,Number>?}
|
|
76
|
-
*/
|
|
77
|
-
this._beforeState = null
|
|
78
|
-
/**
|
|
79
|
-
* Holds the state after the transaction.
|
|
80
|
-
* @type {Map<Number,Number>?}
|
|
81
|
-
*/
|
|
82
|
-
this._afterState = null
|
|
83
|
-
/**
|
|
84
|
-
* All types that were directly modified (property added or child
|
|
85
|
-
* inserted/deleted). New types are not included in this Set.
|
|
86
|
-
* Maps from type to parentSubs (`item.parentSub = null` for YArray)
|
|
87
|
-
* @type {Map<import('../utils/types.js').YType,Set<String|null>>}
|
|
88
|
-
*/
|
|
89
|
-
this.changed = new Map()
|
|
90
|
-
/**
|
|
91
|
-
* Stores the events for the types that observe also child elements.
|
|
92
|
-
* It is mainly used by `observeDeep`.
|
|
93
|
-
* @type {Map<import('../utils/types.js').YType,Array<YEvent<any>>>}
|
|
94
|
-
*/
|
|
95
|
-
this.changedParentTypes = new Map()
|
|
96
|
-
/**
|
|
97
|
-
* @type {Array<AbstractStruct>}
|
|
98
|
-
*/
|
|
99
|
-
this._mergeStructs = []
|
|
100
|
-
/**
|
|
101
|
-
* @type {any}
|
|
102
|
-
*/
|
|
103
|
-
this.origin = origin
|
|
104
|
-
/**
|
|
105
|
-
* Stores meta information on the transaction
|
|
106
|
-
* @type {Map<any,any>}
|
|
107
|
-
*/
|
|
108
|
-
this.meta = new Map()
|
|
109
|
-
/**
|
|
110
|
-
* Whether this change originates from this doc.
|
|
111
|
-
* @type {boolean}
|
|
112
|
-
*/
|
|
113
|
-
this.local = local
|
|
114
|
-
/**
|
|
115
|
-
* @type {Set<Doc>}
|
|
116
|
-
*/
|
|
117
|
-
this.subdocsAdded = new Set()
|
|
118
|
-
/**
|
|
119
|
-
* @type {Set<Doc>}
|
|
120
|
-
*/
|
|
121
|
-
this.subdocsRemoved = new Set()
|
|
122
|
-
/**
|
|
123
|
-
* @type {Set<Doc>}
|
|
124
|
-
*/
|
|
125
|
-
this.subdocsLoaded = new Set()
|
|
126
|
-
/**
|
|
127
|
-
* @type {boolean}
|
|
128
|
-
*/
|
|
129
|
-
this._needFormattingCleanup = false
|
|
130
|
-
this._done = false
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Holds the state before the transaction started.
|
|
135
|
-
*
|
|
136
|
-
* @deprecated
|
|
137
|
-
* @type {Map<Number,Number>}
|
|
138
|
-
*/
|
|
139
|
-
get beforeState () {
|
|
140
|
-
if (this._beforeState == null) {
|
|
141
|
-
const sv = getStateVector(this.doc.store)
|
|
142
|
-
this.insertSet.clients.forEach((ranges, client) => {
|
|
143
|
-
sv.set(client, ranges.getIds()[0].clock)
|
|
144
|
-
})
|
|
145
|
-
this._beforeState = sv
|
|
146
|
-
}
|
|
147
|
-
return this._beforeState
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Holds the state after the transaction.
|
|
152
|
-
*
|
|
153
|
-
* @deprecated
|
|
154
|
-
* @type {Map<Number,Number>}
|
|
155
|
-
*/
|
|
156
|
-
get afterState () {
|
|
157
|
-
if (!this._done) error.unexpectedCase()
|
|
158
|
-
if (this._afterState == null) {
|
|
159
|
-
const sv = getStateVector(this.doc.store)
|
|
160
|
-
this.insertSet.clients.forEach((_ranges, client) => {
|
|
161
|
-
const ranges = _ranges.getIds()
|
|
162
|
-
const d = ranges[ranges.length - 1]
|
|
163
|
-
sv.set(client, d.clock + d.len)
|
|
164
|
-
})
|
|
165
|
-
this._afterState = sv
|
|
166
|
-
}
|
|
167
|
-
return this._afterState
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
173
|
-
* @param {Transaction} transaction
|
|
174
|
-
* @return {boolean} Whether data was written.
|
|
175
|
-
*/
|
|
176
|
-
export const writeUpdateMessageFromTransaction = (encoder, transaction) => {
|
|
177
|
-
if (transaction.deleteSet.clients.size === 0 && transaction.insertSet.clients.size === 0) {
|
|
178
|
-
return false
|
|
179
|
-
}
|
|
180
|
-
writeStructsFromTransaction(encoder, transaction)
|
|
181
|
-
writeIdSet(encoder, transaction.deleteSet)
|
|
182
|
-
return true
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* @param {Transaction} transaction
|
|
187
|
-
*
|
|
188
|
-
* @private
|
|
189
|
-
* @function
|
|
190
|
-
*/
|
|
191
|
-
export const nextID = transaction => {
|
|
192
|
-
const y = transaction.doc
|
|
193
|
-
return createID(y.clientID, getState(y.store, y.clientID))
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* If `type.parent` was added in current transaction, `type` technically
|
|
198
|
-
* did not change, it was just added and we should not fire events for `type`.
|
|
199
|
-
*
|
|
200
|
-
* @param {Transaction} transaction
|
|
201
|
-
* @param {import('../utils/types.js').YType} type
|
|
202
|
-
* @param {string|null} parentSub
|
|
203
|
-
*/
|
|
204
|
-
export const addChangedTypeToTransaction = (transaction, type, parentSub) => {
|
|
205
|
-
const item = type._item
|
|
206
|
-
if (item === null || (!item.deleted && !transaction.insertSet.hasId(item.id))) {
|
|
207
|
-
map.setIfUndefined(transaction.changed, type, set.create).add(parentSub)
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* @param {Array<AbstractStruct>} structs
|
|
213
|
-
* @param {number} pos
|
|
214
|
-
* @return {number} # of merged structs
|
|
215
|
-
*/
|
|
216
|
-
const tryToMergeWithLefts = (structs, pos) => {
|
|
217
|
-
let right = structs[pos]
|
|
218
|
-
let left = structs[pos - 1]
|
|
219
|
-
let i = pos
|
|
220
|
-
for (; i > 0; right = left, left = structs[--i - 1]) {
|
|
221
|
-
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
|
222
|
-
if (left.mergeWith(right)) {
|
|
223
|
-
if (right instanceof Item && right.parentSub !== null && /** @type {AbstractType<any>} */ (right.parent)._map.get(right.parentSub) === right) {
|
|
224
|
-
/** @type {AbstractType<any>} */ (right.parent)._map.set(right.parentSub, /** @type {Item} */ (left))
|
|
225
|
-
}
|
|
226
|
-
continue
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
break
|
|
230
|
-
}
|
|
231
|
-
const merged = pos - i
|
|
232
|
-
if (merged) {
|
|
233
|
-
// remove all merged structs from the array
|
|
234
|
-
structs.splice(pos + 1 - merged, merged)
|
|
235
|
-
}
|
|
236
|
-
return merged
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* @param {Transaction} tr
|
|
241
|
-
* @param {IdSet} ds
|
|
242
|
-
* @param {function(Item):boolean} gcFilter
|
|
243
|
-
*/
|
|
244
|
-
const tryGcDeleteSet = (tr, ds, gcFilter) => {
|
|
245
|
-
for (const [client, _deleteItems] of ds.clients.entries()) {
|
|
246
|
-
const deleteItems = _deleteItems.getIds()
|
|
247
|
-
const structs = /** @type {Array<GC|Item>} */ (tr.doc.store.clients.get(client))
|
|
248
|
-
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
|
249
|
-
const deleteItem = deleteItems[di]
|
|
250
|
-
const endDeleteItemClock = deleteItem.clock + deleteItem.len
|
|
251
|
-
for (
|
|
252
|
-
let si = findIndexSS(structs, deleteItem.clock), struct = structs[si];
|
|
253
|
-
si < structs.length && struct.id.clock < endDeleteItemClock;
|
|
254
|
-
struct = structs[++si]
|
|
255
|
-
) {
|
|
256
|
-
const struct = structs[si]
|
|
257
|
-
if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
|
|
258
|
-
break
|
|
259
|
-
}
|
|
260
|
-
if (struct instanceof Item && struct.deleted && !struct.keep && gcFilter(struct)) {
|
|
261
|
-
struct.gc(tr, false)
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* @param {IdSet} ds
|
|
270
|
-
* @param {StructStore} store
|
|
271
|
-
*/
|
|
272
|
-
const tryMerge = (ds, store) => {
|
|
273
|
-
// try to merge deleted / gc'd items
|
|
274
|
-
// merge from right to left for better efficiency and so we don't miss any merge targets
|
|
275
|
-
ds.clients.forEach((_deleteItems, client) => {
|
|
276
|
-
const deleteItems = _deleteItems.getIds()
|
|
277
|
-
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
|
|
278
|
-
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
|
279
|
-
const deleteItem = deleteItems[di]
|
|
280
|
-
// start with merging the item next to the last deleted item
|
|
281
|
-
const mostRightIndexToCheck = math.min(structs.length - 1, 1 + findIndexSS(structs, deleteItem.clock + deleteItem.len - 1))
|
|
282
|
-
for (
|
|
283
|
-
let si = mostRightIndexToCheck, struct = structs[si];
|
|
284
|
-
si > 0 && struct.id.clock >= deleteItem.clock;
|
|
285
|
-
struct = structs[si]
|
|
286
|
-
) {
|
|
287
|
-
si -= 1 + tryToMergeWithLefts(structs, si)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
})
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* @param {Transaction} tr
|
|
295
|
-
* @param {IdSet} idset
|
|
296
|
-
* @param {function(Item):boolean} gcFilter
|
|
297
|
-
*/
|
|
298
|
-
export const tryGc = (tr, idset, gcFilter) => {
|
|
299
|
-
tryGcDeleteSet(tr, idset, gcFilter)
|
|
300
|
-
tryMerge(idset, tr.doc.store)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* @param {Array<Transaction>} transactionCleanups
|
|
305
|
-
* @param {number} i
|
|
306
|
-
*/
|
|
307
|
-
const cleanupTransactions = (transactionCleanups, i) => {
|
|
308
|
-
if (i < transactionCleanups.length) {
|
|
309
|
-
const transaction = transactionCleanups[i]
|
|
310
|
-
transaction._done = true
|
|
311
|
-
const doc = transaction.doc
|
|
312
|
-
const store = doc.store
|
|
313
|
-
const ds = transaction.deleteSet
|
|
314
|
-
const mergeStructs = transaction._mergeStructs
|
|
315
|
-
// insertIntoIdSet(store.ds, ds)
|
|
316
|
-
try {
|
|
317
|
-
doc.emit('beforeObserverCalls', [transaction, doc])
|
|
318
|
-
/**
|
|
319
|
-
* An array of event callbacks.
|
|
320
|
-
*
|
|
321
|
-
* Each callback is called even if the other ones throw errors.
|
|
322
|
-
*
|
|
323
|
-
* @type {Array<function():void>}
|
|
324
|
-
*/
|
|
325
|
-
const fs = []
|
|
326
|
-
// observe events on changed types
|
|
327
|
-
transaction.changed.forEach((subs, itemtype) =>
|
|
328
|
-
fs.push(() => {
|
|
329
|
-
if (itemtype._item === null || !itemtype._item.deleted) {
|
|
330
|
-
itemtype._callObserver(transaction, subs)
|
|
331
|
-
}
|
|
332
|
-
})
|
|
333
|
-
)
|
|
334
|
-
fs.push(() => {
|
|
335
|
-
// deep observe events
|
|
336
|
-
transaction.changedParentTypes.forEach((events, type) => {
|
|
337
|
-
// We need to think about the possibility that the user transforms the
|
|
338
|
-
// Y.Doc in the event.
|
|
339
|
-
if (type._dEH.l.length > 0 && (type._item === null || !type._item.deleted)) {
|
|
340
|
-
events = events
|
|
341
|
-
.filter(event =>
|
|
342
|
-
event.target._item === null || !event.target._item.deleted
|
|
343
|
-
)
|
|
344
|
-
events
|
|
345
|
-
.forEach(event => {
|
|
346
|
-
event.currentTarget = type
|
|
347
|
-
// path is relative to the current target
|
|
348
|
-
event._path = null
|
|
349
|
-
})
|
|
350
|
-
// sort events by path length so that top-level events are fired first.
|
|
351
|
-
events
|
|
352
|
-
.sort((event1, event2) => event1.path.length - event2.path.length)
|
|
353
|
-
// We don't need to check for events.length
|
|
354
|
-
// because we know it has at least one element
|
|
355
|
-
callEventHandlerListeners(type._dEH, events, transaction)
|
|
356
|
-
}
|
|
357
|
-
})
|
|
358
|
-
})
|
|
359
|
-
fs.push(() => doc.emit('afterTransaction', [transaction, doc]))
|
|
360
|
-
callAll(fs, [])
|
|
361
|
-
if (transaction._needFormattingCleanup && doc.cleanupFormatting) {
|
|
362
|
-
cleanupYTextAfterTransaction(transaction)
|
|
363
|
-
}
|
|
364
|
-
} finally {
|
|
365
|
-
// Replace deleted items with ItemDeleted / GC.
|
|
366
|
-
// This is where content is actually remove from the Yjs Doc.
|
|
367
|
-
if (doc.gc) {
|
|
368
|
-
tryGcDeleteSet(transaction, ds, doc.gcFilter)
|
|
369
|
-
}
|
|
370
|
-
tryMerge(ds, store)
|
|
371
|
-
|
|
372
|
-
// on all affected store.clients props, try to merge
|
|
373
|
-
transaction.insertSet.clients.forEach((ids, client) => {
|
|
374
|
-
const firstClock = ids.getIds()[0].clock
|
|
375
|
-
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
|
|
376
|
-
// we iterate from right to left so we can safely remove entries
|
|
377
|
-
const firstChangePos = math.max(findIndexSS(structs, firstClock), 1)
|
|
378
|
-
for (let i = structs.length - 1; i >= firstChangePos;) {
|
|
379
|
-
i -= 1 + tryToMergeWithLefts(structs, i)
|
|
380
|
-
}
|
|
381
|
-
})
|
|
382
|
-
// try to merge mergeStructs
|
|
383
|
-
// @todo: it makes more sense to transform mergeStructs to a DS, sort it, and merge from right to left
|
|
384
|
-
// but at the moment DS does not handle duplicates
|
|
385
|
-
for (let i = mergeStructs.length - 1; i >= 0; i--) {
|
|
386
|
-
const { client, clock } = mergeStructs[i].id
|
|
387
|
-
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
|
|
388
|
-
const replacedStructPos = findIndexSS(structs, clock)
|
|
389
|
-
if (replacedStructPos + 1 < structs.length) {
|
|
390
|
-
if (tryToMergeWithLefts(structs, replacedStructPos + 1) > 1) {
|
|
391
|
-
continue // no need to perform next check, both are already merged
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if (replacedStructPos > 0) {
|
|
395
|
-
tryToMergeWithLefts(structs, replacedStructPos)
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
if (!transaction.local && transaction.insertSet.clients.has(doc.clientID)) {
|
|
399
|
-
logging.print(logging.ORANGE, logging.BOLD, '[yjs] ', logging.UNBOLD, logging.RED, 'Changed the client-id because another client seems to be using it.')
|
|
400
|
-
doc.clientID = generateNewClientId()
|
|
401
|
-
}
|
|
402
|
-
// @todo Merge all the transactions into one and provide send the data as a single update message
|
|
403
|
-
doc.emit('afterTransactionCleanup', [transaction, doc])
|
|
404
|
-
if (doc._observers.has('update')) {
|
|
405
|
-
const encoder = new UpdateEncoderV1()
|
|
406
|
-
const hasContent = writeUpdateMessageFromTransaction(encoder, transaction)
|
|
407
|
-
if (hasContent) {
|
|
408
|
-
doc.emit('update', [encoder.toUint8Array(), transaction.origin, doc, transaction])
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
if (doc._observers.has('updateV2')) {
|
|
412
|
-
const encoder = new UpdateEncoderV2()
|
|
413
|
-
const hasContent = writeUpdateMessageFromTransaction(encoder, transaction)
|
|
414
|
-
if (hasContent) {
|
|
415
|
-
doc.emit('updateV2', [encoder.toUint8Array(), transaction.origin, doc, transaction])
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
const { subdocsAdded, subdocsLoaded, subdocsRemoved } = transaction
|
|
419
|
-
if (subdocsAdded.size > 0 || subdocsRemoved.size > 0 || subdocsLoaded.size > 0) {
|
|
420
|
-
subdocsAdded.forEach(subdoc => {
|
|
421
|
-
subdoc.clientID = doc.clientID
|
|
422
|
-
if (subdoc.collectionid == null) {
|
|
423
|
-
subdoc.collectionid = doc.collectionid
|
|
424
|
-
}
|
|
425
|
-
doc.subdocs.add(subdoc)
|
|
426
|
-
})
|
|
427
|
-
subdocsRemoved.forEach(subdoc => doc.subdocs.delete(subdoc))
|
|
428
|
-
doc.emit('subdocs', [{ loaded: subdocsLoaded, added: subdocsAdded, removed: subdocsRemoved }, doc, transaction])
|
|
429
|
-
subdocsRemoved.forEach(subdoc => subdoc.destroy())
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (transactionCleanups.length <= i + 1) {
|
|
433
|
-
doc._transactionCleanups = []
|
|
434
|
-
doc.emit('afterAllTransactions', [doc, transactionCleanups])
|
|
435
|
-
} else {
|
|
436
|
-
cleanupTransactions(transactionCleanups, i + 1)
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Implements the functionality of `y.transact(()=>{..})`
|
|
444
|
-
*
|
|
445
|
-
* @template T
|
|
446
|
-
* @param {Doc} doc
|
|
447
|
-
* @param {function(Transaction):T} f
|
|
448
|
-
* @param {any} [origin=true]
|
|
449
|
-
* @return {T}
|
|
450
|
-
*
|
|
451
|
-
* @function
|
|
452
|
-
*/
|
|
453
|
-
export const transact = (doc, f, origin = null, local = true) => {
|
|
454
|
-
const transactionCleanups = doc._transactionCleanups
|
|
455
|
-
let initialCall = false
|
|
456
|
-
/**
|
|
457
|
-
* @type {any}
|
|
458
|
-
*/
|
|
459
|
-
let result = null
|
|
460
|
-
if (doc._transaction === null) {
|
|
461
|
-
initialCall = true
|
|
462
|
-
doc._transaction = new Transaction(doc, origin, local)
|
|
463
|
-
transactionCleanups.push(doc._transaction)
|
|
464
|
-
if (transactionCleanups.length === 1) {
|
|
465
|
-
doc.emit('beforeAllTransactions', [doc])
|
|
466
|
-
}
|
|
467
|
-
doc.emit('beforeTransaction', [doc._transaction, doc])
|
|
468
|
-
}
|
|
469
|
-
try {
|
|
470
|
-
result = f(doc._transaction)
|
|
471
|
-
} finally {
|
|
472
|
-
if (initialCall) {
|
|
473
|
-
const finishCleanup = doc._transaction === transactionCleanups[0]
|
|
474
|
-
doc._transaction = null
|
|
475
|
-
if (finishCleanup) {
|
|
476
|
-
// The first transaction ended, now process observer calls.
|
|
477
|
-
// Observer call may create new transactions for which we need to call the observers and do cleanup.
|
|
478
|
-
// We don't want to nest these calls, so we execute these calls one after
|
|
479
|
-
// another.
|
|
480
|
-
// Also we need to ensure that all cleanups are called, even if the
|
|
481
|
-
// observes throw errors.
|
|
482
|
-
// This file is full of hacky try {} finally {} blocks to ensure that an
|
|
483
|
-
// event can throw errors and also that the cleanup is called.
|
|
484
|
-
cleanupTransactions(transactionCleanups, 0)
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
return result
|
|
489
|
-
}
|