mobx-keystone-yjs 1.5.4 → 1.6.0
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/CHANGELOG.md +57 -45
- package/dist/mobx-keystone-yjs.esm.js +475 -299
- package/dist/mobx-keystone-yjs.esm.mjs +475 -299
- package/dist/mobx-keystone-yjs.umd.js +475 -299
- package/dist/types/binding/YjsTextModel.d.ts +5 -4
- package/dist/types/binding/applyMobxChangeToYjsObject.d.ts +3 -0
- package/dist/types/binding/applyYjsEventToMobx.d.ts +8 -0
- package/dist/types/binding/bindYjsToMobxKeystone.d.ts +1 -1
- package/dist/types/binding/convertJsonToYjsData.d.ts +23 -4
- package/dist/types/binding/convertYjsDataToJson.d.ts +1 -1
- package/dist/types/binding/resolveYjsPath.d.ts +14 -1
- package/dist/types/binding/yjsBindingContext.d.ts +2 -2
- package/dist/types/binding/yjsSnapshotTracking.d.ts +24 -0
- package/dist/types/index.d.ts +7 -6
- package/dist/types/utils/isYjsValueDeleted.d.ts +7 -0
- package/package.json +90 -78
- package/src/binding/YjsTextModel.ts +280 -247
- package/src/binding/applyMobxChangeToYjsObject.ts +77 -0
- package/src/binding/applyYjsEventToMobx.ts +173 -0
- package/src/binding/bindYjsToMobxKeystone.ts +300 -192
- package/src/binding/convertJsonToYjsData.ts +218 -76
- package/src/binding/convertYjsDataToJson.ts +1 -1
- package/src/binding/resolveYjsPath.ts +51 -27
- package/src/binding/yjsSnapshotTracking.ts +40 -0
- package/src/index.ts +11 -10
- package/src/utils/getOrCreateYjsCollectionAtom.ts +27 -27
- package/src/utils/isYjsValueDeleted.ts +14 -0
- package/dist/types/binding/applyMobxKeystonePatchToYjsObject.d.ts +0 -2
- package/dist/types/binding/convertYjsEventToPatches.d.ts +0 -3
- package/src/binding/applyMobxKeystonePatchToYjsObject.ts +0 -98
- package/src/binding/convertYjsEventToPatches.ts +0 -92
|
@@ -1,247 +1,280 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
Frozen,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
model,
|
|
8
|
-
onSnapshot,
|
|
9
|
-
tProp,
|
|
10
|
-
types,
|
|
11
|
-
} from "mobx-keystone"
|
|
12
|
-
import * as Y from "yjs"
|
|
13
|
-
import { failure } from "../utils/error"
|
|
14
|
-
import {
|
|
15
|
-
import { resolveYjsPath } from "./resolveYjsPath"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
*
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
1
|
+
import { computed, createAtom, IAtom, observe, reaction } from "mobx"
|
|
2
|
+
import {
|
|
3
|
+
Frozen,
|
|
4
|
+
frozen,
|
|
5
|
+
getParentToChildPath,
|
|
6
|
+
Model,
|
|
7
|
+
model,
|
|
8
|
+
onSnapshot,
|
|
9
|
+
tProp,
|
|
10
|
+
types,
|
|
11
|
+
} from "mobx-keystone"
|
|
12
|
+
import * as Y from "yjs"
|
|
13
|
+
import { failure } from "../utils/error"
|
|
14
|
+
import { isYjsValueDeleted } from "../utils/isYjsValueDeleted"
|
|
15
|
+
import { resolveYjsPath } from "./resolveYjsPath"
|
|
16
|
+
import { YjsBindingContext, yjsBindingContext } from "./yjsBindingContext"
|
|
17
|
+
|
|
18
|
+
// Delta[][], since each single change is a Delta[]
|
|
19
|
+
// we use frozen so that we can reuse each delta change
|
|
20
|
+
const deltaListType = types.array(types.frozen(types.unchecked<unknown[]>()))
|
|
21
|
+
|
|
22
|
+
export const yjsTextModelId = "mobx-keystone-yjs/YjsTextModel"
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A mobx-keystone model that represents a Yjs.Text object.
|
|
26
|
+
*/
|
|
27
|
+
@model(yjsTextModelId)
|
|
28
|
+
export class YjsTextModel extends Model({
|
|
29
|
+
deltaList: tProp(deltaListType, () => []),
|
|
30
|
+
}) {
|
|
31
|
+
/**
|
|
32
|
+
* Helper function to create a YjsTextModel instance with a simple text.
|
|
33
|
+
*/
|
|
34
|
+
static withText(text: string): YjsTextModel {
|
|
35
|
+
return new DecoratedYjsTextModel({
|
|
36
|
+
deltaList: [
|
|
37
|
+
frozen([
|
|
38
|
+
{
|
|
39
|
+
insert: text,
|
|
40
|
+
},
|
|
41
|
+
]),
|
|
42
|
+
],
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The Y.js path from the bound object to the YjsTextModel instance.
|
|
48
|
+
*/
|
|
49
|
+
@computed
|
|
50
|
+
private get _yjsObjectPath() {
|
|
51
|
+
const ctx = yjsBindingContext.get(this)
|
|
52
|
+
if (ctx?.boundObject == null) {
|
|
53
|
+
throw failure(
|
|
54
|
+
"the YjsTextModel instance must be part of a bound object before it can be accessed"
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const path = getParentToChildPath(ctx.boundObject, this)
|
|
59
|
+
if (!path) {
|
|
60
|
+
throw failure("a path from the bound object to the YjsTextModel instance is not available")
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return path
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The Yjs.Text object present at this mobx-keystone node's path.
|
|
68
|
+
*/
|
|
69
|
+
@computed
|
|
70
|
+
private get _yjsObjectAtPath(): unknown {
|
|
71
|
+
const path = this._yjsObjectPath
|
|
72
|
+
|
|
73
|
+
const ctx = yjsBindingContext.get(this)!
|
|
74
|
+
|
|
75
|
+
return resolveYjsPath(ctx.yjsObject, path)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The Yjs.Text object represented by this mobx-keystone node.
|
|
80
|
+
*/
|
|
81
|
+
@computed
|
|
82
|
+
get yjsText(): Y.Text {
|
|
83
|
+
const yjsObject = this._yjsObjectAtPath
|
|
84
|
+
|
|
85
|
+
if (!(yjsObject instanceof Y.Text)) {
|
|
86
|
+
throw failure(`Y.Text was expected at path ${JSON.stringify(this._yjsObjectPath)}`)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return yjsObject
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Atom that gets changed when the associated Y.js text changes.
|
|
94
|
+
*/
|
|
95
|
+
yjsTextChangedAtom = createAtom("yjsTextChangedAtom")
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The text value of the Yjs.Text object.
|
|
99
|
+
* Shortcut for `yjsText.toString()`, but computed.
|
|
100
|
+
*/
|
|
101
|
+
@computed
|
|
102
|
+
get text(): string {
|
|
103
|
+
this.yjsTextChangedAtom.reportObserved()
|
|
104
|
+
|
|
105
|
+
const ctx = yjsBindingContext.get(this)
|
|
106
|
+
if (ctx?.boundObject != null) {
|
|
107
|
+
try {
|
|
108
|
+
const yjsTextString = this.yjsText.toString()
|
|
109
|
+
// if the yjsText is detached, toString() returns an empty string
|
|
110
|
+
// in that case we should use the deltaList as a fallback
|
|
111
|
+
if (yjsTextString !== "" || this.deltaList.length === 0) {
|
|
112
|
+
return yjsTextString
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// fall back
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// fall back to deltaList
|
|
120
|
+
return this.deltaListToText()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private deltaListToText(): string {
|
|
124
|
+
const doc = new Y.Doc()
|
|
125
|
+
const text = doc.getText()
|
|
126
|
+
this.deltaList.forEach((d) => {
|
|
127
|
+
text.applyDelta(d.data)
|
|
128
|
+
})
|
|
129
|
+
return text.toString()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
protected onInit() {
|
|
133
|
+
const shouldReplicateToYjs = (ctx: YjsBindingContext | undefined): ctx is YjsBindingContext => {
|
|
134
|
+
return !!ctx && !!ctx.boundObject && !ctx.isApplyingYjsChangesToMobxKeystone
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let reapplyDeltasToYjsText = false
|
|
138
|
+
const newDeltas: Frozen<unknown[]>[] = []
|
|
139
|
+
|
|
140
|
+
let disposeObserveDeltaList: (() => void) | undefined
|
|
141
|
+
|
|
142
|
+
const disposeReactionToDeltaListRefChange = reaction(
|
|
143
|
+
() => this.$.deltaList,
|
|
144
|
+
(deltaList) => {
|
|
145
|
+
disposeObserveDeltaList?.()
|
|
146
|
+
disposeObserveDeltaList = undefined
|
|
147
|
+
|
|
148
|
+
disposeObserveDeltaList = observe(deltaList, (change) => {
|
|
149
|
+
if (reapplyDeltasToYjsText) {
|
|
150
|
+
// already gonna replace them all
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
if (!shouldReplicateToYjs(yjsBindingContext.get(this))) {
|
|
154
|
+
// yjs text is already up to date with these changes
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
change.type === "splice" &&
|
|
160
|
+
change.removedCount === 0 &&
|
|
161
|
+
change.addedCount > 0 &&
|
|
162
|
+
change.index === this.deltaList.length
|
|
163
|
+
) {
|
|
164
|
+
// optimization, just adding new ones to the end
|
|
165
|
+
newDeltas.push(...change.added)
|
|
166
|
+
} else {
|
|
167
|
+
// any other change, we need to reapply all deltas
|
|
168
|
+
reapplyDeltasToYjsText = true
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
},
|
|
172
|
+
{ fireImmediately: true }
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
const disposeOnSnapshot = onSnapshot(this, () => {
|
|
176
|
+
try {
|
|
177
|
+
if (reapplyDeltasToYjsText) {
|
|
178
|
+
const ctx = yjsBindingContext.get(this)
|
|
179
|
+
|
|
180
|
+
if (shouldReplicateToYjs(ctx)) {
|
|
181
|
+
const { yjsText } = this
|
|
182
|
+
if (isYjsValueDeleted(yjsText)) {
|
|
183
|
+
throw failure("cannot reapply deltas to deleted Yjs.Text")
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
ctx.yjsDoc.transact(() => {
|
|
187
|
+
// didn't find a better way than this to reapply all deltas
|
|
188
|
+
// without having to re-create the Y.Text object
|
|
189
|
+
if (yjsText.length > 0) {
|
|
190
|
+
yjsText.delete(0, yjsText.length)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.deltaList.forEach((frozenDeltas) => {
|
|
194
|
+
yjsText.applyDelta(frozenDeltas.data)
|
|
195
|
+
})
|
|
196
|
+
}, ctx.yjsOrigin)
|
|
197
|
+
}
|
|
198
|
+
} else if (newDeltas.length > 0) {
|
|
199
|
+
const ctx = yjsBindingContext.get(this)
|
|
200
|
+
|
|
201
|
+
if (shouldReplicateToYjs(ctx)) {
|
|
202
|
+
const { yjsText } = this
|
|
203
|
+
if (isYjsValueDeleted(yjsText)) {
|
|
204
|
+
throw failure("cannot reapply deltas to deleted Yjs.Text")
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
ctx.yjsDoc.transact(() => {
|
|
208
|
+
newDeltas.forEach((frozenDeltas) => {
|
|
209
|
+
yjsText.applyDelta(frozenDeltas.data)
|
|
210
|
+
})
|
|
211
|
+
}, ctx.yjsOrigin)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
reapplyDeltasToYjsText = false
|
|
216
|
+
newDeltas.length = 0
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
const diposeYjsTextChangedAtom = hookYjsTextChangedAtom(
|
|
221
|
+
() => this.yjsText,
|
|
222
|
+
this.yjsTextChangedAtom
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return () => {
|
|
226
|
+
disposeOnSnapshot()
|
|
227
|
+
disposeReactionToDeltaListRefChange()
|
|
228
|
+
disposeObserveDeltaList?.()
|
|
229
|
+
disposeObserveDeltaList = undefined
|
|
230
|
+
|
|
231
|
+
diposeYjsTextChangedAtom()
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// we use this trick just to avoid a babel bug that causes classes used inside classes not to be overriden
|
|
237
|
+
// by the decorator
|
|
238
|
+
const DecoratedYjsTextModel = YjsTextModel
|
|
239
|
+
|
|
240
|
+
function hookYjsTextChangedAtom(getYjsText: () => Y.Text, textChangedAtom: IAtom) {
|
|
241
|
+
let disposeObserveYjsText: (() => void) | undefined
|
|
242
|
+
|
|
243
|
+
const observeFn = () => {
|
|
244
|
+
textChangedAtom.reportChanged()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const disposeReactionToYTextChange = reaction(
|
|
248
|
+
() => {
|
|
249
|
+
try {
|
|
250
|
+
const yjsText = getYjsText()
|
|
251
|
+
return isYjsValueDeleted(yjsText) ? undefined : yjsText
|
|
252
|
+
} catch {
|
|
253
|
+
return undefined
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
(yjsText) => {
|
|
257
|
+
disposeObserveYjsText?.()
|
|
258
|
+
disposeObserveYjsText = undefined
|
|
259
|
+
|
|
260
|
+
if (yjsText) {
|
|
261
|
+
yjsText.observe(observeFn)
|
|
262
|
+
|
|
263
|
+
disposeObserveYjsText = () => {
|
|
264
|
+
yjsText.unobserve(observeFn)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
textChangedAtom.reportChanged()
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
fireImmediately: true,
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return () => {
|
|
276
|
+
disposeReactionToYTextChange()
|
|
277
|
+
disposeObserveYjsText?.()
|
|
278
|
+
disposeObserveYjsText = undefined
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DeepChange, DeepChangeType } from "mobx-keystone"
|
|
2
|
+
import * as Y from "yjs"
|
|
3
|
+
import { failure } from "../utils/error"
|
|
4
|
+
import { isYjsValueDeleted } from "../utils/isYjsValueDeleted"
|
|
5
|
+
import { convertJsonToYjsData } from "./convertJsonToYjsData"
|
|
6
|
+
import { resolveYjsPath } from "./resolveYjsPath"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Converts a snapshot value to a Yjs-compatible value.
|
|
10
|
+
* Note: All values passed here are already snapshots (captured at change time).
|
|
11
|
+
*/
|
|
12
|
+
function convertValue(v: unknown): any {
|
|
13
|
+
// Handle primitives directly
|
|
14
|
+
if (v === null || v === undefined || typeof v !== "object") {
|
|
15
|
+
return v
|
|
16
|
+
}
|
|
17
|
+
// Handle plain arrays - used for empty array init
|
|
18
|
+
if (Array.isArray(v) && v.length === 0) {
|
|
19
|
+
return new Y.Array()
|
|
20
|
+
}
|
|
21
|
+
// Value is already a snapshot, convert to Yjs data
|
|
22
|
+
return convertJsonToYjsData(v as any)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function applyMobxChangeToYjsObject(
|
|
26
|
+
change: DeepChange,
|
|
27
|
+
yjsObject: Y.Map<any> | Y.Array<any> | Y.Text
|
|
28
|
+
): void {
|
|
29
|
+
// Check if the YJS object is deleted
|
|
30
|
+
if (isYjsValueDeleted(yjsObject)) {
|
|
31
|
+
throw failure("cannot apply patch to deleted Yjs value")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const yjsContainer = resolveYjsPath(yjsObject, change.path)
|
|
35
|
+
|
|
36
|
+
if (!yjsContainer) {
|
|
37
|
+
// Container not found, skip this change
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (yjsContainer instanceof Y.Array) {
|
|
42
|
+
if (change.type === DeepChangeType.ArraySplice) {
|
|
43
|
+
// splice
|
|
44
|
+
yjsContainer.delete(change.index, change.removedValues.length)
|
|
45
|
+
if (change.addedValues.length > 0) {
|
|
46
|
+
const valuesToInsert = change.addedValues.map(convertValue)
|
|
47
|
+
yjsContainer.insert(change.index, valuesToInsert)
|
|
48
|
+
}
|
|
49
|
+
} else if (change.type === DeepChangeType.ArrayUpdate) {
|
|
50
|
+
// update
|
|
51
|
+
yjsContainer.delete(change.index, 1)
|
|
52
|
+
yjsContainer.insert(change.index, [convertValue(change.newValue)])
|
|
53
|
+
} else {
|
|
54
|
+
throw failure(`unsupported array change type: ${change.type}`)
|
|
55
|
+
}
|
|
56
|
+
} else if (yjsContainer instanceof Y.Map) {
|
|
57
|
+
if (change.type === DeepChangeType.ObjectAdd || change.type === DeepChangeType.ObjectUpdate) {
|
|
58
|
+
const key = change.key
|
|
59
|
+
if (change.newValue === undefined) {
|
|
60
|
+
yjsContainer.delete(key)
|
|
61
|
+
} else {
|
|
62
|
+
yjsContainer.set(key, convertValue(change.newValue))
|
|
63
|
+
}
|
|
64
|
+
} else if (change.type === DeepChangeType.ObjectRemove) {
|
|
65
|
+
const key = change.key
|
|
66
|
+
yjsContainer.delete(key)
|
|
67
|
+
} else {
|
|
68
|
+
throw failure(`unsupported object change type: ${change.type}`)
|
|
69
|
+
}
|
|
70
|
+
} else if (yjsContainer instanceof Y.Text) {
|
|
71
|
+
// Y.Text is handled differently - init changes for text are managed by YjsTextModel
|
|
72
|
+
// Skip init changes for Y.Text containers
|
|
73
|
+
return
|
|
74
|
+
} else {
|
|
75
|
+
throw failure(`unsupported Yjs container type: ${yjsContainer}`)
|
|
76
|
+
}
|
|
77
|
+
}
|