mobx-keystone-yjs
Version:
Yjs bindings for mobx-keystone
99 lines (94 loc) • 3.23 kB
text/typescript
import { Patch } from "mobx-keystone"
import * as Y from "yjs"
import { failure } from "../utils/error"
import { convertJsonToYjsData } from "./convertJsonToYjsData"
import { PlainValue } from "../plainTypes"
export function applyMobxKeystonePatchToYjsObject(patch: Patch, yjs: unknown): void {
if (patch.path.length > 1) {
const [key, ...rest] = patch.path
if (yjs instanceof Y.Map) {
const child = yjs.get(String(key)) as unknown
if (child === undefined) {
throw failure(
`invalid patch path, key "${key}" not found in Yjs map - patch: ${JSON.stringify(patch)}`
)
}
applyMobxKeystonePatchToYjsObject({ ...patch, path: rest }, child)
} else if (yjs instanceof Y.Array) {
const child = yjs.get(Number(key)) as unknown
if (child === undefined) {
throw failure(
`invalid patch path, key "${key}" not found in Yjs array - patch: ${JSON.stringify(
patch
)}`
)
}
applyMobxKeystonePatchToYjsObject({ ...patch, path: rest }, child)
} else if (yjs instanceof Y.Text) {
// changes to deltaList will be handled by the array observe in the YjsTextModel class
} else {
throw failure(
`invalid patch path, key "${key}" not found in unknown Yjs object - patch: ${JSON.stringify(
patch
)}`
)
}
} else if (patch.path.length === 1) {
if (yjs instanceof Y.Map) {
const key = String(patch.path[0])
switch (patch.op) {
case "add":
case "replace": {
yjs.set(key, convertJsonToYjsData(patch.value as PlainValue))
break
}
case "remove": {
yjs.delete(key)
break
}
default: {
throw failure(`invalid patch operation for map`)
}
}
} else if (yjs instanceof Y.Array) {
const key = patch.path[0]
switch (patch.op) {
case "replace": {
if (key === "length") {
const newLength = patch.value as number
if (yjs.length > newLength) {
const toDelete = yjs.length - newLength
yjs.delete(newLength, toDelete)
} else if (yjs.length < patch.value) {
const toInsert = patch.value - yjs.length
yjs.insert(yjs.length, Array.from({ length: toInsert }).fill(undefined))
}
} else {
yjs.delete(Number(key))
yjs.insert(Number(key), [convertJsonToYjsData(patch.value as PlainValue)])
}
break
}
case "add": {
yjs.insert(Number(key), [convertJsonToYjsData(patch.value as PlainValue)])
break
}
case "remove": {
yjs.delete(Number(key))
break
}
default: {
throw failure(`invalid patch operation for array`)
}
}
} else if (yjs instanceof Y.Text) {
// initialization of a YjsTextModel, do nothing
} else {
throw failure(
`invalid patch path, the Yjs object is of an unkown type, so key "${String(patch.path[0])}" cannot be found in it`
)
}
} else {
throw failure(`invalid patch path, it cannot be empty`)
}
}