mobx-keystone-yjs 1.0.0 → 1.2.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 +13 -5
- package/dist/mobx-keystone-yjs.esm.js +38 -24
- package/dist/mobx-keystone-yjs.esm.mjs +38 -24
- package/dist/mobx-keystone-yjs.umd.js +36 -22
- package/dist/types/binding/convertJsonToYjsData.d.ts +5 -0
- package/dist/types/binding/yjsBindingContext.d.ts +9 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/jsonTypes.d.ts +5 -5
- package/package.json +87 -87
- package/src/binding/applyMobxKeystonePatchToYjsObject.ts +92 -92
- package/src/binding/bindYjsToMobxKeystone.ts +156 -142
- package/src/binding/convertJsonToYjsData.ts +45 -0
- package/src/binding/convertYjsEventToPatches.ts +85 -85
- package/src/binding/yjsBindingContext.ts +11 -0
- package/src/index.ts +9 -2
- package/src/jsonTypes.ts +4 -4
- package/dist/types/binding/toYDataType.d.ts +0 -3
- package/src/binding/toYDataType.ts +0 -41
|
@@ -1,142 +1,156 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AnyDataModel,
|
|
3
|
-
AnyModel,
|
|
4
|
-
AnyStandardType,
|
|
5
|
-
ModelClass,
|
|
6
|
-
Patch,
|
|
7
|
-
TypeToData,
|
|
8
|
-
applyPatches,
|
|
9
|
-
fromSnapshot,
|
|
10
|
-
getParentToChildPath,
|
|
11
|
-
onGlobalPatches,
|
|
12
|
-
onPatches,
|
|
13
|
-
onSnapshot,
|
|
14
|
-
} from "mobx-keystone"
|
|
15
|
-
import * as Y from "yjs"
|
|
16
|
-
import { applyMobxKeystonePatchToYjsObject } from "./applyMobxKeystonePatchToYjsObject"
|
|
17
|
-
import { convertYjsEventToPatches } from "./convertYjsEventToPatches"
|
|
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
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AnyDataModel,
|
|
3
|
+
AnyModel,
|
|
4
|
+
AnyStandardType,
|
|
5
|
+
ModelClass,
|
|
6
|
+
Patch,
|
|
7
|
+
TypeToData,
|
|
8
|
+
applyPatches,
|
|
9
|
+
fromSnapshot,
|
|
10
|
+
getParentToChildPath,
|
|
11
|
+
onGlobalPatches,
|
|
12
|
+
onPatches,
|
|
13
|
+
onSnapshot,
|
|
14
|
+
} from "mobx-keystone"
|
|
15
|
+
import * as Y from "yjs"
|
|
16
|
+
import { applyMobxKeystonePatchToYjsObject } from "./applyMobxKeystonePatchToYjsObject"
|
|
17
|
+
import { convertYjsEventToPatches } from "./convertYjsEventToPatches"
|
|
18
|
+
import { YjsBindingContext, yjsBindingContext } from "./yjsBindingContext"
|
|
19
|
+
|
|
20
|
+
export function bindYjsToMobxKeystone<
|
|
21
|
+
TType extends AnyStandardType | ModelClass<AnyModel> | ModelClass<AnyDataModel>,
|
|
22
|
+
>({
|
|
23
|
+
yjsDoc,
|
|
24
|
+
yjsObject,
|
|
25
|
+
mobxKeystoneType,
|
|
26
|
+
}: {
|
|
27
|
+
yjsDoc: Y.Doc
|
|
28
|
+
yjsObject: Y.Map<unknown> | Y.Array<unknown>
|
|
29
|
+
mobxKeystoneType: TType
|
|
30
|
+
}): {
|
|
31
|
+
boundObject: TypeToData<TType>
|
|
32
|
+
dispose(): void
|
|
33
|
+
yjsOrigin: symbol
|
|
34
|
+
} {
|
|
35
|
+
const yjsOrigin = Symbol("bindYjsToMobxKeystoneTransactionOrigin")
|
|
36
|
+
|
|
37
|
+
const bindingContext: YjsBindingContext = {
|
|
38
|
+
yjsDoc,
|
|
39
|
+
yjsObject,
|
|
40
|
+
mobxKeystoneType,
|
|
41
|
+
yjsOrigin,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const yjsJson = yjsObject.toJSON()
|
|
45
|
+
|
|
46
|
+
const initializationGlobalPatches: { target: object; patches: Patch[] }[] = []
|
|
47
|
+
|
|
48
|
+
const createBoundObject = () => {
|
|
49
|
+
const disposeOnGlobalPatches = onGlobalPatches((target, patches) => {
|
|
50
|
+
initializationGlobalPatches.push({ target, patches })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const boundObject = yjsBindingContext.apply(
|
|
55
|
+
() => fromSnapshot(mobxKeystoneType, yjsJson as any),
|
|
56
|
+
bindingContext
|
|
57
|
+
)
|
|
58
|
+
yjsBindingContext.set(boundObject, bindingContext)
|
|
59
|
+
return boundObject
|
|
60
|
+
} finally {
|
|
61
|
+
disposeOnGlobalPatches()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const boundObject = createBoundObject()
|
|
66
|
+
|
|
67
|
+
let applyingMobxKeystoneChanges = 0
|
|
68
|
+
|
|
69
|
+
// bind any changes from yjs to mobx-keystone
|
|
70
|
+
const observeDeepCb = (events: Y.YEvent<any>[]) => {
|
|
71
|
+
const patches: Patch[] = []
|
|
72
|
+
events.forEach((event) => {
|
|
73
|
+
if (event.transaction.origin !== yjsOrigin) {
|
|
74
|
+
patches.push(...convertYjsEventToPatches(event))
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
if (patches.length > 0) {
|
|
79
|
+
applyingMobxKeystoneChanges++
|
|
80
|
+
try {
|
|
81
|
+
applyPatches(boundObject, patches)
|
|
82
|
+
} finally {
|
|
83
|
+
applyingMobxKeystoneChanges--
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
yjsObject.observeDeep(observeDeepCb)
|
|
89
|
+
|
|
90
|
+
// bind any changes from mobx-keystone to yjs
|
|
91
|
+
let pendingPatches: Patch[] = []
|
|
92
|
+
const disposeOnPatches = onPatches(boundObject, (patches) => {
|
|
93
|
+
if (applyingMobxKeystoneChanges > 0) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pendingPatches.push(...patches)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// this is only used so we can transact all patches to the snapshot boundary
|
|
101
|
+
const disposeOnSnapshot = onSnapshot(boundObject, () => {
|
|
102
|
+
if (pendingPatches.length === 0) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const patches = pendingPatches
|
|
107
|
+
pendingPatches = []
|
|
108
|
+
|
|
109
|
+
yjsDoc.transact(() => {
|
|
110
|
+
patches.forEach((patch) => {
|
|
111
|
+
applyMobxKeystonePatchToYjsObject(patch, yjsObject)
|
|
112
|
+
})
|
|
113
|
+
}, yjsOrigin)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// sync initial patches, that might include setting defaults, IDs, etc
|
|
117
|
+
yjsDoc.transact(() => {
|
|
118
|
+
// we need to skip initializations until we hit the initialization of the bound object
|
|
119
|
+
// this is because default objects might be created and initialized before the main object
|
|
120
|
+
// but we just need to catch when those are actually assigned to the bound object
|
|
121
|
+
let boundObjectFound = false
|
|
122
|
+
|
|
123
|
+
initializationGlobalPatches.forEach(({ target, patches }) => {
|
|
124
|
+
if (!boundObjectFound) {
|
|
125
|
+
if (target !== boundObject) {
|
|
126
|
+
return // skip
|
|
127
|
+
}
|
|
128
|
+
boundObjectFound = true
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const parentToChildPath = getParentToChildPath(boundObject, target)
|
|
132
|
+
// this is undefined only if target is not a child of boundModel
|
|
133
|
+
if (parentToChildPath !== undefined) {
|
|
134
|
+
patches.forEach((patch) => {
|
|
135
|
+
applyMobxKeystonePatchToYjsObject(
|
|
136
|
+
{
|
|
137
|
+
...patch,
|
|
138
|
+
path: [...parentToChildPath, ...patch.path],
|
|
139
|
+
},
|
|
140
|
+
yjsObject
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}, yjsOrigin)
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
boundObject,
|
|
149
|
+
dispose: () => {
|
|
150
|
+
disposeOnPatches()
|
|
151
|
+
disposeOnSnapshot()
|
|
152
|
+
yjsObject.unobserveDeep(observeDeepCb)
|
|
153
|
+
},
|
|
154
|
+
yjsOrigin,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as Y from "yjs"
|
|
2
|
+
import { JsonValue, JsonArray, JsonObject, JsonPrimitive } from "../jsonTypes"
|
|
3
|
+
|
|
4
|
+
function isJsonPrimitive(v: JsonValue): v is JsonPrimitive {
|
|
5
|
+
const t = typeof v
|
|
6
|
+
return t === "string" || t === "number" || t === "boolean" || v === null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isJsonArray(v: JsonValue): v is JsonArray {
|
|
10
|
+
return Array.isArray(v)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isJsonObject(v: JsonValue): v is JsonObject {
|
|
14
|
+
return !isJsonArray(v) && typeof v === "object"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function convertJsonToYjsData(v: JsonValue) {
|
|
18
|
+
if (v === undefined || isJsonPrimitive(v)) {
|
|
19
|
+
return v
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (isJsonArray(v)) {
|
|
23
|
+
const arr = new Y.Array()
|
|
24
|
+
applyJsonArrayYArray(arr, v)
|
|
25
|
+
return arr
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isJsonObject(v)) {
|
|
29
|
+
const map = new Y.Map()
|
|
30
|
+
applyJsonObjectToYMap(map, v)
|
|
31
|
+
return map
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
throw new Error(`unsupported value type: ${v}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function applyJsonArrayYArray(dest: Y.Array<unknown>, source: JsonArray) {
|
|
38
|
+
dest.push(source.map(convertJsonToYjsData))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function applyJsonObjectToYMap(dest: Y.Map<unknown>, source: JsonObject) {
|
|
42
|
+
Object.entries(source).forEach(([k, v]) => {
|
|
43
|
+
dest.set(k, convertJsonToYjsData(v))
|
|
44
|
+
})
|
|
45
|
+
}
|
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
import { Patch } from "mobx-keystone"
|
|
2
|
-
import * as Y from "yjs"
|
|
3
|
-
import {
|
|
4
|
-
import { failure } from "../utils/error"
|
|
5
|
-
|
|
6
|
-
export function convertYjsEventToPatches(event: Y.YEvent<any>): Patch[] {
|
|
7
|
-
const patches: Patch[] = []
|
|
8
|
-
|
|
9
|
-
if (event instanceof Y.YMapEvent) {
|
|
10
|
-
const source = event.target as Y.Map<any>
|
|
11
|
-
|
|
12
|
-
event.changes.keys.forEach((change, key) => {
|
|
13
|
-
const path = [...event.path, key]
|
|
14
|
-
|
|
15
|
-
switch (change.action) {
|
|
16
|
-
case "add":
|
|
17
|
-
patches.push({
|
|
18
|
-
op: "add",
|
|
19
|
-
path,
|
|
20
|
-
value: toPlainValue(source.get(key)),
|
|
21
|
-
})
|
|
22
|
-
break
|
|
23
|
-
|
|
24
|
-
case "update":
|
|
25
|
-
patches.push({
|
|
26
|
-
op: "replace",
|
|
27
|
-
path,
|
|
28
|
-
value: toPlainValue(source.get(key)),
|
|
29
|
-
})
|
|
30
|
-
break
|
|
31
|
-
|
|
32
|
-
case "delete":
|
|
33
|
-
patches.push({
|
|
34
|
-
op: "remove",
|
|
35
|
-
path,
|
|
36
|
-
})
|
|
37
|
-
break
|
|
38
|
-
|
|
39
|
-
default:
|
|
40
|
-
throw failure(`unsupported Yjs map event action: ${change.action}`)
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
} else if (event instanceof Y.YArrayEvent) {
|
|
44
|
-
let retain = 0
|
|
45
|
-
event.changes.delta.forEach((change) => {
|
|
46
|
-
if (change.retain) {
|
|
47
|
-
retain += change.retain
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (change.delete) {
|
|
51
|
-
// remove X items at retain position
|
|
52
|
-
const path = [...event.path, retain]
|
|
53
|
-
for (let i = 0; i < change.delete; i++) {
|
|
54
|
-
patches.push({
|
|
55
|
-
op: "remove",
|
|
56
|
-
path,
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (change.insert) {
|
|
62
|
-
const newValues = Array.isArray(change.insert) ? change.insert : [change.insert]
|
|
63
|
-
newValues.forEach((v) => {
|
|
64
|
-
const path = [...event.path, retain]
|
|
65
|
-
patches.push({
|
|
66
|
-
op: "add",
|
|
67
|
-
path,
|
|
68
|
-
value: toPlainValue(v),
|
|
69
|
-
})
|
|
70
|
-
retain++
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return patches
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function toPlainValue(v: Y.Map<any> | Y.Array<any> |
|
|
80
|
-
if (v instanceof Y.Map || v instanceof Y.Array) {
|
|
81
|
-
return v.toJSON() as
|
|
82
|
-
} else {
|
|
83
|
-
return v
|
|
84
|
-
}
|
|
85
|
-
}
|
|
1
|
+
import { Patch } from "mobx-keystone"
|
|
2
|
+
import * as Y from "yjs"
|
|
3
|
+
import { JsonArray, JsonObject, JsonValue } from "../jsonTypes"
|
|
4
|
+
import { failure } from "../utils/error"
|
|
5
|
+
|
|
6
|
+
export function convertYjsEventToPatches(event: Y.YEvent<any>): Patch[] {
|
|
7
|
+
const patches: Patch[] = []
|
|
8
|
+
|
|
9
|
+
if (event instanceof Y.YMapEvent) {
|
|
10
|
+
const source = event.target as Y.Map<any>
|
|
11
|
+
|
|
12
|
+
event.changes.keys.forEach((change, key) => {
|
|
13
|
+
const path = [...event.path, key]
|
|
14
|
+
|
|
15
|
+
switch (change.action) {
|
|
16
|
+
case "add":
|
|
17
|
+
patches.push({
|
|
18
|
+
op: "add",
|
|
19
|
+
path,
|
|
20
|
+
value: toPlainValue(source.get(key)),
|
|
21
|
+
})
|
|
22
|
+
break
|
|
23
|
+
|
|
24
|
+
case "update":
|
|
25
|
+
patches.push({
|
|
26
|
+
op: "replace",
|
|
27
|
+
path,
|
|
28
|
+
value: toPlainValue(source.get(key)),
|
|
29
|
+
})
|
|
30
|
+
break
|
|
31
|
+
|
|
32
|
+
case "delete":
|
|
33
|
+
patches.push({
|
|
34
|
+
op: "remove",
|
|
35
|
+
path,
|
|
36
|
+
})
|
|
37
|
+
break
|
|
38
|
+
|
|
39
|
+
default:
|
|
40
|
+
throw failure(`unsupported Yjs map event action: ${change.action}`)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
} else if (event instanceof Y.YArrayEvent) {
|
|
44
|
+
let retain = 0
|
|
45
|
+
event.changes.delta.forEach((change) => {
|
|
46
|
+
if (change.retain) {
|
|
47
|
+
retain += change.retain
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (change.delete) {
|
|
51
|
+
// remove X items at retain position
|
|
52
|
+
const path = [...event.path, retain]
|
|
53
|
+
for (let i = 0; i < change.delete; i++) {
|
|
54
|
+
patches.push({
|
|
55
|
+
op: "remove",
|
|
56
|
+
path,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (change.insert) {
|
|
62
|
+
const newValues = Array.isArray(change.insert) ? change.insert : [change.insert]
|
|
63
|
+
newValues.forEach((v) => {
|
|
64
|
+
const path = [...event.path, retain]
|
|
65
|
+
patches.push({
|
|
66
|
+
op: "add",
|
|
67
|
+
path,
|
|
68
|
+
value: toPlainValue(v),
|
|
69
|
+
})
|
|
70
|
+
retain++
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return patches
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function toPlainValue(v: Y.Map<any> | Y.Array<any> | JsonValue) {
|
|
80
|
+
if (v instanceof Y.Map || v instanceof Y.Array) {
|
|
81
|
+
return v.toJSON() as JsonObject | JsonArray
|
|
82
|
+
} else {
|
|
83
|
+
return v
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AnyType, createContext } from "mobx-keystone"
|
|
2
|
+
import * as Y from "yjs"
|
|
3
|
+
|
|
4
|
+
export interface YjsBindingContext {
|
|
5
|
+
yjsDoc: Y.Doc
|
|
6
|
+
yjsObject: Y.Map<unknown> | Y.Array<unknown>
|
|
7
|
+
mobxKeystoneType: AnyType
|
|
8
|
+
yjsOrigin: symbol
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const yjsBindingContext = createContext<YjsBindingContext | undefined>(undefined)
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { bindYjsToMobxKeystone } from "./binding/bindYjsToMobxKeystone"
|
|
2
|
+
export {
|
|
3
|
+
applyJsonArrayYArray,
|
|
4
|
+
applyJsonObjectToYMap,
|
|
5
|
+
convertJsonToYjsData,
|
|
6
|
+
} from "./binding/convertJsonToYjsData"
|
|
7
|
+
export { yjsBindingContext } from "./binding/yjsBindingContext"
|
|
8
|
+
export type { YjsBindingContext } from "./binding/yjsBindingContext"
|
|
9
|
+
export { MobxKeystoneYjsError } from "./utils/error"
|
package/src/jsonTypes.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export type
|
|
3
|
-
export type
|
|
4
|
-
export interface
|
|
1
|
+
export type JsonPrimitive = string | number | boolean | null
|
|
2
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonArray
|
|
3
|
+
export type JsonObject = { [key: string]: JsonValue }
|
|
4
|
+
export interface JsonArray extends Array<JsonValue> {}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import * as Y from "yjs"
|
|
2
|
-
import { JSONValue, JSONArray, JSONObject, JSONPrimitive } from "../jsonTypes"
|
|
3
|
-
|
|
4
|
-
function isJSONPrimitive(v: JSONValue): v is JSONPrimitive {
|
|
5
|
-
const t = typeof v
|
|
6
|
-
return t === "string" || t === "number" || t === "boolean" || v === null
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function isJSONArray(v: JSONValue): v is JSONArray {
|
|
10
|
-
return Array.isArray(v)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function isJSONObject(v: JSONValue): v is JSONObject {
|
|
14
|
-
return !isJSONArray(v) && typeof v === "object"
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function toYDataType(v: JSONValue) {
|
|
18
|
-
if (isJSONPrimitive(v)) {
|
|
19
|
-
return v
|
|
20
|
-
} else if (isJSONArray(v)) {
|
|
21
|
-
const arr = new Y.Array()
|
|
22
|
-
applyJsonArray(arr, v)
|
|
23
|
-
return arr
|
|
24
|
-
} else if (isJSONObject(v)) {
|
|
25
|
-
const map = new Y.Map()
|
|
26
|
-
applyJsonObject(map, v)
|
|
27
|
-
return map
|
|
28
|
-
} else {
|
|
29
|
-
return undefined
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function applyJsonArray(dest: Y.Array<unknown>, source: JSONArray) {
|
|
34
|
-
dest.push(source.map(toYDataType))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function applyJsonObject(dest: Y.Map<unknown>, source: JSONObject) {
|
|
38
|
-
Object.entries(source).forEach(([k, v]) => {
|
|
39
|
-
dest.set(k, toYDataType(v))
|
|
40
|
-
})
|
|
41
|
-
}
|