@kyneta/yjs-schema 1.8.0 → 2.0.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/README.md +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/bind-yjs.test.ts +7 -7
- package/src/__tests__/create.test.ts +50 -50
- package/src/__tests__/eager-write-coherence.test.ts +21 -21
- package/src/__tests__/materialize.test.ts +13 -13
- package/src/__tests__/position.test.ts +18 -18
- package/src/__tests__/record-text-spike.test.ts +34 -34
- package/src/__tests__/substrate.test.ts +53 -53
- package/src/bind-yjs.ts +1 -1
- package/src/index.ts +1 -1
- package/src/substrate.ts +36 -4
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// resolve correctly on the converged state.
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
batch,
|
|
12
12
|
createRef,
|
|
13
13
|
hasPosition,
|
|
14
14
|
type Instruction,
|
|
@@ -55,7 +55,7 @@ function createYjsEnv(initialText: string): PositionTestEnv {
|
|
|
55
55
|
|
|
56
56
|
// Seed the initial text content
|
|
57
57
|
if (initialText.length > 0) {
|
|
58
|
-
|
|
58
|
+
batch(ref, (d: any) => {
|
|
59
59
|
d.title.insert(0, initialText)
|
|
60
60
|
})
|
|
61
61
|
}
|
|
@@ -71,7 +71,7 @@ function createYjsEnv(initialText: string): PositionTestEnv {
|
|
|
71
71
|
positions,
|
|
72
72
|
|
|
73
73
|
insert(index: number, text: string): readonly Instruction[] {
|
|
74
|
-
const ops =
|
|
74
|
+
const ops = batch(ref, (d: any) => {
|
|
75
75
|
d.title.insert(index, text)
|
|
76
76
|
})
|
|
77
77
|
const textOp = ops.find(op => isTextChange(op.change))
|
|
@@ -82,7 +82,7 @@ function createYjsEnv(initialText: string): PositionTestEnv {
|
|
|
82
82
|
},
|
|
83
83
|
|
|
84
84
|
delete(index: number, count: number): readonly Instruction[] {
|
|
85
|
-
const ops =
|
|
85
|
+
const ops = batch(ref, (d: any) => {
|
|
86
86
|
d.title.delete(index, count)
|
|
87
87
|
})
|
|
88
88
|
const textOp = ops.find(op => isTextChange(op.change))
|
|
@@ -111,7 +111,7 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
111
111
|
ensureContainers(doc1, TextSchema)
|
|
112
112
|
const substrate1 = createYjsSubstrate(doc1, TextSchema)
|
|
113
113
|
const ref1 = createRef(TextSchema, substrate1) as any
|
|
114
|
-
|
|
114
|
+
batch(ref1, (d: any) => {
|
|
115
115
|
d.title.insert(0, "hello")
|
|
116
116
|
})
|
|
117
117
|
|
|
@@ -136,10 +136,10 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
136
136
|
const pos2 = textRef2[POSITION].createPosition(4, "left") // before "o"
|
|
137
137
|
|
|
138
138
|
// --- Concurrent edits ---
|
|
139
|
-
|
|
139
|
+
batch(ref1, (d: any) => {
|
|
140
140
|
d.title.insert(0, "AA") // doc1: "AAhello"
|
|
141
141
|
})
|
|
142
|
-
|
|
142
|
+
batch(ref2, (d: any) => {
|
|
143
143
|
d.title.insert(5, "BB") // doc2: "helloBBo"
|
|
144
144
|
})
|
|
145
145
|
|
|
@@ -168,7 +168,7 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
168
168
|
ensureContainers(doc1, TextSchema)
|
|
169
169
|
const substrate1 = createYjsSubstrate(doc1, TextSchema)
|
|
170
170
|
const ref1 = createRef(TextSchema, substrate1) as any
|
|
171
|
-
|
|
171
|
+
batch(ref1, (d: any) => {
|
|
172
172
|
d.title.insert(0, "abc")
|
|
173
173
|
})
|
|
174
174
|
|
|
@@ -190,10 +190,10 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
190
190
|
const rightPos = textRef2[POSITION].createPosition(1, "right")
|
|
191
191
|
|
|
192
192
|
// --- Both insert at index 1 concurrently ---
|
|
193
|
-
|
|
193
|
+
batch(ref1, (d: any) => {
|
|
194
194
|
d.title.insert(1, "X")
|
|
195
195
|
})
|
|
196
|
-
|
|
196
|
+
batch(ref2, (d: any) => {
|
|
197
197
|
d.title.insert(1, "Y")
|
|
198
198
|
})
|
|
199
199
|
|
|
@@ -222,7 +222,7 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
222
222
|
ensureContainers(doc1, TextSchema)
|
|
223
223
|
const substrate1 = createYjsSubstrate(doc1, TextSchema)
|
|
224
224
|
const ref1 = createRef(TextSchema, substrate1) as any
|
|
225
|
-
|
|
225
|
+
batch(ref1, (d: any) => {
|
|
226
226
|
d.title.insert(0, "abcde")
|
|
227
227
|
})
|
|
228
228
|
|
|
@@ -240,7 +240,7 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
240
240
|
expect(pos.resolve()).toBe(3)
|
|
241
241
|
|
|
242
242
|
// Doc2 deletes the range covering position 3
|
|
243
|
-
|
|
243
|
+
batch(ref2, (d: any) => {
|
|
244
244
|
d.title.delete(1, 3) // "ae"
|
|
245
245
|
})
|
|
246
246
|
|
|
@@ -255,7 +255,7 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
255
255
|
expect(afterDelete!).toBeLessThanOrEqual(ref1.title().length)
|
|
256
256
|
|
|
257
257
|
// Now insert new content near the collapsed position
|
|
258
|
-
|
|
258
|
+
batch(ref1, (d: any) => {
|
|
259
259
|
d.title.insert(1, "XYZ")
|
|
260
260
|
})
|
|
261
261
|
|
|
@@ -272,7 +272,7 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
272
272
|
ensureContainers(doc1, TextSchema)
|
|
273
273
|
const substrate1 = createYjsSubstrate(doc1, TextSchema)
|
|
274
274
|
const ref1 = createRef(TextSchema, substrate1) as any
|
|
275
|
-
|
|
275
|
+
batch(ref1, (d: any) => {
|
|
276
276
|
d.title.insert(0, "hello world")
|
|
277
277
|
})
|
|
278
278
|
|
|
@@ -304,7 +304,7 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
304
304
|
ensureContainers(doc1, TextSchema)
|
|
305
305
|
const substrate1 = createYjsSubstrate(doc1, TextSchema)
|
|
306
306
|
const ref1 = createRef(TextSchema, substrate1) as any
|
|
307
|
-
|
|
307
|
+
batch(ref1, (d: any) => {
|
|
308
308
|
d.title.insert(0, "0123456789")
|
|
309
309
|
})
|
|
310
310
|
|
|
@@ -334,13 +334,13 @@ describe("YjsPosition: concurrent edits", () => {
|
|
|
334
334
|
const posC = t3[POSITION].createPosition(8, "right")
|
|
335
335
|
|
|
336
336
|
// Concurrent edits from all three peers
|
|
337
|
-
|
|
337
|
+
batch(ref1, (d: any) => {
|
|
338
338
|
d.title.insert(0, "AA")
|
|
339
339
|
})
|
|
340
|
-
|
|
340
|
+
batch(ref2, (d: any) => {
|
|
341
341
|
d.title.insert(5, "BB")
|
|
342
342
|
})
|
|
343
|
-
|
|
343
|
+
batch(ref3, (d: any) => {
|
|
344
344
|
d.title.delete(7, 2)
|
|
345
345
|
})
|
|
346
346
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// 4. Sync: two peers should converge after exchanging deltas
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
|
-
|
|
20
|
+
batch,
|
|
21
21
|
createDoc,
|
|
22
22
|
exportEntirety,
|
|
23
23
|
exportSince,
|
|
@@ -76,7 +76,7 @@ describe("record-of-struct (plain baseline)", () => {
|
|
|
76
76
|
it("set a record entry and read it back", () => {
|
|
77
77
|
const doc = createDoc(BoundPlainRecord)
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
batch(doc, (d: any) => {
|
|
80
80
|
d.profiles.set("alice", { displayName: "Alice", age: 30 })
|
|
81
81
|
})
|
|
82
82
|
|
|
@@ -89,7 +89,7 @@ describe("record-of-struct (plain baseline)", () => {
|
|
|
89
89
|
it("set multiple entries and read all back", () => {
|
|
90
90
|
const doc = createDoc(BoundPlainRecord)
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
batch(doc, (d: any) => {
|
|
93
93
|
d.profiles.set("alice", { displayName: "Alice", age: 30 })
|
|
94
94
|
d.profiles.set("bob", { displayName: "Bob", age: 25 })
|
|
95
95
|
})
|
|
@@ -104,7 +104,7 @@ describe("record-of-struct (plain baseline)", () => {
|
|
|
104
104
|
it("navigate into a record entry via .at()", () => {
|
|
105
105
|
const doc = createDoc(BoundPlainRecord)
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
batch(doc, (d: any) => {
|
|
108
108
|
d.profiles.set("alice", { displayName: "Alice", age: 30 })
|
|
109
109
|
})
|
|
110
110
|
|
|
@@ -117,7 +117,7 @@ describe("record-of-struct (plain baseline)", () => {
|
|
|
117
117
|
it("syncs record-of-struct between two peers via snapshot", () => {
|
|
118
118
|
const docA = createDoc(BoundPlainRecord)
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
batch(docA, (d: any) => {
|
|
121
121
|
d.profiles.set("alice", { displayName: "Alice", age: 30 })
|
|
122
122
|
})
|
|
123
123
|
|
|
@@ -132,7 +132,7 @@ describe("record-of-struct (plain baseline)", () => {
|
|
|
132
132
|
it("syncs record-of-struct between two peers via delta", () => {
|
|
133
133
|
const docA = createDoc(BoundPlainRecord)
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
batch(docA, (d: any) => {
|
|
136
136
|
d.profiles.set("alice", { displayName: "Alice", age: 30 })
|
|
137
137
|
})
|
|
138
138
|
|
|
@@ -141,7 +141,7 @@ describe("record-of-struct (plain baseline)", () => {
|
|
|
141
141
|
|
|
142
142
|
const v0 = version(docB)
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
batch(docA, (d: any) => {
|
|
145
145
|
d.profiles.set("bob", { displayName: "Bob", age: 25 })
|
|
146
146
|
})
|
|
147
147
|
|
|
@@ -164,7 +164,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
164
164
|
it("set a record entry with text field omitted and read it back", () => {
|
|
165
165
|
const doc = createDoc(BoundProfile)
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
batch(doc, (d: any) => {
|
|
168
168
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
169
169
|
})
|
|
170
170
|
|
|
@@ -177,7 +177,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
177
177
|
it("set a record entry with text field provided and read it back", () => {
|
|
178
178
|
const doc = createDoc(BoundProfile)
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
batch(doc, (d: any) => {
|
|
181
181
|
d.profiles.set("alice", { displayName: "Alice", bio: "Hello world" })
|
|
182
182
|
})
|
|
183
183
|
|
|
@@ -190,7 +190,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
190
190
|
it("navigate into a record entry and read the text", () => {
|
|
191
191
|
const doc = createDoc(BoundProfile)
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
batch(doc, (d: any) => {
|
|
194
194
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
195
195
|
})
|
|
196
196
|
|
|
@@ -203,11 +203,11 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
203
203
|
it("insert text into a text field inside a record entry (field omitted at creation)", () => {
|
|
204
204
|
const doc = createDoc(BoundProfile)
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
batch(doc, (d: any) => {
|
|
207
207
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
208
208
|
})
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
batch(doc, (d: any) => {
|
|
211
211
|
d.profiles.at("alice").bio.insert(0, "Hello world")
|
|
212
212
|
})
|
|
213
213
|
|
|
@@ -217,11 +217,11 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
217
217
|
it("insert text into a text field inside a record entry (field provided at creation)", () => {
|
|
218
218
|
const doc = createDoc(BoundProfile)
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
batch(doc, (d: any) => {
|
|
221
221
|
d.profiles.set("alice", { displayName: "Alice", bio: "Initial" })
|
|
222
222
|
})
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
batch(doc, (d: any) => {
|
|
225
225
|
d.profiles.at("alice").bio.insert(7, " bio")
|
|
226
226
|
})
|
|
227
227
|
|
|
@@ -231,12 +231,12 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
231
231
|
it("set multiple entries and edit text independently", () => {
|
|
232
232
|
const doc = createDoc(BoundProfile)
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
batch(doc, (d: any) => {
|
|
235
235
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
236
236
|
d.profiles.set("bob", { displayName: "Bob" })
|
|
237
237
|
})
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
batch(doc, (d: any) => {
|
|
240
240
|
d.profiles.at("alice").bio.insert(0, "Alice's bio")
|
|
241
241
|
d.profiles.at("bob").bio.insert(0, "Bob's bio")
|
|
242
242
|
})
|
|
@@ -248,7 +248,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
248
248
|
it("subscribe fires on text edit inside record entry", () => {
|
|
249
249
|
const doc = createDoc(BoundProfile)
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
batch(doc, (d: any) => {
|
|
252
252
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
253
253
|
})
|
|
254
254
|
|
|
@@ -257,7 +257,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
257
257
|
fired = true
|
|
258
258
|
})
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
batch(doc, (d: any) => {
|
|
261
261
|
d.profiles.at("alice").bio.insert(0, "Hello")
|
|
262
262
|
})
|
|
263
263
|
|
|
@@ -267,10 +267,10 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
267
267
|
it("syncs text-inside-record via snapshot", () => {
|
|
268
268
|
const docA = createDoc(BoundProfile)
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
batch(docA, (d: any) => {
|
|
271
271
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
272
272
|
})
|
|
273
|
-
|
|
273
|
+
batch(docA, (d: any) => {
|
|
274
274
|
d.profiles.at("alice").bio.insert(0, "Collaborative bio")
|
|
275
275
|
})
|
|
276
276
|
|
|
@@ -288,7 +288,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
288
288
|
it("syncs text-inside-record via delta", () => {
|
|
289
289
|
const docA = createDoc(BoundProfile)
|
|
290
290
|
|
|
291
|
-
|
|
291
|
+
batch(docA, (d: any) => {
|
|
292
292
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
293
293
|
})
|
|
294
294
|
|
|
@@ -298,7 +298,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
298
298
|
const docB = createDoc(BoundProfile, exportEntirety(docA))
|
|
299
299
|
const v0 = version(docB)
|
|
300
300
|
|
|
301
|
-
|
|
301
|
+
batch(docA, (d: any) => {
|
|
302
302
|
d.profiles.at("alice").bio.insert(0, "Hello from A")
|
|
303
303
|
})
|
|
304
304
|
|
|
@@ -311,7 +311,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
311
311
|
})
|
|
312
312
|
|
|
313
313
|
// Text on B is functional — can insert independently
|
|
314
|
-
|
|
314
|
+
batch(docB, (d: any) => {
|
|
315
315
|
d.profiles.at("alice").bio.insert(12, "!")
|
|
316
316
|
})
|
|
317
317
|
expect((docB as any).profiles.at("alice").bio()).toBe("Hello from A!")
|
|
@@ -321,7 +321,7 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
321
321
|
const docA = createDoc(BoundProfile)
|
|
322
322
|
|
|
323
323
|
// Sync initial state: A creates the entry, B starts from snapshot
|
|
324
|
-
|
|
324
|
+
batch(docA, (d: any) => {
|
|
325
325
|
d.profiles.set("alice", { displayName: "Alice" })
|
|
326
326
|
})
|
|
327
327
|
const docB = createDoc(BoundProfile, exportEntirety(docA))
|
|
@@ -330,10 +330,10 @@ describe("text-inside-struct-inside-record", () => {
|
|
|
330
330
|
const vA = version(docA)
|
|
331
331
|
const vB = version(docB)
|
|
332
332
|
|
|
333
|
-
|
|
333
|
+
batch(docA, (d: any) => {
|
|
334
334
|
d.profiles.at("alice").bio.insert(0, "Hello ")
|
|
335
335
|
})
|
|
336
|
-
|
|
336
|
+
batch(docB, (d: any) => {
|
|
337
337
|
d.profiles.at("alice").bio.insert(0, "World")
|
|
338
338
|
})
|
|
339
339
|
|
|
@@ -360,7 +360,7 @@ describe("text-inside-struct-inside-list", () => {
|
|
|
360
360
|
it("push a struct with text field omitted and read it back", () => {
|
|
361
361
|
const doc = createDoc(BoundListProfile)
|
|
362
362
|
|
|
363
|
-
|
|
363
|
+
batch(doc, (d: any) => {
|
|
364
364
|
d.players.push({ name: "Alice" })
|
|
365
365
|
})
|
|
366
366
|
|
|
@@ -372,7 +372,7 @@ describe("text-inside-struct-inside-list", () => {
|
|
|
372
372
|
it("push a struct with text field provided and read it back", () => {
|
|
373
373
|
const doc = createDoc(BoundListProfile)
|
|
374
374
|
|
|
375
|
-
|
|
375
|
+
batch(doc, (d: any) => {
|
|
376
376
|
d.players.push({ name: "Alice", bio: "Hi there" })
|
|
377
377
|
})
|
|
378
378
|
|
|
@@ -384,11 +384,11 @@ describe("text-inside-struct-inside-list", () => {
|
|
|
384
384
|
it("insert text into a text field inside a list item (field omitted at creation)", () => {
|
|
385
385
|
const doc = createDoc(BoundListProfile)
|
|
386
386
|
|
|
387
|
-
|
|
387
|
+
batch(doc, (d: any) => {
|
|
388
388
|
d.players.push({ name: "Alice" })
|
|
389
389
|
})
|
|
390
390
|
|
|
391
|
-
|
|
391
|
+
batch(doc, (d: any) => {
|
|
392
392
|
d.players.at(0).bio.insert(0, "Alice's bio")
|
|
393
393
|
})
|
|
394
394
|
|
|
@@ -398,12 +398,12 @@ describe("text-inside-struct-inside-list", () => {
|
|
|
398
398
|
it("multiple list items with independent text fields", () => {
|
|
399
399
|
const doc = createDoc(BoundListProfile)
|
|
400
400
|
|
|
401
|
-
|
|
401
|
+
batch(doc, (d: any) => {
|
|
402
402
|
d.players.push({ name: "Alice" })
|
|
403
403
|
d.players.push({ name: "Bob" })
|
|
404
404
|
})
|
|
405
405
|
|
|
406
|
-
|
|
406
|
+
batch(doc, (d: any) => {
|
|
407
407
|
d.players.at(0).bio.insert(0, "Alice's bio")
|
|
408
408
|
d.players.at(1).bio.insert(0, "Bob's bio")
|
|
409
409
|
})
|
|
@@ -415,7 +415,7 @@ describe("text-inside-struct-inside-list", () => {
|
|
|
415
415
|
it("syncs list-of-struct-with-text via delta", () => {
|
|
416
416
|
const docA = createDoc(BoundListProfile)
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
batch(docA, (d: any) => {
|
|
419
419
|
d.players.push({ name: "Alice" })
|
|
420
420
|
})
|
|
421
421
|
|
|
@@ -423,7 +423,7 @@ describe("text-inside-struct-inside-list", () => {
|
|
|
423
423
|
const docB = createDoc(BoundListProfile, exportEntirety(docA))
|
|
424
424
|
const v0 = version(docB)
|
|
425
425
|
|
|
426
|
-
|
|
426
|
+
batch(docA, (d: any) => {
|
|
427
427
|
d.players.at(0).bio.insert(0, "Synced bio")
|
|
428
428
|
})
|
|
429
429
|
|